Home | History | Annotate | Download | only in its
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.cts.verifier.camera.its;
     18 
     19 import android.content.BroadcastReceiver;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.IntentFilter;
     23 import android.content.res.Configuration;
     24 import android.hardware.camera2.CameraAccessException;
     25 import android.hardware.camera2.CameraCharacteristics;
     26 import android.hardware.camera2.CameraManager;
     27 import android.os.Bundle;
     28 import android.text.method.ScrollingMovementMethod;
     29 import android.util.Log;
     30 import android.view.WindowManager;
     31 import android.widget.TextView;
     32 import android.widget.Toast;
     33 
     34 import java.util.ArrayList;
     35 import java.util.Arrays;
     36 import java.util.Comparator;
     37 import java.util.HashSet;
     38 import java.util.HashMap;
     39 import java.util.Iterator;
     40 import java.util.List;
     41 import java.util.Set;
     42 import java.util.TreeSet;
     43 import java.io.BufferedReader;
     44 import java.io.FileReader;
     45 import java.io.FileNotFoundException;
     46 import java.io.IOException;
     47 
     48 import com.android.compatibility.common.util.ResultType;
     49 import com.android.compatibility.common.util.ResultUnit;
     50 import com.android.cts.verifier.ArrayTestListAdapter;
     51 import com.android.cts.verifier.DialogTestListActivity;
     52 import com.android.cts.verifier.R;
     53 import com.android.cts.verifier.TestResult;
     54 
     55 import org.json.JSONArray;
     56 import org.json.JSONObject;
     57 
     58 /**
     59  * Test for Camera features that require that the camera be aimed at a specific test scene.
     60  * This test activity requires a USB connection to a computer, and a corresponding host-side run of
     61  * the python scripts found in the CameraITS directory.
     62  */
     63 public class ItsTestActivity extends DialogTestListActivity {
     64     private static final String TAG = "ItsTestActivity";
     65     private static final String EXTRA_CAMERA_ID = "camera.its.extra.CAMERA_ID";
     66     private static final String EXTRA_RESULTS = "camera.its.extra.RESULTS";
     67     private static final String EXTRA_VERSION = "camera.its.extra.VERSION";
     68     private static final String CURRENT_VERSION = "1.0";
     69     private static final String ACTION_ITS_RESULT =
     70             "com.android.cts.verifier.camera.its.ACTION_ITS_RESULT";
     71 
     72     private static final String RESULT_PASS = "PASS";
     73     private static final String RESULT_FAIL = "FAIL";
     74     private static final String RESULT_NOT_EXECUTED = "NOT_EXECUTED";
     75     private static final Set<String> RESULT_VALUES = new HashSet<String>(
     76             Arrays.asList(new String[] {RESULT_PASS, RESULT_FAIL, RESULT_NOT_EXECUTED}));
     77     private static final int MAX_SUMMARY_LEN = 200;
     78 
     79     private final ResultReceiver mResultsReceiver = new ResultReceiver();
     80 
     81     // Initialized in onCreate
     82     ArrayList<String> mNonLegacyCameraIds = null;
     83 
     84     // Scenes
     85     private static final ArrayList<String> mSceneIds = new ArrayList<String> () { {
     86             add("scene0");
     87             add("scene1");
     88             add("scene2");
     89             add("scene3");
     90             add("scene4");
     91             add("scene5");
     92         } };
     93 
     94     // TODO: cache the following in saved bundle
     95     private Set<ResultKey> mAllScenes = null;
     96     // (camera, scene) -> (pass, fail)
     97     private final HashMap<ResultKey, Boolean> mExecutedScenes = new HashMap<>();
     98     // map camera id to ITS summary report path
     99     private final HashMap<ResultKey, String> mSummaryMap = new HashMap<>();
    100 
    101     final class ResultKey {
    102         public final String cameraId;
    103         public final String sceneId;
    104 
    105         public ResultKey(String cameraId, String sceneId) {
    106             this.cameraId = cameraId;
    107             this.sceneId = sceneId;
    108         }
    109 
    110         @Override
    111         public boolean equals(final Object o) {
    112             if (o == null) return false;
    113             if (this == o) return true;
    114             if (o instanceof ResultKey) {
    115                 final ResultKey other = (ResultKey) o;
    116                 return cameraId.equals(other.cameraId) && sceneId.equals(other.sceneId);
    117             }
    118             return false;
    119         }
    120 
    121         @Override
    122         public int hashCode() {
    123             int h = cameraId.hashCode();
    124             h = ((h << 5) - h) ^ sceneId.hashCode();
    125             return h;
    126         }
    127     }
    128 
    129     public ItsTestActivity() {
    130         super(R.layout.its_main,
    131                 R.string.camera_its_test,
    132                 R.string.camera_its_test_info,
    133                 R.string.camera_its_test);
    134     }
    135 
    136     private final Comparator<ResultKey> mComparator = new Comparator<ResultKey>() {
    137         @Override
    138         public int compare(ResultKey k1, ResultKey k2) {
    139             if (k1.cameraId.equals(k2.cameraId))
    140                 return k1.sceneId.compareTo(k2.sceneId);
    141             return k1.cameraId.compareTo(k2.cameraId);
    142         }
    143     };
    144 
    145     class ResultReceiver extends BroadcastReceiver {
    146         @Override
    147         public void onReceive(Context context, Intent intent) {
    148             Log.i(TAG, "Received result for Camera ITS tests");
    149             if (ACTION_ITS_RESULT.equals(intent.getAction())) {
    150                 String version = intent.getStringExtra(EXTRA_VERSION);
    151                 if (version == null || !version.equals(CURRENT_VERSION)) {
    152                     Log.e(TAG, "Its result version mismatch: expect " + CURRENT_VERSION +
    153                             ", got " + ((version == null) ? "null" : version));
    154                     ItsTestActivity.this.showToast(R.string.its_version_mismatch);
    155                     return;
    156                 }
    157 
    158                 String cameraId = intent.getStringExtra(EXTRA_CAMERA_ID);
    159                 String results = intent.getStringExtra(EXTRA_RESULTS);
    160                 if (cameraId == null || results == null) {
    161                     Log.e(TAG, "cameraId = " + ((cameraId == null) ? "null" : cameraId) +
    162                             ", results = " + ((results == null) ? "null" : results));
    163                     return;
    164                 }
    165 
    166                 if (!mNonLegacyCameraIds.contains(cameraId)) {
    167                     Log.e(TAG, "Unknown camera id " + cameraId + " reported to ITS");
    168                     return;
    169                 }
    170 
    171                 try {
    172                     /* Sample JSON results string
    173                     {
    174                        "scene0":{
    175                           "result":"PASS",
    176                           "summary":"/sdcard/cam0_scene0.txt"
    177                        },
    178                        "scene1":{
    179                           "result":"NOT_EXECUTED"
    180                        },
    181                        "scene2":{
    182                           "result":"FAIL",
    183                           "summary":"/sdcard/cam0_scene2.txt"
    184                        }
    185                     }
    186                     */
    187                     JSONObject jsonResults = new JSONObject(results);
    188                     Set<String> scenes = new HashSet<>();
    189                     Iterator<String> keys = jsonResults.keys();
    190                     while (keys.hasNext()) {
    191                         scenes.add(keys.next());
    192                     }
    193                     boolean newScenes = false;
    194                     if (mAllScenes == null) {
    195                         mAllScenes = new TreeSet<>(mComparator);
    196                         newScenes = true;
    197                     } else { // See if scene lists changed
    198                         for (String scene : scenes) {
    199                             if (!mAllScenes.contains(new ResultKey(cameraId, scene))) {
    200                                 // Scene list changed. Cleanup previous test results
    201                                 newScenes = true;
    202                                 break;
    203                             }
    204                         }
    205                         for (ResultKey k : mAllScenes) {
    206                             if (!scenes.contains(k.sceneId)) {
    207                                 newScenes = true;
    208                                 break;
    209                             }
    210                         }
    211                     }
    212                     if (newScenes) {
    213                         mExecutedScenes.clear();
    214                         mAllScenes.clear();
    215                         for (String scene : scenes) {
    216                             for (String c : mNonLegacyCameraIds) {
    217                                 mAllScenes.add(new ResultKey(c, scene));
    218                             }
    219                         }
    220                     }
    221 
    222                     // Update test execution results
    223                     for (String scene : scenes) {
    224                         JSONObject sceneResult = jsonResults.getJSONObject(scene);
    225                         String result = sceneResult.getString("result");
    226                         if (result == null) {
    227                             Log.e(TAG, "Result for " + scene + " is null");
    228                             return;
    229                         }
    230                         Log.i(TAG, "ITS camera" + cameraId + " " + scene + ": result:" + result);
    231                         if (!RESULT_VALUES.contains(result)) {
    232                             Log.e(TAG, "Unknown result for " + scene + ": " + result);
    233                             return;
    234                         }
    235                         ResultKey key = new ResultKey(cameraId, scene);
    236                         if (result.equals(RESULT_PASS) || result.equals(RESULT_FAIL)) {
    237                             boolean pass = result.equals(RESULT_PASS);
    238                             mExecutedScenes.put(key, pass);
    239                             setTestResult(testId(cameraId, scene), pass ?
    240                                     TestResult.TEST_RESULT_PASSED : TestResult.TEST_RESULT_FAILED);
    241                             Log.e(TAG, "setTestResult for " + testId(cameraId, scene) + ": " + result);
    242                             String summary = sceneResult.optString("summary");
    243                             if (!summary.equals("")) {
    244                                 mSummaryMap.put(key, summary);
    245                             }
    246                         } // do nothing for NOT_EXECUTED scenes
    247                     }
    248                 } catch (org.json.JSONException e) {
    249                     Log.e(TAG, "Error reading json result string:" + results , e);
    250                     return;
    251                 }
    252 
    253                 // Set summary if all scenes reported
    254                 if (mSummaryMap.keySet().containsAll(mAllScenes)) {
    255                     StringBuilder summary = new StringBuilder();
    256                     for (String path : mSummaryMap.values()) {
    257                         appendFileContentToSummary(summary, path);
    258                     }
    259                     if (summary.length() > MAX_SUMMARY_LEN) {
    260                         Log.w(TAG, "ITS summary report too long: len: " + summary.length());
    261                     }
    262                     ItsTestActivity.this.getReportLog().setSummary(
    263                             summary.toString(), 1.0, ResultType.NEUTRAL, ResultUnit.NONE);
    264                 }
    265 
    266                 // Display current progress
    267                 StringBuilder progress = new StringBuilder();
    268                 for (ResultKey k : mAllScenes) {
    269                     String status = RESULT_NOT_EXECUTED;
    270                     if (mExecutedScenes.containsKey(k)) {
    271                         status = mExecutedScenes.get(k) ? RESULT_PASS : RESULT_FAIL;
    272                     }
    273                     progress.append(String.format("Cam %s, %s: %s\n",
    274                             k.cameraId, k.sceneId, status));
    275                 }
    276                 TextView progressView = (TextView) findViewById(R.id.its_progress);
    277                 progressView.setMovementMethod(new ScrollingMovementMethod());
    278                 progressView.setText(progress.toString());
    279 
    280 
    281                 // Enable pass button if all scenes pass
    282                 boolean allScenesPassed = true;
    283                 for (ResultKey k : mAllScenes) {
    284                     Boolean pass = mExecutedScenes.get(k);
    285                     if (pass == null || pass == false) {
    286                         allScenesPassed = false;
    287                         break;
    288                     }
    289                 }
    290                 if (allScenesPassed) {
    291                     // Enable pass button
    292                     ItsTestActivity.this.showToast(R.string.its_test_passed);
    293                     ItsTestActivity.this.getPassButton().setEnabled(true);
    294                     ItsTestActivity.this.setTestResultAndFinish(true);
    295                 } else {
    296                     ItsTestActivity.this.getPassButton().setEnabled(false);
    297                 }
    298             }
    299         }
    300 
    301         private void appendFileContentToSummary(StringBuilder summary, String path) {
    302             BufferedReader reader = null;
    303             try {
    304                 reader = new BufferedReader(new FileReader(path));
    305                 String line = null;
    306                 do {
    307                     line = reader.readLine();
    308                     if (line != null) {
    309                         summary.append(line);
    310                     }
    311                 } while (line != null);
    312             } catch (FileNotFoundException e) {
    313                 Log.e(TAG, "Cannot find ITS summary file at " + path);
    314                 summary.append("Cannot find ITS summary file at " + path);
    315             } catch (IOException e) {
    316                 Log.e(TAG, "IO exception when trying to read " + path);
    317                 summary.append("IO exception when trying to read " + path);
    318             } finally {
    319                 if (reader != null) {
    320                     try {
    321                         reader.close();
    322                     } catch (IOException e) {
    323                     }
    324                 }
    325             }
    326         }
    327     }
    328 
    329     @Override
    330     protected void onCreate(Bundle savedInstanceState) {
    331         // Hide the test if all camera devices are legacy
    332         CameraManager manager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE);
    333         try {
    334             String[] cameraIds = manager.getCameraIdList();
    335             mNonLegacyCameraIds = new ArrayList<String>();
    336             boolean allCamerasAreLegacy = true;
    337             for (String id : cameraIds) {
    338                 CameraCharacteristics characteristics = manager.getCameraCharacteristics(id);
    339                 if (characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)
    340                         != CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
    341                     mNonLegacyCameraIds.add(id);
    342                     allCamerasAreLegacy = false;
    343                 }
    344             }
    345             if (allCamerasAreLegacy) {
    346                 showToast(R.string.all_legacy_devices);
    347                 ItsTestActivity.this.getReportLog().setSummary(
    348                         "PASS: all cameras on this device are LEGACY"
    349                         , 1.0, ResultType.NEUTRAL, ResultUnit.NONE);
    350                 setTestResultAndFinish(true);
    351             }
    352         } catch (CameraAccessException e) {
    353             Toast.makeText(ItsTestActivity.this,
    354                     "Received error from camera service while checking device capabilities: "
    355                             + e, Toast.LENGTH_SHORT).show();
    356         }
    357 
    358         super.onCreate(savedInstanceState);
    359         getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    360     }
    361 
    362     @Override
    363     public void showManualTestDialog(final DialogTestListItem test,
    364             final DialogTestListItem.TestCallback callback) {
    365         //Nothing todo for ITS
    366     }
    367 
    368     protected String testTitle(String cam, String scene) {
    369         return "Camera: " + cam + ", " + scene;
    370     }
    371 
    372     protected String testId(String cam, String scene) {
    373         return "Camera_ITS_" + cam + "_" + scene;
    374     }
    375 
    376     protected void setupItsTests(ArrayTestListAdapter adapter) {
    377         for (String cam : mNonLegacyCameraIds) {
    378             for (String scene : mSceneIds) {
    379                 adapter.add(new DialogTestListItem(this,
    380                 testTitle(cam, scene),
    381                 testId(cam, scene)));
    382             }
    383         }
    384     }
    385 
    386     @Override
    387     protected void setupTests(ArrayTestListAdapter adapter) {
    388         setupItsTests(adapter);
    389     }
    390 
    391     @Override
    392     protected void onResume() {
    393         super.onResume();
    394         CameraManager manager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE);
    395         if (manager == null) {
    396             showToast(R.string.no_camera_manager);
    397         } else {
    398             Log.d(TAG, "register ITS result receiver");
    399             IntentFilter filter = new IntentFilter(ACTION_ITS_RESULT);
    400             registerReceiver(mResultsReceiver, filter);
    401         }
    402     }
    403 
    404     @Override
    405     protected void onPause() {
    406         super.onPause();
    407         Log.d(TAG, "unregister ITS result receiver");
    408         unregisterReceiver(mResultsReceiver);
    409     }
    410 
    411     @Override
    412     public void onConfigurationChanged(Configuration newConfig) {
    413         super.onConfigurationChanged(newConfig);
    414         setContentView(R.layout.its_main);
    415         setInfoResources(R.string.camera_its_test, R.string.camera_its_test_info, -1);
    416         setPassFailButtonClickListeners();
    417     }
    418 }
    419