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.ActivityNotFoundException; 20 import android.content.BroadcastReceiver; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.content.res.Configuration; 26 import android.content.res.Resources; 27 import android.hardware.camera2.CameraManager; 28 import android.hardware.cts.helpers.CameraUtils; 29 import android.hardware.devicestate.DeviceState; 30 import android.hardware.devicestate.DeviceStateManager; 31 import android.mediapc.cts.common.CameraRequirement; 32 import android.mediapc.cts.common.PerformanceClassEvaluator; 33 import android.net.Uri; 34 import android.os.Bundle; 35 import android.os.Handler; 36 import android.os.HandlerThread; 37 import android.provider.MediaStore; 38 import android.text.method.ScrollingMovementMethod; 39 import android.util.Log; 40 import android.util.Pair; 41 import android.view.WindowManager; 42 import android.widget.TextView; 43 import android.widget.Toast; 44 45 import androidx.core.content.FileProvider; 46 47 import com.android.compatibility.common.util.ResultType; 48 import com.android.compatibility.common.util.ResultUnit; 49 import com.android.cts.verifier.ArrayTestListAdapter; 50 import com.android.cts.verifier.CtsVerifierReportLog; 51 import com.android.cts.verifier.DialogTestListActivity; 52 import com.android.cts.verifier.R; 53 import com.android.cts.verifier.TestResult; 54 import com.android.internal.util.ArrayUtils; 55 56 import org.json.JSONArray; 57 import org.json.JSONObject; 58 import org.junit.rules.TestName; 59 60 import java.io.BufferedReader; 61 import java.io.File; 62 import java.io.FileNotFoundException; 63 import java.io.FileReader; 64 import java.io.IOException; 65 import java.math.BigDecimal; 66 import java.time.Instant; 67 import java.time.ZoneId; 68 import java.time.format.DateTimeFormatter; 69 import java.util.ArrayList; 70 import java.util.Arrays; 71 import java.util.Collections; 72 import java.util.Comparator; 73 import java.util.HashMap; 74 import java.util.HashSet; 75 import java.util.Iterator; 76 import java.util.List; 77 import java.util.Locale; 78 import java.util.Set; 79 import java.util.TreeSet; 80 import java.util.concurrent.Executor; 81 import java.util.concurrent.LinkedBlockingQueue; 82 import java.util.regex.Matcher; 83 import java.util.regex.Pattern; 84 85 /** 86 * Test for Camera features that require that the camera be aimed at a specific test scene. 87 * This test activity requires a USB connection to a computer, and a corresponding host-side run of 88 * the python scripts found in the CameraITS directory. 89 */ 90 public class ItsTestActivity extends DialogTestListActivity { 91 private static final String TAG = "ItsTestActivity"; 92 private static final String EXTRA_CAMERA_ID = "camera.its.extra.CAMERA_ID"; 93 private static final String EXTRA_RESULTS = "camera.its.extra.RESULTS"; 94 private static final String EXTRA_VERSION = "camera.its.extra.VERSION"; 95 private static final String CURRENT_VERSION = "1.0"; 96 private static final String ACTION_ITS_RESULT = 97 "com.android.cts.verifier.camera.its.ACTION_ITS_RESULT"; 98 private static final String ACTION_ITS_DO_JCA_CAPTURE = 99 "com.android.cts.verifier.camera.its.ACTION_ITS_DO_JCA_CAPTURE"; 100 private static final int REQUEST_IMAGE_CAPTURE = 1; 101 private static final String JCA_PACKAGE_NAME = "com.google.jetpackcamera"; 102 private static final String JCA_ACTIVITY_NAME = "MainActivity"; 103 private static final String JCA_FILES_CHILD_PATHNAME = "Images/JCATestCaptures"; 104 public static final String JCA_CAPTURE_PATH_TAG = "JCA_CAPTURE_PATH"; 105 public static final String JCA_CAPTURE_STATUS_TAG = "JCA_CAPTURE_STATUS"; 106 107 private static final String RESULT_PASS = "PASS"; 108 private static final String RESULT_FAIL = "FAIL"; 109 private static final String RESULT_NOT_EXECUTED = "NOT_EXECUTED"; 110 private static final Set<String> RESULT_VALUES = new HashSet<String>( 111 Arrays.asList(new String[] {RESULT_PASS, RESULT_FAIL, RESULT_NOT_EXECUTED})); 112 private static final int MAX_SUMMARY_LEN = 200; 113 114 private static final Pattern MPC12_CAMERA_LAUNCH_PATTERN = 115 Pattern.compile("camera_launch_time_ms:(\\d+(\\.\\d+)?)"); 116 private static final Pattern MPC12_JPEG_CAPTURE_PATTERN = 117 Pattern.compile("1080p_jpeg_capture_time_ms:(\\d+(\\.\\d+)?)"); 118 private static final Pattern MPC15_ULTRA_HDR_PATTERN = 119 Pattern.compile("has_gainmap.*"); 120 private static final int AVAILABILITY_TIMEOUT_MS = 10; 121 122 private static final Pattern PERF_METRICS_YUV_PLUS_JPEG_PATTERN = 123 Pattern.compile("test_yuv_plus_jpeg_rms_diff:(\\d+(\\.\\d+)?)"); 124 /* TODO b/346817862 - More concise regex. */ 125 private static final Pattern PERF_METRICS_YUV_PLUS_RAW_PATTERN = 126 Pattern.compile("test_yuv_plus_raw.*"); 127 128 private static final String PERF_METRICS_KEY_PREFIX_YUV_PLUS = "yuv_plus"; 129 private static final String PERF_METRICS_KEY_RAW = "raw_"; 130 private static final String PERF_METRICS_KEY_RAW10 = "raw10"; 131 private static final String PERF_METRICS_KEY_RAW12 = "raw12"; 132 private static final String PERF_METRICS_KEY_RMS_DIFF = "rms_diff"; 133 134 private static final Pattern PERF_METRICS_IMU_DRIFT_PATTERN = 135 Pattern.compile("test_imu_drift_.*"); 136 private static final Pattern PERF_METRICS_SENSOR_FUSION_PATTERN = 137 Pattern.compile("test_sensor_fusion_.*"); 138 139 private static final String PERF_METRICS_KEY_PREFIX_SENSOR_FUSION = "sensor_fusion"; 140 private static final String PERF_METRICS_KEY_CORR_DIST = "corr_dist"; 141 private static final String PERF_METRICS_KEY_OFFSET_MS = "offset_ms"; 142 143 private static final Pattern PERF_METRICS_BURST_CAPTURE_PATTERN = 144 Pattern.compile("test_burst_capture_.*"); 145 146 private static final String PERF_METRICS_KEY_PREFIX_BURST_CAPTURE = "burst_capture"; 147 private static final String PERF_METRICS_KEY_FRAMEDURATION = 148 "max_frame_time_minus_frameduration_ns"; 149 150 private static final Pattern PERF_METRICS_LOW_LIGHT_BOOST_PATTERN = 151 Pattern.compile("test_low_light_boost_.*"); 152 private static final Pattern PERF_METRICS_EXTENSION_NIGHT_MODE_PATTERN = 153 Pattern.compile("test_night_extension_.*"); 154 155 private static final String PERF_METRICS_KEY_CHART_LUMA = "chart_luma"; 156 private static final String PERF_METRICS_KEY_AVG_LUMA = "avg_luma"; 157 private static final String PERF_METRICS_KEY_DELTA_AVG_LUMA = "delta_avg_luma"; 158 private static final String PERF_METRICS_KEY_PREFIX_NIGHT = "night_extension"; 159 private static final String PERF_METRICS_KEY_PREFIX_LOW_LIGHT = "low_light_boost"; 160 161 private static final Pattern PERF_METRICS_DISTORTION_PATTERN = 162 Pattern.compile("test_preview_distortion_.*"); 163 164 private static final Pattern PERF_METRICS_INTRINSIC_PATTERN = 165 Pattern.compile("test_lens_intrinsic_calibration_.*"); 166 167 private static final Pattern PERF_METRICS_AEAWB_PATTERN = 168 Pattern.compile("test_ae_awb_regions_.*"); 169 private static final String REPORT_LOG_NAME = "CtsCameraItsTestCases"; 170 171 private static final String ZOOM = "zoom"; 172 private static final String TEST_PATTERN = "^test_"; 173 private static final Pattern PERF_METRICS_MULTICAM_PATTERN = 174 Pattern.compile("test_multi_camera_switch_.*"); 175 176 private static final Pattern PERF_METRICS_PREVIEW_FRAME_DROP_PATTERN = 177 Pattern.compile("test_preview_frame_drop_.*"); 178 179 private static final String PERF_METRICS_KEY_MAX_DELTA = "max_delta"; 180 private static final String PERF_METRICS_KEY_PREFIX_PREVIEW_FRAME_DROP = 181 "preview_frame_drop"; 182 183 private final ResultReceiver mResultsReceiver = new ResultReceiver(); 184 private final BroadcastReceiver mCommandReceiver = new BroadcastReceiver() { 185 @Override 186 public void onReceive(Context context, Intent intent) { 187 Logt.i(TAG, "Received ITS test command"); 188 if (ACTION_ITS_DO_JCA_CAPTURE.equals(intent.getAction())) { 189 Logt.i(TAG, "Doing JCA intent capture"); 190 doJcaCapture(); 191 } else { 192 Logt.e(TAG, "Unknown intent action " + intent.getAction()); 193 } 194 } 195 }; 196 private String mJcaCapturePath = ""; 197 private boolean mReceiverRegistered = false; 198 199 public final TestName mTestName = new TestName(); 200 private boolean mIsFoldableDevice = false; 201 private boolean mIsDeviceFolded = false; 202 private boolean mFoldedTestSetupDone = false; 203 private boolean mUnfoldedTestSetupDone = false; 204 private Set<Pair<String, String>> mUnavailablePhysicalCameras = 205 new HashSet<Pair<String, String>>(); 206 private CameraManager mCameraManager = null; 207 private DeviceStateManager mDeviceStateManager = null; 208 private HandlerThread mCameraThread = null; 209 private Handler mCameraHandler = null; 210 211 // Initialized in onCreate 212 List<String> mToBeTestedCameraIds = null; 213 private String mPrimaryRearCameraId = null; 214 private String mPrimaryFrontCameraId = null; 215 private List<String> mToBeTestedCameraIdsUnfolded = null; 216 private List<String> mToBeTestedCameraIdsFolded = null; 217 private String mPrimaryRearCameraIdUnfolded = null; 218 private String mPrimaryFrontCameraIdUnfolded = null; 219 private ArrayTestListAdapter mAdapter; 220 221 // Scenes 222 private static final List<String> mSceneIds = List.of( 223 "scene0", 224 "scene1_1", 225 "scene1_2", 226 "scene2_a", 227 "scene2_b", 228 "scene2_c", 229 "scene2_d", 230 "scene2_e", 231 "scene2_f", 232 "scene3", 233 "scene4", 234 "scene5", 235 "scene6", 236 "scene7", 237 "scene8", 238 "scene9", 239 "scene_extensions/scene_hdr", 240 "scene_extensions/scene_low_light", 241 "scene_video", 242 "sensor_fusion", 243 "feature_combination", 244 "scene_flash"); 245 246 // This must match scenes of SUB_CAMERA_TESTS in tools/run_all_tests.py 247 private static final List<String> mHiddenPhysicalCameraSceneIds = List.of( 248 "scene0", 249 "scene1_1", 250 "scene1_2", 251 "scene2_a", 252 "scene4", 253 "scene_video", 254 "sensor_fusion"); 255 256 // TODO: cache the following in saved bundle 257 private Set<ResultKey> mAllScenes = null; 258 // (camera, scene) -> (pass, fail) 259 private final HashMap<ResultKey, Boolean> mExecutedScenes = new HashMap<>(); 260 // map camera id to ITS summary report path 261 private final HashMap<ResultKey, String> mSummaryMap = new HashMap<>(); 262 // All primary cameras for which MPC level test has run 263 private Set<ResultKey> mExecutedMpcTests = null; 264 private static final String MPC_LAUNCH_REQ_NUM = "2.2.7.2/7.5/H-1-6"; 265 private static final String MPC_JPEG_CAPTURE_REQ_NUM = "2.2.7.2/7.5/H-1-5"; 266 private static final String MPC_ULTRA_HDR_REQ_NUM = "2.2.7.2/7.5/H-1-20"; 267 // Performance class evaluator used for writing test result 268 PerformanceClassEvaluator mPce = new PerformanceClassEvaluator(mTestName); 269 CameraRequirement.CameraLatencyRequirement mJpegLatencyReq = 270 mPce.addR7_5__H_1_5(); 271 CameraRequirement.CameraLatencyRequirement mLaunchLatencyReq = 272 mPce.addR7_5__H_1_6(); 273 CameraRequirement.CameraUltraHdrRequirement mUltraHdrReq = 274 mPce.addR7_5__H_1_20(); 275 private CtsVerifierReportLog mReportLog; 276 // Json Array to store all jsob objects with ITS metrics information 277 // stored in the report log 278 private final JSONArray mFinalPerfMetricsArr = new JSONArray(); 279 280 private static class HandlerExecutor implements Executor { 281 private final Handler mHandler; 282 HandlerExecutor(Handler handler)283 HandlerExecutor(Handler handler) { 284 mHandler = handler; 285 } 286 287 @Override execute(Runnable runCmd)288 public void execute(Runnable runCmd) { 289 mHandler.post(runCmd); 290 } 291 } 292 293 final class ResultKey { 294 public final String cameraId; 295 public final String sceneId; 296 ResultKey(String cameraId, String sceneId)297 public ResultKey(String cameraId, String sceneId) { 298 this.cameraId = cameraId; 299 this.sceneId = sceneId; 300 } 301 302 @Override equals(final Object o)303 public boolean equals(final Object o) { 304 if (o == null) return false; 305 if (this == o) return true; 306 if (o instanceof ResultKey) { 307 final ResultKey other = (ResultKey) o; 308 return cameraId.equals(other.cameraId) && sceneId.equals(other.sceneId); 309 } 310 return false; 311 } 312 313 @Override hashCode()314 public int hashCode() { 315 int h = cameraId.hashCode(); 316 h = ((h << 5) - h) ^ sceneId.hashCode(); 317 return h; 318 } 319 } 320 ItsTestActivity()321 public ItsTestActivity() { 322 super(R.layout.its_main, 323 R.string.camera_its_test, 324 R.string.camera_its_test_info, 325 R.string.camera_its_test); 326 } 327 328 private final Comparator<ResultKey> mComparator = new Comparator<ResultKey>() { 329 @Override 330 public int compare(ResultKey k1, ResultKey k2) { 331 if (k1.cameraId.equals(k2.cameraId)) 332 return k1.sceneId.compareTo(k2.sceneId); 333 return k1.cameraId.compareTo(k2.cameraId); 334 } 335 }; 336 337 class ResultReceiver extends BroadcastReceiver { 338 @Override onReceive(Context context, Intent intent)339 public void onReceive(Context context, Intent intent) { 340 Log.i(TAG, "Received result for Camera ITS tests"); 341 if (ACTION_ITS_RESULT.equals(intent.getAction())) { 342 String version = intent.getStringExtra(EXTRA_VERSION); 343 if (version == null || !version.equals(CURRENT_VERSION)) { 344 Log.e(TAG, "Its result version mismatch: expect " + CURRENT_VERSION + 345 ", got " + ((version == null) ? "null" : version)); 346 ItsTestActivity.this.showToast(R.string.its_version_mismatch); 347 return; 348 } 349 350 String cameraId = intent.getStringExtra(EXTRA_CAMERA_ID); 351 String results = intent.getStringExtra(EXTRA_RESULTS); 352 if (cameraId == null || results == null) { 353 Log.e(TAG, "cameraId = " + ((cameraId == null) ? "null" : cameraId) + 354 ", results = " + ((results == null) ? "null" : results)); 355 return; 356 } 357 358 if (mIsFoldableDevice) { 359 if (!mIsDeviceFolded) { 360 if (!mToBeTestedCameraIdsUnfolded.contains(cameraId)) { 361 Log.e(TAG, "Unknown camera id " + cameraId + " reported to ITS"); 362 return; 363 } 364 } else { 365 if (!mToBeTestedCameraIdsFolded.contains(cameraId)) { 366 Log.e(TAG, "Unknown camera id " + cameraId + " reported to ITS"); 367 return; 368 } 369 } 370 } else { 371 if (!mToBeTestedCameraIds.contains(cameraId)) { 372 Log.e(TAG, "Unknown camera id " + cameraId + " reported to ITS"); 373 return; 374 } 375 } 376 377 try { 378 /* Sample JSON results string 379 { 380 "scene0":{ 381 "result":"PASS", 382 "summary":"/sdcard/cam0_scene0.txt" 383 }, 384 "scene1":{ 385 "result":"NOT_EXECUTED" 386 }, 387 "scene2":{ 388 "result":"FAIL", 389 "summary":"/sdcard/cam0_scene2.txt" 390 } 391 } 392 */ 393 JSONObject jsonResults = new JSONObject(results); 394 Log.d(TAG,"Results received:" + jsonResults.toString()); 395 Set<String> scenes = new HashSet<>(); 396 Iterator<String> keys = jsonResults.keys(); 397 while (keys.hasNext()) { 398 scenes.add(keys.next()); 399 } 400 401 JSONObject camJsonObj = new JSONObject(); 402 camJsonObj.put("camera_id", cameraId); 403 // Update test execution results 404 for (String scene : scenes) { 405 JSONObject sceneResult = jsonResults.getJSONObject(scene); 406 Log.v(TAG, sceneResult.toString()); 407 String result = sceneResult.getString("result"); 408 if (result == null) { 409 Log.e(TAG, "Result for " + scene + " is null"); 410 return; 411 } 412 Log.i(TAG, "ITS camera" + cameraId + " " + scene + ": result:" + result); 413 if (!RESULT_VALUES.contains(result)) { 414 Log.e(TAG, "Unknown result for " + scene + ": " + result); 415 return; 416 } 417 ResultKey key = new ResultKey(cameraId, scene); 418 if (result.equals(RESULT_PASS) || result.equals(RESULT_FAIL)) { 419 boolean pass = result.equals(RESULT_PASS); 420 mExecutedScenes.put(key, pass); 421 // Get start/end time per camera/scene for result history collection. 422 mStartTime = sceneResult.getLong("start"); 423 mEndTime = sceneResult.getLong("end"); 424 setTestResult(testId(cameraId, scene), pass ? 425 TestResult.TEST_RESULT_PASSED : TestResult.TEST_RESULT_FAILED); 426 Log.e(TAG, "setTestResult for " + testId(cameraId, scene) + ": " + result); 427 String summary = sceneResult.optString("summary"); 428 if (!summary.equals("")) { 429 mSummaryMap.put(key, summary); 430 } 431 } // do nothing for NOT_EXECUTED scenes 432 433 if (sceneResult.isNull("mpc_metrics")) { 434 continue; 435 } 436 // Update MPC level 437 JSONArray metrics = sceneResult.getJSONArray("mpc_metrics"); 438 for (int i = 0; i < metrics.length(); i++) { 439 String mpcResult = metrics.getString(i); 440 try { 441 if (!matchMpcResult(cameraId, mpcResult)) { 442 Log.e(TAG, "Error parsing MPC result string:" + mpcResult); 443 } 444 } catch (Exception e) { 445 Log.e(TAG, "Error parsing MPC result string:" + mpcResult, e); 446 } 447 448 } 449 450 if (sceneResult.isNull("performance_metrics")) { 451 continue; 452 } 453 454 // Update performance metrics with metrics data from all 455 // scenes for each camera 456 JSONArray mArr = sceneResult.getJSONArray("performance_metrics"); 457 for (int i = 0; i < mArr.length(); i++) { 458 String perfResult = mArr.getString(i); 459 try { 460 if (!matchPerfMetricsResult(perfResult, camJsonObj)) { 461 Log.e(TAG, "Error parsing perf result string:" + perfResult); 462 } 463 } catch (Exception e) { 464 Log.e(TAG, "Error parsing perf result string:" + perfResult, e); 465 } 466 } 467 } 468 // Add performance metrics for all scenes along with camera_id as json arr 469 // to CtsVerifierReportLog for each camera. 470 mFinalPerfMetricsArr.put(camJsonObj); 471 mReportLog.addValues("perf_metrics", mFinalPerfMetricsArr); 472 } catch (org.json.JSONException e) { 473 Log.e(TAG, "Error reading json result string:" + results , e); 474 return; 475 } 476 477 // Submitting the report log generates a CtsCameraITSTestCases.reportlog.json 478 // on device at path /sdcard/ReportLogFiles 479 mReportLog.submit(); 480 481 // Set summary if all scenes reported 482 if (mSummaryMap.keySet().containsAll(mAllScenes)) { 483 // Save test summary 484 StringBuilder summary = new StringBuilder(); 485 for (String path : mSummaryMap.values()) { 486 appendFileContentToSummary(summary, path); 487 } 488 if (summary.length() > MAX_SUMMARY_LEN) { 489 Log.w(TAG, "ITS summary report too long: len: " + summary.length()); 490 } 491 ItsTestActivity.this.getReportLog().setSummary( 492 summary.toString(), 1.0, ResultType.NEUTRAL, ResultUnit.NONE); 493 } 494 495 // Display current progress 496 StringBuilder progress = new StringBuilder(); 497 for (ResultKey k : mAllScenes) { 498 String status = RESULT_NOT_EXECUTED; 499 if (mExecutedScenes.containsKey(k)) { 500 status = mExecutedScenes.get(k) ? RESULT_PASS : RESULT_FAIL; 501 } 502 progress.append(String.format("Cam %s, %s: %s\n", 503 k.cameraId, k.sceneId, status)); 504 } 505 TextView progressView = (TextView) findViewById(R.id.its_progress); 506 progressView.setMovementMethod(new ScrollingMovementMethod()); 507 progressView.setText(progress.toString()); 508 509 510 // Enable pass button if all scenes pass 511 boolean allScenesPassed = true; 512 for (ResultKey k : mAllScenes) { 513 Boolean pass = mExecutedScenes.get(k); 514 if (pass == null || pass == false) { 515 allScenesPassed = false; 516 break; 517 } 518 } 519 if (allScenesPassed) { 520 Log.i(TAG, "All scenes passed."); 521 // Enable pass button 522 ItsTestActivity.this.getPassButton().setEnabled(true); 523 ItsTestActivity.this.setTestResultAndFinish(true); 524 } else { 525 ItsTestActivity.this.getPassButton().setEnabled(false); 526 } 527 } 528 } 529 appendFileContentToSummary(StringBuilder summary, String path)530 private void appendFileContentToSummary(StringBuilder summary, String path) { 531 BufferedReader reader = null; 532 try { 533 reader = new BufferedReader(new FileReader(path)); 534 String line = null; 535 do { 536 line = reader.readLine(); 537 if (line != null) { 538 summary.append(line); 539 } 540 } while (line != null); 541 } catch (FileNotFoundException e) { 542 Log.e(TAG, "Cannot find ITS summary file at " + path); 543 summary.append("Cannot find ITS summary file at " + path); 544 } catch (IOException e) { 545 Log.e(TAG, "IO exception when trying to read " + path); 546 summary.append("IO exception when trying to read " + path); 547 } finally { 548 if (reader != null) { 549 try { 550 reader.close(); 551 } catch (IOException e) { 552 } 553 } 554 } 555 } 556 matchMpcResult(String cameraId, String mpcResult)557 private boolean matchMpcResult(String cameraId, String mpcResult) { 558 Matcher launchMatcher = MPC12_CAMERA_LAUNCH_PATTERN.matcher(mpcResult); 559 boolean launchMatches = launchMatcher.matches(); 560 561 Matcher jpegMatcher = MPC12_JPEG_CAPTURE_PATTERN.matcher(mpcResult); 562 boolean jpegMatches = jpegMatcher.matches(); 563 564 Matcher gainmapMatcher = MPC15_ULTRA_HDR_PATTERN.matcher(mpcResult); 565 boolean gainmapMatches = gainmapMatcher.matches(); 566 Log.i(TAG, "mpcResult: " + mpcResult); 567 568 if (!launchMatches && !jpegMatches && !gainmapMatches) { 569 return false; 570 } 571 if (!cameraId.equals(mPrimaryRearCameraId) && 572 !cameraId.equals(mPrimaryFrontCameraId)) { 573 return false; 574 } 575 576 if (launchMatches) { 577 float latency = Float.parseFloat(launchMatcher.group(1)); 578 if (cameraId.equals(mPrimaryRearCameraId)) { 579 mLaunchLatencyReq.setRearCameraLatency(latency); 580 } else { 581 mLaunchLatencyReq.setFrontCameraLatency(latency); 582 } 583 mExecutedMpcTests.add(new ResultKey(cameraId, MPC_LAUNCH_REQ_NUM)); 584 } else if (jpegMatches) { 585 float latency = Float.parseFloat(jpegMatcher.group(1)); 586 if (cameraId.equals(mPrimaryRearCameraId)) { 587 mJpegLatencyReq.setRearCameraLatency(latency); 588 } else { 589 mJpegLatencyReq.setFrontCameraLatency(latency); 590 } 591 mExecutedMpcTests.add(new ResultKey(cameraId, MPC_JPEG_CAPTURE_REQ_NUM)); 592 } else { 593 Log.i(TAG, "Gainmap pattern matches"); 594 String result = mpcResult.split(":")[1]; 595 boolean hasGainMap = false; 596 if (result.equals("true")) { 597 hasGainMap = true; 598 } 599 if (cameraId.equals(mPrimaryRearCameraId)) { 600 mUltraHdrReq.setRearCameraUltraHdrSupported(hasGainMap); 601 } else { 602 mUltraHdrReq.setFrontCameraUltraHdrSupported(hasGainMap); 603 } 604 mExecutedMpcTests.add(new ResultKey(cameraId, MPC_ULTRA_HDR_REQ_NUM)); 605 } 606 607 // Save MPC info once both front primary and rear primary data are collected. 608 if (mExecutedMpcTests.size() == 4) { 609 mPce.submitAndVerify(); 610 } 611 return true; 612 } 613 parsePerfMetrics(String perfMetricsResult, JSONObject obj, List<String> floatKeys, List<String> booleanKeys, List<String> integerKeys)614 private void parsePerfMetrics(String perfMetricsResult, JSONObject obj, 615 List<String> floatKeys, List<String> booleanKeys, List<String> integerKeys) 616 throws org.json.JSONException { 617 String result = perfMetricsResult.replaceFirst(TEST_PATTERN, ""); 618 String resultKey = result.split(":")[0].strip(); 619 String strValue = result.split(":")[1].strip(); 620 621 if (strValue.equalsIgnoreCase("None")) { 622 obj.put(resultKey, strValue); 623 } else if (floatKeys.stream().anyMatch(resultKey::contains)) { 624 float value = Float.parseFloat(strValue); 625 obj.put(resultKey, value); 626 } else if (booleanKeys.stream().anyMatch(resultKey::contains)) { 627 boolean value = Boolean.parseBoolean(strValue); 628 obj.put(resultKey, value); 629 } else if (integerKeys.stream().anyMatch(resultKey::contains)) { 630 int value = Integer.parseInt(strValue); 631 obj.put(resultKey, value); 632 } else { 633 obj.put(resultKey, strValue); 634 } 635 } 636 matchPerfMetricsResult(String perfMetricsResult, JSONObject obj)637 private boolean matchPerfMetricsResult(String perfMetricsResult, JSONObject obj) { 638 Matcher yuvPlusJpegMetricsMatcher = PERF_METRICS_YUV_PLUS_JPEG_PATTERN.matcher( 639 perfMetricsResult); 640 boolean yuvPlusJpegMetricsMatches = yuvPlusJpegMetricsMatcher.matches(); 641 642 Matcher yuvPlusRawMetricsMatcher = PERF_METRICS_YUV_PLUS_RAW_PATTERN.matcher( 643 perfMetricsResult); 644 boolean yuvPlusRawMetricsMatches = yuvPlusRawMetricsMatcher.matches(); 645 646 Matcher imuDriftMetricsMatcher = PERF_METRICS_IMU_DRIFT_PATTERN.matcher( 647 perfMetricsResult); 648 boolean imuDriftMetricsMatches = imuDriftMetricsMatcher.matches(); 649 650 Matcher sensorFusionMetricsMatcher = PERF_METRICS_SENSOR_FUSION_PATTERN.matcher( 651 perfMetricsResult); 652 boolean sensorFusionMetricsMatches = sensorFusionMetricsMatcher.matches(); 653 654 Matcher burstCaptureMetricsMatcher = PERF_METRICS_BURST_CAPTURE_PATTERN.matcher( 655 perfMetricsResult); 656 boolean burstCaptureMetricsMatches = burstCaptureMetricsMatcher.matches(); 657 658 Matcher distortionMetricsMatcher = PERF_METRICS_DISTORTION_PATTERN.matcher( 659 perfMetricsResult); 660 boolean distortionMetricsMatches = distortionMetricsMatcher.matches(); 661 662 Matcher intrinsicMetricsMatcher = PERF_METRICS_INTRINSIC_PATTERN.matcher( 663 perfMetricsResult); 664 boolean intrinsicMetricsMatches = intrinsicMetricsMatcher.matches(); 665 666 Matcher lowLightBoostMetricsMatcher = 667 PERF_METRICS_LOW_LIGHT_BOOST_PATTERN.matcher(perfMetricsResult); 668 boolean lowLightBoostMetricsMatches = lowLightBoostMetricsMatcher.matches(); 669 670 Matcher nightModeExtensionMetricsMatcher = 671 PERF_METRICS_EXTENSION_NIGHT_MODE_PATTERN.matcher(perfMetricsResult); 672 boolean nightModeExtensionMetricsMatches = nightModeExtensionMetricsMatcher.matches(); 673 674 Matcher aeAwbMetricsMatcher = PERF_METRICS_AEAWB_PATTERN.matcher( 675 perfMetricsResult); 676 boolean aeAwbMetricsMatches = aeAwbMetricsMatcher.matches(); 677 678 Matcher multiCamMetricsMatcher = PERF_METRICS_MULTICAM_PATTERN.matcher( 679 perfMetricsResult); 680 boolean multiCamMetricsMatches = multiCamMetricsMatcher.matches(); 681 682 Matcher previewFrameDropMetricsMatcher = 683 PERF_METRICS_PREVIEW_FRAME_DROP_PATTERN.matcher(perfMetricsResult); 684 boolean previewFrameDropMetricsMatches = previewFrameDropMetricsMatcher.matches(); 685 686 687 if (!yuvPlusJpegMetricsMatches && !yuvPlusRawMetricsMatches 688 && !imuDriftMetricsMatches && !sensorFusionMetricsMatches 689 && !burstCaptureMetricsMatches && !distortionMetricsMatches 690 && !intrinsicMetricsMatches && !lowLightBoostMetricsMatches 691 && !nightModeExtensionMetricsMatches && !aeAwbMetricsMatches 692 && !multiCamMetricsMatches && !previewFrameDropMetricsMatches) { 693 return false; 694 } 695 696 try { 697 if (yuvPlusJpegMetricsMatches) { 698 Log.i(TAG, "jpeg pattern matches"); 699 float diff = Float.parseFloat(yuvPlusJpegMetricsMatcher.group(1)); 700 obj.put("yuv_plus_jpeg_rms_diff", diff); 701 } 702 703 if (yuvPlusRawMetricsMatches) { 704 Log.i(TAG, "yuv plus raw pattern matches"); 705 addPerfMetricsResult(PERF_METRICS_KEY_PREFIX_YUV_PLUS, perfMetricsResult, obj); 706 } 707 708 if (imuDriftMetricsMatches) { 709 Log.i(TAG, "imu drift matches"); 710 // remove "test_" from the result 711 String result = perfMetricsResult.replaceFirst(TEST_PATTERN, ""); 712 String resultKey = result.split(":")[0].strip(); 713 if (resultKey.contains("seconds") || resultKey.contains("hz")) { 714 float value = Float.parseFloat(result.split(":")[1].strip()); 715 obj.put(resultKey, value); 716 } else { 717 String value = result.split(":")[1].strip(); 718 obj.put(resultKey, value); 719 } 720 } 721 722 if (sensorFusionMetricsMatches) { 723 Log.i(TAG, "sensor fusion matches"); 724 addPerfMetricsResult(PERF_METRICS_KEY_PREFIX_SENSOR_FUSION, perfMetricsResult, 725 obj); 726 } 727 728 if (burstCaptureMetricsMatches) { 729 Log.i(TAG, "burst capture matches"); 730 addPerfMetricsResult(PERF_METRICS_KEY_PREFIX_BURST_CAPTURE, perfMetricsResult, 731 obj); 732 } 733 734 if (distortionMetricsMatches) { 735 List<String> floatKeys = Arrays.asList(ZOOM, "distortion_error", 736 "chart_coverage"); 737 List<String> integerKeys = Arrays.asList("physical_id"); 738 parsePerfMetrics(perfMetricsResult, obj, floatKeys, Collections.emptyList(), 739 integerKeys); 740 } 741 if (intrinsicMetricsMatches) { 742 List<String> floatKeys = Arrays.asList("max_principal_point_diff"); 743 List<String> booleanKeys = Arrays.asList( 744 "samples_principal_points_diff_detected"); 745 parsePerfMetrics(perfMetricsResult, obj, floatKeys, booleanKeys, 746 Collections.emptyList()); 747 } 748 if (aeAwbMetricsMatches) { 749 List<String> floatKeys = Arrays.asList("_change"); 750 parsePerfMetrics(perfMetricsResult, obj, floatKeys, Collections.emptyList(), 751 Collections.emptyList()); 752 } 753 754 if (lowLightBoostMetricsMatches) { 755 Log.i(TAG, "low light boost matches"); 756 addPerfMetricsResult(PERF_METRICS_KEY_PREFIX_LOW_LIGHT, perfMetricsResult, obj); 757 } 758 759 if (nightModeExtensionMetricsMatches) { 760 Log.i(TAG, "night mode extension matches"); 761 addPerfMetricsResult(PERF_METRICS_KEY_PREFIX_NIGHT, perfMetricsResult, obj); 762 } 763 764 if (multiCamMetricsMatches) { 765 Log.i(TAG, "multi cam metrics matches"); 766 addMultiCamPerfMetricsResult(perfMetricsResult, obj); 767 } 768 769 if (previewFrameDropMetricsMatches) { 770 Log.i(TAG, "preview frame drop matches"); 771 addPerfMetricsResult(PERF_METRICS_KEY_PREFIX_PREVIEW_FRAME_DROP, 772 perfMetricsResult, obj); 773 } 774 } catch (org.json.JSONException e) { 775 Log.e(TAG, "Error when serializing the metrics into a JSONObject", e); 776 } 777 778 return true; 779 } 780 } 781 addMultiCamPerfMetricsResult(String perfMetricsResult, JSONObject obj)782 private void addMultiCamPerfMetricsResult(String perfMetricsResult, 783 JSONObject obj) throws org.json.JSONException { 784 String[] parts = perfMetricsResult.split(":", 2); // Limit to 2 to avoid splitting values 785 if (parts.length == 2) { 786 String key = parts[0].trim().replaceFirst(TEST_PATTERN, ""); 787 String value = parts[1].trim(); 788 Log.i(TAG, "Key: " + key); 789 Log.i(TAG, "Value: " + value); 790 obj.put(key, value); 791 } else { 792 Log.i(TAG, "Invalid output string"); 793 } 794 } 795 796 /* TODO b/346817862 - Move logic to regex as string splits and trims are brittle. */ addPerfMetricsResult(String keyPrefix, String perfMetricsResult, JSONObject obj)797 private void addPerfMetricsResult(String keyPrefix, String perfMetricsResult, 798 JSONObject obj) throws org.json.JSONException { 799 // remove "test_" from the result 800 String result = perfMetricsResult.replaceFirst("^test_", ""); 801 String resultKey = result.split(":")[0].strip(); 802 String value = result.split(":")[1].strip(); 803 if (resultKey.contains(PERF_METRICS_KEY_CHART_LUMA)) { 804 int[] chartLumaValues = Arrays.stream(value.substring(1, value.length() - 1) 805 .split(",")) 806 .map(String::trim) 807 .mapToInt(Integer::parseInt) 808 .toArray(); 809 JSONArray chartLumaValuesJson = new JSONArray(); 810 for (int luma : chartLumaValues) { 811 chartLumaValuesJson.put(luma); 812 } 813 obj.put(keyPrefix + "_" + PERF_METRICS_KEY_CHART_LUMA, chartLumaValuesJson); 814 } else if (resultKey.contains(PERF_METRICS_KEY_DELTA_AVG_LUMA)) { 815 BigDecimal floatValue = new BigDecimal(value); 816 obj.put(keyPrefix + "_" + PERF_METRICS_KEY_DELTA_AVG_LUMA, floatValue); 817 } else if (resultKey.contains(PERF_METRICS_KEY_AVG_LUMA)) { 818 BigDecimal floatValue = new BigDecimal(value); 819 obj.put(keyPrefix + "_" + PERF_METRICS_KEY_AVG_LUMA, floatValue); 820 } else if (resultKey.contains(PERF_METRICS_KEY_RAW)) { 821 BigDecimal floatValue = new BigDecimal(value); 822 obj.put(keyPrefix + PERF_METRICS_KEY_RAW + PERF_METRICS_KEY_RMS_DIFF, floatValue); 823 } else if (resultKey.contains(PERF_METRICS_KEY_RAW10)) { 824 BigDecimal floatValue = new BigDecimal(value); 825 obj.put(keyPrefix + PERF_METRICS_KEY_RAW10 + "_" + PERF_METRICS_KEY_RMS_DIFF, 826 floatValue); 827 } else if (resultKey.contains(PERF_METRICS_KEY_RAW12)) { 828 BigDecimal floatValue = new BigDecimal(value); 829 obj.put(keyPrefix + PERF_METRICS_KEY_RAW12 + "_" + PERF_METRICS_KEY_RMS_DIFF, 830 floatValue); 831 } else if (resultKey.contains(PERF_METRICS_KEY_PREFIX_BURST_CAPTURE)) { 832 BigDecimal floatValue = new BigDecimal(value); 833 obj.put(keyPrefix + "_" + PERF_METRICS_KEY_FRAMEDURATION, floatValue); 834 } else if (resultKey.contains(PERF_METRICS_KEY_CORR_DIST)) { 835 BigDecimal floatValue = new BigDecimal(value); 836 obj.put(keyPrefix + "_" + PERF_METRICS_KEY_CORR_DIST, floatValue); 837 } else if (resultKey.contains(PERF_METRICS_KEY_OFFSET_MS)) { 838 BigDecimal floatValue = new BigDecimal(value); 839 obj.put(keyPrefix + "_" + PERF_METRICS_KEY_OFFSET_MS, floatValue); 840 } else if (resultKey.contains(PERF_METRICS_KEY_MAX_DELTA)) { 841 BigDecimal floatValue = new BigDecimal(value); 842 obj.put(keyPrefix + "_" + PERF_METRICS_KEY_MAX_DELTA, floatValue); 843 } 844 } 845 846 private class FoldStateListener implements 847 DeviceStateManager.DeviceStateCallback { 848 private int[] mFoldedDeviceStates; 849 private boolean mFirstFoldCheck = false; 850 FoldStateListener(Context context)851 FoldStateListener(Context context) { 852 Resources systemRes = Resources.getSystem(); 853 int foldedStatesArrayIdentifier = systemRes.getIdentifier("config_foldedDeviceStates", 854 "array", "android"); 855 mFoldedDeviceStates = systemRes.getIntArray(foldedStatesArrayIdentifier); 856 } 857 858 @Override onDeviceStateChanged(DeviceState state)859 public final void onDeviceStateChanged(DeviceState state) { 860 int stateIdentifier = state.getIdentifier(); 861 boolean folded = ArrayUtils.contains(mFoldedDeviceStates, stateIdentifier); 862 Log.i(TAG, "Is device folded? " + mIsDeviceFolded); 863 if (!mFirstFoldCheck || mIsDeviceFolded != folded) { 864 mIsDeviceFolded = folded; 865 mFirstFoldCheck = true; 866 if (mFoldedTestSetupDone && mUnfoldedTestSetupDone) { 867 Log.i(TAG, "Setup is done for both the states."); 868 } else { 869 runOnUiThread(new Runnable() { 870 @Override 871 public void run() { 872 Log.i(TAG, "set up from onStateChanged"); 873 getCameraIdsForFoldableDevice(); 874 setupItsTestsForFoldableDevice(mAdapter); 875 } 876 }); 877 } 878 } else { 879 Log.i(TAG, "Last state is same as new state."); 880 } 881 } 882 } 883 884 @Override onCreate(Bundle savedInstanceState)885 protected void onCreate(Bundle savedInstanceState) { 886 // Hide the test if all camera devices are legacy 887 mCameraManager = this.getSystemService(CameraManager.class); 888 if (mReportLog == null) { 889 mReportLog = 890 new CtsVerifierReportLog(REPORT_LOG_NAME, "camera_its_results"); 891 } 892 Context context = this.getApplicationContext(); 893 if (mAllScenes == null) { 894 mAllScenes = new TreeSet<>(mComparator); 895 } 896 if (mExecutedMpcTests == null) { 897 mExecutedMpcTests = new TreeSet<>(mComparator); 898 } 899 mCameraThread = new HandlerThread("ItsTestActivityThread"); 900 mCameraThread.start(); 901 mCameraHandler = new Handler(mCameraThread.getLooper()); 902 HandlerExecutor handlerExecutor = new HandlerExecutor(mCameraHandler); 903 // mIsFoldableDevice is set True for foldables to listen to callback 904 // in FoldStateListener 905 mIsFoldableDevice = isFoldableDevice(); 906 Log.i(TAG, "Is device foldable? " + mIsFoldableDevice); 907 if (mIsFoldableDevice) { 908 FoldStateListener foldStateListener = new FoldStateListener(context); 909 mDeviceStateManager = context.getSystemService(DeviceStateManager.class); 910 // onStateChanged will be called upon registration which helps determine 911 // if the foldable device has changed the folded/unfolded state or not. 912 mDeviceStateManager.registerCallback(handlerExecutor, foldStateListener); 913 } 914 if (!mIsFoldableDevice) { 915 try { 916 ItsUtils.ItsCameraIdList cameraIdList = 917 ItsUtils.getItsCompatibleCameraIds(mCameraManager); 918 mToBeTestedCameraIds = cameraIdList.mCameraIdCombos; 919 mPrimaryRearCameraId = cameraIdList.mPrimaryRearCameraId; 920 mPrimaryFrontCameraId = cameraIdList.mPrimaryFrontCameraId; 921 } catch (ItsException e) { 922 Toast.makeText(ItsTestActivity.this, 923 "Received error from camera service while checking device capabilities: " 924 + e, Toast.LENGTH_SHORT).show(); 925 } 926 } 927 928 super.onCreate(savedInstanceState); 929 930 if (!mIsFoldableDevice) { 931 if (mToBeTestedCameraIds.size() == 0) { 932 showToast(R.string.all_exempted_devices); 933 ItsTestActivity.this.getReportLog().setSummary( 934 "PASS: all cameras on this device are exempted from ITS", 935 1.0, ResultType.NEUTRAL, ResultUnit.NONE); 936 setTestResultAndFinish(true); 937 } 938 } 939 // Default locale must be set to "en-us" 940 Locale locale = Locale.getDefault(); 941 if (!Locale.US.equals(locale)) { 942 String toastMessage = "Unsupported default language " + locale + "! " 943 + "Please switch the default language to English (United States) in " 944 + "Settings > Language & input > Languages"; 945 Toast.makeText(ItsTestActivity.this, toastMessage, Toast.LENGTH_LONG).show(); 946 ItsTestActivity.this.getReportLog().setSummary( 947 "FAIL: Default language is not set to " + Locale.US, 948 1.0, ResultType.NEUTRAL, ResultUnit.NONE); 949 setTestResultAndFinish(false); 950 } 951 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 952 } 953 getCameraIdsAvailableForTesting()954 private List<String> getCameraIdsAvailableForTesting() { 955 List<String> toBeTestedCameraIds = new ArrayList<String>(); 956 List<String> availableCameraIdList = new ArrayList<String>(); 957 try { 958 ItsUtils.ItsCameraIdList cameraIdList = 959 ItsUtils.getItsCompatibleCameraIds(mCameraManager); 960 toBeTestedCameraIds = cameraIdList.mCameraIdCombos; 961 mPrimaryRearCameraId = cameraIdList.mPrimaryRearCameraId; 962 mPrimaryFrontCameraId = cameraIdList.mPrimaryFrontCameraId; 963 mUnavailablePhysicalCameras = getUnavailablePhysicalCameras(); 964 Log.i(TAG, "unavailablePhysicalCameras:" 965 + mUnavailablePhysicalCameras.toString()); 966 for (String str : toBeTestedCameraIds) { 967 if (str.contains(".")) { 968 String[] strArr = str.split("\\."); 969 if (mUnavailablePhysicalCameras.contains(new Pair<>(strArr[0], strArr[1]))) { 970 toBeTestedCameraIds.remove(str); 971 } 972 } 973 } 974 Log.i(TAG, "AvailablePhysicalCameras to be tested:" 975 + Arrays.asList(toBeTestedCameraIds.toString())); 976 } catch (ItsException e) { 977 Log.i(TAG, "Received error from camera service while checking device capabilities: " 978 + e); 979 } catch (Exception e) { 980 Log.i(TAG, "Exception: " + e); 981 } 982 983 return toBeTestedCameraIds; 984 } 985 986 // Get camera ids available for testing for device in 987 // each state: folded and unfolded. getCameraIdsForFoldableDevice()988 protected void getCameraIdsForFoldableDevice() { 989 boolean deviceFolded = mIsDeviceFolded; 990 try { 991 if (mIsDeviceFolded) { 992 mToBeTestedCameraIdsFolded = getCameraIdsAvailableForTesting(); 993 } else { 994 mToBeTestedCameraIdsUnfolded = getCameraIdsAvailableForTesting(); 995 } 996 } catch (Exception e) { 997 Log.i(TAG, "Exception: " + e); 998 } 999 } 1000 1001 @Override showManualTestDialog(final DialogTestListItem test, final DialogTestListItem.TestCallback callback)1002 public void showManualTestDialog(final DialogTestListItem test, 1003 final DialogTestListItem.TestCallback callback) { 1004 //Nothing todo for ITS 1005 } 1006 testTitle(String cam, String scene)1007 protected String testTitle(String cam, String scene) { 1008 return "Camera: " + cam + ", " + scene; 1009 } 1010 1011 // CtsVerifier has a "Folded" toggle that selectively surfaces some tests. 1012 // To separate the tests in folded and unfolded states, CtsVerifier adds a [folded] 1013 // suffix to the test id in its internal database depending on the state of the "Folded" 1014 // toggle button. However, CameraITS has tests that it needs to persist across both folded 1015 // and unfolded states.To get the test results to persist, we need CtsVerifier to store and 1016 // look up the same test id regardless of the toggle button state. 1017 // TODO(b/282804139): Update CTS tests to allow activities to write tests that persist 1018 // across the states testId(String cam, String scene)1019 protected String testId(String cam, String scene) { 1020 return "Camera_ITS_" + cam + "_" + scene + "[folded]"; 1021 } 1022 isFoldableDevice()1023 protected boolean isFoldableDevice() { 1024 Context context = this.getApplicationContext(); 1025 return CameraUtils.isDeviceFoldable(context); 1026 } 1027 isDeviceFolded()1028 protected boolean isDeviceFolded() { 1029 return mIsDeviceFolded; 1030 } 1031 getUnavailablePhysicalCameras()1032 protected Set<Pair<String, String>> getUnavailablePhysicalCameras() throws ItsException { 1033 final LinkedBlockingQueue<Pair<String, String>> unavailablePhysicalCamEventQueue = 1034 new LinkedBlockingQueue<>(); 1035 mCameraThread = new HandlerThread("ItsCameraThread"); 1036 mCameraThread.start(); 1037 mCameraHandler = new Handler(mCameraThread.getLooper()); 1038 try { 1039 CameraManager.AvailabilityCallback ac = new CameraManager.AvailabilityCallback() { 1040 @Override 1041 public void onPhysicalCameraUnavailable(String cameraId, String physicalCameraId) { 1042 unavailablePhysicalCamEventQueue.offer(new Pair<>(cameraId, physicalCameraId)); 1043 } 1044 }; 1045 mCameraManager.registerAvailabilityCallback(ac, mCameraHandler); 1046 Set<Pair<String, String>> unavailablePhysicalCameras = 1047 new HashSet<Pair<String, String>>(); 1048 Pair<String, String> candidatePhysicalIds = 1049 unavailablePhysicalCamEventQueue.poll(AVAILABILITY_TIMEOUT_MS, 1050 java.util.concurrent.TimeUnit.MILLISECONDS); 1051 while (candidatePhysicalIds != null) { 1052 unavailablePhysicalCameras.add(candidatePhysicalIds); 1053 candidatePhysicalIds = 1054 unavailablePhysicalCamEventQueue.poll(AVAILABILITY_TIMEOUT_MS, 1055 java.util.concurrent.TimeUnit.MILLISECONDS); 1056 } 1057 mCameraManager.unregisterAvailabilityCallback(ac); 1058 return unavailablePhysicalCameras; 1059 } catch (Exception e) { 1060 throw new ItsException("Exception: ", e); 1061 } 1062 } 1063 setupItsTests(ArrayTestListAdapter adapter)1064 protected void setupItsTests(ArrayTestListAdapter adapter) { 1065 for (String cam : mToBeTestedCameraIds) { 1066 List<String> scenes = cam.contains(ItsUtils.CAMERA_ID_TOKENIZER) 1067 ? mHiddenPhysicalCameraSceneIds : mSceneIds; 1068 for (String scene : scenes) { 1069 // Add camera and scene combinations in mAllScenes to avoid adding n/a scenes for 1070 // devices with sub-cameras. 1071 mAllScenes.add(new ResultKey(cam, scene)); 1072 adapter.add(new DialogTestListItem(this, 1073 testTitle(cam, scene), 1074 testId(cam, scene))); 1075 } 1076 if (mExecutedMpcTests == null) { 1077 mExecutedMpcTests = new TreeSet<>(mComparator); 1078 } 1079 Log.d(TAG, "Total combinations to test on this device:" + mAllScenes.size()); 1080 } 1081 } 1082 setupItsTestsForFoldableDevice(ArrayTestListAdapter adapter)1083 protected void setupItsTestsForFoldableDevice(ArrayTestListAdapter adapter) { 1084 List<String> toBeTestedCameraIds = new ArrayList<String>(); 1085 if (mIsDeviceFolded) { 1086 toBeTestedCameraIds = mToBeTestedCameraIdsFolded; 1087 } else { 1088 toBeTestedCameraIds = mToBeTestedCameraIdsUnfolded; 1089 } 1090 1091 for (String cam : toBeTestedCameraIds) { 1092 List<String> scenes = cam.contains(ItsUtils.CAMERA_ID_TOKENIZER) 1093 ? mHiddenPhysicalCameraSceneIds : mSceneIds; 1094 for (String scene : scenes) { 1095 // Add camera and scene combinations in mAllScenes to avoid adding n/a scenes for 1096 // devices with sub-cameras. 1097 if (cam.contains(mPrimaryFrontCameraId) && mIsDeviceFolded) { 1098 scene = scene + "_folded"; 1099 } 1100 // Rear camera scenes will be added only once. 1101 if (mAllScenes.contains(new ResultKey(cam, scene))) { 1102 continue; 1103 } 1104 // TODO(ruchamk): Remove extra logging after testing. 1105 Log.i(TAG, "Adding cam_id: " + cam + "scene: " + scene); 1106 mAllScenes.add(new ResultKey(cam, scene)); 1107 adapter.add(new DialogTestListItem(this, 1108 testTitle(cam, scene), 1109 testId(cam, scene))); 1110 } 1111 } 1112 Log.d(TAG, "Total combinations to test on this device:" 1113 + mAllScenes.size() + " folded? " + mIsDeviceFolded); 1114 if (mIsDeviceFolded) { 1115 mFoldedTestSetupDone = true; 1116 Log.i(TAG, "mFoldedTestSetupDone"); 1117 } else { 1118 mUnfoldedTestSetupDone = true; 1119 Log.i(TAG, "mUnfoldedTestSetupDone"); 1120 } 1121 if (mFoldedTestSetupDone && mUnfoldedTestSetupDone) { 1122 Log.d(TAG, "Total combinations to test on this foldable " 1123 + "device for both states:" + mAllScenes.size()); 1124 } 1125 adapter.loadTestResults(); 1126 } 1127 1128 @Override setupTests(ArrayTestListAdapter adapter)1129 protected void setupTests(ArrayTestListAdapter adapter) { 1130 mAdapter = adapter; 1131 if (mIsFoldableDevice) { 1132 if (mFoldedTestSetupDone && mUnfoldedTestSetupDone) { 1133 Log.i(TAG, "Set up is done"); 1134 } 1135 } else { 1136 setupItsTests(adapter); 1137 } 1138 } 1139 1140 @Override onResume()1141 protected void onResume() { 1142 super.onResume(); 1143 if (mCameraManager == null) { 1144 showToast(R.string.no_camera_manager); 1145 } else { 1146 Log.d(TAG, "register ITS result receiver and command receiver"); 1147 IntentFilter filter = new IntentFilter(ACTION_ITS_RESULT); 1148 registerReceiver(mResultsReceiver, filter, Context.RECEIVER_EXPORTED); 1149 filter = new IntentFilter(ACTION_ITS_DO_JCA_CAPTURE); 1150 registerReceiver(mCommandReceiver, filter, Context.RECEIVER_EXPORTED); 1151 mReceiverRegistered = true; 1152 } 1153 } 1154 1155 @Override onDestroy()1156 public void onDestroy() { 1157 Log.d(TAG, "unregister ITS result receiver"); 1158 if (mReceiverRegistered) { 1159 unregisterReceiver(mResultsReceiver); 1160 unregisterReceiver(mCommandReceiver); 1161 } 1162 super.onDestroy(); 1163 } 1164 1165 @Override onConfigurationChanged(Configuration newConfig)1166 public void onConfigurationChanged(Configuration newConfig) { 1167 super.onConfigurationChanged(newConfig); 1168 setContentView(R.layout.its_main); 1169 setInfoResources(R.string.camera_its_test, R.string.camera_its_test_info, -1); 1170 setPassFailButtonClickListeners(); 1171 // Changing folded state can incorrectly enable pass button 1172 ItsTestActivity.this.getPassButton().setEnabled(false); 1173 } 1174 1175 @Override handleActivityResult(int requestCode, int resultCode, Intent data)1176 public void handleActivityResult(int requestCode, int resultCode, Intent data) { 1177 Logt.i(TAG, "request code: " + requestCode + ", result code: " + resultCode); 1178 if (requestCode == REQUEST_IMAGE_CAPTURE) { 1179 if (resultCode != RESULT_OK) { 1180 Logt.e(TAG, "Capture failed!"); 1181 } 1182 Intent serviceIntent = new Intent(this, ItsService.class); 1183 serviceIntent.putExtra(JCA_CAPTURE_PATH_TAG, mJcaCapturePath); 1184 serviceIntent.putExtra(JCA_CAPTURE_STATUS_TAG, resultCode); 1185 startService(serviceIntent); 1186 } else { 1187 super.handleActivityResult(requestCode, resultCode, data); 1188 } 1189 } 1190 doJcaCapture()1191 private void doJcaCapture() { 1192 Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 1193 File imageDir = new File(this.getExternalFilesDir(null), JCA_FILES_CHILD_PATHNAME); 1194 imageDir.mkdirs(); 1195 if (!imageDir.exists()) { 1196 Logt.e(TAG, "Could not create image directory"); 1197 return; 1198 } 1199 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss") 1200 .withZone(ZoneId.systemDefault()); 1201 String timestamp = formatter.format(Instant.now()); 1202 File imageFile = new File(imageDir, "ITS_JCA_" + timestamp + ".jpg"); 1203 Logt.i(TAG, "file path: " + imageFile.toString()); 1204 mJcaCapturePath = imageFile.toString(); 1205 Uri photoUri = FileProvider.getUriForFile( 1206 this, 1207 "com.android.cts.verifier.managedprovisioning.fileprovider", 1208 imageFile); 1209 takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); 1210 takePictureIntent.setComponent(new ComponentName( 1211 JCA_PACKAGE_NAME, JCA_PACKAGE_NAME + "." + JCA_ACTIVITY_NAME)); 1212 takePictureIntent.setFlags( 1213 Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 1214 try { 1215 startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE); 1216 } catch (ActivityNotFoundException e) { 1217 Logt.e(TAG, "Error starting image capture intent activity: " + e); 1218 } 1219 } 1220 } 1221