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