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> mToBeTestedCameraIds = 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             add("sensor_fusion");
     93         } };
     94 
     95     // TODO: cache the following in saved bundle
     96     private Set<ResultKey> mAllScenes = null;
     97     // (camera, scene) -> (pass, fail)
     98     private final HashMap<ResultKey, Boolean> mExecutedScenes = new HashMap<>();
     99     // map camera id to ITS summary report path
    100     private final HashMap<ResultKey, String> mSummaryMap = new HashMap<>();
    101 
    102     final class ResultKey {
    103         public final String cameraId;
    104         public final String sceneId;
    105 
    106         public ResultKey(String cameraId, String sceneId) {
    107             this.cameraId = cameraId;
    108             this.sceneId = sceneId;
    109         }
    110 
    111         @Override
    112         public boolean equals(final Object o) {
    113             if (o == null) return false;
    114             if (this == o) return true;
    115             if (o instanceof ResultKey) {
    116                 final ResultKey other = (ResultKey) o;
    117                 return cameraId.equals(other.cameraId) && sceneId.equals(other.sceneId);
    118             }
    119             return false;
    120         }
    121 
    122         @Override
    123         public int hashCode() {
    124             int h = cameraId.hashCode();
    125             h = ((h << 5) - h) ^ sceneId.hashCode();
    126             return h;
    127         }
    128     }
    129 
    130     public ItsTestActivity() {
    131         super(R.layout.its_main,
    132                 R.string.camera_its_test,
    133                 R.string.camera_its_test_info,
    134                 R.string.camera_its_test);
    135     }
    136 
    137     private final Comparator<ResultKey> mComparator = new Comparator<ResultKey>() {
    138         @Override
    139         public int compare(ResultKey k1, ResultKey k2) {
    140             if (k1.cameraId.equals(k2.cameraId))
    141                 return k1.sceneId.compareTo(k2.sceneId);
    142             return k1.cameraId.compareTo(k2.cameraId);
    143         }
    144     };
    145 
    146     class ResultReceiver extends BroadcastReceiver {
    147         @Override
    148         public void onReceive(Context context, Intent intent) {
    149             Log.i(TAG, "Received result for Camera ITS tests");
    150             if (ACTION_ITS_RESULT.equals(intent.getAction())) {
    151                 String version = intent.getStringExtra(EXTRA_VERSION);
    152                 if (version == null || !version.equals(CURRENT_VERSION)) {
    153                     Log.e(TAG, "Its result version mismatch: expect " + CURRENT_VERSION +
    154                             ", got " + ((version == null) ? "null" : version));
    155                     ItsTestActivity.this.showToast(R.string.its_version_mismatch);
    156                     return;
    157                 }
    158 
    159                 String cameraId = intent.getStringExtra(EXTRA_CAMERA_ID);
    160                 String results = intent.getStringExtra(EXTRA_RESULTS);
    161                 if (cameraId == null || results == null) {
    162                     Log.e(TAG, "cameraId = " + ((cameraId == null) ? "null" : cameraId) +
    163                             ", results = " + ((results == null) ? "null" : results));
    164                     return;
    165                 }
    166 
    167                 if (!mToBeTestedCameraIds.contains(cameraId)) {
    168                     Log.e(TAG, "Unknown camera id " + cameraId + " reported to ITS");
    169                     return;
    170                 }
    171 
    172                 try {
    173                     /* Sample JSON results string
    174                     {
    175                        "scene0":{
    176                           "result":"PASS",
    177                           "summary":"/sdcard/cam0_scene0.txt"
    178                        },
    179                        "scene1":{
    180                           "result":"NOT_EXECUTED"
    181                        },
    182                        "scene2":{
    183                           "result":"FAIL",
    184                           "summary":"/sdcard/cam0_scene2.txt"
    185                        }
    186                     }
    187                     */
    188                     JSONObject jsonResults = new JSONObject(results);
    189                     Set<String> scenes = new HashSet<>();
    190                     Iterator<String> keys = jsonResults.keys();
    191                     while (keys.hasNext()) {
    192                         scenes.add(keys.next());
    193                     }
    194                     boolean newScenes = false;
    195                     if (mAllScenes == null) {
    196                         mAllScenes = new TreeSet<>(mComparator);
    197                         newScenes = true;
    198                     } else { // See if scene lists changed
    199                         for (String scene : scenes) {
    200                             if (!mAllScenes.contains(new ResultKey(cameraId, scene))) {
    201                                 // Scene list changed. Cleanup previous test results
    202                                 newScenes = true;
    203                                 break;
    204                             }
    205                         }
    206                         for (ResultKey k : mAllScenes) {
    207                             if (!scenes.contains(k.sceneId)) {
    208                                 newScenes = true;
    209                                 break;
    210                             }
    211                         }
    212                     }
    213                     if (newScenes) {
    214                         mExecutedScenes.clear();
    215                         mAllScenes.clear();
    216                         for (String scene : scenes) {
    217                             for (String c : mToBeTestedCameraIds) {
    218                                 mAllScenes.add(new ResultKey(c, scene));
    219                             }
    220                         }
    221                     }
    222 
    223                     // Update test execution results
    224                     for (String scene : scenes) {
    225                         JSONObject sceneResult = jsonResults.getJSONObject(scene);
    226                         String result = sceneResult.getString("result");
    227                         if (result == null) {
    228                             Log.e(TAG, "Result for " + scene + " is null");
    229                             return;
    230                         }
    231                         Log.i(TAG, "ITS camera" + cameraId + " " + scene + ": result:" + result);
    232                         if (!RESULT_VALUES.contains(result)) {
    233                             Log.e(TAG, "Unknown result for " + scene + ": " + result);
    234                             return;
    235                         }
    236                         ResultKey key = new ResultKey(cameraId, scene);
    237                         if (result.equals(RESULT_PASS) || result.equals(RESULT_FAIL)) {
    238                             boolean pass = result.equals(RESULT_PASS);
    239                             mExecutedScenes.put(key, pass);
    240                             setTestResult(testId(cameraId, scene), pass ?
    241                                     TestResult.TEST_RESULT_PASSED : TestResult.TEST_RESULT_FAILED);
    242                             Log.e(TAG, "setTestResult for " + testId(cameraId, scene) + ": " + result);
    243                             String summary = sceneResult.optString("summary");
    244                             if (!summary.equals("")) {
    245                                 mSummaryMap.put(key, summary);
    246                             }
    247                         } // do nothing for NOT_EXECUTED scenes
    248                     }
    249                 } catch (org.json.JSONException e) {
    250                     Log.e(TAG, "Error reading json result string:" + results , e);
    251                     return;
    252                 }
    253 
    254                 // Set summary if all scenes reported
    255                 if (mSummaryMap.keySet().containsAll(mAllScenes)) {
    256                     StringBuilder summary = new StringBuilder();
    257                     for (String path : mSummaryMap.values()) {
    258                         appendFileContentToSummary(summary, path);
    259                     }
    260                     if (summary.length() > MAX_SUMMARY_LEN) {
    261                         Log.w(TAG, "ITS summary report too long: len: " + summary.length());
    262                     }
    263                     ItsTestActivity.this.getReportLog().setSummary(
    264                             summary.toString(), 1.0, ResultType.NEUTRAL, ResultUnit.NONE);
    265                 }
    266 
    267                 // Display current progress
    268                 StringBuilder progress = new StringBuilder();
    269                 for (ResultKey k : mAllScenes) {
    270                     String status = RESULT_NOT_EXECUTED;
    271                     if (mExecutedScenes.containsKey(k)) {
    272                         status = mExecutedScenes.get(k) ? RESULT_PASS : RESULT_FAIL;
    273                     }
    274                     progress.append(String.format("Cam %s, %s: %s\n",
    275                             k.cameraId, k.sceneId, status));
    276                 }
    277                 TextView progressView = (TextView) findViewById(R.id.its_progress);
    278                 progressView.setMovementMethod(new ScrollingMovementMethod());
    279                 progressView.setText(progress.toString());
    280 
    281 
    282                 // Enable pass button if all scenes pass
    283                 boolean allScenesPassed = true;
    284                 for (ResultKey k : mAllScenes) {
    285                     Boolean pass = mExecutedScenes.get(k);
    286                     if (pass == null || pass == false) {
    287                         allScenesPassed = false;
    288                         break;
    289                     }
    290                 }
    291                 if (allScenesPassed) {
    292                     // Enable pass button
    293                     ItsTestActivity.this.showToast(R.string.its_test_passed);
    294                     ItsTestActivity.this.getPassButton().setEnabled(true);
    295                     ItsTestActivity.this.setTestResultAndFinish(true);
    296                 } else {
    297                     ItsTestActivity.this.getPassButton().setEnabled(false);
    298                 }
    299             }
    300         }
    301 
    302         private void appendFileContentToSummary(StringBuilder summary, String path) {
    303             BufferedReader reader = null;
    304             try {
    305                 reader = new BufferedReader(new FileReader(path));
    306                 String line = null;
    307                 do {
    308                     line = reader.readLine();
    309                     if (line != null) {
    310                         summary.append(line);
    311                     }
    312                 } while (line != null);
    313             } catch (FileNotFoundException e) {
    314                 Log.e(TAG, "Cannot find ITS summary file at " + path);
    315                 summary.append("Cannot find ITS summary file at " + path);
    316             } catch (IOException e) {
    317                 Log.e(TAG, "IO exception when trying to read " + path);
    318                 summary.append("IO exception when trying to read " + path);
    319             } finally {
    320                 if (reader != null) {
    321                     try {
    322                         reader.close();
    323                     } catch (IOException e) {
    324                     }
    325                 }
    326             }
    327         }
    328     }
    329 
    330     @Override
    331     protected void onCreate(Bundle savedInstanceState) {
    332         // Hide the test if all camera devices are legacy
    333         CameraManager manager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE);
    334         try {
    335             String[] cameraIds = manager.getCameraIdList();
    336             mToBeTestedCameraIds = new ArrayList<String>();
    337             for (String id : cameraIds) {
    338                 CameraCharacteristics characteristics = manager.getCameraCharacteristics(id);
    339                 int hwLevel = characteristics.get(
    340                         CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
    341                 if (hwLevel
    342                         != CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY &&
    343                         hwLevel
    344                         != CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL) {
    345                     mToBeTestedCameraIds.add(id);
    346                 }
    347             }
    348             if (mToBeTestedCameraIds.size() == 0) {
    349                 showToast(R.string.all_legacy_devices);
    350                 ItsTestActivity.this.getReportLog().setSummary(
    351                         "PASS: all cameras on this device are LEGACY or EXTERNAL"
    352                         , 1.0, ResultType.NEUTRAL, ResultUnit.NONE);
    353                 setTestResultAndFinish(true);
    354             }
    355         } catch (CameraAccessException e) {
    356             Toast.makeText(ItsTestActivity.this,
    357                     "Received error from camera service while checking device capabilities: "
    358                             + e, Toast.LENGTH_SHORT).show();
    359         }
    360 
    361         super.onCreate(savedInstanceState);
    362         getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    363     }
    364 
    365     @Override
    366     public void showManualTestDialog(final DialogTestListItem test,
    367             final DialogTestListItem.TestCallback callback) {
    368         //Nothing todo for ITS
    369     }
    370 
    371     protected String testTitle(String cam, String scene) {
    372         return "Camera: " + cam + ", " + scene;
    373     }
    374 
    375     protected String testId(String cam, String scene) {
    376         return "Camera_ITS_" + cam + "_" + scene;
    377     }
    378 
    379     protected void setupItsTests(ArrayTestListAdapter adapter) {
    380         for (String cam : mToBeTestedCameraIds) {
    381             for (String scene : mSceneIds) {
    382                 adapter.add(new DialogTestListItem(this,
    383                 testTitle(cam, scene),
    384                 testId(cam, scene)));
    385             }
    386         }
    387     }
    388 
    389     @Override
    390     protected void setupTests(ArrayTestListAdapter adapter) {
    391         setupItsTests(adapter);
    392     }
    393 
    394     @Override
    395     protected void onResume() {
    396         super.onResume();
    397         CameraManager manager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE);
    398         if (manager == null) {
    399             showToast(R.string.no_camera_manager);
    400         } else {
    401             Log.d(TAG, "register ITS result receiver");
    402             IntentFilter filter = new IntentFilter(ACTION_ITS_RESULT);
    403             registerReceiver(mResultsReceiver, filter);
    404         }
    405     }
    406 
    407     @Override
    408     public void onDestroy() {
    409         Log.d(TAG, "unregister ITS result receiver");
    410         unregisterReceiver(mResultsReceiver);
    411         super.onDestroy();
    412     }
    413 
    414     @Override
    415     public void onConfigurationChanged(Configuration newConfig) {
    416         super.onConfigurationChanged(newConfig);
    417         setContentView(R.layout.its_main);
    418         setInfoResources(R.string.camera_its_test, R.string.camera_its_test_info, -1);
    419         setPassFailButtonClickListeners();
    420     }
    421 }
    422