Merge "TIF: Add test cases for DVR APIs"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 29f027e..e09cccd 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -4,6 +4,7 @@
[Builtin Hooks Options]
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
+ hostsidetests
tests/tests/binder_ndk
[Hook Scripts]
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsSerializer.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsSerializer.java
index fbd7522..37df8ad 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsSerializer.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsSerializer.java
@@ -483,6 +483,7 @@
CameraCharacteristics chars = (CameraCharacteristics) md;
List<CameraCharacteristics.Key<?>> charsKeys = chars.getKeys();
List<CaptureRequest.Key<?>> requestKeys = chars.getAvailableCaptureRequestKeys();
+ List<CaptureResult.Key<?>> resultKeys = chars.getAvailableCaptureResultKeys();
Set<String> physicalCamIds = chars.getPhysicalCameraIds();
try {
@@ -494,9 +495,14 @@
for (CaptureRequest.Key<?> k : requestKeys) {
reqKeysArr.put(k.getName());
}
+ JSONArray resKeysArr = new JSONArray();
+ for (CaptureResult.Key<?> k : resultKeys) {
+ resKeysArr.put(k.getName());
+ }
// Avoid using the hidden metadata key name here to prevent confliction
jsonObj.put("camera.characteristics.keys", charKeysArr);
jsonObj.put("camera.characteristics.requestKeys", reqKeysArr);
+ jsonObj.put("camera.characteristics.resultKeys", resKeysArr);
if (!physicalCamIds.isEmpty()) {
JSONArray physCamIdsArr = new JSONArray();
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
index 9eb97c5..8b1e28c 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
@@ -45,6 +45,7 @@
private static final String PERMISSION_PROTECTION = "protection_level";
private static final String PERMISSION_PROTECTION_FLAGS = "protection_level_flags";
+ private static final int SYS_UID_MAX = 10000;
private static final String HAS_SYSTEM_UID = "has_system_uid";
private static final String SHARES_INSTALL_PERMISSION = "shares_install_packages_permission";
@@ -53,7 +54,6 @@
@Override
protected void collectDeviceInfo(DeviceInfoStore store) throws Exception {
final PackageManager pm = getContext().getPackageManager();
- final ApplicationInfo system = pm.getApplicationInfo("android", 0);
final List<PackageInfo> allPackages = pm.getInstalledPackages(PackageManager.GET_PERMISSIONS);
@@ -92,7 +92,7 @@
store.addResult(MIN_SDK, appInfo.minSdkVersion);
store.addResult(TARGET_SDK, appInfo.targetSdkVersion);
- store.addResult(HAS_SYSTEM_UID, appInfo.uid == system.uid);
+ store.addResult(HAS_SYSTEM_UID, appInfo.uid < SYS_UID_MAX);
final boolean canInstall = sharesUidWithPackageHolding(pm, appInfo.uid, INSTALL_PACKAGES_PERMISSION);
store.addResult(SHARES_INSTALL_PERMISSION, canInstall);
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/ShellIdentityUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/ShellIdentityUtils.java
index dc6f6e8..948baaf 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/ShellIdentityUtils.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/ShellIdentityUtils.java
@@ -62,6 +62,21 @@
*
* @param <U> the type of the object against which the method is invoked.
*/
+ public interface ShellPermissionThrowableMethodHelper<T, U, E extends Throwable> {
+ /**
+ * Invokes the method against the target object.
+ *
+ * @param targetObject the object against which the method should be invoked.
+ * @return the result of the target method.
+ */
+ T callMethod(U targetObject) throws E;
+ }
+
+ /**
+ * Utility interface to invoke a method against the target object that may throw an Exception.
+ *
+ * @param <U> the type of the object against which the method is invoked.
+ */
public interface ShellPermissionThrowableMethodHelperNoReturn<U, E extends Throwable> {
/**
* Invokes the method against the target object.
@@ -184,6 +199,27 @@
}
/**
+ * Invokes the specified method on the targetObject as the shell user with only the subset of
+ * permissions specified. The method can be invoked as follows:
+ *
+ * {@code ShellIdentityUtils.invokeMethodWithShellPermissions(mRcsUceAdapter,
+ * (m) -> RcsUceAdapter::getUcePublishState, ImsException.class,
+ * "android.permission.READ_PRIVILEGED_PHONE_STATE")}
+ */
+ public static <T, U, E extends Throwable> T invokeThrowableMethodWithShellPermissions(
+ U targetObject, ShellPermissionThrowableMethodHelper<T, U, E> methodHelper,
+ Class<E> clazz, String... permissions) throws E {
+ final UiAutomation uiAutomation =
+ InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ try {
+ uiAutomation.adoptShellPermissionIdentity(permissions);
+ return methodHelper.callMethod(targetObject);
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ /**
* Invokes the specified method on the targetObject as the shell user for only the permissions
* specified. The method can be invoked as follows:
*
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/MainlineModule.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/MainlineModule.java
index 0907d30..6e48bcc 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/MainlineModule.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/MainlineModule.java
@@ -50,10 +50,10 @@
"9A:4B:85:34:44:86:EC:F5:1F:F8:05:EB:9D:23:17:97:79:BE:B7:EC:81:91:93:5A:CA:67:F0"
+ ":F4:09:02:52:97"),
// Consistency
- TZDATA("com.google.android.tzdata2",
+ TZDATA2("com.google.android.tzdata2",
true, ModuleType.APEX,
- "55:93:DD:78:CB:26:EC:9B:00:59:2A:6A:F5:94:E4:16:1F:FD:B5:E9:F3:71:A7:43:54:5F:93"
- + ":F2:A0:F6:53:89"),
+ "48:F3:A2:98:76:1B:6D:46:75:7C:EE:62:43:66:6A:25:B9:15:B9:42:18:A6:C2:82:72:99:BE"
+ + ":DA:C9:92:AB:E7"),
NETWORK_STACK("com.google.android.networkstack",
true, ModuleType.APK,
"5F:A4:22:12:AD:40:3E:22:DD:6E:FE:75:F3:F3:11:84:05:1F:EF:74:4C:0B:05:BE:5C:73:ED"
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
index ec3594e..5372bc1 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
@@ -83,8 +83,8 @@
"com.android.cts.mediastorageapp", MEDIA_CLAZZ);
private static final Config MEDIA_28 = new Config("CtsMediaStorageApp28.apk",
"com.android.cts.mediastorageapp28", MEDIA_CLAZZ);
- private static final Config MEDIA_FULL = new Config("CtsMediaStorageAppFull.apk",
- "com.android.cts.mediastorageappfull", MEDIA_CLAZZ);
+ private static final Config MEDIA_29 = new Config("CtsMediaStorageApp29.apk",
+ "com.android.cts.mediastorageapp29", MEDIA_CLAZZ);
private static final String PERM_READ_EXTERNAL_STORAGE = "android.permission.READ_EXTERNAL_STORAGE";
private static final String PERM_WRITE_EXTERNAL_STORAGE = "android.permission.WRITE_EXTERNAL_STORAGE";
@@ -478,26 +478,26 @@
doMediaSandboxed(MEDIA_28, false);
}
@Test
- public void testMediaSandboxedFull() throws Exception {
- doMediaSandboxed(MEDIA_FULL, false);
+ public void testMediaSandboxed29() throws Exception {
+ doMediaSandboxed(MEDIA_29, false);
}
private void doMediaSandboxed(Config config, boolean sandboxed) throws Exception {
installPackage(config.apk);
- installPackage(MEDIA_FULL.apk);
+ installPackage(MEDIA_29.apk);
for (int user : mUsers) {
updatePermissions(config.pkg, user, new String[] {
PERM_READ_EXTERNAL_STORAGE,
PERM_WRITE_EXTERNAL_STORAGE,
}, true);
- updatePermissions(MEDIA_FULL.pkg, user, new String[] {
+ updatePermissions(MEDIA_29.pkg, user, new String[] {
PERM_READ_EXTERNAL_STORAGE,
PERM_WRITE_EXTERNAL_STORAGE,
}, true);
- // Create the files needed for the test from MEDIA_FULL pkg since shell
+ // Create the files needed for the test from MEDIA_29 pkg since shell
// can't access secondary user's storage.
- runDeviceTests(MEDIA_FULL.pkg, MEDIA_FULL.clazz, "testStageFiles", user);
+ runDeviceTests(MEDIA_29.pkg, MEDIA_29.clazz, "testStageFiles", user);
if (sandboxed) {
runDeviceTests(config.pkg, config.clazz, "testSandboxed", user);
@@ -505,7 +505,7 @@
runDeviceTests(config.pkg, config.clazz, "testNotSandboxed", user);
}
- runDeviceTests(MEDIA_FULL.pkg, MEDIA_FULL.clazz, "testClearFiles", user);
+ runDeviceTests(MEDIA_29.pkg, MEDIA_29.clazz, "testClearFiles", user);
}
}
@@ -518,8 +518,8 @@
doMediaNone(MEDIA_28);
}
@Test
- public void testMediaNoneFull() throws Exception {
- doMediaNone(MEDIA_FULL);
+ public void testMediaNone29() throws Exception {
+ doMediaNone(MEDIA_29);
}
private void doMediaNone(Config config) throws Exception {
@@ -543,8 +543,8 @@
doMediaRead(MEDIA_28);
}
@Test
- public void testMediaReadFull() throws Exception {
- doMediaRead(MEDIA_FULL);
+ public void testMediaRead29() throws Exception {
+ doMediaRead(MEDIA_29);
}
private void doMediaRead(Config config) throws Exception {
@@ -570,8 +570,8 @@
doMediaWrite(MEDIA_28);
}
@Test
- public void testMediaWriteFull() throws Exception {
- doMediaWrite(MEDIA_FULL);
+ public void testMediaWrite29() throws Exception {
+ doMediaWrite(MEDIA_29);
}
private void doMediaWrite(Config config) throws Exception {
@@ -595,8 +595,8 @@
doMediaEscalation(MEDIA_28);
}
@Test
- public void testMediaEscalationFull() throws Exception {
- doMediaEscalation(MEDIA_FULL);
+ public void testMediaEscalation29() throws Exception {
+ doMediaEscalation(MEDIA_29);
}
private void doMediaEscalation(Config config) throws Exception {
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
index 2b45911..7130c14 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
@@ -23,6 +23,7 @@
import static org.hamcrest.CoreMatchers.containsString;
import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.FlakyTest;
import android.platform.test.annotations.Presubmit;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
@@ -501,12 +502,14 @@
"reviewPermissionWhenServiceIsBound");
}
+ @FlakyTest
public void testGrantDialogToSettingsNoOp() throws Exception {
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_29), true, false));
runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest29",
"openSettingsFromGrantNoOp");
}
+ @FlakyTest
public void testGrantDialogToSettingsDowngrade() throws Exception {
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_29), false, false));
runThrowingTest("com.android.cts.usepermission.UsePermissionTest29",
diff --git a/hostsidetests/appsecurity/test-apps/MediaStorageApp/Android.bp b/hostsidetests/appsecurity/test-apps/MediaStorageApp/Android.bp
index 22cc2f4..1f2b551 100644
--- a/hostsidetests/appsecurity/test-apps/MediaStorageApp/Android.bp
+++ b/hostsidetests/appsecurity/test-apps/MediaStorageApp/Android.bp
@@ -52,7 +52,7 @@
}
android_test_helper_app {
- name: "CtsMediaStorageAppFull",
+ name: "CtsMediaStorageApp29",
defaults: ["cts_support_defaults"],
sdk_version: "test_current",
static_libs: [
@@ -68,5 +68,5 @@
"vts",
"general-tests",
],
- manifest: "AndroidManifestFull.xml",
+ manifest: "AndroidManifest29.xml",
}
diff --git a/hostsidetests/appsecurity/test-apps/MediaStorageApp/AndroidManifestFull.xml b/hostsidetests/appsecurity/test-apps/MediaStorageApp/AndroidManifest29.xml
similarity index 88%
rename from hostsidetests/appsecurity/test-apps/MediaStorageApp/AndroidManifestFull.xml
rename to hostsidetests/appsecurity/test-apps/MediaStorageApp/AndroidManifest29.xml
index 83b15c3..a73ab0e 100644
--- a/hostsidetests/appsecurity/test-apps/MediaStorageApp/AndroidManifestFull.xml
+++ b/hostsidetests/appsecurity/test-apps/MediaStorageApp/AndroidManifest29.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.mediastorageappfull">
+ package="com.android.cts.mediastorageapp29">
<application
android:requestLegacyExternalStorage="true">
@@ -33,9 +33,12 @@
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.cts.mediastorageappfull" />
+ android:targetPackage="com.android.cts.mediastorageapp29" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-sdk
+ android:minSdkVersion="29"
+ android:targetSdkVersion="29" />
</manifest>
diff --git a/hostsidetests/blobstore/Android.bp b/hostsidetests/blobstore/Android.bp
new file mode 100644
index 0000000..398aff8
--- /dev/null
+++ b/hostsidetests/blobstore/Android.bp
@@ -0,0 +1,52 @@
+// Copyright (C) 2020 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.
+
+java_test_host {
+ name: "CtsBlobStoreHostTestCases",
+ defaults: ["cts_defaults"],
+ srcs: ["src/**/*.java"],
+ libs: [
+ "tools-common-prebuilt",
+ "cts-tradefed",
+ "tradefed",
+ "truth-prebuilt"
+ ],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests"
+ ]
+}
+
+android_test_helper_app {
+ name: "CtsBlobStoreHelperApp",
+ srcs: ["test-apps/BlobStoreHelperApp/src/**/*.java"],
+ static_libs: [
+ "compatibility-device-util-axt",
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "BlobStoreTestUtils",
+ "truth-prebuilt",
+ "testng",
+ ],
+ manifest : "test-apps/BlobStoreHelperApp/AndroidManifest.xml",
+ platform_apis: true,
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests"
+ ]
+}
diff --git a/tests/tests/rcs/AndroidTest.xml b/hostsidetests/blobstore/AndroidTest.xml
similarity index 72%
rename from tests/tests/rcs/AndroidTest.xml
rename to hostsidetests/blobstore/AndroidTest.xml
index b51fd84..eaa301d 100644
--- a/tests/tests/rcs/AndroidTest.xml
+++ b/hostsidetests/blobstore/AndroidTest.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
+<!-- Copyright (C) 2020 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.
@@ -13,22 +13,18 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<configuration description="Config for RCS test cases">
+<configuration description="Config for the CTS BlobStore host tests">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="framework" />
-
- <!-- RCS functionality depends on SMS permissions not available to instant apps. -->
+ <!-- Instant apps can't access BlobStoreManager -->
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
-
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
-
- <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="CtsRcsTestCases.apk" />
- </target_preparer>
- <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
- <option name="package" value="android.telephony.ims.cts" />
- <option name="hidden-api-checks" value="false"/>
+ <test class="com.android.tradefed.testtype.HostTest" >
+ <option name="jar" value="CtsBlobStoreHostTestCases.jar" />
</test>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="test-file-name" value="CtsBlobStoreHelperApp.apk" />
+ <option name="cleanup-apks" value="true" />
+ </target_preparer>
</configuration>
diff --git a/hostsidetests/blobstore/OWNERS b/hostsidetests/blobstore/OWNERS
new file mode 100644
index 0000000..16b25bb
--- /dev/null
+++ b/hostsidetests/blobstore/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 95221
+include platform/frameworks/base:apex/blobstore/OWNERS
diff --git a/hostsidetests/blobstore/TEST_MAPPING b/hostsidetests/blobstore/TEST_MAPPING
new file mode 100644
index 0000000..0305e17
--- /dev/null
+++ b/hostsidetests/blobstore/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsBlobStoreHostTestCases"
+ }
+ ]
+}
diff --git a/hostsidetests/blobstore/src/com/android/cts/host/blob/BaseBlobStoreHostTest.java b/hostsidetests/blobstore/src/com/android/cts/host/blob/BaseBlobStoreHostTest.java
new file mode 100644
index 0000000..9e3701d
--- /dev/null
+++ b/hostsidetests/blobstore/src/com/android/cts/host/blob/BaseBlobStoreHostTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2020 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.cts.host.blob;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
+import com.android.tradefed.util.Pair;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+abstract class BaseBlobStoreHostTest extends BaseHostJUnit4Test {
+ private static final long TIMEOUT_BOOT_COMPLETE_MS = 120_000;
+
+ protected static final String KEY_SESSION_ID = "session";
+
+ protected static final String KEY_DIGEST = "digest";
+ protected static final String KEY_EXPIRY = "expiry";
+ protected static final String KEY_LABEL = "label";
+ protected static final String KEY_TAG = "tag";
+
+ protected static final String KEY_ALLOW_PUBLIC = "public";
+
+ protected void runDeviceTest(String testPkg, String testClass, String testMethod)
+ throws Exception {
+ runDeviceTest(testPkg, testClass, testMethod, null);
+ }
+
+ protected void runDeviceTestAsUser(String testPkg, String testClass, String testMethod,
+ int userId) throws Exception {
+ runDeviceTestAsUser(testPkg, testClass, testMethod, null, userId);
+ }
+
+ protected void runDeviceTest(String testPkg, String testClass, String testMethod,
+ Map<String, String> instrumentationArgs) throws Exception {
+ runDeviceTestAsUser(testPkg, testClass, testMethod, instrumentationArgs, -1);
+ }
+
+ protected void runDeviceTestAsUser(String testPkg, String testClass, String testMethod,
+ Map<String, String> instrumentationArgs, int userId) throws Exception {
+ final DeviceTestRunOptions deviceTestRunOptions = new DeviceTestRunOptions(testPkg)
+ .setTestClassName(testClass)
+ .setTestMethodName(testMethod);
+ if (userId != -1) {
+ deviceTestRunOptions.setUserId(userId);
+ }
+ if (instrumentationArgs != null) {
+ for (Map.Entry<String, String> entry : instrumentationArgs.entrySet()) {
+ deviceTestRunOptions.addInstrumentationArg(entry.getKey(), entry.getValue());
+ }
+ }
+ assertWithMessage(testMethod + " failed").that(
+ runDeviceTests(deviceTestRunOptions)).isTrue();
+ }
+
+ protected void rebootAndWaitUntilReady() throws Exception {
+ // TODO: use rebootUserspace()
+ getDevice().rebootUntilOnline();
+ assertWithMessage("Timed out waiting for device to boot").that(
+ getDevice().waitForBootComplete(TIMEOUT_BOOT_COMPLETE_MS)).isTrue();
+ }
+
+ protected boolean isMultiUserSupported() throws Exception {
+ return getDevice().isMultiUserSupported();
+ }
+
+ protected Map<String, String> createArgsFromLastTestRun() {
+ final Map<String, String> args = new HashMap<>();
+ for (String key : new String[] {
+ KEY_SESSION_ID,
+ KEY_DIGEST,
+ KEY_EXPIRY,
+ KEY_LABEL,
+ KEY_TAG
+ }) {
+ final String value = getLastDeviceRunResults().getRunMetrics().get(key);
+ if (value != null) {
+ args.put(key, value);
+ }
+ }
+ return args;
+ }
+
+ protected Map<String, String> createArgs(Pair<String, String>... keyValues) {
+ return Arrays.stream(keyValues).collect(Collectors.toMap(p -> p.first, p -> p.second));
+ }
+}
\ No newline at end of file
diff --git a/hostsidetests/blobstore/src/com/android/cts/host/blob/BlobStoreMultiUserTest.java b/hostsidetests/blobstore/src/com/android/cts/host/blob/BlobStoreMultiUserTest.java
new file mode 100644
index 0000000..9f58c3e
--- /dev/null
+++ b/hostsidetests/blobstore/src/com/android/cts/host/blob/BlobStoreMultiUserTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 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.cts.host.blob;
+
+import static org.junit.Assume.assumeTrue;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.util.Pair;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Map;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class BlobStoreMultiUserTest extends BaseBlobStoreHostTest {
+ private static final String TARGET_APK = "CtsBlobStoreHelperApp.apk";
+ private static final String TARGET_PKG = "com.android.cts.blob.helper";
+ private static final String TEST_CLASS = TARGET_PKG + ".DataCleanupTest";
+
+ private int mPrimaryUserId;
+ private int mSecondaryUserId;
+
+ @Before
+ public void setUp() throws Exception {
+ assumeTrue("Multi-user is not supported on this device",
+ isMultiUserSupported());
+
+ mPrimaryUserId = getDevice().getPrimaryUserId();
+ mSecondaryUserId = getDevice().createUser("Test_User");
+ assertThat(getDevice().startUser(mSecondaryUserId)).isTrue();
+
+ installPackageAsUser(TARGET_APK, true /* grantPermissions */, mPrimaryUserId);
+ installPackageAsUser(TARGET_APK, true /* grantPermissions */, mSecondaryUserId);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mSecondaryUserId > 0) {
+ getDevice().removeUser(mSecondaryUserId);
+ }
+ }
+
+ @Test
+ public void testCreateAndOpenSession() throws Exception {
+ // Create a session.
+ runDeviceTestAsUser(TARGET_PKG, TEST_CLASS, "testCreateSession",
+ mPrimaryUserId);
+ final Map<String, String> args = createArgsFromLastTestRun();
+ // Verify that previously created session can be accessed.
+ runDeviceTestAsUser(TARGET_PKG, TEST_CLASS, "testOpenSession", args,
+ mPrimaryUserId);
+ // verify that previously created session cannot be accessed from another user.
+ runDeviceTestAsUser(TARGET_PKG, TEST_CLASS, "testOpenSession_shouldThrow", args,
+ mSecondaryUserId);
+ }
+
+ @Test
+ public void testCommitAndOpenBlob() throws Exception {
+ Map<String, String> args = createArgs(Pair.create(KEY_ALLOW_PUBLIC, String.valueOf(1)));
+ // Commit a blob.
+ runDeviceTestAsUser(TARGET_PKG, TEST_CLASS, "testCommitBlob", args,
+ mPrimaryUserId);
+ args = createArgsFromLastTestRun();
+ // Verify that previously committed blob can be accessed.
+ runDeviceTestAsUser(TARGET_PKG, TEST_CLASS, "testOpenBlob", args,
+ mPrimaryUserId);
+ // Verify that previously committed blob cannot be access from another user.
+ runDeviceTestAsUser(TARGET_PKG, TEST_CLASS, "testOpenBlob_shouldThrow", args,
+ mSecondaryUserId);
+ }
+}
diff --git a/hostsidetests/blobstore/src/com/android/cts/host/blob/DataCleanupTest.java b/hostsidetests/blobstore/src/com/android/cts/host/blob/DataCleanupTest.java
new file mode 100644
index 0000000..8a3efc8
--- /dev/null
+++ b/hostsidetests/blobstore/src/com/android/cts/host/blob/DataCleanupTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 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.cts.host.blob;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Map;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class DataCleanupTest extends BaseBlobStoreHostTest {
+ private static final String TARGET_APK = "CtsBlobStoreHelperApp.apk";
+ private static final String TARGET_PKG = "com.android.cts.blob.helper";
+ private static final String TEST_CLASS = TARGET_PKG + ".DataCleanupTest";
+
+ @Test
+ public void testPackageUninstall_openSession() throws Exception {
+ installPackage(TARGET_APK);
+ runDeviceTest(TARGET_PKG, TEST_CLASS, "testCreateSession");
+ final Map<String, String> args = createArgsFromLastTestRun();
+ // Verify that previously created session can be accessed.
+ runDeviceTest(TARGET_PKG, TEST_CLASS, "testOpenSession", args);
+ uninstallPackage(TARGET_PKG);
+ installPackage(TARGET_APK);
+ // Verify that the new package cannot access the session.
+ runDeviceTest(TARGET_PKG, TEST_CLASS, "testOpenSession_shouldThrow", args);
+ }
+
+ @Test
+ public void testPackageUninstall_openBlob() throws Exception {
+ installPackage(TARGET_APK);
+ runDeviceTest(TARGET_PKG, TEST_CLASS, "testCommitBlob");
+ final Map<String, String> args = createArgsFromLastTestRun();
+ // Verify that previously committed blob can be accessed.
+ runDeviceTest(TARGET_PKG, TEST_CLASS, "testOpenBlob", args);
+ uninstallPackage(TARGET_PKG);
+ installPackage(TARGET_APK);
+ // Verify that the new package cannot access the blob.
+ runDeviceTest(TARGET_PKG, TEST_CLASS, "testOpenBlob_shouldThrow", args);
+ }
+
+ @Test
+ public void testPackageUninstallAndReboot_openSession() throws Exception {
+ installPackage(TARGET_APK);
+ runDeviceTest(TARGET_PKG, TEST_CLASS, "testCreateSession");
+ final Map<String, String> args = createArgsFromLastTestRun();
+ // Verify that previously created session can be accessed.
+ runDeviceTest(TARGET_PKG, TEST_CLASS, "testOpenSession", args);
+ uninstallPackage(TARGET_PKG);
+ // Reboot the device.
+ rebootAndWaitUntilReady();
+ installPackage(TARGET_APK);
+ // Verify that the new package cannot access the session.
+ runDeviceTest(TARGET_PKG, TEST_CLASS, "testOpenSession_shouldThrow", args);
+ }
+
+ @Test
+ public void testPackageUninstallAndReboot_openBlob() throws Exception {
+ installPackage(TARGET_APK);
+ runDeviceTest(TARGET_PKG, TEST_CLASS, "testCommitBlob");
+ final Map<String, String> args = createArgsFromLastTestRun();
+ // Verify that previously committed blob can be accessed.
+ runDeviceTest(TARGET_PKG, TEST_CLASS, "testOpenBlob", args);
+ uninstallPackage(TARGET_PKG);
+ // Reboot the device.
+ rebootAndWaitUntilReady();
+ installPackage(TARGET_APK);
+ // Verify that the new package cannot access the blob.
+ runDeviceTest(TARGET_PKG, TEST_CLASS, "testOpenBlob_shouldThrow", args);
+ }
+}
diff --git a/hostsidetests/blobstore/src/com/android/cts/host/blob/DataPersistenceTest.java b/hostsidetests/blobstore/src/com/android/cts/host/blob/DataPersistenceTest.java
new file mode 100644
index 0000000..39f9b32
--- /dev/null
+++ b/hostsidetests/blobstore/src/com/android/cts/host/blob/DataPersistenceTest.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 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.cts.host.blob;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class DataPersistenceTest extends BaseBlobStoreHostTest {
+ private static final String TARGET_APK = "CtsBlobStoreHelperApp.apk";
+ private static final String TARGET_PKG = "com.android.cts.blob.helper";
+ private static final String TEST_CLASS = TARGET_PKG + ".DataPersistenceTest";
+
+ @Test
+ public void testDataIsPersistedAcrossReboot() throws Exception {
+ installPackage(TARGET_APK);
+ runDeviceTest(TARGET_PKG, TEST_CLASS, "testCreateSession");
+ rebootAndWaitUntilReady();
+ runDeviceTest(TARGET_PKG, TEST_CLASS, "testOpenSessionAndWrite");
+ rebootAndWaitUntilReady();
+ runDeviceTest(TARGET_PKG, TEST_CLASS, "testCommitSession");
+ rebootAndWaitUntilReady();
+ runDeviceTest(TARGET_PKG, TEST_CLASS, "testOpenBlob");
+ }
+}
\ No newline at end of file
diff --git a/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/AndroidManifest.xml b/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/AndroidManifest.xml
new file mode 100644
index 0000000..a649209
--- /dev/null
+++ b/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 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="com.android.cts.blob.helper">
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.blob.helper" />
+</manifest>
\ No newline at end of file
diff --git a/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/src/com/android/cts/blob/helper/DataCleanupTest.java b/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/src/com/android/cts/blob/helper/DataCleanupTest.java
new file mode 100644
index 0000000..f4a1f1c
--- /dev/null
+++ b/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/src/com/android/cts/blob/helper/DataCleanupTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2020 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.cts.blob.helper;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+import static org.testng.Assert.fail;
+
+import android.app.Instrumentation;
+import android.app.blob.BlobHandle;
+import android.app.blob.BlobStoreManager;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+
+import com.android.cts.blob.DummyBlobData;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Base64;
+import java.util.Random;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+@RunWith(AndroidJUnit4.class)
+public class DataCleanupTest {
+ private static final long TIMEOUT_COMMIT_CALLBACK_MS = 5_000;
+
+ private static final long PARTIAL_FILE_LENGTH_BYTES = 2002;
+
+ private static final String KEY_SESSION_ID = "session";
+
+ private static final String KEY_DIGEST = "digest";
+ private static final String KEY_EXPIRY = "expiry";
+ private static final String KEY_LABEL = "label";
+ private static final String KEY_TAG = "tag";
+
+ private static final String KEY_ALLOW_PUBLIC = "public";
+
+ private Context mContext;
+ private Instrumentation mInstrumentation;
+ private BlobStoreManager mBlobStoreManager;
+ private Random mRandom;
+
+ @Before
+ public void setUp() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mContext = mInstrumentation.getContext();
+ mBlobStoreManager = (BlobStoreManager) mContext.getSystemService(
+ Context.BLOB_STORE_SERVICE);
+ mRandom = new Random(24 /* seed */);
+ }
+
+ @Test
+ public void testCreateSession() throws Exception {
+ final DummyBlobData blobData = new DummyBlobData(mContext, mRandom, "test_data_blob");
+ blobData.prepare();
+
+ final long sessionId = createSession(blobData.getBlobHandle());
+ assertThat(sessionId).isGreaterThan(0L);
+ try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
+ blobData.writeToSession(session, 0, PARTIAL_FILE_LENGTH_BYTES);
+ }
+ addSessionIdToResults(sessionId);
+ }
+
+ @Test
+ public void testOpenSession() throws Exception {
+ final long sessionId = getSessionIdFromArgs();
+ try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
+ assertThat(session.getSize()).isEqualTo(PARTIAL_FILE_LENGTH_BYTES);
+ }
+ }
+
+ @Test
+ public void testOpenSession_shouldThrow() throws Exception {
+ final long sessionId = getSessionIdFromArgs();
+ try {
+ mBlobStoreManager.openSession(sessionId);
+ fail("Should throw");
+ } catch (SecurityException e) {
+ // expected
+ }
+ assertThrows(SecurityException.class,
+ () -> mBlobStoreManager.openSession(sessionId));
+ }
+
+ @Test
+ public void testCommitBlob() throws Exception {
+ final DummyBlobData blobData = new DummyBlobData(mContext, mRandom,
+ "test_data_blob", "test_data_blob");
+ blobData.prepare();
+
+ final long sessionId = createSession(blobData.getBlobHandle());
+ assertThat(sessionId).isGreaterThan(0L);
+ try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
+ blobData.writeToSession(session);
+ if (getShouldAllowPublicFromArgs()) {
+ session.allowPublicAccess();
+ }
+ final CompletableFuture<Integer> callback = new CompletableFuture<>();
+ session.commit(mContext.getMainExecutor(), callback::complete);
+ assertThat(callback.get(TIMEOUT_COMMIT_CALLBACK_MS, TimeUnit.MILLISECONDS))
+ .isEqualTo(0);
+ }
+ addBlobHandleToResults(blobData.getBlobHandle());
+ }
+
+ @Test
+ public void testOpenBlob() throws Exception {
+ final BlobHandle blobHandle = getBlobHandleFromArgs();
+ try (ParcelFileDescriptor pfd = mBlobStoreManager.openBlob(blobHandle)) {
+ assertThat(pfd).isNotNull();
+ }
+ }
+
+ @Test
+ public void testOpenBlob_shouldThrow() throws Exception {
+ final BlobHandle blobHandle = getBlobHandleFromArgs();
+ assertThrows(SecurityException.class,
+ () -> mBlobStoreManager.openBlob(blobHandle));
+ }
+
+ private long createSession(BlobHandle blobHandle) throws Exception {
+ final long sessionId = mBlobStoreManager.createSession(blobHandle);
+ return sessionId;
+ }
+
+ private void addSessionIdToResults(long sessionId) {
+ final Bundle results = new Bundle();
+ results.putLong(KEY_SESSION_ID, sessionId);
+ mInstrumentation.addResults(results);
+ }
+
+ private long getSessionIdFromArgs() {
+ final Bundle args = InstrumentationRegistry.getArguments();
+ return Long.parseLong(args.getString(KEY_SESSION_ID));
+ }
+
+ private void addBlobHandleToResults(BlobHandle blobHandle) {
+ final Bundle results = new Bundle();
+ results.putString(KEY_DIGEST,
+ Base64.getEncoder().encodeToString(blobHandle.getSha256Digest()));
+ results.putLong(KEY_EXPIRY, blobHandle.getExpiryTimeMillis());
+ results.putCharSequence(KEY_LABEL, blobHandle.getLabel().toString());
+ results.putString(KEY_TAG, blobHandle.getTag());
+ mInstrumentation.addResults(results);
+ }
+
+ private BlobHandle getBlobHandleFromArgs() {
+ final Bundle args = InstrumentationRegistry.getArguments();
+ final byte[] digest = Base64.getDecoder().decode(args.getString(KEY_DIGEST));
+ final CharSequence label = args.getString(KEY_LABEL);
+ final long expiryTimeMillis = Long.parseLong(args.getString(KEY_EXPIRY));
+ final String tag = args.getString(KEY_TAG);
+ return BlobHandle.createWithSha256(digest, label, expiryTimeMillis, tag);
+ }
+
+ private boolean getShouldAllowPublicFromArgs() {
+ final Bundle args = InstrumentationRegistry.getArguments();
+ return "1".equals(args.getString(KEY_ALLOW_PUBLIC));
+ }
+}
diff --git a/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/src/com/android/cts/blob/helper/DataPersistenceTest.java b/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/src/com/android/cts/blob/helper/DataPersistenceTest.java
new file mode 100644
index 0000000..a9bf207
--- /dev/null
+++ b/hostsidetests/blobstore/test-apps/BlobStoreHelperApp/src/com/android/cts/blob/helper/DataPersistenceTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2020 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.cts.blob.helper;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.blob.BlobHandle;
+import android.app.blob.BlobStoreManager;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.ParcelFileDescriptor;
+
+import com.android.compatibility.common.util.ShellIdentityUtils;
+import com.android.cts.blob.DummyBlobData;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Base64;
+import java.util.Random;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+@RunWith(AndroidJUnit4.class)
+public class DataPersistenceTest {
+
+ private static final String KEY_SESSION_ID = "id";
+ private static final String KEY_DIGEST = "digest";
+ private static final String KEY_LABEL = "label";
+ private static final String KEY_EXPIRY = "expiry";
+ private static final String KEY_TAG = "tag";
+
+ private static final long PARTIAL_FILE_LENGTH_BYTES = 2002;
+ private static final long TIMEOUT_WAIT_FOR_IDLE_MS = 2_000;
+ private static final long TIMEOUT_COMMIT_CALLBACK_MS = 5_000;
+
+ private Context mContext;
+ private BlobStoreManager mBlobStoreManager;
+ private Random mRandom;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ mBlobStoreManager = (BlobStoreManager) mContext.getSystemService(
+ Context.BLOB_STORE_SERVICE);
+ mRandom = new Random(22 /* seed */);
+ }
+
+ @Test
+ public void testCreateSession() throws Exception {
+ final DummyBlobData blobData = new DummyBlobData(mContext, mRandom, "test_data_blob");
+ blobData.prepare();
+
+ final long sessionId = createSession(blobData.getBlobHandle());
+ assertThat(sessionId).isGreaterThan(0L);
+ try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
+ blobData.writeToSession(session, 0, PARTIAL_FILE_LENGTH_BYTES);
+ }
+ writeSessionIdToDisk(sessionId);
+ writeBlobHandleToDisk(blobData.getBlobHandle());
+
+ ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(mBlobStoreManager,
+ (blobStoreManager) -> blobStoreManager.waitForIdle(TIMEOUT_WAIT_FOR_IDLE_MS),
+ Exception.class, android.Manifest.permission.DUMP);
+ }
+
+ @Test
+ public void testOpenSessionAndWrite() throws Exception {
+ final long sessionId = readSessionIdFromDisk();
+ final DummyBlobData blobData = new DummyBlobData(mContext, mRandom, "test_data_blob");
+ try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
+ assertThat(session.getSize()).isEqualTo(PARTIAL_FILE_LENGTH_BYTES);
+ blobData.writeToSession(session, PARTIAL_FILE_LENGTH_BYTES,
+ blobData.getFileSize() - PARTIAL_FILE_LENGTH_BYTES);
+ }
+ }
+
+ @Test
+ public void testCommitSession() throws Exception {
+ final long sessionId = readSessionIdFromDisk();
+ try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
+ final CompletableFuture<Integer> callback = new CompletableFuture<>();
+ session.commit(mContext.getMainExecutor(), callback::complete);
+ assertThat(callback.get(TIMEOUT_COMMIT_CALLBACK_MS, TimeUnit.MILLISECONDS))
+ .isEqualTo(0);
+ }
+ }
+
+ @Test
+ public void testOpenBlob() throws Exception {
+ final BlobHandle blobHandle = readBlobHandleFromDisk();
+ try (ParcelFileDescriptor pfd = mBlobStoreManager.openBlob(blobHandle)) {
+ assertThat(pfd).isNotNull();
+ }
+ }
+
+ private long createSession(BlobHandle blobHandle) throws Exception {
+ final long sessionId = mBlobStoreManager.createSession(blobHandle);
+ return sessionId;
+ }
+
+ private void writeSessionIdToDisk(long sessionId) {
+ final SharedPreferences sharedPref = getSharedPreferences();
+ assertThat(sharedPref.edit().putLong(KEY_SESSION_ID, sessionId).commit())
+ .isTrue();
+ }
+
+ private long readSessionIdFromDisk() {
+ final SharedPreferences sharedPref = getSharedPreferences();
+ final long sessionId = sharedPref.getLong(KEY_SESSION_ID, -1);
+ assertThat(sessionId).isNotEqualTo(-1);
+ return sessionId;
+ }
+
+ private void writeBlobHandleToDisk(BlobHandle handle) {
+ final SharedPreferences.Editor sharedPrefEditor = getSharedPreferences().edit();
+ sharedPrefEditor.putString(KEY_DIGEST, Base64.getEncoder().encodeToString(
+ handle.getSha256Digest()));
+ sharedPrefEditor.putString(KEY_LABEL, handle.getLabel().toString());
+ sharedPrefEditor.putLong(KEY_EXPIRY, handle.getExpiryTimeMillis());
+ sharedPrefEditor.putString(KEY_TAG, handle.getTag());
+ assertThat(sharedPrefEditor.commit()).isTrue();
+ }
+
+ private BlobHandle readBlobHandleFromDisk() {
+ final SharedPreferences sharedPref = getSharedPreferences();
+ final byte[] digest = Base64.getDecoder().decode(sharedPref.getString(KEY_DIGEST, null));
+ final CharSequence label = sharedPref.getString(KEY_LABEL, null);
+ final long expiryMillis = sharedPref.getLong(KEY_EXPIRY, -1);
+ final String tag = sharedPref.getString(KEY_TAG, null);
+ return BlobHandle.createWithSha256(digest, label, expiryMillis, tag);
+ }
+
+ private SharedPreferences getSharedPreferences() {
+ return mContext.getSharedPreferences(mContext.getPackageName(),
+ Context.MODE_PRIVATE);
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml
index 565573b..f2c3445 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml
@@ -34,6 +34,7 @@
<uses-permission android:name="android.permission.CAMERA" />
<!-- Needed to read the serial number during Device ID attestation tests -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<!-- Add a network security config that trusts user added CAs for tests -->
<application android:networkSecurityConfig="@xml/network_security_config"
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ApplicationHiddenParentTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ApplicationHiddenParentTest.java
new file mode 100644
index 0000000..aba1e59
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ApplicationHiddenParentTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 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.cts.deviceandprofileowner;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.pm.PackageManager;
+
+public class ApplicationHiddenParentTest extends BaseDeviceAdminTest {
+
+ private DevicePolicyManager mParentDevicePolicyManager;
+ private PackageManager mPackageManager;
+
+ private static final String SYSTEM_PACKAGE_TO_HIDE = "com.google.android.youtube";
+ private static final String NON_SYSTEM_PACKAGE_TO_HIDE = "com.android.cts.permissionapp";
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mParentDevicePolicyManager =
+ mDevicePolicyManager.getParentProfileInstance(ADMIN_RECEIVER_COMPONENT);
+ mPackageManager = mContext.getPackageManager();
+ assertThat(mParentDevicePolicyManager).isNotNull();
+
+ assertThat(mDevicePolicyManager.isProfileOwnerApp(ADMIN_RECEIVER_COMPONENT.getPackageName())).isTrue();
+ assertThat(mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()).isTrue();
+ }
+
+ public void testSetApplicationHidden_systemPackage()
+ throws PackageManager.NameNotFoundException {
+ assertThat(mPackageManager.getPackageInfo(SYSTEM_PACKAGE_TO_HIDE, 0)).isNotNull();
+
+ assertThat(mParentDevicePolicyManager.setApplicationHidden(ADMIN_RECEIVER_COMPONENT,
+ SYSTEM_PACKAGE_TO_HIDE, true)).isTrue();
+ assertThat(mParentDevicePolicyManager.isApplicationHidden(ADMIN_RECEIVER_COMPONENT,
+ SYSTEM_PACKAGE_TO_HIDE)).isTrue();
+ assertThat(mPackageManager.getPackageInfo(SYSTEM_PACKAGE_TO_HIDE,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES)).isNotNull();
+
+ assertThat(mParentDevicePolicyManager.setApplicationHidden(ADMIN_RECEIVER_COMPONENT,
+ SYSTEM_PACKAGE_TO_HIDE, false)).isTrue();
+ assertThat(mParentDevicePolicyManager.isApplicationHidden(ADMIN_RECEIVER_COMPONENT,
+ SYSTEM_PACKAGE_TO_HIDE)).isFalse();
+ assertThat(mPackageManager.getPackageInfo(SYSTEM_PACKAGE_TO_HIDE, 0)).isNotNull();
+ }
+
+ public void testSetApplicationHidden_nonSystemPackage() {
+ assertThrows(IllegalArgumentException.class, () -> {
+ mParentDevicePolicyManager.setApplicationHidden(ADMIN_RECEIVER_COMPONENT,
+ NON_SYSTEM_PACKAGE_TO_HIDE, true);
+ mParentDevicePolicyManager.isApplicationHidden(ADMIN_RECEIVER_COMPONENT,
+ NON_SYSTEM_PACKAGE_TO_HIDE);
+ });
+ assertThrows(IllegalArgumentException.class, () -> {
+ mParentDevicePolicyManager.setApplicationHidden(ADMIN_RECEIVER_COMPONENT,
+ NON_SYSTEM_PACKAGE_TO_HIDE, false);
+ mParentDevicePolicyManager.isApplicationHidden(ADMIN_RECEIVER_COMPONENT,
+ NON_SYSTEM_PACKAGE_TO_HIDE);
+ });
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java
index 58a14e6..3c39f37 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java
@@ -66,16 +66,16 @@
mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
}
- public void startLockTask() throws Exception {
- Log.d(TAG, "startLockTask on host-driven test (no cleanup)");
+ public void testStartLockTask_noAsserts() throws Exception {
+ Log.d(TAG, "testStartLockTask_noAsserts on host-driven test (no cleanup)");
setLockTaskPackages(mContext.getPackageName());
setDefaultHomeIntentReceiver();
launchLockTaskActivity();
mUiDevice.waitForIdle();
}
- public void cleanupLockTask() {
- Log.d(TAG, "cleanupLockTask on host-driven test");
+ public void testCleanupLockTask_noAsserts() {
+ Log.d(TAG, "testCleanupLockTask_noAsserts on host-driven test");
mDevicePolicyManager.clearPackagePersistentPreferredActivities(
ADMIN_RECEIVER_COMPONENT,
mContext.getPackageName());
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/InstallUpdateTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/systemupdate/InstallUpdateTest.java
similarity index 93%
rename from hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/InstallUpdateTest.java
rename to hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/systemupdate/InstallUpdateTest.java
index b69a60c..1fd8eff 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/InstallUpdateTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/systemupdate/InstallUpdateTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.cts.deviceowner;
+package com.android.cts.deviceandprofileowner.systemupdate;
import static android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID;
@@ -27,6 +27,7 @@
import android.os.SystemProperties;
import com.android.compatibility.common.util.SystemUtil;
+import com.android.cts.deviceandprofileowner.BaseDeviceAdminTest;
import java.io.File;
import java.util.concurrent.CountDownLatch;
@@ -35,7 +36,7 @@
/**
* Test {@link android.app.admin.DevicePolicyManager#installSystemUpdate}
*/
-public class InstallUpdateTest extends BaseDeviceOwnerTest {
+public class InstallUpdateTest extends BaseDeviceAdminTest {
private static final int BATTERY_STATE_CHANGE_TIMEOUT_MS = 5000;
private static final int BATTERY_STATE_CHANGE_SLEEP_PER_CHECK_MS = 50;
private static final int TEST_BATTERY_THRESHOLD = 10;
@@ -150,7 +151,7 @@
throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
Uri uri = Uri.fromFile(new File(TEST_SYSTEM_UPDATES_DIR, fileName));
- mDevicePolicyManager.installSystemUpdate(getWho(), uri,
+ mDevicePolicyManager.installSystemUpdate(ADMIN_RECEIVER_COMPONENT, uri,
Runnable::run, new InstallSystemUpdateCallback() {
@Override
public void onInstallUpdateError(int errorCode, String errorMessage) {
@@ -163,7 +164,7 @@
}
private void setNonChargingBatteryThreshold(int threshold) {
- SystemUtil.runShellCommand(
+ runShellCommand(
"settings put global device_policy_constants battery_threshold_not_charging="
+ threshold);
}
@@ -173,7 +174,7 @@
}
private void setChargingBatteryThreshold(int threshold) {
- SystemUtil.runShellCommand(
+ runShellCommand(
"settings put global device_policy_constants battery_threshold_charging="
+ threshold);
}
@@ -184,8 +185,8 @@
/** Should be paired with {@link #resetBatteryState()} in a {@code finally} block. */
private void setBatteryStateAndWait(boolean plugged, int level) throws Exception {
- SystemUtil.runShellCommand(plugged ? "cmd battery set ac 1" : "cmd battery unplug");
- SystemUtil.runShellCommand("cmd battery set -f level " + level);
+ runShellCommand(plugged ? "cmd battery set ac 1" : "cmd battery unplug");
+ runShellCommand("cmd battery set -f level " + level);
long startTime = SystemClock.elapsedRealtime();
while (!isBatteryState(plugged, level)
&& SystemClock.elapsedRealtime() <= startTime + BATTERY_STATE_CHANGE_TIMEOUT_MS) {
@@ -211,11 +212,11 @@
}
private void resetBatteryState() {
- SystemUtil.runShellCommand("dumpsys battery reset");
+ runShellCommand("dumpsys battery reset");
}
private void resetDevicePolicyConstants() {
- SystemUtil.runShellCommand("settings delete global device_policy_constants");
+ runShellCommand("settings delete global device_policy_constants");
}
private boolean isDeviceAB() {
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SystemUpdatePolicyTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/systemupdate/SystemUpdatePolicyTest.java
similarity index 94%
rename from hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SystemUpdatePolicyTest.java
rename to hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/systemupdate/SystemUpdatePolicyTest.java
index e9d4126..44de098 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SystemUpdatePolicyTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/systemupdate/SystemUpdatePolicyTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.cts.deviceowner;
+package com.android.cts.deviceandprofileowner.systemupdate;
import static android.provider.Settings.Global.AIRPLANE_MODE_ON;
@@ -33,9 +33,8 @@
import android.provider.Settings.Global;
import android.util.Log;
import android.util.Pair;
-import android.provider.Settings;
-import android.provider.Settings.Global;
+import com.android.cts.deviceandprofileowner.BaseDeviceAdminTest;
import com.google.common.collect.ImmutableList;
import java.time.LocalDate;
import java.time.MonthDay;
@@ -50,7 +49,7 @@
* Test {@link SystemUpdatePolicy}, {@link DevicePolicyManager#setSystemUpdatePolicy} and
* {@link DevicePolicyManager#getSystemUpdatePolicy}
*/
-public class SystemUpdatePolicyTest extends BaseDeviceOwnerTest {
+public class SystemUpdatePolicyTest extends BaseDeviceAdminTest {
private static final String TAG = "SystemUpdatePolicyTest";
@@ -86,7 +85,7 @@
clearFreezeRecord();
mSavedAutoTimeConfig = Settings.Global.getInt(mContext.getContentResolver(),
Global.AUTO_TIME, 0);
- executeShellCommand("settings put global auto_time 0");
+ runShellCommand("settings put global auto_time 0");
mSavedSystemDate = LocalDate.now();
mRestoreDate = false;
mSavedAirplaneMode = getAirplaneMode();
@@ -99,12 +98,12 @@
@Override
protected void tearDown() throws Exception {
- mDevicePolicyManager.setSystemUpdatePolicy(getWho(), null);
+ mDevicePolicyManager.setSystemUpdatePolicy(ADMIN_RECEIVER_COMPONENT, null);
clearFreezeRecord();
if (mRestoreDate) {
setSystemDate(mSavedSystemDate);
}
- executeShellCommand("settings put global auto_time",
+ runShellCommand("settings put global auto_time",
Integer.toString(mSavedAutoTimeConfig));
// This needs to happen last since setSystemDate() relies on the receiver for
// synchronization.
@@ -213,7 +212,7 @@
setSystemDate(LocalDate.of(2018, 2, 28));
setPolicyWithFreezePeriod("01-01", "03-01", "06-01", "06-30");
// Clear policy
- mDevicePolicyManager.setSystemUpdatePolicy(getWho(), null);
+ mDevicePolicyManager.setSystemUpdatePolicy(ADMIN_RECEIVER_COMPONENT, null);
// Set to a conflict period (too close with previous period [2-28, 2-28]) should fail,
// despite the previous policy was cleared from the system just now.
try {
@@ -363,7 +362,7 @@
}
private void testPolicy(SystemUpdatePolicy policy) {
- mDevicePolicyManager.setSystemUpdatePolicy(getWho(), policy);
+ mDevicePolicyManager.setSystemUpdatePolicy(ADMIN_RECEIVER_COMPONENT, policy);
waitForPolicyChangedBroadcast();
SystemUpdatePolicy newPolicy = mDevicePolicyManager.getSystemUpdatePolicy();
if (policy == null) {
@@ -382,7 +381,7 @@
private void setPolicyWithFreezePeriod(String...dates) {
SystemUpdatePolicy policy = SystemUpdatePolicy.createPostponeInstallPolicy();
setFreezePeriods(policy, dates);
- mDevicePolicyManager.setSystemUpdatePolicy(getWho(), policy);
+ mDevicePolicyManager.setSystemUpdatePolicy(ADMIN_RECEIVER_COMPONENT, policy);
List<FreezePeriod> loadedFreezePeriods = mDevicePolicyManager
.getSystemUpdatePolicy().getFreezePeriods();
@@ -438,7 +437,7 @@
}
private void clearFreezeRecord() throws Exception {
- executeShellCommand("dpm", "clear-freeze-period-record");
+ runShellCommand("dpm", "clear-freeze-period-record");
}
private void setSystemDate(LocalDate date) throws Exception {
@@ -447,11 +446,16 @@
c.set(Calendar.YEAR, date.getYear());
c.set(Calendar.MONTH, date.getMonthValue() - 1);
c.set(Calendar.DAY_OF_MONTH, date.getDayOfMonth());
- mDevicePolicyManager.setTime(getWho(), c.getTimeInMillis());
+ mDevicePolicyManager.setTime(ADMIN_RECEIVER_COMPONENT, c.getTimeInMillis());
waitForTimeChangedBroadcast();
}
private void waitForPolicyChangedBroadcast() {
+ if (!isDeviceOwner()) {
+ // ACTION_SYSTEM_UPDATE_POLICY_CHANGED is always sent to system user, skip
+ // waiting for it if we are inside a managed profile.
+ return;
+ }
try {
assertTrue("Timeout while waiting for broadcast.",
mPolicyChangedSemaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
@@ -469,8 +473,8 @@
}
}
- private int getAirplaneMode() throws Settings.SettingNotFoundException {
- int airplaneMode = 0xFF;
+ private int getAirplaneMode() {
+ int airplaneMode;
try {
airplaneMode = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON);
@@ -478,9 +482,8 @@
airplaneMode = 0xFF;
// if the mode is not supported, return a non zero value.
Log.i(TAG, "Airplane mode is not found in Settings. Skipping AirplaneMode update");
- } finally {
- return airplaneMode;
}
+ return airplaneMode;
}
private boolean setAirplaneModeAndWaitBroadcast (int state) throws Exception {
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java
index bb87c64..c494db0 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java
@@ -90,6 +90,8 @@
.add("addUserRestriction")
.add("clearUserRestriction")
.add("getUserRestrictions")
+ .add("setApplicationHidden")
+ .add("isApplicationHidden")
.build();
private static final String LOG_TAG = "ParentProfileTest";
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index 08788e9..7d9dcd5 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -41,13 +41,18 @@
import com.android.tradefed.util.FileUtil;
import com.android.tradefed.util.TarUtil;
+import com.google.common.io.ByteStreams;
+
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -129,6 +134,14 @@
*/
protected static final int USER_ALL = -1;
+ private static final String TEST_UPDATE_LOCATION = "/data/local/tmp/cts/deviceowner";
+
+ /**
+ * Copied from {@link android.app.admin.DevicePolicyManager
+ * .InstallSystemUpdateCallback#UPDATE_ERROR_UPDATE_FILE_INVALID}
+ */
+ protected static final int UPDATE_ERROR_UPDATE_FILE_INVALID = 3;
+
protected CompatibilityBuildHelper mBuildHelper;
private String mPackageVerifier;
private HashSet<String> mAvailableFeatures;
@@ -202,6 +215,7 @@
mFixedUsers.add(getDevice().getCurrentUser());
}
}
+ getDevice().executeShellCommand(" mkdir " + TEST_UPDATE_LOCATION);
removeOwners();
switchUser(USER_SYSTEM);
@@ -226,6 +240,7 @@
}
removeTestUsers();
removeTestPackages();
+ getDevice().executeShellCommand(" rm -r " + TEST_UPDATE_LOCATION);
}
protected void installAppAsUser(String appFileName, int userId) throws FileNotFoundException,
@@ -952,4 +967,22 @@
}
throw new Exception("Default launcher not found");
}
+
+ boolean isDeviceAb() throws DeviceNotAvailableException {
+ final String result = getDevice().executeShellCommand("getprop ro.build.ab_update").trim();
+ return "true".equalsIgnoreCase(result);
+ }
+
+ void pushUpdateFileToDevice(String fileName)
+ throws IOException, DeviceNotAvailableException {
+ File file = File.createTempFile(
+ fileName.split("\\.")[0], "." + fileName.split("\\.")[1]);
+ try (OutputStream outputStream = new FileOutputStream(file)) {
+ InputStream inputStream = getClass().getResourceAsStream("/" + fileName);
+ ByteStreams.copy(inputStream, outputStream);
+ }
+
+ getDevice().pushFile(file, TEST_UPDATE_LOCATION + "/" + fileName);
+ file.delete();
+ }
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 7f69e58..2d3fd0d 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -1196,7 +1196,8 @@
try {
// Just start kiosk mode
- executeDeviceTestMethod(".LockTaskHostDrivenTest", "startLockTask");
+ executeDeviceTestMethod(
+ ".LockTaskHostDrivenTest", "testStartLockTask_noAsserts");
// Reboot while in kiosk mode and then unlock the device
rebootAndWaitUntilReady();
@@ -1205,7 +1206,7 @@
executeDeviceTestMethod(".LockTaskHostDrivenTest",
"testLockTaskIsActiveAndCantBeInterrupted");
} finally {
- executeDeviceTestMethod(".LockTaskHostDrivenTest", "cleanupLockTask");
+ executeDeviceTestMethod(".LockTaskHostDrivenTest", "testCleanupLockTask_noAsserts");
}
}
@@ -1218,7 +1219,8 @@
try {
// Just start kiosk mode
- executeDeviceTestMethod(".LockTaskHostDrivenTest", "startLockTask");
+ executeDeviceTestMethod(
+ ".LockTaskHostDrivenTest", "testStartLockTask_noAsserts");
// Reboot while in kiosk mode and then unlock the device
rebootAndWaitUntilReady();
@@ -1230,7 +1232,7 @@
executeDeviceTestMethod(".LockTaskHostDrivenTest",
"testLockTaskIsActiveAndCantBeInterrupted");
} finally {
- executeDeviceTestMethod(".LockTaskHostDrivenTest", "cleanupLockTask");
+ executeDeviceTestMethod(".LockTaskHostDrivenTest", "testCleanupLockTask_noAsserts");
}
}
@@ -1243,7 +1245,7 @@
executeDeviceTestMethod(".LockTaskHostDrivenTest",
"testLockTaskCanLaunchDefaultDialer");
} finally {
- executeDeviceTestMethod(".LockTaskHostDrivenTest", "cleanupLockTask");
+ executeDeviceTestMethod(".LockTaskHostDrivenTest", "testCleanupLockTask_noAsserts");
}
}
@@ -1256,7 +1258,7 @@
executeDeviceTestMethod(".LockTaskHostDrivenTest",
"testLockTaskCanLaunchEmergencyDialer");
} finally {
- executeDeviceTestMethod(".LockTaskHostDrivenTest", "cleanupLockTask");
+ executeDeviceTestMethod(".LockTaskHostDrivenTest", "testCleanupLockTask_noAsserts");
}
}
@@ -1269,7 +1271,7 @@
executeDeviceTestMethod(".LockTaskHostDrivenTest",
"testLockTaskIsExitedIfNotWhitelisted");
} finally {
- executeDeviceTestMethod(".LockTaskHostDrivenTest", "cleanupLockTask");
+ executeDeviceTestMethod(".LockTaskHostDrivenTest", "testCleanupLockTask_noAsserts");
}
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index 186970c..cdf5bd9 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -32,8 +32,6 @@
import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
import com.android.tradefed.device.DeviceNotAvailableException;
-import com.google.common.io.ByteStreams;
-
import org.junit.Ignore;
import org.junit.Test;
@@ -84,17 +82,10 @@
private static final int SECURITY_EVENTS_BATCH_SIZE = 100;
private static final String ARG_NETWORK_LOGGING_BATCH_COUNT = "batchCount";
- private static final String TEST_UPDATE_LOCATION = "/data/local/tmp/cts/deviceowner";
private static final String LAUNCHER_TESTS_HAS_LAUNCHER_ACTIVITY_APK =
"CtsHasLauncherActivityApp.apk";
- /**
- * Copied from {@link android.app.admin.DevicePolicyManager
- * .InstallSystemUpdateCallback#UPDATE_ERROR_UPDATE_FILE_INVALID}
- */
- private static final int UPDATE_ERROR_UPDATE_FILE_INVALID = 3;
-
private static final int TYPE_NONE = 0;
/**
@@ -135,7 +126,6 @@
fail("Failed to set device owner");
}
- getDevice().executeShellCommand(" mkdir " + TEST_UPDATE_LOCATION);
// Enable the notification listener
getDevice().executeShellCommand("cmd notification allow_listener com.android.cts.deviceowner/com.android.cts.deviceowner.NotificationListener");
}
@@ -151,7 +141,6 @@
getDevice().uninstallPackage(DEVICE_OWNER_PKG);
switchUser(USER_SYSTEM);
removeTestUsers();
- getDevice().executeShellCommand(" rm -r " + TEST_UPDATE_LOCATION);
}
super.tearDown();
@@ -622,16 +611,6 @@
executeDeviceTestMethod(".AffiliationTest", "testSetAffiliationId_containsEmptyString");
}
- @LargeTest
- @Test
- @Ignore("b/145932189")
- public void testSystemUpdatePolicy() throws Exception {
- if (!mHasFeature) {
- return;
- }
- executeDeviceOwnerTest("SystemUpdatePolicyTest");
- }
-
@Test
@Ignore("b/145932189")
public void testSetSystemUpdatePolicyLogged() throws Exception {
@@ -1003,55 +982,6 @@
}
@Test
- public void testInstallUpdate() throws Exception {
- if (!mHasFeature) {
- return;
- }
-
- pushUpdateFileToDevice("notZip.zi");
- pushUpdateFileToDevice("empty.zip");
- pushUpdateFileToDevice("wrongPayload.zip");
- pushUpdateFileToDevice("wrongHash.zip");
- pushUpdateFileToDevice("wrongSize.zip");
- executeDeviceOwnerTest("InstallUpdateTest");
- }
-
- @Test
- public void testInstallUpdateLogged() throws Exception {
- if (!mHasFeature || !isDeviceAb() || !isStatsdEnabled(getDevice())) {
- return;
- }
- pushUpdateFileToDevice("wrongHash.zip");
- assertMetricsLogged(getDevice(), () -> {
- executeDeviceTestMethod(".InstallUpdateTest", "testInstallUpdate_failWrongHash");
- }, new DevicePolicyEventWrapper.Builder(EventId.INSTALL_SYSTEM_UPDATE_VALUE)
- .setAdminPackageName(DEVICE_OWNER_PKG)
- .setBoolean(/* isDeviceAb */ true)
- .build(),
- new DevicePolicyEventWrapper.Builder(EventId.INSTALL_SYSTEM_UPDATE_ERROR_VALUE)
- .setInt(UPDATE_ERROR_UPDATE_FILE_INVALID)
- .build());
- }
-
- private boolean isDeviceAb() throws DeviceNotAvailableException {
- final String result = getDevice().executeShellCommand("getprop ro.build.ab_update").trim();
- return "true".equalsIgnoreCase(result);
- }
-
- private void pushUpdateFileToDevice(String fileName)
- throws IOException, DeviceNotAvailableException {
- File file = File.createTempFile(
- fileName.split("\\.")[0], "." + fileName.split("\\.")[1]);
- try (OutputStream outputStream = new FileOutputStream(file)) {
- InputStream inputStream = getClass().getResourceAsStream("/" + fileName);
- ByteStreams.copy(inputStream, outputStream);
- }
-
- getDevice().pushFile(file, TEST_UPDATE_LOCATION + "/" + fileName);
- file.delete();
- }
-
- @Test
public void testSetKeyguardDisabledLogged() throws Exception {
if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
index e89ef59..649925c 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
@@ -23,12 +23,19 @@
import static org.junit.Assert.fail;
import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.LargeTest;
import android.stats.devicepolicy.EventId;
import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
+import org.junit.Ignore;
import org.junit.Test;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -243,6 +250,48 @@
runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".CommonCriteriaModeTest", mUserId);
}
+ @LargeTest
+ @Test
+ @Ignore("b/145932189")
+ public void testSystemUpdatePolicy() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".systemupdate.SystemUpdatePolicyTest", mUserId);
+ }
+
+ @Test
+ public void testInstallUpdate() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
+ pushUpdateFileToDevice("notZip.zi");
+ pushUpdateFileToDevice("empty.zip");
+ pushUpdateFileToDevice("wrongPayload.zip");
+ pushUpdateFileToDevice("wrongHash.zip");
+ pushUpdateFileToDevice("wrongSize.zip");
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".systemupdate.InstallUpdateTest", mUserId);
+ }
+
+ @Test
+ public void testInstallUpdateLogged() throws Exception {
+ if (!mHasFeature || !isDeviceAb() || !isStatsdEnabled(getDevice())) {
+ return;
+ }
+ pushUpdateFileToDevice("wrongHash.zip");
+ assertMetricsLogged(getDevice(), () -> {
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".systemupdate.InstallUpdateTest",
+ "testInstallUpdate_failWrongHash", mUserId);
+ }, new DevicePolicyEventWrapper.Builder(EventId.INSTALL_SYSTEM_UPDATE_VALUE)
+ .setAdminPackageName(DEVICE_ADMIN_PKG)
+ .setBoolean(/* isDeviceAb */ true)
+ .build(),
+ new DevicePolicyEventWrapper.Builder(EventId.INSTALL_SYSTEM_UPDATE_ERROR_VALUE)
+ .setInt(UPDATE_ERROR_UPDATE_FILE_INVALID)
+ .build());
+ }
+
private int createSecondaryUserAsProfileOwner() throws Exception {
final int userId = createUser();
installAppAsUser(INTENT_RECEIVER_APK, userId);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
index 1cdd8f4..ef8b14e 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
@@ -16,16 +16,21 @@
package com.android.cts.devicepolicy;
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.LargeTest;
+import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.log.LogUtil;
+import org.junit.Ignore;
import org.junit.Test;
/**
@@ -266,8 +271,31 @@
return;
}
- runDeviceTestsAsUser(
- DEVICE_ADMIN_PKG, ".FactoryResetProtectionPolicyTest", mUserId);
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".FactoryResetProtectionPolicyTest", mUserId);
+ }
+
+ @LargeTest
+ @Test
+ @Ignore("b/145932189")
+ public void testSystemUpdatePolicy() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".systemupdate.SystemUpdatePolicyTest", mUserId);
+ }
+
+ @Test
+ public void testInstallUpdate() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
+ pushUpdateFileToDevice("notZip.zi");
+ pushUpdateFileToDevice("empty.zip");
+ pushUpdateFileToDevice("wrongPayload.zip");
+ pushUpdateFileToDevice("wrongHash.zip");
+ pushUpdateFileToDevice("wrongSize.zip");
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".systemupdate.InstallUpdateTest", mUserId);
}
@Test
@@ -302,6 +330,15 @@
runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".AdminConfiguredNetworksTest", mUserId);
}
+ @Test
+ public void testApplicationHidden() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".ApplicationHiddenParentTest", mUserId);
+ }
+
private void removeOrgOwnedProfile() throws DeviceNotAvailableException {
runDeviceTestsAsUser(DEVICE_ADMIN_PKG, RELINQUISH_DEVICE_TEST_CLASS, mUserId);
}
diff --git a/hostsidetests/incident/src/com/android/server/cts/GraphicsStatsValidationTest.java b/hostsidetests/incident/src/com/android/server/cts/GraphicsStatsValidationTest.java
index 1356600..ea1f803 100644
--- a/hostsidetests/incident/src/com/android/server/cts/GraphicsStatsValidationTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/GraphicsStatsValidationTest.java
@@ -107,6 +107,8 @@
int veryJankyDelta = countFramesAbove(statsAfter, 60) - countFramesAbove(statsBefore, 60);
// The 1st frame could be >40ms, but nothing after that should be
assertTrue(veryJankyDelta <= 2);
+ int noGPUJank = countGPUFramesAbove(statsAfter, 60) - countGPUFramesAbove(statsBefore, 60);
+ assertTrue(noGPUJank == 0);
}
public void testDaveyDrawFrame() throws Exception {
@@ -181,6 +183,9 @@
assertTrue(summary.getSlowBitmapUploadCount() <= summary.getJankyFrames());
assertTrue(summary.getSlowDrawCount() <= summary.getJankyFrames());
assertTrue(proto.getHistogramCount() > 0);
+ assertTrue(proto.getGpuHistogramCount() > 0);
+ assertTrue(proto.getPipeline() == GraphicsStatsProto.PipelineType.GL
+ || proto.getPipeline() == GraphicsStatsProto.PipelineType.VULKAN);
int histogramTotal = countTotalFrames(proto);
assertEquals(summary.getTotalFrames(), histogramTotal);
@@ -196,6 +201,16 @@
return totalFrames;
}
+ private int countGPUFramesAbove(GraphicsStatsProto proto, int thresholdMs) {
+ int totalFrames = 0;
+ for (GraphicsStatsHistogramBucketProto bucket : proto.getGpuHistogramList()) {
+ if (bucket.getRenderMillis() >= thresholdMs) {
+ totalFrames += bucket.getFrameCount();
+ }
+ }
+ return totalFrames;
+ }
+
private int countTotalFrames(GraphicsStatsProto proto) {
return countFramesAbove(proto, 0);
}
diff --git a/hostsidetests/securitybulletin/securityPatch/Bug-115739809/poc.cpp b/hostsidetests/securitybulletin/securityPatch/Bug-115739809/poc.cpp
index 137100b..ef0a5cb 100755
--- a/hostsidetests/securitybulletin/securityPatch/Bug-115739809/poc.cpp
+++ b/hostsidetests/securitybulletin/securityPatch/Bug-115739809/poc.cpp
@@ -57,6 +57,8 @@
outMsg->body.key.source = msg.body.key.source;
// int32_t displayId
outMsg->body.key.displayId = msg.body.key.displayId;
+ // std::array<uint8_t, 32> hmac
+ outMsg->body.key.hmac = msg.body.key.hmac;
// int32_t action
outMsg->body.key.action = msg.body.key.action;
// int32_t flags
@@ -84,6 +86,8 @@
outMsg->body.motion.source = msg.body.motion.source;
// int32_t displayId
outMsg->body.motion.displayId = msg.body.motion.displayId;
+ // std::array<uint8_t, 32> hmac
+ outMsg->body.motion.hmac = msg.body.motion.hmac;
// int32_t action
outMsg->body.motion.action = msg.body.motion.action;
// int32_t actionButton
@@ -100,6 +104,10 @@
outMsg->body.motion.edgeFlags = msg.body.motion.edgeFlags;
// nsecs_t downTime
outMsg->body.motion.downTime = msg.body.motion.downTime;
+ // float xScale
+ outMsg->body.motion.xScale = msg.body.motion.xScale;
+ // float yScale
+ outMsg->body.motion.yScale = msg.body.motion.yScale;
// float xOffset
outMsg->body.motion.xOffset = msg.body.motion.xOffset;
// float yOffset
diff --git a/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java b/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java
index 25231b9b..9f3b4df 100644
--- a/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java
+++ b/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java
@@ -99,6 +99,20 @@
getDevice().getProperty("sys.init.userspace_reboot.last_finished")).isNotEmpty();
}
+ @Test
+ public void testBootReasonProperty_shutdown_aborted() throws Exception {
+ getDevice().reboot("userspace_failed,shutdown_aborted");
+ assertThat(getDevice().getProperty("sys.boot.reason")).isEqualTo(
+ "reboot,userspace_failed,shutdown_aborted");
+ }
+
+ @Test
+ public void testBootReasonProperty_mount_userdata_failed() throws Exception {
+ getDevice().reboot("mount_userdata_failed");
+ assertThat(getDevice().getProperty("sys.boot.reason")).isEqualTo(
+ "reboot,mount_userdata_failed");
+ }
+
// TODO(b/135984674): add test case that forces unmount of f2fs userdata.
private boolean isFsCheckpointingSupported() throws Exception {
diff --git a/tests/BlobStore/Android.bp b/tests/BlobStore/Android.bp
index 06dd877..c168eb6 100644
--- a/tests/BlobStore/Android.bp
+++ b/tests/BlobStore/Android.bp
@@ -21,6 +21,7 @@
"compatibility-device-util-axt",
"truth-prebuilt",
"testng",
+ "BlobStoreTestUtils",
],
srcs: [
"src/**/*.java",
diff --git a/tests/BlobStore/TEST_MAPPING b/tests/BlobStore/TEST_MAPPING
new file mode 100644
index 0000000..ee1df4c
--- /dev/null
+++ b/tests/BlobStore/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsBlobStoreTestCases"
+ }
+ ]
+}
diff --git a/tests/BlobStore/lib/Android.bp b/tests/BlobStore/lib/Android.bp
new file mode 100644
index 0000000..edd2b43
--- /dev/null
+++ b/tests/BlobStore/lib/Android.bp
@@ -0,0 +1,20 @@
+// Copyright (C) 2020 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.
+
+java_library {
+ name: "BlobStoreTestUtils",
+ srcs: ["src/**/*.java"],
+ static_libs: ["truth-prebuilt"],
+ platform_apis: true
+}
\ No newline at end of file
diff --git a/tests/BlobStore/lib/src/com/android/cts/blob/DummyBlobData.java b/tests/BlobStore/lib/src/com/android/cts/blob/DummyBlobData.java
new file mode 100644
index 0000000..d31e15c
--- /dev/null
+++ b/tests/BlobStore/lib/src/com/android/cts/blob/DummyBlobData.java
@@ -0,0 +1,219 @@
+/**
+ * Copyright 2020, 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.cts.blob;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.blob.BlobHandle;
+import android.app.blob.BlobStoreManager;
+import android.content.Context;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.nio.file.Files;
+import java.security.MessageDigest;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+
+public class DummyBlobData {
+ private static final long DEFAULT_SIZE_BYTES = 10 * 1024L * 1024L;
+ private static final int BUFFER_SIZE_BYTES = 16 * 1024;
+
+ private final Context mContext;
+ private final Random mRandom;
+ private final File mFile;
+ private final long mFileSize;
+ private final String mLabel;
+
+ byte[] mFileDigest;
+ long mExpiryTimeMs;
+
+ public DummyBlobData(Context context) {
+ this(context, new Random(0), "blob_" + System.nanoTime());
+ }
+
+ public DummyBlobData(Context context, Random random, String fileName) {
+ this(context, DEFAULT_SIZE_BYTES, random, fileName, "Test label");
+ }
+
+ public DummyBlobData(Context context, Random random, String fileName, String label) {
+ this(context, DEFAULT_SIZE_BYTES, random, fileName, label);
+ }
+
+ public DummyBlobData(Context context, long fileSize, Random random, String fileName,
+ String label) {
+ mContext = context;
+ mRandom = random;
+ mFile = new File(mContext.getFilesDir(), fileName);
+ mFileSize = fileSize;
+ mLabel = label;
+ }
+
+ public void prepare() throws Exception {
+ try (RandomAccessFile file = new RandomAccessFile(mFile, "rw")) {
+ writeRandomData(file, mFileSize);
+ }
+ mFileDigest = FileUtils.digest(mFile, "SHA-256");
+ mExpiryTimeMs = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1);
+ }
+
+ public BlobHandle getBlobHandle() throws Exception {
+ return BlobHandle.createWithSha256(createSha256Digest(mFile), mLabel,
+ mExpiryTimeMs, "test_tag");
+ }
+
+ public long getFileSize() throws Exception {
+ return mFileSize;
+ }
+
+ public void delete() {
+ mFile.delete();
+ }
+
+ public void writeToSession(BlobStoreManager.Session session) throws Exception {
+ writeToSession(session, 0, mFileSize);
+ }
+
+ public void writeToSession(BlobStoreManager.Session session,
+ long offsetBytes, long lengthBytes) throws Exception {
+ try (FileInputStream in = new FileInputStream(mFile)) {
+ in.getChannel().position(offsetBytes);
+ try (FileOutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(
+ session.openWrite(offsetBytes, lengthBytes))) {
+ copy(in, out, lengthBytes);
+ }
+ }
+ }
+
+ public void writeToFd(FileDescriptor fd, long offsetBytes, long lengthBytes) throws Exception {
+ try (FileInputStream in = new FileInputStream(mFile)) {
+ in.getChannel().position(offsetBytes);
+ try (FileOutputStream out = new FileOutputStream(fd)) {
+ copy(in, out, lengthBytes);
+ }
+ }
+ }
+
+ private void copy(InputStream in, OutputStream out, long lengthBytes) throws Exception {
+ final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
+ long bytesWrittern = 0;
+ while (bytesWrittern < lengthBytes) {
+ final int toWrite = (bytesWrittern + buffer.length <= lengthBytes)
+ ? buffer.length : (int) (lengthBytes - bytesWrittern);
+ in.read(buffer, 0, toWrite);
+ out.write(buffer, 0, toWrite);
+ bytesWrittern += toWrite;
+ }
+ }
+
+ public void readFromSessionAndVerifyBytes(BlobStoreManager.Session session,
+ long offsetBytes, int lengthBytes) throws Exception {
+ final byte[] expectedBytes = new byte[lengthBytes];
+ try (FileInputStream in = new FileInputStream(mFile)) {
+ read(in, expectedBytes, offsetBytes, lengthBytes);
+ }
+
+ final byte[] actualBytes = new byte[lengthBytes];
+ try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(
+ session.openWrite(0L, 0L))) {
+ read(in, actualBytes, offsetBytes, lengthBytes);
+ }
+
+ assertThat(actualBytes).isEqualTo(expectedBytes);
+
+ }
+
+ private void read(FileInputStream in, byte[] buffer,
+ long offsetBytes, int lengthBytes) throws Exception {
+ in.getChannel().position(offsetBytes);
+ in.read(buffer, 0, lengthBytes);
+ }
+
+ public void readFromSessionAndVerifyDigest(BlobStoreManager.Session session)
+ throws Exception {
+ readFromSessionAndVerifyDigest(session, 0, mFile.length());
+ }
+
+ public void readFromSessionAndVerifyDigest(BlobStoreManager.Session session,
+ long offsetBytes, long lengthBytes) throws Exception {
+ final byte[] actualDigest;
+ try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(
+ session.openWrite(0L, 0L))) {
+ actualDigest = createSha256Digest(in, offsetBytes, lengthBytes);
+ }
+
+ assertThat(actualDigest).isEqualTo(mFileDigest);
+ }
+
+ public void verifyBlob(ParcelFileDescriptor pfd) throws Exception {
+ final byte[] actualDigest;
+ try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+ actualDigest = FileUtils.digest(in, "SHA-256");
+ }
+ assertThat(actualDigest).isEqualTo(mFileDigest);
+ }
+
+ private byte[] createSha256Digest(FileInputStream in, long offsetBytes, long lengthBytes)
+ throws Exception {
+ final MessageDigest digest = MessageDigest.getInstance("SHA-256");
+ in.getChannel().position(offsetBytes);
+ final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
+ long bytesRead = 0;
+ while (bytesRead < lengthBytes) {
+ int toRead = (bytesRead + buffer.length <= lengthBytes)
+ ? buffer.length : (int) (lengthBytes - bytesRead);
+ toRead = in.read(buffer, 0, toRead);
+ digest.update(buffer, 0, toRead);
+ bytesRead += toRead;
+ }
+ return digest.digest();
+ }
+
+ private byte[] createSha256Digest(File file) throws Exception {
+ final MessageDigest digest = MessageDigest.getInstance("SHA-256");
+ try (BufferedInputStream in = new BufferedInputStream(
+ Files.newInputStream(file.toPath()))) {
+ final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
+ int bytesRead;
+ while ((bytesRead = in.read(buffer)) > 0) {
+ digest.update(buffer, 0, bytesRead);
+ }
+ }
+ return digest.digest();
+ }
+
+ private void writeRandomData(RandomAccessFile file, long fileSize)
+ throws Exception {
+ long bytesWritten = 0;
+ final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
+ while (bytesWritten < fileSize) {
+ mRandom.nextBytes(buffer);
+ final int toWrite = (bytesWritten + buffer.length <= fileSize)
+ ? buffer.length : (int) (fileSize - bytesWritten);
+ file.seek(bytesWritten);
+ file.write(buffer, 0, toWrite);
+ bytesWritten += toWrite;
+ }
+ }
+}
diff --git a/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java b/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java
index 52fc287..3979e6c 100644
--- a/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java
+++ b/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java
@@ -22,7 +22,6 @@
import android.app.blob.BlobHandle;
import android.app.blob.BlobStoreManager;
import android.content.Context;
-import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import com.android.cts.blob.R;
@@ -32,19 +31,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-import java.nio.file.Files;
-import java.security.MessageDigest;
import java.util.ArrayList;
-import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@@ -57,7 +45,6 @@
private Context mContext;
private BlobStoreManager mBlobStoreManager;
- private Random mRandom;
private final ArrayList<Long> mCreatedSessionIds = new ArrayList<>();
@@ -66,7 +53,6 @@
mContext = InstrumentationRegistry.getInstrumentation().getContext();
mBlobStoreManager = (BlobStoreManager) mContext.getSystemService(
Context.BLOB_STORE_SERVICE);
- mRandom = new Random(0);
mCreatedSessionIds.clear();
}
@@ -83,7 +69,7 @@
@Test
public void testGetCreateSession() throws Exception {
- final DummyBlobData blobData = new DummyBlobData();
+ final DummyBlobData blobData = new DummyBlobData(mContext);
blobData.prepare();
try {
final long sessionId = createSession(blobData.getBlobHandle());
@@ -96,7 +82,7 @@
@Test
public void testDeleteSession() throws Exception {
- final DummyBlobData blobData = new DummyBlobData();
+ final DummyBlobData blobData = new DummyBlobData(mContext);
blobData.prepare();
try {
final long sessionId = createSession(blobData.getBlobHandle());
@@ -114,13 +100,13 @@
@Test
public void testOpenReadWriteSession() throws Exception {
- final DummyBlobData blobData = new DummyBlobData();
+ final DummyBlobData blobData = new DummyBlobData(mContext);
blobData.prepare();
try {
final long sessionId = createSession(blobData.getBlobHandle());
assertThat(sessionId).isGreaterThan(0L);
try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
- blobData.writeToSession(session, 0, blobData.mFileSize);
+ blobData.writeToSession(session, 0, blobData.getFileSize());
blobData.readFromSessionAndVerifyDigest(session);
blobData.readFromSessionAndVerifyBytes(session,
101 /* offset */, 1001 /* length */);
@@ -136,7 +122,7 @@
@Test
public void testAbandonSession() throws Exception {
- final DummyBlobData blobData = new DummyBlobData();
+ final DummyBlobData blobData = new DummyBlobData(mContext);
blobData.prepare();
try {
final long sessionId = createSession(blobData.getBlobHandle());
@@ -161,7 +147,7 @@
@Test
public void testCloseSession() throws Exception {
- final DummyBlobData blobData = new DummyBlobData();
+ final DummyBlobData blobData = new DummyBlobData(mContext);
blobData.prepare();
try {
final long sessionId = createSession(blobData.getBlobHandle());
@@ -197,7 +183,7 @@
@Test
public void testAllowPublicAccess() throws Exception {
- final DummyBlobData blobData = new DummyBlobData();
+ final DummyBlobData blobData = new DummyBlobData(mContext);
blobData.prepare();
try {
final long sessionId = createSession(blobData.getBlobHandle());
@@ -220,7 +206,7 @@
@Test
public void testAllowSameSignatureAccess() throws Exception {
- final DummyBlobData blobData = new DummyBlobData();
+ final DummyBlobData blobData = new DummyBlobData(mContext);
blobData.prepare();
try {
final long sessionId = createSession(blobData.getBlobHandle());
@@ -243,7 +229,7 @@
@Test
public void testAllowPackageAccess() throws Exception {
- final DummyBlobData blobData = new DummyBlobData();
+ final DummyBlobData blobData = new DummyBlobData(mContext);
blobData.prepare();
try {
final long sessionId = createSession(blobData.getBlobHandle());
@@ -269,7 +255,7 @@
@Test
public void testMixedAccessType() throws Exception {
- final DummyBlobData blobData = new DummyBlobData();
+ final DummyBlobData blobData = new DummyBlobData(mContext);
blobData.prepare();
try {
final long sessionId = createSession(blobData.getBlobHandle());
@@ -294,7 +280,7 @@
@Test
public void testSessionCommit() throws Exception {
- final DummyBlobData blobData = new DummyBlobData();
+ final DummyBlobData blobData = new DummyBlobData(mContext);
blobData.prepare();
try {
final long sessionId = createSession(blobData.getBlobHandle());
@@ -325,7 +311,7 @@
@Test
public void testOpenBlob() throws Exception {
- final DummyBlobData blobData = new DummyBlobData();
+ final DummyBlobData blobData = new DummyBlobData(mContext);
blobData.prepare();
try {
final long sessionId = createSession(blobData.getBlobHandle());
@@ -357,7 +343,7 @@
@Test
public void testAcquireReleaseLease() throws Exception {
- final DummyBlobData blobData = new DummyBlobData();
+ final DummyBlobData blobData = new DummyBlobData(mContext);
blobData.prepare();
try {
final long sessionId = createSession(blobData.getBlobHandle());
@@ -391,167 +377,4 @@
mCreatedSessionIds.add(sessionId);
return sessionId;
}
-
- class DummyBlobData {
- private static final long DEFAULT_SIZE_BYTES = 10 * 1024L * 1024L;
- private static final int BUFFER_SIZE_BYTES = 16 * 1024;
-
- final File mFile;
- final long mFileSize;
-
- byte[] mFileDigest;
- long mExpiryTimeMs;
-
- DummyBlobData() {
- this(DEFAULT_SIZE_BYTES);
- }
-
- DummyBlobData(long fileSize) {
- mFile = new File(mContext.getFilesDir(), "blob_" + System.nanoTime());
- mFileSize = fileSize;
- }
-
- void prepare() throws Exception {
- try (RandomAccessFile file = new RandomAccessFile(mFile, "rw")) {
- writeRandomData(file, mFileSize);
- }
- mFileDigest = FileUtils.digest(mFile, "SHA-256");
- mExpiryTimeMs = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1);
- }
-
- BlobHandle getBlobHandle() throws Exception {
- return BlobHandle.createWithSha256(createSha256Digest(mFile), "Test blob",
- mExpiryTimeMs, "test_tag");
- }
-
- void delete() {
- mFile.delete();
- }
-
- void writeToSession(BlobStoreManager.Session session) throws Exception {
- writeToSession(session, 0, mFileSize);
- }
-
- void writeToSession(BlobStoreManager.Session session, long offsetBytes, long lengthBytes)
- throws Exception {
- try (FileInputStream in = new FileInputStream(mFile)) {
- in.getChannel().position(offsetBytes);
- try (FileOutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(
- session.openWrite(offsetBytes, lengthBytes))) {
- copy(in, out, lengthBytes);
- }
- }
- }
-
- void writeToFd(FileDescriptor fd, long offsetBytes, long lengthBytes) throws Exception {
- try (FileInputStream in = new FileInputStream(mFile)) {
- in.getChannel().position(offsetBytes);
- try (FileOutputStream out = new FileOutputStream(fd)) {
- copy(in, out, lengthBytes);
- }
- }
- }
-
- void copy(InputStream in, OutputStream out, long lengthBytes) throws Exception {
- final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
- long bytesWrittern = 0;
- while (bytesWrittern < lengthBytes) {
- final int toWrite = (bytesWrittern + buffer.length <= lengthBytes)
- ? buffer.length : (int) (lengthBytes - bytesWrittern);
- in.read(buffer, 0, toWrite);
- out.write(buffer, 0, toWrite);
- bytesWrittern += toWrite;
- }
- }
-
- void readFromSessionAndVerifyBytes(BlobStoreManager.Session session,
- long offsetBytes, int lengthBytes) throws Exception {
- final byte[] expectedBytes = new byte[lengthBytes];
- try (FileInputStream in = new FileInputStream(mFile)) {
- read(in, expectedBytes, offsetBytes, lengthBytes);
- }
-
- final byte[] actualBytes = new byte[lengthBytes];
- try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(
- session.openWrite(0L, 0L))) {
- read(in, actualBytes, offsetBytes, lengthBytes);
- }
-
- assertThat(actualBytes).isEqualTo(expectedBytes);
-
- }
-
- private void read(FileInputStream in, byte[] buffer,
- long offsetBytes, int lengthBytes) throws Exception {
- in.getChannel().position(offsetBytes);
- in.read(buffer, 0, lengthBytes);
- }
-
- void readFromSessionAndVerifyDigest(BlobStoreManager.Session session)
- throws Exception {
- readFromSessionAndVerifyDigest(session, 0, mFile.length());
- }
-
- void readFromSessionAndVerifyDigest(BlobStoreManager.Session session,
- long offsetBytes, long lengthBytes) throws Exception {
- final byte[] actualDigest;
- try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(
- session.openWrite(0L, 0L))) {
- actualDigest = createSha256Digest(in, offsetBytes, lengthBytes);
- }
-
- assertThat(actualDigest).isEqualTo(mFileDigest);
- }
-
- void verifyBlob(ParcelFileDescriptor pfd) throws Exception {
- final byte[] actualDigest;
- try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
- actualDigest = FileUtils.digest(in, "SHA-256");
- }
- assertThat(actualDigest).isEqualTo(mFileDigest);
- }
-
- private byte[] createSha256Digest(FileInputStream in, long offsetBytes, long lengthBytes)
- throws Exception {
- final MessageDigest digest = MessageDigest.getInstance("SHA-256");
- in.getChannel().position(offsetBytes);
- final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
- long bytesRead = 0;
- while (bytesRead < lengthBytes) {
- int toRead = (bytesRead + buffer.length <= lengthBytes)
- ? buffer.length : (int) (lengthBytes - bytesRead);
- toRead = in.read(buffer, 0, toRead);
- digest.update(buffer, 0, toRead);
- bytesRead += toRead;
- }
- return digest.digest();
- }
-
- private byte[] createSha256Digest(File file) throws Exception {
- final MessageDigest digest = MessageDigest.getInstance("SHA-256");
- try (BufferedInputStream in = new BufferedInputStream(
- Files.newInputStream(file.toPath()))) {
- final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
- int bytesRead;
- while ((bytesRead = in.read(buffer)) > 0) {
- digest.update(buffer, 0, bytesRead);
- }
- }
- return digest.digest();
- }
-
- private void writeRandomData(RandomAccessFile file, long fileSize)
- throws Exception {
- long bytesWritten = 0;
- final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
- while (bytesWritten < fileSize) {
- mRandom.nextBytes(buffer);
- final int toWrite = (bytesWritten + buffer.length <= fileSize)
- ? buffer.length : (int) (fileSize - bytesWritten);
- file.seek(bytesWritten);
- file.write(buffer, 0, toWrite);
- bytesWritten += toWrite;
- }
- }
- }
}
diff --git a/tests/accessibilityservice/res/xml/stub_gesture_detect_a11y_service.xml b/tests/accessibilityservice/res/xml/stub_gesture_detect_a11y_service.xml
index ca3eab2..7e74518 100644
--- a/tests/accessibilityservice/res/xml/stub_gesture_detect_a11y_service.xml
+++ b/tests/accessibilityservice/res/xml/stub_gesture_detect_a11y_service.xml
@@ -20,7 +20,7 @@
android:description="@string/stub_gesture_detector_a11y_service_description"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackGeneric"
- android:accessibilityFlags="flagDefault|flagRequestTouchExplorationMode|flagReportViewIds"
+ android:accessibilityFlags="flagDefault|flagRequestTouchExplorationMode|flagReportViewIds|flagServiceHandlesDoubleTap"
android:canRequestTouchExplorationMode="true"
android:canRetrieveWindowContent="true"
android:canPerformGestures="true" />
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java
index 8918de3..3bdf81f4 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java
@@ -15,11 +15,11 @@
package android.accessibilityservice.cts;
import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen;
+import static android.accessibilityservice.cts.utils.DisplayUtils.VirtualDisplaySession;
import static android.accessibilityservice.cts.utils.GestureUtils.click;
import static android.accessibilityservice.cts.utils.GestureUtils.endTimeOf;
import static android.accessibilityservice.cts.utils.GestureUtils.longClick;
import static android.accessibilityservice.cts.utils.GestureUtils.startingAt;
-import static android.accessibilityservice.cts.utils.DisplayUtils.VirtualDisplaySession;
import static android.app.UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES;
import static org.junit.Assert.assertEquals;
@@ -149,6 +149,10 @@
final int dy = mStrokeLenPxY;
// Test recognizing various gestures.
+ testGesture(doubleTap(Display.DEFAULT_DISPLAY), AccessibilityService.GESTURE_DOUBLE_TAP);
+ testGesture(
+ doubleTapAndHold(Display.DEFAULT_DISPLAY),
+ AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD);
testPath(p(-dx, +0), AccessibilityService.GESTURE_SWIPE_LEFT);
testPath(p(+dx, +0), AccessibilityService.GESTURE_SWIPE_RIGHT);
testPath(p(+0, -dy), AccessibilityService.GESTURE_SWIPE_UP);
@@ -191,6 +195,14 @@
try {
// Test recognizing various gestures.
+ testGesture(
+ doubleTap(displayId),
+ AccessibilityService.GESTURE_DOUBLE_TAP,
+ displayId);
+ testGesture(
+ doubleTapAndHold(displayId),
+ AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD,
+ displayId);
testPath(p(-dx, +0), AccessibilityService.GESTURE_SWIPE_LEFT, displayId);
testPath(p(+dx, +0), AccessibilityService.GESTURE_SWIPE_RIGHT, displayId);
testPath(p(+0, -dy), AccessibilityService.GESTURE_SWIPE_UP, displayId);
@@ -265,6 +277,11 @@
.setDisplayId(displayId)
.build();
+ testGesture(gesture, gestureId, displayId);
+ }
+
+ /** Dispatch a gesture and make sure it is detected as the specified gesture id. */
+ private void testGesture(GestureDescription gesture, int gestureId, int displayId) {
// Dispatch gesture motions to specified display with GestureDescription..
// Use AccessibilityService.dispatchGesture() instead of Instrumentation.sendPointerSync()
// because accessibility services read gesture events upstream from the point where
@@ -288,6 +305,10 @@
assertEquals(expectedGestureEvent.toString(), mService.getGestureInfo(0).toString());
}
+ private void testGesture(GestureDescription gesture, int gestureId) {
+ testGesture(gesture, gestureId, Display.DEFAULT_DISPLAY);
+ }
+
/** Create a path from startPoint, moving by delta1, then delta2. (delta2 may be null.) */
Path linePath(Point startPoint, Point delta1, Point delta2) {
Path path = new Path();
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
index 553254f..9042d62 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
@@ -258,7 +258,9 @@
/**
* Test the case where we execute a "sloppy" double tap, meaning that the second tap isn't
* exactly in the same location as the first but still within tolerances. It should behave the
- * same as a standard double tap.
+ * same as a standard double tap. Note that this test does not request that double tap be
+ * dispatched to the accessibility service, meaning that it will be handled by the framework and
+ * the view will be clicked.
*/
@Test
@AppModeFull
@@ -280,7 +282,9 @@
/**
* Test the case where we want to click on the item that has accessibility focus by using
- * AccessibilityNodeInfo.performAction.
+ * AccessibilityNodeInfo.performAction. Note that this test does not request that double tap be
+ * dispatched to the accessibility service, meaning that it will be handled by the framework and
+ * the view will be clicked.
*/
@Test
@AppModeFull
@@ -316,7 +320,11 @@
mClickListener.assertNoneClicked();
}
- /** Test the case where we want to long click on the item that has accessibility focus. */
+ /**
+ * Test the case where we want to long click on the item that has accessibility focus. Note that
+ * this test does not request that double tap and hold be dispatched to the accessibility
+ * service, meaning that it will be handled by the framework and the view will be long clicked.
+ */
@Test
@AppModeFull
public void testDoubleTapAndHoldAccessibilityFocus_performsLongClick() {
diff --git a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
index 41201e3..1f8d45e 100644
--- a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
+++ b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
@@ -774,6 +774,11 @@
|| message.contains("can only be called by the device owner"));
}
+ private void assertOrganizationOwnedProfileOwnerMessage(String message) {
+ assertTrue("message is: "+ message,
+ message.contains("is not the profile owner on organization-owned device"));
+ }
+
private void assertDeviceOwnerOrManageUsersMessage(String message) {
assertTrue("message is: "+ message, message.contains("does not own the device")
|| message.contains("can only be called by the device owner")
@@ -811,16 +816,16 @@
}
}
- public void testSetSystemUpdatePolicy_failIfNotDeviceOwner() {
+ public void testSetSystemUpdatePolicy_failIfNotOrganizationOwnedProfileOwner() {
if (!mDeviceAdmin) {
- Log.w(TAG, "Skipping testSetSystemUpdatePolicy_failIfNotDeviceOwner");
+ Log.w(TAG, "Skipping testSetSystemUpdatePolicy_failIfNotOrganizationOwnedProfileOwner");
return;
}
try {
mDevicePolicyManager.setSystemUpdatePolicy(mComponent, null);
fail("did not throw expected SecurityException");
} catch (SecurityException e) {
- assertDeviceOwnerMessage(e.getMessage());
+ assertOrganizationOwnedProfileOwnerMessage(e.getMessage());
}
}
diff --git a/tests/app/src/android/app/cts/ActivityManagerApi29Test.java b/tests/app/src/android/app/cts/ActivityManagerApi29Test.java
index 4c56d1c..71d81da 100644
--- a/tests/app/src/android/app/cts/ActivityManagerApi29Test.java
+++ b/tests/app/src/android/app/cts/ActivityManagerApi29Test.java
@@ -40,6 +40,7 @@
import android.provider.DeviceConfig;
import android.provider.Settings;
+import androidx.test.filters.Suppress;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -61,6 +62,7 @@
* one of the foreground state (including foreground_service state), this operation will be denied
* when the process is in background state.
*/
+@Suppress
@RunWith(AndroidJUnit4.class)
public class ActivityManagerApi29Test {
private static final String PACKAGE_NAME = "android.app.cts.activitymanager.api29";
diff --git a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
index ac50181..1251cda 100644
--- a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
@@ -54,6 +54,7 @@
import android.os.SystemClock;
import android.permission.cts.PermissionUtils;
import android.server.wm.WindowManagerState;
+import androidx.test.filters.Suppress;
import android.support.test.uiautomator.BySelector;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiSelector;
@@ -1725,6 +1726,7 @@
* PROCESS_CAPABILITY_FOREGROUND_LOCATION.
* @throws Exception
*/
+ @Suppress
public void testFgsLocation() throws Exception {
ApplicationInfo app1Info = mContext.getPackageManager().getApplicationInfo(
PACKAGE_NAME_APP1, 0);
@@ -1787,6 +1789,7 @@
* passed from client to service.
* @throws Exception
*/
+ @Suppress
public void testFgsLocationBind() throws Exception {
setupWatchers(3);
@@ -1861,6 +1864,7 @@
* Bound app should be TOP w/flag and BTOP without flag.
* @throws Exception
*/
+ @Suppress
public void testTopBind() throws Exception {
setupWatchers(2);
diff --git a/tests/app/src/android/app/cts/UiModeManagerTest.java b/tests/app/src/android/app/cts/UiModeManagerTest.java
index 4dea1e4..77175c3 100644
--- a/tests/app/src/android/app/cts/UiModeManagerTest.java
+++ b/tests/app/src/android/app/cts/UiModeManagerTest.java
@@ -26,11 +26,12 @@
import android.test.AndroidTestCase;
import android.util.Log;
+import androidx.test.filters.Suppress;
+
import com.android.compatibility.common.util.BatteryUtils;
import com.android.compatibility.common.util.SettingsUtils;
import junit.framework.Assert;
-import org.junit.Ignore;
import java.io.FileInputStream;
import java.io.IOException;
@@ -38,6 +39,9 @@
public class UiModeManagerTest extends AndroidTestCase {
private static final String TAG = "UiModeManagerTest";
+ private static final long MAX_WAIT_TIME = 2 * 1000;
+
+ private static final long WAIT_TIME_INCR = 100;
private UiModeManager mUiModeManager;
@@ -79,17 +83,19 @@
}
}
+ @Suppress //("b/146764875")
public void testNightModeYesPersisted() throws InterruptedException {
+ // Reset the mode to no if it is set to another value
setNightMode(UiModeManager.MODE_NIGHT_NO);
- assertStoredNightModeSetting(UiModeManager.MODE_NIGHT_NO);
setNightMode(UiModeManager.MODE_NIGHT_YES);
assertStoredNightModeSetting(UiModeManager.MODE_NIGHT_YES);
}
+ @Suppress //("b/146764875")
public void testNightModeAutoPersisted() throws InterruptedException {
+ // Reset the mode to no if it is set to another value
setNightMode(UiModeManager.MODE_NIGHT_NO);
- assertStoredNightModeSetting(UiModeManager.MODE_NIGHT_NO);
setNightMode(UiModeManager.MODE_NIGHT_AUTO);
assertStoredNightModeSetting(UiModeManager.MODE_NIGHT_AUTO);
@@ -296,9 +302,18 @@
}
private void assertStoredNightModeSetting(int mode) {
+ int storedModeInt = -1;
// Settings.Secure.UI_NIGHT_MODE
- String storedMode = SettingsUtils.getSecureSetting("ui_night_mode");
- int storedModeInt = Integer.parseInt(storedMode);
+ for (int i = 0; i < MAX_WAIT_TIME; i += WAIT_TIME_INCR) {
+ String storedMode = SettingsUtils.getSecureSetting("ui_night_mode");
+ storedModeInt = Integer.parseInt(storedMode);
+ if (mode == storedModeInt) break;
+ try {
+ Thread.sleep(WAIT_TIME_INCR);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
assertEquals(mode, storedModeInt);
}
diff --git a/tests/appsearch/src/com/android/cts/appsearch/AppSearchManagerTest.java b/tests/appsearch/src/com/android/cts/appsearch/AppSearchManagerTest.java
index c5389af..25ee503 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/AppSearchManagerTest.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/AppSearchManagerTest.java
@@ -17,8 +17,8 @@
import static com.google.common.truth.Truth.assertThat;
-import android.app.appsearch.AppSearch;
import android.app.appsearch.AppSearchBatchResult;
+import android.app.appsearch.AppSearchEmail;
import android.app.appsearch.AppSearchManager;
import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.AppSearchSchema.PropertyConfig;
@@ -66,11 +66,10 @@
@Test
public void testPutDocuments() throws Exception {
// Schema registration
- mAppSearch.setSchema(AppSearch.Email.SCHEMA);
+ mAppSearch.setSchema(AppSearchEmail.SCHEMA);
// Index a document
- AppSearch.Email email =
- AppSearch.Email.newBuilder("uri1")
+ AppSearchEmail email = new AppSearchEmail.Builder("uri1")
.setFrom("from@example.com")
.setTo("to1@example.com", "to2@example.com")
.setSubject("testPut example")
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DatasetTest.java b/tests/autofillservice/src/android/autofillservice/cts/DatasetTest.java
index 82912f0..c9434cb 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DatasetTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DatasetTest.java
@@ -50,7 +50,8 @@
private final InlinePresentation mInlinePresentation = new InlinePresentation(
new Slice.Builder(new Uri.Builder().appendPath("DatasetTest").build(),
new SliceSpec("DatasetTest", 1)).build(),
- new InlinePresentationSpec.Builder(new Size(10, 10), new Size(50, 50)).build());
+ new InlinePresentationSpec.Builder(new Size(10, 10),
+ new Size(50, 50)).build(), /* pinned= */ false);
private final RemoteViews mPresentation = mock(RemoteViews.class);
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
index 73e3828..01d9a1e2 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
@@ -2519,6 +2519,17 @@
CaptureRequest.DISTORTION_CORRECTION_MODE_OFF);
}
+ // Scaler settings
+ if (mStaticInfo.areKeysAvailable(
+ CameraCharacteristics.SCALER_AVAILABLE_ROTATE_AND_CROP_MODES)) {
+ List<Integer> rotateAndCropModes = Arrays.asList(toObject(
+ props.get(CameraCharacteristics.SCALER_AVAILABLE_ROTATE_AND_CROP_MODES)));
+ if (rotateAndCropModes.contains(SCALER_ROTATE_AND_CROP_AUTO)) {
+ mCollector.expectKeyValueEquals(request, SCALER_ROTATE_AND_CROP,
+ CaptureRequest.SCALER_ROTATE_AND_CROP_AUTO);
+ }
+ }
+
// TODO: use the list of keys from CameraCharacteristics to avoid expecting
// keys which are not available by this CameraDevice.
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
index d7556f3..8f9f44b 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -42,6 +42,7 @@
import android.os.ParcelFileDescriptor;
import android.test.AndroidTestCase;
import android.util.Log;
+import android.util.Pair;
import androidx.test.InstrumentationRegistry;
import com.android.compatibility.common.util.PropertyUtil;
@@ -62,6 +63,7 @@
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
+import java.util.Set;
/**
* <p>Basic test for CameraManager class.</p>
@@ -72,6 +74,7 @@
private static final String TAG = "CameraManagerTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private static final int NUM_CAMERA_REOPENS = 10;
+ private static final int AVAILABILITY_TIMEOUT_MS = 10;
private PackageManager mPackageManager;
private NoopCameraListener mListener;
@@ -576,13 +579,32 @@
testCameraManagerListenerCallbacks(/*useExecutor*/ true);
}
+ private <T> void verifyAvailabilityCbsReceived(HashSet<T> expectedCameras,
+ LinkedBlockingQueue<T> queue, LinkedBlockingQueue<T> otherQueue,
+ boolean available) throws Exception {
+ while (expectedCameras.size() > 0) {
+ T id = queue.poll(AVAILABILITY_TIMEOUT_MS,
+ java.util.concurrent.TimeUnit.MILLISECONDS);
+ assertTrue("Did not receive initial " + (available ? "available" : "unavailable")
+ + " notices for some cameras", id != null);
+ expectedCameras.remove(id);
+ }
+ // Verify no unavailable/available cameras were reported
+ assertTrue("Some camera devices are initially " + (available ? "unavailable" : "available"),
+ otherQueue.size() == 0);
+ }
+
private void testCameraManagerListenerCallbacks(boolean useExecutor) throws Exception {
- final int AVAILABILITY_TIMEOUT_MS = 10;
final LinkedBlockingQueue<String> availableEventQueue = new LinkedBlockingQueue<>();
final LinkedBlockingQueue<String> unavailableEventQueue = new LinkedBlockingQueue<>();
final Executor executor = useExecutor ? new HandlerExecutor(mHandler) : null;
+ final LinkedBlockingQueue<Pair<String, String>> availablePhysicalCamEventQueue =
+ new LinkedBlockingQueue<>();
+ final LinkedBlockingQueue<Pair<String, String>> unavailablePhysicalCamEventQueue =
+ new LinkedBlockingQueue<>();
+
CameraManager.AvailabilityCallback ac = new CameraManager.AvailabilityCallback() {
@Override
public void onCameraAvailable(String cameraId) {
@@ -604,6 +626,16 @@
public void onCameraUnavailable(String cameraId) {
unavailableEventQueue.offer(cameraId);
}
+
+ @Override
+ public void onPhysicalCameraAvailable(String cameraId, String physicalCameraId) {
+ availablePhysicalCamEventQueue.offer(new Pair<>(cameraId, physicalCameraId));
+ }
+
+ @Override
+ public void onPhysicalCameraUnavailable(String cameraId, String physicalCameraId) {
+ unavailablePhysicalCamEventQueue.offer(new Pair<>(cameraId, physicalCameraId));
+ }
};
if (useExecutor) {
@@ -620,22 +652,18 @@
// Verify we received available for all cameras' initial state in a reasonable amount of time
HashSet<String> expectedAvailableCameras = new HashSet<String>(Arrays.asList(cameras));
- while (expectedAvailableCameras.size() > 0) {
- String id = availableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
- java.util.concurrent.TimeUnit.MILLISECONDS);
- assertTrue("Did not receive initial availability notices for some cameras",
- id != null);
- expectedAvailableCameras.remove(id);
- }
- // Verify no unavailable cameras were reported
- assertTrue("Some camera devices are initially unavailable",
- unavailableEventQueue.size() == 0);
+ verifyAvailabilityCbsReceived(expectedAvailableCameras, availableEventQueue,
+ unavailableEventQueue, true /*available*/);
// Verify transitions for individual cameras
for (String id : cameras) {
MockStateCallback mockListener = MockStateCallback.mock();
mCameraListener = new BlockingStateCallback(mockListener);
+ // Clear logical camera callback queue in case the initial state of certain physical
+ // cameras are unavailable.
+ unavailablePhysicalCamEventQueue.clear();
+
if (useExecutor) {
mCameraManager.openCamera(id, executor, mCameraListener);
} else {
@@ -657,6 +685,23 @@
assertTrue("Availability events received unexpectedly",
availableEventQueue.size() == 0);
+ // Verify that we see the expected 'unavailable' events if this camera is a physical
+ // camera of another logical multi-camera
+ HashSet<Pair<String, String>> relatedLogicalCameras = new HashSet<>();
+ for (String multiCamId : cameras) {
+ CameraCharacteristics props = mCameraManager.getCameraCharacteristics(multiCamId);
+ Set<String> physicalIds = props.getPhysicalCameraIds();
+ if (physicalIds.contains(id)) {
+ relatedLogicalCameras.add(new Pair<String, String>(multiCamId, id));
+ }
+ }
+
+ HashSet<Pair<String, String>> expectedLogicalCameras =
+ new HashSet<>(relatedLogicalCameras);
+ verifyAvailabilityCbsReceived(expectedLogicalCameras,
+ unavailablePhysicalCamEventQueue, availablePhysicalCamEventQueue,
+ false /*available*/);
+
// Verify that we see the expected 'available' event after closing the camera
camera.close();
@@ -672,6 +717,10 @@
assertTrue("Unavailability events received unexpectedly",
unavailableEventQueue.size() == 0);
+ expectedLogicalCameras = new HashSet<Pair<String, String>>(relatedLogicalCameras);
+ verifyAvailabilityCbsReceived(expectedLogicalCameras,
+ availablePhysicalCamEventQueue, unavailablePhysicalCamEventQueue,
+ true /*available*/);
}
// Verify that we can unregister the listener and see no more events
@@ -718,7 +767,15 @@
candidateId),
candidateId == null);
+ Pair<String, String> candidatePhysicalIds = unavailablePhysicalCamEventQueue.poll(
+ AVAILABILITY_TIMEOUT_MS, java.util.concurrent.TimeUnit.MILLISECONDS);
+ assertTrue("Received unavailability physical camera notice unexpectedly ",
+ candidatePhysicalIds == null);
+ candidatePhysicalIds = availablePhysicalCamEventQueue.poll(
+ AVAILABILITY_TIMEOUT_MS, java.util.concurrent.TimeUnit.MILLISECONDS);
+ assertTrue("Received availability notice for physical camera unexpectedly ",
+ candidatePhysicalIds == null);
}
} // testCameraManagerListenerCallbacks
diff --git a/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
index cf48aa4..6c304d7 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
@@ -987,6 +987,7 @@
resultKeys.add(CaptureResult.NOISE_REDUCTION_MODE);
resultKeys.add(CaptureResult.REQUEST_PIPELINE_DEPTH);
resultKeys.add(CaptureResult.SCALER_CROP_REGION);
+ resultKeys.add(CaptureResult.SCALER_ROTATE_AND_CROP);
resultKeys.add(CaptureResult.SENSOR_EXPOSURE_TIME);
resultKeys.add(CaptureResult.SENSOR_FRAME_DURATION);
resultKeys.add(CaptureResult.SENSOR_SENSITIVITY);
diff --git a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index 09079a6..30dba3e 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -2315,6 +2315,53 @@
}
}
+ /**
+ * Check rotate-and-crop camera reporting.
+ * Every device must report NONE; if actually supporting feature, must report NONE, 90, AUTO at
+ * least.
+ */
+ @Test
+ public void testRotateAndCropCharacteristics() {
+ for (int i = 0; i < mAllCameraIds.length; i++) {
+ Log.i(TAG, "testRotateAndCropCharacteristics: Testing camera ID " + mAllCameraIds[i]);
+
+ CameraCharacteristics c = mCharacteristics.get(i);
+
+ if (!arrayContains(mCameraIdsUnderTest, mAllCameraIds[i])) {
+ // Skip hidden physical cameras
+ continue;
+ }
+
+ int[] availableRotateAndCropModes = c.get(
+ CameraCharacteristics.SCALER_AVAILABLE_ROTATE_AND_CROP_MODES);
+ assertTrue("availableRotateAndCropModes must not be null",
+ availableRotateAndCropModes != null);
+ boolean foundAuto = false;
+ boolean foundNone = false;
+ boolean found90 = false;
+ for (int mode : availableRotateAndCropModes) {
+ switch(mode) {
+ case CameraCharacteristics.SCALER_ROTATE_AND_CROP_NONE:
+ foundNone = true;
+ break;
+ case CameraCharacteristics.SCALER_ROTATE_AND_CROP_90:
+ found90 = true;
+ break;
+ case CameraCharacteristics.SCALER_ROTATE_AND_CROP_AUTO:
+ foundAuto = true;
+ break;
+ }
+ }
+ if (availableRotateAndCropModes.length > 1) {
+ assertTrue("To support SCALER_ROTATE_AND_CROP: NONE, 90, and AUTO must be included",
+ foundNone && found90 && foundAuto);
+ } else {
+ assertTrue("If only one SCALER_ROTATE_AND_CROP value is supported, it must be NONE",
+ foundNone);
+ }
+ }
+ }
+
private boolean matchParametersToCharacteritics(Camera.Parameters params,
Camera.CameraInfo info, CameraCharacteristics ch) {
Integer facing = ch.get(CameraCharacteristics.LENS_FACING);
diff --git a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
index 27e6492..b8dc868 100644
--- a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
@@ -48,6 +48,7 @@
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.Pair;
import android.util.Range;
import android.util.Size;
import android.util.SizeF;
@@ -61,7 +62,9 @@
import java.util.Arrays;
import java.util.ArrayList;
+import java.util.concurrent.LinkedBlockingQueue;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -711,6 +714,29 @@
*/
@Test
public void testActivePhysicalId() throws Exception {
+ final int AVAILABILITY_TIMEOUT_MS = 10;
+ final LinkedBlockingQueue<Pair<String, String>> unavailablePhysicalCamEventQueue =
+ new LinkedBlockingQueue<>();
+ CameraManager.AvailabilityCallback ac = new CameraManager.AvailabilityCallback() {
+ @Override
+ public void onPhysicalCameraUnavailable(String cameraId, String physicalCameraId) {
+ unavailablePhysicalCamEventQueue.offer(new Pair<>(cameraId, physicalCameraId));
+ }
+ };
+
+ mCameraManager.registerAvailabilityCallback(ac, mHandler);
+ Set<Pair<String, String>> unavailablePhysicalCameras = new HashSet<Pair<String, String>>();
+ Pair<String, String> candidatePhysicalIds =
+ unavailablePhysicalCamEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
+ java.util.concurrent.TimeUnit.MILLISECONDS);
+ while (candidatePhysicalIds != null) {
+ unavailablePhysicalCameras.add(candidatePhysicalIds);
+ candidatePhysicalIds =
+ unavailablePhysicalCamEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
+ java.util.concurrent.TimeUnit.MILLISECONDS);
+ }
+ mCameraManager.unregisterAvailabilityCallback(ac);
+
for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing Camera " + id);
@@ -777,10 +803,18 @@
}
}
}
+
+ // Query unavailable physical cameras, and make sure the active physical id
+ // isn't an unavailable physical camera.
+ for (Pair<String, String> unavailPhysicalCamera: unavailablePhysicalCameras) {
+ assertFalse(unavailPhysicalCamera.first.equals(id) &&
+ unavailPhysicalCamera.second.equals(storedActiveId));
+ }
} finally {
closeDevice();
}
}
+
}
/**
diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index 918daeb..101d2e1 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -55,6 +55,8 @@
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -174,6 +176,29 @@
*/
@Test
public void testMandatoryOutputCombinations() throws Exception {
+ final int AVAILABILITY_TIMEOUT_MS = 10;
+ final LinkedBlockingQueue<Pair<String, String>> unavailablePhysicalCamEventQueue =
+ new LinkedBlockingQueue<>();
+ CameraManager.AvailabilityCallback ac = new CameraManager.AvailabilityCallback() {
+ @Override
+ public void onPhysicalCameraUnavailable(String cameraId, String physicalCameraId) {
+ unavailablePhysicalCamEventQueue.offer(new Pair<>(cameraId, physicalCameraId));
+ }
+ };
+
+ mCameraManager.registerAvailabilityCallback(ac, mHandler);
+ Set<Pair<String, String>> unavailablePhysicalCameras = new HashSet<Pair<String, String>>();
+ Pair<String, String> candidatePhysicalIds =
+ unavailablePhysicalCamEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
+ java.util.concurrent.TimeUnit.MILLISECONDS);
+ while (candidatePhysicalIds != null) {
+ unavailablePhysicalCameras.add(candidatePhysicalIds);
+ candidatePhysicalIds =
+ unavailablePhysicalCamEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
+ java.util.concurrent.TimeUnit.MILLISECONDS);
+ }
+ mCameraManager.unregisterAvailabilityCallback(ac);
+
for (String id : mCameraIdsUnderTest) {
openDevice(id);
MandatoryStreamCombination[] combinations =
@@ -204,6 +229,13 @@
// its stream combination through logical camera.
continue;
}
+ for (Pair<String, String> unavailPhysicalCam : unavailablePhysicalCameras) {
+ if (unavailPhysicalCam.first.equals(id) ||
+ unavailPhysicalCam.second.equals(physicalId)) {
+ // This particular physical camera isn't available. Skip.
+ continue;
+ }
+ }
StaticMetadata physicalStaticInfo = mAllStaticInfo.get(physicalId);
MandatoryStreamCombination[] phyCombinations =
physicalStaticInfo.getCharacteristics().get(
diff --git a/tests/camera/src/android/hardware/multiprocess/camera/cts/Camera2Activity.java b/tests/camera/src/android/hardware/multiprocess/camera/cts/Camera2Activity.java
index 6fd9173..789626a 100644
--- a/tests/camera/src/android/hardware/multiprocess/camera/cts/Camera2Activity.java
+++ b/tests/camera/src/android/hardware/multiprocess/camera/cts/Camera2Activity.java
@@ -88,6 +88,22 @@
cameraId);
Log.i(TAG, "Camera " + cameraId + " is unavailable");
}
+
+ @Override
+ public void onPhysicalCameraAvailable(String cameraId, String physicalCameraId) {
+ super.onPhysicalCameraAvailable(cameraId, physicalCameraId);
+ mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_AVAILABLE,
+ cameraId + " : " + physicalCameraId);
+ Log.i(TAG, "Camera " + cameraId + " : " + physicalCameraId + " is available");
+ }
+
+ @Override
+ public void onPhysicalCameraUnavailable(String cameraId, String physicalCameraId) {
+ super.onPhysicalCameraUnavailable(cameraId, physicalCameraId);
+ mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_UNAVAILABLE,
+ cameraId + " : " + physicalCameraId);
+ Log.i(TAG, "Camera " + cameraId + " : " + physicalCameraId + " is unavailable");
+ }
}, null);
final String chosen = cameraIds[0];
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/BroadcastReceiverActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/BroadcastReceiverActivity.java
index c967a32..b3ab326 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/BroadcastReceiverActivity.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/BroadcastReceiverActivity.java
@@ -42,21 +42,28 @@
import android.view.ViewGroup;
import android.view.Window;
+import java.lang.ref.WeakReference;
+
/**
* Activity that registers broadcast receiver .
*/
public class BroadcastReceiverActivity extends Activity {
private static final String TAG = BroadcastReceiverActivity.class.getSimpleName();
- private TestBroadcastReceiver mBroadcastReceiver = new TestBroadcastReceiver();
+ private TestBroadcastReceiver mBroadcastReceiver;
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
- IntentFilter broadcastFilter = new IntentFilter(ACTION_TRIGGER_BROADCAST);
-
- registerReceiver(mBroadcastReceiver, broadcastFilter);
+ final Object receiver = getLastNonConfigurationInstance();
+ if (receiver instanceof TestBroadcastReceiver) {
+ mBroadcastReceiver = (TestBroadcastReceiver) receiver;
+ mBroadcastReceiver.associate(this);
+ } else {
+ mBroadcastReceiver = new TestBroadcastReceiver(this);
+ mBroadcastReceiver.register();
+ }
// Determine if a display cutout is present
final View view = new View(this);
@@ -79,38 +86,79 @@
@Override
protected void onDestroy() {
super.onDestroy();
-
- unregisterReceiver(mBroadcastReceiver);
+ mBroadcastReceiver.destroy();
}
- public class TestBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public Object onRetainNonConfigurationInstance() {
+ return mBroadcastReceiver;
+ }
+
+ /**
+ * The receiver to perform action on the associated activity. If a broadcast intent is received
+ * while the activity is relaunching, it will be handled after the activity is recreated.
+ */
+ private static class TestBroadcastReceiver extends BroadcastReceiver {
+ final Context mAppContext;
+ WeakReference<Activity> mRef;
+
+ TestBroadcastReceiver(Activity activity) {
+ mAppContext = activity.getApplicationContext();
+ associate(activity);
+ }
+
+ void register() {
+ mAppContext.registerReceiver(this, new IntentFilter(ACTION_TRIGGER_BROADCAST));
+ }
+
+ void associate(Activity activity) {
+ mRef = new WeakReference<>(activity);
+ }
+
+ void destroy() {
+ final Activity activity = mRef != null ? mRef.get() : null;
+ if (activity != null && activity.isChangingConfigurations()) {
+ // The activity is destroyed for configuration change. Because it will be recreated
+ // immediately the receiver only needs to associate to the new instance.
+ return;
+ }
+ // The case of real finish.
+ mAppContext.unregisterReceiver(this);
+ }
+
@Override
public void onReceive(Context context, Intent intent) {
final Bundle extras = intent.getExtras();
- Log.i(TAG, "onReceive: extras=" + extras);
-
if (extras == null) {
return;
}
+ // Trigger unparcel so the log can show the content of extras.
+ extras.size();
+ Log.i(TAG, "onReceive: extras=" + extras);
+
+ final Activity activity = mRef.get();
+ if (activity == null) {
+ return;
+ }
+
if (extras.getBoolean(EXTRA_FINISH_BROADCAST)) {
- finish();
+ activity.finish();
}
if (extras.getBoolean(EXTRA_MOVE_BROADCAST_TO_BACK)) {
- moveTaskToBack(true);
+ activity.moveTaskToBack(true);
}
if (extras.containsKey(EXTRA_BROADCAST_ORIENTATION)) {
- setRequestedOrientation(extras.getInt(EXTRA_BROADCAST_ORIENTATION));
+ activity.setRequestedOrientation(extras.getInt(EXTRA_BROADCAST_ORIENTATION));
}
if (extras.getBoolean(EXTRA_DISMISS_KEYGUARD)) {
- getWindow().addFlags(FLAG_DISMISS_KEYGUARD);
+ activity.getWindow().addFlags(FLAG_DISMISS_KEYGUARD);
}
if (extras.getBoolean(EXTRA_DISMISS_KEYGUARD_METHOD)) {
- getSystemService(KeyguardManager.class).requestDismissKeyguard(
- BroadcastReceiverActivity.this,
+ activity.getSystemService(KeyguardManager.class).requestDismissKeyguard(activity,
new KeyguardDismissLoggerCallback(context, BROADCAST_RECEIVER_ACTIVITY));
}
- ActivityLauncher.launchActivityFromExtras(BroadcastReceiverActivity.this, extras);
+ ActivityLauncher.launchActivityFromExtras(activity, extras);
}
}
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java b/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
index 67cabaa..459eda4 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
@@ -21,6 +21,7 @@
import static android.server.wm.dndsourceapp.Components.DRAG_SOURCE;
import static android.server.wm.dndtargetapp.Components.DROP_TARGET;
import static android.server.wm.dndtargetappsdk23.Components.DROP_TARGET_SDK23;
+import static android.view.Display.DEFAULT_DISPLAY;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -142,6 +143,8 @@
Point topLeft = new Point(leftSide ? 0 : displaySize.x / 2, 0);
Point bottomRight = new Point(leftSide ? displaySize.x / 2 : displaySize.x, displaySize.y);
resizeActivityTask(componentName, topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);
+ waitAndAssertTopResumedActivity(componentName, DEFAULT_DISPLAY,
+ "Activity launched as freeform should be resumed");
}
private void injectInput(Point from, Point to, int steps) throws Exception {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java
index c4e7e94..db3c78c 100755
--- a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java
@@ -509,14 +509,15 @@
mAmWmState.assertKeyguardShowingAndNotOccluded();
}
- @Test
- /*
- Turn on keyguard, and launch an activity on top of the keyguard.
- Next, change the orientation of the device to rotate the activity.
- The activity should still remain above keyguard at this point.
- Send the 'finish' broadcast to dismiss the activity.
- Ensure that the activity is gone, and the keyguard is visible.
+
+ /**
+ * Turn on keyguard, and launch an activity on top of the keyguard.
+ * Next, change the orientation of the device to rotate the activity.
+ * The activity should still remain above keyguard at this point.
+ * Send the 'finish' broadcast to dismiss the activity.
+ * Ensure that the activity is gone, and the keyguard is visible.
*/
+ @Test
public void testUnoccludedRotationChange() {
// Go home now to make sure Home is behind Keyguard.
launchHomeActivity();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
index 231b8ee..3338584 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
@@ -214,60 +214,6 @@
mAmWmState.assertVisibility(PIP_ACTIVITY, true);
}
- @Test
- public void testPinnedStackDefaultBounds() {
- // Launch a PIP activity
- launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
- // Wait for animation complete since we are comparing bounds
- waitForEnterPipAnimationComplete(PIP_ACTIVITY);
-
- final RotationSession rotationSession = createManagedRotationSession();
- rotationSession.set(ROTATION_0);
- waitForValidPinnedStackBounds(WindowManagerState::getDefaultPinnedStackBounds);
- WindowManagerState wmState = mAmWmState.getWmState();
- wmState.computeState();
- Rect defaultPipBounds = wmState.getDefaultPinnedStackBounds();
- Rect stableBounds = wmState.getStableBounds();
- assertTrue(defaultPipBounds.width() > 0 && defaultPipBounds.height() > 0);
- assertTrue(stableBounds.contains(defaultPipBounds));
-
- rotationSession.set(ROTATION_90);
- waitForValidPinnedStackBounds(WindowManagerState::getDefaultPinnedStackBounds);
- wmState = mAmWmState.getWmState();
- wmState.computeState();
- defaultPipBounds = wmState.getDefaultPinnedStackBounds();
- stableBounds = wmState.getStableBounds();
- assertTrue(defaultPipBounds.width() > 0 && defaultPipBounds.height() > 0);
- assertTrue(stableBounds.contains(defaultPipBounds));
- }
-
- @Test
- public void testPinnedStackMovementBounds() {
- // Launch a PIP activity
- launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
- // Wait for animation complete since we are comparing bounds
- waitForEnterPipAnimationComplete(PIP_ACTIVITY);
-
- final RotationSession rotationSession = createManagedRotationSession();
- rotationSession.set(ROTATION_0);
- waitForValidPinnedStackBounds(WindowManagerState::getPinnedStackMovementBounds);
- WindowManagerState wmState = mAmWmState.getWmState();
- wmState.computeState();
- Rect pipMovementBounds = wmState.getPinnedStackMovementBounds();
- Rect stableBounds = wmState.getStableBounds();
- assertTrue(pipMovementBounds.width() > 0 && pipMovementBounds.height() > 0);
- assertTrue(stableBounds.contains(pipMovementBounds));
-
- rotationSession.set(ROTATION_90);
- waitForValidPinnedStackBounds(WindowManagerState::getPinnedStackMovementBounds);
- wmState = mAmWmState.getWmState();
- wmState.computeState();
- pipMovementBounds = wmState.getPinnedStackMovementBounds();
- stableBounds = wmState.getStableBounds();
- assertTrue(pipMovementBounds.width() > 0 && pipMovementBounds.height() > 0);
- assertTrue(stableBounds.contains(pipMovementBounds));
- }
-
private void waitForValidPinnedStackBounds(Function<WindowManagerState, Rect> boundsFunc) {
mAmWmState.waitForWithWmState(wmState -> {
final Rect bounds = boundsFunc.apply(wmState);
@@ -1243,108 +1189,6 @@
finalAppSize);
}
- @FlakyTest(bugId = 145133340)
- @Test
- public void testEnterPictureInPictureSavePosition() throws Exception {
- // Ensure we have static shelf offset by running this test over a non-home activity
- launchActivity(NO_RELAUNCH_ACTIVITY);
- mAmWmState.waitForActivityState(mAmWmState.getAmState().getHomeActivityName(),
- STATE_STOPPED);
-
- // Launch PiP activity with auto-enter PiP, save the default position of the PiP
- // (while the PiP is still animating sleep)
- launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
- // Wait for animation complete since we are comparing bounds
- waitForEnterPipAnimationComplete(PIP_ACTIVITY);
- assertPinnedStackExists();
-
- // Move the PiP to a new position on screen
- final Rect initialBounds = new Rect();
- final Rect offsetBounds = new Rect();
- offsetPipWithinMovementBounds(100 /* offsetY */, initialBounds, offsetBounds);
-
- // Expand the PiP back to fullscreen and back into PiP and ensure that it is in the same
- // position as before we expanded (and that the default bounds reflect that)
- mBroadcastActionTrigger.doAction(ACTION_EXPAND_PIP);
- waitForExitPipToFullscreen(PIP_ACTIVITY);
- mBroadcastActionTrigger.doAction(ACTION_ENTER_PIP);
- waitForEnterPipAnimationComplete(PIP_ACTIVITY);
- mAmWmState.computeState(true);
- // Due to rounding in how we save and apply the snap fraction we may be a pixel off, so just
- // account for that in this check
- offsetBounds.inset(-1, -1);
- assertTrue("Expected offsetBounds=" + offsetBounds + " to contain bounds="
- + getPinnedStackBounds(), offsetBounds.contains(getPinnedStackBounds()));
-
- // Expand the PiP, then launch an activity in a new task, and ensure that the PiP goes back
- // to the default position (and not the saved position) the next time it is launched
- mBroadcastActionTrigger.doAction(ACTION_EXPAND_PIP);
- waitForExitPipToFullscreen(PIP_ACTIVITY);
- launchActivity(TEST_ACTIVITY);
- mBroadcastActionTrigger.doAction(TEST_ACTIVITY_ACTION_FINISH_SELF);
- mAmWmState.waitForActivityState(PIP_ACTIVITY, STATE_RESUMED);
- mAmWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
- mBroadcastActionTrigger.doAction(ACTION_ENTER_PIP);
- waitForEnterPipAnimationComplete(PIP_ACTIVITY);
- assertPinnedStackExists();
- assertTrue("Expected initialBounds=" + initialBounds + " to equal bounds="
- + getPinnedStackBounds(), initialBounds.equals(getPinnedStackBounds()));
- }
-
- @Test
- public void testEnterPictureInPictureDiscardSavedPositionOnFinish() throws Exception {
- // Ensure we have static shelf offset by running this test over a non-home activity
- launchActivity(NO_RELAUNCH_ACTIVITY);
- mAmWmState.waitForActivityState(mAmWmState.getAmState().getHomeActivityName(),
- STATE_STOPPED);
-
- // Launch PiP activity with auto-enter PiP, save the default position of the PiP
- // (while the PiP is still animating sleep)
- launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
- // Wait for animation complete since we are comparing bounds
- waitForEnterPipAnimationComplete(PIP_ACTIVITY);
- assertPinnedStackExists();
-
- // Move the PiP to a new position on screen
- final Rect initialBounds = new Rect();
- final Rect offsetBounds = new Rect();
- offsetPipWithinMovementBounds(100 /* offsetY */, initialBounds, offsetBounds);
-
- // Finish the activity
- mBroadcastActionTrigger.doAction(ACTION_FINISH);
- waitForPinnedStackRemoved();
- assertPinnedStackDoesNotExist();
-
- // Ensure that starting the same PiP activity after it finished will go to the default
- // bounds
- launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
- waitForEnterPipAnimationComplete(PIP_ACTIVITY);
- assertPinnedStackExists();
- assertTrue("Expected initialBounds=" + initialBounds + " to equal bounds="
- + getPinnedStackBounds(), initialBounds.equals(getPinnedStackBounds()));
- }
-
- /**
- * Offsets the PiP in a direction by {@param offsetY} such that it is still within the movement
- * bounds.
- */
- private void offsetPipWithinMovementBounds(int offsetY, Rect initialBoundsOut,
- Rect offsetBoundsOut) {
- final ActivityStack stack = getPinnedStack();
- final Rect displayRect = mAmWmState.getWmState().getDisplay(stack.mDisplayId)
- .getDisplayRect();
- initialBoundsOut.set(getPinnedStackBounds());
- offsetBoundsOut.set(initialBoundsOut);
- if (initialBoundsOut.top < displayRect.centerY()) {
- // If the default gravity is top-aligned, offset down instead of up
- offsetBoundsOut.offset(0, offsetY);
- } else {
- offsetBoundsOut.offset(0, -offsetY);
- }
- resizePinnedStack(stack.mStackId, offsetBoundsOut.left, offsetBoundsOut.top,
- offsetBoundsOut.right, offsetBoundsOut.bottom);
- }
-
/** Get app bounds in last applied configuration. */
private Rect getAppBounds(ComponentName activityName) {
final Configuration config = TestJournalContainer.get(activityName).extras
@@ -1374,29 +1218,6 @@
}
/**
- * Asserts that the pinned stack bounds does not intersect with the IME bounds.
- */
- private void assertPinnedStackDoesNotIntersectIME() {
- // Ensure that the IME is visible
- WindowManagerState wmState = mAmWmState.getWmState();
- wmState.computeState();
- WindowManagerState.WindowState imeWinState = wmState.getInputMethodWindowState();
- assertTrue(imeWinState != null);
-
- // Ensure that the PIP movement is constrained by the display bounds intersecting the
- // non-IME bounds
- Rect imeContentFrame = imeWinState.getContentFrame();
- Rect imeContentInsets = imeWinState.getGivenContentInsets();
- Rect imeBounds = new Rect(imeContentFrame.left + imeContentInsets.left,
- imeContentFrame.top + imeContentInsets.top,
- imeContentFrame.right - imeContentInsets.width(),
- imeContentFrame.bottom - imeContentInsets.height());
- wmState.computeState();
- Rect pipMovementBounds = wmState.getPinnedStackMovementBounds();
- assertTrue(!Rect.intersects(pipMovementBounds, imeBounds));
- }
-
- /**
* Asserts that the pinned stack bounds is contained in the display bounds.
*/
private void assertPinnedStackActivityIsInDisplayBounds(ComponentName activityName) {
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
index 87b4902..bc9005f 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
@@ -49,7 +49,6 @@
import com.android.server.wm.nano.DisplayContentProto;
import com.android.server.wm.nano.DisplayFramesProto;
import com.android.server.wm.nano.IdentifierProto;
-import com.android.server.wm.nano.PinnedStackControllerProto;
import com.android.server.wm.nano.StackProto;
import com.android.server.wm.nano.TaskProto;
import com.android.server.wm.nano.WindowContainerProto;
@@ -122,8 +121,6 @@
private String mFocusedApp = null;
private int mFocusedDisplayId = DEFAULT_DISPLAY;
private String mInputMethodWindowAppToken = null;
- private Rect mDefaultPinnedStackBounds = new Rect();
- private Rect mPinnedStackMovementBounds = new Rect();
private final LinkedList<String> mSysDump = new LinkedList();
private int mRotation;
private int mLastOrientation;
@@ -218,11 +215,6 @@
mIsDockedStackMinimized =
displayProto.dockedStackDividerController.minimizedDock;
}
- PinnedStackControllerProto pinnedStackProto = displayProto.pinnedStackController;
- if (pinnedStackProto != null) {
- mDefaultPinnedStackBounds = extract(pinnedStackProto.defaultBounds);
- mPinnedStackMovementBounds = extract(pinnedStackProto.movementBounds);
- }
}
}
for (WindowState w : allWindows) {
@@ -556,14 +548,6 @@
return getDisplay(DEFAULT_DISPLAY).mStableBounds;
}
- Rect getDefaultPinnedStackBounds() {
- return new Rect(mDefaultPinnedStackBounds);
- }
-
- Rect getPinnedStackMovementBounds() {
- return new Rect(mPinnedStackMovementBounds);
- }
-
WindowState findFirstWindowWithType(int type) {
for (WindowState window : mWindowStates) {
if (window.getType() == type) {
@@ -595,8 +579,6 @@
mFocusedApp = null;
mInputMethodWindowAppToken = null;
mIsDockedStackMinimized = false;
- mDefaultPinnedStackBounds.setEmpty();
- mPinnedStackMovementBounds.setEmpty();
mRotation = 0;
mLastOrientation = 0;
mFocusedDisplayId = DEFAULT_DISPLAY;
diff --git a/tests/location/location_fine/src/android/location/cts/fine/GnssClockTest.java b/tests/location/location_fine/src/android/location/cts/fine/GnssClockTest.java
index 7863675..0937da4 100644
--- a/tests/location/location_fine/src/android/location/cts/fine/GnssClockTest.java
+++ b/tests/location/location_fine/src/android/location/cts/fine/GnssClockTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertTrue;
import android.location.GnssClock;
+import android.location.GnssStatus;
import android.os.Parcel;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -81,6 +82,14 @@
clock.resetDriftUncertaintyNanosPerSecond();
assertFalse(clock.hasDriftUncertaintyNanosPerSecond());
+ assertTrue(clock.hasElapsedRealtimeNanos());
+ clock.resetElapsedRealtimeNanos();
+ assertFalse(clock.hasElapsedRealtimeNanos());
+
+ assertTrue(clock.hasElapsedRealtimeUncertaintyNanos());
+ clock.resetElapsedRealtimeUncertaintyNanos();
+ assertFalse(clock.hasElapsedRealtimeUncertaintyNanos());
+
assertTrue(clock.hasFullBiasNanos());
clock.resetFullBiasNanos();
assertFalse(clock.hasFullBiasNanos());
@@ -89,17 +98,21 @@
clock.resetLeapSecond();
assertFalse(clock.hasLeapSecond());
+ assertTrue(clock.hasReferenceConstellationTypeForIsb());
+ clock.resetReferenceConstellationTypeForIsb();
+ assertFalse(clock.hasReferenceConstellationTypeForIsb());
+
+ assertTrue(clock.hasReferenceCarrierFrequencyHzForIsb());
+ clock.resetReferenceCarrierFrequencyHzForIsb();
+ assertFalse(clock.hasReferenceCarrierFrequencyHzForIsb());
+
+ assertTrue(clock.hasReferenceCodeTypeForIsb());
+ clock.resetReferenceCodeTypeForIsb();
+ assertFalse(clock.hasReferenceCodeTypeForIsb());
+
assertTrue(clock.hasTimeUncertaintyNanos());
clock.resetTimeUncertaintyNanos();
assertFalse(clock.hasTimeUncertaintyNanos());
-
- assertTrue(clock.hasElapsedRealtimeNanos());
- clock.resetElapsedRealtimeNanos();
- assertFalse(clock.hasElapsedRealtimeNanos());
-
- assertTrue(clock.hasElapsedRealtimeUncertaintyNanos());
- clock.resetElapsedRealtimeUncertaintyNanos();
- assertFalse(clock.hasElapsedRealtimeUncertaintyNanos());
}
private static void setTestValues(GnssClock clock) {
@@ -107,13 +120,16 @@
clock.setBiasUncertaintyNanos(2.0);
clock.setDriftNanosPerSecond(3.0);
clock.setDriftUncertaintyNanosPerSecond(4.0);
+ clock.setElapsedRealtimeNanos(10987732253L);
+ clock.setElapsedRealtimeUncertaintyNanos(3943523.0);
clock.setFullBiasNanos(5);
clock.setHardwareClockDiscontinuityCount(6);
clock.setLeapSecond(7);
+ clock.setReferenceConstellationTypeForIsb(GnssStatus.CONSTELLATION_GPS);
+ clock.setReferenceCarrierFrequencyHzForIsb(1.59975e+09);
+ clock.setReferenceCodeTypeForIsb("C");
clock.setTimeNanos(8);
clock.setTimeUncertaintyNanos(9.0);
- clock.setElapsedRealtimeNanos(10987732253L);
- clock.setElapsedRealtimeUncertaintyNanos(3943523.0);
}
private static void verifyTestValues(GnssClock clock) {
@@ -121,12 +137,15 @@
assertEquals(2.0, clock.getBiasUncertaintyNanos(), DELTA);
assertEquals(3.0, clock.getDriftNanosPerSecond(), DELTA);
assertEquals(4.0, clock.getDriftUncertaintyNanosPerSecond(), DELTA);
+ assertEquals(10987732253L, clock.getElapsedRealtimeNanos());
+ assertEquals(3943523.0, clock.getElapsedRealtimeUncertaintyNanos(), DELTA);
assertEquals(5, clock.getFullBiasNanos());
assertEquals(6, clock.getHardwareClockDiscontinuityCount());
assertEquals(7, clock.getLeapSecond());
+ assertEquals(GnssStatus.CONSTELLATION_GPS, clock.getReferenceConstellationTypeForIsb());
+ assertEquals(1.59975e+09, clock.getReferenceCarrierFrequencyHzForIsb(), DELTA);
+ assertEquals("C", clock.getReferenceCodeTypeForIsb());
assertEquals(8, clock.getTimeNanos());
assertEquals(9.0, clock.getTimeUncertaintyNanos(), DELTA);
- assertEquals(10987732253L, clock.getElapsedRealtimeNanos());
- assertEquals(3943523.0, clock.getElapsedRealtimeUncertaintyNanos(), DELTA);
}
}
diff --git a/tests/location/location_fine/src/android/location/cts/fine/GnssMeasurementTest.java b/tests/location/location_fine/src/android/location/cts/fine/GnssMeasurementTest.java
index 5c318a3..dccf022 100644
--- a/tests/location/location_fine/src/android/location/cts/fine/GnssMeasurementTest.java
+++ b/tests/location/location_fine/src/android/location/cts/fine/GnssMeasurementTest.java
@@ -99,6 +99,22 @@
assertTrue(measurement.hasBasebandCn0DbHz());
measurement.resetBasebandCn0DbHz();
assertFalse(measurement.hasBasebandCn0DbHz());
+
+ assertTrue(measurement.hasReceiverInterSignalBiasNanos());
+ measurement.resetReceiverInterSignalBiasNanos();
+ assertFalse(measurement.hasReceiverInterSignalBiasNanos());
+
+ assertTrue(measurement.hasReceiverInterSignalBiasUncertaintyNanos());
+ measurement.resetReceiverInterSignalBiasUncertaintyNanos();
+ assertFalse(measurement.hasReceiverInterSignalBiasUncertaintyNanos());
+
+ assertTrue(measurement.hasSatelliteInterSignalBiasNanos());
+ measurement.resetSatelliteInterSignalBiasNanos();
+ assertFalse(measurement.hasSatelliteInterSignalBiasNanos());
+
+ assertTrue(measurement.hasSatelliteInterSignalBiasUncertaintyNanos());
+ measurement.resetSatelliteInterSignalBiasUncertaintyNanos();
+ assertFalse(measurement.hasSatelliteInterSignalBiasUncertaintyNanos());
}
private static void setTestValues(GnssMeasurement measurement) {
@@ -118,6 +134,10 @@
measurement.setPseudorangeRateUncertaintyMetersPerSecond(10.0);
measurement.setReceivedSvTimeNanos(11);
measurement.setReceivedSvTimeUncertaintyNanos(12);
+ measurement.setReceiverInterSignalBiasNanos(1.3);
+ measurement.setReceiverInterSignalBiasUncertaintyNanos(2.5);
+ measurement.setSatelliteInterSignalBiasNanos(5.4);
+ measurement.setSatelliteInterSignalBiasUncertaintyNanos(10.0);
measurement.setSnrInDb(13.0);
measurement.setState(14);
measurement.setSvid(15);
@@ -142,6 +162,10 @@
assertEquals(10.0, measurement.getPseudorangeRateUncertaintyMetersPerSecond(), DELTA);
assertEquals(11, measurement.getReceivedSvTimeNanos());
assertEquals(12, measurement.getReceivedSvTimeUncertaintyNanos());
+ assertEquals(1.3, measurement.getReceiverInterSignalBiasNanos(), DELTA);
+ assertEquals(2.5, measurement.getReceiverInterSignalBiasUncertaintyNanos(), DELTA);
+ assertEquals(5.4, measurement.getSatelliteInterSignalBiasNanos(), DELTA);
+ assertEquals(10.0, measurement.getSatelliteInterSignalBiasUncertaintyNanos(), DELTA);
assertEquals(13.0, measurement.getSnrInDb(), DELTA);
assertEquals(14, measurement.getState());
assertEquals(15, measurement.getSvid());
diff --git a/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java b/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java
index 56f5ff1..58aa065 100644
--- a/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java
+++ b/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java
@@ -614,6 +614,7 @@
}
@Test
+ @AppModeFull(reason = "Instant apps can only receive whitelisted broadcasts")
public void testListenProviderEnable_Broadcast() throws Exception {
try (BroadcastCapture capture = new BroadcastCapture(mContext, PROVIDERS_CHANGED_ACTION)) {
mManager.setTestProviderEnabled(TEST_PROVIDER, false);
diff --git a/tests/media/Android.bp b/tests/media/Android.bp
index f01eef1..889b123 100644
--- a/tests/media/Android.bp
+++ b/tests/media/Android.bp
@@ -24,6 +24,9 @@
"android.test.runner.stubs",
"android.test.base.stubs",
],
+ jni_libs: [
+ "libctsmediav2muxer_jni",
+ ],
srcs: ["src/**/*.java"],
// Tag this module as a cts test artifact
test_suites: [
diff --git a/tests/media/OWNERS b/tests/media/OWNERS
index 7367427..8ffa99e 100644
--- a/tests/media/OWNERS
+++ b/tests/media/OWNERS
@@ -1,2 +1,12 @@
-# Bug component:
-include /tests/tests/media/OWNERS
+# Bug component: 1344
+andrewlewis@google.com
+chz@google.com
+dwkang@google.com
+elaurent@google.com
+essick@google.com
+gkasten@google.com
+hunga@google.com
+jmtrivi@google.com
+lajos@google.com
+marcone@google.com
+wjia@google.com
diff --git a/tests/media/jni/Android.bp b/tests/media/jni/Android.bp
new file mode 100644
index 0000000..e151d37
--- /dev/null
+++ b/tests/media/jni/Android.bp
@@ -0,0 +1,31 @@
+// Copyright (C) 2019 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.
+
+cc_test_library {
+ name: "libctsmediav2muxer_jni",
+ srcs: [
+ "NativeMediaConstants.cpp",
+ "NativeMuxerTest.cpp",
+ ],
+ shared_libs: [
+ "libmediandk",
+ "liblog",
+ ],
+ stl: "libc++_static",
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+ gtest: false,
+}
diff --git a/tests/media/jni/NativeMediaConstants.cpp b/tests/media/jni/NativeMediaConstants.cpp
new file mode 100644
index 0000000..a3867f9
--- /dev/null
+++ b/tests/media/jni/NativeMediaConstants.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "NativeMediaConstants.h"
+
+/* Note: constants used by the native media tests but not available in media ndk api */
+const char* AMEDIA_MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
+const char* AMEDIA_MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
+const char* AMEDIA_MIMETYPE_VIDEO_AVC = "video/avc";
+const char* AMEDIA_MIMETYPE_VIDEO_HEVC = "video/hevc";
+const char* AMEDIA_MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es";
+const char* AMEDIA_MIMETYPE_VIDEO_H263 = "video/3gpp";
+
+const char* AMEDIA_MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
+const char* AMEDIA_MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
+const char* AMEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
+const char* AMEDIA_MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
+const char* AMEDIA_MIMETYPE_AUDIO_OPUS = "audio/opus";
+
diff --git a/tests/media/jni/NativeMediaConstants.h b/tests/media/jni/NativeMediaConstants.h
new file mode 100644
index 0000000..5a94368
--- /dev/null
+++ b/tests/media/jni/NativeMediaConstants.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef MEDIACTSNATIVE_NATIVE_MEDIA_CONSTANTS_H
+#define MEDIACTSNATIVE_NATIVE_MEDIA_CONSTANTS_H
+
+extern const char* AMEDIA_MIMETYPE_VIDEO_VP8;
+extern const char* AMEDIA_MIMETYPE_VIDEO_VP9;
+extern const char* AMEDIA_MIMETYPE_VIDEO_AVC;
+extern const char* AMEDIA_MIMETYPE_VIDEO_HEVC;
+extern const char* AMEDIA_MIMETYPE_VIDEO_MPEG4;
+extern const char* AMEDIA_MIMETYPE_VIDEO_H263;
+
+extern const char* AMEDIA_MIMETYPE_AUDIO_AMR_NB;
+extern const char* AMEDIA_MIMETYPE_AUDIO_AMR_WB;
+extern const char* AMEDIA_MIMETYPE_AUDIO_AAC;
+extern const char* AMEDIA_MIMETYPE_AUDIO_VORBIS;
+extern const char* AMEDIA_MIMETYPE_AUDIO_OPUS;
+
+// TODO(b/146420990)
+typedef enum {
+ OUTPUT_FORMAT_START = 0,
+ OUTPUT_FORMAT_MPEG_4 = OUTPUT_FORMAT_START,
+ OUTPUT_FORMAT_WEBM = OUTPUT_FORMAT_START + 1,
+ OUTPUT_FORMAT_THREE_GPP = OUTPUT_FORMAT_START + 2,
+ OUTPUT_FORMAT_HEIF = OUTPUT_FORMAT_START + 3,
+ OUTPUT_FORMAT_OGG = OUTPUT_FORMAT_START + 4,
+ OUTPUT_FORMAT_LIST_END = OUTPUT_FORMAT_START + 4,
+} MuxerFormat;
+
+#endif // MEDIACTSNATIVE_NATIVE_MEDIA_CONSTANTS_H
diff --git a/tests/media/jni/NativeMuxerTest.cpp b/tests/media/jni/NativeMuxerTest.cpp
new file mode 100644
index 0000000..f9adbd7
--- /dev/null
+++ b/tests/media/jni/NativeMuxerTest.cpp
@@ -0,0 +1,742 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NativeMuxerTest"
+#include <log/log.h>
+
+#include <fcntl.h>
+#include <jni.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <cmath>
+#include <cstring>
+#include <fstream>
+#include <map>
+#include <vector>
+
+#include "NativeMediaConstants.h"
+#include "media/NdkMediaExtractor.h"
+#include "media/NdkMediaFormat.h"
+#include "media/NdkMediaMuxer.h"
+
+/**
+ * MuxerNativeTestHelper breaks a media file to elements that a muxer can use to rebuild its clone.
+ * While testing muxer, if the test doesn't use MediaCodecs class to generate elementary stream,
+ * but uses MediaExtractor, this class will be handy
+ */
+class MuxerNativeTestHelper {
+ public:
+ explicit MuxerNativeTestHelper(const char* srcPath, const char* mime = nullptr,
+ int frameLimit = -1)
+ : mSrcPath(srcPath), mMime(mime), mTrackCount(0), mBuffer(nullptr) {
+ mFrameLimit = frameLimit < 0 ? INT_MAX : frameLimit;
+ splitMediaToMuxerParameters();
+ }
+
+ ~MuxerNativeTestHelper() {
+ for (auto format : mFormat) AMediaFormat_delete(format);
+ delete[] mBuffer;
+ for (const auto& buffInfoTrack : mBufferInfo) {
+ for (auto info : buffInfoTrack) delete info;
+ }
+ }
+
+ int getTrackCount() { return mTrackCount; }
+
+ bool registerTrack(AMediaMuxer* muxer);
+
+ bool insertSampleData(AMediaMuxer* muxer);
+
+ bool muxMedia(AMediaMuxer* muxer);
+
+ bool combineMedias(AMediaMuxer* muxer, MuxerNativeTestHelper* that, const int* repeater);
+
+ bool equals(MuxerNativeTestHelper* that);
+
+ void offsetTimeStamp(int trackID, long tsOffset, int sampleOffset);
+
+ private:
+ void splitMediaToMuxerParameters();
+
+ static const int STTS_TOLERANCE = 100;
+ const char* mSrcPath;
+ const char* mMime;
+ int mTrackCount;
+ std::vector<AMediaFormat*> mFormat;
+ uint8_t* mBuffer;
+ std::vector<std::vector<AMediaCodecBufferInfo*>> mBufferInfo;
+ std::map<int, int> mInpIndexMap;
+ std::vector<int> mTrackIdxOrder;
+ int mFrameLimit;
+ // combineMedias() uses local version of this variable
+ std::map<int, int> mOutIndexMap;
+};
+
+void MuxerNativeTestHelper::splitMediaToMuxerParameters() {
+ FILE* ifp = fopen(mSrcPath, "rbe");
+ int fd;
+ int fileSize;
+ if (ifp) {
+ struct stat buf {};
+ stat(mSrcPath, &buf);
+ fileSize = buf.st_size;
+ fd = fileno(ifp);
+ } else {
+ return;
+ }
+ AMediaExtractor* extractor = AMediaExtractor_new();
+ if (extractor == nullptr) {
+ fclose(ifp);
+ return;
+ }
+ // Set up MediaExtractor to read from the source.
+ media_status_t status = AMediaExtractor_setDataSourceFd(extractor, fd, 0, fileSize);
+ if (AMEDIA_OK != status) {
+ AMediaExtractor_delete(extractor);
+ fclose(ifp);
+ return;
+ }
+
+ // Set up MediaFormat
+ int index = 0;
+ for (size_t trackID = 0; trackID < AMediaExtractor_getTrackCount(extractor); trackID++) {
+ AMediaExtractor_selectTrack(extractor, trackID);
+ AMediaFormat* format = AMediaExtractor_getTrackFormat(extractor, trackID);
+ if (mMime == nullptr) {
+ mTrackCount++;
+ mFormat.push_back(format);
+ mInpIndexMap[trackID] = index++;
+ } else {
+ const char* mime;
+ bool hasKey = AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
+ if (hasKey && !strcmp(mime, mMime)) {
+ mTrackCount++;
+ mFormat.push_back(format);
+ mInpIndexMap[trackID] = index;
+ break;
+ } else {
+ AMediaFormat_delete(format);
+ AMediaExtractor_unselectTrack(extractor, trackID);
+ }
+ }
+ }
+
+ if (mTrackCount <= 0) {
+ AMediaExtractor_delete(extractor);
+ fclose(ifp);
+ return;
+ }
+
+ // Set up location for elementary stream
+ int bufferSize = ((fileSize + 127) >> 7) << 7;
+ // Ideally, Sum of return values of extractor.readSampleData(...) should not exceed
+ // source file size. But in case of Vorbis, aosp extractor appends an additional 4 bytes to
+ // the data at every readSampleData() call. bufferSize <<= 1 empirically large enough to
+ // hold the excess 4 bytes per read call
+ bufferSize <<= 1;
+ mBuffer = new uint8_t[bufferSize];
+ if (mBuffer == nullptr) {
+ mTrackCount = 0;
+ AMediaExtractor_delete(extractor);
+ fclose(ifp);
+ return;
+ }
+
+ // Let MediaExtractor do its thing
+ bool sawEOS = false;
+ int frameCount = 0;
+ int offset = 0;
+ mBufferInfo.resize(mTrackCount);
+ while (!sawEOS && frameCount < mFrameLimit) {
+ auto* bufferInfo = new AMediaCodecBufferInfo();
+ bufferInfo->offset = offset;
+ bufferInfo->size =
+ AMediaExtractor_readSampleData(extractor, mBuffer + offset, (bufferSize - offset));
+ if (bufferInfo->size < 0) {
+ sawEOS = true;
+ } else {
+ bufferInfo->presentationTimeUs = AMediaExtractor_getSampleTime(extractor);
+ bufferInfo->flags = AMediaExtractor_getSampleFlags(extractor);
+ int trackID = AMediaExtractor_getSampleTrackIndex(extractor);
+ mTrackIdxOrder.push_back(trackID);
+ mBufferInfo[(mInpIndexMap.at(trackID))].push_back(bufferInfo);
+ AMediaExtractor_advance(extractor);
+ frameCount++;
+ }
+ offset += bufferInfo->size;
+ }
+
+ AMediaExtractor_delete(extractor);
+ fclose(ifp);
+}
+
+bool MuxerNativeTestHelper::registerTrack(AMediaMuxer* muxer) {
+ for (int trackID = 0; trackID < mTrackCount; trackID++) {
+ int dstIndex = AMediaMuxer_addTrack(muxer, mFormat[trackID]);
+ if (dstIndex < 0) return false;
+ mOutIndexMap[trackID] = dstIndex;
+ }
+ return true;
+}
+
+bool MuxerNativeTestHelper::insertSampleData(AMediaMuxer* muxer) {
+ // write all registered tracks in interleaved order
+ int* frameCount = new int[mTrackCount]{0};
+ for (int trackID : mTrackIdxOrder) {
+ int index = mInpIndexMap.at(trackID);
+ AMediaCodecBufferInfo* info = mBufferInfo[index][frameCount[index]];
+ if (AMediaMuxer_writeSampleData(muxer, mOutIndexMap.at(index), mBuffer, info) !=
+ AMEDIA_OK) {
+ delete[] frameCount;
+ return false;
+ }
+ ALOGV("Track: %d Timestamp: %d", trackID, (int)info->presentationTimeUs);
+ frameCount[index]++;
+ }
+ delete[] frameCount;
+ ALOGV("Total track samples %d", (int)mTrackIdxOrder.size());
+ return true;
+}
+
+bool MuxerNativeTestHelper::muxMedia(AMediaMuxer* muxer) {
+ return (registerTrack(muxer) && (AMediaMuxer_start(muxer) == AMEDIA_OK) &&
+ insertSampleData(muxer) && (AMediaMuxer_stop(muxer) == AMEDIA_OK));
+}
+
+bool MuxerNativeTestHelper::combineMedias(AMediaMuxer* muxer, MuxerNativeTestHelper* that,
+ const int* repeater) {
+ if (that == nullptr) return false;
+ if (repeater == nullptr) return false;
+
+ // register tracks
+ int totalTracksToAdd = repeater[0] * this->mTrackCount + repeater[1] * that->mTrackCount;
+ int outIndexMap[totalTracksToAdd];
+ MuxerNativeTestHelper* group[2]{this, that};
+ for (int k = 0, idx = 0; k < 2; k++) {
+ for (int j = 0; j < repeater[k]; j++) {
+ for (AMediaFormat* format : group[k]->mFormat) {
+ int dstIndex = AMediaMuxer_addTrack(muxer, format);
+ if (dstIndex < 0) return false;
+ outIndexMap[idx++] = dstIndex;
+ }
+ }
+ }
+ // start
+ if (AMediaMuxer_start(muxer) != AMEDIA_OK) return false;
+ // write sample data
+ // write all registered tracks in planar order viz all samples of a track A then all
+ // samples of track B, ...
+ for (int k = 0, idx = 0; k < 2; k++) {
+ for (int j = 0; j < repeater[k]; j++) {
+ for (int i = 0; i < group[k]->mTrackCount; i++) {
+ for (int p = 0; p < group[k]->mBufferInfo[i].size(); p++) {
+ AMediaCodecBufferInfo* info = group[k]->mBufferInfo[i][p];
+ if (AMediaMuxer_writeSampleData(muxer, outIndexMap[idx], group[k]->mBuffer,
+ info) != AMEDIA_OK) {
+ return false;
+ }
+ ALOGV("Track: %d Timestamp: %d", outIndexMap[idx],
+ (int)info->presentationTimeUs);
+ }
+ idx++;
+ }
+ }
+ }
+ // stop
+ return (AMediaMuxer_stop(muxer) == AMEDIA_OK);
+}
+
+// returns true if 'this' stream is a subset of 'o'. That is all tracks in current media
+// stream are present in ref media stream
+bool MuxerNativeTestHelper::equals(MuxerNativeTestHelper* that) {
+ if (this == that) return true;
+ if (that == nullptr) return false;
+
+ for (int i = 0; i < mTrackCount; i++) {
+ AMediaFormat* thisFormat = mFormat[i];
+ const char* thisMime = nullptr;
+ AMediaFormat_getString(thisFormat, AMEDIAFORMAT_KEY_MIME, &thisMime);
+ int j = 0;
+ for (; j < that->mTrackCount; j++) {
+ AMediaFormat* thatFormat = that->mFormat[j];
+ const char* thatMime = nullptr;
+ AMediaFormat_getString(thatFormat, AMEDIAFORMAT_KEY_MIME, &thatMime);
+ if (thisMime != nullptr && thatMime != nullptr && !strcmp(thisMime, thatMime)) {
+ if (mBufferInfo[i].size() == that->mBufferInfo[j].size()) {
+ int flagsDiff = 0, sizeDiff = 0, tsDiff = 0;
+ for (int k = 0; k < mBufferInfo[i].size(); k++) {
+ AMediaCodecBufferInfo* thisInfo = mBufferInfo[i][k];
+ AMediaCodecBufferInfo* thatInfo = that->mBufferInfo[j][k];
+ if (thisInfo->flags != thatInfo->flags) {
+ flagsDiff++;
+ }
+ if (thisInfo->size != thatInfo->size) {
+ sizeDiff++;
+ }
+ if (abs(thisInfo->presentationTimeUs - thatInfo->presentationTimeUs) >
+ STTS_TOLERANCE) {
+ tsDiff++;
+ }
+ }
+ if (flagsDiff == 0 && sizeDiff == 0 && tsDiff == 0)
+ break;
+ else {
+ ALOGV("For mime %s, Total Samples %d, flagsDiff %d, sizeDiff %d, tsDiff %d",
+ thisMime, (int)mBufferInfo[i].size(), flagsDiff, sizeDiff, tsDiff);
+ }
+ }
+ }
+ }
+ if (j == that->mTrackCount) {
+ ALOGV("For mime %s, Couldn't find a match", thisMime);
+ return false;
+ }
+ }
+ return true;
+}
+
+void MuxerNativeTestHelper::offsetTimeStamp(int trackID, long tsOffset, int sampleOffset) {
+ // offset pts of samples from index sampleOffset till the end by tsOffset
+ if (trackID < mTrackCount) {
+ for (int i = sampleOffset; i < mBufferInfo[trackID].size(); i++) {
+ AMediaCodecBufferInfo* info = mBufferInfo[trackID][i];
+ info->presentationTimeUs += tsOffset;
+ }
+ }
+}
+
+static bool isCodecContainerPairValid(MuxerFormat format, const char* mime) {
+ static const std::map<MuxerFormat, std::vector<const char*>> codecListforType = {
+ {OUTPUT_FORMAT_MPEG_4,
+ {AMEDIA_MIMETYPE_VIDEO_MPEG4, AMEDIA_MIMETYPE_VIDEO_H263, AMEDIA_MIMETYPE_VIDEO_AVC,
+ AMEDIA_MIMETYPE_VIDEO_HEVC, AMEDIA_MIMETYPE_AUDIO_AAC}},
+ {OUTPUT_FORMAT_WEBM,
+ {AMEDIA_MIMETYPE_VIDEO_VP8, AMEDIA_MIMETYPE_VIDEO_VP9, AMEDIA_MIMETYPE_AUDIO_VORBIS,
+ AMEDIA_MIMETYPE_AUDIO_OPUS}},
+ {OUTPUT_FORMAT_THREE_GPP,
+ {AMEDIA_MIMETYPE_VIDEO_MPEG4, AMEDIA_MIMETYPE_VIDEO_H263, AMEDIA_MIMETYPE_VIDEO_AVC,
+ AMEDIA_MIMETYPE_AUDIO_AAC, AMEDIA_MIMETYPE_AUDIO_AMR_NB,
+ AMEDIA_MIMETYPE_AUDIO_AMR_WB}},
+ {OUTPUT_FORMAT_OGG, {AMEDIA_MIMETYPE_AUDIO_OPUS}},
+ };
+
+ if (format == OUTPUT_FORMAT_MPEG_4 &&
+ strncmp(mime, "application/", strlen("application/")) == 0)
+ return true;
+
+ auto it = codecListforType.find(format);
+ if (it != codecListforType.end())
+ for (auto it2 : it->second)
+ if (strcmp(it2, mime) == 0) return true;
+
+ return false;
+}
+
+static jboolean nativeTestSetLocation(JNIEnv* env, jobject, jint jformat, jstring jsrcPath,
+ jstring jdstPath) {
+ bool isPass = true;
+ bool isGeoDataSupported;
+ const float atlanticLat = 14.59f;
+ const float atlanticLong = 28.67f;
+ const float tooFarNorth = 90.5f;
+ const float tooFarWest = -180.5f;
+ const float tooFarSouth = -90.5f;
+ const float tooFarEast = 180.5f;
+ const float annapurnaLat = 28.59f;
+ const float annapurnaLong = 83.82f;
+ const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
+ FILE* ofp = fopen(cdstPath, "wbe+");
+ if (ofp) {
+ AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)jformat);
+ media_status_t status = AMediaMuxer_setLocation(muxer, tooFarNorth, atlanticLong);
+ if (status == AMEDIA_OK) {
+ isPass = false;
+ ALOGE("setLocation succeeds on bad args: (%f, %f)", tooFarNorth, atlanticLong);
+ }
+ status = AMediaMuxer_setLocation(muxer, tooFarSouth, atlanticLong);
+ if (status == AMEDIA_OK) {
+ isPass = false;
+ ALOGE("setLocation succeeds on bad args: (%f, %f)", tooFarSouth, atlanticLong);
+ }
+ status = AMediaMuxer_setLocation(muxer, atlanticLat, tooFarWest);
+ if (status == AMEDIA_OK) {
+ isPass = false;
+ ALOGE("setLocation succeeds on bad args: (%f, %f)", atlanticLat, tooFarWest);
+ }
+ status = AMediaMuxer_setLocation(muxer, atlanticLat, tooFarEast);
+ if (status == AMEDIA_OK) {
+ isPass = false;
+ ALOGE("setLocation succeeds on bad args: (%f, %f)", atlanticLat, tooFarEast);
+ }
+ status = AMediaMuxer_setLocation(muxer, tooFarNorth, tooFarWest);
+ if (status == AMEDIA_OK) {
+ isPass = false;
+ ALOGE("setLocation succeeds on bad args: (%f, %f)", tooFarNorth, tooFarWest);
+ }
+ status = AMediaMuxer_setLocation(muxer, atlanticLat, atlanticLong);
+ isGeoDataSupported = (status == AMEDIA_OK);
+ if (isGeoDataSupported) {
+ status = AMediaMuxer_setLocation(muxer, annapurnaLat, annapurnaLong);
+ if (status != AMEDIA_OK) {
+ isPass = false;
+ ALOGE("setLocation fails on args: (%f, %f)", annapurnaLat, annapurnaLong);
+ }
+ } else {
+ isPass &= ((MuxerFormat)jformat != OUTPUT_FORMAT_MPEG_4 &&
+ (MuxerFormat)jformat != OUTPUT_FORMAT_THREE_GPP);
+ }
+ const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr);
+ auto* mediaInfo = new MuxerNativeTestHelper(csrcPath);
+ if (mediaInfo->registerTrack(muxer) && AMediaMuxer_start(muxer) == AMEDIA_OK) {
+ status = AMediaMuxer_setLocation(muxer, atlanticLat, atlanticLong);
+ if (status == AMEDIA_OK) {
+ isPass = false;
+ ALOGE("setLocation succeeds after starting the muxer");
+ }
+ if (mediaInfo->insertSampleData(muxer) && AMediaMuxer_stop(muxer) == AMEDIA_OK) {
+ status = AMediaMuxer_setLocation(muxer, atlanticLat, atlanticLong);
+ if (status == AMEDIA_OK) {
+ isPass = false;
+ ALOGE("setLocation succeeds after stopping the muxer");
+ }
+ } else {
+ isPass = false;
+ ALOGE("failed to writeSampleData or stop muxer");
+ }
+ } else {
+ isPass = false;
+ ALOGE("failed to addTrack or start muxer");
+ }
+ delete mediaInfo;
+ env->ReleaseStringUTFChars(jsrcPath, csrcPath);
+ AMediaMuxer_delete(muxer);
+ fclose(ofp);
+ } else {
+ isPass = false;
+ ALOGE("failed to open output file %s", cdstPath);
+ }
+ env->ReleaseStringUTFChars(jdstPath, cdstPath);
+ return static_cast<jboolean>(isPass);
+}
+
+static jboolean nativeTestSetOrientationHint(JNIEnv* env, jobject, jint jformat, jstring jsrcPath,
+ jstring jdstPath) {
+ bool isPass = true;
+ bool isOrientationSupported;
+ const int badRotation[] = {360, 45, -90};
+ const int oldRotation = 90;
+ const int currRotation = 180;
+ const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
+ FILE* ofp = fopen(cdstPath, "wbe+");
+ if (ofp) {
+ AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)jformat);
+ media_status_t status;
+ for (int degrees : badRotation) {
+ status = AMediaMuxer_setOrientationHint(muxer, degrees);
+ if (status == AMEDIA_OK) {
+ isPass = false;
+ ALOGE("setOrientationHint succeeds on bad args: %d", degrees);
+ }
+ }
+ status = AMediaMuxer_setOrientationHint(muxer, oldRotation);
+ isOrientationSupported = (status == AMEDIA_OK);
+ if (isOrientationSupported) {
+ status = AMediaMuxer_setOrientationHint(muxer, currRotation);
+ if (status != AMEDIA_OK) {
+ isPass = false;
+ ALOGE("setOrientationHint fails on args: %d", currRotation);
+ }
+ } else {
+ isPass &= ((MuxerFormat)jformat != OUTPUT_FORMAT_MPEG_4 &&
+ (MuxerFormat)jformat != OUTPUT_FORMAT_THREE_GPP);
+ }
+ const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr);
+ auto* mediaInfo = new MuxerNativeTestHelper(csrcPath);
+ if (mediaInfo->registerTrack(muxer) && AMediaMuxer_start(muxer) == AMEDIA_OK) {
+ status = AMediaMuxer_setOrientationHint(muxer, currRotation);
+ if (status == AMEDIA_OK) {
+ isPass = false;
+ ALOGE("setOrientationHint succeeds after starting the muxer");
+ }
+ if (mediaInfo->insertSampleData(muxer) && AMediaMuxer_stop(muxer) == AMEDIA_OK) {
+ status = AMediaMuxer_setOrientationHint(muxer, currRotation);
+ if (status == AMEDIA_OK) {
+ isPass = false;
+ ALOGE("setOrientationHint succeeds after stopping the muxer");
+ }
+ } else {
+ isPass = false;
+ ALOGE("failed to writeSampleData or stop muxer");
+ }
+ } else {
+ isPass = false;
+ ALOGE("failed to addTrack or start muxer");
+ }
+ delete mediaInfo;
+ env->ReleaseStringUTFChars(jsrcPath, csrcPath);
+ AMediaMuxer_delete(muxer);
+ fclose(ofp);
+ } else {
+ isPass = false;
+ ALOGE("failed to open output file %s", cdstPath);
+ }
+ env->ReleaseStringUTFChars(jdstPath, cdstPath);
+ return static_cast<jboolean>(isPass);
+}
+
+static jboolean nativeTestMultiTrack(JNIEnv* env, jobject, jint jformat, jstring jsrcPathA,
+ jstring jsrcPathB, jstring jrefPath, jstring jdstPath) {
+ bool isPass = true;
+ const char* csrcPathA = env->GetStringUTFChars(jsrcPathA, nullptr);
+ const char* csrcPathB = env->GetStringUTFChars(jsrcPathB, nullptr);
+ auto* mediaInfoA = new MuxerNativeTestHelper(csrcPathA);
+ auto* mediaInfoB = new MuxerNativeTestHelper(csrcPathB);
+ if (mediaInfoA->getTrackCount() == 1 && mediaInfoB->getTrackCount() == 1) {
+ const char* crefPath = env->GetStringUTFChars(jrefPath, nullptr);
+ // number of times to repeat {mSrcFileA, mSrcFileB} in Output
+ int numTracks[][2]{{1, 1}, {2, 0}, {0, 2}, {1, 2}, {2, 1}};
+ // prepare reference
+ FILE* rfp = fopen(crefPath, "wbe+");
+ if (rfp) {
+ AMediaMuxer* muxer = AMediaMuxer_new(fileno(rfp), (OutputFormat)jformat);
+ bool muxStatus = mediaInfoA->combineMedias(muxer, mediaInfoB, numTracks[0]);
+ AMediaMuxer_delete(muxer);
+ fclose(rfp);
+ if (muxStatus) {
+ auto* refInfo = new MuxerNativeTestHelper(crefPath);
+ if (!mediaInfoA->equals(refInfo) || !mediaInfoB->equals(refInfo)) {
+ isPass = false;
+ ALOGE("testMultiTrack: inputs: %s %s, fmt: %d, error ! muxing src A and src B "
+ "failed", csrcPathA, csrcPathB, jformat);
+ } else {
+ const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
+ for (int i = 1; i < sizeof(numTracks) / sizeof(numTracks[0]) && isPass; i++) {
+ FILE* ofp = fopen(cdstPath, "wbe+");
+ if (ofp) {
+ muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)jformat);
+ bool status =
+ mediaInfoA->combineMedias(muxer, mediaInfoB, numTracks[i]);
+ AMediaMuxer_delete(muxer);
+ fclose(ofp);
+ if (status) {
+ auto* dstInfo = new MuxerNativeTestHelper(cdstPath);
+ if (!dstInfo->equals(refInfo)) {
+ isPass = false;
+ ALOGE("testMultiTrack: inputs: %s %s, fmt: %d, error ! muxing "
+ "src A: %d, src B: %d failed", csrcPathA, csrcPathB,
+ jformat, numTracks[i][0], numTracks[i][1]);
+ }
+ delete dstInfo;
+ } else {
+ if ((MuxerFormat)jformat != OUTPUT_FORMAT_MPEG_4) {
+ isPass = false;
+ ALOGE("testMultiTrack: inputs: %s %s, fmt: %d, error ! muxing "
+ "src A: %d, src B: %d failed", csrcPathA, csrcPathB,
+ jformat, numTracks[i][0], numTracks[i][1]);
+ }
+ }
+ } else {
+ isPass = false;
+ ALOGE("failed to open output file %s", cdstPath);
+ }
+ }
+ env->ReleaseStringUTFChars(jdstPath, cdstPath);
+ }
+ delete refInfo;
+ } else {
+ if ((MuxerFormat)jformat != OUTPUT_FORMAT_OGG) {
+ isPass = false;
+ ALOGE("testMultiTrack: inputs: %s %s, fmt: %d, error ! muxing src A and src B "
+ "failed", csrcPathA, csrcPathB, jformat);
+ }
+ }
+ } else {
+ isPass = false;
+ ALOGE("failed to open reference output file %s", crefPath);
+ }
+ env->ReleaseStringUTFChars(jrefPath, crefPath);
+ } else {
+ isPass = false;
+ if (mediaInfoA->getTrackCount() != 1) {
+ ALOGE("error: file %s, track count exp/rec - %d/%d", csrcPathA, 1,
+ mediaInfoA->getTrackCount());
+ }
+ if (mediaInfoB->getTrackCount() != 1) {
+ ALOGE("error: file %s, track count exp/rec - %d/%d", csrcPathB, 1,
+ mediaInfoB->getTrackCount());
+ }
+ }
+ env->ReleaseStringUTFChars(jsrcPathA, csrcPathA);
+ env->ReleaseStringUTFChars(jsrcPathB, csrcPathB);
+ delete mediaInfoA;
+ delete mediaInfoB;
+ return static_cast<jboolean>(isPass);
+}
+
+static jboolean nativeTestOffsetPts(JNIEnv* env, jobject, jint format, jstring jsrcPath,
+ jstring jdstPath, jintArray joffsetIndices) {
+ bool isPass = true;
+ const int OFFSET_TS = 111000;
+ jsize len = env->GetArrayLength(joffsetIndices);
+ jint* coffsetIndices = env->GetIntArrayElements(joffsetIndices, nullptr);
+ const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr);
+ const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
+ auto* mediaInfo = new MuxerNativeTestHelper(csrcPath);
+ if (mediaInfo->getTrackCount() != 0) {
+ for (int trackID = 0; trackID < mediaInfo->getTrackCount() && isPass; trackID++) {
+ for (int i = 0; i < len; i++) {
+ mediaInfo->offsetTimeStamp(trackID, OFFSET_TS, coffsetIndices[i]);
+ }
+ FILE* ofp = fopen(cdstPath, "wbe+");
+ if (ofp) {
+ AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)format);
+ mediaInfo->muxMedia(muxer);
+ AMediaMuxer_delete(muxer);
+ fclose(ofp);
+ auto* outInfo = new MuxerNativeTestHelper(cdstPath);
+ isPass = mediaInfo->equals(outInfo);
+ if (!isPass) {
+ ALOGE("Validation failed after adding timestamp offset to track %d", trackID);
+ }
+ delete outInfo;
+ } else {
+ isPass = false;
+ ALOGE("failed to open output file %s", cdstPath);
+ }
+ for (int i = len - 1; i >= 0; i--) {
+ mediaInfo->offsetTimeStamp(trackID, -OFFSET_TS, coffsetIndices[i]);
+ }
+ }
+ } else {
+ isPass = false;
+ ALOGE("no valid track found in input file %s", csrcPath);
+ }
+ env->ReleaseStringUTFChars(jdstPath, cdstPath);
+ env->ReleaseStringUTFChars(jsrcPath, csrcPath);
+ env->ReleaseIntArrayElements(joffsetIndices, coffsetIndices, 0);
+ delete mediaInfo;
+ return static_cast<jboolean>(isPass);
+}
+
+static jboolean nativeTestSimpleMux(JNIEnv* env, jobject, jstring jsrcPath, jstring jdstPath,
+ jstring jmime, jstring jselector) {
+ bool isPass = true;
+ const char* cmime = env->GetStringUTFChars(jmime, nullptr);
+ const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr);
+ const char* cselector = env->GetStringUTFChars(jselector, nullptr);
+ auto* mediaInfo = new MuxerNativeTestHelper(csrcPath, cmime);
+ static const std::map<MuxerFormat, const char*> formatStringPair = {
+ {OUTPUT_FORMAT_MPEG_4, "mp4"},
+ {OUTPUT_FORMAT_WEBM, "webm"},
+ {OUTPUT_FORMAT_THREE_GPP, "3gp"},
+ {OUTPUT_FORMAT_HEIF, "heif"},
+ {OUTPUT_FORMAT_OGG, "ogg"}};
+ if (mediaInfo->getTrackCount() == 1) {
+ const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
+ for (int fmt = OUTPUT_FORMAT_START; fmt <= OUTPUT_FORMAT_LIST_END && isPass; fmt++) {
+ auto it = formatStringPair.find((MuxerFormat)fmt);
+ if (it == formatStringPair.end() || strstr(cselector, it->second) == nullptr) {
+ continue;
+ }
+ if (fmt == OUTPUT_FORMAT_WEBM) continue; // TODO(b/146923551)
+ FILE* ofp = fopen(cdstPath, "wbe+");
+ if (ofp) {
+ AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)fmt);
+ bool muxStatus = mediaInfo->muxMedia(muxer);
+ bool result = true;
+ AMediaMuxer_delete(muxer);
+ fclose(ofp);
+ if (muxStatus) {
+ auto* outInfo = new MuxerNativeTestHelper(cdstPath, cmime);
+ result = mediaInfo->equals(outInfo);
+ delete outInfo;
+ }
+ if ((muxStatus && !result) ||
+ (!muxStatus && isCodecContainerPairValid((MuxerFormat)fmt, cmime))) {
+ isPass = false;
+ ALOGE("error: file %s, mime %s, output != clone(input) for format %d", csrcPath,
+ cmime, fmt);
+ }
+ } else {
+ isPass = false;
+ ALOGE("error: file %s, mime %s, failed to open output file %s", csrcPath, cmime,
+ cdstPath);
+ }
+ }
+ env->ReleaseStringUTFChars(jdstPath, cdstPath);
+ } else {
+ isPass = false;
+ ALOGE("error: file %s, mime %s, track count exp/rec - %d/%d", csrcPath, cmime, 1,
+ mediaInfo->getTrackCount());
+ }
+ env->ReleaseStringUTFChars(jselector, cselector);
+ env->ReleaseStringUTFChars(jsrcPath, csrcPath);
+ env->ReleaseStringUTFChars(jmime, cmime);
+ delete mediaInfo;
+ return static_cast<jboolean>(isPass);
+}
+
+int registerAndroidMediaV2CtsMuxerTestApi(JNIEnv* env) {
+ const JNINativeMethod methodTable[] = {
+ {"nativeTestSetOrientationHint", "(ILjava/lang/String;Ljava/lang/String;)Z",
+ (void*)nativeTestSetOrientationHint},
+ {"nativeTestSetLocation", "(ILjava/lang/String;Ljava/lang/String;)Z",
+ (void*)nativeTestSetLocation},
+ };
+ jclass c = env->FindClass("android/mediav2/cts/MuxerTest$TestApi");
+ return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
+}
+
+int registerAndroidMediaV2CtsMuxerTestMultiTrack(JNIEnv* env) {
+ const JNINativeMethod methodTable[] = {
+ {"nativeTestMultiTrack",
+ "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z",
+ (void*)nativeTestMultiTrack},
+ };
+ jclass c = env->FindClass("android/mediav2/cts/MuxerTest$TestMultiTrack");
+ return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
+}
+
+int registerAndroidMediaV2CtsMuxerTestOffsetPts(JNIEnv* env) {
+ const JNINativeMethod methodTable[] = {
+ {"nativeTestOffsetPts", "(ILjava/lang/String;Ljava/lang/String;[I)Z",
+ (void*)nativeTestOffsetPts},
+ };
+ jclass c = env->FindClass("android/mediav2/cts/MuxerTest$TestOffsetPts");
+ return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
+}
+
+int registerAndroidMediaV2CtsMuxerTestSimpleMux(JNIEnv* env) {
+ const JNINativeMethod methodTable[] = {
+ {"nativeTestSimpleMux",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z",
+ (void*)nativeTestSimpleMux},
+ };
+ jclass c = env->FindClass("android/mediav2/cts/MuxerTest$TestSimpleMux");
+ return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
+}
+
+extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
+ JNIEnv* env;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) return JNI_ERR;
+ if (registerAndroidMediaV2CtsMuxerTestApi(env) != JNI_OK) return JNI_ERR;
+ if (registerAndroidMediaV2CtsMuxerTestMultiTrack(env) != JNI_OK) return JNI_ERR;
+ if (registerAndroidMediaV2CtsMuxerTestOffsetPts(env) != JNI_OK) return JNI_ERR;
+ if (registerAndroidMediaV2CtsMuxerTestSimpleMux(env) != JNI_OK) return JNI_ERR;
+ return JNI_VERSION_1_6;
+}
\ No newline at end of file
diff --git a/tests/media/src/android/mediav2/cts/MuxerTest.java b/tests/media/src/android/mediav2/cts/MuxerTest.java
new file mode 100644
index 0000000..b20ee08
--- /dev/null
+++ b/tests/media/src/android/mediav2/cts/MuxerTest.java
@@ -0,0 +1,1008 @@
+/*
+ * Copyright (C) 2019 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.mediav2.cts;
+
+import android.media.MediaCodec;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.media.MediaMetadataRetriever;
+import android.media.MediaMuxer;
+import android.util.Log;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * MuxerTestHelper breaks a media file to elements that a muxer can use to rebuild its clone.
+ * While testing muxer, if the test doesn't use MediaCodecs class to generate elementary
+ * stream, but uses MediaExtractor, this class will be handy
+ */
+class MuxerTestHelper {
+ private static final String LOG_TAG = MuxerTestHelper.class.getSimpleName();
+ private static final boolean ENABLE_LOGS = false;
+ static final int STTS_TOLERANCE = 100;
+ private String mSrcPath;
+ private String mMime;
+ private int mTrackCount;
+ private ArrayList<MediaFormat> mFormat = new ArrayList<>();
+ private ByteBuffer mBuff;
+ private ArrayList<ArrayList<MediaCodec.BufferInfo>> mBufferInfo;
+ private HashMap<Integer, Integer> mInpIndexMap = new HashMap<>();
+ private ArrayList<Integer> mTrackIdxOrder = new ArrayList<>();
+ private int mFrameLimit;
+ // combineMedias() uses local version of this variable
+ private HashMap<Integer, Integer> mOutIndexMap = new HashMap<>();
+
+ private void splitMediaToMuxerParameters() throws IOException {
+ // Set up MediaExtractor to read from the source.
+ MediaExtractor extractor = new MediaExtractor();
+ extractor.setDataSource(mSrcPath);
+
+ // Set up MediaFormat
+ int index = 0;
+ for (int trackID = 0; trackID < extractor.getTrackCount(); trackID++) {
+ extractor.selectTrack(trackID);
+ MediaFormat format = extractor.getTrackFormat(trackID);
+ if (mMime == null) {
+ mTrackCount++;
+ mFormat.add(format);
+ mInpIndexMap.put(trackID, index++);
+ } else {
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ if (mime != null && mime.equals(mMime)) {
+ mTrackCount++;
+ mFormat.add(format);
+ mInpIndexMap.put(trackID, index);
+ break;
+ } else {
+ extractor.unselectTrack(trackID);
+ }
+ }
+ }
+
+ if (0 == mTrackCount) {
+ extractor.release();
+ throw new IllegalArgumentException("could not find usable track in file " + mSrcPath);
+ }
+
+ // Set up location for elementary stream
+ File file = new File(mSrcPath);
+ int bufferSize = (int) file.length();
+ bufferSize = ((bufferSize + 127) >> 7) << 7;
+ // Ideally, Sum of return values of extractor.readSampleData(...) should not exceed
+ // source file size. But in case of Vorbis, aosp extractor appends an additional 4 bytes to
+ // the data at every readSampleData() call. bufferSize <<= 1 empirically large enough to
+ // hold the excess 4 bytes per read call
+ bufferSize <<= 1;
+ mBuff = ByteBuffer.allocate(bufferSize);
+
+ // Set up space for bufferInfo of all samples of all tracks
+ mBufferInfo = new ArrayList<>(mTrackCount);
+ for (index = 0; index < mTrackCount; index++) {
+ mBufferInfo.add(new ArrayList<MediaCodec.BufferInfo>());
+ }
+
+ // Let MediaExtractor do its thing
+ boolean sawEOS = false;
+ int frameCount = 0;
+ int offset = 0;
+ while (!sawEOS && frameCount < mFrameLimit) {
+ int trackID;
+ MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
+ bufferInfo.offset = offset;
+ bufferInfo.size = extractor.readSampleData(mBuff, offset);
+
+ if (bufferInfo.size < 0) {
+ sawEOS = true;
+ } else {
+ bufferInfo.presentationTimeUs = extractor.getSampleTime();
+ bufferInfo.flags = extractor.getSampleFlags();
+ trackID = extractor.getSampleTrackIndex();
+ mTrackIdxOrder.add(trackID);
+ mBufferInfo.get(mInpIndexMap.get(trackID)).add(bufferInfo);
+ extractor.advance();
+ frameCount++;
+ }
+ offset += bufferInfo.size;
+ }
+ extractor.release();
+ }
+
+ void registerTrack(MediaMuxer muxer) {
+ for (int trackID = 0; trackID < mTrackCount; trackID++) {
+ int dstIndex = muxer.addTrack(mFormat.get(trackID));
+ mOutIndexMap.put(trackID, dstIndex);
+ }
+ }
+
+ void insertSampleData(MediaMuxer muxer) {
+ // write all registered tracks in interleaved order
+ int[] frameCount = new int[mTrackCount];
+ for (int i = 0; i < mTrackIdxOrder.size(); i++) {
+ int trackID = mTrackIdxOrder.get(i);
+ int index = mInpIndexMap.get(trackID);
+ MediaCodec.BufferInfo bufferInfo = mBufferInfo.get(index).get(frameCount[index]);
+ muxer.writeSampleData(mOutIndexMap.get(index), mBuff, bufferInfo);
+ frameCount[index]++;
+ if (ENABLE_LOGS) {
+ Log.v(LOG_TAG, "Track: " + index + " Timestamp: " + bufferInfo.presentationTimeUs);
+ }
+ }
+ if (ENABLE_LOGS) {
+ Log.v(LOG_TAG, "Total samples: " + mTrackIdxOrder.size());
+ }
+ }
+
+ void muxMedia(MediaMuxer muxer) {
+ registerTrack(muxer);
+ muxer.start();
+ insertSampleData(muxer);
+ muxer.stop();
+ }
+
+ void combineMedias(MediaMuxer muxer, Object o, int[] repeater) {
+ if (o == null || getClass() != o.getClass())
+ throw new IllegalArgumentException("Invalid Object handle");
+ if (null == repeater || repeater.length < 2)
+ throw new IllegalArgumentException("Invalid Parameter, repeater");
+ MuxerTestHelper that = (MuxerTestHelper) o;
+
+ // add tracks
+ int totalTracksToAdd = repeater[0] * this.mTrackCount + repeater[1] * that.mTrackCount;
+ int[] outIndexMap = new int[totalTracksToAdd];
+ MuxerTestHelper[] group = {this, that};
+ for (int k = 0, idx = 0; k < group.length; k++) {
+ for (int j = 0; j < repeater[k]; j++) {
+ for (MediaFormat format : group[k].mFormat) {
+ outIndexMap[idx++] = muxer.addTrack(format);
+ }
+ }
+ }
+
+ // mux samples
+ // write all registered tracks in planar order viz all samples of a track A then all
+ // samples of track B, ...
+ muxer.start();
+ for (int k = 0, idx = 0; k < group.length; k++) {
+ for (int j = 0; j < repeater[k]; j++) {
+ for (int i = 0; i < group[k].mTrackCount; i++) {
+ ArrayList<MediaCodec.BufferInfo> bufInfos = group[k].mBufferInfo.get(i);
+ for (int p = 0; p < bufInfos.size(); p++) {
+ MediaCodec.BufferInfo bufInfo = bufInfos.get(p);
+ muxer.writeSampleData(outIndexMap[idx], group[k].mBuff, bufInfo);
+ if (ENABLE_LOGS) {
+ Log.v(LOG_TAG, "Track: " + outIndexMap[idx] + " Timestamp: " +
+ bufInfo.presentationTimeUs);
+ }
+ }
+ idx++;
+ }
+ }
+ }
+ muxer.stop();
+ }
+
+ MuxerTestHelper(String srcPath, String mime, int frameLimit) throws IOException {
+ mSrcPath = srcPath;
+ mMime = mime;
+ if (frameLimit < 0) frameLimit = Integer.MAX_VALUE;
+ mFrameLimit = frameLimit;
+ splitMediaToMuxerParameters();
+ }
+
+ MuxerTestHelper(String srcPath, String mime) throws IOException {
+ this(srcPath, mime, -1);
+ }
+
+ MuxerTestHelper(String srcPath, int frameLimit) throws IOException {
+ this(srcPath, null, frameLimit);
+ }
+
+ MuxerTestHelper(String srcPath) throws IOException {
+ this(srcPath, null, -1);
+ }
+
+ int getTrackCount() {
+ return mTrackCount;
+ }
+
+ // offset pts of samples from index sampleOffset till the end by tsOffset
+ void offsetTimeStamp(int trackID, long tsOffset, int sampleOffset) {
+ if (trackID < mTrackCount) {
+ for (int i = sampleOffset; i < mBufferInfo.get(trackID).size(); i++) {
+ MediaCodec.BufferInfo bufferInfo = mBufferInfo.get(trackID).get(i);
+ bufferInfo.presentationTimeUs += tsOffset;
+ }
+ }
+ }
+
+ @Override
+ // returns true if 'this' stream is a subset of 'o'. That is all tracks in current media
+ // stream are present in ref media stream
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ MuxerTestHelper that = (MuxerTestHelper) o;
+
+ for (int i = 0; i < mTrackCount; i++) {
+ MediaFormat thisFormat = mFormat.get(i);
+ String thisMime = thisFormat.getString(MediaFormat.KEY_MIME);
+ int j = 0;
+ for (; j < that.mTrackCount; j++) {
+ MediaFormat thatFormat = that.mFormat.get(j);
+ String thatMime = thatFormat.getString(MediaFormat.KEY_MIME);
+ if (thisMime != null && thisMime.equals(thatMime)) {
+ if (mBufferInfo.get(i).size() == that.mBufferInfo.get(j).size()) {
+ int flagsDiff = 0, sizeDiff = 0, tsDiff = 0;
+ for (int k = 0; k < mBufferInfo.get(i).size(); k++) {
+ MediaCodec.BufferInfo thisInfo = mBufferInfo.get(i).get(k);
+ MediaCodec.BufferInfo thatInfo = that.mBufferInfo.get(j).get(k);
+ if (thisInfo.flags != thatInfo.flags) {
+ flagsDiff++;
+ }
+ if (thisInfo.size != thatInfo.size) {
+ sizeDiff++;
+ }
+ if (Math.abs(
+ thisInfo.presentationTimeUs - thatInfo.presentationTimeUs) >
+ STTS_TOLERANCE) {
+ tsDiff++;
+ }
+ }
+ if (flagsDiff != 0 || sizeDiff != 0 || tsDiff != 0) {
+ if (ENABLE_LOGS) {
+ Log.d(LOG_TAG, "For track: " + thisMime +
+ " Total Samples: " + mBufferInfo.get(i).size() +
+ " flagsDiff: " + flagsDiff +
+ " sizeDiff: " + sizeDiff +
+ " tsDiff: " + tsDiff);
+ }
+ } else break;
+ } else {
+ if (ENABLE_LOGS) {
+ Log.d(LOG_TAG, "Mime matched but sample count different." +
+ " Total Samples ref/test: " + mBufferInfo.get(i).size() + '/' +
+ that.mBufferInfo.get(j).size());
+ }
+ }
+ }
+ }
+ if (j == that.mTrackCount) {
+ if (ENABLE_LOGS) {
+ Log.d(LOG_TAG, "For track: " + thisMime + " Couldn't find a match ");
+ }
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
+@RunWith(Enclosed.class)
+public class MuxerTest {
+ // duplicate definitions of hide fields of MediaMuxer.OutputFormat.
+ private static final int MUXER_OUTPUT_FIRST = 0;
+ private static final int MUXER_OUTPUT_LAST = MediaMuxer.OutputFormat.MUXER_OUTPUT_OGG;
+
+ private static final String MUX_SEL_KEY = "mux-sel";
+ private static String selector;
+ private static boolean[] muxSelector = new boolean[MUXER_OUTPUT_LAST + 1];
+ private static HashMap<Integer, String> formatStringPair = new HashMap<>();
+
+ static {
+ android.os.Bundle args = InstrumentationRegistry.getArguments();
+ final String defSel = "mp4;webm;3gp;ogg";
+ selector = (null == args.getString(MUX_SEL_KEY)) ? defSel : args.getString(MUX_SEL_KEY);
+
+ createFormatStringPair();
+ for (int format = MUXER_OUTPUT_FIRST; format <= MUXER_OUTPUT_LAST; format++) {
+ muxSelector[format] = selector.contains(formatStringPair.get(format));
+ }
+ }
+
+ static private void createFormatStringPair() {
+ formatStringPair.put(MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, "mp4");
+ formatStringPair.put(MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM, "webm");
+ formatStringPair.put(MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP, "3gp");
+ formatStringPair.put(MediaMuxer.OutputFormat.MUXER_OUTPUT_HEIF, "heif");
+ formatStringPair.put(MediaMuxer.OutputFormat.MUXER_OUTPUT_OGG, "ogg");
+ }
+
+ static private boolean shouldRunTest(int format) {
+ return muxSelector[format];
+ }
+
+ /**
+ * Tests MediaMuxer API that are dependent on MediaMuxer.OutputFormat. setLocation,
+ * setOrientationHint are dependent on the mime type and OutputFormat. Legality of these APIs
+ * are tested in this class.
+ */
+ @SmallTest
+ @RunWith(Parameterized.class)
+ public static class TestApi {
+ private int mOutFormat;
+ private String mSrcFile;
+ private String mInpPath;
+ private String mOutPath;
+ private static final float annapurnaLat = 28.59f;
+ private static final float annapurnaLong = 83.82f;
+ private static final float TOLERANCE = 0.0002f;
+ private static final int currRotation = 180;
+
+ static {
+ System.loadLibrary("ctsmediav2muxer_jni");
+ }
+
+ @Before
+ public void prologue() throws IOException {
+ mInpPath = WorkDir.getMediaDirString() + mSrcFile;
+ mOutPath = File.createTempFile("tmp", ".out").getAbsolutePath();
+ }
+
+ @After
+ public void epilogue() {
+ new File(mOutPath).delete();
+ }
+
+ @Parameterized.Parameters
+ public static Collection<Object[]> input() {
+ return Arrays.asList(new Object[][]{
+ {MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, "bbb_cif_768kbps_30fps_avc.mp4"},
+ {MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM, "bbb_cif_768kbps_30fps_vp9.mkv"},
+ {MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP, "bbb_cif_768kbps_30fps_h263.mp4"},
+ {MediaMuxer.OutputFormat.MUXER_OUTPUT_OGG, "bbb_stereo_48kHz_192kbps_opus.ogg"},
+ });
+ }
+
+ public TestApi(int outFormat, String srcFile) {
+ mOutFormat = outFormat;
+ mSrcFile = srcFile;
+ }
+
+ private native boolean nativeTestSetLocation(int format, String srcPath, String outPath);
+
+ private native boolean nativeTestSetOrientationHint(int format, String srcPath,
+ String outPath);
+
+ private void verifyLocationInFile(String fileName) {
+ if (mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4 &&
+ mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP) return;
+ MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+ retriever.setDataSource(fileName);
+
+ // parsing String location and recover the location information in floats
+ // Make sure the tolerance is very small - due to rounding errors.
+
+ // Get the position of the -/+ sign in location String, which indicates
+ // the beginning of the longitude.
+ String loc = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION);
+ assertTrue(loc != null);
+ int minusIndex = loc.lastIndexOf('-');
+ int plusIndex = loc.lastIndexOf('+');
+
+ assertTrue("+ or - is not found or found only at the beginning [" + loc + "]",
+ (minusIndex > 0 || plusIndex > 0));
+ int index = Math.max(minusIndex, plusIndex);
+
+ float latitude = Float.parseFloat(loc.substring(0, index - 1));
+ int lastIndex = loc.lastIndexOf('/', index);
+ if (lastIndex == -1) {
+ lastIndex = loc.length();
+ }
+ float longitude = Float.parseFloat(loc.substring(index, lastIndex - 1));
+ assertTrue("Incorrect latitude: " + latitude + " [" + loc + "]",
+ Math.abs(latitude - annapurnaLat) <= TOLERANCE);
+ assertTrue("Incorrect longitude: " + longitude + " [" + loc + "]",
+ Math.abs(longitude - annapurnaLong) <= TOLERANCE);
+ retriever.release();
+ }
+
+ private void verifyOrientation(String fileName) {
+ if (mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4 &&
+ mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP) return;
+ MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+ retriever.setDataSource(fileName);
+
+ String testDegrees = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
+ assertTrue(testDegrees != null);
+ assertEquals("Different degrees " + currRotation + " and " + testDegrees,
+ currRotation, Integer.parseInt(testDegrees));
+ retriever.release();
+ }
+
+ @Test
+ public void testSetLocation() throws IOException {
+ Assume.assumeTrue(shouldRunTest(mOutFormat));
+ MediaMuxer muxer = new MediaMuxer(mOutPath, mOutFormat);
+ try {
+ boolean isGeoDataSupported = false;
+ final float tooFarNorth = 90.5f;
+ final float tooFarWest = -180.5f;
+ final float tooFarSouth = -90.5f;
+ final float tooFarEast = 180.5f;
+ final float atlanticLat = 14.59f;
+ final float atlanticLong = 28.67f;
+
+ try {
+ muxer.setLocation(tooFarNorth, atlanticLong);
+ fail("setLocation succeeded with bad argument: [" + tooFarNorth + "," +
+ atlanticLong + "]");
+ } catch (Exception e) {
+ // expected
+ }
+ try {
+ muxer.setLocation(tooFarSouth, atlanticLong);
+ fail("setLocation succeeded with bad argument: [" + tooFarSouth + "," +
+ atlanticLong + "]");
+ } catch (Exception e) {
+ // expected
+ }
+ try {
+ muxer.setLocation(atlanticLat, tooFarWest);
+ fail("setLocation succeeded with bad argument: [" + atlanticLat + "," +
+ tooFarWest + "]");
+ } catch (Exception e) {
+ // expected
+ }
+ try {
+ muxer.setLocation(atlanticLat, tooFarEast);
+ fail("setLocation succeeded with bad argument: [" + atlanticLat + "," +
+ tooFarEast + "]");
+ } catch (Exception e) {
+ // expected
+ }
+ try {
+ muxer.setLocation(tooFarNorth, tooFarWest);
+ fail("setLocation succeeded with bad argument: [" + tooFarNorth + "," +
+ tooFarWest + "]");
+ } catch (Exception e) {
+ // expected
+ }
+ try {
+ muxer.setLocation(atlanticLat, atlanticLong);
+ isGeoDataSupported = true;
+ } catch (Exception e) {
+ // can happen
+ }
+
+ if (isGeoDataSupported) {
+ try {
+ muxer.setLocation(annapurnaLat, annapurnaLong);
+ } catch (IllegalArgumentException e) {
+ fail(e.getMessage());
+ }
+ } else {
+ assertTrue(mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4 &&
+ mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP);
+ }
+
+ MuxerTestHelper mediaInfo = new MuxerTestHelper(mInpPath, 60);
+ mediaInfo.registerTrack(muxer);
+ muxer.start();
+ // after start
+ try {
+ muxer.setLocation(0.0f, 0.0f);
+ fail("SetLocation succeeded after muxer.start()");
+ } catch (IllegalStateException e) {
+ // Exception
+ }
+ mediaInfo.insertSampleData(muxer);
+ muxer.stop();
+ // after stop
+ try {
+ muxer.setLocation(annapurnaLat, annapurnaLong);
+ fail("setLocation() succeeded after muxer.stop()");
+ } catch (IllegalStateException e) {
+ // expected
+ }
+ muxer.release();
+ // after release
+ try {
+ muxer.setLocation(annapurnaLat, annapurnaLong);
+ fail("setLocation() succeeded after muxer.release()");
+ } catch (IllegalStateException e) {
+ // expected
+ }
+ verifyLocationInFile(mOutPath);
+ } finally {
+ muxer.release();
+ }
+ }
+
+ @Test
+ public void testSetOrientationHint() throws IOException {
+ Assume.assumeTrue(shouldRunTest(mOutFormat));
+ MediaMuxer muxer = new MediaMuxer(mOutPath, mOutFormat);
+ try {
+ boolean isOrientationSupported = false;
+ final int[] badRotation = {360, 45, -90};
+ final int oldRotation = 90;
+
+ for (int degree : badRotation) {
+ try {
+ muxer.setOrientationHint(degree);
+ fail("setOrientationHint() succeeded with bad argument :" + degree);
+ } catch (Exception e) {
+ // expected
+ }
+ }
+ try {
+ muxer.setOrientationHint(oldRotation);
+ isOrientationSupported = true;
+ } catch (Exception e) {
+ // can happen
+ }
+ if (isOrientationSupported) {
+ try {
+ muxer.setOrientationHint(currRotation);
+ } catch (IllegalArgumentException e) {
+ fail(e.getMessage());
+ }
+ } else {
+ assertTrue(mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4 &&
+ mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP);
+ }
+
+ MuxerTestHelper mediaInfo = new MuxerTestHelper(mInpPath, 60);
+ mediaInfo.registerTrack(muxer);
+ muxer.start();
+ // after start
+ try {
+ muxer.setOrientationHint(0);
+ fail("setOrientationHint succeeded after muxer.start()");
+ } catch (IllegalStateException e) {
+ // Exception
+ }
+ mediaInfo.insertSampleData(muxer);
+ muxer.stop();
+ // after stop
+ try {
+ muxer.setOrientationHint(currRotation);
+ fail("setOrientationHint() succeeded after muxer.stop()");
+ } catch (IllegalStateException e) {
+ // expected
+ }
+ muxer.release();
+ // after release
+ try {
+ muxer.setOrientationHint(currRotation);
+ fail("setOrientationHint() succeeded after muxer.release()");
+ } catch (IllegalStateException e) {
+ // expected
+ }
+ verifyOrientation(mOutPath);
+ } finally {
+ muxer.release();
+ }
+ }
+
+ @Test
+ public void testSetLocationNative() {
+ Assume.assumeTrue(shouldRunTest(mOutFormat));
+ assertTrue(nativeTestSetLocation(mOutFormat, mInpPath, mOutPath));
+ verifyLocationInFile(mOutPath);
+ }
+
+ @Test
+ public void testSetOrientationHintNative() {
+ Assume.assumeTrue(shouldRunTest(mOutFormat));
+ assertTrue(nativeTestSetOrientationHint(mOutFormat, mInpPath, mOutPath));
+ verifyOrientation(mOutPath);
+ }
+ }
+
+ /**
+ * Tests muxing multiple Video/Audio Tracks
+ */
+ @LargeTest
+ @RunWith(Parameterized.class)
+ public static class TestMultiTrack {
+ private int mOutFormat;
+ private String mSrcFileA;
+ private String mSrcFileB;
+ private String mInpPathA;
+ private String mInpPathB;
+ private String mRefPath;
+ private String mOutPath;
+
+ static {
+ System.loadLibrary("ctsmediav2muxer_jni");
+ }
+
+ @Before
+ public void prologue() throws IOException {
+ mInpPathA = WorkDir.getMediaDirString() + mSrcFileA;
+ mInpPathB = WorkDir.getMediaDirString() + mSrcFileB;
+ mRefPath = File.createTempFile("ref", ".out").getAbsolutePath();
+ mOutPath = File.createTempFile("tmp", ".out").getAbsolutePath();
+ }
+
+ @After
+ public void epilogue() {
+ new File(mRefPath).delete();
+ new File(mOutPath).delete();
+ }
+
+ @Parameterized.Parameters
+ public static Collection<Object[]> input() {
+ return Arrays.asList(new Object[][]{
+ // audio, video are 3 sec
+ {MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, "bbb_cif_768kbps_30fps_mpeg4" +
+ ".mkv", "bbb_stereo_48kHz_192kbps_aac.mp4"},
+ {MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM, "bbb_cif_768kbps_30fps_vp9.mkv"
+ , "bbb_stereo_48kHz_192kbps_vorbis.ogg"},
+ {MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP, "bbb_cif_768kbps_30fps_h263.mp4"
+ , "bbb_mono_16kHz_20kbps_amrwb.amr"},
+
+ // audio 3 sec, video 10 sec
+ {MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, "bbb_qcif_512kbps_30fps_avc" +
+ ".mp4", "bbb_stereo_48kHz_192kbps_aac.mp4"},
+ {MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM, "bbb_qcif_512kbps_30fps_vp9.webm"
+ , "bbb_stereo_48kHz_192kbps_vorbis.ogg"},
+ {MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP, "bbb_qcif_512kbps_30fps_h263.3gp"
+ , "bbb_mono_16kHz_20kbps_amrwb.amr"},
+
+ // audio 10 sec, video 3 sec
+ {MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, "bbb_cif_768kbps_30fps_mpeg4" +
+ ".mkv", "bbb_stereo_48kHz_128kbps_aac.mp4"},
+ {MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM, "bbb_cif_768kbps_30fps_vp9.mkv"
+ , "bbb_stereo_48kHz_128kbps_vorbis.ogg"},
+ {MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP, "bbb_cif_768kbps_30fps_h263.mp4"
+ , "bbb_mono_8kHz_8kbps_amrnb.3gp"},
+ });
+ }
+
+ public TestMultiTrack(int outFormat, String srcFileA, String srcFileB) {
+ mOutFormat = outFormat;
+ mSrcFileA = srcFileA;
+ mSrcFileB = srcFileB;
+ }
+
+ private native boolean nativeTestMultiTrack(int format, String fileA, String fileB,
+ String fileR, String fileO);
+
+ @Test
+ public void testMultiTrack() throws IOException {
+ Assume.assumeTrue(shouldRunTest(mOutFormat));
+ Assume.assumeTrue("TODO(b/146423022)",
+ mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM);
+ // number of times to repeat {mSrcFileA, mSrcFileB} in Output
+ final int[][] numTracks = {{2, 0}, {0, 2}, {1, 2}, {2, 1}};
+
+ MuxerTestHelper mediaInfoA = new MuxerTestHelper(mInpPathA);
+ MuxerTestHelper mediaInfoB = new MuxerTestHelper(mInpPathB);
+ assertEquals("error! unexpected track count", 1, mediaInfoA.getTrackCount());
+ assertEquals("error! unexpected track count", 1, mediaInfoB.getTrackCount());
+
+ // prepare reference
+ RandomAccessFile refFile = new RandomAccessFile(mRefPath, "rws");
+ MediaMuxer muxer = new MediaMuxer(refFile.getFD(), mOutFormat);
+ MuxerTestHelper refInfo = null;
+ String msg = String.format("testMultiTrack: inputs: %s %s, fmt: %d ", mSrcFileA,
+ mSrcFileB, mOutFormat);
+ try {
+ mediaInfoA.combineMedias(muxer, mediaInfoB, new int[]{1, 1});
+ refInfo = new MuxerTestHelper(mRefPath);
+ if (!mediaInfoA.equals(refInfo) || !mediaInfoB.equals(refInfo)) {
+ fail(msg + "error ! muxing src A and src B failed");
+ }
+ } catch (Exception e) {
+ if (mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_OGG) {
+ fail(msg + "error ! muxing src A and src B failed");
+ }
+ } finally {
+ muxer.release();
+ refFile.close();
+ }
+
+ // test multi-track
+ for (int[] numTrack : numTracks) {
+ RandomAccessFile outFile = new RandomAccessFile(mOutPath, "rws");
+ muxer = new MediaMuxer(outFile.getFD(), mOutFormat);
+ try {
+ mediaInfoA.combineMedias(muxer, mediaInfoB, numTrack);
+ MuxerTestHelper outInfo = new MuxerTestHelper(mOutPath);
+ if (!outInfo.equals(refInfo)) {
+ fail(msg + " error ! muxing src A: " + numTrack[0] + " src B: " +
+ numTrack[1] + "failed");
+ }
+ } catch (Exception e) {
+ if (mOutFormat == MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4) {
+ fail(msg + " error ! muxing src A: " + numTrack[0] + " src B: " +
+ numTrack[1] + "failed");
+ }
+ } finally {
+ muxer.release();
+ outFile.close();
+ }
+ }
+ }
+
+ @Test
+ public void testMultiTrackNative() {
+ Assume.assumeTrue(shouldRunTest(mOutFormat));
+ Assume.assumeTrue("TODO(b/146423022)",
+ mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM);
+ assertTrue(nativeTestMultiTrack(mOutFormat, mInpPathA, mInpPathB, mRefPath, mOutPath));
+ }
+ }
+
+ /**
+ * Add an offset to the presentation time of samples of a track. Mux with the added offset,
+ * validate by re-extracting the muxer output file and compare with original.
+ */
+ @Ignore("TODO(test fails for mp4, need to check if test is faulty)")
+ @LargeTest
+ @RunWith(Parameterized.class)
+ public static class TestOffsetPts {
+ private String mSrcFile;
+ private int mOutFormat;
+ private int[] mOffsetIndices;
+ private String mInpPath;
+ private String mOutPath;
+
+ static {
+ System.loadLibrary("ctsmediav2muxer_jni");
+ }
+
+ @Before
+ public void prologue() throws IOException {
+ mInpPath = WorkDir.getMediaDirString() + mSrcFile;
+ mOutPath = File.createTempFile("tmp", ".out").getAbsolutePath();
+ }
+
+ @After
+ public void epilogue() {
+ new File(mOutPath).delete();
+ }
+
+ @Parameterized.Parameters
+ public static Collection<Object[]> input() {
+ return Arrays.asList(new Object[][]{
+ {MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4,
+ "bbb_cif_768kbps_30fps_hevc_stereo_48kHz_192kbps_aac.mp4",
+ new int[]{0}},
+ {MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM,
+ "bbb_cif_768kbps_30fps_vp8_stereo_48kHz_192kbps_vorbis.webm",
+ new int[]{0}},
+ {MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP,
+ "bbb_cif_768kbps_30fps_mpeg4_mono_16kHz_20kbps_amrwb.3gp",
+ new int[]{0}},
+ {MediaMuxer.OutputFormat.MUXER_OUTPUT_OGG, "bbb_stereo_48kHz_192kbps_opus.ogg",
+ new int[]{10}},
+ {MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, "bbb_cif_768kbps_30fps_avc.mp4",
+ new int[]{6, 50, 77}},
+ {MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM, "bbb_cif_768kbps_30fps_vp9.mkv",
+ new int[]{8, 44}},
+ });
+ }
+
+ public TestOffsetPts(int outFormat, String file, int[] offsetIndices) {
+ mOutFormat = outFormat;
+ mSrcFile = file;
+ mOffsetIndices = offsetIndices;
+ }
+
+ private native boolean nativeTestOffsetPts(int format, String srcFile, String dstFile,
+ int[] offsetIndices);
+
+ @Test
+ public void testOffsetPresentationTime() throws IOException {
+ final int OFFSET_TS = 111000;
+ Assume.assumeTrue(shouldRunTest(mOutFormat));
+ Assume.assumeTrue("TODO(b/146423022)",
+ mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM);
+ Assume.assumeTrue("TODO(b/146421018)",
+ mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_OGG);
+ assertTrue(OFFSET_TS > MuxerTestHelper.STTS_TOLERANCE);
+ MuxerTestHelper mediaInfo = new MuxerTestHelper(mInpPath);
+ for (int trackID = 0; trackID < mediaInfo.getTrackCount(); trackID++) {
+ for (int i = 0; i < mOffsetIndices.length; i++) {
+ mediaInfo.offsetTimeStamp(trackID, OFFSET_TS, mOffsetIndices[i]);
+ }
+ MediaMuxer muxer = new MediaMuxer(mOutPath, mOutFormat);
+ mediaInfo.muxMedia(muxer);
+ muxer.release();
+ MuxerTestHelper outInfo = new MuxerTestHelper(mOutPath);
+ if (!outInfo.equals(mediaInfo)) {
+ String msg = String.format(
+ "testOffsetPresentationTime: inp: %s, fmt: %d, trackID %d", mSrcFile,
+ mOutFormat, trackID);
+ fail(msg + "error! output != input");
+ }
+ for (int i = mOffsetIndices.length - 1; i >= 0; i--) {
+ mediaInfo.offsetTimeStamp(trackID, -OFFSET_TS, mOffsetIndices[i]);
+ }
+ }
+ }
+
+ @Test
+ public void testOffsetPresentationTimeNative() {
+ Assume.assumeTrue(shouldRunTest(mOutFormat));
+ Assume.assumeTrue("TODO(b/146423022)",
+ mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM);
+ Assume.assumeTrue("TODO(b/146421018)",
+ mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_OGG);
+ assertTrue(nativeTestOffsetPts(mOutFormat, mInpPath, mOutPath, mOffsetIndices));
+ }
+ }
+
+ /**
+ * Audio, Video Codecs support a variety of file-types/container formats. For example,
+ * AAC-LC supports MPEG4, 3GPP. Vorbis supports OGG and WEBM. H.263 supports 3GPP and WEBM.
+ * This test takes the output of a codec and muxes it in to all possible container formats.
+ * The results are checked for inconsistencies with the requirements of CDD.
+ */
+ @LargeTest
+ @RunWith(Parameterized.class)
+ public static class TestSimpleMux {
+ private final List<String> codecListforTypeMp4 =
+ Arrays.asList(MediaFormat.MIMETYPE_VIDEO_MPEG4, MediaFormat.MIMETYPE_VIDEO_H263,
+ MediaFormat.MIMETYPE_VIDEO_AVC, MediaFormat.MIMETYPE_VIDEO_HEVC,
+ MediaFormat.MIMETYPE_AUDIO_AAC);
+ private final List<String> codecListforTypeWebm =
+ Arrays.asList(MediaFormat.MIMETYPE_VIDEO_VP8, MediaFormat.MIMETYPE_VIDEO_VP9,
+ MediaFormat.MIMETYPE_AUDIO_VORBIS, MediaFormat.MIMETYPE_AUDIO_OPUS);
+ private final List<String> codecListforType3gp =
+ Arrays.asList(MediaFormat.MIMETYPE_VIDEO_MPEG4, MediaFormat.MIMETYPE_VIDEO_H263,
+ MediaFormat.MIMETYPE_VIDEO_AVC, MediaFormat.MIMETYPE_AUDIO_AAC,
+ MediaFormat.MIMETYPE_AUDIO_AMR_NB, MediaFormat.MIMETYPE_AUDIO_AMR_WB);
+ private final List<String> codecListforTypeOgg =
+ Arrays.asList(MediaFormat.MIMETYPE_AUDIO_OPUS);
+ private String mMime;
+ private String mSrcFile;
+ private String mInpPath;
+ private String mOutPath;
+
+ static {
+ System.loadLibrary("ctsmediav2muxer_jni");
+ }
+
+ public TestSimpleMux(String mime, String srcFile) {
+ mMime = mime;
+ mSrcFile = srcFile;
+ }
+
+ @Before
+ public void prologue() throws IOException {
+ mInpPath = WorkDir.getMediaDirString() + mSrcFile;
+ mOutPath = File.createTempFile("tmp", ".out").getAbsolutePath();
+ }
+
+ @After
+ public void epilogue() {
+ new File(mOutPath).delete();
+ }
+
+ private boolean isCodecContainerPairValid(int format) {
+ boolean result = false;
+ if (format == MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
+ result = codecListforTypeMp4.contains(mMime) || mMime.startsWith("application/");
+ else if (format == MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM) {
+ return codecListforTypeWebm.contains(mMime);
+ } else if (format == MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP) {
+ result = codecListforType3gp.contains(mMime);
+ } else if (format == MediaMuxer.OutputFormat.MUXER_OUTPUT_OGG) {
+ result = codecListforTypeOgg.contains(mMime);
+ }
+ return result;
+ }
+
+ private native boolean nativeTestSimpleMux(String srcPath, String outPath, String mime,
+ String selector);
+
+ @Parameterized.Parameters
+ public static Collection<Object[]> input() {
+ return Arrays.asList(new Object[][]{
+ // Video Codecs
+ {MediaFormat.MIMETYPE_VIDEO_H263,
+ "bbb_cif_768kbps_30fps_h263_mono_8kHz_12kbps_amrnb.3gp"},
+ {MediaFormat.MIMETYPE_VIDEO_AVC,
+ "bbb_cif_768kbps_30fps_avc_stereo_48kHz_192kbps_vorbis.mp4"},
+ {MediaFormat.MIMETYPE_VIDEO_HEVC,
+ "bbb_cif_768kbps_30fps_hevc_stereo_48kHz_192kbps_opus.mp4"},
+ {MediaFormat.MIMETYPE_VIDEO_MPEG4,
+ "bbb_cif_768kbps_30fps_mpeg4_mono_16kHz_20kbps_amrwb.3gp"},
+ {MediaFormat.MIMETYPE_VIDEO_VP8,
+ "bbb_cif_768kbps_30fps_vp8_stereo_48kHz_192kbps_vorbis.webm"},
+ {MediaFormat.MIMETYPE_VIDEO_VP9,
+ "bbb_cif_768kbps_30fps_vp9_stereo_48kHz_192kbps_opus.webm"},
+ // Audio Codecs
+ {MediaFormat.MIMETYPE_AUDIO_AAC,
+ "bbb_stereo_48kHz_128kbps_aac.mp4"},
+ {MediaFormat.MIMETYPE_AUDIO_AMR_NB,
+ "bbb_cif_768kbps_30fps_h263_mono_8kHz_12kbps_amrnb.3gp"},
+ {MediaFormat.MIMETYPE_AUDIO_AMR_WB,
+ "bbb_cif_768kbps_30fps_mpeg4_mono_16kHz_20kbps_amrwb.3gp"},
+ {MediaFormat.MIMETYPE_AUDIO_OPUS,
+ "bbb_cif_768kbps_30fps_vp9_stereo_48kHz_192kbps_opus.webm"},
+ {MediaFormat.MIMETYPE_AUDIO_VORBIS,
+ "bbb_cif_768kbps_30fps_vp8_stereo_48kHz_192kbps_vorbis.webm"},
+ // Metadata
+ {"application/gyro",
+ "video_176x144_3gp_h263_300kbps_25fps_aac_stereo_128kbps_11025hz_metadata_gyro_non_compliant.3gp"},
+ {"application/gyro",
+ "video_176x144_3gp_h263_300kbps_25fps_aac_stereo_128kbps_11025hz_metadata_gyro_compliant.3gp"},
+ });
+ }
+
+ @Test
+ public void testSimpleMux() throws IOException {
+ Assume.assumeTrue("TODO(b/146421018)",
+ !mMime.equals(MediaFormat.MIMETYPE_AUDIO_OPUS));
+ Assume.assumeTrue("TODO(b/146923287)",
+ !mMime.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS));
+ MuxerTestHelper mediaInfo = new MuxerTestHelper(mInpPath, mMime);
+ assertEquals("error! unexpected track count", 1, mediaInfo.getTrackCount());
+ for (int format = MUXER_OUTPUT_FIRST; format <= MUXER_OUTPUT_LAST; format++) {
+ if (!shouldRunTest(format)) continue;
+ // TODO(b/146923551)
+ if (format == MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM) continue;
+ String msg = String.format("testSimpleMux: inp: %s, mime: %s, fmt: %d ", mSrcFile,
+ mMime, format);
+ MediaMuxer muxer = new MediaMuxer(mOutPath, format);
+ try {
+ mediaInfo.muxMedia(muxer);
+ MuxerTestHelper outInfo = new MuxerTestHelper(mOutPath);
+ if (!mediaInfo.equals(outInfo)) {
+ fail(msg + "error! output != clone(input)");
+ }
+ } catch (Exception e) {
+ if (isCodecContainerPairValid(format)) {
+ fail(msg + "error! incompatible mime and output format");
+ }
+ } finally {
+ muxer.release();
+ }
+ }
+ }
+
+ @Test
+ public void testSimpleMuxNative() {
+ Assume.assumeTrue("TODO(b/146421018)",
+ !mMime.equals(MediaFormat.MIMETYPE_AUDIO_OPUS));
+ Assume.assumeTrue("TODO(b/146923287)",
+ !mMime.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS));
+ assertTrue(nativeTestSimpleMux(mInpPath, mOutPath, mMime, selector));
+ }
+ }
+}
diff --git a/tests/signature/api-check/system-annotation/AndroidTest.xml b/tests/signature/api-check/system-annotation/AndroidTest.xml
index 43183bf..691516c 100644
--- a/tests/signature/api-check/system-annotation/AndroidTest.xml
+++ b/tests/signature/api-check/system-annotation/AndroidTest.xml
@@ -28,7 +28,7 @@
<option name="runner" value="repackaged.android.test.InstrumentationTestRunner" />
<option name="class" value="android.signature.cts.api.AnnotationTest" />
<option name="instrumentation-arg" key="expected-api-files" value="system-current.txt,system-removed.txt,car-system-current.txt,car-system-removed.txt" />
- <option name="instrumentation-arg" key="annotation-for-exact-match" value="android.annotation.SystemApi" />
+ <option name="instrumentation-arg" key="annotation-for-exact-match" value="@android.annotation.SystemApi\(client=PRIVILEGED_APPS,\ process=ALL\)" />
<option name="runtime-hint" value="30s" />
</test>
diff --git a/tests/signature/lib/common/src/android/signature/cts/AnnotationChecker.java b/tests/signature/lib/common/src/android/signature/cts/AnnotationChecker.java
index ab111a4..cd6db94 100644
--- a/tests/signature/lib/common/src/android/signature/cts/AnnotationChecker.java
+++ b/tests/signature/lib/common/src/android/signature/cts/AnnotationChecker.java
@@ -28,7 +28,7 @@
*/
public class AnnotationChecker extends AbstractApiChecker {
- private final Class<? extends Annotation> annotationClass;
+ private final String annotationSpec;
private final Map<String, Class<?>> annotatedClassesMap = new HashMap<>();
private final Map<String, Set<Constructor<?>>> annotatedConstructorsMap = new HashMap<>();
@@ -40,24 +40,24 @@
* android.annotation.SystemApi)
*/
public AnnotationChecker(
- ResultObserver resultObserver, ClassProvider classProvider, String annotationName) {
+ ResultObserver resultObserver, ClassProvider classProvider, String annotationSpec) {
super(classProvider, resultObserver);
- annotationClass = ReflectionHelper.getAnnotationClass(annotationName);
+ this.annotationSpec = annotationSpec;
classProvider.getAllClasses().forEach(clazz -> {
- if (clazz.isAnnotationPresent(annotationClass)) {
+ if (ReflectionHelper.hasMatchingAnnotation(clazz, annotationSpec)) {
annotatedClassesMap.put(clazz.getName(), clazz);
}
Set<Constructor<?>> constructors = ReflectionHelper.getAnnotatedConstructors(clazz,
- annotationClass);
+ annotationSpec);
if (!constructors.isEmpty()) {
annotatedConstructorsMap.put(clazz.getName(), constructors);
}
- Set<Method> methods = ReflectionHelper.getAnnotatedMethods(clazz, annotationClass);
+ Set<Method> methods = ReflectionHelper.getAnnotatedMethods(clazz, annotationSpec);
if (!methods.isEmpty()) {
annotatedMethodsMap.put(clazz.getName(), methods);
}
- Set<Field> fields = ReflectionHelper.getAnnotatedFields(clazz, annotationClass);
+ Set<Field> fields = ReflectionHelper.getAnnotatedFields(clazz, annotationSpec);
if (!fields.isEmpty()) {
annotatedFieldsMap.put(clazz.getName(), fields);
}
@@ -68,27 +68,27 @@
public void checkDeferred() {
for (Class<?> clazz : annotatedClassesMap.values()) {
resultObserver.notifyFailure(FailureType.EXTRA_CLASS, clazz.getName(),
- "Class annotated with " + annotationClass.getName()
+ "Class annotated with " + annotationSpec
+ " does not exist in the documented API");
}
for (Set<Constructor<?>> set : annotatedConstructorsMap.values()) {
for (Constructor<?> c : set) {
resultObserver.notifyFailure(FailureType.EXTRA_METHOD, c.toString(),
- "Constructor annotated with " + annotationClass.getName()
+ "Constructor annotated with " + annotationSpec
+ " does not exist in the API");
}
}
for (Set<Method> set : annotatedMethodsMap.values()) {
for (Method m : set) {
resultObserver.notifyFailure(FailureType.EXTRA_METHOD, m.toString(),
- "Method annotated with " + annotationClass.getName()
+ "Method annotated with " + annotationSpec
+ " does not exist in the API");
}
}
for (Set<Field> set : annotatedFieldsMap.values()) {
for (Field f : set) {
resultObserver.notifyFailure(FailureType.EXTRA_FIELD, f.toString(),
- "Field annotated with " + annotationClass.getName()
+ "Field annotated with " + annotationSpec
+ " does not exist in the API");
}
}
@@ -116,10 +116,10 @@
protected void checkField(JDiffClassDescription classDescription, Class<?> runtimeClass,
JDiffClassDescription.JDiffField fieldDescription, Field field) {
// make sure that the field (or its declaring class) is annotated
- if (!ReflectionHelper.isAnnotatedOrInAnnotatedClass(field, annotationClass)) {
+ if (!ReflectionHelper.isAnnotatedOrInAnnotatedClass(field, annotationSpec)) {
resultObserver.notifyFailure(FailureType.MISSING_ANNOTATION,
field.toString(),
- "Annotation " + annotationClass.getName() + " is missing");
+ "Annotation " + annotationSpec + " is missing");
}
// remove it from the set if found in the API doc
@@ -136,11 +136,11 @@
.get(runtimeClass.getName());
// make sure that the constructor (or its declaring class) is annotated
- if (annotationClass != null) {
- if (!ReflectionHelper.isAnnotatedOrInAnnotatedClass(ctor, annotationClass)) {
+ if (annotationSpec != null) {
+ if (!ReflectionHelper.isAnnotatedOrInAnnotatedClass(ctor, annotationSpec)) {
resultObserver.notifyFailure(FailureType.MISSING_ANNOTATION,
ctor.toString(),
- "Annotation " + annotationClass.getName() + " is missing");
+ "Annotation " + annotationSpec + " is missing");
}
}
@@ -155,12 +155,12 @@
JDiffClassDescription.JDiffMethod methodDescription, Method method) {
// make sure that the method (or its declaring class) is annotated or overriding
// annotated method.
- if (!ReflectionHelper.isAnnotatedOrInAnnotatedClass(method, annotationClass)
+ if (!ReflectionHelper.isAnnotatedOrInAnnotatedClass(method, annotationSpec)
&& !ReflectionHelper.isOverridingAnnotatedMethod(method,
- annotationClass)) {
+ annotationSpec)) {
resultObserver.notifyFailure(FailureType.MISSING_ANNOTATION,
method.toString(),
- "Annotation " + annotationClass.getName() + " is missing");
+ "Annotation " + annotationSpec + " is missing");
}
// remove it from the set if found in the API doc
diff --git a/tests/signature/lib/common/src/android/signature/cts/DexMethod.java b/tests/signature/lib/common/src/android/signature/cts/DexMethod.java
index 9f209fe..dfe1934 100644
--- a/tests/signature/lib/common/src/android/signature/cts/DexMethod.java
+++ b/tests/signature/lib/common/src/android/signature/cts/DexMethod.java
@@ -23,6 +23,7 @@
public class DexMethod extends DexMember {
private final List<String> mParamTypeList;
+ private static final Pattern REGEX_SIGNATURE = Pattern.compile("^\\((.*)\\)(.*)$");
public DexMethod(String className, String name, String signature, String[] flags) {
super(className, name, parseDexReturnType(signature), flags);
@@ -52,7 +53,7 @@
}
private static Matcher matchSignature(String signature) {
- Matcher m = Pattern.compile("^\\((.*)\\)(.*)$").matcher(signature);
+ Matcher m = REGEX_SIGNATURE.matcher(signature);
if (!m.matches()) {
throw new RuntimeException("Could not parse method signature: " + signature);
}
diff --git a/tests/signature/lib/common/src/android/signature/cts/ReflectionHelper.java b/tests/signature/lib/common/src/android/signature/cts/ReflectionHelper.java
index 6b6140a..9c3741e 100644
--- a/tests/signature/lib/common/src/android/signature/cts/ReflectionHelper.java
+++ b/tests/signature/lib/common/src/android/signature/cts/ReflectionHelper.java
@@ -18,6 +18,7 @@
import android.signature.cts.JDiffClassDescription.JDiffConstructor;
import android.signature.cts.JDiffClassDescription.JDiffMethod;
import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
@@ -420,33 +421,24 @@
}
}
- /**
- * Returns a Class representing an annotation type of the given name.
- */
- @SuppressWarnings("unchecked")
- public static Class<? extends Annotation> getAnnotationClass(String name) {
- try {
- Class<?> clazz = Class.forName(
- name, false, ReflectionHelper.class.getClassLoader());
- if (clazz.isAnnotation()) {
- return (Class<? extends Annotation>) clazz;
- } else {
- return null;
+ public static boolean hasMatchingAnnotation(AnnotatedElement elem, String annotationSpec) {
+ for (Annotation a : elem.getAnnotations()) {
+ if (a.toString().equals(annotationSpec)) {
+ return true;
}
- } catch (ClassNotFoundException e) {
- return null;
}
+ return false;
}
/**
* Returns a list of constructors which are annotated with the given annotation class.
*/
public static Set<Constructor<?>> getAnnotatedConstructors(Class<?> clazz,
- Class<? extends Annotation> annotation) {
+ String annotationSpec) {
Set<Constructor<?>> result = new HashSet<>();
- if (annotation != null) {
+ if (annotationSpec != null) {
for (Constructor<?> c : clazz.getDeclaredConstructors()) {
- if (c.isAnnotationPresent(annotation)) {
+ if (hasMatchingAnnotation(c, annotationSpec)) {
// TODO(b/71630695): currently, some API members are not annotated, because
// a member is automatically added to the API set if it is in a class with
// annotation and it is not @hide. <member>.getDeclaringClass().
@@ -464,12 +456,11 @@
/**
* Returns a list of methods which are annotated with the given annotation class.
*/
- public static Set<Method> getAnnotatedMethods(Class<?> clazz,
- Class<? extends Annotation> annotation) {
+ public static Set<Method> getAnnotatedMethods(Class<?> clazz, String annotationSpec) {
Set<Method> result = new HashSet<>();
- if (annotation != null) {
+ if (annotationSpec != null) {
for (Method m : clazz.getDeclaredMethods()) {
- if (m.isAnnotationPresent(annotation)) {
+ if (hasMatchingAnnotation(m, annotationSpec)) {
// TODO(b/71630695): see getAnnotatedConstructors for details
result.add(m);
}
@@ -481,12 +472,11 @@
/**
* Returns a list of fields which are annotated with the given annotation class.
*/
- public static Set<Field> getAnnotatedFields(Class<?> clazz,
- Class<? extends Annotation> annotation) {
+ public static Set<Field> getAnnotatedFields(Class<?> clazz, String annotationSpec) {
Set<Field> result = new HashSet<>();
- if (annotation != null) {
+ if (annotationSpec != null) {
for (Field f : clazz.getDeclaredFields()) {
- if (f.isAnnotationPresent(annotation)) {
+ if (hasMatchingAnnotation(f, annotationSpec)) {
// TODO(b/71630695): see getAnnotatedConstructors for details
result.add(f);
}
@@ -495,46 +485,42 @@
return result;
}
- private static boolean isInAnnotatedClass(Member m,
- Class<? extends Annotation> annotationClass) {
+ private static boolean isInAnnotatedClass(Member m, String annotationSpec) {
Class<?> clazz = m.getDeclaringClass();
do {
- if (clazz.isAnnotationPresent(annotationClass)) {
+ if (hasMatchingAnnotation(clazz, annotationSpec)) {
return true;
}
} while ((clazz = clazz.getDeclaringClass()) != null);
return false;
}
- public static boolean isAnnotatedOrInAnnotatedClass(Field field,
- Class<? extends Annotation> annotationClass) {
- if (annotationClass == null) {
+ public static boolean isAnnotatedOrInAnnotatedClass(Field field, String annotationSpec) {
+ if (annotationSpec == null) {
return true;
}
- return field.isAnnotationPresent(annotationClass)
- || isInAnnotatedClass(field, annotationClass);
+ return hasMatchingAnnotation(field, annotationSpec)
+ || isInAnnotatedClass(field, annotationSpec);
}
public static boolean isAnnotatedOrInAnnotatedClass(Constructor<?> constructor,
- Class<? extends Annotation> annotationClass) {
- if (annotationClass == null) {
+ String annotationSpec) {
+ if (annotationSpec == null) {
return true;
}
- return constructor.isAnnotationPresent(annotationClass)
- || isInAnnotatedClass(constructor, annotationClass);
+ return hasMatchingAnnotation(constructor, annotationSpec)
+ || isInAnnotatedClass(constructor, annotationSpec);
}
- public static boolean isAnnotatedOrInAnnotatedClass(Method method,
- Class<? extends Annotation> annotationClass) {
- if (annotationClass == null) {
+ public static boolean isAnnotatedOrInAnnotatedClass(Method method, String annotationSpec) {
+ if (annotationSpec == null) {
return true;
}
- return method.isAnnotationPresent(annotationClass)
- || isInAnnotatedClass(method, annotationClass);
+ return hasMatchingAnnotation(method, annotationSpec)
+ || isInAnnotatedClass(method, annotationSpec);
}
- public static boolean isOverridingAnnotatedMethod(Method method,
- Class<? extends Annotation> annotationClass) {
+ public static boolean isOverridingAnnotatedMethod(Method method, String annotationSpec) {
Class<?> clazz = method.getDeclaringClass();
while (!(clazz = clazz.getSuperclass()).equals(Object.class)) {
try {
@@ -542,7 +528,7 @@
overriddenMethod = clazz.getDeclaredMethod(method.getName(),
method.getParameterTypes());
if (overriddenMethod != null) {
- return isAnnotatedOrInAnnotatedClass(overriddenMethod, annotationClass);
+ return isAnnotatedOrInAnnotatedClass(overriddenMethod, annotationSpec);
}
} catch (NoSuchMethodException e) {
continue;
diff --git a/tests/signature/tests/src/android/signature/cts/tests/AnnotationCheckerTest.java b/tests/signature/tests/src/android/signature/cts/tests/AnnotationCheckerTest.java
index 7682a50..90dada9 100644
--- a/tests/signature/tests/src/android/signature/cts/tests/AnnotationCheckerTest.java
+++ b/tests/signature/tests/src/android/signature/cts/tests/AnnotationCheckerTest.java
@@ -37,7 +37,8 @@
@Override
protected AnnotationChecker createChecker(ResultObserver resultObserver,
ClassProvider provider) {
- return new AnnotationChecker(resultObserver, provider, ApiAnnotation.class.getName());
+ return new AnnotationChecker(resultObserver, provider,
+ "@android.signature.cts.tests.data.ApiAnnotation()");
}
private static void addConstructor(JDiffClassDescription clz, String... paramTypes) {
diff --git a/tests/tests/app/src/android/app/cts/KeyguardInitialLockTest.java b/tests/tests/app/src/android/app/cts/KeyguardInitialLockTest.java
new file mode 100644
index 0000000..244621b
--- /dev/null
+++ b/tests/tests/app/src/android/app/cts/KeyguardInitialLockTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 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.app;
+
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.test.AndroidTestCase;
+import android.util.ArraySet;
+
+import java.util.List;
+
+public class KeyguardInitialLockTest extends AndroidTestCase {
+
+ public void testSetInitialLockPermission() {
+ final ArraySet<String> allowedPackages = new ArraySet();
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_SETUP_WIZARD);
+ PackageManager pm = getContext().getPackageManager();
+ final List<ResolveInfo> activities =
+ pm.queryIntentActivities(intent, PackageManager.MATCH_DISABLED_COMPONENTS);
+ String validPkg = "";
+ for (ResolveInfo ri : activities) {
+ if (ri != null) {
+ allowedPackages.add(ri.activityInfo.packageName);
+ validPkg = ri.activityInfo.packageName;
+ }
+ }
+ final List<PackageInfo> holding = pm.getPackagesHoldingPermissions(new String[]{
+ android.Manifest.permission.SET_INITIAL_LOCK
+ }, PackageManager.MATCH_UNINSTALLED_PACKAGES);
+ for (PackageInfo pi : holding) {
+ if (!allowedPackages.contains(pi.packageName)) {
+ fail("The SET_INITIAL_LOCK permission must not be held by " + pi.packageName
+ + " and must be revoked for security reasons [" + validPkg + "]");
+ }
+ }
+ }
+}
diff --git a/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java b/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java
index 8e243c9..81149f0 100644
--- a/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java
+++ b/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java
@@ -106,7 +106,6 @@
QUERIES_NOTHING_SHARED_USER
};
- private static Context sContext;
private static Handler sResponseHandler;
private static HandlerThread sResponseThread;
@@ -138,10 +137,6 @@
@Before
public void setupTest() {
if (!sGlobalFeatureEnabled) return;
-
- if (sContext == null) {
- sContext = InstrumentationRegistry.getInstrumentation().getContext();
- }
setFeatureEnabledForAll(true);
}
@@ -319,7 +314,7 @@
},
sResponseHandler);
intent.putExtra("remoteCallback", callback);
- sContext.startActivity(intent);
+ InstrumentationRegistry.getInstrumentation().getContext().startActivity(intent);
if (!latch.block(TimeUnit.SECONDS.toMillis(10))) {
throw new TimeoutException(
"Latch timed out while awiating a response from " + targetPackageName);
diff --git a/tests/tests/bionic/Android.build.copy.libs.mk b/tests/tests/bionic/Android.build.copy.libs.mk
index d933bbc..5867547 100644
--- a/tests/tests/bionic/Android.build.copy.libs.mk
+++ b/tests/tests/bionic/Android.build.copy.libs.mk
@@ -43,6 +43,9 @@
libdlext_test_runpath_zip/libdlext_test_runpath_zip_zipaligned.zip \
libdlext_test_zip/libdlext_test_zip.so \
libdlext_test_zip/libdlext_test_zip_zipaligned.zip \
+ libgnu-hash-table-library.so \
+ librelr-new.so \
+ librelr-old.so \
libsegment_gap_inner.so \
libsegment_gap_outer.so \
libsysv-hash-table-library.so \
@@ -88,6 +91,9 @@
libtest_elftls_shared_var_ie.so \
libtest_elftls_tprel.so \
libtest_empty.so \
+ libtest_ifunc.so \
+ libtest_ifunc_variable.so \
+ libtest_ifunc_variable_impl.so \
libtest_indirect_thread_local_dtor.so \
libtest_init_fini_order_child.so \
libtest_init_fini_order_grand_child.so \
@@ -150,13 +156,6 @@
public_namespace_libs/libtest_missing_symbol.so \
public_namespace_libs/libtest_missing_symbol_child_public.so \
-# These libraries are not built for mips.
-my_bionic_testlib_files_non_mips := \
- libgnu-hash-table-library.so \
- libtest_ifunc.so \
- libtest_ifunc_variable.so \
- libtest_ifunc_variable_impl.so \
-
my_bionic_testlibs_src_dir := \
$($(cts_bionic_tests_2nd_arch_prefix)TARGET_OUT_DATA_NATIVE_TESTS)/bionic-loader-test-libs
my_bionic_testlibs_out_dir := $(cts_bionic_tests_dir)/bionic-loader-test-libs
@@ -165,12 +164,6 @@
$(foreach lib, $(my_bionic_testlib_files), \
$(my_bionic_testlibs_src_dir)/$(lib):$(my_bionic_testlibs_out_dir)/$(lib))
-ifneq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),mips mips64))
-LOCAL_COMPATIBILITY_SUPPORT_FILES += \
- $(foreach lib, $(my_bionic_testlib_files_non_mips), \
- $(my_bionic_testlibs_src_dir)/$(lib):$(my_bionic_testlibs_out_dir)/$(lib))
-endif
-
# Special casing for libtest_dt_runpath_y.so. Since we use the standard ARM CTS
# to test ARM-on-x86 devices where ${LIB} is expanded to lib/arm, the lib
# is installed to ./lib/arm as well as ./lib to make sure that the lib can be
@@ -186,7 +179,6 @@
LOCAL_COMPATIBILITY_SUPPORT_FILES += $(src):$(dst)
my_bionic_testlib_files :=
-my_bionic_testlib_files_non_mips :=
my_bionic_testlibs_src_dir :=
my_bionic_testlibs_out_dir :=
cts_bionic_tests_dir :=
diff --git a/tests/tests/carrierapi/AndroidManifest.xml b/tests/tests/carrierapi/AndroidManifest.xml
index 978651f..52057ac 100644
--- a/tests/tests/carrierapi/AndroidManifest.xml
+++ b/tests/tests/carrierapi/AndroidManifest.xml
@@ -22,6 +22,7 @@
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/tests/tests/content/pm/OWNERS b/tests/tests/content/pm/OWNERS
new file mode 100644
index 0000000..941653d
--- /dev/null
+++ b/tests/tests/content/pm/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 36137
+patb@google.com
+toddke@google.com
+narayan@google.com
diff --git a/tests/tests/rcs/Android.bp b/tests/tests/content/pm/SecureFrp/Android.bp
similarity index 64%
rename from tests/tests/rcs/Android.bp
rename to tests/tests/content/pm/SecureFrp/Android.bp
index 5de3331..91abdc6 100644
--- a/tests/tests/rcs/Android.bp
+++ b/tests/tests/content/pm/SecureFrp/Android.bp
@@ -1,10 +1,10 @@
-// Copyright (C) 2019 The Android Open Source Project
+// Copyright (C) 2020 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
+// 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,
@@ -13,23 +13,23 @@
// limitations under the License.
android_test {
- name: "CtsRcsTestCases",
- defaults: ["cts_defaults"],
+ name: "CtsSecureFrpInstallTestCases",
+
+ srcs: ["src/**/*.java"],
+
static_libs: [
- "compatibility-device-util-axt",
"androidx.test.rules",
- "truth-prebuilt",
+ "compatibility-device-util-axt",
+ "ctstestrunner-axt",
+ "cts-install-lib",
],
- libs: [
- "android.test.runner.stubs",
- "android.test.base.stubs",
- ],
- srcs: ["src/**/*.java"],
- // Tag this module as a cts test artifact
+
+ sdk_version: "test_current",
+
test_suites: [
"cts",
"vts",
"general-tests",
+ "mts",
],
- platform_apis: true,
}
diff --git a/tests/tests/content/pm/SecureFrp/AndroidManifest.xml b/tests/tests/content/pm/SecureFrp/AndroidManifest.xml
new file mode 100644
index 0000000..f1b5184
--- /dev/null
+++ b/tests/tests/content/pm/SecureFrp/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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="com.android.tests.securefrpinstall" >
+
+ <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
+
+ <application>
+ <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
+ android:exported="true" />
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.tests.securefrpinstall"
+ android:label="AtomicInstall Test"/>
+
+</manifest>
diff --git a/tests/tests/content/pm/SecureFrp/AndroidTest.xml b/tests/tests/content/pm/SecureFrp/AndroidTest.xml
new file mode 100644
index 0000000..bd0ce82
--- /dev/null
+++ b/tests/tests/content/pm/SecureFrp/AndroidTest.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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="Runs the secure FRP install tests">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <!-- Instant apps can't install packages. -->
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+ <!-- target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="mkdir -p /data/local/tmp/cts/pm" />
+ <option name="teardown-command" value="rm -rf /data/local/tmp/cts"/>
+ </target_preparer -->
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsSecureFrpInstallTestCases.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
+ <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
+ </target_preparer>
+
+ <!-- target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="TestAppAv1.apk->/data/local/tmp/cts/pm/TestAppAv1.apk" />
+ </target_preparer -->
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.tests.securefrpinstall" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
diff --git a/tests/tests/content/pm/SecureFrp/TEST_MAPPING b/tests/tests/content/pm/SecureFrp/TEST_MAPPING
new file mode 100644
index 0000000..c87e54e
--- /dev/null
+++ b/tests/tests/content/pm/SecureFrp/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsSecureFrpInstallTestCases"
+ }
+ ]
+}
diff --git a/tests/tests/content/pm/SecureFrp/src/com/android/tests/securefrpinstall/SecureFrpInstallTest.java b/tests/tests/content/pm/SecureFrp/src/com/android/tests/securefrpinstall/SecureFrpInstallTest.java
new file mode 100644
index 0000000..2f1dfc7
--- /dev/null
+++ b/tests/tests/content/pm/SecureFrp/src/com/android/tests/securefrpinstall/SecureFrpInstallTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 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.tests.atomicinstall;
+
+import static org.junit.Assert.fail;
+
+import android.Manifest;
+import android.content.pm.PackageManager;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.SystemUtil;
+import com.android.cts.install.lib.Install;
+import com.android.cts.install.lib.TestApp;
+import com.android.cts.install.lib.Uninstall;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for package installation while Secure FRP mode is enabled. */
+@RunWith(JUnit4.class)
+public class SecureFrpInstallTest {
+
+ private static PackageManager sPackageManager;
+
+ private static void adoptShellPermissions() {
+ InstrumentationRegistry
+ .getInstrumentation()
+ .getUiAutomation()
+ .adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES, Manifest.permission.DELETE_PACKAGES);
+ }
+
+ private static void dropShellPermissions() {
+ InstrumentationRegistry
+ .getInstrumentation()
+ .getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+
+ private static void setSecureFrp(boolean secureFrp) throws Exception {
+ adoptShellPermissions();
+ SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
+ "settings put secure secure_frp_mode " + (secureFrp ? "1" : "0"));
+ dropShellPermissions();
+ }
+
+ private static void assertInstalled() throws Exception {
+ sPackageManager.getPackageInfo(TestApp.A, 0);
+ }
+
+ private static void assertNotInstalled() {
+ try {
+ sPackageManager.getPackageInfo(TestApp.A, 0);
+ fail("Package should not be installed");
+ } catch (PackageManager.NameNotFoundException expected) {
+ }
+ }
+
+ @BeforeClass
+ public static void initialize() {
+ sPackageManager = InstrumentationRegistry
+ .getInstrumentation()
+ .getContext()
+ .getPackageManager();
+ }
+
+ @Before
+ public void setup() throws Exception {
+ adoptShellPermissions();
+ Uninstall.packages(TestApp.A);
+ dropShellPermissions();
+ }
+
+ @After
+ public void teardown() throws Exception {
+ dropShellPermissions();
+ setSecureFrp(false);
+ }
+
+ /** Tests a SecurityException is thrown while in secure FRP mode. */
+ @Test
+ public void testPackageInstallApi() throws Exception {
+ setSecureFrp(true);
+ try {
+ Install.single(TestApp.A1).commit();
+ fail("Expected a SecurityException");
+ } catch (SecurityException expected) {
+ }
+ assertNotInstalled();
+ }
+
+ /** Tests can install when granted INSTALL_PACKAGES permission; even in secure FRP mode. */
+ @Test
+ public void testPackageInstallApiAsShell() throws Exception {
+ setSecureFrp(true);
+ adoptShellPermissions();
+ Install.single(TestApp.A1).commit();
+ dropShellPermissions();
+ assertInstalled();
+ }
+}
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
index 00e6b32..972994f 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
@@ -42,6 +42,7 @@
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Optional;
+import java.util.Random;
import java.util.stream.Collectors;
@RunWith(Parameterized.class)
@@ -148,12 +149,35 @@
}
@Test
+ public void testAppInstallErr() throws Exception {
+ if (!mStreaming) {
+ return;
+ }
+ File file = new File(createApkPath(TEST_HW5));
+ String command = "pm " + mInstall + " -t -g " + file.getPath() + (new Random()).nextLong();
+ String commandResult = executeShellCommand(command);
+ assertEquals("Failure [INSTALL_FAILED_MEDIA_UNAVAILABLE: Failed to prepare image.]\n",
+ commandResult);
+ assertFalse(isAppInstalled(TEST_APP_PACKAGE));
+ }
+
+ @Test
public void testAppInstallStdIn() throws Exception {
installPackageStdIn(TEST_HW5);
assertTrue(isAppInstalled(TEST_APP_PACKAGE));
}
@Test
+ public void testAppInstallStdInErr() throws Exception {
+ File file = new File(createApkPath(TEST_HW5));
+ String commandResult = executeShellCommand("pm " + mInstall + " -t -g -S " + file.length(),
+ new File[]{});
+ assertTrue(commandResult,
+ commandResult.startsWith("Failure [INSTALL_PARSE_FAILED_NOT_APK"));
+ assertFalse(isAppInstalled(TEST_APP_PACKAGE));
+ }
+
+ @Test
public void testAppUpdate() throws Exception {
installPackage(TEST_HW5);
assertTrue(isAppInstalled(TEST_APP_PACKAGE));
diff --git a/tests/tests/graphics/AndroidManifest.xml b/tests/tests/graphics/AndroidManifest.xml
index 82abb58..98c392a 100644
--- a/tests/tests/graphics/AndroidManifest.xml
+++ b/tests/tests/graphics/AndroidManifest.xml
@@ -29,6 +29,14 @@
android:label="CameraGpuCtsActivity">
</activity>
+ <activity android:name="android.graphics.cts.FrameRateCtsActivity"
+ android:label="FrameRateCtsActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
<activity android:name="android.graphics.cts.ImageViewCtsActivity"
android:label="ImageViewCtsActivity">
<intent-filter>
diff --git a/tests/tests/graphics/jni/Android.bp b/tests/tests/graphics/jni/Android.bp
index 0a302de..87105a2 100644
--- a/tests/tests/graphics/jni/Android.bp
+++ b/tests/tests/graphics/jni/Android.bp
@@ -22,6 +22,7 @@
"android_graphics_cts_ASurfaceTextureTest.cpp",
"android_graphics_cts_BasicVulkanGpuTest.cpp",
"android_graphics_cts_BitmapTest.cpp",
+ "android_graphics_cts_FrameRateCtsActivity.cpp",
"android_graphics_cts_SyncTest.cpp",
"android_graphics_cts_CameraGpuCtsActivity.cpp",
"android_graphics_cts_CameraVulkanGpuTest.cpp",
diff --git a/tests/tests/graphics/jni/CtsGraphicsJniOnLoad.cpp b/tests/tests/graphics/jni/CtsGraphicsJniOnLoad.cpp
index 1d7dd89..050f403 100644
--- a/tests/tests/graphics/jni/CtsGraphicsJniOnLoad.cpp
+++ b/tests/tests/graphics/jni/CtsGraphicsJniOnLoad.cpp
@@ -24,6 +24,7 @@
extern int register_android_graphics_cts_BitmapTest(JNIEnv*);
extern int register_android_graphics_cts_CameraGpuCtsActivity(JNIEnv*);
extern int register_android_graphics_cts_CameraVulkanGpuTest(JNIEnv*);
+extern int register_android_graphics_cts_FrameRateCtsActivity(JNIEnv*);
extern int register_android_graphics_cts_MediaVulkanGpuTest(JNIEnv*);
extern int register_android_graphics_cts_VulkanFeaturesTest(JNIEnv*);
extern int register_android_graphics_cts_VulkanPreTransformCtsActivity(JNIEnv*);
@@ -46,6 +47,8 @@
return JNI_ERR;
if (register_android_graphics_cts_CameraVulkanGpuTest(env))
return JNI_ERR;
+ if (register_android_graphics_cts_FrameRateCtsActivity(env))
+ return JNI_ERR;
if (register_android_graphics_cts_MediaVulkanGpuTest(env))
return JNI_ERR;
if (register_android_graphics_cts_VulkanFeaturesTest(env))
diff --git a/tests/tests/graphics/jni/android_graphics_cts_AImageDecoderTest.cpp b/tests/tests/graphics/jni/android_graphics_cts_AImageDecoderTest.cpp
index 01096c4..637cecb 100644
--- a/tests/tests/graphics/jni/android_graphics_cts_AImageDecoderTest.cpp
+++ b/tests/tests/graphics/jni/android_graphics_cts_AImageDecoderTest.cpp
@@ -34,6 +34,7 @@
#include <memory>
#include <stdio.h>
#include <unistd.h>
+#include <vector>
#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
@@ -146,7 +147,6 @@
ASSERT_EQ(0, AImageDecoderHeaderInfo_getWidth(nullptr));
ASSERT_EQ(0, AImageDecoderHeaderInfo_getHeight(nullptr));
ASSERT_EQ(nullptr, AImageDecoderHeaderInfo_getMimeType(nullptr));
- ASSERT_EQ(false, AImageDecoderHeaderInfo_isAnimated(nullptr));
ASSERT_EQ(ANDROID_IMAGE_DECODER_BAD_PARAMETER, AImageDecoderHeaderInfo_getDataSpace(nullptr));
{
@@ -169,7 +169,7 @@
}
static void testInfo(JNIEnv* env, jclass, jlong imageDecoderPtr, jint width, jint height,
- jstring jMimeType, jboolean isAnimated, jboolean isF16, jint dataSpace) {
+ jstring jMimeType, jboolean isF16, jint dataSpace) {
AImageDecoder* decoder = reinterpret_cast<AImageDecoder*>(imageDecoderPtr);
ASSERT_NE(decoder, nullptr);
DecoderDeleter decoderDeleter(decoder, AImageDecoder_delete);
@@ -185,7 +185,6 @@
ASSERT_NE(mimeType, nullptr);
ASSERT_EQ(0, strcmp(mimeType, AImageDecoderHeaderInfo_getMimeType(info)));
env->ReleaseStringUTFChars(jMimeType, mimeType);
- ASSERT_EQ(isAnimated, AImageDecoderHeaderInfo_isAnimated(info));
auto format = AImageDecoderHeaderInfo_getAndroidBitmapFormat(info);
if (isF16) {
ASSERT_EQ(ANDROID_BITMAP_FORMAT_RGBA_F16, format);
@@ -366,7 +365,7 @@
}
}
-static int bytesPerPixel(AndroidBitmapFormat format) {
+static int bytesPerPixel(int32_t format) {
switch (format) {
case ANDROID_BITMAP_FORMAT_RGBA_8888:
return 4;
@@ -378,6 +377,7 @@
return 8;
case ANDROID_BITMAP_FORMAT_NONE:
case ANDROID_BITMAP_FORMAT_RGBA_4444:
+ default:
return 0;
}
}
@@ -455,14 +455,14 @@
return false; \
}
-static bool bitmapsEqual(JNIEnv* env, jobject jbitmap, AndroidBitmapFormat format,
+static bool bitmapsEqual(JNIEnv* env, jobject jbitmap, int32_t androidBitmapFormat,
int width, int height, int alphaFlags, size_t minStride,
void* pixelsA, size_t strideA) {
AndroidBitmapInfo jInfo;
int bitmapResult = AndroidBitmap_getInfo(env, jbitmap, &jInfo);
EXPECT_EQ("Failed to getInfo on Bitmap", ANDROID_BITMAP_RESULT_SUCCESS, bitmapResult);
- EXPECT_EQ("Wrong format", jInfo.format, format);
+ EXPECT_EQ("Wrong format", jInfo.format, androidBitmapFormat);
// If the image is truly opaque, the Java Bitmap will report OPAQUE, even if
// the AImageDecoder requested PREMUL/UNPREMUL. In that case, it is okay for
@@ -613,26 +613,62 @@
ASSERT_NE(info, nullptr);
const int height = AImageDecoderHeaderInfo_getHeight(info);
- size_t minStride = AImageDecoder_getMinimumStride(decoder);
+ const int origWidth = AImageDecoderHeaderInfo_getWidth(info);
- void* pixels = nullptr;
-
- // The code in this loop relies on minStride being used first.
- for (size_t stride : { minStride, minStride * 2, minStride * 3 }) {
- size_t size = stride * (height - 1) + minStride;
- void* decodePixels = malloc(size);
- int result = AImageDecoder_decodeImage(decoder, decodePixels, stride, size);
+ for (int width : { origWidth, origWidth / 3 }) {
+ if (width == 0) {
+ // The 1 x 1 image cannot be downscaled.
+ continue;
+ }
+ int result = AImageDecoder_setTargetSize(decoder, width, height);
ASSERT_EQ(ANDROID_IMAGE_DECODER_SUCCESS, result);
+ for (AndroidBitmapFormat format : {
+ ANDROID_BITMAP_FORMAT_RGBA_8888,
+ ANDROID_BITMAP_FORMAT_RGB_565,
+ ANDROID_BITMAP_FORMAT_A_8,
+ ANDROID_BITMAP_FORMAT_RGBA_F16,
+ }) {
+ result = AImageDecoder_setAndroidBitmapFormat(decoder, format);
+ if (result != ANDROID_IMAGE_DECODER_SUCCESS) {
+ // Not all images can be decoded to all formats. This is okay, and
+ // we've tested that we can decode to the expected formats elsewhere.
+ continue;
+ }
- if (pixels == nullptr) {
- pixels = decodePixels;
- } else {
- ASSERT_TRUE(bitmapsEqual(minStride, height, pixels, minStride, decodePixels, stride));
- free(decodePixels);
+ size_t minStride = AImageDecoder_getMinimumStride(decoder);
+ void* pixels = nullptr;
+
+ // The code in the below loop relies on minStride being used first.
+ std::vector<size_t> strides;
+ strides.push_back(minStride);
+ for (int i = 1; i <= 16; i++) {
+ strides.push_back(i + minStride);
+ }
+ strides.push_back(minStride * 2);
+ strides.push_back(minStride * 3);
+ for (size_t stride : strides) {
+ size_t size = stride * (height - 1) + minStride;
+ void* decodePixels = malloc(size);
+ result = AImageDecoder_decodeImage(decoder, decodePixels, stride, size);
+ if ((stride - minStride) % bytesPerPixel(format) != 0) {
+ // The stride is not pixel aligned.
+ ASSERT_EQ(ANDROID_IMAGE_DECODER_BAD_PARAMETER, result);
+ continue;
+ }
+ ASSERT_EQ(ANDROID_IMAGE_DECODER_SUCCESS, result);
+
+ if (pixels == nullptr) {
+ pixels = decodePixels;
+ } else {
+ ASSERT_TRUE(bitmapsEqual(minStride, height, pixels, minStride, decodePixels,
+ stride));
+ free(decodePixels);
+ }
+ }
+
+ free(pixels);
}
}
-
- free(pixels);
}
static void testSetTargetSize(JNIEnv* env, jclass, jlong imageDecoderPtr) {
@@ -641,14 +677,14 @@
const size_t defaultStride = AImageDecoder_getMinimumStride(decoder);
- for (int width : { -1, 0, -500 }) {
+ for (int32_t width : { -1, 0, -500 }) {
int result = AImageDecoder_setTargetSize(decoder, width, 100);
ASSERT_EQ(ANDROID_IMAGE_DECODER_INVALID_SCALE, result);
// stride is unchanged, as the target size did not change.
ASSERT_EQ(defaultStride, AImageDecoder_getMinimumStride(decoder));
}
- for (int height : { -1, 0, -300 }) {
+ for (int32_t height : { -1, 0, -300 }) {
int result = AImageDecoder_setTargetSize(decoder, 100, height);
ASSERT_EQ(ANDROID_IMAGE_DECODER_INVALID_SCALE, result);
// stride is unchanged, as the target size did not change.
@@ -659,7 +695,7 @@
ASSERT_NE(info, nullptr);
const int bpp = bytesPerPixel(AImageDecoderHeaderInfo_getAndroidBitmapFormat(info));
- for (int width : { 7, 100, 275, 300 }) {
+ for (int32_t width : { 7, 100, 275, 300 }) {
int result = AImageDecoder_setTargetSize(decoder, width, 100);
ASSERT_EQ(ANDROID_IMAGE_DECODER_SUCCESS, result);
@@ -706,8 +742,8 @@
struct SampledSizeParams {
AImageDecoder* decoder;
int sampleSize;
- int* width;
- int* height;
+ int32_t* width;
+ int32_t* height;
};
static void testComputeSampledSize(JNIEnv* env, jclass, jlong imageDecoderPtr,
@@ -720,7 +756,7 @@
const int32_t origWidth = AImageDecoderHeaderInfo_getWidth(info);
const int32_t origHeight = AImageDecoderHeaderInfo_getHeight(info);
- int width, height;
+ int32_t width, height;
// Test some bad parameters.
for (SampledSizeParams p : std::initializer_list<SampledSizeParams>{
{ nullptr, 2, &width, &height },
@@ -865,7 +901,7 @@
const int32_t width = AImageDecoderHeaderInfo_getWidth(info);
const int32_t height = AImageDecoderHeaderInfo_getHeight(info);
- const AndroidBitmapFormat format = AImageDecoderHeaderInfo_getAndroidBitmapFormat(info);
+ const auto format = AImageDecoderHeaderInfo_getAndroidBitmapFormat(info);
const size_t defaultStride = AImageDecoder_getMinimumStride(decoder);
if (width == 1 && height == 1) {
@@ -930,7 +966,7 @@
ASSERT_EQ(ANDROID_IMAGE_DECODER_SUCCESS, result);
ASSERT_EQ(defaultStride, AImageDecoder_getMinimumStride(decoder));
- int newWidth = width / 2, newHeight = height / 2;
+ int32_t newWidth = width / 2, newHeight = height / 2;
result = AImageDecoder_setTargetSize(decoder, newWidth, newHeight);
ASSERT_EQ(ANDROID_IMAGE_DECODER_SUCCESS, result);
const size_t halfStride = AImageDecoder_getMinimumStride(decoder);
@@ -1182,7 +1218,7 @@
static JNINativeMethod gMethods[] = {
{ "nTestEmptyCreate", "()V", (void*) testEmptyCreate },
{ "nTestNullDecoder", "(" ASSET_MANAGER STRING ")V", (void*) testNullDecoder },
- { "nTestInfo", "(JII" STRING "ZZI)V", (void*) testInfo },
+ { "nTestInfo", "(JII" STRING "ZI)V", (void*) testInfo },
{ "nOpenAsset", "(" ASSET_MANAGER STRING ")J", (void*) openAssetNative },
{ "nCloseAsset", "(J)V", (void*) closeAsset },
{ "nCreateFromAsset", "(J)J", (void*) createFromAsset },
diff --git a/tests/tests/graphics/jni/android_graphics_cts_FrameRateCtsActivity.cpp b/tests/tests/graphics/jni/android_graphics_cts_FrameRateCtsActivity.cpp
new file mode 100644
index 0000000..1361e3d
--- /dev/null
+++ b/tests/tests/graphics/jni/android_graphics_cts_FrameRateCtsActivity.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2020 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.
+ *
+ */
+
+#define LOG_TAG "FrameRateCtsActivity"
+
+#include <android/native_window.h>
+#include <android/native_window_jni.h>
+#include <android/surface_control.h>
+#include <jni.h>
+
+#include <array>
+
+namespace {
+
+jint nativeWindowSetFrameRate(JNIEnv* env, jclass, jobject jSurface, jfloat frameRate) {
+ ANativeWindow* window = nullptr;
+ if (jSurface) {
+ window = ANativeWindow_fromSurface(env, jSurface);
+ }
+ return ANativeWindow_setFrameRate(window, frameRate);
+}
+
+jlong surfaceControlCreate(JNIEnv* env, jclass, jobject jParentSurface) {
+ ANativeWindow* window = nullptr;
+ if (jParentSurface) {
+ window = ANativeWindow_fromSurface(env, jParentSurface);
+ }
+ if (!window) {
+ return 0;
+ }
+ return reinterpret_cast<jlong>(
+ ASurfaceControl_createFromWindow(window, "SetFrameRateTestSurface"));
+}
+
+void surfaceControlDestroy(JNIEnv*, jclass, jlong surfaceControlLong) {
+ if (surfaceControlLong == 0) {
+ return;
+ }
+ ASurfaceControl* surfaceControl = reinterpret_cast<ASurfaceControl*>(surfaceControlLong);
+ ASurfaceTransaction* transaction = ASurfaceTransaction_create();
+ ASurfaceTransaction_reparent(transaction, surfaceControl, nullptr);
+ ASurfaceTransaction_apply(transaction);
+ ASurfaceTransaction_delete(transaction);
+ ASurfaceControl_release(surfaceControl);
+}
+
+void surfaceControlSetFrameRate(JNIEnv*, jclass, jlong surfaceControlLong, jfloat frameRate) {
+ ASurfaceControl* surfaceControl = reinterpret_cast<ASurfaceControl*>(surfaceControlLong);
+ ASurfaceTransaction* transaction = ASurfaceTransaction_create();
+ ASurfaceTransaction_setFrameRate(transaction, surfaceControl, frameRate);
+ ASurfaceTransaction_apply(transaction);
+ ASurfaceTransaction_delete(transaction);
+}
+
+const std::array<JNINativeMethod, 4> JNI_METHODS = {{
+ {"nativeWindowSetFrameRate", "(Landroid/view/Surface;F)I", (void*)nativeWindowSetFrameRate},
+ {"nativeSurfaceControlCreate", "(Landroid/view/Surface;)J", (void*)surfaceControlCreate},
+ {"nativeSurfaceControlDestroy", "(J)V", (void*)surfaceControlDestroy},
+ {"nativeSurfaceControlSetFrameRate", "(JF)V", (void*)surfaceControlSetFrameRate},
+}};
+
+} // namespace
+
+int register_android_graphics_cts_FrameRateCtsActivity(JNIEnv* env) {
+ jclass clazz = env->FindClass("android/graphics/cts/FrameRateCtsActivity");
+ return env->RegisterNatives(clazz, JNI_METHODS.data(), JNI_METHODS.size());
+}
diff --git a/tests/tests/graphics/src/android/graphics/cts/AImageDecoderTest.java b/tests/tests/graphics/src/android/graphics/cts/AImageDecoderTest.java
index bebed43..df11998 100644
--- a/tests/tests/graphics/src/android/graphics/cts/AImageDecoderTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/AImageDecoderTest.java
@@ -93,20 +93,20 @@
// For testing all of the assets as premul and unpremul.
private static Object[] getAssetRecordsUnpremul() {
- return ImageDecoderTest.crossProduct(getAssetRecords(), new Object[] { true, false });
+ return Utils.crossProduct(getAssetRecords(), new Object[] { true, false });
}
private static Object[] getRecordsUnpremul() {
- return ImageDecoderTest.crossProduct(getRecords(), new Object[] { true, false });
+ return Utils.crossProduct(getRecords(), new Object[] { true, false });
}
// For testing all of the assets at different sample sizes.
private static Object[] getAssetRecordsSample() {
- return ImageDecoderTest.crossProduct(getAssetRecords(), new Object[] { 2, 3, 4, 8, 16 });
+ return Utils.crossProduct(getAssetRecords(), new Object[] { 2, 3, 4, 8, 16 });
}
private static Object[] getRecordsSample() {
- return ImageDecoderTest.crossProduct(getRecords(), new Object[] { 2, 3, 4, 8, 16 });
+ return Utils.crossProduct(getRecords(), new Object[] { 2, 3, 4, 8, 16 });
}
@Test
@@ -131,8 +131,7 @@
long aimagedecoder = nCreateFromAssetBuffer(asset);
nTestInfo(aimagedecoder, record.width, record.height, "image/png",
- false /* isAnimated */, record.isF16,
- nativeDataSpace(record.getColorSpace()));
+ record.isF16, nativeDataSpace(record.getColorSpace()));
nCloseAsset(asset);
}
@@ -145,8 +144,7 @@
long aimagedecoder = nCreateFromAssetFd(asset);
nTestInfo(aimagedecoder, record.width, record.height, "image/png",
- false /* isAnimated */, record.isF16,
- nativeDataSpace(record.getColorSpace()));
+ record.isF16, nativeDataSpace(record.getColorSpace()));
nCloseAsset(asset);
}
@@ -157,8 +155,7 @@
long aimagedecoder = nCreateFromAsset(asset);
nTestInfo(aimagedecoder, record.width, record.height, "image/png",
- false /* isAnimated */, record.isF16,
- nativeDataSpace(record.getColorSpace()));
+ record.isF16, nativeDataSpace(record.getColorSpace()));
nCloseAsset(asset);
}
@@ -183,8 +180,7 @@
long aimagedecoder = nCreateFromFd(pfd.getFd());
nTestInfo(aimagedecoder, record.width, record.height, record.mimeType,
- false /* isAnimated */, false /*isF16*/,
- nativeDataSpace(record.colorSpace));
+ false /*isF16*/, nativeDataSpace(record.colorSpace));
} catch (FileNotFoundException e) {
fail("Could not open " + Utils.getAsResourceUri(record.resId));
}
@@ -201,8 +197,7 @@
long aimagedecoder = nCreateFromFd(pfd.getFd());
nTestInfo(aimagedecoder, record.width, record.height, record.mimeType,
- false /* isAnimated */, false /*isF16*/,
- nativeDataSpace(record.colorSpace));
+ false /*isF16*/, nativeDataSpace(record.colorSpace));
} catch (FileNotFoundException e) {
fail("Could not open " + Utils.getAsResourceUri(record.resId));
} catch (ErrnoException err) {
@@ -920,7 +915,7 @@
}
private static Object[] rgbColorSpacesAndCompressFormats() {
- return ImageDecoderTest.crossProduct(rgbColorSpaces(), Bitmap.CompressFormat.values());
+ return Utils.crossProduct(rgbColorSpaces(), Bitmap.CompressFormat.values());
}
String toMimeType(Bitmap.CompressFormat format) {
@@ -959,7 +954,7 @@
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file,
ParcelFileDescriptor.MODE_READ_ONLY)) {
long aimagedecoder = nCreateFromFd(pfd.getFd());
- nTestInfo(aimagedecoder, width, height, toMimeType(format), false, false, dataSpace);
+ nTestInfo(aimagedecoder, width, height, toMimeType(format), false, dataSpace);
} catch (IOException e) {
e.printStackTrace();
fail("Could not read " + file);
@@ -1023,7 +1018,7 @@
// For convenience, all methods that take aimagedecoder as a parameter delete
// it.
private static native void nTestInfo(long aimagedecoder, int width, int height,
- String mimeType, boolean isAnimated, boolean isF16, int dataspace);
+ String mimeType, boolean isF16, int dataspace);
private static native void nTestSetFormat(long aimagedecoder, boolean isF16, boolean isGray);
private static native void nTestSetUnpremul(long aimagedecoder, boolean hasAlpha);
private static native void nTestGetMinimumStride(long aimagedecoder,
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java
index 93d3d8d..5603b9d 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java
@@ -1038,22 +1038,37 @@
assertTrue(pass);
}
- private Object[] compressFormats() {
- return Bitmap.CompressFormat.values();
+ private Object[] compressFormatsAndColorSpaces() {
+ return Utils.crossProduct(Bitmap.CompressFormat.values(),
+ BitmapTest.getRgbColorSpaces().toArray());
}
@Test
- @Parameters(method = "compressFormats")
- public void testEncodeP3(Bitmap.CompressFormat format) {
+ @Parameters(method = "compressFormatsAndColorSpaces")
+ public void testEncodeColorSpace(Bitmap.CompressFormat format, ColorSpace colorSpace) {
Bitmap b = null;
+ ColorSpace decodedColorSpace = null;
ImageDecoder.Source src = ImageDecoder.createSource(mResources.getAssets(),
"blue-16bit-srgb.png");
try {
b = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ decoder.setTargetColorSpace(colorSpace);
});
assertNotNull(b);
assertEquals(Bitmap.Config.RGBA_F16, b.getConfig());
+ decodedColorSpace = b.getColorSpace();
+
+ // Requesting a ColorSpace with an EXTENDED variant will use the EXTENDED one because
+ // the image is 16-bit.
+ if (colorSpace == ColorSpace.get(ColorSpace.Named.SRGB)) {
+ assertSame(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB), decodedColorSpace);
+ } else if (colorSpace == ColorSpace.get(ColorSpace.Named.LINEAR_SRGB)) {
+ assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB),
+ decodedColorSpace);
+ } else {
+ assertSame(colorSpace, decodedColorSpace);
+ }
} catch (IOException e) {
fail("Failed with " + e);
}
@@ -1065,9 +1080,28 @@
src = ImageDecoder.createSource(ByteBuffer.wrap(array));
try {
- Bitmap b2 = ImageDecoder.decodeBitmap(src);
- assertEquals("Wrong color space for " + format,
- ColorSpace.get(ColorSpace.Named.DISPLAY_P3), b2.getColorSpace());
+ Bitmap b2 = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
+ ColorSpace encodedColorSpace = b2.getColorSpace();
+ if (format == Bitmap.CompressFormat.PNG) {
+ assertEquals(Bitmap.Config.RGBA_F16, b2.getConfig());
+ assertSame(decodedColorSpace, encodedColorSpace);
+ } else {
+ // Compressing to the other formats does not support creating a compressed version
+ // that we will decode to F16.
+ assertEquals(Bitmap.Config.ARGB_8888, b2.getConfig());
+
+ // Decoding an EXTENDED variant to 8888 results in the non-extended variant.
+ if (decodedColorSpace == ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)) {
+ assertSame(ColorSpace.get(ColorSpace.Named.SRGB), encodedColorSpace);
+ } else if (decodedColorSpace
+ == ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB)) {
+ assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_SRGB), encodedColorSpace);
+ } else {
+ assertSame(decodedColorSpace, encodedColorSpace);
+ }
+ }
} catch (IOException e) {
fail("Failed with " + e);
}
diff --git a/tests/tests/graphics/src/android/graphics/cts/FrameRateCtsActivity.java b/tests/tests/graphics/src/android/graphics/cts/FrameRateCtsActivity.java
new file mode 100644
index 0000000..37a91a4
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/cts/FrameRateCtsActivity.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2020 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.graphics.cts;
+
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.hardware.display.DisplayManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.view.Display;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * An Activity to help with frame rate testing.
+ */
+public class FrameRateCtsActivity extends Activity {
+ static {
+ System.loadLibrary("ctsgraphics_jni");
+ }
+
+ private static String TAG = "FrameRateCtsActivity";
+ private static final long WAIT_FOR_SURFACE_TIMEOUT_SECONDS = 3;
+ private static final long FRAME_RATE_SWITCH_GRACE_PERIOD_SECONDS = 1;
+ private static final long STABLE_FRAME_RATE_WAIT_SECONDS = 1;
+
+ private DisplayManager mDisplayManager;
+ private SurfaceView mSurfaceView;
+ private Handler mHandler = new Handler(Looper.getMainLooper());
+ private Object mLock = new Object();
+ private Surface mSurface = null;
+ private float mDeviceFrameRate;
+ private ArrayList<Float> mFrameRateChangedEvents = new ArrayList<Float>();
+
+ SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ synchronized (mLock) {
+ mSurface = holder.getSurface();
+ mLock.notify();
+ }
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ synchronized (mLock) {
+ mSurface = null;
+ mLock.notify();
+ }
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
+ };
+
+ DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {}
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ return;
+ }
+ synchronized (mLock) {
+ float frameRate = mDisplayManager.getDisplay(displayId).getMode().getRefreshRate();
+ if (frameRate != mDeviceFrameRate) {
+ Log.i(TAG,
+ String.format("Frame rate changed: %.0f --> %.0f", mDeviceFrameRate,
+ frameRate));
+ mDeviceFrameRate = frameRate;
+ mFrameRateChangedEvents.add(frameRate);
+ mLock.notify();
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {}
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ synchronized (mLock) {
+ mDisplayManager = (DisplayManager) getSystemService(DISPLAY_SERVICE);
+ mDeviceFrameRate =
+ mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getMode().getRefreshRate();
+ mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
+ mSurfaceView = new SurfaceView(this);
+ mSurfaceView.setWillNotDraw(false);
+ mSurfaceView.setZOrderOnTop(true);
+ setContentView(mSurfaceView,
+ new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ mSurfaceView.getHolder().addCallback(mSurfaceHolderCallback);
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ }
+
+ private ArrayList<Float> getFrameRatesToTest() {
+ Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ Display.Mode[] modes = display.getSupportedModes();
+ Display.Mode currentMode = display.getMode();
+ ArrayList<Float> frameRates = new ArrayList<Float>();
+ for (Display.Mode mode : modes) {
+ if (mode.getPhysicalWidth() == currentMode.getPhysicalWidth()
+ && mode.getPhysicalHeight() == currentMode.getPhysicalHeight()) {
+ frameRates.add(mode.getRefreshRate());
+ }
+ }
+ Collections.sort(frameRates);
+ ArrayList<Float> uniqueFrameRates = new ArrayList<Float>();
+ for (float frameRate : frameRates) {
+ if (uniqueFrameRates.isEmpty()
+ || frameRate - uniqueFrameRates.get(uniqueFrameRates.size() - 1) >= 1.0f) {
+ uniqueFrameRates.add(frameRate);
+ }
+ }
+ return uniqueFrameRates;
+ }
+
+ private void waitForSurface() throws InterruptedException {
+ if (mSurface == null) {
+ Log.i(TAG, "Waiting for surface");
+ }
+ long nowNanos = System.nanoTime();
+ long endTimeNanos = nowNanos + WAIT_FOR_SURFACE_TIMEOUT_SECONDS * 1_000_000_000L;
+ while (mSurface == null) {
+ long timeRemainingMillis = (endTimeNanos - nowNanos) / 1_000_000;
+ assertTrue("Never got a surface", timeRemainingMillis > 0);
+ mLock.wait(timeRemainingMillis);
+ nowNanos = System.nanoTime();
+ }
+ }
+
+ private boolean isDeviceFrameRateCompatibleWithAppRequest(
+ float deviceFrameRate, float appRequestedFrameRate) {
+ float multiple = deviceFrameRate / appRequestedFrameRate;
+ int roundedMultiple = Math.round(multiple);
+ return roundedMultiple > 0
+ && Math.abs(roundedMultiple * appRequestedFrameRate - deviceFrameRate) <= 0.1f;
+ }
+
+ private void postBuffer() {
+ Canvas canvas = mSurfaceView.getHolder().lockHardwareCanvas();
+ canvas.drawColor(Color.BLUE);
+ mSurfaceView.getHolder().unlockCanvasAndPost(canvas);
+ }
+
+ private void setFrameRateSurface(float frameRate) {
+ Log.i(TAG, String.format("Setting frame rate to %.0f", frameRate));
+ mSurface.setFrameRate(frameRate);
+ postBuffer();
+ }
+
+ private void setFrameRateANativeWindow(float frameRate) {
+ Log.i(TAG, String.format("Setting frame rate to %.0f", frameRate));
+ nativeWindowSetFrameRate(mSurface, frameRate);
+ postBuffer();
+ }
+
+ private void setFrameRateSurfaceControl(float frameRate) {
+ Log.i(TAG, String.format("Setting frame rate to %.0f", frameRate));
+ SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+ transaction.setFrameRate(mSurfaceView.getSurfaceControl(), frameRate).apply();
+ transaction.close();
+ postBuffer();
+ }
+
+ private void setFrameRateNativeSurfaceControl(long surfaceControl, float frameRate) {
+ Log.i(TAG, String.format("Setting frame rate to %.0f", frameRate));
+ nativeSurfaceControlSetFrameRate(surfaceControl, frameRate);
+ }
+
+ private void verifyCompatibleAndStableFrameRate(float appRequestedFrameRate)
+ throws InterruptedException {
+ Log.i(TAG, "Verifying compatible and stable frame rate");
+ long nowNanos = System.nanoTime();
+ long gracePeriodEndTimeNanos =
+ nowNanos + FRAME_RATE_SWITCH_GRACE_PERIOD_SECONDS * 1_000_000_000L;
+ while (true) {
+ // Wait until we switch to a compatible frame rate.
+ while (!isDeviceFrameRateCompatibleWithAppRequest(
+ mDeviceFrameRate, appRequestedFrameRate)
+ && gracePeriodEndTimeNanos > nowNanos) {
+ mLock.wait((gracePeriodEndTimeNanos - nowNanos) / 1_000_000);
+ nowNanos = System.nanoTime();
+ assertTrue("Lost the surface", mSurface != null);
+ }
+
+ // TODO(b/148033900): Remove the if and error log below, and replace it with this
+ // assertTrue() call, once we have a way to ignore display manager policy.
+ //
+ // assertTrue(
+ // String.format(
+ // "Timed out waiting for a stable and compatible frame rate."
+ // + " requested=%.0f current=%.0f.",
+ // appRequestedFrameRate, mDeviceFrameRate),
+ // gracePeriodEndTimeNanos > nowNanos);
+ if (nowNanos >= gracePeriodEndTimeNanos) {
+ Log.e(TAG,
+ String.format(
+ "Timed out waiting for a stable and compatible frame rate."
+ + " requested=%.0f current=%.0f.",
+ appRequestedFrameRate, mDeviceFrameRate));
+ return;
+ }
+
+ // We've switched to a compatible frame rate. Now wait for a while to see if we stay at
+ // that frame rate.
+ long endTimeNanos = nowNanos + STABLE_FRAME_RATE_WAIT_SECONDS * 1_000_000_000L;
+ while (endTimeNanos > nowNanos) {
+ mFrameRateChangedEvents.clear();
+ mLock.wait((endTimeNanos - nowNanos) / 1_000_000);
+ nowNanos = System.nanoTime();
+ assertTrue("Lost the surface", mSurface != null);
+ if (!mFrameRateChangedEvents.isEmpty()) {
+ break;
+ }
+ if (nowNanos >= endTimeNanos) {
+ Log.i(TAG, String.format("Stable frame rate %.0f verified", mDeviceFrameRate));
+ return;
+ }
+ }
+ }
+ }
+
+ public void testSurfaceSetFrameRate() throws InterruptedException {
+ ArrayList<Float> frameRatesToTest = getFrameRatesToTest();
+ Log.i(TAG, "Testing Surface.setFrameRate()");
+ synchronized (mLock) {
+ waitForSurface();
+ for (float frameRate : frameRatesToTest) {
+ setFrameRateSurface(frameRate);
+ verifyCompatibleAndStableFrameRate(frameRate);
+ }
+ setFrameRateSurface(-100.f);
+ Thread.sleep(1000);
+ setFrameRateSurface(0.f);
+ }
+ Log.i(TAG, "Done testing Surface.setFrameRate()");
+ }
+
+ public void testANativeWindowSetFrameRate() throws InterruptedException {
+ ArrayList<Float> frameRatesToTest = getFrameRatesToTest();
+ Log.i(TAG, "Testing ANativeWindow_setFrameRate()");
+ synchronized (mLock) {
+ waitForSurface();
+ for (float frameRate : frameRatesToTest) {
+ setFrameRateANativeWindow(frameRate);
+ verifyCompatibleAndStableFrameRate(frameRate);
+ }
+ setFrameRateANativeWindow(-100.f);
+ Thread.sleep(1000);
+ setFrameRateANativeWindow(0.f);
+ }
+ Log.i(TAG, "Done testing ANativeWindow_setFrameRate()");
+ }
+
+ public void testSurfaceControlSetFrameRate() throws InterruptedException {
+ ArrayList<Float> frameRatesToTest = getFrameRatesToTest();
+ Log.i(TAG, "Testing SurfaceControl.Transaction.setFrameRate()");
+ synchronized (mLock) {
+ waitForSurface();
+ for (float frameRate : frameRatesToTest) {
+ setFrameRateSurfaceControl(frameRate);
+ verifyCompatibleAndStableFrameRate(frameRate);
+ }
+ setFrameRateSurfaceControl(-100.f);
+ Thread.sleep(1000);
+ setFrameRateSurfaceControl(0.f);
+ }
+ Log.i(TAG, "Done testing SurfaceControl.Transaction.setFrameRate()");
+ }
+
+ public void testNativeSurfaceControlSetFrameRate() throws InterruptedException {
+ ArrayList<Float> frameRatesToTest = getFrameRatesToTest();
+ Log.i(TAG, "Testing ASurfaceTransaction_setFrameRate()");
+ long nativeSurfaceControl = 0;
+ try {
+ synchronized (mLock) {
+ waitForSurface();
+ nativeSurfaceControl = nativeSurfaceControlCreate(mSurface);
+ assertTrue("Failed to create a native SurfaceControl", nativeSurfaceControl != 0);
+ for (float frameRate : frameRatesToTest) {
+ setFrameRateNativeSurfaceControl(nativeSurfaceControl, frameRate);
+ verifyCompatibleAndStableFrameRate(frameRate);
+ }
+ setFrameRateNativeSurfaceControl(nativeSurfaceControl, -100.f);
+ Thread.sleep(1000);
+ setFrameRateNativeSurfaceControl(nativeSurfaceControl, 0.f);
+ }
+ } finally {
+ nativeSurfaceControlDestroy(nativeSurfaceControl);
+ }
+ Log.i(TAG, "Done testing ASurfaceTransaction_setFrameRate()");
+ }
+
+ private static native int nativeWindowSetFrameRate(Surface surface, float frameRate);
+ private static native long nativeSurfaceControlCreate(Surface parentSurface);
+ private static native void nativeSurfaceControlDestroy(long surfaceControl);
+ private static native void nativeSurfaceControlSetFrameRate(
+ long surfaceControl, float frameRate);
+}
diff --git a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
index c5a8221..687e316 100644
--- a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
@@ -2480,25 +2480,10 @@
}
}
-
- static Object[] crossProduct(Object[] a, Object[] b) {
- final int length = a.length * b.length;
- Object[] ret = new Object[length];
- for (int i = 0; i < a.length; i++) {
- for (int j = 0; j < b.length; j++) {
- int index = i * b.length + j;
- assertNull(ret[index]);
- ret[index] = new Object[] { a[i], b[j] };
- }
- }
- return ret;
- }
-
private Object[] getRecordsAsSources() {
- return crossProduct(getRecords(), mCreators);
+ return Utils.crossProduct(getRecords(), mCreators);
}
-
@Test
@LargeTest
@Parameters(method = "getRecordsAsSources")
@@ -2530,7 +2515,7 @@
}
private Object[] getRecordsAsUris() {
- return crossProduct(getRecords(), mUriCreators);
+ return Utils.crossProduct(getRecords(), mUriCreators);
}
diff --git a/tests/tests/graphics/src/android/graphics/cts/SetFrameRateTest.java b/tests/tests/graphics/src/android/graphics/cts/SetFrameRateTest.java
new file mode 100644
index 0000000..65dcfcd
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/cts/SetFrameRateTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 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.graphics.cts;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class SetFrameRateTest {
+ @Rule
+ public ActivityTestRule<FrameRateCtsActivity> mActivityRule =
+ new ActivityTestRule<>(FrameRateCtsActivity.class);
+
+ @Test
+ public void testSetFrameRate() throws InterruptedException {
+ FrameRateCtsActivity activity = mActivityRule.getActivity();
+ activity.testSurfaceSetFrameRate();
+ activity.testANativeWindowSetFrameRate();
+ activity.testSurfaceControlSetFrameRate();
+ activity.testNativeSurfaceControlSetFrameRate();
+ }
+}
diff --git a/tests/tests/graphics/src/android/graphics/cts/Utils.java b/tests/tests/graphics/src/android/graphics/cts/Utils.java
index f7229ed..85b5b40 100644
--- a/tests/tests/graphics/src/android/graphics/cts/Utils.java
+++ b/tests/tests/graphics/src/android/graphics/cts/Utils.java
@@ -16,6 +16,7 @@
package android.graphics.cts;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -112,4 +113,20 @@
}
return file;
}
+
+ /**
+ * Helper for JUnit-Params tests to combine inputs.
+ */
+ static Object[] crossProduct(Object[] a, Object[] b) {
+ final int length = a.length * b.length;
+ Object[] ret = new Object[length];
+ for (int i = 0; i < a.length; i++) {
+ for (int j = 0; j < b.length; j++) {
+ int index = i * b.length + j;
+ assertNull(ret[index]);
+ ret[index] = new Object[] { a[i], b[j] };
+ }
+ }
+ return ret;
+ }
}
diff --git a/tests/tests/hardware/src/android/hardware/lights/cts/LightsManagerTest.java b/tests/tests/hardware/src/android/hardware/lights/cts/LightsManagerTest.java
new file mode 100755
index 0000000..a30ca46
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/lights/cts/LightsManagerTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2020 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.hardware.lights.cts.tests;
+
+import static android.hardware.lights.LightsRequest.Builder;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Context;
+import android.hardware.lights.Light;
+import android.hardware.lights.LightState;
+import android.hardware.lights.LightsManager;
+
+import androidx.annotation.ColorInt;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LightsManagerTest {
+
+ private static final @ColorInt int TAN = 0xffd2b48c;
+ private static final @ColorInt int RED = 0xffff0000;
+
+ private LightsManager mManager;
+ private List<Light> mLights;
+
+ @Before
+ public void setUp() {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity(
+ android.Manifest.permission.CONTROL_DEVICE_LIGHTS);
+
+ final Context context = InstrumentationRegistry.getTargetContext();
+ mManager = context.getSystemService(LightsManager.class);
+ mLights = mManager.getLights();
+ }
+
+ @After
+ public void tearDown() {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+
+ @Test
+ public void testControlLightsPermissionIsRequiredToUseLights() {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity();
+ try {
+ mManager.getLights();
+ fail("Expected SecurityException to be thrown for getLights()");
+ } catch (SecurityException expected) {
+ }
+
+ try (LightsManager.LightsSession session = mManager.openSession()) {
+ fail("Expected SecurityException to be thrown for openSession()");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void testControlSingleLight() {
+ assumeTrue(mLights.size() >= 1);
+
+ try (LightsManager.LightsSession session = mManager.openSession()) {
+ // When the session requests to turn a single light on:
+ session.setLights(new Builder()
+ .setLight(mLights.get(0), new LightState(RED))
+ .build());
+
+ // Then the light should turn on.
+ assertThat(mManager.getLightState(mLights.get(0)).getColor()).isEqualTo(RED);
+ }
+ }
+
+ @Test
+ public void testControlMultipleLights() {
+ assumeTrue(mLights.size() >= 2);
+
+ try (LightsManager.LightsSession session = mManager.openSession()) {
+ // When the session requests to turn two of the lights on:
+ session.setLights(new Builder()
+ .setLight(mLights.get(0), new LightState(0xffaaaaff))
+ .setLight(mLights.get(1), new LightState(0xffbbbbff))
+ .build());
+
+ // Then both should turn on.
+ assertThat(mManager.getLightState(mLights.get(0)).getColor()).isEqualTo(0xffaaaaff);
+ assertThat(mManager.getLightState(mLights.get(1)).getColor()).isEqualTo(0xffbbbbff);
+
+ // Any others should remain off.
+ for (int i = 2; i < mLights.size(); i++) {
+ assertThat(mManager.getLightState(mLights.get(i)).getColor()).isEqualTo(0x00);
+ }
+ }
+ }
+
+ @Test
+ public void testControlLights_onlyEffectiveForLifetimeOfClient() {
+ assumeTrue(mLights.size() >= 1);
+
+ // The light should begin by being off.
+ assertThat(mManager.getLightState(mLights.get(0)).getColor()).isEqualTo(0x00);
+
+ try (LightsManager.LightsSession session = mManager.openSession()) {
+ // When a session commits changes:
+ session.setLights(new Builder().setLight(mLights.get(0), new LightState(TAN)).build());
+ // Then the light should turn on.
+ assertThat(mManager.getLightState(mLights.get(0)).getColor()).isEqualTo(TAN);
+
+ // When the session goes away:
+ session.close();
+ // Then the light should turn off.
+ assertThat(mManager.getLightState(mLights.get(0)).getColor()).isEqualTo(0x00);
+ }
+ }
+
+ @Test
+ public void testControlLights_firstCallerWinsContention() {
+ assumeTrue(mLights.size() >= 1);
+
+ try (LightsManager.LightsSession session1 = mManager.openSession();
+ LightsManager.LightsSession session2 = mManager.openSession()) {
+
+ // When session1 and session2 both request the same light:
+ session1.setLights(new Builder().setLight(mLights.get(0), new LightState(TAN)).build());
+ session2.setLights(new Builder().setLight(mLights.get(0), new LightState(RED)).build());
+ // Then session1 should win because it was created first.
+ assertThat(mManager.getLightState(mLights.get(0)).getColor()).isEqualTo(TAN);
+
+ // When session1 goes away:
+ session1.close();
+ // Then session2 should have its request go into effect.
+ assertThat(mManager.getLightState(mLights.get(0)).getColor()).isEqualTo(RED);
+
+ // When session2 goes away:
+ session2.close();
+ // Then the light should turn off because there are no more sessions.
+ assertThat(mManager.getLightState(mLights.get(0)).getColor()).isEqualTo(0);
+ }
+ }
+
+ @Test
+ public void testClearLight() {
+ assumeTrue(mLights.size() >= 1);
+
+ try (LightsManager.LightsSession session = mManager.openSession()) {
+ // When the session turns a light on:
+ session.setLights(new Builder().setLight(mLights.get(0), new LightState(RED)).build());
+ // And then the session clears it again:
+ session.setLights(new Builder().clearLight(mLights.get(0)).build());
+ // Then the light should turn back off.
+ assertThat(mManager.getLightState(mLights.get(0)).getColor()).isEqualTo(0);
+ }
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
index 0847800..55fecfe 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
@@ -885,9 +885,9 @@
assertNotNull(rootOfTrust.getVerifiedBootKey());
assertTrue("Verified boot key is only " + rootOfTrust.getVerifiedBootKey().length +
" bytes long", rootOfTrust.getVerifiedBootKey().length >= 32);
- checkEntropy(rootOfTrust.getVerifiedBootKey());
if (requireLocked) {
assertTrue(rootOfTrust.isDeviceLocked());
+ checkEntropy(rootOfTrust.getVerifiedBootKey());
assertEquals(KM_VERIFIED_BOOT_VERIFIED, rootOfTrust.getVerifiedBootState());
}
}
diff --git a/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp b/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
index 6db5494..11b0c62 100644
--- a/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
+++ b/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
@@ -744,7 +744,7 @@
!gListenerGotValidExpiryTime ||
!gOnKeyChangeListenerOK) && count++ < 5) {
// Prevents race condition when the event arrives late
- usleep(2000);
+ usleep(10000);
}
if (!gGotVendorDefinedEvent) {
diff --git a/tests/tests/media/res/raw/id3test0.mp3 b/tests/tests/media/res/raw/id3test0.mp3
new file mode 100644
index 0000000..5730b6b
--- /dev/null
+++ b/tests/tests/media/res/raw/id3test0.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/id3test1.mp3 b/tests/tests/media/res/raw/id3test1.mp3
new file mode 100644
index 0000000..af52e0f
--- /dev/null
+++ b/tests/tests/media/res/raw/id3test1.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/id3test10.mp3 b/tests/tests/media/res/raw/id3test10.mp3
new file mode 100644
index 0000000..ac01a00
--- /dev/null
+++ b/tests/tests/media/res/raw/id3test10.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/id3test11.mp3 b/tests/tests/media/res/raw/id3test11.mp3
new file mode 100644
index 0000000..5786b80
--- /dev/null
+++ b/tests/tests/media/res/raw/id3test11.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/id3test2.mp3 b/tests/tests/media/res/raw/id3test2.mp3
new file mode 100644
index 0000000..7fdb737
--- /dev/null
+++ b/tests/tests/media/res/raw/id3test2.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/id3test3.mp3 b/tests/tests/media/res/raw/id3test3.mp3
new file mode 100644
index 0000000..a9ce936
--- /dev/null
+++ b/tests/tests/media/res/raw/id3test3.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/id3test4.mp3 b/tests/tests/media/res/raw/id3test4.mp3
new file mode 100644
index 0000000..f2d2df9
--- /dev/null
+++ b/tests/tests/media/res/raw/id3test4.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/id3test5.mp3 b/tests/tests/media/res/raw/id3test5.mp3
new file mode 100644
index 0000000..4ee1200
--- /dev/null
+++ b/tests/tests/media/res/raw/id3test5.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/id3test6.mp3 b/tests/tests/media/res/raw/id3test6.mp3
new file mode 100644
index 0000000..017e0c0
--- /dev/null
+++ b/tests/tests/media/res/raw/id3test6.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/id3test7.mp3 b/tests/tests/media/res/raw/id3test7.mp3
new file mode 100644
index 0000000..d106a46
--- /dev/null
+++ b/tests/tests/media/res/raw/id3test7.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/id3test8.mp3 b/tests/tests/media/res/raw/id3test8.mp3
new file mode 100644
index 0000000..ab83c86
--- /dev/null
+++ b/tests/tests/media/res/raw/id3test8.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/id3test9.mp3 b/tests/tests/media/res/raw/id3test9.mp3
new file mode 100644
index 0000000..84d2c49
--- /dev/null
+++ b/tests/tests/media/res/raw/id3test9.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/programstream.mpeg b/tests/tests/media/res/raw/programstream.mpeg
new file mode 100644
index 0000000..6f8a480
--- /dev/null
+++ b/tests/tests/media/res/raw/programstream.mpeg
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/DecoderTest.java b/tests/tests/media/src/android/media/cts/DecoderTest.java
index f967b3e..75ea6da 100644
--- a/tests/tests/media/src/android/media/cts/DecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTest.java
@@ -23,6 +23,7 @@
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.graphics.ImageFormat;
+import android.hardware.display.DisplayManager;
import android.media.cts.CodecUtils;
import android.media.Image;
import android.media.AudioFormat;
@@ -36,6 +37,7 @@
import android.media.MediaFormat;
import android.platform.test.annotations.AppModeFull;
import android.util.Log;
+import android.view.Display;
import android.view.Surface;
import android.net.Uri;
@@ -89,6 +91,7 @@
private static final String VIDEO_URL_KEY = "decoder_test_video_url";
private static final String MODULE_NAME = "CtsMediaTestCases";
private DynamicConfigDeviceSide dynamicConfig;
+ private DisplayManager mDisplayManager;
@Override
protected void setUp() throws Exception {
@@ -114,6 +117,7 @@
masterFd.close();
dynamicConfig = new DynamicConfigDeviceSide(MODULE_NAME);
+ mDisplayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
}
@Override
@@ -924,7 +928,12 @@
}
String[] decoderNames = MediaUtils.getDecoderNames(format);
- if (decoderNames == null || decoderNames.length == 0) {
+ int numberOfSupportedHdrTypes =
+ mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getHdrCapabilities()
+ .getSupportedHdrTypes().length;
+
+ if (decoderNames == null || decoderNames.length == 0
+ || numberOfSupportedHdrTypes == 0) {
MediaUtils.skipTest("No video codecs supports HDR");
return;
}
diff --git a/tests/tests/media/src/android/media/cts/MediaExtractorTest.java b/tests/tests/media/src/android/media/cts/MediaExtractorTest.java
index 21149c6..01a39bf 100644
--- a/tests/tests/media/src/android/media/cts/MediaExtractorTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaExtractorTest.java
@@ -23,6 +23,7 @@
import android.icu.util.ULocale;
import android.media.AudioFormat;
import android.media.AudioPresentation;
+import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaDataSource;
import android.media.MediaExtractor;
@@ -784,6 +785,75 @@
assertTrue("could not read alac mov", totalSize > 0);
}
+ public void testProgramStreamExtraction() throws Exception {
+ AssetFileDescriptor testFd = mResources.openRawResourceFd(R.raw.programstream);
+
+ MediaExtractor extractor = new MediaExtractor();
+ extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
+ testFd.getLength());
+ testFd.close();
+ assertEquals("There must be 2 tracks", 2, extractor.getTrackCount());
+ extractor.selectTrack(0);
+ extractor.selectTrack(1);
+ boolean lastAdvanceResult = true;
+ boolean lastReadResult = true;
+ int [] bytesRead = new int[2];
+ MediaCodec [] codecs = { null, null };
+
+ try {
+ MediaFormat f = extractor.getTrackFormat(0);
+ codecs[0] = MediaCodec.createDecoderByType(f.getString(MediaFormat.KEY_MIME));
+ codecs[0].configure(f, null /* surface */, null /* crypto */, 0 /* flags */);
+ codecs[0].start();
+ } catch (IOException | IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ MediaFormat f = extractor.getTrackFormat(1);
+ codecs[1] = MediaCodec.createDecoderByType(f.getString(MediaFormat.KEY_MIME));
+ codecs[1].configure(f, null /* surface */, null /* crypto */, 0 /* flags */);
+ codecs[1].start();
+ } catch (IOException | IllegalArgumentException e) {
+ // ignore
+ }
+
+ ByteBuffer buf = ByteBuffer.allocate(2*1024*1024);
+ MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+ while(true) {
+ for (MediaCodec codec : codecs) {
+ if (codec != null) {
+ int idx = codec.dequeueOutputBuffer(info, 5);
+ if (idx >= 0) {
+ codec.releaseOutputBuffer(idx, false);
+ }
+ }
+ }
+ int trackIdx = extractor.getSampleTrackIndex();
+ MediaCodec codec = codecs[trackIdx];
+ ByteBuffer b = buf;
+ int bufIdx = -1;
+ if (codec != null) {
+ bufIdx = codec.dequeueInputBuffer(-1);
+ b = codec.getInputBuffer(bufIdx);
+ }
+ int n = extractor.readSampleData(b, 0);
+ if (n > 0) {
+ bytesRead[trackIdx] += n;
+ }
+ if (codec != null) {
+ int sampleFlags = extractor.getSampleFlags();
+ long sampleTime = extractor.getSampleTime();
+ codec.queueInputBuffer(bufIdx, 0, n, sampleTime, sampleFlags);
+ }
+ if (!extractor.advance()) {
+ break;
+ }
+ }
+ assertTrue("did not read from track 0", bytesRead[0] > 0);
+ assertTrue("did not read from track 1", bytesRead[1] > 0);
+ extractor.release();
+ }
+
private void doTestAdvance(int res) throws Exception {
AssetFileDescriptor testFd = mResources.openRawResourceFd(res);
diff --git a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
index 9066c1b..ffe7a1f 100644
--- a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
@@ -323,6 +323,29 @@
mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_WRITER));
}
+ public void testGenreParsing() {
+ Object [][] genres = {
+ { R.raw.id3test0, null },
+ { R.raw.id3test1, "Country" },
+ { R.raw.id3test2, "Classic Rock, Android" },
+ { R.raw.id3test3, null },
+ { R.raw.id3test4, "Classic Rock, (Android)" },
+ { R.raw.id3test5, null },
+ { R.raw.id3test6, "Funk, Grunge, Hip-Hop" },
+ { R.raw.id3test7, null },
+ { R.raw.id3test8, "Disco" },
+ { R.raw.id3test9, "Cover" },
+ { R.raw.id3test10, "Pop, Remix" },
+ { R.raw.id3test11, "Remix" },
+ };
+ for (Object [] genre: genres) {
+ setDataSourceFd((Integer)genre[0] /* resource id */);
+ assertEquals("Unexpected genre: ",
+ genre[1] /* genre */,
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_GENRE));
+ }
+ }
+
public void testBitsPerSampleAndSampleRate() {
setDataSourceFd(R.raw.testwav_16bit_44100hz);
diff --git a/tests/tests/nativemedia/mediametrics/src/MediaMetricsTest.cpp b/tests/tests/nativemedia/mediametrics/src/MediaMetricsTest.cpp
index fef80ba..4267da7 100644
--- a/tests/tests/nativemedia/mediametrics/src/MediaMetricsTest.cpp
+++ b/tests/tests/nativemedia/mediametrics/src/MediaMetricsTest.cpp
@@ -19,7 +19,7 @@
#include <gtest/gtest.h>
#include <utils/Log.h>
-#include <MediaMetrics.h>
+#include <media/MediaMetrics.h>
//-----------------------------------------------------------------
class MediaMetricsTest : public ::testing::Test {
diff --git a/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java
index f872b5c..6e87253 100644
--- a/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java
+++ b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java
@@ -35,6 +35,7 @@
import android.app.UiAutomation;
import android.content.ComponentName;
import android.content.Context;
+import android.content.IntentFilter;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.service.notification.Condition;
@@ -62,6 +63,8 @@
private NotificationManager mNm;
private ActivityManager mActivityManager;
private Context mContext;
+ private ZenModeBroadcastReceiver mModeReceiver;
+ private IntentFilter mModeFilter;
private ArraySet<String> ids = new ArraySet<>();
@Before
@@ -77,10 +80,15 @@
mNm = (NotificationManager) mContext.getSystemService(
Context.NOTIFICATION_SERVICE);
mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+ mModeReceiver = new ZenModeBroadcastReceiver();
+ mModeFilter = new IntentFilter();
+ mModeFilter.addAction(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
+ mContext.registerReceiver(mModeReceiver, mModeFilter);
}
@After
public void tearDown() throws Exception {
+ mContext.unregisterReceiver(mModeReceiver);
if (mNm == null) {
// assumption in setUp is false, so mNm is not initialized
return;
@@ -115,8 +123,12 @@
final ComponentName cn = SecondaryConditionProviderService.getId();
// add rule
+ mModeReceiver.reset();
+
addRule(cn, INTERRUPTION_FILTER_ALARMS, true);
pollForSubscribe(SecondaryConditionProviderService.getInstance());
+
+ mModeReceiver.waitFor(1/*Secondary only*/, 1000/*Limit is 1 second*/);
assertEquals(INTERRUPTION_FILTER_ALARMS, mNm.getCurrentInterruptionFilter());
// unbind service
@@ -143,11 +155,15 @@
pollForConnection(SecondaryConditionProviderService.class, true);
// add rules for both
+ mModeReceiver.reset();
+
addRule(LegacyConditionProviderService.getId(), INTERRUPTION_FILTER_PRIORITY, true);
pollForSubscribe(LegacyConditionProviderService.getInstance());
addRule(SecondaryConditionProviderService.getId(), INTERRUPTION_FILTER_ALARMS, true);
pollForSubscribe(SecondaryConditionProviderService.getInstance());
+
+ mModeReceiver.waitFor(2/*Legacy and Secondary*/, 1000/*Limit is 1 second*/);
assertEquals(INTERRUPTION_FILTER_ALARMS, mNm.getCurrentInterruptionFilter());
// unbind one of the services
@@ -175,11 +191,15 @@
pollForConnection(SecondaryConditionProviderService.class, true);
// add rules for both
+ mModeReceiver.reset();
+
addRule(LegacyConditionProviderService.getId(), INTERRUPTION_FILTER_PRIORITY, true);
pollForSubscribe(LegacyConditionProviderService.getInstance());
addRule(SecondaryConditionProviderService.getId(), INTERRUPTION_FILTER_ALARMS, true);
pollForSubscribe(SecondaryConditionProviderService.getInstance());
+
+ mModeReceiver.waitFor(2/*Legacy and Secondary*/, 1000/*Limit is 1 second*/);
assertEquals(INTERRUPTION_FILTER_ALARMS, mNm.getCurrentInterruptionFilter());
// unbind one of the services
diff --git a/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/ZenModeBroadcastReceiver.java b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/ZenModeBroadcastReceiver.java
new file mode 100644
index 0000000..7c735e5
--- /dev/null
+++ b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/ZenModeBroadcastReceiver.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 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.app.notification.legacy.cts;
+
+import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED;
+import static java.lang.Thread.sleep;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+public class ZenModeBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction() == null ||
+ intent.getPackage() == null) {
+ return;
+ }
+
+ if (intent.getAction().equals(ACTION_INTERRUPTION_FILTER_CHANGED) &&
+ intent.getPackage().equals(InstrumentationRegistry.getContext().getPackageName())) {
+ mCount++;
+ }
+ }
+
+ public void reset() {
+ mCount = 0;
+ mReset = true;
+ }
+
+ public void waitFor(int count, int ms) throws IllegalStateException {
+ if (!mReset) {
+ throw new IllegalStateException("Call reset() before waitFor()!");
+ }
+ mReset = false;
+
+ final int delayMs = 100;
+ while (ms > 0 && mCount < count) {
+ ms -= delayMs;
+ try {
+ sleep(delayMs);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ Log.d(TAG, "Exit from wait due to " +
+ (mCount < count ? "timeout" : "intents") + ".");
+ }
+
+ private static String TAG = "CpsTest";
+ private int mCount = 0;
+ private boolean mReset = true;
+}
diff --git a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/IntentTest.kt b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/IntentTest.kt
index a8a8917..c6af7c8 100644
--- a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/IntentTest.kt
+++ b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/IntentTest.kt
@@ -21,13 +21,20 @@
import android.content.Intent
import android.net.Uri
import android.platform.test.annotations.AppModeFull
+
import androidx.test.InstrumentationRegistry
import androidx.test.runner.AndroidJUnit4
+
import com.android.compatibility.common.util.AppOpsUtils
+import com.android.compatibility.common.util.SystemUtil.runShellCommand
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+
+import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+
import java.util.concurrent.TimeUnit
private const val INSTALL_BUTTON_ID = "button1"
@@ -38,11 +45,22 @@
class IntentTest : PackageInstallerTestBase() {
private val context = InstrumentationRegistry.getTargetContext()
+ private fun setSecureFrp(secureFrp: Boolean) {
+ runWithShellPermissionIdentity {
+ runShellCommand("settings put secure secure_frp_mode ${if (secureFrp) 1 else 0}")
+ }
+ }
+
@Before
fun allowToInstallPackages() {
AppOpsUtils.setOpMode(context.packageName, APP_OP_STR, MODE_ALLOWED)
}
+ @After
+ fun disableSecureFrp() {
+ setSecureFrp(false)
+ }
+
/**
* Check that we can install an app via a package-installer intent
*/
@@ -92,4 +110,22 @@
assertEquals(RESULT_OK, reinstall.get(TIMEOUT, TimeUnit.MILLISECONDS))
assertInstalled()
}
+
+ /**
+ * Check that we can't install an app via a package-installer intent if Secure FRP is enabled
+ */
+ @Test
+ fun packageNotInstalledSecureFrp() {
+ setSecureFrp(true)
+ try {
+ val installation = startInstallationViaIntent()
+ clickInstallerUIButton(INSTALL_BUTTON_ID)
+
+ // Install should not have succeeded
+ assertEquals(RESULT_CANCELED, installation.get(TIMEOUT, TimeUnit.MILLISECONDS))
+ assertNotInstalled()
+ } finally {
+ setSecureFrp(false)
+ }
+ }
}
diff --git a/tests/tests/permission/src/android/permission/cts/ServicesInstantAppsCannotAccessTests.java b/tests/tests/permission/src/android/permission/cts/ServicesInstantAppsCannotAccessTests.java
index 8609e33..84c2dd6 100644
--- a/tests/tests/permission/src/android/permission/cts/ServicesInstantAppsCannotAccessTests.java
+++ b/tests/tests/permission/src/android/permission/cts/ServicesInstantAppsCannotAccessTests.java
@@ -25,11 +25,15 @@
import static android.content.Context.WIFI_P2P_SERVICE;
import static android.content.Context.WIFI_SERVICE;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
+import android.app.WallpaperManager;
import android.content.Context;
import android.platform.test.annotations.AppModeInstant;
+import com.android.compatibility.common.util.RequiredServiceRule;
+
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -68,8 +72,14 @@
@Test
public void cannotGetWallpaperManager() {
- assertNull(InstrumentationRegistry.getTargetContext().getSystemService(
- WALLPAPER_SERVICE));
+ WallpaperManager mgr = (WallpaperManager) InstrumentationRegistry.getTargetContext()
+ .getSystemService(WALLPAPER_SERVICE);
+ boolean supported = RequiredServiceRule.hasService("wallpaper");
+ if (supported) {
+ assertNull(mgr);
+ } else {
+ assertFalse(mgr.isWallpaperSupported());
+ }
}
@Test
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index 7a7fa44..904beef 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -1462,6 +1462,13 @@
android:protectionLevel="signature|privileged" />
<uses-permission android:name="android.permission.LOCATION_HARDWARE"/>
+ <!-- @SystemApi Allows an application to use the Context Hub.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.ACCESS_CONTEXT_HUB"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows an application to create mock location providers for testing.
<p>Protection level: signature
@hide
@@ -2637,6 +2644,11 @@
<permission android:name="android.permission.READ_DEVICE_CONFIG"
android:protectionLevel="signature|preinstalled" />
+ <!-- @SystemApi @hide Allows an application to monitor access to config settings.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.MONITOR_DEVICE_CONFIG_ACCESS"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi @TestApi Allows an application to call
{@link android.app.ActivityManager#forceStopPackage}.
@hide -->
@@ -3238,6 +3250,13 @@
<permission android:name="android.permission.BIND_AUTOFILL_FIELD_CLASSIFICATION_SERVICE"
android:protectionLevel="signature" />
+ <!-- Must be required by an {@link android.service.autofill.InlineSuggestionRenderService}
+ to ensure that only the system can bind to it.
+ @hide This is not a third-party API (intended for OEMs and system apps).
+ -->
+ <permission android:name="android.permission.BIND_INLINE_SUGGESTION_RENDER_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- Must be required by a android.service.textclassifier.TextClassifierService,
to ensure that only the system can bind to it.
@SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
@@ -4200,6 +4219,11 @@
<permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"
android:protectionLevel="signature" />
+ <!-- Allows applications to set the initial lockscreen state.
+ <p>Not for use by third-party applications. @hide -->
+ <permission android:name="android.permission.SET_INITIAL_LOCK"
+ android:protectionLevel="signature|setup"/>
+
<!-- Allows managing (adding, removing) fingerprint templates. Reserved for the system. @hide -->
<permission android:name="android.permission.MANAGE_FINGERPRINT"
android:protectionLevel="signature|privileged" />
diff --git a/tests/tests/rcs/AndroidManifest.xml b/tests/tests/rcs/AndroidManifest.xml
deleted file mode 100755
index 15a4038..0000000
--- a/tests/tests/rcs/AndroidManifest.xml
+++ /dev/null
@@ -1,92 +0,0 @@
-<?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.telephony.ims.cts"
- android:targetSandboxVersion="2">
-
- <uses-permission android:name="android.permission.READ_SMS" />
- <uses-permission android:name="android.permission.SEND_SMS" />
- <uses-permission android:name="android.permission.RECEIVE_SMS" />
- <uses-permission android:name="android.permission.RECEIVE_MMS" />
-
- <application>
- <uses-library android:name="android.test.runner" />
-
- <!-- Required to be default SMS app -->
- <receiver android:name="android.telephony.ims.SmsApplicationSmsDeliverReceiver"
- android:permission="android.permission.BROADCAST_SMS">
-
- <intent-filter>
- <action android:name="android.provider.Telephony.SMS_DELIVER" />
- </intent-filter>
-
- </receiver>
-
- <!-- Required to be default SMS app -->
- <receiver android:name="android.telephony.ims.SmsApplicationWapPushDeliverReceiver"
- android:permission="android.permission.BROADCAST_WAP_PUSH">
-
- <intent-filter>
- <action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
- <data android:mimeType="application/vnd.wap.mms-message" />
- </intent-filter>
-
- </receiver>
-
- <!-- Required to be default SMS app -->
- <service android:name="android.telephony.ims.SmsApplicationService"
- android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"
- android:exported="true" >
- <intent-filter>
- <action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:scheme="sms" />
- <data android:scheme="smsto" />
- <data android:scheme="mms" />
- <data android:scheme="mmsto" />
- </intent-filter>
- </service>
-
- <!-- Required to be default SMS app -->
- <activity
- android:name="android.telephony.ims.SmsApplicationActivity"
- android:label="RCS CTS Test Application Activity"
- android:windowSoftInputMode="stateHidden">
-
- <intent-filter>
- <action android:name="android.intent.action.SEND" />
- <action android:name="android.intent.action.SENDTO" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.BROWSABLE" />
- <data android:scheme="sms" />
- <data android:scheme="smsto" />
- <data android:scheme="mms" />
- <data android:scheme="mmsto" />
- </intent-filter>
- </activity>
-
- </application>
-
- <!-- self-instrumenting test package. -->
- <instrumentation
- android:name="androidx.test.runner.AndroidJUnitRunner"
- android:label="RCS CTS tests"
- android:targetPackage="android.telephony.ims.cts" >
- </instrumentation>
-</manifest>
-
diff --git a/tests/tests/rcs/OWNERS b/tests/tests/rcs/OWNERS
deleted file mode 100644
index f901c4f..0000000
--- a/tests/tests/rcs/OWNERS
+++ /dev/null
@@ -1,16 +0,0 @@
-amitmahajan@google.com
-breadley@google.com
-fionaxu@google.com
-hallliu@google.com
-jackyu@google.com
-jminjie@google.com
-lelandmiller@google.com
-mpq@google.com
-nazaninb@google.com
-paulye@google.com
-refuhoo@google.com
-rgreenwalt@google.com
-sahinc@google.com
-satk@google.com
-shuoq@google.com
-tgunn@google.com
\ No newline at end of file
diff --git a/tests/tests/rcs/src/android/telephony/ims/SmsApplicationActivity.java b/tests/tests/rcs/src/android/telephony/ims/SmsApplicationActivity.java
deleted file mode 100644
index 3588046..0000000
--- a/tests/tests/rcs/src/android/telephony/ims/SmsApplicationActivity.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2019 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.telephony.ims;
-
-import android.app.Activity;
-
-/**
- * This activity is used to provide the interface required for a default SMS application. It
- * intentionally has no custom behavior.
- */
-public class SmsApplicationActivity extends Activity {}
-
diff --git a/tests/tests/rcs/src/android/telephony/ims/SmsApplicationService.java b/tests/tests/rcs/src/android/telephony/ims/SmsApplicationService.java
deleted file mode 100644
index 302228e..0000000
--- a/tests/tests/rcs/src/android/telephony/ims/SmsApplicationService.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2019 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.telephony.ims;
-
-import android.app.IntentService;
-import android.content.Intent;
-
-/**
- * This service is used to provide the interface required for a default SMS application. It
- * intentionally has no custom behavior.
- */
-public class SmsApplicationService extends IntentService {
- private static final String TAG = "SmsApplicationService";
-
- public SmsApplicationService() {
- super(TAG);
- }
-
- @Override
- protected void onHandleIntent(Intent intent) {
- // Do nothing
- }
-}
diff --git a/tests/tests/rcs/src/android/telephony/ims/SmsApplicationSmsDeliverReceiver.java b/tests/tests/rcs/src/android/telephony/ims/SmsApplicationSmsDeliverReceiver.java
deleted file mode 100644
index 8bcc9bc..0000000
--- a/tests/tests/rcs/src/android/telephony/ims/SmsApplicationSmsDeliverReceiver.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2019 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.telephony.ims;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-/**
- * This receiver is used to provide the interface required for a default SMS application. It
- * intentionally has no custom behavior.
- */
-public class SmsApplicationSmsDeliverReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- // Do nothing
- }
-}
diff --git a/tests/tests/rcs/src/android/telephony/ims/SmsApplicationWapPushDeliverReceiver.java b/tests/tests/rcs/src/android/telephony/ims/SmsApplicationWapPushDeliverReceiver.java
deleted file mode 100644
index 5f6ea5b..0000000
--- a/tests/tests/rcs/src/android/telephony/ims/SmsApplicationWapPushDeliverReceiver.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2019 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.telephony.ims;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-/**
- * This receiver is used to provide the interface required for a default SMS application. It
- * intentionally has no custom behavior.
- */
-public class SmsApplicationWapPushDeliverReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- // Do nothing
- }
-}
diff --git a/tests/tests/rcs/src/android/telephony/ims/cts/DefaultSmsAppHelper.java b/tests/tests/rcs/src/android/telephony/ims/cts/DefaultSmsAppHelper.java
deleted file mode 100644
index a26cd05..0000000
--- a/tests/tests/rcs/src/android/telephony/ims/cts/DefaultSmsAppHelper.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2019 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.telephony.ims.cts;
-
-import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
-
-import androidx.test.InstrumentationRegistry;
-
-class DefaultSmsAppHelper {
- static void ensureDefaultSmsApp() {
- String packageName =
- InstrumentationRegistry.getInstrumentation().getContext().getPackageName();
- runShellCommand(
- String.format("settings put secure sms_default_application %s", packageName));
- }
-}
diff --git a/tests/tests/rcs/src/android/telephony/ims/cts/Rcs1To1ThreadTest.java b/tests/tests/rcs/src/android/telephony/ims/cts/Rcs1To1ThreadTest.java
deleted file mode 100644
index 516a5bb..0000000
--- a/tests/tests/rcs/src/android/telephony/ims/cts/Rcs1To1ThreadTest.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2019 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.telephony.ims.cts;
-
-import static android.provider.Telephony.RcsColumns.IS_RCS_TABLE_SCHEMA_CODE_COMPLETE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.telephony.ims.Rcs1To1Thread;
-import android.telephony.ims.RcsMessageManager;
-import android.telephony.ims.RcsMessageStoreException;
-import android.telephony.ims.RcsParticipant;
-
-import androidx.test.InstrumentationRegistry;
-
-import org.junit.AfterClass;
-import org.junit.Assume;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-public class Rcs1To1ThreadTest {
- private RcsMessageManager mRcsMessageManager;
- private Context mContext;
-
- @BeforeClass
- public static void ensureDefaultSmsApp() {
- DefaultSmsAppHelper.ensureDefaultSmsApp();
- }
-
- @Before
- public void setupTestEnvironment() {
- // Used to skip tests for production builds without RCS tables, will be removed when
- // IS_RCS_TABLE_SCHEMA_CODE_COMPLETE flag is removed.
- Assume.assumeTrue(IS_RCS_TABLE_SCHEMA_CODE_COMPLETE);
-
- mContext = InstrumentationRegistry.getTargetContext();
- mRcsMessageManager = mContext.getSystemService(RcsMessageManager.class);
-
- cleanup();
- }
-
- @AfterClass
- public static void cleanup() {
- // TODO(b/123997749) should clean RCS message store here
- }
-
- @Test
- public void testRcs1To1Thread_isGroupReturnsFalse() throws RcsMessageStoreException {
- RcsParticipant participant = mRcsMessageManager.createRcsParticipant(
- "+1234567890", "Alice");
- Rcs1To1Thread thread = mRcsMessageManager.createRcs1To1Thread(participant);
-
- assertThat(thread.isGroup()).isFalse();
- }
-
- @Test
- public void testRcs1To1Thread_fallbackThreadIdCanBeSet() throws RcsMessageStoreException {
- RcsParticipant participant = mRcsMessageManager.createRcsParticipant(
- "+1234567890", "Alice");
- Rcs1To1Thread thread = mRcsMessageManager.createRcs1To1Thread(participant);
-
- thread.setFallbackThreadId(2);
-
- assertThat(thread.getFallbackThreadId()).isEqualTo(2);
- }
-}
diff --git a/tests/tests/rcs/src/android/telephony/ims/cts/RcsEventTest.java b/tests/tests/rcs/src/android/telephony/ims/cts/RcsEventTest.java
deleted file mode 100644
index c6ed244..0000000
--- a/tests/tests/rcs/src/android/telephony/ims/cts/RcsEventTest.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (C) 2019 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.telephony.ims.cts;
-
-import static android.provider.Telephony.RcsColumns.IS_RCS_TABLE_SCHEMA_CODE_COMPLETE;
-
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import android.content.Context;
-import android.net.Uri;
-import android.telephony.ims.RcsEvent;
-import android.telephony.ims.RcsEventQueryParams;
-import android.telephony.ims.RcsEventQueryResult;
-import android.telephony.ims.RcsGroupThread;
-import android.telephony.ims.RcsGroupThreadEvent;
-import android.telephony.ims.RcsGroupThreadIconChangedEvent;
-import android.telephony.ims.RcsGroupThreadNameChangedEvent;
-import android.telephony.ims.RcsGroupThreadParticipantJoinedEvent;
-import android.telephony.ims.RcsGroupThreadParticipantLeftEvent;
-import android.telephony.ims.RcsMessageManager;
-import android.telephony.ims.RcsMessageStoreException;
-import android.telephony.ims.RcsParticipant;
-import android.telephony.ims.RcsParticipantAliasChangedEvent;
-
-import androidx.test.InstrumentationRegistry;
-
-import com.google.android.collect.Lists;
-
-import org.junit.AfterClass;
-import org.junit.Assume;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import java.util.function.Predicate;
-
-public class RcsEventTest {
- private RcsMessageManager mRcsMessageManager;
-
- private long mTimestamp;
- private RcsParticipant mParticipant1;
- private RcsParticipant mParticipant2;
- private RcsGroupThread mGroupThread;
-
- @BeforeClass
- public static void ensureDefaultSmsApp() {
- DefaultSmsAppHelper.ensureDefaultSmsApp();
- }
-
-
- @Before
- public void setupTestEnvironment() throws RcsMessageStoreException {
- // Used to skip tests for production builds without RCS tables, will be removed when
- // IS_RCS_TABLE_SCHEMA_CODE_COMPLETE flag is removed.
- Assume.assumeTrue(IS_RCS_TABLE_SCHEMA_CODE_COMPLETE);
-
- Context context = InstrumentationRegistry.getTargetContext();
- mRcsMessageManager = context.getSystemService(RcsMessageManager.class);
-
- cleanup();
-
- mTimestamp = 1234567890;
- mParticipant1 = mRcsMessageManager.createRcsParticipant("403", "p1");
- mParticipant2 = mRcsMessageManager.createRcsParticipant("404", "p2");
- mGroupThread = mRcsMessageManager.createGroupThread(
- Lists.newArrayList(mParticipant1, mParticipant2), "groupName", Uri.EMPTY);
-
- }
-
- @AfterClass
- public static void cleanup() {
- // TODO(b/123997749) should clean RCS message store here
- }
-
- @Test
- public void testCreateRcsEvent_canSaveAndQueryGroupThreadParticipantJoinedEvent()
- throws RcsMessageStoreException {
- RcsGroupThreadParticipantJoinedEvent rcsGroupThreadParticipantJoinedEvent =
- new RcsGroupThreadParticipantJoinedEvent(
- mTimestamp, mGroupThread, mParticipant1, mParticipant2);
-
- mRcsMessageManager.persistRcsEvent(rcsGroupThreadParticipantJoinedEvent);
-
- assertMatchingEventInQuery(
- RcsEventQueryParams.GROUP_THREAD_PARTICIPANT_JOINED_EVENT,
- event -> matches(rcsGroupThreadParticipantJoinedEvent, event));
- }
-
- @Test
- public void testCreateRcsEvent_canSaveAndQueryGroupThreadNameChangedEvent()
- throws RcsMessageStoreException {
- RcsGroupThreadNameChangedEvent rcsGroupThreadNameChangedEvent =
- new RcsGroupThreadNameChangedEvent(
- mTimestamp, mGroupThread, mParticipant1, "newName");
-
- mRcsMessageManager.persistRcsEvent(rcsGroupThreadNameChangedEvent);
-
- assertMatchingEventInQuery(
- RcsEventQueryParams.GROUP_THREAD_NAME_CHANGED_EVENT,
- event -> matches(rcsGroupThreadNameChangedEvent, event));
- }
-
- @Test
- public void testCreateRcsEvent_canSaveAndQueryParticipantAliasChangedEvent()
- throws RcsMessageStoreException {
- RcsParticipantAliasChangedEvent rcsParticipantAliasChangedEvent
- = new RcsParticipantAliasChangedEvent(mTimestamp, mParticipant1, "newAlias");
-
- mRcsMessageManager.persistRcsEvent(rcsParticipantAliasChangedEvent);
-
- assertMatchingEventInQuery(
- RcsEventQueryParams.PARTICIPANT_ALIAS_CHANGED_EVENT,
- event -> matches(rcsParticipantAliasChangedEvent, event));
- }
-
- @Test
- public void testCreateRcsEvent_canSaveAndQueryGroupThreadParticipantLeftEvent()
- throws RcsMessageStoreException {
- RcsGroupThreadParticipantLeftEvent rcsGroupThreadParticipantLeftEvent =
- new RcsGroupThreadParticipantLeftEvent(
- mTimestamp, mGroupThread, mParticipant1, mParticipant2);
-
- mRcsMessageManager.persistRcsEvent(rcsGroupThreadParticipantLeftEvent);
-
- assertMatchingEventInQuery(
- RcsEventQueryParams.GROUP_THREAD_PARTICIPANT_LEFT_EVENT,
- event -> matches(rcsGroupThreadParticipantLeftEvent, event));
- }
-
- @Test
- public void testCreateRcsEvent_canSaveAndQueryGroupThreadIconChangedEvent()
- throws RcsMessageStoreException {
- Uri newIcon = Uri.parse("cool/new/icon");
-
- RcsGroupThreadIconChangedEvent rcsGroupThreadIconChangedEvent =
- new RcsGroupThreadIconChangedEvent(
- mTimestamp, mGroupThread, mParticipant1, newIcon);
-
- mRcsMessageManager.persistRcsEvent(rcsGroupThreadIconChangedEvent);
-
- assertMatchingEventInQuery(
- RcsEventQueryParams.GROUP_THREAD_ICON_CHANGED_EVENT,
- event -> matches(rcsGroupThreadIconChangedEvent, event));
- }
-
- private void assertMatchingEventInQuery(int queryMessageType, Predicate<RcsEvent> predicate)
- throws RcsMessageStoreException {
- RcsEventQueryResult queryResult = mRcsMessageManager.getRcsEvents(
- new RcsEventQueryParams.Builder()
- .setEventType(queryMessageType)
- .build());
-
- boolean foundMatch = queryResult.getEvents().stream().anyMatch(predicate);
-
- assertWithMessage(queryResult.getEvents().toString()).that(foundMatch).isTrue();
- }
-
- private boolean matches(RcsGroupThreadParticipantJoinedEvent expected, RcsEvent actual) {
- if (!(actual instanceof RcsGroupThreadParticipantJoinedEvent)) {
- return false;
- }
- RcsGroupThreadParticipantJoinedEvent actualParticipantJoinedEvent =
- (RcsGroupThreadParticipantJoinedEvent) actual;
-
- return matchesGroupThreadEvent(expected, actualParticipantJoinedEvent)
- && actualParticipantJoinedEvent.getJoinedParticipant().getId()
- == expected.getJoinedParticipant().getId();
- }
-
-
- private boolean matches(RcsGroupThreadNameChangedEvent expected, RcsEvent actual) {
- if (!(actual instanceof RcsGroupThreadNameChangedEvent)) {
- return false;
- }
- RcsGroupThreadNameChangedEvent actualGroupThreadNameChangedEvent =
- (RcsGroupThreadNameChangedEvent) actual;
-
- return matchesGroupThreadEvent(expected, actualGroupThreadNameChangedEvent)
- && actualGroupThreadNameChangedEvent.getNewName().equals(expected.getNewName());
- }
-
- private boolean matches(RcsGroupThreadParticipantLeftEvent expected, RcsEvent actual) {
- if (!(actual instanceof RcsGroupThreadParticipantLeftEvent)) {
- return false;
- }
- RcsGroupThreadParticipantLeftEvent actualParticipantLeftEvent =
- (RcsGroupThreadParticipantLeftEvent) actual;
-
- return matchesGroupThreadEvent(expected, actualParticipantLeftEvent)
- && actualParticipantLeftEvent.getLeavingParticipant().getId()
- == expected.getLeavingParticipant().getId();
- }
-
-
- private boolean matches(RcsGroupThreadIconChangedEvent expected, RcsEvent actual) {
- if (!(actual instanceof RcsGroupThreadIconChangedEvent)) {
- return false;
- }
- RcsGroupThreadIconChangedEvent actualIconChangedEvent =
- (RcsGroupThreadIconChangedEvent) actual;
-
- return matchesGroupThreadEvent(expected, actualIconChangedEvent)
- && actualIconChangedEvent.getNewIcon().equals(expected.getNewIcon());
- }
-
- private boolean matchesGroupThreadEvent(
- RcsGroupThreadEvent expected, RcsGroupThreadEvent actual) {
- return matchesRcsEventFields(expected, actual)
- && actual.getOriginatingParticipant().getId()
- == expected.getOriginatingParticipant().getId()
- && actual.getRcsGroupThread().getThreadId()
- == expected.getRcsGroupThread().getThreadId();
- }
-
- private boolean matches(RcsParticipantAliasChangedEvent expected, RcsEvent actual) {
- if (!(actual instanceof RcsParticipantAliasChangedEvent)) {
- return false;
- }
- RcsParticipantAliasChangedEvent actualIconChangedEvent =
- (RcsParticipantAliasChangedEvent) actual;
-
- return matchesRcsEventFields(expected, actual)
- && actualIconChangedEvent.getParticipant().getId()
- == expected.getParticipant().getId()
- && actualIconChangedEvent.getNewAlias().equals(expected.getNewAlias());
- }
-
- private boolean matchesRcsEventFields(RcsEvent expected, RcsEvent actual) {
- return actual.getTimestamp() == expected.getTimestamp();
- }
-}
diff --git a/tests/tests/rcs/src/android/telephony/ims/cts/RcsParticipantTest.java b/tests/tests/rcs/src/android/telephony/ims/cts/RcsParticipantTest.java
deleted file mode 100644
index 7da8e63..0000000
--- a/tests/tests/rcs/src/android/telephony/ims/cts/RcsParticipantTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2019 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.telephony.ims.cts;
-
-import static android.provider.Telephony.RcsColumns.IS_RCS_TABLE_SCHEMA_CODE_COMPLETE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.telephony.ims.RcsMessageManager;
-import android.telephony.ims.RcsMessageStoreException;
-import android.telephony.ims.RcsParticipant;
-
-import androidx.test.InstrumentationRegistry;
-
-import org.junit.AfterClass;
-import org.junit.Assume;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-public class RcsParticipantTest {
- RcsMessageManager mRcsMessageManager;
- Context mContext;
-
- @BeforeClass
- public static void ensureDefaultSmsApp() {
- DefaultSmsAppHelper.ensureDefaultSmsApp();
- }
-
- @Before
- public void setupTestEnvironment() {
- // Used to skip tests for production builds without RCS tables, will be removed when
- // IS_RCS_TABLE_SCHEMA_CODE_COMPLETE flag is removed.
- Assume.assumeTrue(IS_RCS_TABLE_SCHEMA_CODE_COMPLETE);
-
- mContext = InstrumentationRegistry.getTargetContext();
- mRcsMessageManager = mContext.getSystemService(RcsMessageManager.class);
-
- cleanup();
- }
-
- @AfterClass
- public static void cleanup() {
- // TODO(b/123997749) should clean RCS message store here
- }
-
- @Test
- public void testCreateRcsParticipant_returnsValidParticipant() throws RcsMessageStoreException {
- String expectedCanonicalAddress = "+12223334444";
- String expectedAlias = "test_alias";
-
- createAndValidateParticipant(expectedCanonicalAddress, expectedAlias);
- }
-
- @Test
- public void testCreateRcsParticipant_shouldNotCrashForExistingCanonicalAddress()
- throws RcsMessageStoreException {
- String expectedCanonicalAddress = "+12223334444";
- String expectedAlias1 = "test_alias_1";
- String expectedAlias2 = "test_alias_2";
-
- createAndValidateParticipant(expectedCanonicalAddress, expectedAlias1);
- createAndValidateParticipant(expectedCanonicalAddress, expectedAlias2);
- }
-
- private void createAndValidateParticipant(String expectedCanonicalAddress,
- String expectedAlias) throws RcsMessageStoreException {
- RcsParticipant rcsParticipant =
- mRcsMessageManager.createRcsParticipant(expectedCanonicalAddress, expectedAlias);
-
- assertThat(rcsParticipant).isNotNull();
- assertThat(rcsParticipant.getId()).isGreaterThan(0);
- assertThat(rcsParticipant.getAlias()).isEqualTo(expectedAlias);
- }
-}
diff --git a/tests/tests/rcs/src/android/telephony/ims/cts/RcsProviderPermissionsTest.java b/tests/tests/rcs/src/android/telephony/ims/cts/RcsProviderPermissionsTest.java
deleted file mode 100644
index c1e0af0..0000000
--- a/tests/tests/rcs/src/android/telephony/ims/cts/RcsProviderPermissionsTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2019 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.telephony.ims.cts;
-
-import static android.provider.Telephony.RcsColumns.IS_RCS_TABLE_SCHEMA_CODE_COMPLETE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.ContentProviderClient;
-import android.content.Context;
-import android.provider.Telephony;
-
-import androidx.test.InstrumentationRegistry;
-
-import org.junit.Assert;
-import org.junit.Assume;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-public class RcsProviderPermissionsTest {
- @BeforeClass
- public static void ensureDefaultSmsApp() {
- DefaultSmsAppHelper.ensureDefaultSmsApp();
- }
-
- @Before
- public void setupTestEnvironment() {
- // Used to skip tests for production builds without RCS tables, will be removed when
- // IS_RCS_TABLE_SCHEMA_CODE_COMPLETE flag is removed.
- Assume.assumeTrue(IS_RCS_TABLE_SCHEMA_CODE_COMPLETE);
- }
-
- @Test
- public void testRcsProvider_shouldNotHaveAccess() {
- Context context = InstrumentationRegistry.getTargetContext();
-
- try (ContentProviderClient client =
- context.getContentResolver().acquireContentProviderClient(
- Telephony.RcsColumns.AUTHORITY)) {
- assertThat(client).isNull();
- } catch (SecurityException e) {
- return;
- }
- Assert.fail();
- }
-}
diff --git a/tests/tests/security/res/raw/sig_com_google_android_tzdata2.bin b/tests/tests/security/res/raw/sig_com_google_android_tzdata2.bin
new file mode 100644
index 0000000..95b0e10
--- /dev/null
+++ b/tests/tests/security/res/raw/sig_com_google_android_tzdata2.bin
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/CertificateData.java b/tests/tests/security/src/android/security/cts/CertificateData.java
index ef27222..0b4780f 100644
--- a/tests/tests/security/src/android/security/cts/CertificateData.java
+++ b/tests/tests/security/src/android/security/cts/CertificateData.java
@@ -33,14 +33,12 @@
"92:5A:8F:8D:2C:6D:04:E0:66:5F:59:6A:FF:22:D8:63:E8:25:6F:3F",
"75:E0:AB:B6:13:85:12:27:1C:04:F8:5F:DD:DE:38:E4:B7:24:2E:FE",
"DA:C9:02:4F:54:D8:F6:DF:94:93:5F:B1:73:26:38:CA:6A:D7:7C:13",
- "74:20:74:41:72:9C:DD:92:EC:79:31:D8:23:10:8D:C2:81:92:E2:BB",
"F4:8B:11:BF:DE:AB:BE:94:54:20:71:E6:41:DE:6B:BE:88:2B:40:B9",
"58:E8:AB:B0:36:15:33:FB:80:F7:9B:1B:6D:29:D3:FF:8D:5F:00:F0",
"55:A6:72:3E:CB:F2:EC:CD:C3:23:74:70:19:9D:2A:BE:11:E3:81:D1",
"D6:9B:56:11:48:F0:1C:77:C5:45:78:C1:09:26:DF:5B:85:69:76:AD",
"78:6A:74:AC:76:AB:14:7F:9C:6A:30:50:BA:9E:A8:7E:FE:9A:CE:3C",
"09:3C:61:F3:8B:8B:DC:7D:55:DF:75:38:02:05:00:E1:25:F5:C8:36",
- "8E:1C:74:F8:A6:20:B9:E5:8A:F4:61:FA:EC:2B:47:56:51:1A:52:C6",
"27:96:BA:E6:3F:18:01:E2:77:26:1B:A0:D7:77:70:02:8F:20:EE:E4",
"AD:7E:1C:28:B0:64:EF:8F:60:03:40:20:14:C3:D0:E3:37:0E:B5:8A",
"8D:17:84:D5:37:F3:03:7D:EC:70:FE:57:8B:51:9A:99:E6:10:D7:B0",
@@ -67,11 +65,9 @@
"8C:F4:27:FD:79:0C:3A:D1:66:06:8D:E8:1E:57:EF:BB:93:22:72:D4",
"2F:78:3D:25:52:18:A7:4A:65:39:71:B5:2C:A2:9C:45:15:6F:E9:19",
"BA:29:41:60:77:98:3F:F4:F3:EF:F2:31:05:3B:2E:EA:6D:4D:45:FD",
- "85:A4:08:C0:9C:19:3E:5D:51:58:7D:CD:D6:13:30:FD:8C:DE:37:BF",
"9B:AA:E5:9F:56:EE:21:CB:43:5A:BE:25:93:DF:A7:F0:40:D1:1D:CB",
"36:79:CA:35:66:87:72:30:4D:30:A5:FB:87:3B:0F:A7:7B:B7:0D:54",
"1B:8E:EA:57:96:29:1A:C9:39:EA:B8:0A:81:1A:73:73:C0:93:79:67",
- "6E:26:64:F3:56:BF:34:55:BF:D1:93:3F:7C:01:DE:D8:13:DA:8A:A6",
"74:3A:F0:52:9B:D0:32:A0:F4:4A:83:CD:D4:BA:A9:7B:7C:2E:C4:9A",
"D8:EB:6B:41:51:92:59:E0:F3:E7:85:00:C0:3D:B6:88:97:C9:EE:FC",
"66:31:BF:9E:F7:4F:9E:B6:C9:D5:A6:0C:BA:6A:BE:D1:F7:BD:EF:7B",
@@ -97,6 +93,7 @@
"49:0A:75:74:DE:87:0A:47:FE:58:EE:F6:C7:6B:EB:C6:0B:12:40:99",
"B5:1C:06:7C:EE:2B:0C:3D:F8:55:AB:2D:92:F4:FE:39:D4:E7:0F:0E",
"29:36:21:02:8B:20:ED:02:F5:66:C5:32:D1:D6:ED:90:9F:45:00:2F",
+ "B6:AF:43:C2:9B:81:53:7D:F6:EF:6B:C3:1F:1F:60:15:0C:EE:48:66",
"37:9A:19:7B:41:85:45:35:0C:A6:03:69:F3:3C:2E:AF:47:4F:20:79",
"FA:B7:EE:36:97:26:62:FB:2D:B0:2A:F6:BF:03:FD:E8:7C:4B:2F:9B",
"C3:19:7C:39:24:E6:54:AF:1B:C4:AB:20:95:7A:E2:C3:0E:13:02:6A",
@@ -124,10 +121,12 @@
"9C:BB:48:53:F6:A4:F6:D3:52:A4:E8:32:52:55:60:13:F5:AD:AF:65",
"B1:BC:96:8B:D4:F4:9D:62:2A:A8:9A:81:F2:15:01:52:A4:1D:82:9C",
"20:D8:06:40:DF:9B:25:F5:12:25:3A:11:EA:F7:59:8A:EB:14:B5:47",
+ "30:43:FA:4F:F2:57:DC:A0:C3:80:EE:2E:58:EA:78:B2:3F:E6:BB:C1",
"CF:9E:87:6D:D3:EB:FC:42:26:97:A3:B5:A3:7A:A0:76:A9:06:23:48",
"2B:B1:F5:3E:55:0C:1D:C5:F1:D4:E6:B7:6A:46:4B:55:06:02:AC:21",
"EC:50:35:07:B2:15:C4:95:62:19:E2:A8:9A:5B:42:99:2C:4C:2C:20",
"47:BE:AB:C9:22:EA:E8:0E:78:78:34:62:A7:9F:45:C2:54:FD:E6:8B",
+ "58:A2:D0:EC:20:52:81:5B:C1:F3:F8:64:02:24:4E:C2:8E:02:4B:02",
"3A:44:73:5A:E5:81:90:1F:24:86:61:46:1E:3B:9C:C4:5F:F5:3A:1B",
"B3:1E:B1:B7:40:E3:6C:84:02:DA:DC:37:D4:4D:F5:D4:67:49:52:F9",
"58:D1:DF:95:95:67:6B:63:C0:F0:5B:1C:17:4D:8B:84:0B:C8:78:BD",
@@ -148,11 +147,10 @@
"F6:10:84:07:D6:F8:BB:67:98:0C:C2:E2:44:C2:EB:AE:1C:EF:63:BE",
"AF:E5:D2:44:A8:D1:19:42:30:FF:47:9F:E2:F8:97:BB:CD:7A:8C:B4",
"5F:3B:8C:F2:F8:10:B3:7D:78:B4:CE:EC:19:19:C3:73:34:B9:C7:74",
- "9D:70:BB:01:A5:A4:A0:18:11:2E:F7:1C:01:B9:32:C5:34:E7:88:A8",
"96:C9:1B:0B:95:B4:10:98:42:FA:D0:D8:22:79:FE:60:FA:B9:16:83",
- "4F:65:8E:1F:E9:06:D8:28:02:E9:54:47:41:C9:54:25:5D:69:CC:1A",
"A3:A1:B0:6F:24:61:23:4A:E3:36:A5:C2:37:FC:A6:FF:DD:F0:D7:3A",
"D8:A6:33:2C:E0:03:6F:B1:85:F6:63:4F:7D:6A:06:65:26:32:28:27",
+ "E7:2E:F1:DF:FC:B2:09:28:CF:5D:D4:D5:67:37:B1:51:CB:86:4F:01",
"01:0C:06:95:A6:98:19:14:FF:BF:5F:C6:B0:B6:95:EA:29:E9:12:A6",
"0F:F9:40:76:18:D3:D7:6A:4B:98:F0:A8:35:9E:0C:FD:27:AC:CC:ED",
"48:12:BD:92:3C:A8:C4:39:06:E7:30:6D:27:96:E6:A4:CF:22:2E:7D",
@@ -161,6 +159,8 @@
"89:DF:74:FE:5C:F4:0F:4A:80:F9:E3:37:7D:54:DA:91:E1:01:31:8E",
"7E:04:DE:89:6A:3E:66:6D:00:E6:87:D3:3F:FA:D9:3B:E8:3D:34:9E",
"E1:C9:50:E6:EF:22:F8:4C:56:45:72:8B:92:20:60:D7:D5:A7:A3:E8",
+ "14:88:4E:86:26:37:B0:26:AF:59:62:5C:40:77:EC:35:29:BA:96:01",
+ "8A:C7:AD:8F:73:AC:4E:C1:B5:75:4D:A5:40:F4:FC:CF:7C:B5:8E:8C",
"4E:B6:D5:78:49:9B:1C:CF:5F:58:1E:AD:56:BE:3D:9B:67:44:A5:E5",
"5A:8C:EF:45:D7:A6:98:59:76:7A:8C:8B:44:96:B5:78:CF:47:4B:1A",
"8D:A7:F9:65:EC:5E:FC:37:91:0F:1C:6E:59:FD:C1:CC:6A:6E:DE:16",
diff --git a/tests/tests/security/src/android/security/cts/NanoAppBundleTest.java b/tests/tests/security/src/android/security/cts/NanoAppBundleTest.java
index b621491..e7ab8eb 100644
--- a/tests/tests/security/src/android/security/cts/NanoAppBundleTest.java
+++ b/tests/tests/security/src/android/security/cts/NanoAppBundleTest.java
@@ -130,7 +130,7 @@
ActivityInfo info = intent.resolveActivityInfo(
mContext.getPackageManager(), intent.getFlags());
// Will throw NullPointerException if activity not found.
- if (info.exported) {
+ if (info != null && info.exported) {
mContext.startActivity(intent);
} else {
Log.i(TAG, "Activity is not exported");
diff --git a/tests/tests/security/src/android/security/cts/PackageSignatureTest.java b/tests/tests/security/src/android/security/cts/PackageSignatureTest.java
index 283910bf..3aec394 100644
--- a/tests/tests/security/src/android/security/cts/PackageSignatureTest.java
+++ b/tests/tests/security/src/android/security/cts/PackageSignatureTest.java
@@ -70,6 +70,14 @@
badPackages.isEmpty());
}
+ /**
+ * Returns the well-known dev-key signatures, e.g. to detect cases where devices under test are
+ * using modules that have been signed using dev keys; Google will supply modules that have been
+ * signed with production keys in all cases.
+ *
+ * <p>See {@link #writeSignature(String, String)} for instructions for how to create the raw
+ * .bin files when adding entries to this list.
+ */
private Set<Signature> getWellKnownSignatures() throws NotFoundException, IOException {
Set<Signature> wellKnownSignatures = new HashSet<Signature>();
wellKnownSignatures.add(getSignature(R.raw.sig_media));
@@ -95,6 +103,9 @@
wellKnownSignatures.add(getSignature(R.raw.sig_com_google_android_resolv));
wellKnownSignatures.add(getSignature(R.raw.sig_com_google_android_runtime_debug));
wellKnownSignatures.add(getSignature(R.raw.sig_com_google_android_runtime_release));
+ wellKnownSignatures.add(getSignature(R.raw.sig_com_google_android_tzdata2));
+ // The following keys are no longer in use by modules, but it won't negatively affect tests
+ // to include their signatures here too.
wellKnownSignatures.add(getSignature(R.raw.sig_com_google_android_tzdata));
return wellKnownSignatures;
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
index 10cd943..8185462 100644
--- a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
@@ -455,6 +455,56 @@
assertConnectionState(connection, Connection.STATE_DISCONNECTED);
}
+ public void testRejectIncomingCallWithUnwantedReason() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+
+ addAndVerifyNewIncomingCall(createTestNumber(), null);
+ final MockConnection connection = verifyConnectionForIncomingCall();
+
+ final MockInCallService inCallService = mInCallCallbacks.getService();
+
+ final Call call = inCallService.getLastCall();
+
+ assertCallState(call, Call.STATE_RINGING);
+ assertConnectionState(connection, Connection.STATE_RINGING);
+
+ call.reject(Call.REJECT_REASON_UNWANTED);
+
+ assertCallState(call, Call.STATE_DISCONNECTED);
+ assertConnectionState(connection, Connection.STATE_DISCONNECTED);
+ // The mock connection just stashes the reject reason in the disconnect cause string reason
+ // for tracking purposes.
+ assertEquals(Integer.toString(Call.REJECT_REASON_UNWANTED),
+ connection.getDisconnectCause().getReason());
+ }
+
+ public void testRejectIncomingCallWithDeclinedReason() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+
+ addAndVerifyNewIncomingCall(createTestNumber(), null);
+ final MockConnection connection = verifyConnectionForIncomingCall();
+
+ final MockInCallService inCallService = mInCallCallbacks.getService();
+
+ final Call call = inCallService.getLastCall();
+
+ assertCallState(call, Call.STATE_RINGING);
+ assertConnectionState(connection, Connection.STATE_RINGING);
+
+ call.reject(Call.REJECT_REASON_DECLINED);
+
+ assertCallState(call, Call.STATE_DISCONNECTED);
+ assertConnectionState(connection, Connection.STATE_DISCONNECTED);
+ // The mock connection just stashes the reject reason in the disconnect cause string reason
+ // for tracking purposes.
+ assertEquals(Integer.toString(Call.REJECT_REASON_DECLINED),
+ connection.getDisconnectCause().getReason());
+ }
+
public void testRejectIncomingCallWithMessage() {
if (!mShouldTestTelecom) {
return;
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockConnection.java b/tests/tests/telecom/src/android/telecom/cts/MockConnection.java
index 1704db9..463eb40 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockConnection.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockConnection.java
@@ -82,6 +82,14 @@
}
@Override
+ public void onReject(int rejectReason) {
+ super.onReject(rejectReason);
+ setDisconnected(new DisconnectCause(DisconnectCause.REJECTED,
+ Integer.toString(rejectReason)));
+ destroy();
+ }
+
+ @Override
public void onReject(String reason) {
super.onReject();
setDisconnected(new DisconnectCause(DisconnectCause.REJECTED, reason));
diff --git a/tests/tests/telephony/current/AndroidTest.xml b/tests/tests/telephony/current/AndroidTest.xml
index 326b336..99aa5cb 100644
--- a/tests/tests/telephony/current/AndroidTest.xml
+++ b/tests/tests/telephony/current/AndroidTest.xml
@@ -34,6 +34,7 @@
<option name="test-file-name" value="TestSmsRetrieverApp.apk"/>
<option name="test-file-name" value="TestFinancialSmsApp.apk"/>
<option name="test-file-name" value="LocationAccessingApp.apk"/>
+ <option name="test-file-name" value="LocationAccessingAppSdk28.apk"/>
<option name="test-file-name" value="TestExternalImsServiceApp.apk"/>
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/tests/tests/telephony/current/LocationAccessingApp/Android.bp b/tests/tests/telephony/current/LocationAccessingApp/Android.bp
index 5604c75..da9ffce 100644
--- a/tests/tests/telephony/current/LocationAccessingApp/Android.bp
+++ b/tests/tests/telephony/current/LocationAccessingApp/Android.bp
@@ -2,8 +2,7 @@
name: "LocationAccessingApp",
defaults: ["cts_defaults"],
srcs: [
- "src/**/*.java",
- "aidl/**/I*.aidl",
+ ":location_accessing_app_srcs",
],
aidl: {
local_include_dirs: ["aidl/"],
@@ -15,3 +14,11 @@
"mts",
],
}
+
+filegroup {
+ name: "location_accessing_app_srcs",
+ srcs: [
+ "src/**/*.java",
+ "aidl/**/I*.aidl",
+ ],
+}
\ No newline at end of file
diff --git a/tests/tests/telephony/current/LocationAccessingApp/sdk28/Android.bp b/tests/tests/telephony/current/LocationAccessingApp/sdk28/Android.bp
new file mode 100644
index 0000000..ed6d2a6
--- /dev/null
+++ b/tests/tests/telephony/current/LocationAccessingApp/sdk28/Android.bp
@@ -0,0 +1,13 @@
+android_test {
+ name: "LocationAccessingAppSdk28",
+ defaults: ["cts_defaults"],
+ srcs: [
+ ":location_accessing_app_srcs",
+ ],
+ sdk_version: "test_current",
+ test_suites: [
+ "cts",
+ "vts",
+ "mts",
+ ],
+}
diff --git a/tests/tests/telephony/current/LocationAccessingApp/sdk28/AndroidManifest.xml b/tests/tests/telephony/current/LocationAccessingApp/sdk28/AndroidManifest.xml
new file mode 100644
index 0000000..811d9ce
--- /dev/null
+++ b/tests/tests/telephony/current/LocationAccessingApp/sdk28/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.telephony.cts.locationaccessingapp.sdk28">
+
+ <uses-sdk android:targetSdkVersion="28"/>
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+
+ <application android:label="LocationAccessingAppSdk28">
+ <service android:name="android.telephony.cts.locationaccessingapp.CtsLocationAccessService"
+ android:launchMode="singleInstance">
+ <intent-filter>
+ <action android:name="android.telephony.cts.locationaccessingapp.ACTION_CONTROL" />
+ </intent-filter>
+ </service>
+ </application>
+</manifest>
+
diff --git a/tests/tests/telephony/current/LocationAccessingApp/src/android/telephony/cts/locationaccessingapp/CtsLocationAccessService.java b/tests/tests/telephony/current/LocationAccessingApp/src/android/telephony/cts/locationaccessingapp/CtsLocationAccessService.java
index fa9608a..861de55 100644
--- a/tests/tests/telephony/current/LocationAccessingApp/src/android/telephony/cts/locationaccessingapp/CtsLocationAccessService.java
+++ b/tests/tests/telephony/current/LocationAccessingApp/src/android/telephony/cts/locationaccessingapp/CtsLocationAccessService.java
@@ -18,6 +18,7 @@
import android.app.Service;
import android.content.Intent;
+import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -27,6 +28,8 @@
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
+import android.telephony.cdma.CdmaCellLocation;
+import android.telephony.gsm.GsmCellLocation;
import java.util.Collections;
import java.util.List;
@@ -60,7 +63,17 @@
result = mTelephonyManager.getAllCellInfo();
break;
case COMMAND_GET_CELL_LOCATION:
- result = mTelephonyManager.getCellLocation();
+ result = new Bundle();
+ CellLocation cellLocation = mTelephonyManager.getCellLocation();
+ if (cellLocation instanceof GsmCellLocation) {
+ ((GsmCellLocation) cellLocation).fillInNotifierBundle((Bundle) result);
+ } else if (cellLocation instanceof CdmaCellLocation) {
+ ((CdmaCellLocation) cellLocation).fillInNotifierBundle((Bundle) result);
+ } else if (cellLocation == null) {
+ result = null;
+ } else {
+ throw new RuntimeException("Unexpected celllocation type");
+ }
break;
case COMMAND_GET_SERVICE_STATE_FROM_LISTENER:
result = listenForServiceState();
diff --git a/tests/tests/telephony/current/TestExternalImsServiceApp/aidl/android/telephony/cts/externalimsservice/ITestExternalImsService.aidl b/tests/tests/telephony/current/TestExternalImsServiceApp/aidl/android/telephony/cts/externalimsservice/ITestExternalImsService.aidl
index e8bd95c..2529ada 100644
--- a/tests/tests/telephony/current/TestExternalImsServiceApp/aidl/android/telephony/cts/externalimsservice/ITestExternalImsService.aidl
+++ b/tests/tests/telephony/current/TestExternalImsServiceApp/aidl/android/telephony/cts/externalimsservice/ITestExternalImsService.aidl
@@ -29,14 +29,4 @@
boolean isRcsFeatureCreated();
boolean isMmTelFeatureCreated();
void resetState();
- void notifyRcsCapabilitiesStatusChanged(int capability);
- boolean isRcsCapable(int capability, int radioTech);
- boolean isRcsAvailable(int capability);
- String getConfigString(int subId, int item);
-
- // IMS registration status changed
- void triggerImsOnRegistered(int radioTech);
- void triggerImsOnRegistering(int radioTech);
- void triggerImsOnDeregistered(in ImsReasonInfo info);
- void triggerImsOnTechnologyChangeFailed(int imsRadioTech, in ImsReasonInfo info);
}
diff --git a/tests/tests/telephony/current/TestExternalImsServiceApp/src/android/telephony/cts/externalimsservice/TestExternalImsService.java b/tests/tests/telephony/current/TestExternalImsServiceApp/src/android/telephony/cts/externalimsservice/TestExternalImsService.java
index b0bbdfb..9101ae5 100644
--- a/tests/tests/telephony/current/TestExternalImsServiceApp/src/android/telephony/cts/externalimsservice/TestExternalImsService.java
+++ b/tests/tests/telephony/current/TestExternalImsServiceApp/src/android/telephony/cts/externalimsservice/TestExternalImsService.java
@@ -18,13 +18,9 @@
import android.content.Intent;
import android.os.IBinder;
-import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.cts.ImsUtils;
import android.telephony.ims.cts.TestImsService;
-import android.telephony.ims.feature.RcsFeature.RcsImsCapabilities;
-import android.telephony.ims.stub.ImsConfigImplBase;
import android.telephony.ims.stub.ImsFeatureConfiguration;
-import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.Log;
/**
@@ -60,49 +56,6 @@
public void resetState() {
TestExternalImsService.this.resetState();
}
-
- public String getConfigString(int subId, int item) {
- ImsConfigImplBase imsConfig = TestExternalImsService.this.getImsService().getConfig(
- subId);
- if (imsConfig != null) {
- return imsConfig.getConfigString(item);
- }
- return null;
- }
-
- public void triggerImsOnRegistered(int imsRadioTech) {
- ImsRegistrationImplBase imsReg = TestExternalImsService.this.getImsRegistration();
- imsReg.onRegistered(imsRadioTech);
- }
-
- public void triggerImsOnRegistering(int imsRadioTech) {
- ImsRegistrationImplBase imsReg = TestExternalImsService.this.getImsRegistration();
- imsReg.onRegistering(imsRadioTech);
- }
-
- public void triggerImsOnDeregistered(ImsReasonInfo info) {
- ImsRegistrationImplBase imsReg = TestExternalImsService.this.getImsRegistration();
- imsReg.onDeregistered(info);
- }
-
- public void triggerImsOnTechnologyChangeFailed(int imsRadioTech, ImsReasonInfo info) {
- ImsRegistrationImplBase imsReg = TestExternalImsService.this.getImsRegistration();
- imsReg.onTechnologyChangeFailed(imsRadioTech, info);
- }
-
- public void notifyRcsCapabilitiesStatusChanged(int capability) {
- RcsImsCapabilities capabilities = new RcsImsCapabilities(capability);
- getRcsFeature().notifyCapabilitiesStatusChanged(capabilities);
- }
-
- public boolean isRcsCapable(int capability, int radioTech) {
- return getRcsFeature().queryCapabilityConfiguration(capability, radioTech);
- }
-
- public boolean isRcsAvailable(int capability) {
- RcsImsCapabilities capabilityStatus = getRcsFeature().queryCapabilityStatus();
- return capabilityStatus.isCapable(capability);
- }
}
@Override
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/CellBroadcastIntentsTest.java b/tests/tests/telephony/current/src/android/telephony/cts/CellBroadcastIntentsTest.java
index 3ca8ee4..4035372 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/CellBroadcastIntentsTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CellBroadcastIntentsTest.java
@@ -19,42 +19,59 @@
import static junit.framework.Assert.fail;
-import android.content.Intent;
import android.os.UserHandle;
+import android.telephony.CbGeoUtils.Geometry;
import android.telephony.CellBroadcastIntents;
+import android.telephony.SmsCbEtwsInfo;
+import android.telephony.SmsCbLocation;
+import android.telephony.SmsCbMessage;
import androidx.test.InstrumentationRegistry;
-import com.android.compatibility.common.util.ShellIdentityUtils;
+import com.android.internal.telephony.gsm.SmsCbConstants;
import org.junit.Test;
+import java.util.ArrayList;
+import java.util.List;
+
public class CellBroadcastIntentsTest {
- private static final String TEST_ACTION = "test_action";
+ private static final int TEST_MESSAGE_FORMAT = SmsCbMessage.MESSAGE_FORMAT_3GPP2;
+ private static final int TEST_GEO_SCOPE = SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE;
+ private static final int TEST_SERIAL = 1234;
+ private static final String TEST_PLMN = "111222";
+ private static final SmsCbLocation TEST_LOCATION = new SmsCbLocation(TEST_PLMN, -1, -1);
+ private static final int TEST_SERVICE_CATEGORY = 4097;
+ private static final String TEST_LANGUAGE = "en";
+ private static final String TEST_BODY = "test body";
+ private static final int TEST_PRIORITY = 0;
+ private static final int TEST_ETWS_WARNING_TYPE =
+ SmsCbConstants.MESSAGE_ID_ETWS_OTHER_EMERGENCY_TYPE;
+ private static final SmsCbEtwsInfo TEST_ETWS_INFO = new SmsCbEtwsInfo(TEST_ETWS_WARNING_TYPE,
+ false, false, false, null);
+
+ private static final int TEST_MAX_WAIT_TIME = 0;
+ private static final List<Geometry> TEST_GEOS = new ArrayList<>();
+ private static final int TEST_RECEIVED_TIME = 11000;
+ private static final int TEST_SLOT = 0;
+ private static final int TEST_SUB_ID = 1;
@Test
public void testGetIntentForBackgroundReceivers() {
try {
- CellBroadcastIntents.sendOrderedBroadcastForBackgroundReceivers(
- InstrumentationRegistry.getContext(), UserHandle.ALL, new Intent(TEST_ACTION),
- null, null, null, null, 0, null, null);
+ SmsCbMessage message = new SmsCbMessage(TEST_MESSAGE_FORMAT, TEST_GEO_SCOPE,
+ TEST_SERIAL, TEST_LOCATION, TEST_SERVICE_CATEGORY, TEST_LANGUAGE, TEST_BODY,
+ TEST_PRIORITY, TEST_ETWS_INFO, null, TEST_MAX_WAIT_TIME, TEST_GEOS,
+ TEST_RECEIVED_TIME, TEST_SLOT, TEST_SUB_ID);
+
+ CellBroadcastIntents.sendSmsCbReceivedBroadcast(
+ InstrumentationRegistry.getContext(), UserHandle.ALL, message,
+ null, null, 0, TEST_SLOT);
} catch (SecurityException e) {
// expected
return;
}
fail();
}
-
- @Test
- public void testGetIntentForBackgroundReceiversWithPermission() {
- ShellIdentityUtils.invokeStaticMethodWithShellPermissions(
- () -> {
- CellBroadcastIntents.sendOrderedBroadcastForBackgroundReceivers(
- InstrumentationRegistry.getContext(), UserHandle.ALL,
- new Intent(TEST_ACTION),
- null, null, null, null, 0, null, null);
- return true;
- });
- }
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
index 4959248..af2d19b 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -42,6 +42,7 @@
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Build;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.os.PersistableBundle;
@@ -53,6 +54,7 @@
import android.telephony.Annotation.RadioPowerState;
import android.telephony.AvailableNetworkInfo;
import android.telephony.CallAttributes;
+import android.telephony.CallForwardingInfo;
import android.telephony.CallQuality;
import android.telephony.CarrierConfigManager;
import android.telephony.CellLocation;
@@ -165,6 +167,8 @@
private static final int MAX_FPLMN_NUM = 100;
private static final int MIN_FPLMN_NUM = 3;
+ private static final String TEST_FORWARD_NUMBER = "54321";
+
static {
EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>();
EMERGENCY_NUMBER_SOURCE_SET.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING);
@@ -187,6 +191,12 @@
EMERGENCY_SERVICE_CATEGORY_SET.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC);
}
+ private static final String LOCATION_ACCESS_APP_CURRENT_PACKAGE =
+ CtsLocationAccessService.class.getPackage().getName();
+
+ private static final String LOCATION_ACCESS_APP_SDK28_PACKAGE =
+ CtsLocationAccessService.class.getPackage().getName() + ".sdk28";
+
private int mTestSub;
private TelephonyManagerTest.CarrierConfigReceiver mReceiver;
@@ -517,6 +527,8 @@
(tm) -> tm.isDataConnectionEnabled());
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.isAnyRadioPoweredOn());
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+ (tm) -> tm.resetIms(tm.getSlotIndex()));
// Verify TelephonyManager.getCarrierPrivilegedPackagesForAllActiveSubscriptions
List<String> resultForGetCarrierPrivilegedApis =
@@ -533,11 +545,106 @@
}
@Test
+ public void testGetCallForwarding() {
+ List<Integer> callForwardingReasons = new ArrayList<>();
+ callForwardingReasons.add(CallForwardingInfo.REASON_UNCONDITIONAL);
+ callForwardingReasons.add(CallForwardingInfo.REASON_BUSY);
+ callForwardingReasons.add(CallForwardingInfo.REASON_NO_REPLY);
+ callForwardingReasons.add(CallForwardingInfo.REASON_NOT_REACHABLE);
+ callForwardingReasons.add(CallForwardingInfo.REASON_ALL);
+ callForwardingReasons.add(CallForwardingInfo.REASON_ALL_CONDITIONAL);
+
+ Set<Integer> callForwardingStatus = new HashSet<Integer>();
+ callForwardingStatus.add(CallForwardingInfo.STATUS_INACTIVE);
+ callForwardingStatus.add(CallForwardingInfo.STATUS_ACTIVE);
+ callForwardingStatus.add(CallForwardingInfo.STATUS_FDN_CHECK_FAILURE);
+ callForwardingStatus.add(CallForwardingInfo.STATUS_UNKNOWN_ERROR);
+ callForwardingStatus.add(CallForwardingInfo.STATUS_NOT_SUPPORTED);
+
+ for (int callForwardingReasonToGet : callForwardingReasons) {
+ Log.d(TAG, "[testGetCallForwarding] callForwardingReasonToGet: "
+ + callForwardingReasonToGet);
+ CallForwardingInfo callForwardingInfo =
+ ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ (tm) -> tm.getCallForwarding(callForwardingReasonToGet));
+
+ assertNotNull(callForwardingInfo);
+ assertTrue(callForwardingStatus.contains(callForwardingInfo.getStatus()));
+ assertTrue(callForwardingReasons.contains(callForwardingInfo.getReason()));
+ callForwardingInfo.getNumber();
+ assertTrue(callForwardingInfo.getTimeoutSeconds() >= 0);
+ }
+ }
+
+ @Test
+ public void testSetCallForwarding() {
+ List<Integer> callForwardingReasons = new ArrayList<>();
+ callForwardingReasons.add(CallForwardingInfo.REASON_UNCONDITIONAL);
+ callForwardingReasons.add(CallForwardingInfo.REASON_BUSY);
+ callForwardingReasons.add(CallForwardingInfo.REASON_NO_REPLY);
+ callForwardingReasons.add(CallForwardingInfo.REASON_NOT_REACHABLE);
+ callForwardingReasons.add(CallForwardingInfo.REASON_ALL);
+ callForwardingReasons.add(CallForwardingInfo.REASON_ALL_CONDITIONAL);
+
+ // Enable Call Forwarding
+ for (int callForwardingReasonToEnable : callForwardingReasons) {
+ final CallForwardingInfo callForwardingInfoToEnable = new CallForwardingInfo(
+ CallForwardingInfo.STATUS_ACTIVE,
+ callForwardingReasonToEnable,
+ TEST_FORWARD_NUMBER,
+ 1 /** time seconds */);
+ Log.d(TAG, "[testSetCallForwarding] Enable Call Forwarding. Status: "
+ + CallForwardingInfo.STATUS_ACTIVE + " Reason: " + callForwardingReasonToEnable
+ + " Number: " + TEST_FORWARD_NUMBER + " Time Seconds: 1");
+ ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ (tm) -> tm.setCallForwarding(callForwardingInfoToEnable));
+ }
+
+ // Disable Call Forwarding
+ for (int callForwardingReasonToDisable : callForwardingReasons) {
+ final CallForwardingInfo callForwardingInfoToDisable = new CallForwardingInfo(
+ CallForwardingInfo.STATUS_INACTIVE,
+ callForwardingReasonToDisable,
+ TEST_FORWARD_NUMBER,
+ 1 /** time seconds */);
+ Log.d(TAG, "[testSetCallForwarding] Disable Call Forwarding. Status: "
+ + CallForwardingInfo.STATUS_INACTIVE + " Reason: "
+ + callForwardingReasonToDisable + " Number: " + TEST_FORWARD_NUMBER
+ + " Time Seconds: 1");
+ ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ (tm) -> tm.setCallForwarding(callForwardingInfoToDisable));
+ }
+ }
+
+ @Test
+ public void testGetCallWaitingStatus() {
+ Set<Integer> callWaitingStatus = new HashSet<Integer>();
+ callWaitingStatus.add(TelephonyManager.CALL_WAITING_STATUS_ACTIVE);
+ callWaitingStatus.add(TelephonyManager.CALL_WAITING_STATUS_INACTIVE);
+ callWaitingStatus.add(TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR);
+ callWaitingStatus.add(TelephonyManager.CALL_WAITING_STATUS_NOT_SUPPORTED);
+
+ int status = ShellIdentityUtils.invokeMethodWithShellPermissions(
+ mTelephonyManager, (tm) -> tm.getCallWaitingStatus());
+ assertTrue(callWaitingStatus.contains(status));
+ }
+
+ @Test
+ public void testSetCallWaitingStatus() {
+ ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ (tm) -> tm.setCallWaitingStatus(true));
+ ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ (tm) -> tm.setCallWaitingStatus(false));
+ }
+
+ @Test
public void testCellLocationFinePermission() {
- withRevokedPermission(() -> {
+ withRevokedPermission(LOCATION_ACCESS_APP_CURRENT_PACKAGE, () -> {
try {
- CellLocation cellLocation = (CellLocation) performLocationAccessCommand(
+ Bundle cellLocationBundle = (Bundle) performLocationAccessCommand(
CtsLocationAccessService.COMMAND_GET_CELL_LOCATION);
+ CellLocation cellLocation = cellLocationBundle == null ? null :
+ CellLocation.newFromBundle(cellLocationBundle);
assertTrue(cellLocation == null || cellLocation.isEmpty());
} catch (SecurityException e) {
// expected
@@ -555,12 +662,12 @@
@Test
public void testServiceStateLocationSanitization() {
- withRevokedPermission(() -> {
+ withRevokedPermission(LOCATION_ACCESS_APP_CURRENT_PACKAGE, () -> {
ServiceState ss = (ServiceState) performLocationAccessCommand(
CtsLocationAccessService.COMMAND_GET_SERVICE_STATE);
assertServiceStateSanitization(ss, true);
- withRevokedPermission(() -> {
+ withRevokedPermission(LOCATION_ACCESS_APP_CURRENT_PACKAGE, () -> {
ServiceState ss1 = (ServiceState) performLocationAccessCommand(
CtsLocationAccessService.COMMAND_GET_SERVICE_STATE);
assertServiceStateSanitization(ss1, false);
@@ -574,12 +681,12 @@
public void testServiceStateListeningWithoutPermissions() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) return;
- withRevokedPermission(() -> {
+ withRevokedPermission(LOCATION_ACCESS_APP_CURRENT_PACKAGE, () -> {
ServiceState ss = (ServiceState) performLocationAccessCommand(
CtsLocationAccessService.COMMAND_GET_SERVICE_STATE_FROM_LISTENER);
assertServiceStateSanitization(ss, true);
- withRevokedPermission(() -> {
+ withRevokedPermission(LOCATION_ACCESS_APP_CURRENT_PACKAGE, () -> {
ServiceState ss1 = (ServiceState) performLocationAccessCommand(
CtsLocationAccessService
.COMMAND_GET_SERVICE_STATE_FROM_LISTENER);
@@ -592,7 +699,7 @@
@Test
public void testRegistryPermissionsForCellLocation() {
- withRevokedPermission(() -> {
+ withRevokedPermission(LOCATION_ACCESS_APP_CURRENT_PACKAGE, () -> {
CellLocation cellLocation = (CellLocation) performLocationAccessCommand(
CtsLocationAccessService.COMMAND_LISTEN_CELL_LOCATION);
assertNull(cellLocation);
@@ -602,7 +709,7 @@
@Test
public void testRegistryPermissionsForCellInfo() {
- withRevokedPermission(() -> {
+ withRevokedPermission(LOCATION_ACCESS_APP_CURRENT_PACKAGE, () -> {
CellLocation cellLocation = (CellLocation) performLocationAccessCommand(
CtsLocationAccessService.COMMAND_LISTEN_CELL_INFO);
assertNull(cellLocation);
@@ -610,11 +717,51 @@
Manifest.permission.ACCESS_FINE_LOCATION);
}
+ @Test
+ public void testSdk28CellLocation() {
+ // Verify that a target-sdk 28 app can access cell location with ACCESS_COARSE_LOCATION, but
+ // not with no location permissions at all.
+ withRevokedPermission(LOCATION_ACCESS_APP_SDK28_PACKAGE, () -> {
+ try {
+ performLocationAccessCommandSdk28(
+ CtsLocationAccessService.COMMAND_GET_CELL_LOCATION);
+ } catch (SecurityException e) {
+ fail("SDK28 should have access to cell location with coarse permission");
+ }
+
+ withRevokedPermission(LOCATION_ACCESS_APP_SDK28_PACKAGE, () -> {
+ try {
+ Bundle cellLocationBundle = (Bundle) performLocationAccessCommandSdk28(
+ CtsLocationAccessService.COMMAND_GET_CELL_LOCATION);
+ CellLocation cellLocation = cellLocationBundle == null ? null :
+ CellLocation.newFromBundle(cellLocationBundle);
+ assertTrue(cellLocation == null || cellLocation.isEmpty());
+ } catch (SecurityException e) {
+ // expected
+ }
+ }, Manifest.permission.ACCESS_COARSE_LOCATION);
+ }, Manifest.permission.ACCESS_FINE_LOCATION);
+
+ }
private ICtsLocationAccessControl getLocationAccessAppControl() {
Intent bindIntent = new Intent(CtsLocationAccessService.CONTROL_ACTION);
- bindIntent.setComponent(new ComponentName(CtsLocationAccessService.class.getPackageName$(),
+ bindIntent.setComponent(new ComponentName(
+ LOCATION_ACCESS_APP_CURRENT_PACKAGE,
CtsLocationAccessService.class.getName()));
+ return bindLocationAccessControl(bindIntent);
+ }
+
+ private ICtsLocationAccessControl getLocationAccessAppControlSdk28() {
+ Intent bindIntent = new Intent(CtsLocationAccessService.CONTROL_ACTION);
+ bindIntent.setComponent(new ComponentName(
+ LOCATION_ACCESS_APP_SDK28_PACKAGE,
+ CtsLocationAccessService.class.getName()));
+
+ return bindLocationAccessControl(bindIntent);
+ }
+
+ private ICtsLocationAccessControl bindLocationAccessControl(Intent bindIntent) {
LinkedBlockingQueue<ICtsLocationAccessControl> pipe =
new LinkedBlockingQueue<>();
getContext().bindService(bindIntent, new ServiceConnection() {
@@ -649,16 +796,25 @@
return null;
}
- private void withRevokedPermission(Runnable r, String permission) {
+ private Object performLocationAccessCommandSdk28(String command) {
+ ICtsLocationAccessControl control = getLocationAccessAppControlSdk28();
+ try {
+ List ret = control.performCommand(command);
+ if (!ret.isEmpty()) return ret.get(0);
+ } catch (RemoteException e) {
+ fail("Remote exception");
+ }
+ return null;
+ }
+
+ private void withRevokedPermission(String packageName, Runnable r, String permission) {
InstrumentationRegistry.getInstrumentation()
- .getUiAutomation().revokeRuntimePermission(
- CtsLocationAccessService.class.getPackageName$(), permission);
+ .getUiAutomation().revokeRuntimePermission(packageName, permission);
try {
r.run();
} finally {
InstrumentationRegistry.getInstrumentation()
- .getUiAutomation().grantRuntimePermission(
- CtsLocationAccessService.class.getPackageName$(), permission);
+ .getUiAutomation().grantRuntimePermission(packageName, permission);
}
}
diff --git a/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java b/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java
index 151d899..feda07b 100644
--- a/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java
@@ -267,7 +267,7 @@
assertEquals(EuiccManager.ERROR_EUICC_MISSING, 10006);
assertEquals(EuiccManager.ERROR_UNSUPPORTED_VERSION, 10007);
assertEquals(EuiccManager.ERROR_SIM_MISSING, 10008);
- assertEquals(EuiccManager.ERROR_EUICC_GSMA_INSTALL_ERROR, 10009);
+ assertEquals(EuiccManager.ERROR_INSTALL_PROFILE, 10009);
assertEquals(EuiccManager.ERROR_DISALLOWED_BY_PPR, 10010);
assertEquals(EuiccManager.ERROR_ADDRESS_MISSING, 10011);
assertEquals(EuiccManager.ERROR_CERTIFICATE_ERROR, 10012);
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsMmTelManagerTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsMmTelManagerTest.java
index a917d93..f96928a 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsMmTelManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsMmTelManagerTest.java
@@ -34,6 +34,7 @@
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsManager;
import android.telephony.ims.ImsMmTelManager;
import android.telephony.ims.feature.MmTelFeature;
@@ -140,9 +141,10 @@
@Test
public void testGetVoWiFiSetting_noPermission() {
try {
- ImsMmTelManager mMmTelManager = ImsMmTelManager.createForSubscriptionId(sTestSub);
+ ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+ ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(sTestSub);
boolean isEnabled = mMmTelManager.isVoWiFiSettingEnabled();
- fail("Expected SecurityException for missing permissoins");
+ fail("Expected SecurityException for missing permissions");
} catch (SecurityException ex) {
/* Expected */
}
@@ -171,7 +173,8 @@
CountDownLatch contentObservedLatch = new CountDownLatch(1);
ContentObserver observer = createObserver(callingUri, contentObservedLatch);
- ImsMmTelManager mMmTelManager = ImsMmTelManager.createForSubscriptionId(sTestSub);
+ ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+ ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(sTestSub);
boolean isEnabled = ShellIdentityUtils.invokeMethodWithShellPermissions(mMmTelManager,
ImsMmTelManager::isAdvancedCallingSettingEnabled);
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
@@ -206,7 +209,8 @@
CountDownLatch contentObservedLatch = new CountDownLatch(1);
ContentObserver observer = createObserver(callingUri, contentObservedLatch);
- ImsMmTelManager mMmTelManager = ImsMmTelManager.createForSubscriptionId(sTestSub);
+ ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+ ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(sTestSub);
boolean isEnabled = ShellIdentityUtils.invokeMethodWithShellPermissions(mMmTelManager,
ImsMmTelManager::isVtSettingEnabled);
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
@@ -244,7 +248,8 @@
CountDownLatch contentObservedLatch = new CountDownLatch(1);
ContentObserver observer = createObserver(callingUri, contentObservedLatch);
- ImsMmTelManager mMmTelManager = ImsMmTelManager.createForSubscriptionId(sTestSub);
+ ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+ ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(sTestSub);
boolean isEnabled = ShellIdentityUtils.invokeMethodWithShellPermissions(mMmTelManager,
ImsMmTelManager::isVoWiFiSettingEnabled);
@@ -278,7 +283,8 @@
CountDownLatch contentObservedLatch = new CountDownLatch(1);
ContentObserver observer = createObserver(callingUri, contentObservedLatch);
- ImsMmTelManager mMmTelManager = ImsMmTelManager.createForSubscriptionId(sTestSub);
+ ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+ ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(sTestSub);
boolean isEnabled = ShellIdentityUtils.invokeMethodWithShellPermissions(mMmTelManager,
ImsMmTelManager::isVoWiFiRoamingSettingEnabled);
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
@@ -304,7 +310,8 @@
return;
}
try {
- ImsMmTelManager mMmTelManager = ImsMmTelManager.createForSubscriptionId(sTestSub);
+ ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+ ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(sTestSub);
int oldMode = mMmTelManager.getVoWiFiModeSetting();
fail("Expected SecurityException for missing permissoins");
} catch (SecurityException ex) {
@@ -322,7 +329,8 @@
return;
}
try {
- ImsMmTelManager mMmTelManager = ImsMmTelManager.createForSubscriptionId(sTestSub);
+ ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+ ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(sTestSub);
int oldMode = mMmTelManager.getVoWiFiRoamingModeSetting();
fail("Expected SecurityException for missing permissoins");
} catch (SecurityException ex) {
@@ -350,7 +358,8 @@
CountDownLatch contentObservedLatch = new CountDownLatch(1);
ContentObserver observer = createObserver(callingUri, contentObservedLatch);
- ImsMmTelManager mMmTelManager = ImsMmTelManager.createForSubscriptionId(sTestSub);
+ ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+ ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(sTestSub);
int oldMode = ShellIdentityUtils.invokeMethodWithShellPermissions(mMmTelManager,
ImsMmTelManager::getVoWiFiModeSetting);
// Keep the mode in the bounds 0-2
@@ -389,7 +398,8 @@
CountDownLatch contentObservedLatch = new CountDownLatch(1);
ContentObserver observer = createObserver(callingUri, contentObservedLatch);
- ImsMmTelManager mMmTelManager = ImsMmTelManager.createForSubscriptionId(sTestSub);
+ ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+ ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(sTestSub);
int oldMode = ShellIdentityUtils.invokeMethodWithShellPermissions(mMmTelManager,
ImsMmTelManager::getVoWiFiRoamingModeSetting);
// Keep the mode in the bounds 0-2
@@ -418,7 +428,8 @@
return;
}
- ImsMmTelManager mMmTelManager = ImsMmTelManager.createForSubscriptionId(sTestSub);
+ ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+ ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(sTestSub);
// setRttCapabilitySetting
try {
mMmTelManager.setRttCapabilitySetting(false);
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
index 78705d2..d5a61c8 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
@@ -40,7 +40,6 @@
import android.telephony.TelephonyManager;
import android.telephony.cts.AsyncSmsMessageListener;
import android.telephony.cts.SmsReceiverHelper;
-import android.telephony.cts.externalimsservice.ITestExternalImsService;
import android.telephony.ims.ImsException;
import android.telephony.ims.ImsManager;
import android.telephony.ims.ImsMmTelManager;
@@ -51,6 +50,7 @@
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.feature.RcsFeature.RcsImsCapabilities;
+import android.telephony.ims.stub.ImsConfigImplBase;
import android.telephony.ims.stub.ImsFeatureConfiguration;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.Base64;
@@ -671,7 +671,8 @@
}
private Integer getFeatureState() throws Exception {
- ImsMmTelManager mmTelManager = ImsMmTelManager.createForSubscriptionId(sTestSub);
+ ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+ ImsMmTelManager mmTelManager = imsManager.getImsMmTelManager(sTestSub);
LinkedBlockingQueue<Integer> state = new LinkedBlockingQueue<>(1);
ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(mmTelManager,
(m) -> m.getFeatureState(Runnable::run, state::offer), ImsException.class);
@@ -722,7 +723,8 @@
final UiAutomation automan = InstrumentationRegistry.getInstrumentation().getUiAutomation();
// Latch will count down here (we callback on the state during registration).
try {
- ImsMmTelManager mmTelManager = ImsMmTelManager.createForSubscriptionId(sTestSub);
+ ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+ ImsMmTelManager mmTelManager = imsManager.getImsMmTelManager(sTestSub);
mmTelManager.registerImsRegistrationCallback(getContext().getMainExecutor(), callback);
fail("registerImsRegistrationCallback requires READ_PRECISE_PHONE_STATE permission.");
} catch (SecurityException e) {
@@ -731,7 +733,8 @@
try {
automan.adoptShellPermissionIdentity();
- ImsMmTelManager mmTelManager = ImsMmTelManager.createForSubscriptionId(sTestSub);
+ ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+ ImsMmTelManager mmTelManager = imsManager.getImsMmTelManager(sTestSub);
mmTelManager.registerImsRegistrationCallback(getContext().getMainExecutor(), callback);
} finally {
automan.dropShellPermissionIdentity();
@@ -759,14 +762,16 @@
try {
automan.adoptShellPermissionIdentity();
- ImsMmTelManager mmTelManager = ImsMmTelManager.createForSubscriptionId(sTestSub);
+ ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+ ImsMmTelManager mmTelManager = imsManager.getImsMmTelManager(sTestSub);
mmTelManager.unregisterImsRegistrationCallback(callback);
} finally {
automan.dropShellPermissionIdentity();
}
try {
- ImsMmTelManager mmTelManager = ImsMmTelManager.createForSubscriptionId(sTestSub);
+ ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+ ImsMmTelManager mmTelManager = imsManager.getImsMmTelManager(sTestSub);
mmTelManager.unregisterImsRegistrationCallback(callback);
fail("unregisterImsRegistrationCallback requires READ_PRECISE_PHONE_STATE permission.");
} catch (SecurityException e) {
@@ -787,42 +792,42 @@
}
// Connect to device ImsService with RcsFeature
- triggerFrameworkConnectToDeviceImsServiceBindRcsFeature();
+ triggerFrameworkConnectToLocalImsServiceBindRcsFeature();
ImsRcsManager imsRcsManager = imsManager.getImsRcsManager(sTestSub);
- ITestExternalImsService testImsService = sServiceConnector.getExternalService();
// Wait for the framework to set the capabilities on the ImsService
- testImsService.waitForLatchCountdown(TestImsService.LATCH_RCS_CAP_SET);
+ sServiceConnector.getCarrierService().waitForLatchCountdown(
+ TestImsService.LATCH_RCS_CAP_SET);
// Start de-registered
- sServiceConnector.getExternalService().triggerImsOnDeregistered(
+ sServiceConnector.getCarrierService().getImsRegistration().onDeregistered(
new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED,
ImsReasonInfo.CODE_UNSPECIFIED, ""));
LinkedBlockingQueue<Integer> mQueue = new LinkedBlockingQueue<>();
RegistrationManager.RegistrationCallback callback =
new RegistrationManager.RegistrationCallback() {
- @Override
- public void onRegistered(int imsTransportType) {
- mQueue.offer(imsTransportType);
- }
+ @Override
+ public void onRegistered(int imsTransportType) {
+ mQueue.offer(imsTransportType);
+ }
- @Override
- public void onRegistering(int imsTransportType) {
- mQueue.offer(imsTransportType);
- }
+ @Override
+ public void onRegistering(int imsTransportType) {
+ mQueue.offer(imsTransportType);
+ }
- @Override
- public void onUnregistered(ImsReasonInfo info) {
- mQueue.offer(info.getCode());
- }
+ @Override
+ public void onUnregistered(ImsReasonInfo info) {
+ mQueue.offer(info.getCode());
+ }
- @Override
- public void onTechnologyChangeFailed(int imsTransportType, ImsReasonInfo info) {
- mQueue.offer(imsTransportType);
- mQueue.offer(info.getCode());
- }
- };
+ @Override
+ public void onTechnologyChangeFailed(int imsTransportType, ImsReasonInfo info) {
+ mQueue.offer(imsTransportType);
+ mQueue.offer(info.getCode());
+ }
+ };
final UiAutomation automan = InstrumentationRegistry.getInstrumentation().getUiAutomation();
try {
@@ -835,17 +840,17 @@
assertEquals(ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED, waitForIntResult(mQueue));
// Start registration
- sServiceConnector.getExternalService().triggerImsOnRegistering(
+ sServiceConnector.getCarrierService().getImsRegistration().onRegistering(
ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, waitForIntResult(mQueue));
// Complete registration
- sServiceConnector.getExternalService().triggerImsOnRegistered(
+ sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, waitForIntResult(mQueue));
// Fail handover to IWLAN
- sServiceConnector.getExternalService().triggerImsOnTechnologyChangeFailed(
+ sServiceConnector.getCarrierService().getImsRegistration().onTechnologyChangeFailed(
ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_HO_NOT_FEASIBLE,
ImsReasonInfo.CODE_UNSPECIFIED, ""));
@@ -865,7 +870,8 @@
if (!ImsUtils.shouldTestImsService()) {
return;
}
- RegistrationManager regManager = ImsMmTelManager.createForSubscriptionId(sTestSub);
+ ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+ RegistrationManager regManager = imsManager.getImsMmTelManager(sTestSub);
LinkedBlockingQueue<Integer> mQueue = new LinkedBlockingQueue<>();
triggerFrameworkConnectToCarrierImsService();
@@ -899,8 +905,7 @@
}
};
- ImsMmTelManager mmTelManager =
- ImsMmTelManager.createForSubscriptionId(sTestSub);
+ ImsMmTelManager mmTelManager = imsManager.getImsMmTelManager(sTestSub);
ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(mmTelManager,
(m) -> m.registerImsRegistrationCallback(getContext().getMainExecutor(), callback),
ImsException.class);
@@ -956,12 +961,12 @@
}
// Connect to device ImsService with RcsFeature
- triggerFrameworkConnectToDeviceImsServiceBindRcsFeature();
- ITestExternalImsService testImsService = sServiceConnector.getExternalService();
- testImsService.waitForLatchCountdown(TestImsService.LATCH_RCS_CAP_SET);
+ triggerFrameworkConnectToLocalImsServiceBindRcsFeature();
+ sServiceConnector.getCarrierService().waitForLatchCountdown(
+ TestImsService.LATCH_RCS_CAP_SET);
// Start de-registered
- sServiceConnector.getExternalService().triggerImsOnDeregistered(
+ sServiceConnector.getCarrierService().getImsRegistration().onDeregistered(
new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED,
ImsReasonInfo.CODE_UNSPECIFIED, ""));
@@ -1003,21 +1008,21 @@
AccessNetworkConstants.TRANSPORT_TYPE_INVALID);
// Start registration
- sServiceConnector.getExternalService().triggerImsOnRegistering(
+ sServiceConnector.getCarrierService().getImsRegistration().onRegistering(
ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, waitForIntResult(mQueue));
verifyRegistrationState(imsRcsManager, RegistrationManager.REGISTRATION_STATE_REGISTERING);
verifyRegistrationTransportType(imsRcsManager, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
// Complete registration
- sServiceConnector.getExternalService().triggerImsOnRegistered(
+ sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, waitForIntResult(mQueue));
verifyRegistrationState(imsRcsManager, RegistrationManager.REGISTRATION_STATE_REGISTERED);
verifyRegistrationTransportType(imsRcsManager, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
// Fail handover to IWLAN
- sServiceConnector.getExternalService().triggerImsOnTechnologyChangeFailed(
+ sServiceConnector.getCarrierService().getImsRegistration().onTechnologyChangeFailed(
ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_HO_NOT_FEASIBLE,
ImsReasonInfo.CODE_UNSPECIFIED, ""));
@@ -1026,7 +1031,7 @@
verifyRegistrationTransportType(imsRcsManager, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
// handover to IWLAN
- sServiceConnector.getExternalService().triggerImsOnRegistered(
+ sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WLAN, waitForIntResult(mQueue));
verifyRegistrationTransportType(imsRcsManager, AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
@@ -1040,7 +1045,8 @@
return;
}
- ImsMmTelManager mmTelManager = ImsMmTelManager.createForSubscriptionId(sTestSub);
+ ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+ ImsMmTelManager mmTelManager = imsManager.getImsMmTelManager(sTestSub);
triggerFrameworkConnectToCarrierImsService();
@@ -1129,7 +1135,11 @@
@Test
public void testProvisioningManagerNotifyAutoConfig() throws Exception {
- triggerFrameworkConnectToDeviceImsServiceBindRcsFeature();
+ if (!ImsUtils.shouldTestImsService()) {
+ return;
+ }
+
+ triggerFrameworkConnectToLocalImsServiceBindRcsFeature();
ProvisioningManager provisioningManager =
ProvisioningManager.createForSubscriptionId(sTestSub);
@@ -1139,15 +1149,15 @@
automan.adoptShellPermissionIdentity();
provisioningManager.notifyRcsAutoConfigurationReceived(
TEST_AUTOCONFIG_CONTENT.getBytes(), false);
+ ImsConfigImplBase config = sServiceConnector.getCarrierService().getConfig();
+ Assert.assertNotNull(config);
assertEquals(TEST_AUTOCONFIG_CONTENT,
- sServiceConnector.getExternalService().getConfigString(sTestSlot,
- ImsUtils.ITEM_NON_COMPRESSED));
+ config.getConfigString(ImsUtils.ITEM_NON_COMPRESSED));
provisioningManager.notifyRcsAutoConfigurationReceived(
TEST_AUTOCONFIG_CONTENT.getBytes(), true);
assertEquals(TEST_AUTOCONFIG_CONTENT,
- sServiceConnector.getExternalService().getConfigString(sTestSlot,
- ImsUtils.ITEM_COMPRESSED));
+ config.getConfigString(ImsUtils.ITEM_COMPRESSED));
} finally {
automan.dropShellPermissionIdentity();
}
@@ -1165,17 +1175,20 @@
}
// Connect to device ImsService with RcsFeature
- triggerFrameworkConnectToDeviceImsServiceBindRcsFeature();
+ triggerFrameworkConnectToLocalImsServiceBindRcsFeature();
int registrationTech = ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
ImsRcsManager imsRcsManager = imsManager.getImsRcsManager(sTestSub);
- ITestExternalImsService testImsService = sServiceConnector.getExternalService();
// Wait for the framework to set the capabilities on the ImsService
- testImsService.waitForLatchCountdown(TestImsService.LATCH_RCS_CAP_SET);
+ sServiceConnector.getCarrierService().waitForLatchCountdown(
+ TestImsService.LATCH_RCS_CAP_SET);
// Make sure we start off with none-capability
- testImsService.triggerImsOnRegistered(ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
- testImsService.notifyRcsCapabilitiesStatusChanged(RCS_CAP_NONE);
+ sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+ RcsImsCapabilities noCapabilities = new RcsImsCapabilities(RCS_CAP_NONE);
+ sServiceConnector.getCarrierService().getRcsFeature()
+ .notifyCapabilitiesStatusChanged(noCapabilities);
// Make sure the capabilities match the API getter for capabilities
final UiAutomation automan = InstrumentationRegistry.getInstrumentation().getUiAutomation();
@@ -1183,7 +1196,9 @@
try {
automan.adoptShellPermissionIdentity();
// Make sure we are tracking voice capability over LTE properly.
- assertEquals(testImsService.isRcsAvailable(RCS_CAP_PRESENCE),
+ RcsImsCapabilities availability = sServiceConnector.getCarrierService()
+ .getRcsFeature().queryCapabilityStatus();
+ assertEquals(availability.isCapable(RCS_CAP_PRESENCE),
imsRcsManager.isAvailable(RCS_CAP_PRESENCE));
} finally {
automan.dropShellPermissionIdentity();
@@ -1238,7 +1253,9 @@
}
// Notify the SIP OPTIONS capability status changed
- testImsService.notifyRcsCapabilitiesStatusChanged(RCS_CAP_OPTIONS);
+ RcsImsCapabilities optionsCap = new RcsImsCapabilities(RCS_CAP_OPTIONS);
+ sServiceConnector.getCarrierService().getRcsFeature()
+ .notifyCapabilitiesStatusChanged(optionsCap);
capCb = waitForResult(mQueue);
// The SIP OPTIONS capability from onAvailabilityChanged should be enabled.
@@ -1536,6 +1553,22 @@
Thread.sleep(1000);
}
+ private void triggerFrameworkConnectToLocalImsServiceBindRcsFeature() throws Exception {
+ // Connect to the ImsService with the RCS feature.
+ assertTrue(sServiceConnector.connectCarrierImsService(new ImsFeatureConfiguration.Builder()
+ .addFeature(sTestSlot, ImsFeature.FEATURE_RCS)
+ .build()));
+ // The RcsFeature is created when the ImsService is bound. If it wasn't created, then the
+ // Framework did not call it.
+ sServiceConnector.getCarrierService().waitForLatchCountdown(
+ TestImsService.LATCH_CREATE_RCS);
+ sServiceConnector.getCarrierService().waitForLatchCountdown(
+ TestImsService.LATCH_RCS_READY);
+ // Make sure the RcsFeature was created in the test service.
+ assertNotNull("Device ImsService created, but TestDeviceImsService#createRcsFeature was not"
+ + "called!", sServiceConnector.getCarrierService().getRcsFeature());
+ }
+
private void triggerFrameworkConnectToCarrierImsService() throws Exception {
// Connect to the ImsService with the MmTel feature.
assertTrue(sServiceConnector.connectCarrierImsService(new ImsFeatureConfiguration.Builder()
@@ -1551,20 +1584,6 @@
sServiceConnector.getCarrierService().getMmTelFeature());
}
- private void triggerFrameworkConnectToDeviceImsServiceBindRcsFeature() throws Exception {
- // Connect to the ImsService with the RCS feature.
- assertTrue(sServiceConnector.connectDeviceImsService(new ImsFeatureConfiguration.Builder()
- .addFeature(sTestSlot, ImsFeature.FEATURE_RCS)
- .build()));
- // The RcsFeature is created when the ImsService is bound. If it wasn't created, then the
- // Framework did not call it.
- sServiceConnector.getExternalService().waitForLatchCountdown(
- TestImsService.LATCH_CREATE_RCS);
- // Make sure the RcsFeature was created in the test service.
- assertTrue("Device ImsService created, but TestDeviceImsService#createRcsFeature was not"
- + "called!", sServiceConnector.getExternalService().isRcsFeatureCreated());
- }
-
private void verifyRegistrationState(RegistrationManager regManager, int expectedState)
throws Exception {
LinkedBlockingQueue<Integer> mQueue = new LinkedBlockingQueue<>();
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/RcsUceAdapterTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/RcsUceAdapterTest.java
new file mode 100644
index 0000000..8ab1f78
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/RcsUceAdapterTest.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2020 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.telephony.ims.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Looper;
+import android.telecom.PhoneAccount;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsManager;
+import android.telephony.ims.RcsUceAdapter;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.ShellIdentityUtils;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsUceAdapterTest {
+
+ private static int sTestSub = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private static final Uri TEST_NUMBER_URI =
+ Uri.fromParts(PhoneAccount.SCHEME_TEL, "6505551212", null);
+
+ @BeforeClass
+ public static void beforeAllTests() {
+ if (!ImsUtils.shouldTestImsService()) {
+ return;
+ }
+
+ sTestSub = ImsUtils.getPreferredActiveSubId();
+
+ if (Looper.getMainLooper() == null) {
+ Looper.prepareMainLooper();
+ }
+ }
+
+ @Before
+ public void beforeTest() {
+ if (!ImsUtils.shouldTestImsService()) {
+ return;
+ }
+
+ if (!SubscriptionManager.isValidSubscriptionId(sTestSub)) {
+ fail("This test requires that there is a SIM in the device!");
+ }
+ }
+
+ @Test
+ public void testGetAndSetUceSetting() throws Exception {
+ if (!ImsUtils.shouldTestImsService()) {
+ return;
+ }
+
+ ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+ RcsUceAdapter adapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
+ assertNotNull("RcsUceAdapter can not be null!", adapter);
+
+ Boolean isEnabled = null;
+ try {
+ isEnabled = ShellIdentityUtils.invokeThrowableMethodWithShellPermissions(
+ adapter, RcsUceAdapter::isUceSettingEnabled, ImsException.class,
+ "android.permission.READ_PRIVILEGED_PHONE_STATE");
+ assertNotNull(isEnabled);
+
+ boolean userSetIsEnabled = isEnabled;
+ ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(
+ adapter, a -> a.setUceSettingEnabled(!userSetIsEnabled), ImsException.class,
+ "android.permission.MODIFY_PHONE_STATE");
+
+
+ Boolean setResult = ShellIdentityUtils.invokeThrowableMethodWithShellPermissions(
+ adapter, RcsUceAdapter::isUceSettingEnabled, ImsException.class,
+ "android.permission.READ_PRIVILEGED_PHONE_STATE");
+ assertNotNull(setResult);
+ assertEquals("Incorrect setting!", !userSetIsEnabled, setResult);
+ } catch (ImsException e) {
+ if (e.getCode() != ImsException.CODE_ERROR_UNSUPPORTED_OPERATION) {
+ fail("failed getting UCE setting with code: " + e.getCode());
+ }
+ } finally {
+ if (isEnabled != null) {
+ boolean userSetIsEnabled = isEnabled;
+ // set back to user preference
+ ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(
+ adapter, a -> a.setUceSettingEnabled(userSetIsEnabled), ImsException.class,
+ "android.permission.MODIFY_PHONE_STATE");
+ }
+ }
+ }
+
+ @Test
+ public void testMethodPermissions() throws Exception {
+ if (!ImsUtils.shouldTestImsService()) {
+ return;
+ }
+ ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+ RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
+ assertNotNull("UCE adapter should not be null!", uceAdapter);
+
+ // requestCapabilities
+ ArrayList<Uri> numbers = new ArrayList<>(1);
+ numbers.add(TEST_NUMBER_URI);
+ try {
+ ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(uceAdapter,
+ (m) -> m.requestCapabilities(Runnable::run, numbers,
+ new RcsUceAdapter.CapabilitiesCallback() {
+ }), ImsException.class,
+ "android.permission.READ_PRIVILEGED_PHONE_STATE");
+ } catch (SecurityException e) {
+ fail("requestCapabilities should succeed with READ_PRIVILEGED_PHONE_STATE.");
+ } catch (ImsException e) {
+ // unsupported is a valid fail cause.
+ if (e.getCode() != ImsException.CODE_ERROR_UNSUPPORTED_OPERATION) {
+ fail("request capabilities failed with code " + e.getCode());
+ }
+ }
+
+ try {
+ uceAdapter.requestCapabilities(Runnable::run, numbers,
+ new RcsUceAdapter.CapabilitiesCallback() {});
+ fail("requestCapabilities should require READ_PRIVILEGED_PHONE_STATE.");
+ } catch (SecurityException e) {
+ //expected
+ }
+
+ // getUcePublishState
+ try {
+ Integer result = ShellIdentityUtils.invokeThrowableMethodWithShellPermissions(
+ uceAdapter, RcsUceAdapter::getUcePublishState, ImsException.class,
+ "android.permission.READ_PRIVILEGED_PHONE_STATE");
+ assertNotNull("result from getUcePublishState should not be null", result);
+ } catch (SecurityException e) {
+ fail("getUcePublishState should succeed with READ_PRIVILEGED_PHONE_STATE.");
+ } catch (ImsException e) {
+ // unsupported is a valid fail cause.
+ if (e.getCode() != ImsException.CODE_ERROR_UNSUPPORTED_OPERATION) {
+ fail("getUcePublishState failed with code " + e.getCode());
+ }
+ }
+ try {
+ uceAdapter.getUcePublishState();
+ fail("requestCapabilities should require READ_PRIVILEGED_PHONE_STATE.");
+ } catch (SecurityException e) {
+ //expected
+ }
+
+ //isUceSettingEnabled
+ Boolean isUceSettingEnabledResult = null;
+ try {
+ isUceSettingEnabledResult =
+ ShellIdentityUtils.invokeThrowableMethodWithShellPermissions(
+ uceAdapter, RcsUceAdapter::isUceSettingEnabled, ImsException.class,
+ "android.permission.READ_PRIVILEGED_PHONE_STATE");
+ assertNotNull("result from isUceSettingEnabled should not be null",
+ isUceSettingEnabledResult);
+ } catch (SecurityException e) {
+ fail("isUceSettingEnabled should succeed with READ_PRIVILEGED_PHONE_STATE.");
+ } catch (ImsException e) {
+ // unsupported is a valid fail cause.
+ if (e.getCode() != ImsException.CODE_ERROR_UNSUPPORTED_OPERATION) {
+ fail("isUceSettingEnabled failed with code " + e.getCode());
+ }
+ }
+ try {
+ uceAdapter.getUcePublishState();
+ fail("isUceSettingEnabled should require READ_PRIVILEGED_PHONE_STATE.");
+ } catch (SecurityException e) {
+ //expected
+ }
+
+ //setUceSettingEnabled
+ boolean isUceSettingEnabled =
+ (isUceSettingEnabledResult == null ? false : isUceSettingEnabledResult);
+ try {
+ ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(uceAdapter,
+ (m) -> m.setUceSettingEnabled(isUceSettingEnabled), ImsException.class,
+ "android.permission.MODIFY_PHONE_STATE");
+ } catch (SecurityException e) {
+ fail("setUceSettingEnabled should succeed with MODIFY_PHONE_STATE.");
+ } catch (ImsException e) {
+ // unsupported is a valid fail cause.
+ if (e.getCode() != ImsException.CODE_ERROR_UNSUPPORTED_OPERATION) {
+ fail("setUceSettingEnabled failed with code " + e.getCode());
+ }
+ }
+
+ try {
+ uceAdapter.requestCapabilities(Runnable::run, numbers,
+ new RcsUceAdapter.CapabilitiesCallback() {});
+ fail("setUceSettingEnabled should require MODIFY_PHONE_STATE.");
+ } catch (SecurityException e) {
+ //expected
+ }
+ }
+
+ private static Context getContext() {
+ return InstrumentationRegistry.getInstrumentation().getContext();
+ }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsService.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsService.java
index ef7702f..eb3fc18 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsService.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsService.java
@@ -299,4 +299,8 @@
return sImsRegistrationImplBase;
}
}
+
+ public ImsConfigImplBase getConfig() {
+ return mTestImsConfig;
+ }
}
diff --git a/tests/tests/widget/src/android/widget/cts/ToastTest.java b/tests/tests/widget/src/android/widget/cts/ToastTest.java
index e9ad80d..fd19757 100644
--- a/tests/tests/widget/src/android/widget/cts/ToastTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ToastTest.java
@@ -516,19 +516,19 @@
assertNotNull(toast);
assertEquals(Toast.LENGTH_SHORT, toast.getDuration());
View view = toast.getView();
- assertNotNull(view);
+ assertNull(view);
toast = Toast.makeText(mContext, "cts", Toast.LENGTH_LONG);
assertNotNull(toast);
assertEquals(Toast.LENGTH_LONG, toast.getDuration());
view = toast.getView();
- assertNotNull(view);
+ assertNull(view);
toast = Toast.makeText(mContext, null, Toast.LENGTH_LONG);
assertNotNull(toast);
assertEquals(Toast.LENGTH_LONG, toast.getDuration());
view = toast.getView();
- assertNotNull(view);
+ assertNull(view);
}
@UiThreadTest
@@ -545,13 +545,13 @@
assertNotNull(toast);
assertEquals(Toast.LENGTH_LONG, toast.getDuration());
View view = toast.getView();
- assertNotNull(view);
+ assertNull(view);
toast = Toast.makeText(mContext, R.string.hello_android, Toast.LENGTH_SHORT);
assertNotNull(toast);
assertEquals(Toast.LENGTH_SHORT, toast.getDuration());
view = toast.getView();
- assertNotNull(view);
+ assertNull(view);
}
@UiThreadTest
@@ -592,10 +592,10 @@
}
@UiThreadTest
- @Test(expected=RuntimeException.class)
- public void testSetTextFromStringNullView() {
+ @Test(expected = IllegalStateException.class)
+ public void testSetTextFromStringNonNullView() {
Toast toast = Toast.makeText(mContext, R.string.text, Toast.LENGTH_LONG);
- toast.setView(null);
+ toast.setView(new TextView(mContext));
toast.setText(null);
}
diff --git a/tests/tests/widget29/AndroidTest.xml b/tests/tests/widget29/AndroidTest.xml
index 39d3911..e48e8ae 100644
--- a/tests/tests/widget29/AndroidTest.xml
+++ b/tests/tests/widget29/AndroidTest.xml
@@ -22,6 +22,11 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsWidgetTestCases29.apk" />
</target_preparer>
+ <!-- TODO(b/144152069): Remove this when feature is gated on targetSdk -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="am compat disable CHANGE_TEXT_TOASTS_IN_THE_SYSTEM android.widget.cts29" />
+ <option name="teardown-command" value="am compat reset CHANGE_TEXT_TOASTS_IN_THE_SYSTEM android.widget.cts29" />
+ </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.widget.cts29" />
<option name="runtime-hint" value="11m55s" />
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 1ca3cfa..216d764 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
@@ -490,6 +490,7 @@
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.SCALER_AVAILABLE_ROTATE_AND_CROP_MODES.getName());
charsKeyNames.add(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1.getName());
charsKeyNames.add(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2.getName());
charsKeyNames.add(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1.getName());