CameraITS: ItsTestActivity changes for foldables.
Bug: 261697137
Test: Installing CtsVerifier apk and verifying test
list setting up correctly.
Change-Id: I3f690d90687c79c68b355e26ed951c65a9ecf251
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 6d11ea5..5cc98fd 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -2970,7 +2970,7 @@
<activity android:name=".camera.its.ItsTestActivity"
android:label="@string/camera_its_test"
android:launchMode="singleTop"
- android:configChanges="keyboardHidden|screenSize"
+ android:configChanges="keyboardHidden|screenSize|screenLayout"
android:exported="true"
android:screenOrientation="landscape">
<intent-filter>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
index 9f43e85..825461f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
@@ -21,44 +21,52 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.hardware.camera2.CameraManager;
+import android.hardware.cts.helpers.CameraUtils;
+import android.hardware.devicestate.DeviceStateManager;
import android.mediapc.cts.common.PerformanceClassEvaluator;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
+import android.util.Pair;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;
-import java.io.BufferedReader;
-import java.io.FileReader;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
import com.android.compatibility.common.util.ResultType;
import com.android.compatibility.common.util.ResultUnit;
import com.android.cts.verifier.ArrayTestListAdapter;
import com.android.cts.verifier.DialogTestListActivity;
import com.android.cts.verifier.R;
import com.android.cts.verifier.TestResult;
+import com.android.internal.util.ArrayUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.rules.TestName;
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
/**
* Test for Camera features that require that the camera be aimed at a specific test scene.
* This test activity requires a USB connection to a computer, and a corresponding host-side run of
@@ -84,16 +92,32 @@
Pattern.compile("camera_launch_time_ms:(\\d+(\\.\\d+)?)");
private static final Pattern MPC12_JPEG_CAPTURE_PATTERN =
Pattern.compile("1080p_jpeg_capture_time_ms:(\\d+(\\.\\d+)?)");
+ private static final int AVAILABILITY_TIMEOUT_MS = 10;
private final ResultReceiver mResultsReceiver = new ResultReceiver();
private boolean mReceiverRegistered = false;
public final TestName mTestName = new TestName();
+ private boolean mIsFoldableDevice = false;
+ private boolean mIsDeviceFolded = false;
+ private boolean mFoldedTestSetupDone = false;
+ private boolean mUnfoldedTestSetupDone = false;
+ private Set<Pair<String, String>> mUnavailablePhysicalCameras =
+ new HashSet<Pair<String, String>>();
+ private CameraManager mCameraManager = null;
+ private DeviceStateManager mDeviceStateManager = null;
+ private HandlerThread mCameraThread = null;
+ private Handler mCameraHandler = null;
// Initialized in onCreate
List<String> mToBeTestedCameraIds = null;
- String mPrimaryRearCameraId = null;
- String mPrimaryFrontCameraId = null;
+ private String mPrimaryRearCameraId = null;
+ private String mPrimaryFrontCameraId = null;
+ private List<String> mToBeTestedCameraIdsUnfolded = null;
+ private List<String> mToBeTestedCameraIdsFolded = null;
+ private String mPrimaryRearCameraIdUnfolded = null;
+ private String mPrimaryFrontCameraIdUnfolded = null;
+ private ArrayTestListAdapter mAdapter;
// Scenes
private static final ArrayList<String> mSceneIds = new ArrayList<String> () {{
@@ -138,6 +162,18 @@
PerformanceClassEvaluator.CameraLatencyRequirement mLaunchLatencyReq =
mPce.addR7_5__H_1_6();
+ private static class HandlerExecutor implements Executor {
+ private final Handler mHandler;
+
+ HandlerExecutor(Handler handler) {
+ mHandler = handler;
+ }
+
+ @Override
+ public void execute(Runnable runCmd) {
+ mHandler.post(runCmd);
+ }
+ }
final class ResultKey {
public final String cameraId;
@@ -204,9 +240,23 @@
return;
}
- if (!mToBeTestedCameraIds.contains(cameraId)) {
- Log.e(TAG, "Unknown camera id " + cameraId + " reported to ITS");
- return;
+ if (mIsFoldableDevice) {
+ if (!mIsDeviceFolded) {
+ if (!mToBeTestedCameraIdsUnfolded.contains(cameraId)) {
+ Log.e(TAG, "Unknown camera id " + cameraId + " reported to ITS");
+ return;
+ }
+ } else {
+ if (!mToBeTestedCameraIdsFolded.contains(cameraId)) {
+ Log.e(TAG, "Unknown camera id " + cameraId + " reported to ITS");
+ return;
+ }
+ }
+ } else {
+ if (!mToBeTestedCameraIds.contains(cameraId)) {
+ Log.e(TAG, "Unknown camera id " + cameraId + " reported to ITS");
+ return;
+ }
}
try {
@@ -398,36 +448,100 @@
}
}
+ private class FoldStateListener implements
+ DeviceStateManager.DeviceStateCallback {
+ private int[] mFoldedDeviceStates;
+ private boolean mFirstFoldCheck = false;
+
+ FoldStateListener(Context context) {
+ Resources systemRes = Resources.getSystem();
+ int foldedStatesArrayIdentifier = systemRes.getIdentifier("config_foldedDeviceStates",
+ "array", "android");
+ mFoldedDeviceStates = systemRes.getIntArray(foldedStatesArrayIdentifier);
+ }
+
+ @Override
+ public final void onStateChanged(int state) {
+ boolean folded = ArrayUtils.contains(mFoldedDeviceStates, state);
+ Log.i(TAG, "Is device folded? " + mIsDeviceFolded);
+ if (!mFirstFoldCheck || mIsDeviceFolded != folded) {
+ mIsDeviceFolded = folded;
+ mFirstFoldCheck = true;
+ if (mFoldedTestSetupDone && mUnfoldedTestSetupDone) {
+ Log.i(TAG, "Setup is done for both the states.");
+ } else {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Log.i(TAG, "set up from onStateChanged");
+ getCameraIdsForFoldableDevice();
+ setupItsTestsForFoldableDevice(mAdapter);
+ }
+ });
+ }
+ } else {
+ Log.i(TAG, "Last state is same as new state.");
+ }
+ }
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
// Hide the test if all camera devices are legacy
- CameraManager manager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE);
- try {
- ItsUtils.ItsCameraIdList cameraIdList =
- ItsUtils.getItsCompatibleCameraIds(manager);
- mToBeTestedCameraIds = cameraIdList.mCameraIdCombos;
- mPrimaryRearCameraId = cameraIdList.mPrimaryRearCameraId;
- mPrimaryFrontCameraId = cameraIdList.mPrimaryFrontCameraId;
- } catch (ItsException e) {
- Toast.makeText(ItsTestActivity.this,
- "Received error from camera service while checking device capabilities: "
- + e, Toast.LENGTH_SHORT).show();
+ mCameraManager = this.getSystemService(CameraManager.class);
+ Context context = this.getApplicationContext();
+ if (mAllScenes == null) {
+ mAllScenes = new TreeSet<>(mComparator);
+ }
+ if (mExecutedMpcTests == null) {
+ mExecutedMpcTests = new TreeSet<>(mComparator);
+ }
+ mCameraThread = new HandlerThread("ItsTestActivityThread");
+ mCameraThread.start();
+ mCameraHandler = new Handler(mCameraThread.getLooper());
+ HandlerExecutor handlerExecutor = new HandlerExecutor(mCameraHandler);
+ // mIsFoldableDevice is set True for foldables to listen to callback
+ // in FoldStateListener
+ mIsFoldableDevice = isFoldableDevice();
+ Log.i(TAG, "Is device foldable? " + mIsFoldableDevice);
+ if (mIsFoldableDevice) {
+ FoldStateListener foldStateListener = new FoldStateListener(context);
+ mDeviceStateManager = context.getSystemService(DeviceStateManager.class);
+ // onStateChanged will be called upon registration which helps determine
+ // if the foldable device has changed the folded/unfolded state or not.
+ mDeviceStateManager.registerCallback(handlerExecutor, foldStateListener);
+ }
+ if (!mIsFoldableDevice) {
+ try {
+ ItsUtils.ItsCameraIdList cameraIdList =
+ ItsUtils.getItsCompatibleCameraIds(mCameraManager);
+ mToBeTestedCameraIds = cameraIdList.mCameraIdCombos;
+ mPrimaryRearCameraId = cameraIdList.mPrimaryRearCameraId;
+ mPrimaryFrontCameraId = cameraIdList.mPrimaryFrontCameraId;
+ } catch (ItsException e) {
+ Toast.makeText(ItsTestActivity.this,
+ "Received error from camera service while checking device capabilities: "
+ + e, Toast.LENGTH_SHORT).show();
+ }
}
super.onCreate(savedInstanceState);
- if (mToBeTestedCameraIds.size() == 0) {
- showToast(R.string.all_exempted_devices);
- ItsTestActivity.this.getReportLog().setSummary(
- "PASS: all cameras on this device are exempted from ITS"
- , 1.0, ResultType.NEUTRAL, ResultUnit.NONE);
- setTestResultAndFinish(true);
+
+ if (!mIsFoldableDevice) {
+ if (mToBeTestedCameraIds.size() == 0) {
+ showToast(R.string.all_exempted_devices);
+ ItsTestActivity.this.getReportLog().setSummary(
+ "PASS: all cameras on this device are exempted from ITS",
+ 1.0, ResultType.NEUTRAL, ResultUnit.NONE);
+ setTestResultAndFinish(true);
+ }
}
// Default locale must be set to "en-us"
Locale locale = Locale.getDefault();
if (!Locale.US.equals(locale)) {
- String toastMessage = "Unsupported default language " + locale + "! " +
- "Please switch the default language to English (United States) in " +
- "Settings > Language & input > Languages";
+ String toastMessage = "Unsupported default language " + locale + "! "
+ + "Please switch the default language to English (United States) in "
+ + "Settings > Language & input > Languages";
Toast.makeText(ItsTestActivity.this, toastMessage, Toast.LENGTH_LONG).show();
ItsTestActivity.this.getReportLog().setSummary(
"FAIL: Default language is not set to " + Locale.US,
@@ -437,6 +551,53 @@
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
+ private List<String> getCameraIdsAvailableForTesting() {
+ List<String> toBeTestedCameraIds = new ArrayList<String>();
+ List<String> availableCameraIdList = new ArrayList<String>();
+ try {
+ ItsUtils.ItsCameraIdList cameraIdList =
+ ItsUtils.getItsCompatibleCameraIds(mCameraManager);
+ toBeTestedCameraIds = cameraIdList.mCameraIdCombos;
+ mPrimaryRearCameraId = cameraIdList.mPrimaryRearCameraId;
+ mPrimaryFrontCameraId = cameraIdList.mPrimaryFrontCameraId;
+ mUnavailablePhysicalCameras = getUnavailablePhysicalCameras();
+ Log.i(TAG, "unavailablePhysicalCameras:"
+ + mUnavailablePhysicalCameras.toString());
+ for (String str : toBeTestedCameraIds) {
+ if (str.contains(".")) {
+ String[] strArr = str.split("\\.");
+ if (mUnavailablePhysicalCameras.contains(new Pair<>(strArr[0], strArr[1]))) {
+ toBeTestedCameraIds.remove(str);
+ }
+ }
+ }
+ Log.i(TAG, "AvailablePhysicalCameras to be tested:"
+ + Arrays.asList(toBeTestedCameraIds.toString()));
+ } catch (ItsException e) {
+ Log.i(TAG, "Received error from camera service while checking device capabilities: "
+ + e);
+ } catch (Exception e) {
+ Log.i(TAG, "Exception: " + e);
+ }
+
+ return toBeTestedCameraIds;
+ }
+
+ // Get camera ids available for testing for device in
+ // each state: folded and unfolded.
+ protected void getCameraIdsForFoldableDevice() {
+ boolean deviceFolded = mIsDeviceFolded;
+ try {
+ if (mIsDeviceFolded) {
+ mToBeTestedCameraIdsFolded = getCameraIdsAvailableForTesting();
+ } else {
+ mToBeTestedCameraIdsUnfolded = getCameraIdsAvailableForTesting();
+ }
+ } catch (Exception e) {
+ Log.i(TAG, "Exception: " + e);
+ }
+ }
+
@Override
public void showManualTestDialog(final DialogTestListItem test,
final DialogTestListItem.TestCallback callback) {
@@ -451,38 +612,127 @@
return "Camera_ITS_" + cam + "_" + scene;
}
+ protected boolean isFoldableDevice() {
+ Context context = this.getApplicationContext();
+ return CameraUtils.isDeviceFoldable(context);
+ }
+
+ protected boolean isDeviceFolded() {
+ return mIsDeviceFolded;
+ }
+
+ protected Set<Pair<String, String>> getUnavailablePhysicalCameras() throws ItsException {
+ final LinkedBlockingQueue<Pair<String, String>> unavailablePhysicalCamEventQueue =
+ new LinkedBlockingQueue<>();
+ mCameraThread = new HandlerThread("ItsCameraThread");
+ mCameraThread.start();
+ mCameraHandler = new Handler(mCameraThread.getLooper());
+ try {
+ CameraManager.AvailabilityCallback ac = new CameraManager.AvailabilityCallback() {
+ @Override
+ public void onPhysicalCameraUnavailable(String cameraId, String physicalCameraId) {
+ unavailablePhysicalCamEventQueue.offer(new Pair<>(cameraId, physicalCameraId));
+ }
+ };
+ mCameraManager.registerAvailabilityCallback(ac, mCameraHandler);
+ Set<Pair<String, String>> unavailablePhysicalCameras =
+ new HashSet<Pair<String, String>>();
+ Pair<String, String> candidatePhysicalIds =
+ unavailablePhysicalCamEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
+ java.util.concurrent.TimeUnit.MILLISECONDS);
+ while (candidatePhysicalIds != null) {
+ unavailablePhysicalCameras.add(candidatePhysicalIds);
+ candidatePhysicalIds =
+ unavailablePhysicalCamEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
+ java.util.concurrent.TimeUnit.MILLISECONDS);
+ }
+ mCameraManager.unregisterAvailabilityCallback(ac);
+ return unavailablePhysicalCameras;
+ } catch (Exception e) {
+ throw new ItsException("Exception: ", e);
+ }
+ }
+
protected void setupItsTests(ArrayTestListAdapter adapter) {
for (String cam : mToBeTestedCameraIds) {
- List<String> scenes = cam.contains(ItsUtils.CAMERA_ID_TOKENIZER) ?
- mHiddenPhysicalCameraSceneIds : mSceneIds;
+ List<String> scenes = cam.contains(ItsUtils.CAMERA_ID_TOKENIZER)
+ ? mHiddenPhysicalCameraSceneIds : mSceneIds;
for (String scene : scenes) {
// Add camera and scene combinations in mAllScenes to avoid adding n/a scenes for
// devices with sub-cameras.
- if(mAllScenes == null){
- mAllScenes = new TreeSet<>(mComparator);
- }
mAllScenes.add(new ResultKey(cam, scene));
adapter.add(new DialogTestListItem(this,
- testTitle(cam, scene),
- testId(cam, scene)));
+ testTitle(cam, scene),
+ testId(cam, scene)));
}
if (mExecutedMpcTests == null) {
mExecutedMpcTests = new TreeSet<>(mComparator);
}
- Log.d(TAG,"Total combinations to test on this device:" + mAllScenes.size());
+ Log.d(TAG, "Total combinations to test on this device:" + mAllScenes.size());
}
}
+ protected void setupItsTestsForFoldableDevice(ArrayTestListAdapter adapter) {
+ List<String> toBeTestedCameraIds = new ArrayList<String>();
+ if (mIsDeviceFolded) {
+ toBeTestedCameraIds = mToBeTestedCameraIdsFolded;
+ } else {
+ toBeTestedCameraIds = mToBeTestedCameraIdsUnfolded;
+ }
+
+ for (String cam : toBeTestedCameraIds) {
+ List<String> scenes = cam.contains(ItsUtils.CAMERA_ID_TOKENIZER)
+ ? mHiddenPhysicalCameraSceneIds : mSceneIds;
+ for (String scene : scenes) {
+ // Add camera and scene combinations in mAllScenes to avoid adding n/a scenes for
+ // devices with sub-cameras.
+ if (cam.contains(mPrimaryFrontCameraId) && mIsDeviceFolded) {
+ scene = scene + "_folded";
+ }
+ // Rear camera scenes will be added only once.
+ if (mAllScenes.contains(new ResultKey(cam, scene))) {
+ continue;
+ }
+ // TODO(ruchamk): Remove extra logging after testing.
+ Log.i(TAG, "Adding cam_id: " + cam + "scene: " + scene);
+ mAllScenes.add(new ResultKey(cam, scene));
+ adapter.add(new DialogTestListItem(this,
+ testTitle(cam, scene),
+ testId(cam, scene)));
+ }
+ }
+ Log.d(TAG, "Total combinations to test on this device:"
+ + mAllScenes.size() + " folded? " + mIsDeviceFolded);
+ if (mIsDeviceFolded) {
+ mFoldedTestSetupDone = true;
+ Log.i(TAG, "mFoldedTestSetupDone");
+ } else {
+ mUnfoldedTestSetupDone = true;
+ Log.i(TAG, "mUnfoldedTestSetupDone");
+ }
+ if (mFoldedTestSetupDone && mUnfoldedTestSetupDone) {
+ Log.d(TAG, "Total combinations to test on this foldable "
+ + "device for both states:" + mAllScenes.size());
+ }
+ adapter.loadTestResults();
+ }
+
@Override
protected void setupTests(ArrayTestListAdapter adapter) {
- setupItsTests(adapter);
+ mAdapter = adapter;
+ if (mIsFoldableDevice) {
+ if (mFoldedTestSetupDone && mUnfoldedTestSetupDone) {
+ Log.i(TAG, "Set up is done");
+ }
+ } else {
+ setupItsTests(adapter);
+ }
}
@Override
protected void onResume() {
super.onResume();
- CameraManager manager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE);
- if (manager == null) {
+ if (mCameraManager == null) {
showToast(R.string.no_camera_manager);
} else {
Log.d(TAG, "register ITS result receiver");