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.content.res.Resources; 25 import android.hardware.camera2.CameraManager; 26 import android.hardware.cts.helpers.CameraUtils; 27 import android.hardware.devicestate.DeviceStateManager; 28 import android.mediapc.cts.common.PerformanceClassEvaluator; 29 import android.os.Bundle; 30 import android.os.Handler; 31 import android.os.HandlerThread; 32 import android.text.method.ScrollingMovementMethod; 33 import android.util.Log; 34 import android.util.Pair; 35 import android.view.WindowManager; 36 import android.widget.TextView; 37 import android.widget.Toast; 38 39 import com.android.compatibility.common.util.ResultType; 40 import com.android.compatibility.common.util.ResultUnit; 41 import com.android.cts.verifier.ArrayTestListAdapter; 42 import com.android.cts.verifier.DialogTestListActivity; 43 import com.android.cts.verifier.R; 44 import com.android.cts.verifier.TestResult; 45 import com.android.internal.util.ArrayUtils; 46 47 import org.json.JSONArray; 48 import org.json.JSONObject; 49 import org.junit.rules.TestName; 50 51 import java.io.BufferedReader; 52 import java.io.FileNotFoundException; 53 import java.io.FileReader; 54 import java.io.IOException; 55 import java.util.ArrayList; 56 import java.util.Arrays; 57 import java.util.Comparator; 58 import java.util.HashMap; 59 import java.util.HashSet; 60 import java.util.Iterator; 61 import java.util.List; 62 import java.util.Locale; 63 import java.util.Set; 64 import java.util.TreeSet; 65 import java.util.concurrent.Executor; 66 import java.util.concurrent.LinkedBlockingQueue; 67 import java.util.regex.Matcher; 68 import java.util.regex.Pattern; 69 70 /** 71 * Test for Camera features that require that the camera be aimed at a specific test scene. 72 * This test activity requires a USB connection to a computer, and a corresponding host-side run of 73 * the python scripts found in the CameraITS directory. 74 */ 75 public class ItsTestActivity extends DialogTestListActivity { 76 private static final String TAG = "ItsTestActivity"; 77 private static final String EXTRA_CAMERA_ID = "camera.its.extra.CAMERA_ID"; 78 private static final String EXTRA_RESULTS = "camera.its.extra.RESULTS"; 79 private static final String EXTRA_VERSION = "camera.its.extra.VERSION"; 80 private static final String CURRENT_VERSION = "1.0"; 81 private static final String ACTION_ITS_RESULT = 82 "com.android.cts.verifier.camera.its.ACTION_ITS_RESULT"; 83 84 private static final String RESULT_PASS = "PASS"; 85 private static final String RESULT_FAIL = "FAIL"; 86 private static final String RESULT_NOT_EXECUTED = "NOT_EXECUTED"; 87 private static final Set<String> RESULT_VALUES = new HashSet<String>( 88 Arrays.asList(new String[] {RESULT_PASS, RESULT_FAIL, RESULT_NOT_EXECUTED})); 89 private static final int MAX_SUMMARY_LEN = 200; 90 91 private static final Pattern MPC12_CAMERA_LAUNCH_PATTERN = 92 Pattern.compile("camera_launch_time_ms:(\\d+(\\.\\d+)?)"); 93 private static final Pattern MPC12_JPEG_CAPTURE_PATTERN = 94 Pattern.compile("1080p_jpeg_capture_time_ms:(\\d+(\\.\\d+)?)"); 95 private static final int AVAILABILITY_TIMEOUT_MS = 10; 96 97 private final ResultReceiver mResultsReceiver = new ResultReceiver(); 98 private boolean mReceiverRegistered = false; 99 100 public final TestName mTestName = new TestName(); 101 private boolean mIsFoldableDevice = false; 102 private boolean mIsDeviceFolded = false; 103 private boolean mFoldedTestSetupDone = false; 104 private boolean mUnfoldedTestSetupDone = false; 105 private Set<Pair<String, String>> mUnavailablePhysicalCameras = 106 new HashSet<Pair<String, String>>(); 107 private CameraManager mCameraManager = null; 108 private DeviceStateManager mDeviceStateManager = null; 109 private HandlerThread mCameraThread = null; 110 private Handler mCameraHandler = null; 111 112 // Initialized in onCreate 113 List<String> mToBeTestedCameraIds = null; 114 private String mPrimaryRearCameraId = null; 115 private String mPrimaryFrontCameraId = null; 116 private List<String> mToBeTestedCameraIdsUnfolded = null; 117 private List<String> mToBeTestedCameraIdsFolded = null; 118 private String mPrimaryRearCameraIdUnfolded = null; 119 private String mPrimaryFrontCameraIdUnfolded = null; 120 private ArrayTestListAdapter mAdapter; 121 122 // Scenes 123 private static final List<String> mSceneIds = List.of( 124 "scene0", 125 "scene1_1", 126 "scene1_2", 127 "scene2_a", 128 "scene2_b", 129 "scene2_c", 130 "scene2_d", 131 "scene2_e", 132 "scene2_f", 133 "scene3", 134 "scene4", 135 "scene5", 136 "scene6", 137 "scene_extensions/scene_hdr", 138 "scene_extensions/scene_night", 139 "sensor_fusion"); 140 141 // This must match scenes of SUB_CAMERA_TESTS in tools/run_all_tests.py 142 private static final List<String> mHiddenPhysicalCameraSceneIds = List.of( 143 "scene0", 144 "scene1_1", 145 "scene1_2", 146 "scene2_a", 147 "scene4", 148 "sensor_fusion"); 149 150 // TODO: cache the following in saved bundle 151 private Set<ResultKey> mAllScenes = null; 152 // (camera, scene) -> (pass, fail) 153 private final HashMap<ResultKey, Boolean> mExecutedScenes = new HashMap<>(); 154 // map camera id to ITS summary report path 155 private final HashMap<ResultKey, String> mSummaryMap = new HashMap<>(); 156 // All primary cameras for which MPC level test has run 157 private Set<ResultKey> mExecutedMpcTests = null; 158 private static final String MPC_LAUNCH_REQ_NUM = "2.2.7.2/7.5/H-1-6"; 159 private static final String MPC_JPEG_CAPTURE_REQ_NUM = "2.2.7.2/7.5/H-1-5"; 160 // Performance class evaluator used for writing test result 161 PerformanceClassEvaluator mPce = new PerformanceClassEvaluator(mTestName); 162 PerformanceClassEvaluator.CameraLatencyRequirement mJpegLatencyReq = 163 mPce.addR7_5__H_1_5(); 164 PerformanceClassEvaluator.CameraLatencyRequirement mLaunchLatencyReq = 165 mPce.addR7_5__H_1_6(); 166 167 private static class HandlerExecutor implements Executor { 168 private final Handler mHandler; 169 HandlerExecutor(Handler handler)170 HandlerExecutor(Handler handler) { 171 mHandler = handler; 172 } 173 174 @Override execute(Runnable runCmd)175 public void execute(Runnable runCmd) { 176 mHandler.post(runCmd); 177 } 178 } 179 180 final class ResultKey { 181 public final String cameraId; 182 public final String sceneId; 183 ResultKey(String cameraId, String sceneId)184 public ResultKey(String cameraId, String sceneId) { 185 this.cameraId = cameraId; 186 this.sceneId = sceneId; 187 } 188 189 @Override equals(final Object o)190 public boolean equals(final Object o) { 191 if (o == null) return false; 192 if (this == o) return true; 193 if (o instanceof ResultKey) { 194 final ResultKey other = (ResultKey) o; 195 return cameraId.equals(other.cameraId) && sceneId.equals(other.sceneId); 196 } 197 return false; 198 } 199 200 @Override hashCode()201 public int hashCode() { 202 int h = cameraId.hashCode(); 203 h = ((h << 5) - h) ^ sceneId.hashCode(); 204 return h; 205 } 206 } 207 ItsTestActivity()208 public ItsTestActivity() { 209 super(R.layout.its_main, 210 R.string.camera_its_test, 211 R.string.camera_its_test_info, 212 R.string.camera_its_test); 213 } 214 215 private final Comparator<ResultKey> mComparator = new Comparator<ResultKey>() { 216 @Override 217 public int compare(ResultKey k1, ResultKey k2) { 218 if (k1.cameraId.equals(k2.cameraId)) 219 return k1.sceneId.compareTo(k2.sceneId); 220 return k1.cameraId.compareTo(k2.cameraId); 221 } 222 }; 223 224 class ResultReceiver extends BroadcastReceiver { 225 @Override onReceive(Context context, Intent intent)226 public void onReceive(Context context, Intent intent) { 227 Log.i(TAG, "Received result for Camera ITS tests"); 228 if (ACTION_ITS_RESULT.equals(intent.getAction())) { 229 String version = intent.getStringExtra(EXTRA_VERSION); 230 if (version == null || !version.equals(CURRENT_VERSION)) { 231 Log.e(TAG, "Its result version mismatch: expect " + CURRENT_VERSION + 232 ", got " + ((version == null) ? "null" : version)); 233 ItsTestActivity.this.showToast(R.string.its_version_mismatch); 234 return; 235 } 236 237 String cameraId = intent.getStringExtra(EXTRA_CAMERA_ID); 238 String results = intent.getStringExtra(EXTRA_RESULTS); 239 if (cameraId == null || results == null) { 240 Log.e(TAG, "cameraId = " + ((cameraId == null) ? "null" : cameraId) + 241 ", results = " + ((results == null) ? "null" : results)); 242 return; 243 } 244 245 if (mIsFoldableDevice) { 246 if (!mIsDeviceFolded) { 247 if (!mToBeTestedCameraIdsUnfolded.contains(cameraId)) { 248 Log.e(TAG, "Unknown camera id " + cameraId + " reported to ITS"); 249 return; 250 } 251 } else { 252 if (!mToBeTestedCameraIdsFolded.contains(cameraId)) { 253 Log.e(TAG, "Unknown camera id " + cameraId + " reported to ITS"); 254 return; 255 } 256 } 257 } else { 258 if (!mToBeTestedCameraIds.contains(cameraId)) { 259 Log.e(TAG, "Unknown camera id " + cameraId + " reported to ITS"); 260 return; 261 } 262 } 263 264 try { 265 /* Sample JSON results string 266 { 267 "scene0":{ 268 "result":"PASS", 269 "summary":"/sdcard/cam0_scene0.txt" 270 }, 271 "scene1":{ 272 "result":"NOT_EXECUTED" 273 }, 274 "scene2":{ 275 "result":"FAIL", 276 "summary":"/sdcard/cam0_scene2.txt" 277 } 278 } 279 */ 280 JSONObject jsonResults = new JSONObject(results); 281 Log.d(TAG,"Results received:" + jsonResults.toString()); 282 Set<String> scenes = new HashSet<>(); 283 Iterator<String> keys = jsonResults.keys(); 284 while (keys.hasNext()) { 285 scenes.add(keys.next()); 286 } 287 288 // Update test execution results 289 for (String scene : scenes) { 290 JSONObject sceneResult = jsonResults.getJSONObject(scene); 291 Log.v(TAG, sceneResult.toString()); 292 String result = sceneResult.getString("result"); 293 if (result == null) { 294 Log.e(TAG, "Result for " + scene + " is null"); 295 return; 296 } 297 Log.i(TAG, "ITS camera" + cameraId + " " + scene + ": result:" + result); 298 if (!RESULT_VALUES.contains(result)) { 299 Log.e(TAG, "Unknown result for " + scene + ": " + result); 300 return; 301 } 302 ResultKey key = new ResultKey(cameraId, scene); 303 if (result.equals(RESULT_PASS) || result.equals(RESULT_FAIL)) { 304 boolean pass = result.equals(RESULT_PASS); 305 mExecutedScenes.put(key, pass); 306 // Get start/end time per camera/scene for result history collection. 307 mStartTime = sceneResult.getLong("start"); 308 mEndTime = sceneResult.getLong("end"); 309 setTestResult(testId(cameraId, scene), pass ? 310 TestResult.TEST_RESULT_PASSED : TestResult.TEST_RESULT_FAILED); 311 Log.e(TAG, "setTestResult for " + testId(cameraId, scene) + ": " + result); 312 String summary = sceneResult.optString("summary"); 313 if (!summary.equals("")) { 314 mSummaryMap.put(key, summary); 315 } 316 } // do nothing for NOT_EXECUTED scenes 317 318 if (sceneResult.isNull("mpc_metrics")) { 319 continue; 320 } 321 // Update MPC level 322 JSONArray metrics = sceneResult.getJSONArray("mpc_metrics"); 323 for (int i = 0; i < metrics.length(); i++) { 324 String mpcResult = metrics.getString(i); 325 if (!matchMpcResult(cameraId, mpcResult)) { 326 Log.e(TAG, "Error parsing MPC result string:" + mpcResult); 327 return; 328 } 329 } 330 } 331 } catch (org.json.JSONException e) { 332 Log.e(TAG, "Error reading json result string:" + results , e); 333 return; 334 } 335 336 // Set summary if all scenes reported 337 if (mSummaryMap.keySet().containsAll(mAllScenes)) { 338 // Save test summary 339 StringBuilder summary = new StringBuilder(); 340 for (String path : mSummaryMap.values()) { 341 appendFileContentToSummary(summary, path); 342 } 343 if (summary.length() > MAX_SUMMARY_LEN) { 344 Log.w(TAG, "ITS summary report too long: len: " + summary.length()); 345 } 346 ItsTestActivity.this.getReportLog().setSummary( 347 summary.toString(), 1.0, ResultType.NEUTRAL, ResultUnit.NONE); 348 } 349 350 // Display current progress 351 StringBuilder progress = new StringBuilder(); 352 for (ResultKey k : mAllScenes) { 353 String status = RESULT_NOT_EXECUTED; 354 if (mExecutedScenes.containsKey(k)) { 355 status = mExecutedScenes.get(k) ? RESULT_PASS : RESULT_FAIL; 356 } 357 progress.append(String.format("Cam %s, %s: %s\n", 358 k.cameraId, k.sceneId, status)); 359 } 360 TextView progressView = (TextView) findViewById(R.id.its_progress); 361 progressView.setMovementMethod(new ScrollingMovementMethod()); 362 progressView.setText(progress.toString()); 363 364 365 // Enable pass button if all scenes pass 366 boolean allScenesPassed = true; 367 for (ResultKey k : mAllScenes) { 368 Boolean pass = mExecutedScenes.get(k); 369 if (pass == null || pass == false) { 370 allScenesPassed = false; 371 break; 372 } 373 } 374 if (allScenesPassed) { 375 Log.i(TAG, "All scenes passed."); 376 // Enable pass button 377 ItsTestActivity.this.getPassButton().setEnabled(true); 378 ItsTestActivity.this.setTestResultAndFinish(true); 379 } else { 380 ItsTestActivity.this.getPassButton().setEnabled(false); 381 } 382 } 383 } 384 appendFileContentToSummary(StringBuilder summary, String path)385 private void appendFileContentToSummary(StringBuilder summary, String path) { 386 BufferedReader reader = null; 387 try { 388 reader = new BufferedReader(new FileReader(path)); 389 String line = null; 390 do { 391 line = reader.readLine(); 392 if (line != null) { 393 summary.append(line); 394 } 395 } while (line != null); 396 } catch (FileNotFoundException e) { 397 Log.e(TAG, "Cannot find ITS summary file at " + path); 398 summary.append("Cannot find ITS summary file at " + path); 399 } catch (IOException e) { 400 Log.e(TAG, "IO exception when trying to read " + path); 401 summary.append("IO exception when trying to read " + path); 402 } finally { 403 if (reader != null) { 404 try { 405 reader.close(); 406 } catch (IOException e) { 407 } 408 } 409 } 410 } 411 matchMpcResult(String cameraId, String mpcResult)412 private boolean matchMpcResult(String cameraId, String mpcResult) { 413 Matcher launchMatcher = MPC12_CAMERA_LAUNCH_PATTERN.matcher(mpcResult); 414 boolean launchMatches = launchMatcher.matches(); 415 416 Matcher jpegMatcher = MPC12_JPEG_CAPTURE_PATTERN.matcher(mpcResult); 417 boolean jpegMatches = jpegMatcher.matches(); 418 419 if (!launchMatches && !jpegMatches) { 420 return false; 421 } 422 if (!cameraId.equals(mPrimaryRearCameraId) && 423 !cameraId.equals(mPrimaryFrontCameraId)) { 424 return false; 425 } 426 427 if (launchMatches) { 428 float latency = Float.parseFloat(launchMatcher.group(1)); 429 if (cameraId.equals(mPrimaryRearCameraId)) { 430 mLaunchLatencyReq.setRearCameraLatency(latency); 431 } else { 432 mLaunchLatencyReq.setFrontCameraLatency(latency); 433 } 434 mExecutedMpcTests.add(new ResultKey(cameraId, MPC_LAUNCH_REQ_NUM)); 435 } else { 436 float latency = Float.parseFloat(jpegMatcher.group(1)); 437 if (cameraId.equals(mPrimaryRearCameraId)) { 438 mJpegLatencyReq.setRearCameraLatency(latency); 439 } else { 440 mJpegLatencyReq.setFrontCameraLatency(latency); 441 } 442 mExecutedMpcTests.add(new ResultKey(cameraId, MPC_JPEG_CAPTURE_REQ_NUM)); 443 } 444 445 // Save MPC info once both front primary and rear primary data are collected. 446 if (mExecutedMpcTests.size() == 4) { 447 mPce.submitAndVerify(); 448 } 449 return true; 450 } 451 } 452 453 private class FoldStateListener implements 454 DeviceStateManager.DeviceStateCallback { 455 private int[] mFoldedDeviceStates; 456 private boolean mFirstFoldCheck = false; 457 FoldStateListener(Context context)458 FoldStateListener(Context context) { 459 Resources systemRes = Resources.getSystem(); 460 int foldedStatesArrayIdentifier = systemRes.getIdentifier("config_foldedDeviceStates", 461 "array", "android"); 462 mFoldedDeviceStates = systemRes.getIntArray(foldedStatesArrayIdentifier); 463 } 464 465 @Override onStateChanged(int state)466 public final void onStateChanged(int state) { 467 boolean folded = ArrayUtils.contains(mFoldedDeviceStates, state); 468 Log.i(TAG, "Is device folded? " + mIsDeviceFolded); 469 if (!mFirstFoldCheck || mIsDeviceFolded != folded) { 470 mIsDeviceFolded = folded; 471 mFirstFoldCheck = true; 472 if (mFoldedTestSetupDone && mUnfoldedTestSetupDone) { 473 Log.i(TAG, "Setup is done for both the states."); 474 } else { 475 runOnUiThread(new Runnable() { 476 @Override 477 public void run() { 478 Log.i(TAG, "set up from onStateChanged"); 479 getCameraIdsForFoldableDevice(); 480 setupItsTestsForFoldableDevice(mAdapter); 481 } 482 }); 483 } 484 } else { 485 Log.i(TAG, "Last state is same as new state."); 486 } 487 } 488 } 489 490 @Override onCreate(Bundle savedInstanceState)491 protected void onCreate(Bundle savedInstanceState) { 492 // Hide the test if all camera devices are legacy 493 mCameraManager = this.getSystemService(CameraManager.class); 494 Context context = this.getApplicationContext(); 495 if (mAllScenes == null) { 496 mAllScenes = new TreeSet<>(mComparator); 497 } 498 if (mExecutedMpcTests == null) { 499 mExecutedMpcTests = new TreeSet<>(mComparator); 500 } 501 mCameraThread = new HandlerThread("ItsTestActivityThread"); 502 mCameraThread.start(); 503 mCameraHandler = new Handler(mCameraThread.getLooper()); 504 HandlerExecutor handlerExecutor = new HandlerExecutor(mCameraHandler); 505 // mIsFoldableDevice is set True for foldables to listen to callback 506 // in FoldStateListener 507 mIsFoldableDevice = isFoldableDevice(); 508 Log.i(TAG, "Is device foldable? " + mIsFoldableDevice); 509 if (mIsFoldableDevice) { 510 FoldStateListener foldStateListener = new FoldStateListener(context); 511 mDeviceStateManager = context.getSystemService(DeviceStateManager.class); 512 // onStateChanged will be called upon registration which helps determine 513 // if the foldable device has changed the folded/unfolded state or not. 514 mDeviceStateManager.registerCallback(handlerExecutor, foldStateListener); 515 } 516 if (!mIsFoldableDevice) { 517 try { 518 ItsUtils.ItsCameraIdList cameraIdList = 519 ItsUtils.getItsCompatibleCameraIds(mCameraManager); 520 mToBeTestedCameraIds = cameraIdList.mCameraIdCombos; 521 mPrimaryRearCameraId = cameraIdList.mPrimaryRearCameraId; 522 mPrimaryFrontCameraId = cameraIdList.mPrimaryFrontCameraId; 523 } catch (ItsException e) { 524 Toast.makeText(ItsTestActivity.this, 525 "Received error from camera service while checking device capabilities: " 526 + e, Toast.LENGTH_SHORT).show(); 527 } 528 } 529 530 super.onCreate(savedInstanceState); 531 532 if (!mIsFoldableDevice) { 533 if (mToBeTestedCameraIds.size() == 0) { 534 showToast(R.string.all_exempted_devices); 535 ItsTestActivity.this.getReportLog().setSummary( 536 "PASS: all cameras on this device are exempted from ITS", 537 1.0, ResultType.NEUTRAL, ResultUnit.NONE); 538 setTestResultAndFinish(true); 539 } 540 } 541 // Default locale must be set to "en-us" 542 Locale locale = Locale.getDefault(); 543 if (!Locale.US.equals(locale)) { 544 String toastMessage = "Unsupported default language " + locale + "! " 545 + "Please switch the default language to English (United States) in " 546 + "Settings > Language & input > Languages"; 547 Toast.makeText(ItsTestActivity.this, toastMessage, Toast.LENGTH_LONG).show(); 548 ItsTestActivity.this.getReportLog().setSummary( 549 "FAIL: Default language is not set to " + Locale.US, 550 1.0, ResultType.NEUTRAL, ResultUnit.NONE); 551 setTestResultAndFinish(false); 552 } 553 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 554 } 555 getCameraIdsAvailableForTesting()556 private List<String> getCameraIdsAvailableForTesting() { 557 List<String> toBeTestedCameraIds = new ArrayList<String>(); 558 List<String> availableCameraIdList = new ArrayList<String>(); 559 try { 560 ItsUtils.ItsCameraIdList cameraIdList = 561 ItsUtils.getItsCompatibleCameraIds(mCameraManager); 562 toBeTestedCameraIds = cameraIdList.mCameraIdCombos; 563 mPrimaryRearCameraId = cameraIdList.mPrimaryRearCameraId; 564 mPrimaryFrontCameraId = cameraIdList.mPrimaryFrontCameraId; 565 mUnavailablePhysicalCameras = getUnavailablePhysicalCameras(); 566 Log.i(TAG, "unavailablePhysicalCameras:" 567 + mUnavailablePhysicalCameras.toString()); 568 for (String str : toBeTestedCameraIds) { 569 if (str.contains(".")) { 570 String[] strArr = str.split("\\."); 571 if (mUnavailablePhysicalCameras.contains(new Pair<>(strArr[0], strArr[1]))) { 572 toBeTestedCameraIds.remove(str); 573 } 574 } 575 } 576 Log.i(TAG, "AvailablePhysicalCameras to be tested:" 577 + Arrays.asList(toBeTestedCameraIds.toString())); 578 } catch (ItsException e) { 579 Log.i(TAG, "Received error from camera service while checking device capabilities: " 580 + e); 581 } catch (Exception e) { 582 Log.i(TAG, "Exception: " + e); 583 } 584 585 return toBeTestedCameraIds; 586 } 587 588 // Get camera ids available for testing for device in 589 // each state: folded and unfolded. getCameraIdsForFoldableDevice()590 protected void getCameraIdsForFoldableDevice() { 591 boolean deviceFolded = mIsDeviceFolded; 592 try { 593 if (mIsDeviceFolded) { 594 mToBeTestedCameraIdsFolded = getCameraIdsAvailableForTesting(); 595 } else { 596 mToBeTestedCameraIdsUnfolded = getCameraIdsAvailableForTesting(); 597 } 598 } catch (Exception e) { 599 Log.i(TAG, "Exception: " + e); 600 } 601 } 602 603 @Override showManualTestDialog(final DialogTestListItem test, final DialogTestListItem.TestCallback callback)604 public void showManualTestDialog(final DialogTestListItem test, 605 final DialogTestListItem.TestCallback callback) { 606 //Nothing todo for ITS 607 } 608 testTitle(String cam, String scene)609 protected String testTitle(String cam, String scene) { 610 return "Camera: " + cam + ", " + scene; 611 } 612 613 // CtsVerifier has a "Folded" toggle that selectively surfaces some tests. 614 // To separate the tests in folded and unfolded states, CtsVerifier adds a [folded] 615 // suffix to the test id in its internal database depending on the state of the "Folded" 616 // toggle button. However, CameraITS has tests that it needs to persist across both folded 617 // and unfolded states.To get the test results to persist, we need CtsVerifier to store and 618 // look up the same test id regardless of the toggle button state. 619 // TODO(b/282804139): Update CTS tests to allow activities to write tests that persist 620 // across the states testId(String cam, String scene)621 protected String testId(String cam, String scene) { 622 return "Camera_ITS_" + cam + "_" + scene + "[folded]"; 623 } 624 isFoldableDevice()625 protected boolean isFoldableDevice() { 626 Context context = this.getApplicationContext(); 627 return CameraUtils.isDeviceFoldable(context); 628 } 629 isDeviceFolded()630 protected boolean isDeviceFolded() { 631 return mIsDeviceFolded; 632 } 633 getUnavailablePhysicalCameras()634 protected Set<Pair<String, String>> getUnavailablePhysicalCameras() throws ItsException { 635 final LinkedBlockingQueue<Pair<String, String>> unavailablePhysicalCamEventQueue = 636 new LinkedBlockingQueue<>(); 637 mCameraThread = new HandlerThread("ItsCameraThread"); 638 mCameraThread.start(); 639 mCameraHandler = new Handler(mCameraThread.getLooper()); 640 try { 641 CameraManager.AvailabilityCallback ac = new CameraManager.AvailabilityCallback() { 642 @Override 643 public void onPhysicalCameraUnavailable(String cameraId, String physicalCameraId) { 644 unavailablePhysicalCamEventQueue.offer(new Pair<>(cameraId, physicalCameraId)); 645 } 646 }; 647 mCameraManager.registerAvailabilityCallback(ac, mCameraHandler); 648 Set<Pair<String, String>> unavailablePhysicalCameras = 649 new HashSet<Pair<String, String>>(); 650 Pair<String, String> candidatePhysicalIds = 651 unavailablePhysicalCamEventQueue.poll(AVAILABILITY_TIMEOUT_MS, 652 java.util.concurrent.TimeUnit.MILLISECONDS); 653 while (candidatePhysicalIds != null) { 654 unavailablePhysicalCameras.add(candidatePhysicalIds); 655 candidatePhysicalIds = 656 unavailablePhysicalCamEventQueue.poll(AVAILABILITY_TIMEOUT_MS, 657 java.util.concurrent.TimeUnit.MILLISECONDS); 658 } 659 mCameraManager.unregisterAvailabilityCallback(ac); 660 return unavailablePhysicalCameras; 661 } catch (Exception e) { 662 throw new ItsException("Exception: ", e); 663 } 664 } 665 setupItsTests(ArrayTestListAdapter adapter)666 protected void setupItsTests(ArrayTestListAdapter adapter) { 667 for (String cam : mToBeTestedCameraIds) { 668 List<String> scenes = cam.contains(ItsUtils.CAMERA_ID_TOKENIZER) 669 ? mHiddenPhysicalCameraSceneIds : mSceneIds; 670 for (String scene : scenes) { 671 // Add camera and scene combinations in mAllScenes to avoid adding n/a scenes for 672 // devices with sub-cameras. 673 mAllScenes.add(new ResultKey(cam, scene)); 674 adapter.add(new DialogTestListItem(this, 675 testTitle(cam, scene), 676 testId(cam, scene))); 677 } 678 if (mExecutedMpcTests == null) { 679 mExecutedMpcTests = new TreeSet<>(mComparator); 680 } 681 Log.d(TAG, "Total combinations to test on this device:" + mAllScenes.size()); 682 } 683 } 684 setupItsTestsForFoldableDevice(ArrayTestListAdapter adapter)685 protected void setupItsTestsForFoldableDevice(ArrayTestListAdapter adapter) { 686 List<String> toBeTestedCameraIds = new ArrayList<String>(); 687 if (mIsDeviceFolded) { 688 toBeTestedCameraIds = mToBeTestedCameraIdsFolded; 689 } else { 690 toBeTestedCameraIds = mToBeTestedCameraIdsUnfolded; 691 } 692 693 for (String cam : toBeTestedCameraIds) { 694 List<String> scenes = cam.contains(ItsUtils.CAMERA_ID_TOKENIZER) 695 ? mHiddenPhysicalCameraSceneIds : mSceneIds; 696 for (String scene : scenes) { 697 // Add camera and scene combinations in mAllScenes to avoid adding n/a scenes for 698 // devices with sub-cameras. 699 if (cam.contains(mPrimaryFrontCameraId) && mIsDeviceFolded) { 700 scene = scene + "_folded"; 701 } 702 // Rear camera scenes will be added only once. 703 if (mAllScenes.contains(new ResultKey(cam, scene))) { 704 continue; 705 } 706 // TODO(ruchamk): Remove extra logging after testing. 707 Log.i(TAG, "Adding cam_id: " + cam + "scene: " + scene); 708 mAllScenes.add(new ResultKey(cam, scene)); 709 adapter.add(new DialogTestListItem(this, 710 testTitle(cam, scene), 711 testId(cam, scene))); 712 } 713 } 714 Log.d(TAG, "Total combinations to test on this device:" 715 + mAllScenes.size() + " folded? " + mIsDeviceFolded); 716 if (mIsDeviceFolded) { 717 mFoldedTestSetupDone = true; 718 Log.i(TAG, "mFoldedTestSetupDone"); 719 } else { 720 mUnfoldedTestSetupDone = true; 721 Log.i(TAG, "mUnfoldedTestSetupDone"); 722 } 723 if (mFoldedTestSetupDone && mUnfoldedTestSetupDone) { 724 Log.d(TAG, "Total combinations to test on this foldable " 725 + "device for both states:" + mAllScenes.size()); 726 } 727 adapter.loadTestResults(); 728 } 729 730 @Override setupTests(ArrayTestListAdapter adapter)731 protected void setupTests(ArrayTestListAdapter adapter) { 732 mAdapter = adapter; 733 if (mIsFoldableDevice) { 734 if (mFoldedTestSetupDone && mUnfoldedTestSetupDone) { 735 Log.i(TAG, "Set up is done"); 736 } 737 } else { 738 setupItsTests(adapter); 739 } 740 } 741 742 @Override onResume()743 protected void onResume() { 744 super.onResume(); 745 if (mCameraManager == null) { 746 showToast(R.string.no_camera_manager); 747 } else { 748 Log.d(TAG, "register ITS result receiver"); 749 IntentFilter filter = new IntentFilter(ACTION_ITS_RESULT); 750 registerReceiver(mResultsReceiver, filter, Context.RECEIVER_EXPORTED); 751 mReceiverRegistered = true; 752 } 753 } 754 755 @Override onDestroy()756 public void onDestroy() { 757 Log.d(TAG, "unregister ITS result receiver"); 758 if (mReceiverRegistered) { 759 unregisterReceiver(mResultsReceiver); 760 } 761 super.onDestroy(); 762 } 763 764 @Override onConfigurationChanged(Configuration newConfig)765 public void onConfigurationChanged(Configuration newConfig) { 766 super.onConfigurationChanged(newConfig); 767 setContentView(R.layout.its_main); 768 setInfoResources(R.string.camera_its_test, R.string.camera_its_test_info, -1); 769 setPassFailButtonClickListeners(); 770 // Changing folded state can incorrectly enable pass button 771 ItsTestActivity.this.getPassButton().setEnabled(false); 772 } 773 } 774