Merge "Test behavior when declaring duplicate permissions"
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/RequiredServiceRule.java b/common/device-side/util/src/com/android/compatibility/common/util/RequiredServiceRule.java
new file mode 100644
index 0000000..eee05e2
--- /dev/null
+++ b/common/device-side/util/src/com/android/compatibility/common/util/RequiredServiceRule.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compatibility.common.util;
+
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Custom JUnit4 rule that does not run a test case if the device does not have a given service.
+ */
+public class RequiredServiceRule implements TestRule {
+ private static final String TAG = "RequiredServiceRule";
+
+ private final String mService;
+ private final boolean mHasService;
+
+ /**
+ * Creates a rule for the given service.
+ */
+ public RequiredServiceRule(@NonNull String service) {
+ mService = service;
+ mHasService = hasService(service);
+ }
+
+ @Override
+ public Statement apply(@NonNull Statement base, @NonNull Description description) {
+ return new Statement() {
+
+ @Override
+ public void evaluate() throws Throwable {
+ if (!mHasService) {
+ Log.d(TAG, "skipping "
+ + description.getClassName() + "#" + description.getMethodName()
+ + " because device does not have service '" + mService + "'");
+ return;
+ }
+ base.evaluate();
+ }
+ };
+ }
+
+ private static boolean hasService(@NonNull String service) {
+ // TODO: ideally should call SystemServiceManager directly, but we would need to open
+ // some @Testing APIs for that.
+ String command = "service check " + service;
+ try {
+ String commandOutput = SystemUtil.runShellCommand(
+ InstrumentationRegistry.getInstrumentation(), command);
+ return !commandOutput.contains("not found");
+ } catch (Exception e) {
+ Log.w(TAG, "Exception running '" + command + "': " + e);
+ return false;
+ }
+ }
+}
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
index 32fffdb..5cc78d2 100644
--- a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
@@ -574,7 +574,7 @@
}
private void checkDataConnection(String[] parts) {
- assertEquals(25, parts.length);
+ assertEquals(26, parts.length);
assertInteger(parts[4]); // none
assertInteger(parts[5]); // gprs
assertInteger(parts[6]); // edge
@@ -595,7 +595,8 @@
assertInteger(parts[21]); // td_scdma
assertInteger(parts[22]); // iwlan
assertInteger(parts[23]); // lte_ca
- assertInteger(parts[24]); // other
+ assertInteger(parts[24]); // nr
+ assertInteger(parts[25]); // other
}
private void checkWifiState(String[] parts) {
diff --git a/tests/JobScheduler/AndroidTest.xml b/tests/JobScheduler/AndroidTest.xml
index 4820aa2..83365d7 100644
--- a/tests/JobScheduler/AndroidTest.xml
+++ b/tests/JobScheduler/AndroidTest.xml
@@ -21,6 +21,10 @@
<option name="test-file-name" value="CtsJobSchedulerTestCases.apk" />
<option name="test-file-name" value="CtsJobTestApp.apk" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="cmd thermalservice override-status 0" />
+ <option name="teardown-command" value="cmd thermalservice reset" />
+ </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.jobscheduler.cts" />
<option name="runtime-hint" value="2m" />
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/ConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/ConstraintTest.java
index 75f312a..f3a895e 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/ConstraintTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/ConstraintTest.java
@@ -105,7 +105,6 @@
@Override
public void setUp() throws Exception {
super.setUp();
- SystemUtil.runShellCommand(getInstrumentation(), "cmd thermalservice override-status 0");
kTestEnvironment.setUp();
kTriggerTestEnvironment.setUp();
mJobScheduler.cancelAll();
@@ -118,7 +117,6 @@
SystemUtil.runShellCommand(getInstrumentation(), "cmd devicestoragemonitor reset");
mStorageStateChanged = false;
}
- SystemUtil.runShellCommand(getInstrumentation(), "cmd thermalservice reset");
}
/**
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
index 4bbf328..ffe684d 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
@@ -119,8 +119,6 @@
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- // Lock thermal service to not throttling
- mUiDevice.executeShellCommand("cmd thermalservice override-status 0");
mPowerManager = mContext.getSystemService(PowerManager.class);
mDeviceInDoze = mPowerManager.isDeviceIdleMode();
mTestPackageUid = mContext.getPackageManager().getPackageUid(TEST_APP_PACKAGE, 0);
@@ -275,8 +273,6 @@
Thread.sleep(500); // To avoid any race between unregister and the next register in setUp
waitUntilTestAppNotInTempWhitelist();
- // Reset thermal service
- mUiDevice.executeShellCommand("cmd thermalservice reset");
}
private boolean isTestAppTempWhitelisted() throws Exception {
diff --git a/tests/JobSchedulerSharedUid/AndroidTest.xml b/tests/JobSchedulerSharedUid/AndroidTest.xml
index db88860..183551d 100644
--- a/tests/JobSchedulerSharedUid/AndroidTest.xml
+++ b/tests/JobSchedulerSharedUid/AndroidTest.xml
@@ -23,6 +23,10 @@
<option name="test-file-name" value="CtsJobSchedulerSharedUid.apk" />
<option name="test-file-name" value="CtsJobSharedUidTestApp.apk" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="cmd thermalservice override-status 0" />
+ <option name="teardown-command" value="cmd thermalservice reset" />
+ </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.jobscheduler.cts.shareduidtests" />
<option name="runtime-hint" value="2m" />
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
index f263d24..c86cbdf 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
@@ -72,7 +72,6 @@
import android.platform.test.annotations.AppModeFull;
import android.platform.test.annotations.Presubmit;
import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.FlakyTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.MediumTest;
@@ -193,7 +192,6 @@
assertNotNull("Did not receive expected event: " + expected, awaitedEvent);
}
- @FlakyTest(bugId = 116260122)
@MediumTest
@Presubmit
@Test
@@ -364,7 +362,6 @@
assertNotNull("Did not receive expected event: " + expected, awaitedTextChangeEvent);
}
- @FlakyTest(bugId = 114543540)
@MediumTest
@Presubmit
@Test
@@ -403,7 +400,6 @@
assertNotNull("Did not receive expected event: " + expected, awaitedEvent);
}
- @FlakyTest(bugId = 114543540)
@MediumTest
@AppModeFull
@SuppressWarnings("deprecation")
@@ -553,7 +549,6 @@
}
@AppModeFull
- @FlakyTest(bugId = 116260122)
@MediumTest
@Presubmit
@Test
@@ -633,7 +628,6 @@
}
}
- @FlakyTest(bugId = 114543540)
@MediumTest
@Presubmit
@Test
@@ -663,7 +657,6 @@
editTextNode.isHeading());
}
- @FlakyTest(bugId = 116260122)
@MediumTest
@Presubmit
@Test
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java
index ecf34b9..06da001 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java
@@ -33,7 +33,6 @@
import android.app.UiAutomation;
import android.platform.test.annotations.Presubmit;
import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.FlakyTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.MediumTest;
@@ -90,7 +89,6 @@
sInstrumentation, sUiAutomation, mActivityRule);
}
- @FlakyTest(bugId = 114543540)
@MediumTest
@Presubmit
@Test
@@ -190,7 +188,6 @@
assertFalse(rootLinearLayout.isAccessibilityFocused());
}
- @FlakyTest(bugId = 116260122)
@MediumTest
@Presubmit
@Test
diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index af1b555..995a2c7 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -35,6 +35,8 @@
import android.hardware.camera2.params.InputConfiguration;
import android.hardware.camera2.params.OisSample;
import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.MandatoryStreamCombination;
+import android.hardware.camera2.params.MandatoryStreamCombination.MandatoryStreamInformation;
import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.CamcorderProfile;
@@ -160,10 +162,10 @@
}
/**
- * Test for making sure the required output combinations for each hardware level and capability
- * work as expected.
+ * Test for making sure the required logical output combinations for each hardware level and
+ * capability work as expected.
*/
- public void testMandatoryOutputCombinations() throws Exception {
+ public void testMandatoryLogicalOutputCombinations() throws Exception {
/**
* Tables for maximum sizes to try for each hardware level and capability.
*
@@ -173,59 +175,6 @@
* Each row of the table is a set of (format, max resolution) pairs, using the below consts
*/
- // Enum values are defined in MaxStreamSizes
- final int[][] LEGACY_COMBINATIONS = {
- // Simple preview, GPU video processing, or no-preview video recording
- {PRIV, MAXIMUM},
- // No-viewfinder still image capture
- {JPEG, MAXIMUM},
- // In-application video/image processing
- {YUV, MAXIMUM},
- // Standard still imaging.
- {PRIV, PREVIEW, JPEG, MAXIMUM},
- // In-app processing plus still capture.
- {YUV, PREVIEW, JPEG, MAXIMUM},
- // Standard recording.
- {PRIV, PREVIEW, PRIV, PREVIEW},
- // Preview plus in-app processing.
- {PRIV, PREVIEW, YUV, PREVIEW},
- // Still capture plus in-app processing.
- {PRIV, PREVIEW, YUV, PREVIEW, JPEG, MAXIMUM}
- };
-
- final int[][] LIMITED_COMBINATIONS = {
- // High-resolution video recording with preview.
- {PRIV, PREVIEW, PRIV, RECORD },
- // High-resolution in-app video processing with preview.
- {PRIV, PREVIEW, YUV , RECORD },
- // Two-input in-app video processing.
- {YUV , PREVIEW, YUV , RECORD },
- // High-resolution recording with video snapshot.
- {PRIV, PREVIEW, PRIV, RECORD, JPEG, RECORD },
- // High-resolution in-app processing with video snapshot.
- {PRIV, PREVIEW, YUV, RECORD, JPEG, RECORD },
- // Two-input in-app processing with still capture.
- {YUV , PREVIEW, YUV, PREVIEW, JPEG, MAXIMUM }
- };
-
- final int[][] BURST_COMBINATIONS = {
- // Maximum-resolution GPU processing with preview.
- {PRIV, PREVIEW, PRIV, MAXIMUM },
- // Maximum-resolution in-app processing with preview.
- {PRIV, PREVIEW, YUV, MAXIMUM },
- // Maximum-resolution two-input in-app processsing.
- {YUV, PREVIEW, YUV, MAXIMUM },
- };
-
- final int[][] FULL_COMBINATIONS = {
- // Video recording with maximum-size video snapshot.
- {PRIV, PREVIEW, PRIV, PREVIEW, JPEG, MAXIMUM },
- // Standard video recording plus maximum-resolution in-app processing.
- {YUV, VGA, PRIV, PREVIEW, YUV, MAXIMUM },
- // Preview plus two-input maximum-resolution in-app processing.
- {YUV, VGA, YUV, PREVIEW, YUV, MAXIMUM }
- };
-
final int[][] RAW_COMBINATIONS = {
// No-preview DNG capture.
{RAW, MAXIMUM },
@@ -245,16 +194,7 @@
{YUV, PREVIEW, JPEG, MAXIMUM, RAW, MAXIMUM}
};
- final int[][] LEVEL_3_COMBINATIONS = {
- // In-app viewfinder analysis with dynamic selection of output format
- {PRIV, PREVIEW, PRIV, VGA, YUV, MAXIMUM, RAW, MAXIMUM},
- // In-app viewfinder analysis with dynamic selection of output format
- {PRIV, PREVIEW, PRIV, VGA, JPEG, MAXIMUM, RAW, MAXIMUM}
- };
-
- final int[][][] TABLES =
- { LEGACY_COMBINATIONS, LIMITED_COMBINATIONS, BURST_COMBINATIONS, FULL_COMBINATIONS,
- RAW_COMBINATIONS, LEVEL_3_COMBINATIONS };
+ final int[][][] TABLES = { RAW_COMBINATIONS };
sanityCheckConfigurationTables(TABLES);
@@ -271,58 +211,13 @@
Log.v(TAG, "StreamConfigurationMap: " + streamConfigurationMapString);
}
- // Always run legacy-level tests for color-supporting devices
-
- if (mStaticInfo.isColorOutputSupported()) {
- for (int[] config : LEGACY_COMBINATIONS) {
- testOutputCombination(id, config, maxSizes);
- }
- }
-
// Then run higher-level tests if applicable
-
if (!mStaticInfo.isHardwareLevelLegacy()) {
-
- // If not legacy, at least limited, so run limited-level tests
-
- if (mStaticInfo.isColorOutputSupported()) {
- for (int[] config : LIMITED_COMBINATIONS) {
- testOutputCombination(id, config, maxSizes);
- }
- }
-
- // Check for BURST_CAPTURE, FULL and RAW and run those if appropriate
-
- if (mStaticInfo.isCapabilitySupported(
- CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) {
- for (int[] config : BURST_COMBINATIONS) {
- testOutputCombination(id, config, maxSizes);
- }
- }
-
- if (mStaticInfo.isHardwareLevelAtLeastFull()) {
- for (int[] config : FULL_COMBINATIONS) {
- testOutputCombination(id, config, maxSizes);
- }
- }
-
- if (mStaticInfo.isCapabilitySupported(
- CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
- for (int[] config : RAW_COMBINATIONS) {
- testOutputCombination(id, config, maxSizes);
- }
- } else if (mStaticInfo.isLogicalMultiCamera()) {
+ if (mStaticInfo.isLogicalMultiCamera()) {
for (int[] config : RAW_COMBINATIONS) {
testMultiCameraOutputCombination(id, config, maxSizes);
}
}
-
- if (mStaticInfo.isHardwareLevelAtLeast(
- CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3)) {
- for (int[] config: LEVEL_3_COMBINATIONS) {
- testOutputCombination(id, config, maxSizes);
- }
- }
}
closeDevice(id);
@@ -330,105 +225,24 @@
}
/**
- * Test for making sure the required reprocess input/output combinations for each hardware
- * level and capability work as expected.
+ * Test for making sure the mandatory stream combinations work as expected.
*/
- public void testMandatoryReprocessConfigurations() throws Exception {
-
- /**
- * For each stream combination, verify that
- * 1. A reprocessable session can be created using the stream combination.
- * 2. Reprocess capture requests targeting YUV and JPEG outputs are successful.
- */
- final int[][] LIMITED_COMBINATIONS = {
- // Input Outputs
- {PRIV, MAXIMUM, JPEG, MAXIMUM},
- {YUV , MAXIMUM, JPEG, MAXIMUM},
- {PRIV, MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM},
- {YUV , MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM},
- {PRIV, MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM},
- {YUV , MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM},
- {PRIV, MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
- {YUV, MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
- };
-
- final int[][] FULL_COMBINATIONS = {
- // Input Outputs
- {YUV , MAXIMUM, PRIV, PREVIEW},
- {YUV , MAXIMUM, YUV , PREVIEW},
- {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , RECORD},
- {YUV , MAXIMUM, PRIV, PREVIEW, YUV , RECORD},
- {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , MAXIMUM},
- {PRIV, MAXIMUM, YUV , PREVIEW, YUV , MAXIMUM},
- {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
- {YUV , MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
- };
-
- final int[][] RAW_COMBINATIONS = {
- // Input Outputs
- {PRIV, MAXIMUM, YUV , PREVIEW, RAW , MAXIMUM},
- {YUV , MAXIMUM, YUV , PREVIEW, RAW , MAXIMUM},
- {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
- {YUV , MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
- {PRIV, MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
- {YUV , MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
- {PRIV, MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
- {YUV , MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
- {PRIV, MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
- {YUV , MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
- };
-
- final int[][] LEVEL_3_COMBINATIONS = {
- // Input Outputs
- // In-app viewfinder analysis with YUV->YUV ZSL and RAW
- {YUV , MAXIMUM, PRIV, PREVIEW, PRIV, VGA, RAW, MAXIMUM},
- // In-app viewfinder analysis with PRIV->JPEG ZSL and RAW
- {PRIV, MAXIMUM, PRIV, PREVIEW, PRIV, VGA, RAW, MAXIMUM, JPEG, MAXIMUM},
- // In-app viewfinder analysis with YUV->JPEG ZSL and RAW
- {YUV , MAXIMUM, PRIV, PREVIEW, PRIV, VGA, RAW, MAXIMUM, JPEG, MAXIMUM},
- };
-
- final int[][][] TABLES =
- { LIMITED_COMBINATIONS, FULL_COMBINATIONS, RAW_COMBINATIONS, LEVEL_3_COMBINATIONS };
-
- sanityCheckConfigurationTables(TABLES);
-
+ public void testMandatoryOutputCombinations() throws Exception {
for (String id : mCameraIds) {
- CameraCharacteristics cc = mCameraManager.getCameraCharacteristics(id);
- StaticMetadata staticInfo = new StaticMetadata(cc);
- MaxStreamSizes maxSizes = new MaxStreamSizes(staticInfo, id, getContext());
-
- // Skip the test for legacy devices.
- if (staticInfo.isHardwareLevelLegacy()) {
+ openDevice(id);
+ MandatoryStreamCombination[] combinations =
+ mStaticInfo.getCharacteristics().get(
+ CameraCharacteristics.SCALER_MANDATORY_STREAM_COMBINATIONS);
+ if (combinations == null) {
+ Log.i(TAG, "No mandatory stream combinations for camera: " + id + " skip test");
+ closeDevice(id);
continue;
}
- openDevice(id);
-
try {
- for (int[] config : LIMITED_COMBINATIONS) {
- testReprocessStreamCombination(id, config, maxSizes, staticInfo);
- }
-
- // Check FULL devices
- if (staticInfo.isHardwareLevelAtLeastFull()) {
- for (int[] config : FULL_COMBINATIONS) {
- testReprocessStreamCombination(id, config, maxSizes, staticInfo);
- }
- }
-
- // Check devices with RAW capability.
- if (staticInfo.isCapabilitySupported(
- CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
- for (int[] config : RAW_COMBINATIONS) {
- testReprocessStreamCombination(id, config, maxSizes, staticInfo);
- }
- }
-
- if (mStaticInfo.isHardwareLevelAtLeast(
- CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3)) {
- for (int[] config: LEVEL_3_COMBINATIONS) {
- testReprocessStreamCombination(id, config, maxSizes, staticInfo);
+ for (MandatoryStreamCombination combination : combinations) {
+ if (!combination.isReprocessable()) {
+ testMandatoryStreamCombination(id, combination);
}
}
} finally {
@@ -437,6 +251,568 @@
}
}
+ private void setupConfigurationTargets(List<MandatoryStreamInformation> streamsInfo,
+ List<SurfaceTexture> privTargets, List<ImageReader> jpegTargets,
+ List<ImageReader> yuvTargets, List<ImageReader> y8Targets,
+ List<ImageReader> rawTargets, List<OutputConfiguration> outputConfigs,
+ int numBuffers, boolean substituteY8, MandatoryStreamInformation overrideStreamInfo,
+ List<String> overridePhysicalCameraIds, List<Size> overridePhysicalCameraSizes) {
+
+ ImageDropperListener imageDropperListener = new ImageDropperListener();
+
+ for (MandatoryStreamInformation streamInfo : streamsInfo) {
+ if (streamInfo.isInput()) {
+ continue;
+ }
+ int format = streamInfo.getFormat();
+ if (substituteY8 && (format == ImageFormat.YUV_420_888)) {
+ format = ImageFormat.Y8;
+ }
+ Surface newSurface;
+ Size[] availableSizes = new Size[streamInfo.getAvailableSizes().size()];
+ availableSizes = streamInfo.getAvailableSizes().toArray(availableSizes);
+ Size targetSize = CameraTestUtils.getMaxSize(availableSizes);
+
+ int numConfigs = 1;
+ if ((overrideStreamInfo == streamInfo) && overridePhysicalCameraIds != null &&
+ overridePhysicalCameraIds.size() > 1) {
+ numConfigs = overridePhysicalCameraIds.size();
+ }
+ for (int j = 0; j < numConfigs; j++) {
+ targetSize = (numConfigs == 1) ? targetSize : overridePhysicalCameraSizes.get(j);
+ switch (format) {
+ case ImageFormat.PRIVATE: {
+ SurfaceTexture target = new SurfaceTexture(/*random int*/1);
+ target.setDefaultBufferSize(targetSize.getWidth(), targetSize.getHeight());
+ OutputConfiguration config = new OutputConfiguration(new Surface(target));
+ if (numConfigs > 1) {
+ config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
+ }
+ outputConfigs.add(config);
+ privTargets.add(target);
+ break;
+ }
+ case ImageFormat.JPEG: {
+ ImageReader target = ImageReader.newInstance(targetSize.getWidth(),
+ targetSize.getHeight(), format, numBuffers);
+ target.setOnImageAvailableListener(imageDropperListener, mHandler);
+ OutputConfiguration config = new OutputConfiguration(target.getSurface());
+ if (numConfigs > 1) {
+ config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
+ }
+ outputConfigs.add(config);
+ jpegTargets.add(target);
+ break;
+ }
+ case ImageFormat.YUV_420_888: {
+ ImageReader target = ImageReader.newInstance(targetSize.getWidth(),
+ targetSize.getHeight(), format, numBuffers);
+ target.setOnImageAvailableListener(imageDropperListener, mHandler);
+ OutputConfiguration config = new OutputConfiguration(target.getSurface());
+ if (numConfigs > 1) {
+ config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
+ }
+ outputConfigs.add(config);
+ yuvTargets.add(target);
+ break;
+ }
+ case ImageFormat.Y8: {
+ ImageReader target = ImageReader.newInstance(targetSize.getWidth(),
+ targetSize.getHeight(), format, numBuffers);
+ target.setOnImageAvailableListener(imageDropperListener, mHandler);
+ OutputConfiguration config = new OutputConfiguration(target.getSurface());
+ if (numConfigs > 1) {
+ config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
+ }
+ outputConfigs.add(config);
+ y8Targets.add(target);
+ break;
+ }
+ case ImageFormat.RAW_SENSOR: {
+ // targetSize could be null in the logical camera case where only
+ // physical camera supports RAW stream.
+ if (targetSize != null) {
+ ImageReader target = ImageReader.newInstance(targetSize.getWidth(),
+ targetSize.getHeight(), format, numBuffers);
+ target.setOnImageAvailableListener(imageDropperListener, mHandler);
+ OutputConfiguration config =
+ new OutputConfiguration(target.getSurface());
+ if (numConfigs > 1) {
+ config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
+ }
+ outputConfigs.add(config);
+ rawTargets.add(target);
+ }
+ break;
+ }
+ default:
+ fail("Unknown output format " + format);
+ }
+ }
+ }
+ }
+
+ private void testMandatoryStreamCombination(String cameraId,
+ MandatoryStreamCombination combination) throws Exception {
+ // Check whether substituting YUV_888 format with Y8 format
+ boolean substituteY8 = false;
+ if (mStaticInfo.isMonochromeWithY8()) {
+ List<MandatoryStreamInformation> streamsInfo = combination.getStreamsInformation();
+ for (MandatoryStreamInformation streamInfo : streamsInfo) {
+ if (streamInfo.getFormat() == ImageFormat.YUV_420_888) {
+ substituteY8 = true;
+ break;
+ }
+ }
+ }
+
+ // Test camera output combination
+ Log.i(TAG, "Testing mandatory stream combination: " + combination.getDescription() +
+ " on camera: " + cameraId);
+ testMandatoryStreamCombination(cameraId, combination, /*substituteY8*/false);
+
+ if (substituteY8) {
+ testMandatoryStreamCombination(cameraId, combination, substituteY8);
+ }
+
+ // Test substituting YUV_888/RAW with physical streams for logical camera
+ if (mStaticInfo.isLogicalMultiCamera()) {
+ Log.i(TAG, String.format("Testing logical Camera %s, combination: %s",
+ cameraId, combination.getDescription()));
+
+ testMultiCameraOutputCombination(cameraId, combination, /*substituteY8*/false);
+
+ if (substituteY8) {
+ testMultiCameraOutputCombination(cameraId, combination, substituteY8);
+ }
+ }
+ }
+
+ private void testMultiCameraOutputCombination(String cameraId,
+ MandatoryStreamCombination combination, boolean substituteY8) throws Exception {
+
+ // Timeout is relaxed by 1 second for LEGACY devices to reduce false positive rate in CTS
+ final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 2000 : 1000;
+ final int MIN_RESULT_COUNT = 3;
+ Set<String> physicalCameraIds = mStaticInfo.getCharacteristics().getPhysicalCameraIds();
+
+ List<MandatoryStreamInformation> streamsInfo = combination.getStreamsInformation();
+ for (MandatoryStreamInformation streamInfo : streamsInfo) {
+ int format = streamInfo.getFormat();
+ if (substituteY8 && (format == ImageFormat.YUV_420_888)) {
+ format = ImageFormat.Y8;
+ }
+ if (format != ImageFormat.YUV_420_888 && format != ImageFormat.Y8 &&
+ format != ImageFormat.RAW_SENSOR) {
+ continue;
+ }
+
+ // Find physical cameras with matching size.
+ Size[] availableSizes = new Size[streamInfo.getAvailableSizes().size()];
+ availableSizes = streamInfo.getAvailableSizes().toArray(availableSizes);
+ Size targetSize = CameraTestUtils.getMaxSize(availableSizes);
+
+ List<String> physicalCamerasForSize = new ArrayList<String>();
+ List<Size> physicalCameraSizes = new ArrayList<Size>();
+ for (String physicalId : physicalCameraIds) {
+ Size[] sizes = mAllStaticInfo.get(physicalId).getAvailableSizesForFormatChecked(
+ format, StaticMetadata.StreamDirection.Output);
+ if (targetSize != null) {
+ if (Arrays.asList(sizes).contains(targetSize)) {
+ physicalCameraSizes.add(targetSize);
+ physicalCamerasForSize.add(physicalId);
+ }
+ } else if (format == ImageFormat.RAW_SENSOR && sizes.length > 0) {
+ physicalCamerasForSize.add(physicalId);
+ physicalCameraSizes.add(CameraTestUtils.getMaxSize(sizes));
+ }
+ if (physicalCamerasForSize.size() == 2) {
+ break;
+ }
+ }
+ if (physicalCamerasForSize.size() < 2) {
+ continue;
+ }
+
+ // Set up outputs
+ List<OutputConfiguration> outputConfigs = new ArrayList<OutputConfiguration>();
+ List<SurfaceTexture> privTargets = new ArrayList<SurfaceTexture>();
+ List<ImageReader> jpegTargets = new ArrayList<ImageReader>();
+ List<ImageReader> yuvTargets = new ArrayList<ImageReader>();
+ List<ImageReader> y8Targets = new ArrayList<ImageReader>();
+ List<ImageReader> rawTargets = new ArrayList<ImageReader>();
+
+ setupConfigurationTargets(streamsInfo, privTargets, jpegTargets, yuvTargets,
+ y8Targets, rawTargets, outputConfigs, MIN_RESULT_COUNT, substituteY8,
+ streamInfo, physicalCamerasForSize, physicalCameraSizes);
+
+ boolean haveSession = false;
+ try {
+ CaptureRequest.Builder requestBuilder =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+ for (OutputConfiguration c : outputConfigs) {
+ requestBuilder.addTarget(c.getSurface());
+ }
+
+ CameraCaptureSession.CaptureCallback mockCaptureCallback =
+ mock(CameraCaptureSession.CaptureCallback.class);
+
+ assertTrue(String.format("Session configuration query %s failed",
+ combination.getDescription()),
+ checkSessionConfiguration(mCamera, mHandler, outputConfigs,
+ /*inputConfig*/ null, SessionConfiguration.SESSION_REGULAR,
+ /*expectedResult*/ true));
+
+ createSessionByConfigs(outputConfigs);
+ haveSession = true;
+ CaptureRequest request = requestBuilder.build();
+ mCameraSession.setRepeatingRequest(request, mockCaptureCallback, mHandler);
+
+ verify(mockCaptureCallback,
+ timeout(TIMEOUT_FOR_RESULT_MS * MIN_RESULT_COUNT).atLeast(MIN_RESULT_COUNT))
+ .onCaptureCompleted(
+ eq(mCameraSession),
+ eq(request),
+ isA(TotalCaptureResult.class));
+ verify(mockCaptureCallback, never()).
+ onCaptureFailed(
+ eq(mCameraSession),
+ eq(request),
+ isA(CaptureFailure.class));
+
+ } catch (Throwable e) {
+ mCollector.addMessage(String.format("Output combination: %s failed due to: %s",
+ combination.getDescription(), e.getMessage()));
+ }
+ if (haveSession) {
+ try {
+ Log.i(TAG, String.format("Done camera %s, combination: %s, closing session",
+ cameraId, combination.getDescription()));
+ stopCapture(/*fast*/false);
+ } catch (Throwable e) {
+ mCollector.addMessage(
+ String.format("Closing down for output combination: %s failed due to: %s",
+ combination.getDescription(), e.getMessage()));
+ }
+ }
+
+ for (SurfaceTexture target : privTargets) {
+ target.release();
+ }
+ for (ImageReader target : jpegTargets) {
+ target.close();
+ }
+ for (ImageReader target : yuvTargets) {
+ target.close();
+ }
+ for (ImageReader target : y8Targets) {
+ target.close();
+ }
+ for (ImageReader target : rawTargets) {
+ target.close();
+ }
+ }
+ }
+
+ private void testMandatoryStreamCombination(String cameraId,
+ MandatoryStreamCombination combination, boolean substituteY8) throws Exception {
+
+ // Timeout is relaxed by 1 second for LEGACY devices to reduce false positive rate in CTS
+ final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 2000 : 1000;
+ final int MIN_RESULT_COUNT = 3;
+
+ // Set up outputs
+ List<OutputConfiguration> outputConfigs = new ArrayList<OutputConfiguration>();
+ List<SurfaceTexture> privTargets = new ArrayList<SurfaceTexture>();
+ List<ImageReader> jpegTargets = new ArrayList<ImageReader>();
+ List<ImageReader> yuvTargets = new ArrayList<ImageReader>();
+ List<ImageReader> y8Targets = new ArrayList<ImageReader>();
+ List<ImageReader> rawTargets = new ArrayList<ImageReader>();
+
+ setupConfigurationTargets(combination.getStreamsInformation(), privTargets, jpegTargets,
+ yuvTargets, y8Targets, rawTargets, outputConfigs, MIN_RESULT_COUNT, substituteY8,
+ null /*overrideStreamInfo*/, null /*overridePhysicalCameraIds*/,
+ null /* overridePhysicalCameraSizes) */);
+
+ boolean haveSession = false;
+ try {
+ CaptureRequest.Builder requestBuilder =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+ for (OutputConfiguration c : outputConfigs) {
+ requestBuilder.addTarget(c.getSurface());
+ }
+
+ CameraCaptureSession.CaptureCallback mockCaptureCallback =
+ mock(CameraCaptureSession.CaptureCallback.class);
+
+ assertTrue(String.format("Session configuration query fro combination: %s failed",
+ combination.getDescription()), checkSessionConfiguration(mCamera,
+ mHandler, outputConfigs, /*inputConfig*/ null,
+ SessionConfiguration.SESSION_REGULAR, /*expectedResult*/ true));
+
+ createSessionByConfigs(outputConfigs);
+ haveSession = true;
+ CaptureRequest request = requestBuilder.build();
+ mCameraSession.setRepeatingRequest(request, mockCaptureCallback, mHandler);
+
+ verify(mockCaptureCallback,
+ timeout(TIMEOUT_FOR_RESULT_MS * MIN_RESULT_COUNT).atLeast(MIN_RESULT_COUNT))
+ .onCaptureCompleted(
+ eq(mCameraSession),
+ eq(request),
+ isA(TotalCaptureResult.class));
+ verify(mockCaptureCallback, never()).
+ onCaptureFailed(
+ eq(mCameraSession),
+ eq(request),
+ isA(CaptureFailure.class));
+
+ } catch (Throwable e) {
+ mCollector.addMessage(String.format("Mandatory stream combination: %s failed due: %s",
+ combination.getDescription(), e.getMessage()));
+ }
+ if (haveSession) {
+ try {
+ Log.i(TAG, String.format("Done with camera %s, combination: %s, closing session",
+ cameraId, combination.getDescription()));
+ stopCapture(/*fast*/false);
+ } catch (Throwable e) {
+ mCollector.addMessage(
+ String.format("Closing down for combination: %s failed due to: %s",
+ combination.getDescription(), e.getMessage()));
+ }
+ }
+
+ for (SurfaceTexture target : privTargets) {
+ target.release();
+ }
+ for (ImageReader target : jpegTargets) {
+ target.close();
+ }
+ for (ImageReader target : yuvTargets) {
+ target.close();
+ }
+ for (ImageReader target : y8Targets) {
+ target.close();
+ }
+ for (ImageReader target : rawTargets) {
+ target.close();
+ }
+ }
+
+ /**
+ * Test for making sure the required reprocess input/output combinations for each hardware
+ * level and capability work as expected.
+ */
+ public void testMandatoryReprocessConfigurations() throws Exception {
+ for (String id : mCameraIds) {
+ openDevice(id);
+ MandatoryStreamCombination[] combinations =
+ mStaticInfo.getCharacteristics().get(
+ CameraCharacteristics.SCALER_MANDATORY_STREAM_COMBINATIONS);
+ if (combinations == null) {
+ Log.i(TAG, "No mandatory stream combinations for camera: " + id + " skip test");
+ closeDevice(id);
+ continue;
+ }
+
+ try {
+ for (MandatoryStreamCombination combination : combinations) {
+ if (combination.isReprocessable()) {
+ Log.i(TAG, "Testing mandatory reprocessable stream combination: " +
+ combination.getDescription() + " on camera: " + id);
+ testMandatoryReprocessableStreamCombination(id, combination);
+ }
+ }
+ } finally {
+ closeDevice(id);
+ }
+ }
+ }
+
+ private void testMandatoryReprocessableStreamCombination(String cameraId,
+ MandatoryStreamCombination combination) {
+ // Test reprocess stream combination
+ testMandatoryReprocessableStreamCombination(cameraId, combination, /*substituteY8*/false);
+
+ // Test substituting YUV_888 format with Y8 format in reprocess stream combination.
+ if (mStaticInfo.isMonochromeWithY8()) {
+ List<MandatoryStreamInformation> streamsInfo = combination.getStreamsInformation();
+ boolean hasY8 = false;
+ for (MandatoryStreamInformation streamInfo : streamsInfo) {
+ if (streamInfo.getFormat() == ImageFormat.YUV_420_888) {
+ hasY8 = true;
+ break;
+ }
+ }
+ if (hasY8) {
+ testMandatoryReprocessableStreamCombination(cameraId, combination, hasY8);
+ }
+ }
+ }
+
+ private void testMandatoryReprocessableStreamCombination(String cameraId,
+ MandatoryStreamCombination combination, boolean substituteY8) {
+
+ final int TIMEOUT_FOR_RESULT_MS = 3000;
+ final int NUM_REPROCESS_CAPTURES_PER_CONFIG = 3;
+
+ List<SurfaceTexture> privTargets = new ArrayList<>();
+ List<ImageReader> jpegTargets = new ArrayList<>();
+ List<ImageReader> yuvTargets = new ArrayList<>();
+ List<ImageReader> y8Targets = new ArrayList<>();
+ List<ImageReader> rawTargets = new ArrayList<>();
+ ArrayList<Surface> outputSurfaces = new ArrayList<>();
+ List<OutputConfiguration> outputConfigs = new ArrayList<OutputConfiguration>();
+ ImageReader inputReader = null;
+ ImageWriter inputWriter = null;
+ SimpleImageReaderListener inputReaderListener = new SimpleImageReaderListener();
+ SimpleCaptureCallback inputCaptureListener = new SimpleCaptureCallback();
+ SimpleCaptureCallback reprocessOutputCaptureListener = new SimpleCaptureCallback();
+
+ List<MandatoryStreamInformation> streamInfo = combination.getStreamsInformation();
+ assertTrue("Reprocessable stream combinations should have at least 3 or more streams",
+ (streamInfo != null) && (streamInfo.size() >= 3));
+
+ assertTrue("The first mandatory stream information in a reprocessable combination must " +
+ "always be input", streamInfo.get(0).isInput());
+
+ List<Size> inputSizes = streamInfo.get(0).getAvailableSizes();
+ int inputFormat = streamInfo.get(0).getFormat();
+ if (substituteY8 && (inputFormat == ImageFormat.YUV_420_888)) {
+ inputFormat = ImageFormat.Y8;
+ }
+
+ try {
+ // The second stream information entry is the ZSL stream, which is configured
+ // separately.
+ setupConfigurationTargets(streamInfo.subList(2, streamInfo.size()), privTargets,
+ jpegTargets, yuvTargets, y8Targets, rawTargets, outputConfigs,
+ NUM_REPROCESS_CAPTURES_PER_CONFIG, substituteY8, null /*overrideStreamInfo*/,
+ null /*overridePhysicalCameraIds*/, null /* overridePhysicalCameraSizes) */);
+
+ outputSurfaces.ensureCapacity(outputConfigs.size());
+ for (OutputConfiguration config : outputConfigs) {
+ outputSurfaces.add(config.getSurface());
+ }
+
+ InputConfiguration inputConfig = new InputConfiguration(inputSizes.get(0).getWidth(),
+ inputSizes.get(0).getHeight(), inputFormat);
+
+ // For each config, YUV and JPEG outputs will be tested. (For YUV/Y8 reprocessing,
+ // the YUV/Y8 ImageReader for input is also used for output.)
+ final boolean inputIsYuv = inputConfig.getFormat() == ImageFormat.YUV_420_888;
+ final boolean inputIsY8 = inputConfig.getFormat() == ImageFormat.Y8;
+ final boolean useYuv = inputIsYuv || yuvTargets.size() > 0;
+ final boolean useY8 = inputIsY8 || y8Targets.size() > 0;
+ final int totalNumReprocessCaptures = NUM_REPROCESS_CAPTURES_PER_CONFIG * (
+ ((inputIsYuv || inputIsY8) ? 1 : 0) +
+ jpegTargets.size() + (useYuv ? yuvTargets.size() : y8Targets.size()));
+
+ // It needs 1 input buffer for each reprocess capture + the number of buffers
+ // that will be used as outputs.
+ inputReader = ImageReader.newInstance(inputConfig.getWidth(), inputConfig.getHeight(),
+ inputConfig.getFormat(),
+ totalNumReprocessCaptures + NUM_REPROCESS_CAPTURES_PER_CONFIG);
+ inputReader.setOnImageAvailableListener(inputReaderListener, mHandler);
+ outputSurfaces.add(inputReader.getSurface());
+
+ assertTrue(String.format("Session configuration query %s failed",
+ combination.getDescription()),
+ checkSessionConfigurationWithSurfaces(mCamera, mHandler, outputSurfaces,
+ inputConfig, SessionConfiguration.SESSION_REGULAR, /*expectedResult*/ true));
+
+ // Verify we can create a reprocessable session with the input and all outputs.
+ BlockingSessionCallback sessionListener = new BlockingSessionCallback();
+ CameraCaptureSession session = configureReprocessableCameraSession(mCamera,
+ inputConfig, outputSurfaces, sessionListener, mHandler);
+ inputWriter = ImageWriter.newInstance(session.getInputSurface(),
+ totalNumReprocessCaptures);
+
+ // Prepare a request for reprocess input
+ CaptureRequest.Builder builder = mCamera.createCaptureRequest(
+ CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
+ builder.addTarget(inputReader.getSurface());
+
+ for (int i = 0; i < totalNumReprocessCaptures; i++) {
+ session.capture(builder.build(), inputCaptureListener, mHandler);
+ }
+
+ List<CaptureRequest> reprocessRequests = new ArrayList<>();
+ List<Surface> reprocessOutputs = new ArrayList<>();
+ if (inputIsYuv || inputIsY8) {
+ reprocessOutputs.add(inputReader.getSurface());
+ }
+
+ for (ImageReader reader : jpegTargets) {
+ reprocessOutputs.add(reader.getSurface());
+ }
+
+ for (ImageReader reader : yuvTargets) {
+ reprocessOutputs.add(reader.getSurface());
+ }
+
+ for (ImageReader reader : y8Targets) {
+ reprocessOutputs.add(reader.getSurface());
+ }
+
+ for (int i = 0; i < NUM_REPROCESS_CAPTURES_PER_CONFIG; i++) {
+ for (Surface output : reprocessOutputs) {
+ TotalCaptureResult result = inputCaptureListener.getTotalCaptureResult(
+ TIMEOUT_FOR_RESULT_MS);
+ builder = mCamera.createReprocessCaptureRequest(result);
+ inputWriter.queueInputImage(
+ inputReaderListener.getImage(TIMEOUT_FOR_RESULT_MS));
+ builder.addTarget(output);
+ reprocessRequests.add(builder.build());
+ }
+ }
+
+ session.captureBurst(reprocessRequests, reprocessOutputCaptureListener, mHandler);
+
+ for (int i = 0; i < reprocessOutputs.size() * NUM_REPROCESS_CAPTURES_PER_CONFIG; i++) {
+ TotalCaptureResult result = reprocessOutputCaptureListener.getTotalCaptureResult(
+ TIMEOUT_FOR_RESULT_MS);
+ }
+ } catch (Throwable e) {
+ mCollector.addMessage(String.format("Reprocess stream combination %s failed due to: %s",
+ combination.getDescription(), e.getMessage()));
+ } finally {
+ inputReaderListener.drain();
+ reprocessOutputCaptureListener.drain();
+
+ for (SurfaceTexture target : privTargets) {
+ target.release();
+ }
+
+ for (ImageReader target : jpegTargets) {
+ target.close();
+ }
+
+ for (ImageReader target : yuvTargets) {
+ target.close();
+ }
+
+ for (ImageReader target : y8Targets) {
+ target.close();
+ }
+
+ for (ImageReader target : rawTargets) {
+ target.close();
+ }
+
+ if (inputReader != null) {
+ inputReader.close();
+ }
+
+ if (inputWriter != null) {
+ inputWriter.close();
+ }
+ }
+ }
+
public void testBasicTriggerSequence() throws Exception {
for (String id : mCameraIds) {
@@ -1546,11 +1922,7 @@
static final int RECORD = 1;
static final int MAXIMUM = 2;
static final int VGA = 3;
- static final int VGA_FULL_FOV = 4;
- static final int MAX_30FPS = 5;
- static final int RESOLUTION_COUNT = 6;
-
- static final long FRAME_DURATION_30FPS_NSEC = (long) 1e9 / 30;
+ static final int RESOLUTION_COUNT = 4;
public MaxStreamSizes(StaticMetadata sm, String cameraId, Context context) {
Size[] privSizes = sm.getAvailableSizesForFormatChecked(ImageFormat.PRIVATE,
@@ -1605,96 +1977,6 @@
}
}
- if (sm.isColorOutputSupported() && !sm.isHardwareLevelLegacy()) {
- // VGA resolution, but with aspect ratio matching full res FOV
- float fullFovAspect = maxYuvSizes[MAXIMUM].getWidth() / (float) maxYuvSizes[MAXIMUM].getHeight();
- Size vgaFullFovSize = new Size(640, (int) (640 / fullFovAspect));
-
- maxPrivSizes[VGA_FULL_FOV] = vgaFullFovSize;
- maxYuvSizes[VGA_FULL_FOV] = vgaFullFovSize;
- maxJpegSizes[VGA_FULL_FOV] = vgaFullFovSize;
- if (sm.isMonochromeWithY8()) {
- maxY8Sizes[VGA_FULL_FOV] = vgaFullFovSize;
- }
-
- // Max resolution that runs at 30fps
-
- Size maxPriv30fpsSize = null;
- Size maxYuv30fpsSize = null;
- Size maxY830fpsSize = null;
- Size maxJpeg30fpsSize = null;
- Comparator<Size> comparator = new SizeComparator();
- for (Map.Entry<Size, Long> e :
- sm.getAvailableMinFrameDurationsForFormatChecked(ImageFormat.PRIVATE).
- entrySet()) {
- Size s = e.getKey();
- Long minDuration = e.getValue();
- Log.d(TAG, String.format("Priv Size: %s, duration %d limit %d", s, minDuration, FRAME_DURATION_30FPS_NSEC));
- if (minDuration <= FRAME_DURATION_30FPS_NSEC) {
- if (maxPriv30fpsSize == null ||
- comparator.compare(maxPriv30fpsSize, s) < 0) {
- maxPriv30fpsSize = s;
- }
- }
- }
- assertTrue("No PRIVATE resolution available at 30fps!", maxPriv30fpsSize != null);
-
- for (Map.Entry<Size, Long> e :
- sm.getAvailableMinFrameDurationsForFormatChecked(
- ImageFormat.YUV_420_888).
- entrySet()) {
- Size s = e.getKey();
- Long minDuration = e.getValue();
- Log.d(TAG, String.format("YUV Size: %s, duration %d limit %d", s, minDuration, FRAME_DURATION_30FPS_NSEC));
- if (minDuration <= FRAME_DURATION_30FPS_NSEC) {
- if (maxYuv30fpsSize == null ||
- comparator.compare(maxYuv30fpsSize, s) < 0) {
- maxYuv30fpsSize = s;
- }
- }
- }
- assertTrue("No YUV_420_888 resolution available at 30fps!", maxYuv30fpsSize != null);
-
- if (sm.isMonochromeWithY8()) {
- for (Map.Entry<Size, Long> e :
- sm.getAvailableMinFrameDurationsForFormatChecked(
- ImageFormat.Y8).
- entrySet()) {
- Size s = e.getKey();
- Long minDuration = e.getValue();
- Log.d(TAG, String.format("Y8 Size: %s, duration %d limit %d",
- s, minDuration, FRAME_DURATION_30FPS_NSEC));
- if (minDuration <= FRAME_DURATION_30FPS_NSEC) {
- if (maxY830fpsSize == null ||
- comparator.compare(maxY830fpsSize, s) < 0) {
- maxY830fpsSize = s;
- }
- }
- }
- assertTrue("No Y8 resolution available at 30fps!", maxY830fpsSize != null);
- }
-
- for (Map.Entry<Size, Long> e :
- sm.getAvailableMinFrameDurationsForFormatChecked(ImageFormat.JPEG).
- entrySet()) {
- Size s = e.getKey();
- Long minDuration = e.getValue();
- Log.d(TAG, String.format("JPEG Size: %s, duration %d limit %d", s, minDuration, FRAME_DURATION_30FPS_NSEC));
- if (minDuration <= FRAME_DURATION_30FPS_NSEC) {
- if (maxJpeg30fpsSize == null ||
- comparator.compare(maxJpeg30fpsSize, s) < 0) {
- maxJpeg30fpsSize = s;
- }
- }
- }
- assertTrue("No JPEG resolution available at 30fps!", maxJpeg30fpsSize != null);
-
- maxPrivSizes[MAX_30FPS] = maxPriv30fpsSize;
- maxYuvSizes[MAX_30FPS] = maxYuv30fpsSize;
- maxY8Sizes[MAX_30FPS] = maxY830fpsSize;
- maxJpegSizes[MAX_30FPS] = maxJpeg30fpsSize;
- }
-
Size[] privInputSizes = configs.getInputSizes(ImageFormat.PRIVATE);
maxInputPrivSize = privInputSizes != null ?
CameraTestUtils.getMaxSize(privInputSizes) : null;
@@ -1782,12 +2064,6 @@
case VGA:
b.append("VGA]");
break;
- case VGA_FULL_FOV:
- b.append("VGA_FULL_FOV]");
- break;
- case MAX_30FPS:
- b.append("MAX_30FPS]");
- break;
default:
b.append("UNK]");
break;
@@ -1795,339 +2071,6 @@
}
}
- /**
- * Return an InputConfiguration for a given reprocess configuration.
- */
- private InputConfiguration getInputConfig(int[] reprocessConfig, MaxStreamSizes maxSizes) {
- int format;
- Size size;
-
- if (reprocessConfig[1] != MAXIMUM) {
- throw new IllegalArgumentException("Test only supports MAXIMUM input");
- }
-
- switch (reprocessConfig[0]) {
- case PRIV:
- format = ImageFormat.PRIVATE;
- size = maxSizes.maxInputPrivSize;
- break;
- case YUV:
- format = ImageFormat.YUV_420_888;
- size = maxSizes.maxInputYuvSize;
- break;
- case Y8:
- format = ImageFormat.Y8;
- size = maxSizes.maxInputY8Size;
- break;
- default:
- throw new IllegalArgumentException("Input format not supported: " +
- reprocessConfig[0]);
- }
-
- return new InputConfiguration(size.getWidth(), size.getHeight(), format);
- }
-
- private void testReprocessStreamCombination(String cameraId, int[] reprocessConfig,
- MaxStreamSizes maxSizes, StaticMetadata staticInfo) throws Exception {
- // Test reprocess stream combination
- testSingleReprocessStreamCombination(cameraId, reprocessConfig, maxSizes, staticInfo);
-
- // Test substituting YUV_888 format with Y8 format in reprocess stream combination.
- if (mStaticInfo.isMonochromeWithY8()) {
- int[] substitutedCfg = reprocessConfig.clone();
- boolean hasY8 = false;
- for (int i = 0; i < reprocessConfig.length; i += 2) {
- if (substitutedCfg[i] == YUV) {
- substitutedCfg[i] = Y8;
- hasY8 = true;
- }
- }
- if (hasY8) {
- testSingleReprocessStreamCombination(cameraId, substitutedCfg, maxSizes, staticInfo);
- }
- }
-
- }
- private void testSingleReprocessStreamCombination(String cameraId, int[] reprocessConfig,
- MaxStreamSizes maxSizes, StaticMetadata staticInfo) throws Exception {
-
- Log.i(TAG, String.format("Testing Camera %s, reprocess config: %s", cameraId,
- MaxStreamSizes.reprocessConfigToString(reprocessConfig)));
-
- final int TIMEOUT_FOR_RESULT_MS = 3000;
- final int NUM_REPROCESS_CAPTURES_PER_CONFIG = 3;
-
- List<SurfaceTexture> privTargets = new ArrayList<>();
- List<ImageReader> jpegTargets = new ArrayList<>();
- List<ImageReader> yuvTargets = new ArrayList<>();
- List<ImageReader> y8Targets = new ArrayList<>();
- List<ImageReader> rawTargets = new ArrayList<>();
- List<Surface> outputSurfaces = new ArrayList<>();
- ImageReader inputReader = null;
- ImageWriter inputWriter = null;
- SimpleImageReaderListener inputReaderListener = new SimpleImageReaderListener();
- SimpleCaptureCallback inputCaptureListener = new SimpleCaptureCallback();
- SimpleCaptureCallback reprocessOutputCaptureListener = new SimpleCaptureCallback();
-
- boolean supportYuvReprocess = staticInfo.isCapabilitySupported(
- CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
- boolean supportOpaqueReprocess = staticInfo.isCapabilitySupported(
- CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
-
- // Skip the configuration if the format is not supported for reprocessing.
- if (((reprocessConfig[0] == YUV || reprocessConfig[0] == Y8) && !supportYuvReprocess) ||
- (reprocessConfig[0] == PRIV && !supportOpaqueReprocess)) {
- return;
- }
-
- try {
- // reprocessConfig[2..] are additional outputs
- setupConfigurationTargets(
- Arrays.copyOfRange(reprocessConfig, 2, reprocessConfig.length),
- maxSizes, privTargets, jpegTargets, yuvTargets, y8Targets,
- rawTargets, outputSurfaces, NUM_REPROCESS_CAPTURES_PER_CONFIG);
-
- // reprocessConfig[0:1] is input
- InputConfiguration inputConfig = getInputConfig(
- Arrays.copyOfRange(reprocessConfig, 0, 2), maxSizes);
-
- // For each config, YUV and JPEG outputs will be tested. (For YUV/Y8 reprocessing,
- // the YUV/Y8 ImageReader for input is also used for output.)
- final boolean inputIsYuv = inputConfig.getFormat() == ImageFormat.YUV_420_888;
- final boolean inputIsY8 = inputConfig.getFormat() == ImageFormat.Y8;
- final boolean useYuv = inputIsYuv || yuvTargets.size() > 0;
- final boolean useY8 = inputIsY8 || y8Targets.size() > 0;
- final int totalNumReprocessCaptures = NUM_REPROCESS_CAPTURES_PER_CONFIG * (
- ((inputIsYuv || inputIsY8) ? 1 : 0) +
- jpegTargets.size() + (useYuv ? yuvTargets.size() : y8Targets.size()));
-
- // It needs 1 input buffer for each reprocess capture + the number of buffers
- // that will be used as outputs.
- inputReader = ImageReader.newInstance(inputConfig.getWidth(), inputConfig.getHeight(),
- inputConfig.getFormat(),
- totalNumReprocessCaptures + NUM_REPROCESS_CAPTURES_PER_CONFIG);
- inputReader.setOnImageAvailableListener(inputReaderListener, mHandler);
- outputSurfaces.add(inputReader.getSurface());
-
- assertTrue(String.format("Session configuration query %s failed",
- MaxStreamSizes.reprocessConfigToString(reprocessConfig)),
- checkSessionConfigurationWithSurfaces(mCamera, mHandler, outputSurfaces,
- inputConfig, SessionConfiguration.SESSION_REGULAR, /*expectedResult*/ true));
-
- // Verify we can create a reprocessable session with the input and all outputs.
- BlockingSessionCallback sessionListener = new BlockingSessionCallback();
- CameraCaptureSession session = configureReprocessableCameraSession(mCamera,
- inputConfig, outputSurfaces, sessionListener, mHandler);
- inputWriter = ImageWriter.newInstance(session.getInputSurface(),
- totalNumReprocessCaptures);
-
- // Prepare a request for reprocess input
- CaptureRequest.Builder builder = mCamera.createCaptureRequest(
- CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
- builder.addTarget(inputReader.getSurface());
-
- for (int i = 0; i < totalNumReprocessCaptures; i++) {
- session.capture(builder.build(), inputCaptureListener, mHandler);
- }
-
- List<CaptureRequest> reprocessRequests = new ArrayList<>();
- List<Surface> reprocessOutputs = new ArrayList<>();
- if (inputIsYuv || inputIsY8) {
- reprocessOutputs.add(inputReader.getSurface());
- }
-
- for (ImageReader reader : jpegTargets) {
- reprocessOutputs.add(reader.getSurface());
- }
-
- for (ImageReader reader : yuvTargets) {
- reprocessOutputs.add(reader.getSurface());
- }
-
- for (ImageReader reader : y8Targets) {
- reprocessOutputs.add(reader.getSurface());
- }
-
- for (int i = 0; i < NUM_REPROCESS_CAPTURES_PER_CONFIG; i++) {
- for (Surface output : reprocessOutputs) {
- TotalCaptureResult result = inputCaptureListener.getTotalCaptureResult(
- TIMEOUT_FOR_RESULT_MS);
- builder = mCamera.createReprocessCaptureRequest(result);
- inputWriter.queueInputImage(
- inputReaderListener.getImage(TIMEOUT_FOR_RESULT_MS));
- builder.addTarget(output);
- reprocessRequests.add(builder.build());
- }
- }
-
- session.captureBurst(reprocessRequests, reprocessOutputCaptureListener, mHandler);
-
- for (int i = 0; i < reprocessOutputs.size() * NUM_REPROCESS_CAPTURES_PER_CONFIG; i++) {
- TotalCaptureResult result = reprocessOutputCaptureListener.getTotalCaptureResult(
- TIMEOUT_FOR_RESULT_MS);
- }
- } catch (Throwable e) {
- mCollector.addMessage(String.format("Reprocess stream combination %s failed due to: %s",
- MaxStreamSizes.reprocessConfigToString(reprocessConfig), e.getMessage()));
- } finally {
- inputReaderListener.drain();
- reprocessOutputCaptureListener.drain();
-
- for (SurfaceTexture target : privTargets) {
- target.release();
- }
-
- for (ImageReader target : jpegTargets) {
- target.close();
- }
-
- for (ImageReader target : yuvTargets) {
- target.close();
- }
-
- for (ImageReader target : y8Targets) {
- target.close();
- }
-
- for (ImageReader target : rawTargets) {
- target.close();
- }
-
- if (inputReader != null) {
- inputReader.close();
- }
-
- if (inputWriter != null) {
- inputWriter.close();
- }
- }
- }
-
- private void testOutputCombination(String cameraId, int[] config, MaxStreamSizes maxSizes)
- throws Exception {
-
-
- // Check whether substituting YUV_888 format with Y8 format
- boolean substituteY8 = false;
- int[] substitutedCfg = config.clone();
- if (mStaticInfo.isMonochromeWithY8()) {
- for (int i = 0; i < config.length; i += 2) {
- if (substitutedCfg[i] == YUV) {
- substitutedCfg[i] = Y8;
- substituteY8 = true;
- }
- }
- }
-
- // Test camera output combination
- Log.i(TAG, String.format("Testing single Camera %s, config %s",
- cameraId, MaxStreamSizes.configToString(config)));
- testSingleCameraOutputCombination(cameraId, config, maxSizes);
-
- if (substituteY8) {
- Log.i(TAG, String.format("Testing single Camera %s, config %s",
- cameraId, MaxStreamSizes.configToString(substitutedCfg)));
- testSingleCameraOutputCombination(cameraId, substitutedCfg, maxSizes);
- }
-
- // Test substituting YUV_888/RAW with physical streams for logical camera
- if (mStaticInfo.isLogicalMultiCamera()) {
- Log.i(TAG, String.format("Testing logical Camera %s, config %s",
- cameraId, MaxStreamSizes.configToString(config)));
-
- testMultiCameraOutputCombination(cameraId, config, maxSizes);
-
- if (substituteY8) {
- Log.i(TAG, String.format("Testing logical Camera %s, config %s",
- cameraId, MaxStreamSizes.configToString(substitutedCfg)));
- testMultiCameraOutputCombination(cameraId, substitutedCfg, maxSizes);
- }
- }
- }
-
- private void testSingleCameraOutputCombination(String cameraId, int[] config,
- MaxStreamSizes maxSizes) throws Exception {
-
- // Timeout is relaxed by 1 second for LEGACY devices to reduce false positive rate in CTS
- final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 2000 : 1000;
- final int MIN_RESULT_COUNT = 3;
-
- // Set up outputs
- List<OutputConfiguration> outputConfigs = new ArrayList<OutputConfiguration>();
- List<SurfaceTexture> privTargets = new ArrayList<SurfaceTexture>();
- List<ImageReader> jpegTargets = new ArrayList<ImageReader>();
- List<ImageReader> yuvTargets = new ArrayList<ImageReader>();
- List<ImageReader> rawTargets = new ArrayList<ImageReader>();
- List<ImageReader> y8Targets = new ArrayList<ImageReader>();
-
- setupConfigurationTargets(config, maxSizes, privTargets, jpegTargets, yuvTargets,
- y8Targets, rawTargets, outputConfigs, MIN_RESULT_COUNT, -1 /*overrideStreamIndex*/,
- null /*overridePhysicalCameraIds*/, null /*overridePhysicalCameraSizes*/);
-
- boolean haveSession = false;
- try {
- CaptureRequest.Builder requestBuilder =
- mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
-
- for (OutputConfiguration c : outputConfigs) {
- requestBuilder.addTarget(c.getSurface());
- }
-
- CameraCaptureSession.CaptureCallback mockCaptureCallback =
- mock(CameraCaptureSession.CaptureCallback.class);
-
- assertTrue(String.format("Session configuration query %s failed",
- MaxStreamSizes.configToString(config)), checkSessionConfiguration(mCamera,
- mHandler, outputConfigs, /*inputConfig*/ null,
- SessionConfiguration.SESSION_REGULAR, /*expectedResult*/ true));
-
- createSessionByConfigs(outputConfigs);
- haveSession = true;
- CaptureRequest request = requestBuilder.build();
- mCameraSession.setRepeatingRequest(request, mockCaptureCallback, mHandler);
-
- verify(mockCaptureCallback,
- timeout(TIMEOUT_FOR_RESULT_MS * MIN_RESULT_COUNT).atLeast(MIN_RESULT_COUNT))
- .onCaptureCompleted(
- eq(mCameraSession),
- eq(request),
- isA(TotalCaptureResult.class));
- verify(mockCaptureCallback, never()).
- onCaptureFailed(
- eq(mCameraSession),
- eq(request),
- isA(CaptureFailure.class));
-
- } catch (Throwable e) {
- mCollector.addMessage(String.format("Output combination %s failed due to: %s",
- MaxStreamSizes.configToString(config), e.getMessage()));
- }
- if (haveSession) {
- try {
- Log.i(TAG, String.format("Done with camera %s, config %s, closing session",
- cameraId, MaxStreamSizes.configToString(config)));
- stopCapture(/*fast*/false);
- } catch (Throwable e) {
- mCollector.addMessage(
- String.format("Closing down for output combination %s failed due to: %s",
- MaxStreamSizes.configToString(config), e.getMessage()));
- }
- }
-
- for (SurfaceTexture target : privTargets) {
- target.release();
- }
- for (ImageReader target : jpegTargets) {
- target.close();
- }
- for (ImageReader target : yuvTargets) {
- target.close();
- }
- for (ImageReader target : rawTargets) {
- target.close();
- }
- }
-
private void testMultiCameraOutputCombination(String cameraId, int[] config,
MaxStreamSizes maxSizes) throws Exception {
@@ -2253,21 +2196,6 @@
private void setupConfigurationTargets(int[] configs, MaxStreamSizes maxSizes,
List<SurfaceTexture> privTargets, List<ImageReader> jpegTargets,
List<ImageReader> yuvTargets, List<ImageReader> y8Targets,
- List<ImageReader> rawTargets, List<Surface> outputSurfaces, int numBuffers) {
- List<OutputConfiguration> outputConfigs = new ArrayList<OutputConfiguration> ();
-
- setupConfigurationTargets(configs, maxSizes, privTargets, jpegTargets, yuvTargets,
- y8Targets, rawTargets, outputConfigs, numBuffers, -1 /*overrideStreamIndex*/,
- null /*overridePhysicalCameraIds*/, null /* overridePhysicalCameraSizes) */);
-
- for (OutputConfiguration outputConfig : outputConfigs) {
- outputSurfaces.add(outputConfig.getSurface());
- }
- }
-
- private void setupConfigurationTargets(int[] configs, MaxStreamSizes maxSizes,
- List<SurfaceTexture> privTargets, List<ImageReader> jpegTargets,
- List<ImageReader> yuvTargets, List<ImageReader> y8Targets,
List<ImageReader> rawTargets, List<OutputConfiguration> outputConfigs, int numBuffers,
int overrideStreamIndex, List<String> overridePhysicalCameraIds,
List<Size> overridePhysicalCameraSizes) {
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
index ee0ec57..a8866c5 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
@@ -19,10 +19,6 @@
import static android.contentcaptureservice.cts.Helper.TAG;
import static android.contentcaptureservice.cts.Helper.resetService;
-import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
-
-import static org.junit.Assume.assumeFalse;
-
import android.app.Application;
import android.content.Context;
import android.content.Intent;
@@ -34,13 +30,16 @@
import androidx.annotation.NonNull;
+import com.android.compatibility.common.util.RequiredServiceRule;
+import com.android.compatibility.common.util.SafeCleanerRule;
+
import org.junit.After;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
+
/**
* Base class for all (or most :-) integration tests in this CTS suite.
*/
@@ -50,31 +49,41 @@
protected static final Context sContext = InstrumentationRegistry.getTargetContext();
- @Rule
- public final RuleChain mLookAllTheseRules = RuleChain
- .outerRule(getActivityTestRule());
-
protected ActivitiesWatcher mActivitiesWatcher;
private final Class<A> mActivityClass;
+ private final RequiredServiceRule mRequiredServiceRule =
+ new RequiredServiceRule("content_capture");
+ private final ContentCaptureLoggingTestRule mLoggingRule =
+ new ContentCaptureLoggingTestRule(TAG);
+
+ protected final SafeCleanerRule mSafeCleanerRule = new SafeCleanerRule()
+ .setDumper(mLoggingRule)
+ .add(() -> {
+ return CtsSmartSuggestionsService.getExceptions();
+ });
+
+ @Rule
+ public final RuleChain mLookAllTheseRules = RuleChain
+ //
+ // mRequiredServiceRule should be first so the test can be skipped right away
+ .outerRule(mRequiredServiceRule)
+ //
+ // mLoggingRule wraps the test but doesn't interfere with it
+ .around(mLoggingRule)
+ //
+ // mSafeCleanerRule will catch errors
+ .around(mSafeCleanerRule)
+ //
+ // Finally, let subclasses set their ActivityTestRule
+ .around(getActivityTestRule());
+
+
protected AbstractContentCaptureIntegrationTest(@NonNull Class<A> activityClass) {
mActivityClass = activityClass;
}
- @BeforeClass
- public static void checkSupported() {
- // TODO(b/119638958): use a @Rule to skip it and/or check for the Global Settings directly
- final String checkService = runShellCommand("service check content_capture").trim();
- final boolean notSupported = checkService.contains("not found");
- if (notSupported) {
- final String msg = "Skipping test because Content Capture is not supported on device";
- Log.i(TAG, msg);
- assumeFalse(msg, notSupported);
- return;
- }
- }
-
@Before
public void registerLifecycleCallback() {
Log.d(TAG, "Registering lifecycle callback");
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankActivityTest.java
index 9a7b071..7714b14 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankActivityTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankActivityTest.java
@@ -67,21 +67,16 @@
watcher.waitFor(DESTROYED);
final CtsSmartSuggestionsService service = CtsSmartSuggestionsService.getInstance();
- try {
- final Session session = service.getFinishedSession(BlankActivity.class);
+ final Session session = service.getFinishedSession(BlankActivity.class);
- assertRightActivity(session, activity);
+ assertRightActivity(session, activity);
- final List<ContentCaptureEvent> events = session.getEvents();
- Log.v(TAG, "events: " + events);
- assertThat(events).hasSize(4);
- assertLifecycleEvent(events.get(0), TYPE_ACTIVITY_STARTED);
- assertLifecycleEvent(events.get(1), TYPE_ACTIVITY_RESUMED);
- assertLifecycleEvent(events.get(2), TYPE_ACTIVITY_PAUSED);
- assertLifecycleEvent(events.get(3), TYPE_ACTIVITY_STOPPED);
- } finally {
- // TODO(b/119638958): move to @Rule SafeCleaner
- CtsSmartSuggestionsService.assertNoExceptions();
- }
+ final List<ContentCaptureEvent> events = session.getEvents();
+ Log.v(TAG, "events: " + events);
+ assertThat(events).hasSize(4);
+ assertLifecycleEvent(events.get(0), TYPE_ACTIVITY_STARTED);
+ assertLifecycleEvent(events.get(1), TYPE_ACTIVITY_RESUMED);
+ assertLifecycleEvent(events.get(2), TYPE_ACTIVITY_PAUSED);
+ assertLifecycleEvent(events.get(3), TYPE_ACTIVITY_STOPPED);
}
}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ContentCaptureLoggingTestRule.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ContentCaptureLoggingTestRule.java
new file mode 100644
index 0000000..b0dca6f
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ContentCaptureLoggingTestRule.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.contentcaptureservice.cts;
+
+import static android.contentcaptureservice.cts.Helper.TAG;
+import static android.contentcaptureservice.cts.common.ShellHelper.runShellCommand;
+
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.compatibility.common.util.SafeCleanerRule;
+
+import org.junit.AssumptionViolatedException;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Custom JUnit4 rule that improves ContentCapture-related logging by:
+ *
+ * <ol>
+ * <li>Setting logging level to verbose before test start.
+ * <li>Call {@code dumpsys} in case of failure.
+ * </ol>
+ */
+public class ContentCaptureLoggingTestRule implements TestRule, SafeCleanerRule.Dumper {
+
+ private final String mTag;
+ private boolean mDumped;
+
+ public ContentCaptureLoggingTestRule(String tag) {
+ mTag = tag;
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+
+ @Override
+ public void evaluate() throws Throwable {
+ // TODO(b/119638958): set verbose logging once ContentCapture supports it
+ final String testName = description.getDisplayName();
+ try {
+ base.evaluate();
+ } catch (Throwable t) {
+ dump(testName, t);
+ throw t;
+ } finally {
+ // TODO(b/119638958): recover logging level
+ }
+ }
+ };
+ }
+
+ @Override
+ public void dump(@NonNull String testName, @NonNull Throwable t) {
+ if (mDumped) {
+ Log.e(mTag, "dump(" + testName + "): already dumped");
+ return;
+ }
+ if ((t instanceof AssumptionViolatedException)) {
+ // This exception is used to indicate a test should be skipped and is
+ // ignored by JUnit runners - we don't need to dump it...
+ Log.w(TAG, "ignoring exception: " + t);
+ return;
+ }
+ // TODO(b/119638958, b/120784831): should dump to a file (and integrate with tradefed)
+ // instead of outputting to log directly...
+ Log.e(mTag, "Dumping after exception on " + testName, t);
+ final String autofillDump = runShellCommand("dumpsys content_capture");
+ Log.e(mTag, "content_capture dump: \n" + autofillDump);
+ final String activityDump = runShellCommand("dumpsys activity top --contentcapture");
+ Log.e(mTag, "top activity dump: \n" + activityDump);
+ mDumped = true;
+ }
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsSmartSuggestionsService.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsSmartSuggestionsService.java
index 0983108..ca5b9cf 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsSmartSuggestionsService.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsSmartSuggestionsService.java
@@ -171,15 +171,10 @@
}
/**
- * Asserts that no exception was thrown while the service handlded requests.
+ * Gets the exceptions that were thrown while the service handlded requests.
*/
- public static void assertNoExceptions() throws Exception {
- if (sExceptions.isEmpty()) return;
- if (sExceptions.size() == 1) {
- throwException(sExceptions.get(0));
- }
- // TODO(b/119638958): use a MultipleExceptions class (from common)
- throw new AssertionError("Multiple exceptions: " + sExceptions);
+ public static List<Throwable> getExceptions() throws Exception {
+ return Collections.unmodifiableList(sExceptions);
}
private void throwIllegalSessionStateException(@NonNull String fmt, @Nullable Object...args) {
@@ -210,16 +205,6 @@
}
}
- private static void throwException(Throwable t) throws Exception {
- if (t instanceof Exception) {
- throw (Exception) t;
- }
- if (t instanceof Error) {
- throw (Error) t;
- }
- throw new Exception(t);
- }
-
public final class Session {
public final InteractionSessionId id;
public final InteractionContext context;
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java
index 1b886c4..a640e36 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java
@@ -15,7 +15,7 @@
*/
package android.contentcaptureservice.cts;
-import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static android.contentcaptureservice.cts.common.ShellHelper.runShellCommand;
import android.os.SystemClock;
import android.util.Log;
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivityTest.java
index b0c1267..72b92dc 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivityTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivityTest.java
@@ -71,52 +71,47 @@
watcher.waitFor(DESTROYED);
final CtsSmartSuggestionsService service = CtsSmartSuggestionsService.getInstance();
- try {
- final Session session = service.getFinishedSession(LoginActivity.class);
+ final Session session = service.getFinishedSession(LoginActivity.class);
- assertRightActivity(session, activity);
+ assertRightActivity(session, activity);
- final List<ContentCaptureEvent> events = session.getEvents();
- Log.v(TAG, "events: " + events);
- // TODO(b/119638958): ideally it should be 14 so it reflects just the views defined
- // in the layout - right now it's generating events for 2 intermediate parents
- // (android:action_mode_bar_stub and android:content), we should try to create an
- // activity without them
+ final List<ContentCaptureEvent> events = session.getEvents();
+ Log.v(TAG, "events: " + events);
+ // TODO(b/119638958): ideally it should be 14 so it reflects just the views defined
+ // in the layout - right now it's generating events for 2 intermediate parents
+ // (android:action_mode_bar_stub and android:content), we should try to create an
+ // activity without them
- final AutofillId rootId = activity.mRootView.getAutofillId();
+ final AutofillId rootId = activity.mRootView.getAutofillId();
- assertThat(events).hasSize(18);
- assertLifecycleEvent(events.get(0), TYPE_ACTIVITY_STARTED);
- assertLifecycleEvent(events.get(1), TYPE_ACTIVITY_RESUMED);
- assertViewAppeared(events.get(2), activity.mUsernameLabel, rootId);
- assertViewAppeared(events.get(3), activity.mUsername, rootId);
- assertViewAppeared(events.get(4), activity.mPasswordLabel, rootId);
- assertViewAppeared(events.get(5), activity.mPassword, rootId);
- // TODO(b/119638958): get rid of those intermediated parents
- final View grandpa1 = (View) activity.mRootView.getParent();
- final View grandpa2 = (View) grandpa1.getParent();
- final View decorView = (View) grandpa2.getParent();
+ assertThat(events).hasSize(18);
+ assertLifecycleEvent(events.get(0), TYPE_ACTIVITY_STARTED);
+ assertLifecycleEvent(events.get(1), TYPE_ACTIVITY_RESUMED);
+ assertViewAppeared(events.get(2), activity.mUsernameLabel, rootId);
+ assertViewAppeared(events.get(3), activity.mUsername, rootId);
+ assertViewAppeared(events.get(4), activity.mPasswordLabel, rootId);
+ assertViewAppeared(events.get(5), activity.mPassword, rootId);
+ // TODO(b/119638958): get rid of those intermediated parents
+ final View grandpa1 = (View) activity.mRootView.getParent();
+ final View grandpa2 = (View) grandpa1.getParent();
+ final View decorView = (View) grandpa2.getParent();
- assertViewAppeared(events.get(6), activity.mRootView, grandpa1.getAutofillId());
- assertViewAppeared(events.get(7), grandpa1, grandpa2.getAutofillId());
- assertViewAppeared(events.get(8), grandpa2, decorView.getAutofillId());
+ assertViewAppeared(events.get(6), activity.mRootView, grandpa1.getAutofillId());
+ assertViewAppeared(events.get(7), grandpa1, grandpa2.getAutofillId());
+ assertViewAppeared(events.get(8), grandpa2, decorView.getAutofillId());
- // TODO(b/119638958): VIEW_DISAPPEARED events should be send before the activity
- // stopped - if we don't deprecate the latter, we should change the manager to make sure
- // they're send in that order (or dropped)
- assertLifecycleEvent(events.get(9), TYPE_ACTIVITY_PAUSED);
- assertLifecycleEvent(events.get(10), TYPE_ACTIVITY_STOPPED);
+ // TODO(b/119638958): VIEW_DISAPPEARED events should be send before the activity
+ // stopped - if we don't deprecate the latter, we should change the manager to make sure
+ // they're send in that order (or dropped)
+ assertLifecycleEvent(events.get(9), TYPE_ACTIVITY_PAUSED);
+ assertLifecycleEvent(events.get(10), TYPE_ACTIVITY_STOPPED);
- assertViewDisappeared(events.get(11), grandpa2.getAutofillId());
- assertViewDisappeared(events.get(12), grandpa1.getAutofillId());
- assertViewDisappeared(events.get(13), activity.mRootView.getAutofillId());
- assertViewDisappeared(events.get(14), activity.mUsernameLabel.getAutofillId());
- assertViewDisappeared(events.get(15), activity.mUsername.getAutofillId());
- assertViewDisappeared(events.get(16), activity.mPasswordLabel.getAutofillId());
- assertViewDisappeared(events.get(17), activity.mPassword.getAutofillId());
- } finally {
- // TODO(b/119638958): move to @Rule SafeCleaner
- CtsSmartSuggestionsService.assertNoExceptions();
- }
+ assertViewDisappeared(events.get(11), grandpa2.getAutofillId());
+ assertViewDisappeared(events.get(12), grandpa1.getAutofillId());
+ assertViewDisappeared(events.get(13), activity.mRootView.getAutofillId());
+ assertViewDisappeared(events.get(14), activity.mUsernameLabel.getAutofillId());
+ assertViewDisappeared(events.get(15), activity.mUsername.getAutofillId());
+ assertViewDisappeared(events.get(16), activity.mPasswordLabel.getAutofillId());
+ assertViewDisappeared(events.get(17), activity.mPassword.getAutofillId());
}
}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/common/ShellHelper.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/common/ShellHelper.java
new file mode 100644
index 0000000..a9b1481
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/common/ShellHelper.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.contentcaptureservice.cts.common;
+
+import android.support.test.InstrumentationRegistry;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+/**
+ * Provides Shell-based utilities such as running a command.
+ */
+public final class ShellHelper {
+
+ private static final String TAG = "ShellHelper";
+
+ /**
+ * Runs a Shell command, returning a trimmed response.
+ */
+ @NonNull
+ public static String runShellCommand(@NonNull String template, Object... args) {
+ final String command = String.format(template, args);
+ Log.d(TAG, "runShellCommand(): " + command);
+ try {
+ final String result = SystemUtil
+ .runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
+ return TextUtils.isEmpty(result) ? "" : result.trim();
+ } catch (Exception e) {
+ throw new RuntimeException("Command '" + command + "' failed: ", e);
+ }
+ }
+
+ private ShellHelper() {
+ throw new UnsupportedOperationException("contain static methods only");
+ }
+}
diff --git a/tests/framework/base/activitymanager/AndroidManifest.xml b/tests/framework/base/activitymanager/AndroidManifest.xml
index 62abe73..d113cb4 100644
--- a/tests/framework/base/activitymanager/AndroidManifest.xml
+++ b/tests/framework/base/activitymanager/AndroidManifest.xml
@@ -19,6 +19,7 @@
package="android.server.cts.am">
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<application android:label="CtsActivityManagerDeviceTestCases">
<uses-library android:name="android.test.runner" />
@@ -49,6 +50,23 @@
android:label="MaxAspectRatioUnsetActivity"
android:resizeableActivity="false" />
+ <activity
+ android:name="android.server.am.AspectRatioTests$MinAspectRatioActivity"
+ android:label="MinAspectRatioActivity"
+ android:minAspectRatio="5.0"
+ android:resizeableActivity="false" />
+
+ <activity
+ android:name="android.server.am.AspectRatioTests$MinAspectRatioResizeableActivity"
+ android:label="MinAspectRatioResizeableActivity"
+ android:minAspectRatio="5.0"
+ android:resizeableActivity="true" />
+
+ <activity
+ android:name="android.server.am.AspectRatioTests$MinAspectRatioUnsetActivity"
+ android:label="MinAspectRatioUnsetActivity"
+ android:resizeableActivity="false" />
+
<activity android:name="android.server.am.ActivityManagerTestBase$SideActivity"
android:resizeableActivity="true"
android:taskAffinity="nobody.but.SideActivity"/>
diff --git a/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTests.java b/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTests.java
index 808d06d..a313c35 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTests.java
@@ -16,24 +16,16 @@
package android.server.am;
-import static android.content.pm.PackageManager.FEATURE_WATCH;
-
-import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.lessThanOrEqualTo;
import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
import static org.junit.Assume.assumeThat;
import android.app.Activity;
-import android.content.Context;
import android.platform.test.annotations.Presubmit;
-import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
-import android.util.DisplayMetrics;
import android.view.Display;
-import android.view.WindowManager;
import org.junit.Rule;
import org.junit.Test;
@@ -50,11 +42,8 @@
// The max. aspect ratio the test activities are using.
private static final float MAX_ASPECT_RATIO = 1.0f;
- // The minimum supported device aspect ratio.
- private static final float MIN_DEVICE_ASPECT_RATIO = 1.333f;
-
- // The minimum supported device aspect ratio for watches.
- private static final float MIN_WATCH_DEVICE_ASPECT_RATIO = 1.0f;
+ // The min. aspect ratio the test activities are using.
+ private static final float MIN_ASPECT_RATIO = 5.0f;
// Test target activity that has maxAspectRatio="true" and resizeableActivity="false".
public static class MaxAspectRatioActivity extends Activity {
@@ -74,6 +63,18 @@
public static class MetaDataMaxAspectRatioActivity extends Activity {
}
+ // Test target activity that has minAspectRatio="true" and resizeableActivity="false".
+ public static class MinAspectRatioActivity extends Activity {
+ }
+
+ // Test target activity that has minAspectRatio="5.0" and resizeableActivity="true".
+ public static class MinAspectRatioResizeableActivity extends Activity {
+ }
+
+ // Test target activity that has no minAspectRatio defined and resizeableActivity="false".
+ public static class MinAspectRatioUnsetActivity extends Activity {
+ }
+
@Rule
public ActivityTestRule<MaxAspectRatioActivity> mMaxAspectRatioActivity =
new ActivityTestRule<>(MaxAspectRatioActivity.class,
@@ -94,22 +95,20 @@
new ActivityTestRule<>(MaxAspectRatioUnsetActivity.class,
false /* initialTouchMode */, false /* launchActivity */);
- @Test
- public void testDeviceAspectRatio() {
- final Context context = InstrumentationRegistry.getInstrumentation().getContext();
- final WindowManager wm = context.getSystemService(WindowManager.class);
- final Display display = wm.getDefaultDisplay();
- final DisplayMetrics metrics = new DisplayMetrics();
- display.getRealMetrics(metrics);
+ @Rule
+ public ActivityTestRule<MinAspectRatioActivity> mMinAspectRatioActivity =
+ new ActivityTestRule<>(MinAspectRatioActivity.class,
+ false /* initialTouchMode */, false /* launchActivity */);
- float longSide = Math.max(metrics.widthPixels, metrics.heightPixels);
- float shortSide = Math.min(metrics.widthPixels, metrics.heightPixels);
- float deviceAspectRatio = longSide / shortSide;
- float expectedMinAspectRatio = context.getPackageManager().hasSystemFeature(FEATURE_WATCH)
- ? MIN_WATCH_DEVICE_ASPECT_RATIO : MIN_DEVICE_ASPECT_RATIO;
+ @Rule
+ public ActivityTestRule<MinAspectRatioResizeableActivity> mMinAspectRatioResizeableActivity =
+ new ActivityTestRule<>(MinAspectRatioResizeableActivity.class,
+ false /* initialTouchMode */, false /* launchActivity */);
- assertThat(deviceAspectRatio, greaterThanOrEqualTo(expectedMinAspectRatio));
- }
+ @Rule
+ public ActivityTestRule<MinAspectRatioUnsetActivity> mMinAspectRatioUnsetActivity =
+ new ActivityTestRule<>(MinAspectRatioUnsetActivity.class,
+ false /* initialTouchMode */, false /* launchActivity */);
@Test
public void testMaxAspectRatio() {
@@ -150,4 +149,35 @@
assertThat(actual, greaterThanOrEqualToInexact(getDefaultDisplayAspectRatio()));
});
}
+
+ @Test
+ public void testMinAspectRatio() {
+ // Activity has a minAspectRatio, assert the ratio is at least that.
+ runAspectRatioTest(mMinAspectRatioActivity, (actual, displayId) -> {
+ assertThat(actual, greaterThanOrEqualToInexact(MIN_ASPECT_RATIO));
+ });
+ }
+
+ @Test
+ public void testMinAspectRatioResizeableActivity() {
+ // Since this activity is resizeable, the minAspectRatio should be ignored.
+ runAspectRatioTest(mMinAspectRatioResizeableActivity, (actual, displayId) -> {
+ // TODO(b/69982434): Add ability to get native aspect ratio non-default display.
+ assumeThat(displayId, is(Display.DEFAULT_DISPLAY));
+
+ assertThat(actual, lessThanOrEqualToInexact(getDefaultDisplayAspectRatio()));
+ });
+ }
+
+ @Test
+ public void testMinAspectRatioUnsetActivity() {
+ // Since this activity didn't set an explicit minAspectRatio, there should be no such
+ // ratio enforced.
+ runAspectRatioTest(mMinAspectRatioUnsetActivity, (actual, displayId) -> {
+ // TODO(b/69982434): Add ability to get native aspect ratio non-default display.
+ assumeThat(displayId, is(Display.DEFAULT_DISPLAY));
+
+ assertThat(actual, lessThanOrEqualToInexact(getDefaultDisplayAspectRatio()));
+ });
+ }
}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
index 739c05a..777823b 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
@@ -33,6 +33,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
+import android.app.KeyguardManager;
import android.content.ComponentName;
import org.junit.Before;
@@ -70,6 +71,23 @@
}
@Test
+ public void testDisableKeyguard_thenSettingCredential_reenablesKeyguard_b119322269() {
+ final KeyguardManager.KeyguardLock keyguardLock = mContext.getSystemService(
+ KeyguardManager.class).newKeyguardLock("KeyguardLockedTests");
+
+ try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
+ lockScreenSession.gotoKeyguard();
+ keyguardLock.disableKeyguard();
+
+ lockScreenSession.setLockCredential();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
+ } finally {
+ keyguardLock.reenableKeyguard();
+ }
+ }
+
+ @Test
public void testDismissKeyguard() throws Exception {
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
lockScreenSession.setLockCredential()
diff --git a/tests/framework/base/activitymanager/testsdk28/Android.mk b/tests/framework/base/activitymanager/testsdk28/Android.mk
new file mode 100644
index 0000000..e1bb155
--- /dev/null
+++ b/tests/framework/base/activitymanager/testsdk28/Android.mk
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests optional
+
+LOCAL_PACKAGE_NAME := CtsActivityManagerDeviceSdk28TestCases
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+ ../src/android/server/am/AspectRatioTestsBase.java
+
+LOCAL_SDK_VERSION := 28
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ cts-amwm-util
+
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/framework/base/activitymanager/testsdk28/AndroidManifest.xml b/tests/framework/base/activitymanager/testsdk28/AndroidManifest.xml
new file mode 100644
index 0000000..9ba1dc0
--- /dev/null
+++ b/tests/framework/base/activitymanager/testsdk28/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.server.cts.am.testsdk28">
+
+ <uses-sdk android:targetSdkVersion="28" />
+
+ <application android:label="CtsActivityManagerDeviceSdk28TestCases">
+ <uses-library android:name="android.test.runner" />
+
+ <activity
+ android:name="android.server.am.AspectRatioSdk28Tests$Sdk28MinAspectRatioActivity"
+ android:label="Sdk28MinAspectRatioActivity"
+ android:exported="true"
+ android:resizeableActivity="false" />
+ </application>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.server.cts.am.testsdk28" />
+
+</manifest>
diff --git a/tests/framework/base/activitymanager/testsdk28/AndroidTest.xml b/tests/framework/base/activitymanager/testsdk28/AndroidTest.xml
new file mode 100644
index 0000000..0040fce
--- /dev/null
+++ b/tests/framework/base/activitymanager/testsdk28/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<configuration description="Config for CTS ActivityManager SDK 28 compatibility test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsActivityManagerDeviceSdk28TestCases.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="android.server.cts.am.testsdk28" />
+ <option name="runtime-hint" value="1m" />
+ </test>
+</configuration>
diff --git a/tests/framework/base/activitymanager/testsdk28/src/android/server/am/AspectRatioSdk28Tests.java b/tests/framework/base/activitymanager/testsdk28/src/android/server/am/AspectRatioSdk28Tests.java
new file mode 100644
index 0000000..7eaa464
--- /dev/null
+++ b/tests/framework/base/activitymanager/testsdk28/src/android/server/am/AspectRatioSdk28Tests.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import static android.content.pm.PackageManager.FEATURE_WATCH;
+
+import static org.junit.Assert.assertThat;
+
+import android.app.Activity;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Run: atest AspectRatioSdk28Tests
+ */
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public class AspectRatioSdk28Tests extends AspectRatioTestsBase {
+
+ // The minimum supported device aspect ratio for pre-Q devices.
+ private static final float MIN_DEVICE_ASPECT_RATIO = 1.333f;
+
+ // The minimum supported device aspect ratio for watches.
+ private static final float MIN_WATCH_DEVICE_ASPECT_RATIO = 1.0f;
+
+
+ // Test target activity that has targetSdk="28" and resizeableActivity="false".
+ public static class Sdk28MinAspectRatioActivity extends Activity {
+ }
+
+ @Rule
+ public ActivityTestRule<?> mSdk28MinAspectRatioActivity = new ActivityTestRule<>(
+ Sdk28MinAspectRatioActivity.class, false /* initialTouchMode */,
+ false /* launchActivity */);
+
+ @Test
+ public void testMaxAspectRatioPreQActivity() {
+ boolean isWatch = InstrumentationRegistry.getContext().getPackageManager()
+ .hasSystemFeature(FEATURE_WATCH);
+ float minAspectRatio = isWatch ? MIN_WATCH_DEVICE_ASPECT_RATIO : MIN_DEVICE_ASPECT_RATIO;
+
+ runAspectRatioTest(mSdk28MinAspectRatioActivity, (actual, displayId) -> {
+ assertThat(actual, greaterThanOrEqualToInexact(minAspectRatio));
+ });
+ }
+}
diff --git a/tests/tests/batterysaving/AndroidTest.xml b/tests/tests/batterysaving/AndroidTest.xml
index aa0e786..fff0c0d 100644
--- a/tests/tests/batterysaving/AndroidTest.xml
+++ b/tests/tests/batterysaving/AndroidTest.xml
@@ -33,6 +33,8 @@
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="am set-standby-bucket android.os.cts.batterysaving.app_target_api_25 10" />
<option name="run-command" value="am set-standby-bucket android.os.cts.batterysaving.app_target_api_current 10" />
+ <option name="run-command" value="cmd thermalservice override-status 0" />
+ <option name="teardown-command" value="cmd thermalservice reset" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/tests/tests/content/AndroidTest.xml b/tests/tests/content/AndroidTest.xml
index 5570067..a975b5b 100644
--- a/tests/tests/content/AndroidTest.xml
+++ b/tests/tests/content/AndroidTest.xml
@@ -20,6 +20,8 @@
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="mkdir -p /data/local/tmp/cts/content" />
<option name="teardown-command" value="rm -rf /data/local/tmp/cts"/>
+ <option name="run-command" value="cmd thermalservice override-status 0" />
+ <option name="teardown-command" value="cmd thermalservice reset" />
</target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
diff --git a/tests/tests/media/res/raw/a_4_haptic.ogg b/tests/tests/media/res/raw/a_4_haptic.ogg
new file mode 100644
index 0000000..bae9cd8
--- /dev/null
+++ b/tests/tests/media/res/raw/a_4_haptic.ogg
Binary files differ
diff --git a/tests/tests/media/res/raw/b_5_haptic.ogg b/tests/tests/media/res/raw/b_5_haptic.ogg
new file mode 100644
index 0000000..bae9cd8
--- /dev/null
+++ b/tests/tests/media/res/raw/b_5_haptic.ogg
Binary files differ
diff --git a/tests/tests/media/res/raw/c_sharp_5_haptic.ogg b/tests/tests/media/res/raw/c_sharp_5_haptic.ogg
new file mode 100644
index 0000000..bae9cd8
--- /dev/null
+++ b/tests/tests/media/res/raw/c_sharp_5_haptic.ogg
Binary files differ
diff --git a/tests/tests/media/res/raw/e_5_haptic.ogg b/tests/tests/media/res/raw/e_5_haptic.ogg
new file mode 100644
index 0000000..bae9cd8
--- /dev/null
+++ b/tests/tests/media/res/raw/e_5_haptic.ogg
Binary files differ
diff --git a/tests/tests/media/res/raw/g_sharp_5_haptic.ogg b/tests/tests/media/res/raw/g_sharp_5_haptic.ogg
new file mode 100644
index 0000000..bae9cd8
--- /dev/null
+++ b/tests/tests/media/res/raw/g_sharp_5_haptic.ogg
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index d59d689..c10bfbe 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -1463,6 +1463,11 @@
}
}
+ public void testIsHapticPlaybackSupported() throws Exception {
+ // Calling the API to make sure it doesn't crash.
+ Log.i(TAG, "isHapticPlaybackSupported: " + AudioManager.isHapticPlaybackSupported());
+ }
+
private void setInterruptionFilter(int filter) throws Exception {
mNm.setInterruptionFilter(filter);
for (int i = 0; i < 5; i++) {
diff --git a/tests/tests/media/src/android/media/cts/SoundPoolHapticTest.java b/tests/tests/media/src/android/media/cts/SoundPoolHapticTest.java
new file mode 100644
index 0000000..0dad9fa
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/SoundPoolHapticTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import android.media.cts.R;
+import android.platform.test.annotations.AppModeFull;
+
+@AppModeFull(reason = "TODO: evaluate and port to instant")
+public class SoundPoolHapticTest extends SoundPoolTest {
+ // Test files are mocked audio-haptic coupled ogg files for regression testing.
+ // TODO: Update with actual audio-haptic ogg files.
+
+ @Override
+ protected int getSoundA() {
+ return R.raw.a_4_haptic;
+ }
+
+ @Override
+ protected int getSoundCs() {
+ return R.raw.c_sharp_5_haptic;
+ }
+
+ @Override
+ protected int getSoundE() {
+ return R.raw.e_5_haptic;
+ }
+
+ @Override
+ protected int getSoundB() {
+ return R.raw.b_5_haptic;
+ }
+
+ @Override
+ protected int getSoundGs() {
+ return R.raw.g_sharp_5_haptic;
+ }
+
+ @Override
+ protected String getFileName() {
+ return "a_4_haptic.ogg";
+ }
+}
diff --git a/tests/tests/simpleperf/Android.mk b/tests/tests/simpleperf/Android.mk
index 94c2a17..24152c6 100644
--- a/tests/tests/simpleperf/Android.mk
+++ b/tests/tests/simpleperf/Android.mk
@@ -18,6 +18,8 @@
LOCAL_STATIC_LIBRARIES += \
libbacktrace \
libunwindstack \
+ libdexfile_support \
+ libdexfile_external \
libdexfile \
libziparchive \
libz \
diff --git a/tests/tests/syncmanager/AndroidTest.xml b/tests/tests/syncmanager/AndroidTest.xml
index 66919ad..d8fd6a3 100644
--- a/tests/tests/syncmanager/AndroidTest.xml
+++ b/tests/tests/syncmanager/AndroidTest.xml
@@ -35,6 +35,8 @@
<option name="run-command" value="am set-standby-bucket android.content.syncmanager.cts.app1 10" />
<option name="run-command" value="am set-standby-bucket android.content.syncmanager.cts.app2 10" />
<option name="run-command" value="setprop log.tag.SyncManager VERBOSE" />
+ <option name="run-command" value="cmd thermalservice override-status 0" />
+ <option name="teardown-command" value="cmd thermalservice reset" />
<option name="teardown-command" value="setprop log.tag.SyncManager INFO" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/tests/tests/telephony/src/android/telephony/cts/SubscriptionManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/SubscriptionManagerTest.java
index 7b0018a..f47b8d9 100644
--- a/tests/tests/telephony/src/android/telephony/cts/SubscriptionManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SubscriptionManagerTest.java
@@ -314,6 +314,40 @@
}
}
+ @Test
+ public void testSettingOpportunisticSubscription() throws Exception {
+ if (!isSupported()) return;
+
+ // Set subscription to be opportunistic. This should fail
+ // because we don't have MODIFY_PHONE_STATE or carrier privilege permission.
+ try {
+ mSm.setOpportunistic(true, mSubId);
+ fail();
+ } catch (SecurityException expected) {
+ }
+
+ // Shouldn't crash.
+ SubscriptionInfo info = mSm.getActiveSubscriptionInfo(mSubId);
+ info.isOpportunistic();
+ }
+
+ @Test
+ public void testSettingSubscriptionMeteredNess() throws Exception {
+ if (!isSupported()) return;
+
+ // Set subscription to be un-metered. This should fail
+ // because we don't have MODIFY_PHONE_STATE or carrier privilege permission.
+ try {
+ mSm.setMetered(false, mSubId);
+ fail();
+ } catch (SecurityException expected) {
+ }
+
+ // Shouldn't crash.
+ SubscriptionInfo info = mSm.getActiveSubscriptionInfo(mSubId);
+ info.isMetered();
+ }
+
private void assertOverrideSuccess(SubscriptionPlan... plans) {
mSm.setSubscriptionPlans(mSubId, Arrays.asList(plans));
mSm.setSubscriptionOverrideCongested(mSubId, false, 0);
diff --git a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
index a6ecd3b..5d17417 100644
--- a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
@@ -43,6 +43,7 @@
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
import android.text.TextUtils;
import android.util.Log;
@@ -55,6 +56,7 @@
import org.junit.runner.RunWith;
import java.util.List;
+import java.util.Map;
import java.util.regex.Pattern;
/**
@@ -704,4 +706,41 @@
} catch (SecurityException expected) {
}
}
+
+ /**
+ * Tests TelephonyManager.getCurrentEmergencyNumberList.
+ */
+ @Test
+ public void testGetCurrentEmergencyNumberList() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+ Map<Integer, List<EmergencyNumber>> emergencyNumberList
+ = mTelephonyManager.getCurrentEmergencyNumberList();
+ // TODO enhance it later
+ }
+
+ /**
+ * Tests TelephonyManager.isCurrentEmergencyNumber.
+ */
+ @Test
+ public void testIsCurrentEmergencyNumber() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+ boolean isEmergencyNumber = mTelephonyManager.isCurrentEmergencyNumber("911");
+ // TODO enhance it later
+ }
+
+ /**
+ * Tests TelephonyManager.isCurrentPotentialEmergencyNumber.
+ */
+ @Test
+ public void testIsCurrentPotentialEmergencyNumber() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+ boolean isEmergencyNumber = mTelephonyManager.isCurrentPotentialEmergencyNumber("911");
+ // TODO enhance it later
+ }
}
diff --git a/tests/tests/text/src/android/text/style/cts/LineBackgroundSpan_StandardTest.java b/tests/tests/text/src/android/text/style/cts/LineBackgroundSpan_StandardTest.java
index 9e5825d..3440484 100644
--- a/tests/tests/text/src/android/text/style/cts/LineBackgroundSpan_StandardTest.java
+++ b/tests/tests/text/src/android/text/style/cts/LineBackgroundSpan_StandardTest.java
@@ -50,8 +50,9 @@
try {
span.writeToParcel(p, 0);
p.setDataPosition(0);
- LineBackgroundSpan.Standard parcelSpan = new LineBackgroundSpan.Standard(p);
+ final LineBackgroundSpan.Standard parcelSpan = new LineBackgroundSpan.Standard(p);
assertEquals(COLOR, parcelSpan.getColor());
+ assertEquals(span.getSpanTypeId(), parcelSpan.getSpanTypeId());
} finally {
p.recycle();
}
@@ -72,7 +73,7 @@
span.drawBackground(canvas, paint, left, right, top, baseline, bottom,
text, 0, text.length(), 0);
- verify(canvas).drawRect(left, right, top, bottom, paint);
+ verify(canvas).drawRect(left, top, right, bottom, paint);
}
}
diff --git a/tests/tests/view/src/android/view/textclassifier/cts/ConversationActionsTest.java b/tests/tests/view/src/android/view/textclassifier/cts/ConversationActionsTest.java
index ab56bce..54f6166 100644
--- a/tests/tests/view/src/android/view/textclassifier/cts/ConversationActionsTest.java
+++ b/tests/tests/view/src/android/view/textclassifier/cts/ConversationActionsTest.java
@@ -45,6 +45,8 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ConversationActionsTest {
+ private static final String ID = "ID";
+ private static final String CONVERSATION_ID = "conversation_id";
private static final String TEXT = "TEXT";
private static final Person PERSON = new Person.Builder().setKey(TEXT).build();
private static final ZonedDateTime TIME =
@@ -170,6 +172,7 @@
.build();
ConversationActions.Request request =
new ConversationActions.Request.Builder(Collections.singletonList(message))
+ .setConversationId(CONVERSATION_ID)
.setHints(Collections.singletonList(ConversationActions.HINT_FOR_IN_APP))
.setMaxSuggestions(10)
.setTypeConfig(typeConfig)
@@ -217,20 +220,37 @@
}
@Test
- public void testConversationActions() {
+ public void testConversationActions_full() {
ConversationActions.ConversationAction conversationAction =
new ConversationActions.ConversationAction.Builder(
ConversationActions.TYPE_CALL_PHONE)
.build();
ConversationActions conversationActions =
- new ConversationActions(Arrays.asList(conversationAction));
+ new ConversationActions(Arrays.asList(conversationAction), ID);
ConversationActions recovered =
parcelizeDeparcelize(conversationActions, ConversationActions.CREATOR);
- assertConversationActions(conversationActions);
- assertConversationActions(recovered);
+ assertFullConversationActions(conversationActions);
+ assertFullConversationActions(recovered);
+ }
+
+ @Test
+ public void testConversationActions_minimal() {
+ ConversationActions.ConversationAction conversationAction =
+ new ConversationActions.ConversationAction.Builder(
+ ConversationActions.TYPE_CALL_PHONE)
+ .build();
+
+ ConversationActions conversationActions =
+ new ConversationActions(Arrays.asList(conversationAction), null);
+
+ ConversationActions recovered =
+ parcelizeDeparcelize(conversationActions, ConversationActions.CREATOR);
+
+ assertMinimalConversationActions(conversationActions);
+ assertMinimalConversationActions(recovered);
}
private void assertFullMessage(ConversationActions.Message message) {
@@ -289,6 +309,7 @@
assertThat(request.getHints()).isEmpty();
assertThat(request.getMaxSuggestions()).isEqualTo(0);
assertThat(request.getTypeConfig()).isNotNull();
+ assertThat(request.getConversationId()).isNull();
}
private void assertFullRequest(ConversationActions.Request request) {
@@ -298,6 +319,7 @@
assertThat(request.getHints()).containsExactly(ConversationActions.HINT_FOR_IN_APP);
assertThat(request.getMaxSuggestions()).isEqualTo(10);
assertThat(request.getTypeConfig().shouldIncludeTypesFromTextClassifier()).isFalse();
+ assertThat(request.getConversationId()).isEqualTo(CONVERSATION_ID);
}
private void assertMinimalConversationAction(
@@ -316,10 +338,18 @@
assertThat(conversationAction.getExtras().keySet()).containsExactly(TEXT);
}
- private void assertConversationActions(ConversationActions conversationActions) {
+ private void assertMinimalConversationActions(ConversationActions conversationActions) {
assertThat(conversationActions.getConversationActions()).hasSize(1);
assertThat(conversationActions.getConversationActions().get(0).getType())
.isEqualTo(ConversationActions.TYPE_CALL_PHONE);
+ assertThat(conversationActions.getId()).isNull();
+ }
+
+ private void assertFullConversationActions(ConversationActions conversationActions) {
+ assertThat(conversationActions.getConversationActions()).hasSize(1);
+ assertThat(conversationActions.getConversationActions().get(0).getType())
+ .isEqualTo(ConversationActions.TYPE_CALL_PHONE);
+ assertThat(conversationActions.getId()).isEqualTo(ID);
}
private <T extends Parcelable> T parcelizeDeparcelize(
diff --git a/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationManagerTest.java b/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationManagerTest.java
index add14d9..1cd65b8 100644
--- a/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationManagerTest.java
+++ b/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationManagerTest.java
@@ -36,6 +36,7 @@
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextClassifierEvent;
import android.view.textclassifier.TextLanguage;
import android.view.textclassifier.TextLinks;
import android.view.textclassifier.TextSelection;
@@ -199,6 +200,12 @@
}
@Test
+ public void testOnTextClassifierEvent() {
+ // Doesn't crash.
+ mClassifier.onTextClassifierEvent(new TextClassifierEvent.Builder(0, 0).build());
+ }
+
+ @Test
public void testLanguageDetection() {
assertValidResult(mClassifier.detectLanguage(TEXT_LANGUAGE_REQUEST));
}
diff --git a/tests/tests/view/src/android/view/textclassifier/cts/TextClassifierEventTest.java b/tests/tests/view/src/android/view/textclassifier/cts/TextClassifierEventTest.java
new file mode 100644
index 0000000..f1ca661
--- /dev/null
+++ b/tests/tests/view/src/android/view/textclassifier/cts/TextClassifierEventTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.textclassifier.TextClassificationContext;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextClassifierEvent;
+import android.widget.TextView;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TextClassifierEventTest {
+
+ @Test
+ public void testMinimumEvent() {
+ final TextClassifierEvent event = new TextClassifierEvent.Builder(
+ TextClassifierEvent.CATEGORY_UNDEFINED, TextClassifierEvent.TYPE_UNDEFINED)
+ .build();
+
+ assertThat(event.getEventCategory()).isEqualTo(TextClassifierEvent.CATEGORY_UNDEFINED);
+ assertThat(event.getEventType()).isEqualTo(TextClassifierEvent.TYPE_UNDEFINED);
+ assertThat(event.getEventIndex()).isEqualTo(0);
+ assertThat(event.getEventTime()).isEqualTo(0);
+ assertThat(event.getEntityType()).isNull();
+ assertThat(event.getRelativeWordStartIndex()).isEqualTo(0);
+ assertThat(event.getRelativeWordEndIndex()).isEqualTo(0);
+ assertThat(event.getRelativeSuggestedWordStartIndex()).isEqualTo(0);
+ assertThat(event.getRelativeSuggestedWordEndIndex()).isEqualTo(0);
+ assertThat(event.getLanguage()).isNull();
+ assertThat(event.getResultId()).isNull();
+ assertThat(event.getActionIndices()).isEmpty();
+ assertThat(event.getExtras()).isEqualTo(Bundle.EMPTY);
+ assertThat(event.getEventContext()).isNull();
+ }
+
+ @Test
+ public void testFullEvent() {
+ final Bundle extra = new Bundle();
+ extra.putString("key", "value");
+ final long now = System.currentTimeMillis();
+ final String resultId = "androidtc-en-v606-1234";
+ final TextClassifierEvent event = new TextClassifierEvent.Builder(
+ TextClassifierEvent.CATEGORY_LINKIFY,
+ TextClassifierEvent.TYPE_LINK_CLICKED)
+ .setEventIndex(2)
+ .setEventTime(now)
+ .setEntityType(TextClassifier.TYPE_ADDRESS)
+ .setRelativeWordStartIndex(1)
+ .setRelativeWordEndIndex(2)
+ .setRelativeSuggestedWordStartIndex(-1)
+ .setRelativeSuggestedWordEndIndex(3)
+ .setLanguage("en")
+ .setResultId(resultId)
+ .setActionIndices(1, 2, 5)
+ .setExtras(extra)
+ .setEventContext(new TextClassificationContext.Builder(
+ "pkg", TextClassifier.WIDGET_TYPE_TEXTVIEW)
+ .setWidgetVersion(TextView.class.getName())
+ .build())
+ .build();
+
+ assertThat(event.getEventCategory()).isEqualTo(TextClassifierEvent.CATEGORY_LINKIFY);
+ assertThat(event.getEventType()).isEqualTo(TextClassifierEvent.TYPE_LINK_CLICKED);
+ assertThat(event.getEventIndex()).isEqualTo(2);
+ assertThat(event.getEventTime()).isEqualTo(now);
+ assertThat(event.getEntityType()).isEqualTo(TextClassifier.TYPE_ADDRESS);
+ assertThat(event.getRelativeWordStartIndex()).isEqualTo(1);
+ assertThat(event.getRelativeWordEndIndex()).isEqualTo(2);
+ assertThat(event.getRelativeSuggestedWordStartIndex()).isEqualTo(-1);
+ assertThat(event.getRelativeSuggestedWordEndIndex()).isEqualTo(3);
+ assertThat(event.getLanguage()).isEqualTo("en");
+ assertThat(event.getResultId()).isEqualTo(resultId);
+ assertThat(event.getActionIndices()).asList().containsExactly(1, 2, 5);
+ assertThat(event.getExtras().get("key")).isEqualTo("value");
+ assertThat(event.getEventContext().getPackageName()).isEqualTo("pkg");
+ assertThat(event.getEventContext().getWidgetType())
+ .isEqualTo(TextClassifier.WIDGET_TYPE_TEXTVIEW);
+ assertThat(event.getEventContext().getWidgetVersion()).isEqualTo(TextView.class.getName());
+ }
+
+ @Test
+ public void testParcelUnparcel() {
+ final Bundle extra = new Bundle();
+ extra.putString("k", "v");
+ final TextClassifierEvent event = new TextClassifierEvent.Builder(
+ TextClassifierEvent.CATEGORY_SELECTION,
+ TextClassifierEvent.TYPE_SELECTION_MODIFIED)
+ .setEventIndex(1)
+ .setEventTime(1000)
+ .setEntityType(TextClassifier.TYPE_DATE)
+ .setRelativeWordStartIndex(4)
+ .setRelativeWordEndIndex(3)
+ .setRelativeSuggestedWordStartIndex(2)
+ .setRelativeSuggestedWordEndIndex(1)
+ .setLanguage("de")
+ .setResultId("id")
+ .setActionIndices(3)
+ .setExtras(extra)
+ .setEventContext(new TextClassificationContext.Builder(
+ InstrumentationRegistry.getTargetContext().getPackageName(),
+ TextClassifier.WIDGET_TYPE_UNSELECTABLE_TEXTVIEW)
+ .setWidgetVersion("notification")
+ .build())
+ .build();
+
+ final Parcel parcel = Parcel.obtain();
+ event.writeToParcel(parcel, event.describeContents());
+ parcel.setDataPosition(0);
+ final TextClassifierEvent result = TextClassifierEvent.CREATOR.createFromParcel(parcel);
+
+ assertThat(result.getEventCategory()).isEqualTo(TextClassifierEvent.CATEGORY_SELECTION);
+ assertThat(result.getEventType()).isEqualTo(TextClassifierEvent.TYPE_SELECTION_MODIFIED);
+ assertThat(result.getEventIndex()).isEqualTo(1);
+ assertThat(result.getEventTime()).isEqualTo(1000);
+ assertThat(result.getEntityType()).isEqualTo(TextClassifier.TYPE_DATE);
+ assertThat(result.getRelativeWordStartIndex()).isEqualTo(4);
+ assertThat(result.getRelativeWordEndIndex()).isEqualTo(3);
+ assertThat(result.getRelativeSuggestedWordStartIndex()).isEqualTo(2);
+ assertThat(result.getRelativeSuggestedWordEndIndex()).isEqualTo(1);
+ assertThat(result.getLanguage()).isEqualTo("de");
+ assertThat(result.getResultId()).isEqualTo("id");
+ assertThat(result.getActionIndices()).asList().containsExactly(3);
+ assertThat(result.getExtras().get("k")).isEqualTo("v");
+ assertThat(result.getEventContext().getPackageName())
+ .isEqualTo(InstrumentationRegistry.getTargetContext().getPackageName());
+ assertThat(result.getEventContext().getWidgetType())
+ .isEqualTo(TextClassifier.WIDGET_TYPE_UNSELECTABLE_TEXTVIEW);
+ assertThat(result.getEventContext().getWidgetVersion()).isEqualTo("notification");
+ }
+}
diff --git a/tests/tests/view/src/android/view/textclassifier/cts/TextClassifierValueObjectsTest.java b/tests/tests/view/src/android/view/textclassifier/cts/TextClassifierValueObjectsTest.java
index 9212f0f..03e080e 100644
--- a/tests/tests/view/src/android/view/textclassifier/cts/TextClassifierValueObjectsTest.java
+++ b/tests/tests/view/src/android/view/textclassifier/cts/TextClassifierValueObjectsTest.java
@@ -168,7 +168,7 @@
final TextSelection.Request request = new TextSelection.Request.Builder(TEXT, START, END)
.setDefaultLocales(LOCALES)
.build();
- assertEquals(TEXT, request.getText());
+ assertEquals(TEXT, request.getText().toString());
assertEquals(START, request.getStartIndex());
assertEquals(END, request.getEndIndex());
assertEquals(LOCALES, request.getDefaultLocales());
diff --git a/tests/tests/widget/res/layout/textview_isHorizontallyScrolling_layout.xml b/tests/tests/widget/res/layout/textview_isHorizontallyScrolling_layout.xml
new file mode 100644
index 0000000..e2d3aaa
--- /dev/null
+++ b/tests/tests/widget/res/layout/textview_isHorizontallyScrolling_layout.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TextView
+ android:id="@+id/textView_scrollHorizontally_default"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <!-- In this case, scrollHorizontally is overwritten by singleLine, which is a bug. -->
+ <TextView
+ android:id="@+id/textView_scrollHorizontally_true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollHorizontally="true" />
+
+ <TextView
+ android:id="@+id/textView_scrollHorizontally_false"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollHorizontally="false" />
+
+ <TextView
+ android:id="@+id/textView_scrollHorizontally_default_singleLine_true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true" />
+
+ <TextView
+ android:id="@+id/textView_scrollHorizontally_true_singleLine_true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollHorizontally="true"
+ android:singleLine="true" />
+
+ <TextView
+ android:id="@+id/textView_scrollHorizontally_false_singleLine_true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollHorizontally="false"
+ android:singleLine="true" />
+
+ <TextView
+ android:id="@+id/textView_scrollHorizontally_default_singleLine_false"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="false" />
+
+ <TextView
+ android:id="@+id/textView_scrollHorizontally_false_singleLine_false"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollHorizontally="false"
+ android:singleLine="false" />
+
+ <!-- In this case, scrollHorizontally is overwritten by singleLine, which is a bug. -->
+ <TextView
+ android:id="@+id/textView_scrollHorizontally_true_singleLine_false"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollHorizontally="true"
+ android:singleLine="false" />
+</LinearLayout>
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewIsHorizontallyScrollingTest.java b/tests/tests/widget/src/android/widget/cts/TextViewIsHorizontallyScrollingTest.java
new file mode 100644
index 0000000..f3e3847
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/TextViewIsHorizontallyScrollingTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget.cts;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+/**
+ * Test {@link TextView#isHorizontallyScrolling}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TextViewIsHorizontallyScrollingTest {
+ private ViewGroup mViewGroup;
+
+ @Before
+ public void setup() {
+ final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ final LayoutInflater inflater = LayoutInflater.from(context);
+ mViewGroup = (ViewGroup) inflater.inflate(R.layout.textview_isHorizontallyScrolling_layout,
+ null);
+ }
+
+ @Test
+ public void testIsHorizontallyScrollingDefaultIsFalse() {
+ final TextView textView = mViewGroup.findViewById(
+ R.id.textView_scrollHorizontally_default);
+
+ assertFalse(textView.isHorizontallyScrolling());
+ }
+
+ @Test
+ public void testIsHorizontallyScrollingSameAsGiven() {
+ final TextView textView = mViewGroup.findViewById(
+ R.id.textView_scrollHorizontally_default);
+
+ textView.setHorizontallyScrolling(true);
+ assertTrue(textView.isHorizontallyScrolling());
+ }
+
+ @Test
+ public void testIsHorizontallyScrollingTrueToFalse() {
+ final TextView textView = mViewGroup.findViewById(
+ R.id.textView_scrollHorizontally_default);
+ textView.setHorizontallyScrolling(true);
+ assertTrue(textView.isHorizontallyScrolling());
+
+ textView.setHorizontallyScrolling(false);
+ assertFalse(textView.isHorizontallyScrolling());
+ }
+
+ @Test
+ public void testIsHorizontallyScrollingSetInXML() {
+ final TextView textViewTrue = mViewGroup.findViewById(
+ R.id.textView_scrollHorizontally_true);
+ // It should return true here. But because of this bug b/120448952,
+ // singleLine will overwrite scrollHorizontally.
+ assertFalse(textViewTrue.isHorizontallyScrolling());
+
+ final TextView textViewFalse = mViewGroup.findViewById(
+ R.id.textView_scrollHorizontally_false);
+ assertFalse(textViewFalse.isHorizontallyScrolling());
+ }
+
+ @Test
+ public void testIsHorizontallyScrollingSetInXML_returnTrueWhenSingleLineIsTrue() {
+ final TextView textViewDefault = mViewGroup.findViewById(
+ R.id.textView_scrollHorizontally_default_singleLine_true);
+ assertTrue(textViewDefault.isHorizontallyScrolling());
+
+ final TextView textViewTrue = mViewGroup.findViewById(
+ R.id.textView_scrollHorizontally_true_singleLine_true);
+ assertTrue(textViewTrue.isHorizontallyScrolling());
+
+ final TextView textViewFalse = mViewGroup.findViewById(
+ R.id.textView_scrollHorizontally_false_singleLine_true);
+ assertTrue(textViewFalse.isHorizontallyScrolling());
+ }
+
+ @Test
+ public void testIsHorizontallyScrollingSetInXML_returnGivenValueWhenSingleLineIsFalse() {
+ final TextView textViewDefault = mViewGroup.findViewById(
+ R.id.textView_scrollHorizontally_default_singleLine_false);
+ assertFalse(textViewDefault.isHorizontallyScrolling());
+
+ final TextView textViewTrue = mViewGroup.findViewById(
+ R.id.textView_scrollHorizontally_true_singleLine_false);
+ // It should return true here. But because of this bug b/120448952,
+ // singleLine will overwrite scrollHorizontally.
+ assertFalse(textViewTrue.isHorizontallyScrolling());
+
+ final TextView textViewFalse = mViewGroup.findViewById(
+ R.id.textView_scrollHorizontally_false_singleLine_false);
+ assertFalse(textViewFalse.isHorizontallyScrolling());
+ }
+}
diff --git a/tests/tvprovider/src/android/tvprovider/cts/TvProviderPerfTest.java b/tests/tvprovider/src/android/tvprovider/cts/TvProviderPerfTest.java
index e9c7ae0..ac8eed99 100644
--- a/tests/tvprovider/src/android/tvprovider/cts/TvProviderPerfTest.java
+++ b/tests/tvprovider/src/android/tvprovider/cts/TvProviderPerfTest.java
@@ -30,6 +30,7 @@
import android.media.tv.TvContract.Programs;
import android.net.Uri;
import android.os.RemoteException;
+import android.util.Log;
import com.android.compatibility.common.util.CtsAndroidTestCase;
import com.android.compatibility.common.util.DeviceReportLog;
@@ -51,6 +52,7 @@
private static final int TRANSACTION_RUNS = 100;
private static final int QUERY_RUNS = 10;
private static final String REPORT_LOG_NAME = "CtsTvProviderTestCases";
+ private static final String TAG = "TvProviderPerfTest";
private ContentResolver mContentResolver;
private String mInputId;
@@ -81,11 +83,16 @@
if (!mHasTvInputFramework) return;
double[] averages = new double[5];
- // Insert
+ Log.d(TAG, "Insert");
final ArrayList<ContentProviderOperation> operations = new ArrayList<>();
final int TRANSACTION_SIZE = 1000;
double[] applyBatchTimes = MeasureTime.measure(TRANSACTION_RUNS, new MeasureRun() {
@Override
+ public void prepare(int i) {
+ mContentResolver.delete(Channels.CONTENT_URI, null, null);
+ }
+
+ @Override
public void run(int i) {
operations.clear();
for (int j = 0; j < TRANSACTION_SIZE; ++j) {
@@ -111,7 +118,7 @@
ResultUnit.MS);
averages[0] = Stat.getAverage(applyBatchTimes);
- // Update
+ Log.d(TAG, "Update");
final String[] projection = { Channels._ID };
try (final Cursor cursor = mContentResolver.query(Channels.CONTENT_URI,
projection, null, null, null)) {
@@ -139,7 +146,7 @@
ResultUnit.MS);
averages[1] = Stat.getAverage(applyBatchTimes);
- // Query channels
+ Log.d(TAG, "Query channels");
applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() {
@Override
public void run(int i) {
@@ -155,7 +162,7 @@
ResultType.LOWER_BETTER, ResultUnit.MS);
averages[2] = Stat.getAverage(applyBatchTimes);
- // Query a channel
+ Log.d(TAG, "Query a channel");
try (final Cursor cursor = mContentResolver.query(Channels.CONTENT_URI,
projection, null, null, null)) {
applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() {
@@ -175,7 +182,7 @@
ResultType.LOWER_BETTER, ResultUnit.MS);
averages[3] = Stat.getAverage(applyBatchTimes);
- // Delete
+ Log.d(TAG, "Delete");
applyBatchTimes = MeasureTime.measure(1, new MeasureRun() {
@Override
public void run(int i) {
@@ -195,7 +202,7 @@
if (!mHasTvInputFramework) return;
double[] averages = new double[7];
- // Prepare (insert channels)
+ Log.d(TAG, "Prepare (insert channels)");
final ArrayList<ContentProviderOperation> operations = new ArrayList<>();
final int TRANSACTION_SIZE = 1000;
final int NUM_CHANNELS = 100;
@@ -222,7 +229,7 @@
throw new RuntimeException(e);
}
- // Insert
+ Log.d(TAG, "Insert programs");
double[] applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() {
@Override
public void run(int i) {
@@ -249,7 +256,7 @@
ResultUnit.MS);
averages[0] = Stat.getAverage(applyBatchTimes);
- // Update
+ Log.d(TAG, "Update programs");
final long PROGRAM_DURATION_MS = 60 * 1000;
final String[] projection = { Programs._ID };
applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() {
@@ -284,7 +291,7 @@
ResultUnit.MS);
averages[1] = Stat.getAverage(applyBatchTimes);
- // Query programs
+ Log.d(TAG, "Query programs");
applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() {
@Override
public void run(int i) {
@@ -300,7 +307,7 @@
ResultType.LOWER_BETTER, ResultUnit.MS);
averages[2] = Stat.getAverage(applyBatchTimes);
- // Query programs with selection
+ Log.d(TAG, "Query programs with selection");
applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() {
@Override
public void run(int i) {
@@ -320,7 +327,7 @@
ResultType.LOWER_BETTER, ResultUnit.MS);
averages[3] = Stat.getAverage(applyBatchTimes);
- // Query a program
+ Log.d(TAG, "Query a program");
try (final Cursor cursor = mContentResolver.query(Programs.CONTENT_URI,
projection, null, null, null)) {
applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() {
@@ -340,7 +347,7 @@
ResultType.LOWER_BETTER, ResultUnit.MS);
averages[4] = Stat.getAverage(applyBatchTimes);
- // Delete programs
+ Log.d(TAG, "Delete programs");
applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() {
@Override
public void run(int i) {
@@ -357,7 +364,7 @@
ResultType.LOWER_BETTER, ResultUnit.MS);
averages[5] = Stat.getAverage(applyBatchTimes);
- // Delete channels
+ Log.d(TAG, "Delete channels");
applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() {
@Override
public void run(int i) {
diff --git a/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java b/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java
index 20c5c27..160c9b5 100644
--- a/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java
+++ b/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java
@@ -487,6 +487,7 @@
charsKeyNames.add(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM.getName());
charsKeyNames.add(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP.getName());
charsKeyNames.add(CameraCharacteristics.SCALER_CROPPING_TYPE.getName());
+ charsKeyNames.add(CameraCharacteristics.SCALER_MANDATORY_STREAM_COMBINATIONS.getName());
charsKeyNames.add(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1.getName());
charsKeyNames.add(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2.getName());
charsKeyNames.add(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1.getName());