Merge "Throw AssumptionViolatedException if not live_tv"
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/IsolatedSplitsTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/IsolatedSplitsTests.java
index fd7a783..f258dff 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/IsolatedSplitsTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/IsolatedSplitsTests.java
@@ -44,6 +44,7 @@
private static final String APK_FEATURE_B_pl = "CtsIsolatedSplitAppFeatureB_pl.apk";
private static final String APK_FEATURE_C = "CtsIsolatedSplitAppFeatureC.apk";
private static final String APK_FEATURE_C_pl = "CtsIsolatedSplitAppFeatureC_pl.apk";
+ private static final String APK_FEATURE_A_DiffRev = "CtsIsolatedSplitAppFeatureADiffRev.apk";
@Before
public void setUp() throws Exception {
@@ -246,4 +247,50 @@
Utils.runDeviceTestsAsCurrentUser(getDevice(), PKG, TEST_CLASS,
"shouldLoadFeatureCDefault");
}
+
+ @Test
+ @AppModeFull(reason = "'full' portion of the hostside test")
+ public void testSplitsInheritInstall_full() throws Exception {
+ testSplitsInheritInstall(false);
+ }
+
+ @Test
+ @AppModeInstant(reason = "'instant' portion of the hostside test")
+ public void testSplitsInheritInstall_instant() throws Exception {
+ testSplitsInheritInstall(true);
+ }
+
+ private void testSplitsInheritInstall(boolean instant) throws Exception {
+ new InstallMultiple(instant).addFile(APK_BASE).addFile(APK_FEATURE_A).addFile(APK_FEATURE_B)
+ .addFile(APK_FEATURE_C).run();
+ Utils.runDeviceTestsAsCurrentUser(getDevice(), PKG, TEST_CLASS,
+ "shouldLoadFeatureADefault");
+
+ new InstallMultiple(instant).inheritFrom(PKG).addFile(APK_FEATURE_A_DiffRev).run();
+ Utils.runDeviceTestsAsCurrentUser(getDevice(), PKG, TEST_CLASS,
+ "shouldLoadFeatureADiffRevision");
+ }
+
+ @Test
+ @AppModeFull(reason = "'full' portion of the hostside test")
+ public void testSplitsRemoved_full() throws Exception {
+ testSplitsRemoved(false);
+ }
+
+ @Test
+ @AppModeInstant(reason = "'instant' portion of the hostside test")
+ public void testSplitsRemoved_instant() throws Exception {
+ testSplitsRemoved(true);
+ }
+
+ private void testSplitsRemoved(boolean instant) throws Exception {
+ new InstallMultiple(instant).addFile(APK_BASE).addFile(APK_FEATURE_A).addFile(APK_FEATURE_B)
+ .addFile(APK_FEATURE_C).run();
+ Utils.runDeviceTestsAsCurrentUser(getDevice(), PKG, TEST_CLASS,
+ "shouldLoadFeatureCDefault");
+
+ new InstallMultiple(instant).inheritFrom(PKG).removeSplit("feature_c").run();
+ Utils.runDeviceTestsAsCurrentUser(getDevice(), PKG, TEST_CLASS,
+ "shouldNotFoundFeatureC");
+ }
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/SessionReferrerUriTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/SessionReferrerUriTest.java
index 695ed26..7fa2618 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/SessionReferrerUriTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/SessionReferrerUriTest.java
@@ -54,9 +54,10 @@
@Test
@AppModeFull(reason = "Only full apps may install")
public void testSessionReferrerUriVisibleToOwner() throws DeviceNotAvailableException {
- Utils.runDeviceTests(getDevice(), SESSION_INSPECTOR_PKG_A,
+ // Device test launches an Activity, so it should run under current user.
+ Utils.runDeviceTestsAsCurrentUser(getDevice(), SESSION_INSPECTOR_PKG_A,
"com.android.cts.sessioninspector.SessionInspectorTest", "testOnlyOwnerCanSee");
- Utils.runDeviceTests(getDevice(), SESSION_INSPECTOR_PKG_B,
+ Utils.runDeviceTestsAsCurrentUser(getDevice(), SESSION_INSPECTOR_PKG_B,
"com.android.cts.sessioninspector.SessionInspectorTest", "testOnlyOwnerCanSee");
}
}
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/Android.bp b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/Android.bp
index 796b0b0..6472461 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/Android.bp
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/Android.bp
@@ -29,6 +29,7 @@
static_libs: [
"ctstestrunner-axt",
"androidx.test.rules",
+ "truth-prebuilt",
],
srcs: ["src/**/*.java"],
// Generate a locale split.
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/AndroidManifest.xml
index 57320b3..521a96b 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/AndroidManifest.xml
@@ -21,7 +21,7 @@
<application android:label="IsolatedSplitApp">
- <activity android:name=".BaseActivity"
+ <activity android:name=".BaseActivity" android:theme="@style/Theme_Base"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/Android.bp b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/Android.bp
index 07d7fa6..cc2ea8c 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/Android.bp
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/Android.bp
@@ -27,6 +27,7 @@
// Make sure our test locale polish is not stripped.
aapt_include_all_resources: true,
srcs: ["**/*.java"],
+ resource_dirs: ["res"],
// Generate a locale split.
package_splits: ["pl"],
libs: ["CtsIsolatedSplitApp"],
@@ -39,3 +40,31 @@
"--package-id 0x80",
],
}
+
+android_test_helper_app {
+ name: "CtsIsolatedSplitAppFeatureADiffRev",
+ defaults: ["cts_support_defaults"],
+ sdk_version: "current",
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
+ // Feature splits are dependent on this split, so it must be exported.
+ export_package_resources: true,
+ // Make sure our test locale polish is not stripped.
+ aapt_include_all_resources: true,
+ srcs: ["**/*.java"],
+ resource_dirs: ["res_diffrev"],
+ // Generate a locale split.
+ package_splits: ["pl"],
+ libs: ["CtsIsolatedSplitApp"],
+ // Although feature splits use unique resource package names, they must all
+ // have the same manifest package name to be considered one app.
+ aaptflags: [
+ "--rename-manifest-package com.android.cts.isolatedsplitapp",
+ // Assign a unique package ID to this feature split. Since these are
+ // isolated splits, it must only be unique across a dependency chain.
+ "--package-id 0x80",
+ "--revision-code 10"
+ ],
+}
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/AndroidManifest.xml
index 7e2e1da..e7c2016 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/AndroidManifest.xml
@@ -20,7 +20,7 @@
android:targetSandboxVersion="2">
<application>
- <activity android:name=".FeatureAActivity"
+ <activity android:name=".FeatureAActivity" android:theme="@style/Theme_Feature_A"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/drawable/feature_a_color_drawable.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/drawable/feature_a_color_drawable.xml
new file mode 100644
index 0000000..cba22a6
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/drawable/feature_a_color_drawable.xml
@@ -0,0 +1,20 @@
+<?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.
+ */
+-->
+<color xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/themePrimaryColor"/>
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/layout/feature_a_textview.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/layout/feature_a_textview.xml
new file mode 100644
index 0000000..fdc22fb
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/layout/feature_a_textview.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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
+
+ https://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.
+ -->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/feature_a_text"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/feature_a_string"
+ android:background="?android:attr/colorBackground">
+</TextView>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/values-pl/colors.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/values-pl/colors.xml
new file mode 100644
index 0000000..9fb73b9
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/values-pl/colors.xml
@@ -0,0 +1,24 @@
+<?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.
+ -->
+
+<resources>
+ <color name="theme_base_color">@color/yellow</color>
+ <color name="theme_primary_color">@color/dark_gray</color>
+ <color name="theme_color_background">@color/gray</color>
+ <color name="navigation_bar_color">@color/light_gray</color>
+ <color name="status_bar_color">@color/blue</color>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/values-pl/values.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/values-pl/values.xml
index 2929156..aef6469 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/values-pl/values.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/values-pl/values.xml
@@ -16,4 +16,5 @@
<resources>
<string name="feature_a_string">Feature A String Polish</string>
+ <string name="theme_name">Feature A Theme Polish</string>
</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/values/attrs.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/values/attrs.xml
new file mode 100644
index 0000000..efd04b8
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/values/attrs.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources>
+ <attr name="themePrimaryColor" format="color|reference"/>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/values/colors.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/values/colors.xml
new file mode 100644
index 0000000..dd4e007
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/values/colors.xml
@@ -0,0 +1,30 @@
+<?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.
+ -->
+
+<resources>
+ <color name="theme_base_color">@color/blue</color>
+ <color name="theme_primary_color">@color/gray</color>
+ <color name="theme_color_background">@color/light_gray</color>
+ <color name="navigation_bar_color">@color/dark_gray</color>
+ <color name="status_bar_color">@color/yellow</color>
+
+ <color name="blue">#ff0000ff</color>
+ <color name="gray">#ff888888</color>
+ <color name="light_gray">#ff444444</color>
+ <color name="dark_gray">#ffcccccc</color>
+ <color name="yellow">#ffffff00</color>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/values/styles.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/values/styles.xml
new file mode 100644
index 0000000..f1d6f91
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/values/styles.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:base="http://schemas.android.com/apk/res/com.android.cts.isolatedsplitapp">
+ <style name="Theme_Feature_A" parent="@base:style/Theme_Base">
+ <item name="base:themeName">@string/theme_name</item>
+ <item name="base:themeBaseColor">@color/theme_base_color</item>
+ <item name="themePrimaryColor">@color/theme_primary_color</item>
+ <item name="android:colorBackground">@color/theme_color_background</item>
+ <item name="android:windowBackground">@drawable/feature_a_color_drawable</item>
+ <item name="android:navigationBarColor">@color/navigation_bar_color</item>
+ <item name="android:statusBarColor">@color/status_bar_color</item>
+ </style>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/values/values.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/values/values.xml
index dc1289c..6310ae1 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/values/values.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res/values/values.xml
@@ -16,4 +16,5 @@
<resources>
<string name="feature_a_string">Feature A String Default</string>
+ <string name="theme_name">Feature A Theme</string>
</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/drawable/feature_a_color_drawable.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/drawable/feature_a_color_drawable.xml
new file mode 100644
index 0000000..cba22a6
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/drawable/feature_a_color_drawable.xml
@@ -0,0 +1,20 @@
+<?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.
+ */
+-->
+<color xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/themePrimaryColor"/>
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/layout/feature_a_textview.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/layout/feature_a_textview.xml
new file mode 100644
index 0000000..fdc22fb
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/layout/feature_a_textview.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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
+
+ https://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.
+ -->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/feature_a_text"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/feature_a_string"
+ android:background="?android:attr/colorBackground">
+</TextView>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/values-pl/colors.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/values-pl/colors.xml
new file mode 100644
index 0000000..c071bb7
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/values-pl/colors.xml
@@ -0,0 +1,24 @@
+<?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.
+ -->
+
+<resources>
+ <color name="theme_base_color">@color/blue</color>
+ <color name="theme_primary_color">@color/yellow</color>
+ <color name="theme_color_background">@color/dark_gray</color>
+ <color name="navigation_bar_color">@color/gray</color>
+ <color name="status_bar_color">@color/light_gray</color>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/values-pl/values.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/values-pl/values.xml
new file mode 100644
index 0000000..1caedf6
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/values-pl/values.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<resources>
+ <string name="feature_a_string">Feature A String Polish Diff Revision</string>
+ <string name="theme_name">Feature A Theme Polish Diff Revision</string>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/values/attrs.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/values/attrs.xml
new file mode 100644
index 0000000..efd04b8
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/values/attrs.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources>
+ <attr name="themePrimaryColor" format="color|reference"/>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/values/colors.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/values/colors.xml
new file mode 100644
index 0000000..70756b1
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/values/colors.xml
@@ -0,0 +1,30 @@
+<?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.
+ -->
+
+<resources>
+ <color name="theme_base_color">@color/yellow</color>
+ <color name="theme_primary_color">@color/blue</color>
+ <color name="theme_color_background">@color/gray</color>
+ <color name="navigation_bar_color">@color/light_gray</color>
+ <color name="status_bar_color">@color/dark_gray</color>
+
+ <color name="blue">#ff0000ff</color>
+ <color name="gray">#ff888888</color>
+ <color name="light_gray">#ff444444</color>
+ <color name="dark_gray">#ffcccccc</color>
+ <color name="yellow">#ffffff00</color>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/values/styles.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/values/styles.xml
new file mode 100644
index 0000000..f1d6f91
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/values/styles.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:base="http://schemas.android.com/apk/res/com.android.cts.isolatedsplitapp">
+ <style name="Theme_Feature_A" parent="@base:style/Theme_Base">
+ <item name="base:themeName">@string/theme_name</item>
+ <item name="base:themeBaseColor">@color/theme_base_color</item>
+ <item name="themePrimaryColor">@color/theme_primary_color</item>
+ <item name="android:colorBackground">@color/theme_color_background</item>
+ <item name="android:windowBackground">@drawable/feature_a_color_drawable</item>
+ <item name="android:navigationBarColor">@color/navigation_bar_color</item>
+ <item name="android:statusBarColor">@color/status_bar_color</item>
+ </style>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/values/values.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/values/values.xml
new file mode 100644
index 0000000..a6bdeaa
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/res_diffrev/values/values.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<resources>
+ <string name="feature_a_string">Feature A String Diff Revision</string>
+ <string name="theme_name">Feature A Theme Diff Revision</string>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/src/com/android/cts/isolatedsplitapp/feature_a/FeatureAActivity.java b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/src/com/android/cts/isolatedsplitapp/feature_a/FeatureAActivity.java
index df06bd9..3a0e961 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/src/com/android/cts/isolatedsplitapp/feature_a/FeatureAActivity.java
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/src/com/android/cts/isolatedsplitapp/feature_a/FeatureAActivity.java
@@ -15,8 +15,18 @@
*/
package com.android.cts.isolatedsplitapp.feature_a;
+import android.os.Bundle;
+import android.widget.LinearLayout;
+
import com.android.cts.isolatedsplitapp.BaseActivity;
public class FeatureAActivity extends BaseActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final LinearLayout linearLayout = findViewById(
+ com.android.cts.isolatedsplitapp.R.id.base_layout);
+ getLayoutInflater().inflate(R.layout.feature_a_textview, linearLayout);
+ }
}
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/AndroidManifest.xml
index 2b616cc..35e924f 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/AndroidManifest.xml
@@ -22,7 +22,7 @@
<uses-split android:name="feature_a"/>
<application>
- <activity android:name=".FeatureBActivity"
+ <activity android:name=".FeatureBActivity" android:theme="@style/Theme_Feature_B"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/drawable/feature_b_color_drawable.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/drawable/feature_b_color_drawable.xml
new file mode 100644
index 0000000..ce4a7a4
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/drawable/feature_b_color_drawable.xml
@@ -0,0 +1,20 @@
+<?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.
+ */
+-->
+<color xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/themeSecondaryColor"/>
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/layout/feature_b_textview.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/layout/feature_b_textview.xml
new file mode 100644
index 0000000..42f9207
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/layout/feature_b_textview.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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
+
+ https://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.
+ -->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/feature_b_text"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/feature_b_string"
+ android:background="?android:attr/colorBackground">
+</TextView>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/values-pl/colors.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/values-pl/colors.xml
new file mode 100644
index 0000000..2ee94b9
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/values-pl/colors.xml
@@ -0,0 +1,24 @@
+<?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.
+ -->
+
+<resources>
+ <color name="theme_base_color">@color/mintcream</color>
+ <color name="theme_secondary_color">@color/linen</color>
+ <color name="theme_color_background">@color/pink</color>
+ <color name="navigation_bar_color">@color/orange</color>
+ <color name="status_bar_color">@color/purple</color>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/values-pl/values.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/values-pl/values.xml
index fc46307..52d0bd6 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/values-pl/values.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/values-pl/values.xml
@@ -16,4 +16,5 @@
<resources>
<string name="feature_b_string">Feature B String Polish</string>
+ <string name="theme_name">Feature B Theme Polish</string>
</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/values/attrs.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/values/attrs.xml
new file mode 100644
index 0000000..c9eae8e
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/values/attrs.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources>
+ <attr name="themeSecondaryColor" format="color|reference"/>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/values/colors.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/values/colors.xml
new file mode 100644
index 0000000..3f774d8
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/values/colors.xml
@@ -0,0 +1,30 @@
+<?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.
+ -->
+
+<resources>
+ <color name="theme_base_color">@color/purple</color>
+ <color name="theme_secondary_color">@color/pink</color>
+ <color name="theme_color_background">@color/orange</color>
+ <color name="navigation_bar_color">@color/linen</color>
+ <color name="status_bar_color">@color/mintcream</color>
+
+ <color name="purple">#ff800080</color>
+ <color name="pink">#ffffc0cb</color>
+ <color name="orange">#ffffa500</color>
+ <color name="linen">#fffaf0e6</color>
+ <color name="mintcream">#fff5fffa</color>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/values/styles.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/values/styles.xml
new file mode 100644
index 0000000..230f0eb
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/values/styles.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:base="http://schemas.android.com/apk/res/com.android.cts.isolatedsplitapp">
+ <style name="Theme_Feature_B" parent="@base:style/Theme_Base">
+ <item name="base:themeName">@string/theme_name</item>
+ <item name="base:themeBaseColor">@color/theme_base_color</item>
+ <item name="themeSecondaryColor">@color/theme_secondary_color</item>
+ <item name="android:colorBackground">@color/theme_color_background</item>
+ <item name="android:windowBackground">@drawable/feature_b_color_drawable</item>
+ <item name="android:navigationBarColor">@color/navigation_bar_color</item>
+ <item name="android:statusBarColor">@color/status_bar_color</item>
+ </style>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/values/values.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/values/values.xml
index 421ce55..ceef1dd 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/values/values.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/res/values/values.xml
@@ -16,4 +16,5 @@
<resources>
<string name="feature_b_string">Feature B String Default</string>
+ <string name="theme_name">Feature B Theme</string>
</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/src/com/android/cts/isolatedsplitapp/feature_b/FeatureBActivity.java b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/src/com/android/cts/isolatedsplitapp/feature_b/FeatureBActivity.java
index 038555d..b5c3ad0 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/src/com/android/cts/isolatedsplitapp/feature_b/FeatureBActivity.java
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/src/com/android/cts/isolatedsplitapp/feature_b/FeatureBActivity.java
@@ -15,8 +15,18 @@
*/
package com.android.cts.isolatedsplitapp.feature_b;
+import android.os.Bundle;
+import android.widget.LinearLayout;
+
import com.android.cts.isolatedsplitapp.feature_a.FeatureAActivity;
public class FeatureBActivity extends FeatureAActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final LinearLayout linearLayout = findViewById(
+ com.android.cts.isolatedsplitapp.R.id.base_layout);
+ getLayoutInflater().inflate(R.layout.feature_b_textview, linearLayout);
+ }
}
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/AndroidManifest.xml
index 3e65a43..4bbc9ce 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/AndroidManifest.xml
@@ -20,7 +20,7 @@
android:targetSandboxVersion="2">
<application>
- <activity android:name=".FeatureCActivity"
+ <activity android:name=".FeatureCActivity" android:theme="@style/Theme_Feature_C"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/drawable/feature_c_color_drawable.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/drawable/feature_c_color_drawable.xml
new file mode 100644
index 0000000..1bc19ff
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/drawable/feature_c_color_drawable.xml
@@ -0,0 +1,20 @@
+<?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.
+ */
+-->
+<color xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/themeTertiaryColor"/>
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/layout/feature_c_textview.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/layout/feature_c_textview.xml
new file mode 100644
index 0000000..a69eceb
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/layout/feature_c_textview.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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
+
+ https://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.
+ -->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/feature_c_text"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/feature_c_string"
+ android:background="?android:attr/colorBackground">
+</TextView>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/values-pl/colors.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/values-pl/colors.xml
new file mode 100644
index 0000000..086261e
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/values-pl/colors.xml
@@ -0,0 +1,24 @@
+<?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.
+ -->
+
+<resources>
+ <color name="theme_base_color">@color/olive</color>
+ <color name="theme_tertiary_color">@color/navy</color>
+ <color name="theme_color_background">@color/magenta</color>
+ <color name="navigation_bar_color">@color/maroon</color>
+ <color name="status_bar_color">@color/cyan</color>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/values-pl/values.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/values-pl/values.xml
index 2ab54a8..e8c2850 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/values-pl/values.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/values-pl/values.xml
@@ -16,4 +16,5 @@
<resources>
<string name="feature_c_string">Feature C String Polish</string>
+ <string name="theme_name">Feature C Theme Polish</string>
</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/values/attrs.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/values/attrs.xml
new file mode 100644
index 0000000..007706d
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/values/attrs.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources>
+ <attr name="themeTertiaryColor" format="color|reference"/>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/values/colors.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/values/colors.xml
new file mode 100644
index 0000000..3bad5b3
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/values/colors.xml
@@ -0,0 +1,30 @@
+<?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.
+ -->
+
+<resources>
+ <color name="theme_base_color">@color/cyan</color>
+ <color name="theme_tertiary_color">@color/magenta</color>
+ <color name="theme_color_background">@color/maroon</color>
+ <color name="navigation_bar_color">@color/navy</color>
+ <color name="status_bar_color">@color/olive</color>
+
+ <color name="cyan">#ff00ffff</color>
+ <color name="magenta">#ffff00ff</color>
+ <color name="maroon">#ff800000</color>
+ <color name="navy">#ff000080</color>
+ <color name="olive">#ff808000</color>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/values/styles.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/values/styles.xml
new file mode 100644
index 0000000..2dc1a3e
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/values/styles.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:base="http://schemas.android.com/apk/res/com.android.cts.isolatedsplitapp">
+ <style name="Theme_Feature_C" parent="@base:style/Theme_Base">
+ <item name="base:themeName">@string/theme_name</item>
+ <item name="base:themeBaseColor">@color/theme_base_color</item>
+ <item name="themeTertiaryColor">@color/theme_tertiary_color</item>
+ <item name="android:colorBackground">@color/theme_color_background</item>
+ <item name="android:windowBackground">@drawable/feature_c_color_drawable</item>
+ <item name="android:navigationBarColor">@color/navigation_bar_color</item>
+ <item name="android:statusBarColor">@color/status_bar_color</item>
+ </style>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/values/values.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/values/values.xml
index 09f48ad..248e53b 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/values/values.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/res/values/values.xml
@@ -16,4 +16,5 @@
<resources>
<string name="feature_c_string">Feature C String Default</string>
+ <string name="theme_name">Feature C Theme</string>
</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/src/com/android/cts/isolatedsplitapp/feature_c/FeatureCActivity.java b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/src/com/android/cts/isolatedsplitapp/feature_c/FeatureCActivity.java
index dab09a8..e97fd89 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/src/com/android/cts/isolatedsplitapp/feature_c/FeatureCActivity.java
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/src/com/android/cts/isolatedsplitapp/feature_c/FeatureCActivity.java
@@ -15,8 +15,18 @@
*/
package com.android.cts.isolatedsplitapp.feature_c;
+import android.os.Bundle;
+import android.widget.LinearLayout;
+
import com.android.cts.isolatedsplitapp.BaseActivity;
public class FeatureCActivity extends BaseActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final LinearLayout linearLayout = findViewById(
+ com.android.cts.isolatedsplitapp.R.id.base_layout);
+ getLayoutInflater().inflate(R.layout.feature_c_textview, linearLayout);
+ }
}
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/drawable/base_color_drawable.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/drawable/base_color_drawable.xml
new file mode 100644
index 0000000..b9382ae
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/drawable/base_color_drawable.xml
@@ -0,0 +1,20 @@
+<?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.
+ */
+-->
+<color xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/themeBaseColor"/>
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/layout/base_linearlayout.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/layout/base_linearlayout.xml
new file mode 100644
index 0000000..191266a
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/layout/base_linearlayout.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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
+
+ https://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/base_layout"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?attr/themeBaseColor">
+</LinearLayout>
+
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values-pl/colors.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values-pl/colors.xml
new file mode 100644
index 0000000..e2a6512
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values-pl/colors.xml
@@ -0,0 +1,22 @@
+<?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.
+ -->
+
+<resources>
+ <color name="theme_base_color">@color/green</color>
+ <color name="navigation_bar_color">@color/black</color>
+ <color name="status_bar_color">@color/red</color>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values-pl/values.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values-pl/values.xml
index a2b389d..457709d 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values-pl/values.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values-pl/values.xml
@@ -16,4 +16,5 @@
<resources>
<string name="base_string">Base String Polish</string>
+ <string name="theme_name">Base Theme Polish</string>
</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values/attrs.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values/attrs.xml
new file mode 100644
index 0000000..d657844
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values/attrs.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources>
+ <attr name="themeName" format="string"/>
+ <attr name="themeBaseColor" format="color|reference"/>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values/colors.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values/colors.xml
new file mode 100644
index 0000000..3e19b55
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values/colors.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.
+ -->
+
+<resources>
+ <color name="theme_base_color">@color/black</color>
+ <color name="navigation_bar_color">@color/red</color>
+ <color name="status_bar_color">@color/green</color>
+
+ <color name="black">#ff000000</color>
+ <color name="red">#ffff0000</color>
+ <color name="green">#ff00ff00</color>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values/public.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values/public.xml
new file mode 100644
index 0000000..0b43b0a
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values/public.xml
@@ -0,0 +1,22 @@
+<?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.
+ -->
+
+<resources>
+ <public name="Theme_Base" type="style"/>
+ <public name="themeName" type="attr"/>
+ <public name="themeBaseColor" type="attr"/>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values/styles.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values/styles.xml
new file mode 100644
index 0000000..305edf5
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values/styles.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<resources>
+ <style name="Theme_Base" parent="@android:style/Theme.Material">
+ <item name="themeName">@string/theme_name</item>
+ <item name="themeBaseColor">@color/theme_base_color</item>
+ <item name="android:windowBackground">@drawable/base_color_drawable</item>
+ <item name="android:navigationBarColor">@color/navigation_bar_color</item>
+ <item name="android:statusBarColor">@color/status_bar_color</item>
+ </style>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values/values.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values/values.xml
index da33f0b..ec07804 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values/values.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/res/values/values.xml
@@ -16,4 +16,5 @@
<resources>
<string name="base_string">Base String Default</string>
+ <string name="theme_name">Base Theme</string>
</resources>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/src/com/android/cts/isolatedsplitapp/BaseActivity.java b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/src/com/android/cts/isolatedsplitapp/BaseActivity.java
index e0fafc6..421fab0 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/src/com/android/cts/isolatedsplitapp/BaseActivity.java
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/src/com/android/cts/isolatedsplitapp/BaseActivity.java
@@ -17,6 +17,28 @@
package com.android.cts.isolatedsplitapp;
import android.app.Activity;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.Bundle;
public class BaseActivity extends Activity {
+ private static Configuration sOverrideConfiguration;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.base_linearlayout);
+ }
+
+ @Override
+ protected void attachBaseContext(Context newBase) {
+ super.attachBaseContext(newBase);
+ if (sOverrideConfiguration != null) {
+ applyOverrideConfiguration(sOverrideConfiguration);
+ }
+ }
+
+ public static void setOverrideConfiguration(Configuration overrideConfiguration) {
+ sOverrideConfiguration = overrideConfiguration;
+ }
}
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/src/com/android/cts/isolatedsplitapp/SplitAppTest.java b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/src/com/android/cts/isolatedsplitapp/SplitAppTest.java
index 300f978..abc6257 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/src/com/android/cts/isolatedsplitapp/SplitAppTest.java
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/src/com/android/cts/isolatedsplitapp/SplitAppTest.java
@@ -26,12 +26,17 @@
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.view.View;
+import android.widget.LinearLayout;
import androidx.test.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
@@ -47,6 +52,8 @@
public class SplitAppTest {
private static final String PACKAGE = "com.android.cts.isolatedsplitapp";
+ private static final ComponentName BASE_ACTIVITY =
+ ComponentName.createRelative(PACKAGE, ".BaseActivity");
private static final ComponentName FEATURE_A_ACTIVITY =
ComponentName.createRelative(PACKAGE, ".feature_a.FeatureAActivity");
private static final ComponentName FEATURE_B_ACTIVITY =
@@ -60,84 +67,123 @@
"com.android.cts.isolatedsplitapp.feature_b:string/feature_b_string";
private static final String FEATURE_C_STRING =
"com.android.cts.isolatedsplitapp.feature_c:string/feature_c_string";
+ private static final String FEATURE_A_TEXTVIEW_ID =
+ "com.android.cts.isolatedsplitapp.feature_a:id/feature_a_text";
+ private static final String FEATURE_B_TEXTVIEW_ID =
+ "com.android.cts.isolatedsplitapp.feature_b:id/feature_b_text";
+ private static final String FEATURE_C_TEXTVIEW_ID =
+ "com.android.cts.isolatedsplitapp.feature_c:id/feature_c_text";
private static final Configuration PL = new Configuration();
static {
PL.setLocale(Locale.forLanguageTag("pl"));
}
- @Rule
- public ActivityTestRule<BaseActivity> mBaseActivityRule =
- new ActivityTestRule<>(BaseActivity.class);
-
- // Do not launch this activity lazily. We use this rule to launch all feature Activities,
+ // Do not launch this activity lazily. We use this rule to launch all Activities,
// so we use #launchActivity() with the correct Intent.
@Rule
- public ActivityTestRule<Activity> mFeatureActivityRule =
+ public ActivityTestRule<Activity> mActivityRule =
new ActivityTestRule<>(Activity.class, true /*initialTouchMode*/,
false /*launchActivity*/);
@Rule
public AppContextTestRule mAppContextTestRule = new AppContextTestRule();
+ @Before
+ public void setUp() {
+ BaseActivity.setOverrideConfiguration(null);
+ }
+
@Test
public void shouldLoadDefault() throws Exception {
- final Context context = mBaseActivityRule.getActivity();
- final Resources resources = context.getResources();
+ final Activity activity = mActivityRule.launchActivity(
+ new Intent().setComponent(BASE_ACTIVITY));
+ final TestTheme testTheme = new TestTheme(activity, R.style.Theme_Base);
+ final Resources resources = activity.getResources();
assertThat(resources, notNullValue());
assertThat(resources.getString(R.string.base_string), equalTo("Base String Default"));
+ testTheme.assertThemeBaseValues();
+
+ // Test the theme applied to the activity correctly
+ assertActivityThemeApplied(activity, testTheme);
// The base does not depend on any splits so no splits should be accessible.
- assertActivitiesDoNotExist(context, FEATURE_A_ACTIVITY, FEATURE_B_ACTIVITY,
+ assertActivitiesDoNotExist(activity, FEATURE_A_ACTIVITY, FEATURE_B_ACTIVITY,
FEATURE_C_ACTIVITY);
- assertResourcesDoNotExist(context, FEATURE_A_STRING, FEATURE_B_STRING, FEATURE_C_STRING);
+ assertResourcesDoNotExist(activity, FEATURE_A_STRING, FEATURE_B_STRING, FEATURE_C_STRING,
+ TestTheme.THEME_FEATURE_A, TestTheme.THEME_FEATURE_B, TestTheme.THEME_FEATURE_C);
}
@Test
public void shouldLoadPolishLocale() throws Exception {
- final Context context = mBaseActivityRule.getActivity().createConfigurationContext(PL);
- final Resources resources = context.getResources();
+ BaseActivity.setOverrideConfiguration(PL);
+ final Activity activity = mActivityRule.launchActivity(
+ new Intent().setComponent(BASE_ACTIVITY));
+ final TestTheme testTheme = new TestTheme(activity, R.style.Theme_Base);
+ final Resources resources = activity.getResources();
assertThat(resources, notNullValue());
assertThat(resources.getString(R.string.base_string), equalTo("Base String Polish"));
+ testTheme.assertThemeBaseValues_pl();
+
+ // Test the theme applied to the activity correctly
+ assertActivityThemeApplied(activity, testTheme);
// The base does not depend on any splits so no splits should be accessible.
- assertActivitiesDoNotExist(context, FEATURE_A_ACTIVITY, FEATURE_B_ACTIVITY,
+ assertActivitiesDoNotExist(activity, FEATURE_A_ACTIVITY, FEATURE_B_ACTIVITY,
FEATURE_C_ACTIVITY);
- assertResourcesDoNotExist(context, FEATURE_A_STRING, FEATURE_B_STRING, FEATURE_C_STRING);
+ assertResourcesDoNotExist(activity, FEATURE_A_STRING, FEATURE_B_STRING, FEATURE_C_STRING,
+ TestTheme.THEME_FEATURE_A, TestTheme.THEME_FEATURE_B, TestTheme.THEME_FEATURE_C);
}
@Test
public void shouldLoadFeatureADefault() throws Exception {
- final Context context = mFeatureActivityRule.launchActivity(
+ final Activity activity = mActivityRule.launchActivity(
new Intent().setComponent(FEATURE_A_ACTIVITY));
- final Resources resources = context.getResources();
+ final TestTheme testTheme = new TestTheme(activity, TestTheme.THEME_FEATURE_A);
+ final Resources resources = activity.getResources();
assertThat(resources, notNullValue());
assertThat(resources.getString(R.string.base_string), equalTo("Base String Default"));
+ new TestTheme(activity, R.style.Theme_Base).assertThemeBaseValues();
int resourceId = resources.getIdentifier(FEATURE_A_STRING, null, null);
assertThat(resources.getString(resourceId), equalTo("Feature A String Default"));
+ testTheme.assertThemeFeatureAValues();
- assertActivitiesDoNotExist(context, FEATURE_B_ACTIVITY, FEATURE_C_ACTIVITY);
- assertResourcesDoNotExist(context, FEATURE_B_STRING, FEATURE_C_STRING);
+ // Test the theme applied to the activity correctly
+ assertActivityThemeApplied(activity, testTheme);
+ assertTextViewBGColor(activity, FEATURE_A_TEXTVIEW_ID, testTheme.mColorBackground);
+
+ assertActivitiesDoNotExist(activity, FEATURE_B_ACTIVITY, FEATURE_C_ACTIVITY);
+ assertResourcesDoNotExist(activity, FEATURE_B_STRING, FEATURE_C_STRING,
+ TestTheme.THEME_FEATURE_B, TestTheme.THEME_FEATURE_C);
}
@Test
public void shouldLoadFeatureAPolishLocale() throws Exception {
- final Context context = mFeatureActivityRule.launchActivity(
- new Intent().setComponent(FEATURE_A_ACTIVITY)).createConfigurationContext(PL);
- final Resources resources = context.getResources();
+ BaseActivity.setOverrideConfiguration(PL);
+ final Activity activity = mActivityRule.launchActivity(
+ new Intent().setComponent(FEATURE_A_ACTIVITY));
+ final TestTheme testTheme = new TestTheme(activity, TestTheme.THEME_FEATURE_A);
+ final Resources resources = activity.getResources();
assertThat(resources, notNullValue());
assertThat(resources.getString(R.string.base_string), equalTo("Base String Polish"));
+ new TestTheme(activity, R.style.Theme_Base).assertThemeBaseValues_pl();
int resourceId = resources.getIdentifier(FEATURE_A_STRING, null, null);
assertThat(resources.getString(resourceId), equalTo("Feature A String Polish"));
+ testTheme.assertThemeFeatureAValues_pl();
- assertActivitiesDoNotExist(context, FEATURE_B_ACTIVITY, FEATURE_C_ACTIVITY);
- assertResourcesDoNotExist(context, FEATURE_B_STRING, FEATURE_C_STRING);
+ // Test the theme applied to the activity correctly
+ assertActivityThemeApplied(activity, testTheme);
+ assertTextViewBGColor(activity, FEATURE_A_TEXTVIEW_ID, testTheme.mColorBackground);
+
+ assertActivitiesDoNotExist(activity, FEATURE_B_ACTIVITY, FEATURE_C_ACTIVITY);
+ assertResourcesDoNotExist(activity, FEATURE_B_STRING, FEATURE_C_STRING,
+ TestTheme.THEME_FEATURE_B, TestTheme.THEME_FEATURE_C);
}
@Test
@@ -154,40 +200,57 @@
@Test
public void shouldLoadFeatureBDefault() throws Exception {
// Feature B depends on A, so we expect both to be available.
- final Context context = mFeatureActivityRule.launchActivity(
+ final Activity activity = mActivityRule.launchActivity(
new Intent().setComponent(FEATURE_B_ACTIVITY));
- final Resources resources = context.getResources();
+ final TestTheme testTheme = new TestTheme(activity, TestTheme.THEME_FEATURE_B);
+ final Resources resources = activity.getResources();
assertThat(resources, notNullValue());
assertThat(resources.getString(R.string.base_string), equalTo("Base String Default"));
+ new TestTheme(activity, R.style.Theme_Base).assertThemeBaseValues();
int resourceId = resources.getIdentifier(FEATURE_A_STRING, null, null);
assertThat(resources.getString(resourceId), equalTo("Feature A String Default"));
+ new TestTheme(activity, TestTheme.THEME_FEATURE_A).assertThemeFeatureAValues();
resourceId = resources.getIdentifier(FEATURE_B_STRING, null, null);
assertThat(resources.getString(resourceId), equalTo("Feature B String Default"));
+ testTheme.assertThemeFeatureBValues();
- assertActivitiesDoNotExist(context, FEATURE_C_ACTIVITY);
- assertResourcesDoNotExist(context, FEATURE_C_STRING);
+ // Test the theme applied to the activity correctly
+ assertActivityThemeApplied(activity, testTheme);
+ assertTextViewBGColor(activity, FEATURE_B_TEXTVIEW_ID, testTheme.mColorBackground);
+
+ assertActivitiesDoNotExist(activity, FEATURE_C_ACTIVITY);
+ assertResourcesDoNotExist(activity, FEATURE_C_STRING, TestTheme.THEME_FEATURE_C);
}
@Test
public void shouldLoadFeatureBPolishLocale() throws Exception {
- final Context context = mFeatureActivityRule.launchActivity(
- new Intent().setComponent(FEATURE_B_ACTIVITY)).createConfigurationContext(PL);
- final Resources resources = context.getResources();
+ BaseActivity.setOverrideConfiguration(PL);
+ final Activity activity = mActivityRule.launchActivity(
+ new Intent().setComponent(FEATURE_B_ACTIVITY));
+ final TestTheme testTheme = new TestTheme(activity, TestTheme.THEME_FEATURE_B);
+ final Resources resources = activity.getResources();
assertThat(resources, notNullValue());
assertThat(resources.getString(R.string.base_string), equalTo("Base String Polish"));
+ new TestTheme(activity, R.style.Theme_Base).assertThemeBaseValues_pl();
int resourceId = resources.getIdentifier(FEATURE_A_STRING, null, null);
assertThat(resources.getString(resourceId), equalTo("Feature A String Polish"));
+ new TestTheme(activity, TestTheme.THEME_FEATURE_A).assertThemeFeatureAValues_pl();
resourceId = resources.getIdentifier(FEATURE_B_STRING, null, null);
assertThat(resources.getString(resourceId), equalTo("Feature B String Polish"));
+ testTheme.assertThemeFeatureBValues_pl();
- assertActivitiesDoNotExist(context, FEATURE_C_ACTIVITY);
- assertResourcesDoNotExist(context, FEATURE_C_STRING);
+ // Test the theme applied to the activity correctly
+ assertActivityThemeApplied(activity, testTheme);
+ assertTextViewBGColor(activity, FEATURE_B_TEXTVIEW_ID, testTheme.mColorBackground);
+
+ assertActivitiesDoNotExist(activity, FEATURE_C_ACTIVITY);
+ assertResourcesDoNotExist(activity, FEATURE_C_STRING, TestTheme.THEME_FEATURE_C);
}
@Test
@@ -203,34 +266,75 @@
@Test
public void shouldLoadFeatureCDefault() throws Exception {
- final Context context = mFeatureActivityRule.launchActivity(
+ final Activity activity = mActivityRule.launchActivity(
new Intent().setComponent(FEATURE_C_ACTIVITY));
- final Resources resources = context.getResources();
+ final TestTheme testTheme = new TestTheme(activity, TestTheme.THEME_FEATURE_C);
+ final Resources resources = activity.getResources();
assertThat(resources, notNullValue());
assertThat(resources.getString(R.string.base_string), equalTo("Base String Default"));
+ new TestTheme(activity, R.style.Theme_Base).assertThemeBaseValues();
int resourceId = resources.getIdentifier(FEATURE_C_STRING, null, null);
assertThat(resources.getString(resourceId), equalTo("Feature C String Default"));
+ testTheme.assertThemeFeatureCValues();
- assertActivitiesDoNotExist(context, FEATURE_A_ACTIVITY, FEATURE_B_ACTIVITY);
- assertResourcesDoNotExist(context, FEATURE_A_STRING, FEATURE_B_STRING);
+ // Test the theme applied to the activity correctly
+ assertActivityThemeApplied(activity, testTheme);
+ assertTextViewBGColor(activity, FEATURE_C_TEXTVIEW_ID, testTheme.mColorBackground);
+
+ assertActivitiesDoNotExist(activity, FEATURE_A_ACTIVITY, FEATURE_B_ACTIVITY);
+ assertResourcesDoNotExist(activity, FEATURE_A_STRING, FEATURE_B_STRING,
+ TestTheme.THEME_FEATURE_A, TestTheme.THEME_FEATURE_B);
}
@Test
public void shouldLoadFeatureCPolishLocale() throws Exception {
- final Context context = mFeatureActivityRule.launchActivity(
- new Intent().setComponent(FEATURE_C_ACTIVITY)).createConfigurationContext(PL);
- final Resources resources = context.getResources();
+ BaseActivity.setOverrideConfiguration(PL);
+ final Activity activity = mActivityRule.launchActivity(
+ new Intent().setComponent(FEATURE_C_ACTIVITY));
+ final TestTheme testTheme = new TestTheme(activity, TestTheme.THEME_FEATURE_C);
+ final Resources resources = activity.getResources();
assertThat(resources, notNullValue());
assertThat(resources.getString(R.string.base_string), equalTo("Base String Polish"));
+ new TestTheme(activity, R.style.Theme_Base).assertThemeBaseValues_pl();
int resourceId = resources.getIdentifier(FEATURE_C_STRING, null, null);
assertThat(resources.getString(resourceId), equalTo("Feature C String Polish"));
+ testTheme.assertThemeFeatureCValues_pl();
- assertActivitiesDoNotExist(context, FEATURE_A_ACTIVITY, FEATURE_B_ACTIVITY);
- assertResourcesDoNotExist(context, FEATURE_A_STRING, FEATURE_B_STRING);
+ // Test the theme applied to the activity correctly
+ assertActivityThemeApplied(activity, testTheme);
+ assertTextViewBGColor(activity, FEATURE_C_TEXTVIEW_ID, testTheme.mColorBackground);
+
+ assertActivitiesDoNotExist(activity, FEATURE_A_ACTIVITY, FEATURE_B_ACTIVITY);
+ assertResourcesDoNotExist(activity, FEATURE_A_STRING, FEATURE_B_STRING,
+ TestTheme.THEME_FEATURE_A, TestTheme.THEME_FEATURE_B);
+ }
+
+ @Test
+ public void shouldLoadFeatureADiffRevision() throws Exception {
+ final Activity activity = mActivityRule.launchActivity(
+ new Intent().setComponent(FEATURE_A_ACTIVITY));
+ final TestTheme testTheme = new TestTheme(activity, TestTheme.THEME_FEATURE_A);
+ final Resources resources = activity.getResources();
+ assertThat(resources, notNullValue());
+
+ assertThat(resources.getString(R.string.base_string), equalTo("Base String Default"));
+ new TestTheme(activity, R.style.Theme_Base).assertThemeBaseValues();
+
+ int resourceId = resources.getIdentifier(FEATURE_A_STRING, null, null);
+ assertThat(resources.getString(resourceId), equalTo("Feature A String Diff Revision"));
+ testTheme.assertThemeFeatureAValuesDiffRev();
+
+ // Test the theme applied to the activity correctly
+ assertActivityThemeApplied(activity, testTheme);
+ assertTextViewBGColor(activity, FEATURE_A_TEXTVIEW_ID, testTheme.mColorBackground);
+
+ assertActivitiesDoNotExist(activity, FEATURE_B_ACTIVITY, FEATURE_C_ACTIVITY);
+ assertResourcesDoNotExist(activity, FEATURE_B_STRING, FEATURE_C_STRING,
+ TestTheme.THEME_FEATURE_B, TestTheme.THEME_FEATURE_C);
}
@Test
@@ -244,6 +348,20 @@
assertThat(results.getString("feature_c"), equalTo("Feature C String Default"));
}
+ @Test
+ public void shouldNotFoundFeatureC() throws Exception {
+ assertActivityDoNotExist(FEATURE_C_ACTIVITY);
+ }
+
+ private void assertActivityDoNotExist(ComponentName activity) {
+ try {
+ mActivityRule.launchActivity(new Intent().setComponent(activity));
+ fail("Activity " + activity + " is accessible");
+ } catch (RuntimeException e) {
+ // Pass.
+ }
+ }
+
private static void assertActivitiesDoNotExist(Context context, ComponentName... activities) {
for (ComponentName activity : activities) {
try {
@@ -265,6 +383,41 @@
}
}
+ private static void assertActivityThemeApplied(Activity activity, TestTheme testTheme) {
+ assertBaseLayoutBGColor(activity, testTheme.mBaseColor);
+ assertThat(activity.getWindow().getStatusBarColor(), equalTo(testTheme.mStatusBarColor));
+ assertThat(activity.getWindow().getNavigationBarColor(),
+ equalTo(testTheme.mNavigationBarColor));
+ assertDrawableColor(activity.getWindow().getDecorView().getBackground(),
+ testTheme.mWindowBackground);
+ }
+
+ private static void assertBaseLayoutBGColor(Activity activity, int expected) {
+ final LinearLayout layout = activity.findViewById(R.id.base_layout);
+ final Drawable background = layout.getBackground();
+ assertDrawableColor(background, expected);
+ }
+
+ private static void assertTextViewBGColor(Activity activity, String nameOfIdentifier,
+ int expected) {
+ final int viewId = activity.getResources().getIdentifier(nameOfIdentifier, null, null);
+ assertThat(viewId, not(equalTo(0)));
+
+ final View view = activity.findViewById(viewId);
+ final Drawable background = view.getBackground();
+ assertDrawableColor(background, expected);
+ }
+
+ private static void assertDrawableColor(Drawable drawable, int expected) {
+ int color = 0;
+ if (drawable instanceof ColorDrawable) {
+ color = ((ColorDrawable) drawable).getColor();
+ } else {
+ fail("Can't get drawable color");
+ }
+ assertThat(color, equalTo(expected));
+ }
+
private static class ExtrasResultReceiver extends BroadcastReceiver {
private final CompletableFuture<Bundle> mResult = new CompletableFuture<>();
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/src/com/android/cts/isolatedsplitapp/TestTheme.java b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/src/com/android/cts/isolatedsplitapp/TestTheme.java
new file mode 100644
index 0000000..5909a26
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/src/com/android/cts/isolatedsplitapp/TestTheme.java
@@ -0,0 +1,240 @@
+/*
+ * 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.isolatedsplitapp;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.view.ContextThemeWrapper;
+
+/**
+ * A helper class to retrieve theme values of Theme_Base, Theme_Feature_A, Theme_Feature_B or
+ * Theme_Feature_C.
+ */
+public class TestTheme {
+
+ public static final String THEME_FEATURE_A =
+ "com.android.cts.isolatedsplitapp.feature_a:style/Theme_Feature_A";
+ public static final String THEME_FEATURE_B =
+ "com.android.cts.isolatedsplitapp.feature_b:style/Theme_Feature_B";
+ public static final String THEME_FEATURE_C =
+ "com.android.cts.isolatedsplitapp.feature_c:style/Theme_Feature_C";
+
+ public static final int COLOR_BLACK = 0xFF000000;
+ public static final int COLOR_RED = 0xFFFF0000;
+ public static final int COLOR_GREEN = 0xFF00FF00;
+ public static final int COLOR_BLUE = 0xFF0000FF;
+ public static final int COLOR_GRAY = 0xFF888888;
+ public static final int COLOR_LTGRAY = 0xFF444444;
+ public static final int COLOR_DKGRAY = 0xFFCCCCCC;
+ public static final int COLOR_YELLOW = 0xFFFFFF00;
+ public static final int COLOR_PURPLE = 0xFF800080;
+ public static final int COLOR_PINK = 0xFFFFC0CB;
+ public static final int COLOR_ORANGE = 0xFFFFA500;
+ public static final int COLOR_LINEN = 0xFFFAF0E6;
+ public static final int COLOR_MINTCREAM = 0xFFF5FFFA;
+ public static final int COLOR_CYAN = 0xFF00FFFF;
+ public static final int COLOR_MAGENTA = 0xFFFF00FF;
+ public static final int COLOR_MAROON = 0xFF800000;
+ public static final int COLOR_NAVY = 0xFF000080;
+ public static final int COLOR_OLIVE = 0xFF808000;
+
+ private static final String ATTR_THEME_PRIMARY_COLOR =
+ "com.android.cts.isolatedsplitapp.feature_a:attr/themePrimaryColor";
+ private static final String ATTR_THEME_SECONDARY_COLOR =
+ "com.android.cts.isolatedsplitapp.feature_b:attr/themeSecondaryColor";
+ private static final String ATTR_THEME_TERTIARY_COLOR =
+ "com.android.cts.isolatedsplitapp.feature_c:attr/themeTertiaryColor";
+
+ /** {@link R.attr.themeName} */
+ public String mName;
+
+ /** {@link R.attr.themeBaseColor} */
+ public int mBaseColor;
+
+ /** {@link #ATTR_THEME_PRIMARY_COLOR} */
+ public int mPrimaryColor;
+
+ /** {@link #ATTR_THEME_SECONDARY_COLOR} */
+ public int mSecondaryColor;
+
+ /** {@link #ATTR_THEME_TERTIARY_COLOR} */
+ public int mTertiaryColor;
+
+ /** {#link android.R.attr.colorBackground} */
+ public int mColorBackground;
+
+ /** {#link android.R.attr.navigationBarColor} */
+ public int mNavigationBarColor;
+
+ /** {#link android.R.attr.statusBarColor} */
+ public int mStatusBarColor;
+
+ /** {#link android.R.attr.windowBackground} */
+ public int mWindowBackground;
+
+ public TestTheme(Context context, String nameOfIdentifier) {
+ setTheme(context, nameOfIdentifier);
+ }
+
+ public TestTheme(Context context, int themeId) {
+ setTheme(context, themeId);
+ }
+
+ public void assertThemeBaseValues() {
+ assertThat(mName).isEqualTo("Base Theme");
+ assertThat(mBaseColor).isEqualTo(COLOR_BLACK);
+ assertThat(mNavigationBarColor).isEqualTo(COLOR_RED);
+ assertThat(mStatusBarColor).isEqualTo(COLOR_GREEN);
+ assertThat(mWindowBackground).isEqualTo(mBaseColor);
+ }
+
+ public void assertThemeBaseValues_pl() {
+ assertThat(mName).isEqualTo("Base Theme Polish");
+ assertThat(mBaseColor).isEqualTo(COLOR_GREEN);
+ assertThat(mNavigationBarColor).isEqualTo(COLOR_BLACK);
+ assertThat(mStatusBarColor).isEqualTo(COLOR_RED);
+ assertThat(mWindowBackground).isEqualTo(mBaseColor);
+ }
+
+ public void assertThemeFeatureAValues() {
+ assertThat(mName).isEqualTo("Feature A Theme");
+ assertThat(mBaseColor).isEqualTo(COLOR_BLUE);
+ assertThat(mPrimaryColor).isEqualTo(COLOR_GRAY);
+ assertThat(mColorBackground).isEqualTo(COLOR_LTGRAY);
+ assertThat(mNavigationBarColor).isEqualTo(COLOR_DKGRAY);
+ assertThat(mStatusBarColor).isEqualTo(COLOR_YELLOW);
+ assertThat(mWindowBackground).isEqualTo(mPrimaryColor);
+ }
+
+ public void assertThemeFeatureAValues_pl() {
+ assertThat(mName).isEqualTo("Feature A Theme Polish");
+ assertThat(mBaseColor).isEqualTo(COLOR_YELLOW);
+ assertThat(mPrimaryColor).isEqualTo(COLOR_DKGRAY);
+ assertThat(mColorBackground).isEqualTo(COLOR_GRAY);
+ assertThat(mNavigationBarColor).isEqualTo(COLOR_LTGRAY);
+ assertThat(mStatusBarColor).isEqualTo(COLOR_BLUE);
+ assertThat(mWindowBackground).isEqualTo(mPrimaryColor);
+ }
+
+ public void assertThemeFeatureAValuesDiffRev() {
+ assertThat(mName).isEqualTo("Feature A Theme Diff Revision");
+ assertThat(mBaseColor).isEqualTo(COLOR_YELLOW);
+ assertThat(mPrimaryColor).isEqualTo(COLOR_BLUE);
+ assertThat(mColorBackground).isEqualTo(COLOR_GRAY);
+ assertThat(mNavigationBarColor).isEqualTo(COLOR_LTGRAY);
+ assertThat(mStatusBarColor).isEqualTo(COLOR_DKGRAY);
+ assertThat(mWindowBackground).isEqualTo(mPrimaryColor);
+ }
+
+ public void assertThemeFeatureBValues() {
+ assertThat(mName).isEqualTo("Feature B Theme");
+ assertThat(mBaseColor).isEqualTo(COLOR_PURPLE);
+ assertThat(mSecondaryColor).isEqualTo(COLOR_PINK);
+ assertThat(mColorBackground).isEqualTo(COLOR_ORANGE);
+ assertThat(mNavigationBarColor).isEqualTo(COLOR_LINEN);
+ assertThat(mStatusBarColor).isEqualTo(COLOR_MINTCREAM);
+ assertThat(mWindowBackground).isEqualTo(mSecondaryColor);
+ }
+
+ public void assertThemeFeatureBValues_pl() {
+ assertThat(mName).isEqualTo("Feature B Theme Polish");
+ assertThat(mBaseColor).isEqualTo(COLOR_MINTCREAM);
+ assertThat(mSecondaryColor).isEqualTo(COLOR_LINEN);
+ assertThat(mColorBackground).isEqualTo(COLOR_PINK);
+ assertThat(mNavigationBarColor).isEqualTo(COLOR_ORANGE);
+ assertThat(mStatusBarColor).isEqualTo(COLOR_PURPLE);
+ assertThat(mWindowBackground).isEqualTo(mSecondaryColor);
+ }
+
+ public void assertThemeFeatureCValues() {
+ assertThat(mName).isEqualTo("Feature C Theme");
+ assertThat(mBaseColor).isEqualTo(COLOR_CYAN);
+ assertThat(mTertiaryColor).isEqualTo(COLOR_MAGENTA);
+ assertThat(mColorBackground).isEqualTo(COLOR_MAROON);
+ assertThat(mNavigationBarColor).isEqualTo(COLOR_NAVY);
+ assertThat(mStatusBarColor).isEqualTo(COLOR_OLIVE);
+ assertThat(mWindowBackground).isEqualTo(mTertiaryColor);
+ }
+
+ public void assertThemeFeatureCValues_pl() {
+ assertThat(mName).isEqualTo("Feature C Theme Polish");
+ assertThat(mBaseColor).isEqualTo(COLOR_OLIVE);
+ assertThat(mTertiaryColor).isEqualTo(COLOR_NAVY);
+ assertThat(mColorBackground).isEqualTo(COLOR_MAGENTA);
+ assertThat(mNavigationBarColor).isEqualTo(COLOR_MAROON);
+ assertThat(mStatusBarColor).isEqualTo(COLOR_CYAN);
+ assertThat(mWindowBackground).isEqualTo(mTertiaryColor);
+ }
+
+ private void setTheme(Context context, String nameOfIdentifier) {
+ final int themeId = resolveResourceId(context , nameOfIdentifier);
+ if (themeId == 0) {
+ throw new IllegalArgumentException("Failed to a resource identifier for the "
+ + nameOfIdentifier);
+ }
+ setTheme(context, themeId);
+ }
+
+ private void setTheme(Context context, int themeId) {
+ final Resources.Theme theme = new ContextThemeWrapper(context, themeId).getTheme();
+ mName = getString(theme, R.attr.themeName);
+ mBaseColor = getColor(theme, R.attr.themeBaseColor);
+ mPrimaryColor = getColor(theme, resolveResourceId(context, ATTR_THEME_PRIMARY_COLOR));
+ mSecondaryColor = getColor(theme, resolveResourceId(context, ATTR_THEME_SECONDARY_COLOR));
+ mTertiaryColor = getColor(theme, resolveResourceId(context, ATTR_THEME_TERTIARY_COLOR));
+ mColorBackground = getColor(theme, android.R.attr.colorBackground);
+ mNavigationBarColor = getColor(theme, android.R.attr.navigationBarColor);
+ mStatusBarColor = getColor(theme, android.R.attr.statusBarColor);
+ mWindowBackground = getDrawableColor(theme, android.R.attr.windowBackground);
+ }
+
+ private int resolveResourceId(Context context, String nameOfIdentifier) {
+ return context.getResources().getIdentifier(nameOfIdentifier, null, null);
+ }
+
+ private String getString(Resources.Theme theme, int resourceId) {
+ final TypedArray ta = theme.obtainStyledAttributes(new int[] {resourceId});
+ final String string = ta.getString(0);
+ ta.recycle();
+ return string;
+ }
+
+ private int getColor(Resources.Theme theme, int resourceId) {
+ if (resourceId == 0) {
+ return 0;
+ }
+ final TypedArray ta = theme.obtainStyledAttributes(new int[] {resourceId});
+ final int color = ta.getColor(0, 0);
+ ta.recycle();
+ return color;
+ }
+
+ private int getDrawableColor(Resources.Theme theme, int resourceId) {
+ final TypedArray ta = theme.obtainStyledAttributes(new int[] {resourceId});
+ final Drawable color = ta.getDrawable(0);
+ ta.recycle();
+ if (!(color instanceof ColorDrawable)) {
+ return 0;
+ }
+ return ((ColorDrawable) color).getColor();
+ }
+}
diff --git a/hostsidetests/install/app/AndroidManifest.xml b/hostsidetests/install/app/AndroidManifest.xml
index 01130f2..449d24f 100644
--- a/hostsidetests/install/app/AndroidManifest.xml
+++ b/hostsidetests/install/app/AndroidManifest.xml
@@ -18,8 +18,6 @@
package="android.cts.install">
<application>
<uses-library android:name="android.test.runner"/>
- <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
- android:exported="true"/>
<!-- This activity is necessary to register the test app as the default home activity (i.e.
to receive SESSION_COMMITTED broadcasts.) -->
<activity android:name=".LauncherActivity"
diff --git a/hostsidetests/os/app/src/android/os/app/TestFgService.java b/hostsidetests/os/app/src/android/os/app/TestFgService.java
index 3548105..e751d47 100644
--- a/hostsidetests/os/app/src/android/os/app/TestFgService.java
+++ b/hostsidetests/os/app/src/android/os/app/TestFgService.java
@@ -42,6 +42,7 @@
.setContentTitle("Foreground service")
.setContentText("Ongoing test app foreground service is live")
.setSmallIcon(NOTIFICATION_ID)
+ .setShowForegroundImmediately(true)
.build();
Log.i(TAG, "TestFgService starting foreground: pid=" + Process.myPid());
diff --git a/hostsidetests/os/test-apps/StaticSharedLibTestApp/AndroidManifest.xml b/hostsidetests/os/test-apps/StaticSharedLibTestApp/AndroidManifest.xml
index 361d21a..51589ea 100755
--- a/hostsidetests/os/test-apps/StaticSharedLibTestApp/AndroidManifest.xml
+++ b/hostsidetests/os/test-apps/StaticSharedLibTestApp/AndroidManifest.xml
@@ -19,8 +19,6 @@
package="android.os.lib.app">
<application>
- <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
- android:exported="true" />
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/hostsidetests/rollback/app/AndroidManifest.xml b/hostsidetests/rollback/app/AndroidManifest.xml
index 71cf5c1..76d239d 100644
--- a/hostsidetests/rollback/app/AndroidManifest.xml
+++ b/hostsidetests/rollback/app/AndroidManifest.xml
@@ -23,8 +23,6 @@
</queries>
<application>
- <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
- android:exported="true" />
<uses-library android:name="android.test.runner" />
</application>
diff --git a/hostsidetests/rollback/app2/AndroidManifest.xml b/hostsidetests/rollback/app2/AndroidManifest.xml
index be6d483..7e90f85 100644
--- a/hostsidetests/rollback/app2/AndroidManifest.xml
+++ b/hostsidetests/rollback/app2/AndroidManifest.xml
@@ -18,8 +18,6 @@
package="com.android.cts.rollback.host.app2" >
<application>
- <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
- android:exported="true" />
<uses-library android:name="android.test.runner" />
</application>
diff --git a/hostsidetests/scopedstorage/AndroidManifest.xml b/hostsidetests/scopedstorage/AndroidManifest.xml
index 0394f4b..da158c3 100644
--- a/hostsidetests/scopedstorage/AndroidManifest.xml
+++ b/hostsidetests/scopedstorage/AndroidManifest.xml
@@ -21,8 +21,6 @@
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application>
- <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
- android:exported="true" />
<uses-library android:name="android.test.runner" />
</application>
diff --git a/hostsidetests/scopedstorage/device/AndroidManifest.xml b/hostsidetests/scopedstorage/device/AndroidManifest.xml
index 0e91f9a..6f716fb 100644
--- a/hostsidetests/scopedstorage/device/AndroidManifest.xml
+++ b/hostsidetests/scopedstorage/device/AndroidManifest.xml
@@ -18,8 +18,6 @@
package="android.scopedstorage.cts.device">
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application android:label="Scoped Storage Device Tests">
- <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
- android:exported="true" />
<uses-library android:name="android.test.runner" />
</application>
diff --git a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
index faf334b..f88bfe9 100644
--- a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
+++ b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
@@ -69,6 +69,7 @@
import static android.scopedstorage.cts.lib.TestUtils.grantPermission;
import static android.scopedstorage.cts.lib.TestUtils.installApp;
import static android.scopedstorage.cts.lib.TestUtils.installAppWithStoragePermissions;
+import static android.scopedstorage.cts.lib.TestUtils.isAppInstalled;
import static android.scopedstorage.cts.lib.TestUtils.listAs;
import static android.scopedstorage.cts.lib.TestUtils.openWithMediaProvider;
import static android.scopedstorage.cts.lib.TestUtils.pollForExternalStorageState;
@@ -132,6 +133,7 @@
import com.google.common.io.Files;
import org.junit.After;
+import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -179,13 +181,17 @@
static final String FILE_CREATION_ERROR_MESSAGE = "No such file or directory";
- private static final TestApp TEST_APP_A = new TestApp("TestAppA",
+ // This app is installed with READ_EXTERNAL_STORAGE permission before tests are run
+ private static final TestApp APP_A_HAS_RES = new TestApp("TestAppA",
"android.scopedstorage.cts.testapp.A", 1, false, "CtsScopedStorageTestAppA.apk");
- private static final TestApp TEST_APP_B = new TestApp("TestAppB",
+ // This app is installed without any permissions before tests are run
+ private static final TestApp APP_B_NO_PERMS = new TestApp("TestAppB",
"android.scopedstorage.cts.testapp.B", 1, false, "CtsScopedStorageTestAppB.apk");
- private static final TestApp TEST_APP_C = new TestApp("TestAppC",
+ // This app is not installed at test startup - please install before using.
+ private static final TestApp APP_C = new TestApp("TestAppC",
"android.scopedstorage.cts.testapp.C", 1, false, "CtsScopedStorageTestAppC.apk");
- private static final TestApp TEST_APP_C_LEGACY = new TestApp("TestAppCLegacy",
+ // This app is not installed at test startup - please install before using.
+ private static final TestApp APP_C_LEGACY = new TestApp("TestAppCLegacy",
"android.scopedstorage.cts.testapp.C", 1, false, "CtsScopedStorageTestAppCLegacy.apk");
private static final String[] SYSTEM_GALERY_APPOPS = {
AppOpsManager.OPSTR_WRITE_MEDIA_IMAGES, AppOpsManager.OPSTR_WRITE_MEDIA_VIDEO};
@@ -208,6 +214,12 @@
}
}
+ @BeforeClass
+ public static void installApps() throws Exception {
+ installAppWithStoragePermissions(APP_A_HAS_RES);
+ installApp(APP_B_NO_PERMS);
+ }
+
@Before
public void setup() throws Exception {
// skips all test cases if FUSE is not active.
@@ -224,6 +236,12 @@
executeShellCommand("rm -r /sdcard/Android/data/com.android.shell");
}
+ @AfterClass
+ public static void uninstallApps() {
+ uninstallAppNoThrow(APP_B_NO_PERMS);
+ uninstallAppNoThrow(APP_A_HAS_RES);
+ }
+
@Before
public void setupExternalStorage() {
setupDefaultDirectories();
@@ -404,15 +422,12 @@
assertThat(videoFile.createNewFile()).isTrue();
}
- // Install TEST_APP_A with READ_EXTERNAL_STORAGE permission.
- installAppWithStoragePermissions(TEST_APP_A);
-
- // TEST_APP_A should not be able to read/write to other app's external files directory.
- assertThat(canOpenFileAs(TEST_APP_A, videoFile, false /* forWrite */)).isFalse();
- assertThat(canOpenFileAs(TEST_APP_A, videoFile, true /* forWrite */)).isFalse();
- // TEST_APP_A should not be able to delete files in other app's external files
+ // App A should not be able to read/write to other app's external files directory.
+ assertThat(canOpenFileAs(APP_A_HAS_RES, videoFile, false /* forWrite */)).isFalse();
+ assertThat(canOpenFileAs(APP_A_HAS_RES, videoFile, true /* forWrite */)).isFalse();
+ // App A should not be able to delete files in other app's external files
// directory.
- assertThat(deleteFileAs(TEST_APP_A, videoFile.getPath())).isFalse();
+ assertThat(deleteFileAs(APP_A_HAS_RES, videoFile.getPath())).isFalse();
// Apps should have read/write access in their own app's external files directory.
assertThat(canOpen(videoFile, false /* forWrite */)).isTrue();
@@ -421,7 +436,6 @@
assertThat(videoFile.delete()).isTrue();
} finally {
videoFile.delete();
- uninstallAppNoThrow(TEST_APP_A);
}
}
@@ -498,10 +512,8 @@
final File mediaFile = new File(getPicturesDir(), IMAGE_FILE_NAME);
final File nonMediaFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
try {
- installApp(TEST_APP_A);
-
- assertThat(createFileAs(TEST_APP_A, mediaFile.getPath())).isTrue();
- assertThat(createFileAs(TEST_APP_A, nonMediaFile.getPath())).isTrue();
+ assertThat(createFileAs(APP_B_NO_PERMS, mediaFile.getPath())).isTrue();
+ assertThat(createFileAs(APP_B_NO_PERMS, nonMediaFile.getPath())).isTrue();
// We can still see that the files exist
assertThat(mediaFile.exists()).isTrue();
@@ -513,9 +525,8 @@
assertThat(canOpen(mediaFile, /* forWrite */ false)).isFalse();
assertThat(canOpen(nonMediaFile, /* forWrite */ true)).isFalse();
} finally {
- deleteFileAsNoThrow(TEST_APP_A, nonMediaFile.getPath());
- deleteFileAsNoThrow(TEST_APP_A, mediaFile.getPath());
- uninstallAppNoThrow(TEST_APP_A);
+ deleteFileAsNoThrow(APP_B_NO_PERMS, nonMediaFile.getPath());
+ deleteFileAsNoThrow(APP_B_NO_PERMS, mediaFile.getPath());
}
}
@@ -525,10 +536,9 @@
final File mediaFile = new File(dirInDownload, IMAGE_FILE_NAME);
final File nonMediaFile = new File(dirInDownload, NONMEDIA_FILE_NAME);
try {
- installApp(TEST_APP_A);
assertThat(dirInDownload.mkdir()).isTrue();
// Have another app create a media file in the directory
- assertThat(createFileAs(TEST_APP_A, mediaFile.getPath())).isTrue();
+ assertThat(createFileAs(APP_B_NO_PERMS, mediaFile.getPath())).isTrue();
// Can't delete the directory since it contains another app's content
assertThat(dirInDownload.delete()).isFalse();
@@ -536,7 +546,7 @@
assertThat(deleteRecursively(dirInDownload)).isFalse();
// Have another app create a non-media file in the directory
- assertThat(createFileAs(TEST_APP_A, nonMediaFile.getPath())).isTrue();
+ assertThat(createFileAs(APP_B_NO_PERMS, nonMediaFile.getPath())).isTrue();
// Can't delete the directory since it contains another app's content
assertThat(dirInDownload.delete()).isFalse();
@@ -544,26 +554,25 @@
assertThat(deleteRecursively(dirInDownload)).isFalse();
// Delete only the media file and keep the non-media file
- assertThat(deleteFileAs(TEST_APP_A, mediaFile.getPath())).isTrue();
+ assertThat(deleteFileAs(APP_B_NO_PERMS, mediaFile.getPath())).isTrue();
// Directory now has only the non-media file contributed by another app, so we still
// can't delete it nor its content
assertThat(dirInDownload.delete()).isFalse();
assertThat(deleteRecursively(dirInDownload)).isFalse();
// Delete the last file belonging to another app
- assertThat(deleteFileAs(TEST_APP_A, nonMediaFile.getPath())).isTrue();
+ assertThat(deleteFileAs(APP_B_NO_PERMS, nonMediaFile.getPath())).isTrue();
// Create our own file
assertThat(nonMediaFile.createNewFile()).isTrue();
// Now that the directory only has content that was contributed by us, we can delete it
assertThat(deleteRecursively(dirInDownload)).isTrue();
} finally {
- deleteFileAsNoThrow(TEST_APP_A, nonMediaFile.getPath());
- deleteFileAsNoThrow(TEST_APP_A, mediaFile.getPath());
+ deleteFileAsNoThrow(APP_B_NO_PERMS, nonMediaFile.getPath());
+ deleteFileAsNoThrow(APP_B_NO_PERMS, mediaFile.getPath());
// At this point, we're not sure who created this file, so we'll have both apps
// deleting it
mediaFile.delete();
- uninstallAppNoThrow(TEST_APP_A);
dirInDownload.delete();
}
}
@@ -685,32 +694,23 @@
assertThat(dir.mkdir()).isTrue();
}
- // Install TEST_APP_A and create media file in the new directory.
- installApp(TEST_APP_A);
- assertThat(createFileAs(TEST_APP_A, videoFile.getPath())).isTrue();
- // TEST_APP_A should see TEST_DIRECTORY in DCIM and new file in TEST_DIRECTORY.
- assertThat(listAs(TEST_APP_A, dcimDir.getPath())).contains(TEST_DIRECTORY_NAME);
- assertThat(listAs(TEST_APP_A, dir.getPath())).containsExactly(videoFileName);
+ assertThat(createFileAs(APP_B_NO_PERMS, videoFile.getPath())).isTrue();
+ // App B should see TEST_DIRECTORY in DCIM and new file in TEST_DIRECTORY.
+ assertThat(listAs(APP_B_NO_PERMS, dcimDir.getPath())).contains(TEST_DIRECTORY_NAME);
+ assertThat(listAs(APP_B_NO_PERMS, dir.getPath())).containsExactly(videoFileName);
- // Install TEST_APP_B with storage permission.
- installAppWithStoragePermissions(TEST_APP_B);
- // TEST_APP_B with storage permission should see TEST_DIRECTORY in DCIM and new file
+ // App A has storage permission, so should see TEST_DIRECTORY in DCIM and new file
// in TEST_DIRECTORY.
- assertThat(listAs(TEST_APP_B, dcimDir.getPath())).contains(TEST_DIRECTORY_NAME);
- assertThat(listAs(TEST_APP_B, dir.getPath())).containsExactly(videoFileName);
+ assertThat(listAs(APP_A_HAS_RES, dcimDir.getPath())).contains(TEST_DIRECTORY_NAME);
+ assertThat(listAs(APP_A_HAS_RES, dir.getPath())).containsExactly(videoFileName);
- // Revoke storage permission for TEST_APP_B
- revokePermission(
- TEST_APP_B.getPackageName(), Manifest.permission.READ_EXTERNAL_STORAGE);
- // TEST_APP_B without storage permission should see TEST_DIRECTORY in DCIM and should
- // not see new file in new TEST_DIRECTORY.
- assertThat(listAs(TEST_APP_B, dcimDir.getPath())).contains(TEST_DIRECTORY_NAME);
- assertThat(listAs(TEST_APP_B, dir.getPath())).doesNotContain(videoFileName);
+ // We are an app without storage permission; should see TEST_DIRECTORY in DCIM and
+ // should not see new file in new TEST_DIRECTORY.
+ assertThat(dcimDir.list()).asList().contains(TEST_DIRECTORY_NAME);
+ assertThat(dir.list()).asList().doesNotContain(videoFileName);
} finally {
- uninstallAppNoThrow(TEST_APP_B);
- deleteFileAsNoThrow(TEST_APP_A, videoFile.getPath());
+ deleteFileAsNoThrow(APP_B_NO_PERMS, videoFile.getPath());
dir.delete();
- uninstallAppNoThrow(TEST_APP_A);
}
}
@@ -728,26 +728,21 @@
assertThat(dir.mkdir()).isTrue();
}
- // Install TEST_APP_A and create non media file in the new directory.
- installApp(TEST_APP_A);
- assertThat(createFileAs(TEST_APP_A, pdfFile.getPath())).isTrue();
+ // Have App B create non media file in the new directory.
+ assertThat(createFileAs(APP_B_NO_PERMS, pdfFile.getPath())).isTrue();
- // TEST_APP_A should see TEST_DIRECTORY in downloadDir and new non media file in
+ // App B should see TEST_DIRECTORY in downloadDir and new non media file in
// TEST_DIRECTORY.
- assertThat(listAs(TEST_APP_A, downloadDir.getPath())).contains(TEST_DIRECTORY_NAME);
- assertThat(listAs(TEST_APP_A, dir.getPath())).containsExactly(pdfFileName);
+ assertThat(listAs(APP_B_NO_PERMS, downloadDir.getPath())).contains(TEST_DIRECTORY_NAME);
+ assertThat(listAs(APP_B_NO_PERMS, dir.getPath())).containsExactly(pdfFileName);
- // Install TEST_APP_B with storage permission.
- installAppWithStoragePermissions(TEST_APP_B);
- // TEST_APP_B with storage permission should see TEST_DIRECTORY in downloadDir
- // and should not see new non media file in TEST_DIRECTORY.
- assertThat(listAs(TEST_APP_B, downloadDir.getPath())).contains(TEST_DIRECTORY_NAME);
- assertThat(listAs(TEST_APP_B, dir.getPath())).doesNotContain(pdfFileName);
+ // APP B with storage permission should see TEST_DIRECTORY in downloadDir
+ // and should not see non media file in TEST_DIRECTORY.
+ assertThat(listAs(APP_A_HAS_RES, downloadDir.getPath())).contains(TEST_DIRECTORY_NAME);
+ assertThat(listAs(APP_A_HAS_RES, dir.getPath())).doesNotContain(pdfFileName);
} finally {
- uninstallAppNoThrow(TEST_APP_B);
- deleteFileAsNoThrow(TEST_APP_A, pdfFile.getPath());
+ deleteFileAsNoThrow(APP_B_NO_PERMS, pdfFile.getPath());
dir.delete();
- uninstallAppNoThrow(TEST_APP_A);
}
}
@@ -768,17 +763,13 @@
// files and directories in its external directory.
assertDirectoryContains(nonmediaFile.getParentFile(), nonmediaFile);
- // Install TEST_APP_A with READ_EXTERNAL_STORAGE permission.
- // TEST_APP_A should not see other app's external files directory.
- installAppWithStoragePermissions(TEST_APP_A);
-
+ // App A should not see other app's external files directory despite RES.
assertThrows(IOException.class,
- () -> listAs(TEST_APP_A, getAndroidDataDir().getPath()));
+ () -> listAs(APP_A_HAS_RES, getAndroidDataDir().getPath()));
assertThrows(IOException.class,
- () -> listAs(TEST_APP_A, getExternalFilesDir().getPath()));
+ () -> listAs(APP_A_HAS_RES, getExternalFilesDir().getPath()));
} finally {
nonmediaFile.delete();
- uninstallAppNoThrow(TEST_APP_A);
}
}
@@ -799,18 +790,15 @@
// files.
assertDirectoryContains(videoFile.getParentFile(), videoFile);
- // Install TEST_APP_A with READ_EXTERNAL_STORAGE permission.
- // TEST_APP_A with storage permission should see other app's external media directory.
- installAppWithStoragePermissions(TEST_APP_A);
+ // App A with storage permission should see other app's external media directory.
// Apps with READ_EXTERNAL_STORAGE can list files in other app's external media
// directory.
- assertThat(listAs(TEST_APP_A, getAndroidMediaDir().getPath()))
+ assertThat(listAs(APP_A_HAS_RES, getAndroidMediaDir().getPath()))
.contains(THIS_PACKAGE_NAME);
- assertThat(listAs(TEST_APP_A, getExternalMediaDir().getPath()))
+ assertThat(listAs(APP_A_HAS_RES, getExternalMediaDir().getPath()))
.containsExactly(videoFile.getName());
} finally {
videoFile.delete();
- uninstallAppNoThrow(TEST_APP_A);
}
}
@@ -834,19 +822,17 @@
HashMap<String, String> exif = getExifMetadata(jpgFile);
assertExifMetadataMatch(exif, originalExif);
- installAppWithStoragePermissions(TEST_APP_A);
HashMap<String, String> exifFromTestApp =
- readExifMetadataFromTestApp(TEST_APP_A, jpgFile.getPath());
+ readExifMetadataFromTestApp(APP_A_HAS_RES, jpgFile.getPath());
// Other apps shouldn't have access to the same metadata without explicit permission
assertExifMetadataMismatch(exifFromTestApp, originalExif);
- // TODO(b/146346138): Test that if we give TEST_APP_A write URI permission,
+ // TODO(b/146346138): Test that if we give APP_A write URI permission,
// it would be able to access the metadata.
} // Intentionally keep the original streams open during the test so bytes are more
// likely to be in the VFS cache from both file opens
} finally {
jpgFile.delete();
- uninstallAppNoThrow(TEST_APP_A);
}
}
@@ -1129,33 +1115,33 @@
@Test
public void testReadStorageInvalidation() throws Exception {
- testAppOpInvalidation(TEST_APP_C, new File(getDcimDir(), "read_storage.jpg"),
+ testAppOpInvalidation(APP_B_NO_PERMS, new File(getDcimDir(), "read_storage.jpg"),
Manifest.permission.READ_EXTERNAL_STORAGE,
AppOpsManager.OPSTR_READ_EXTERNAL_STORAGE, /* forWrite */ false);
}
@Test
public void testWriteStorageInvalidation() throws Exception {
- testAppOpInvalidation(TEST_APP_C_LEGACY, new File(getDcimDir(), "write_storage.jpg"),
+ testAppOpInvalidation(APP_C_LEGACY, new File(getDcimDir(), "write_storage.jpg"),
Manifest.permission.WRITE_EXTERNAL_STORAGE,
AppOpsManager.OPSTR_WRITE_EXTERNAL_STORAGE, /* forWrite */ true);
}
@Test
public void testManageStorageInvalidation() throws Exception {
- testAppOpInvalidation(TEST_APP_C, new File(getDownloadDir(), "manage_storage.pdf"),
+ testAppOpInvalidation(APP_C, new File(getDownloadDir(), "manage_storage.pdf"),
/* permission */ null, OPSTR_MANAGE_EXTERNAL_STORAGE, /* forWrite */ true);
}
@Test
public void testWriteImagesInvalidation() throws Exception {
- testAppOpInvalidation(TEST_APP_C, new File(getDcimDir(), "write_images.jpg"),
+ testAppOpInvalidation(APP_B_NO_PERMS, new File(getDcimDir(), "write_images.jpg"),
/* permission */ null, AppOpsManager.OPSTR_WRITE_MEDIA_IMAGES, /* forWrite */ true);
}
@Test
public void testWriteVideoInvalidation() throws Exception {
- testAppOpInvalidation(TEST_APP_C, new File(getDcimDir(), "write_video.mp4"),
+ testAppOpInvalidation(APP_B_NO_PERMS, new File(getDcimDir(), "write_video.mp4"),
/* permission */ null, AppOpsManager.OPSTR_WRITE_MEDIA_VIDEO, /* forWrite */ true);
}
@@ -1177,27 +1163,27 @@
assertExifMetadataMatch(exif, originalExif);
// Install test app
- installAppWithStoragePermissions(TEST_APP_C);
+ installAppWithStoragePermissions(APP_C);
// Grant A_M_L and verify access to sensitive data
- grantPermission(TEST_APP_C.getPackageName(), Manifest.permission.ACCESS_MEDIA_LOCATION);
+ grantPermission(APP_C.getPackageName(), Manifest.permission.ACCESS_MEDIA_LOCATION);
HashMap<String, String> exifFromTestApp =
- readExifMetadataFromTestApp(TEST_APP_C, imgFile.getPath());
+ readExifMetadataFromTestApp(APP_C, imgFile.getPath());
assertExifMetadataMatch(exifFromTestApp, originalExif);
// Revoke A_M_L and verify sensitive data redaction
revokePermission(
- TEST_APP_C.getPackageName(), Manifest.permission.ACCESS_MEDIA_LOCATION);
- exifFromTestApp = readExifMetadataFromTestApp(TEST_APP_C, imgFile.getPath());
+ APP_C.getPackageName(), Manifest.permission.ACCESS_MEDIA_LOCATION);
+ exifFromTestApp = readExifMetadataFromTestApp(APP_C, imgFile.getPath());
assertExifMetadataMismatch(exifFromTestApp, originalExif);
// Re-grant A_M_L and verify access to sensitive data
- grantPermission(TEST_APP_C.getPackageName(), Manifest.permission.ACCESS_MEDIA_LOCATION);
- exifFromTestApp = readExifMetadataFromTestApp(TEST_APP_C, imgFile.getPath());
+ grantPermission(APP_C.getPackageName(), Manifest.permission.ACCESS_MEDIA_LOCATION);
+ exifFromTestApp = readExifMetadataFromTestApp(APP_C, imgFile.getPath());
assertExifMetadataMatch(exifFromTestApp, originalExif);
} finally {
imgFile.delete();
- uninstallAppNoThrow(TEST_APP_C);
+ uninstallAppNoThrow(APP_C);
}
}
@@ -1208,24 +1194,24 @@
assertThat(file.createNewFile()).isTrue();
// Install legacy
- installAppWithStoragePermissions(TEST_APP_C_LEGACY);
- grantPermission(TEST_APP_C_LEGACY.getPackageName(),
+ installAppWithStoragePermissions(APP_C_LEGACY);
+ grantPermission(APP_C_LEGACY.getPackageName(),
Manifest.permission.WRITE_EXTERNAL_STORAGE); // Grants write access for legacy
// Legacy app can read and write media files contributed by others
- assertThat(canOpenFileAs(TEST_APP_C_LEGACY, file, /* forWrite */ false)).isTrue();
- assertThat(canOpenFileAs(TEST_APP_C_LEGACY, file, /* forWrite */ true)).isTrue();
+ assertThat(canOpenFileAs(APP_C_LEGACY, file, /* forWrite */ false)).isTrue();
+ assertThat(canOpenFileAs(APP_C_LEGACY, file, /* forWrite */ true)).isTrue();
// Update to non-legacy
- installAppWithStoragePermissions(TEST_APP_C);
- grantPermission(TEST_APP_C_LEGACY.getPackageName(),
+ installAppWithStoragePermissions(APP_C);
+ grantPermission(APP_C_LEGACY.getPackageName(),
Manifest.permission.WRITE_EXTERNAL_STORAGE); // No effect for non-legacy
// Non-legacy app can read media files contributed by others
- assertThat(canOpenFileAs(TEST_APP_C, file, /* forWrite */ false)).isTrue();
+ assertThat(canOpenFileAs(APP_C, file, /* forWrite */ false)).isTrue();
// But cannot write
- assertThat(canOpenFileAs(TEST_APP_C, file, /* forWrite */ true)).isFalse();
+ assertThat(canOpenFileAs(APP_C, file, /* forWrite */ true)).isFalse();
} finally {
file.delete();
- uninstallAppNoThrow(TEST_APP_C);
+ uninstallAppNoThrow(APP_C);
}
}
@@ -1237,28 +1223,35 @@
assertThat(file.createNewFile()).isTrue();
// Install
- installAppWithStoragePermissions(TEST_APP_C);
- assertThat(canOpenFileAs(TEST_APP_C, file, /* forWrite */ false)).isTrue();
+ installAppWithStoragePermissions(APP_C);
+ assertThat(canOpenFileAs(APP_C, file, /* forWrite */ false)).isTrue();
// Re-install
- uninstallAppNoThrow(TEST_APP_C);
- installApp(TEST_APP_C);
- assertThat(canOpenFileAs(TEST_APP_C, file, /* forWrite */ false)).isFalse();
+ uninstallAppNoThrow(APP_C);
+ installApp(APP_C);
+ assertThat(canOpenFileAs(APP_C, file, /* forWrite */ false)).isFalse();
} finally {
file.delete();
- uninstallAppNoThrow(TEST_APP_C);
+ uninstallAppNoThrow(APP_C);
}
}
private void testAppOpInvalidation(TestApp app, File file, @Nullable String permission,
String opstr, boolean forWrite) throws Exception {
+ boolean alreadyInstalled = true;
try {
- installApp(app);
+ if (!isAppInstalled(app)) {
+ alreadyInstalled = false;
+ installApp(app);
+ }
assertThat(file.createNewFile()).isTrue();
assertAppOpInvalidation(app, file, permission, opstr, forWrite);
} finally {
file.delete();
- uninstallApp(app);
+ if (!alreadyInstalled) {
+ // only uninstall if we installed this app here
+ uninstallApp(app);
+ }
}
}
@@ -1300,11 +1293,10 @@
final File imageInAnObviouslyWrongPlace = new File(getMusicDir(), IMAGE_FILE_NAME);
try {
- installApp(TEST_APP_A);
allowAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
// Have another app create an image file
- assertThat(createFileAs(TEST_APP_A, otherAppImageFile.getPath())).isTrue();
+ assertThat(createFileAs(APP_B_NO_PERMS, otherAppImageFile.getPath())).isTrue();
assertThat(otherAppImageFile.exists()).isTrue();
// Assert we can write to the file
@@ -1323,12 +1315,11 @@
assertCanCreateFile(topLevelImageFile);
assertCanCreateFile(imageInAnObviouslyWrongPlace);
- // Put the file back in its place and let TEST_APP_A delete it
+ // Put the file back in its place and let APP B delete it
assertThat(otherAppImageFile.createNewFile()).isTrue();
} finally {
- deleteFileAsNoThrow(TEST_APP_A, otherAppImageFile.getAbsolutePath());
+ deleteFileAsNoThrow(APP_B_NO_PERMS, otherAppImageFile.getAbsolutePath());
otherAppImageFile.delete();
- uninstallApp(TEST_APP_A);
denyAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
}
}
@@ -1340,11 +1331,10 @@
final File audioInAnObviouslyWrongPlace = new File(getPicturesDir(), AUDIO_FILE_NAME);
try {
- installApp(TEST_APP_A);
allowAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
// Have another app create an audio file
- assertThat(createFileAs(TEST_APP_A, otherAppAudioFile.getPath())).isTrue();
+ assertThat(createFileAs(APP_B_NO_PERMS, otherAppAudioFile.getPath())).isTrue();
assertThat(otherAppAudioFile.exists()).isTrue();
// Assert we can't access the file
@@ -1364,8 +1354,7 @@
audioInAnObviouslyWrongPlace.createNewFile();
});
} finally {
- deleteFileAs(TEST_APP_A, otherAppAudioFile.getPath());
- uninstallApp(TEST_APP_A);
+ deleteFileAs(APP_B_NO_PERMS, otherAppAudioFile.getPath());
topLevelAudioFile.delete();
audioInAnObviouslyWrongPlace.delete();
denyAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
@@ -1380,11 +1369,10 @@
final File topLevelVideoFile = new File(getExternalStorageDir(), VIDEO_FILE_NAME);
final File musicFile = new File(getMusicDir(), AUDIO_FILE_NAME);
try {
- installApp(TEST_APP_A);
allowAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
// Have another app create a video file
- assertThat(createFileAs(TEST_APP_A, otherAppVideoFile.getPath())).isTrue();
+ assertThat(createFileAs(APP_B_NO_PERMS, otherAppVideoFile.getPath())).isTrue();
assertThat(otherAppVideoFile.exists()).isTrue();
// Write some data to the file
@@ -1408,8 +1396,7 @@
assertThat(imageFile.renameTo(musicFile)).isTrue();
assertThat(getFileRowIdFromDatabase(musicFile)).isEqualTo(-1);
} finally {
- deleteFileAsNoThrow(TEST_APP_A, otherAppVideoFile.getAbsolutePath());
- uninstallApp(TEST_APP_A);
+ deleteFileAsNoThrow(APP_B_NO_PERMS, otherAppVideoFile.getAbsolutePath());
imageFile.delete();
videoFile.delete();
topLevelVideoFile.delete();
@@ -1524,20 +1511,18 @@
final File videoFile1 = new File(getDcimDir(), VIDEO_FILE_NAME);
final File videoFile2 = new File(getMoviesDir(), VIDEO_FILE_NAME);
try {
- installApp(TEST_APP_A);
- assertThat(createFileAs(TEST_APP_A, videoFile1.getAbsolutePath())).isTrue();
- // App can't rename a file owned by TEST_APP_A.
+ assertThat(createFileAs(APP_B_NO_PERMS, videoFile1.getAbsolutePath())).isTrue();
+ // App can't rename a file owned by APP B.
assertCantRenameFile(videoFile1, videoFile2);
assertThat(videoFile2.createNewFile()).isTrue();
- // App can't rename a file to videoFile1 which is owned by TEST_APP_A
+ // App can't rename a file to videoFile1 which is owned by APP B.
assertCantRenameFile(videoFile2, videoFile1);
// TODO(b/146346138): Test that app with right URI permission should be able to rename
// the corresponding file
} finally {
- deleteFileAsNoThrow(TEST_APP_A, videoFile1.getAbsolutePath());
+ deleteFileAsNoThrow(APP_B_NO_PERMS, videoFile1.getAbsolutePath());
videoFile2.delete();
- uninstallAppNoThrow(TEST_APP_A);
}
}
@@ -1625,20 +1610,17 @@
File videoFile = new File(mediaDirectory1, VIDEO_FILE_NAME);
try {
- installApp(TEST_APP_A);
-
if (!mediaDirectory1.exists()) {
assertThat(mediaDirectory1.mkdirs()).isTrue();
}
- assertThat(createFileAs(TEST_APP_A, videoFile.getAbsolutePath())).isTrue();
+ assertThat(createFileAs(APP_B_NO_PERMS, videoFile.getAbsolutePath())).isTrue();
// App doesn't have access to videoFile1, can't rename mediaDirectory1.
assertThat(mediaDirectory1.renameTo(mediaDirectory2)).isFalse();
assertThat(videoFile.exists()).isTrue();
// Test app can delete the file since the file is not moved to new directory.
- assertThat(deleteFileAs(TEST_APP_A, videoFile.getAbsolutePath())).isTrue();
+ assertThat(deleteFileAs(APP_B_NO_PERMS, videoFile.getAbsolutePath())).isTrue();
} finally {
- deleteFileAsNoThrow(TEST_APP_A, videoFile.getAbsolutePath());
- uninstallAppNoThrow(TEST_APP_A);
+ deleteFileAsNoThrow(APP_B_NO_PERMS, videoFile.getAbsolutePath());
mediaDirectory1.delete();
}
}
@@ -1844,17 +1826,17 @@
assertDirectoryContains(dcimDir, hiddenImageFile);
- installApp(TEST_APP_A, true);
// TestApp with read permissions can't see the hidden image file created by other app
- assertThat(listAs(TEST_APP_A, dcimDir.getAbsolutePath()))
+ assertThat(listAs(APP_B_NO_PERMS, dcimDir.getAbsolutePath()))
.doesNotContain(hiddenImageFileName);
final int testAppUid =
- getContext().getPackageManager().getPackageUid(TEST_APP_A.getPackageName(), 0);
+ getContext().getPackageManager().getPackageUid(APP_B_NO_PERMS.getPackageName(),
+ 0);
// FileManager can see the hidden image file created by other app
try {
allowAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
- assertThat(listAs(TEST_APP_A, dcimDir.getAbsolutePath()))
+ assertThat(listAs(APP_B_NO_PERMS, dcimDir.getAbsolutePath()))
.contains(hiddenImageFileName);
} finally {
denyAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
@@ -1863,14 +1845,13 @@
// Gallery can not see the hidden image file created by other app
try {
allowAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
- assertThat(listAs(TEST_APP_A, dcimDir.getAbsolutePath()))
+ assertThat(listAs(APP_B_NO_PERMS, dcimDir.getAbsolutePath()))
.doesNotContain(hiddenImageFileName);
} finally {
denyAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
}
} finally {
hiddenImageFile.delete();
- uninstallAppNoThrow(TEST_APP_A);
}
}
@@ -1885,20 +1866,18 @@
Uri pendingPdfFileUri = null;
Uri trashedPdfFileUri = null;
try {
- installAppWithStoragePermissions(TEST_APP_A);
-
pendingImgaeFileUri = createPendingFile(pendingImageFile);
- assertOpenPendingOrTrashed(pendingImgaeFileUri, TEST_APP_A, /*isImageOrVideo*/ true);
+ assertOpenPendingOrTrashed(pendingImgaeFileUri, APP_A_HAS_RES, /*isImageOrVideo*/ true);
pendingPdfFileUri = createPendingFile(pendingPdfFile);
- assertOpenPendingOrTrashed(pendingPdfFileUri, TEST_APP_A,
+ assertOpenPendingOrTrashed(pendingPdfFileUri, APP_A_HAS_RES,
/*isImageOrVideo*/ false);
trashedVideoFileUri = createTrashedFile(trashedVideoFile);
- assertOpenPendingOrTrashed(trashedVideoFileUri, TEST_APP_A, /*isImageOrVideo*/ true);
+ assertOpenPendingOrTrashed(trashedVideoFileUri, APP_A_HAS_RES, /*isImageOrVideo*/ true);
trashedPdfFileUri = createTrashedFile(trashedPdfFile);
- assertOpenPendingOrTrashed(trashedPdfFileUri, TEST_APP_A,
+ assertOpenPendingOrTrashed(trashedPdfFileUri, APP_A_HAS_RES,
/*isImageOrVideo*/ false);
} finally {
@@ -1906,7 +1885,6 @@
trashedPdfFile);
deleteWithMediaProviderNoThrow(pendingImgaeFileUri, trashedVideoFileUri,
pendingPdfFileUri, trashedPdfFileUri);
- uninstallAppNoThrow(TEST_APP_A);
}
}
@@ -1917,33 +1895,30 @@
Uri imageFileUri = null;
Uri pdfFileUri = null;
try {
- installAppWithStoragePermissions(TEST_APP_A);
-
imageFileUri = createPendingFile(imageFile);
// Check that only owner package, file manager and system gallery can list pending image
// file.
- assertListPendingOrTrashed(imageFileUri, imageFile, TEST_APP_A,
+ assertListPendingOrTrashed(imageFileUri, imageFile, APP_A_HAS_RES,
/*isImageOrVideo*/ true);
trashFile(imageFileUri);
// Check that only owner package, file manager and system gallery can list trashed image
// file.
- assertListPendingOrTrashed(imageFileUri, imageFile, TEST_APP_A,
+ assertListPendingOrTrashed(imageFileUri, imageFile, APP_A_HAS_RES,
/*isImageOrVideo*/ true);
pdfFileUri = createPendingFile(pdfFile);
// Check that only owner package, file manager can list pending non media file.
- assertListPendingOrTrashed(pdfFileUri, pdfFile, TEST_APP_A,
+ assertListPendingOrTrashed(pdfFileUri, pdfFile, APP_A_HAS_RES,
/*isImageOrVideo*/ false);
trashFile(pdfFileUri);
// Check that only owner package, file manager can list trashed non media file.
- assertListPendingOrTrashed(pdfFileUri, pdfFile, TEST_APP_A,
+ assertListPendingOrTrashed(pdfFileUri, pdfFile, APP_A_HAS_RES,
/*isImageOrVideo*/ false);
} finally {
deleteWithMediaProviderNoThrow(imageFileUri, pdfFileUri);
deleteFiles(imageFile, pdfFile);
- uninstallAppNoThrow(TEST_APP_A);
}
}
@@ -1973,18 +1948,17 @@
pendingPdfFilePath = getFilePathFromUri(createPendingFile(pendingPdfFile));
trashedPdfFilePath = getFilePathFromUri(createTrashedFile(trashedPdfFile));
- installAppWithStoragePermissions(TEST_APP_A);
-
// App can't delete other app's pending and trashed file.
- assertCantDeletePathsAs(TEST_APP_A, pendingVideoFilePath, trashedImageFilePath,
+ assertCantDeletePathsAs(APP_A_HAS_RES, pendingVideoFilePath, trashedImageFilePath,
pendingPdfFilePath, trashedPdfFilePath);
final int testAppUid =
- getContext().getPackageManager().getPackageUid(TEST_APP_A.getPackageName(), 0);
+ getContext().getPackageManager().getPackageUid(APP_A_HAS_RES.getPackageName(),
+ 0);
try {
allowAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
// File Manager can delete any pending and trashed file
- assertCanDeletePathsAs(TEST_APP_A, pendingVideoFilePath, trashedImageFilePath,
+ assertCanDeletePathsAs(APP_A_HAS_RES, pendingVideoFilePath, trashedImageFilePath,
pendingPdfFilePath, trashedPdfFilePath);
} finally {
denyAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
@@ -2000,12 +1974,12 @@
// System Gallery can delete any pending and trashed image or video file.
assertTrue(isMediaTypeImageOrVideo(new File(pendingVideoFilePath)));
assertTrue(isMediaTypeImageOrVideo(new File(trashedImageFilePath)));
- assertCanDeletePathsAs(TEST_APP_A, pendingVideoFilePath, trashedImageFilePath);
+ assertCanDeletePathsAs(APP_A_HAS_RES, pendingVideoFilePath, trashedImageFilePath);
// System Gallery can't delete other app's pending and trashed pdf file.
assertFalse(isMediaTypeImageOrVideo(new File(pendingPdfFilePath)));
assertFalse(isMediaTypeImageOrVideo(new File(trashedPdfFilePath)));
- assertCantDeletePathsAs(TEST_APP_A, pendingPdfFilePath, trashedPdfFilePath);
+ assertCantDeletePathsAs(APP_A_HAS_RES, pendingPdfFilePath, trashedPdfFilePath);
} finally {
denyAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
}
@@ -2013,7 +1987,6 @@
deletePaths(pendingVideoFilePath, trashedImageFilePath, pendingPdfFilePath,
trashedPdfFilePath);
deleteFiles(pendingVideoFile, trashedImageFile, pendingPdfFile, trashedPdfFile);
- uninstallAppNoThrow(TEST_APP_A);
}
}
@@ -2024,10 +1997,9 @@
final File otherAppMusic = new File(getMusicDir(), "other" + AUDIO_FILE_NAME);
final File otherHiddenFile = new File(getPicturesDir(), ".otherHiddenFile.jpg");
try {
- installApp(TEST_APP_A);
// Apps can't query other app's pending file, hence create file and publish it.
assertCreatePublishedFilesAs(
- TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf, otherHiddenFile);
+ APP_B_NO_PERMS, otherAppImg, otherAppMusic, otherAppPdf, otherHiddenFile);
// Since the test doesn't have READ_EXTERNAL_STORAGE nor any other special permissions,
// it can't query for another app's contents.
@@ -2036,8 +2008,7 @@
assertCantQueryFile(otherAppPdf);
assertCantQueryFile(otherHiddenFile);
} finally {
- deleteFilesAs(TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf, otherHiddenFile);
- uninstallApp(TEST_APP_A);
+ deleteFilesAs(APP_B_NO_PERMS, otherAppImg, otherAppMusic, otherAppPdf, otherHiddenFile);
}
}
@@ -2048,10 +2019,9 @@
final File otherAppMusic = new File(getMusicDir(), "other" + AUDIO_FILE_NAME);
final File otherHiddenFile = new File(getPicturesDir(), ".otherHiddenFile.jpg");
try {
- installApp(TEST_APP_A);
// Apps can't query other app's pending file, hence create file and publish it.
assertCreatePublishedFilesAs(
- TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf, otherHiddenFile);
+ APP_B_NO_PERMS, otherAppImg, otherAppMusic, otherAppPdf, otherHiddenFile);
// System gallery apps have access to video and image files
allowAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
@@ -2064,8 +2034,7 @@
assertCantQueryFile(otherAppPdf);
} finally {
denyAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
- deleteFilesAs(TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf, otherHiddenFile);
- uninstallApp(TEST_APP_A);
+ deleteFilesAs(APP_B_NO_PERMS, otherAppImg, otherAppMusic, otherAppPdf, otherHiddenFile);
}
}
@@ -2091,10 +2060,9 @@
executeShellCommand("touch " + otherAppPdfFile1);
MediaStore.scanFile(getContentResolver(), otherAppPdfFile1);
- installAppWithStoragePermissions(TEST_APP_A);
allowAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
- assertCreateFilesAs(TEST_APP_A, otherAppImageFile1, otherAppVideoFile1);
+ assertCreateFilesAs(APP_A_HAS_RES, otherAppImageFile1, otherAppVideoFile1);
// System gallery privileges don't go beyond DCIM, Movies and Pictures boundaries.
assertCantRenameDirectory(dirInDcim, dirInPodcasts, /*oldFilesList*/ null);
@@ -2118,7 +2086,6 @@
otherAppPdfFile2.delete();
dirInDcim.delete();
dirInPictures.delete();
- uninstallAppNoThrow(TEST_APP_A);
denyAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
}
}
@@ -2145,8 +2112,7 @@
assertThat(cr.openFileDescriptor(uriOfOldFile, "rw")).isNotNull();
assertThat(imageFile.delete()).isTrue();
- installApp(TEST_APP_A);
- assertThat(createFileAs(TEST_APP_A, imageFile.getAbsolutePath())).isTrue();
+ assertThat(createFileAs(APP_B_NO_PERMS, imageFile.getAbsolutePath())).isTrue();
final Uri uriOfNewFile = MediaStore.scanFile(getContentResolver(), imageFile);
assertThat(uriOfNewFile).isNotNull();
@@ -2155,8 +2121,7 @@
.isNotEqualTo(oldRowId);
} finally {
imageFile.delete();
- deleteFileAsNoThrow(TEST_APP_A, imageFile.getAbsolutePath());
- uninstallAppNoThrow(TEST_APP_A);
+ deleteFileAsNoThrow(APP_B_NO_PERMS, imageFile.getAbsolutePath());
}
}
@@ -2280,8 +2245,7 @@
assertThat(c.getInt(0)).isEqualTo(0);
}
- installAppWithStoragePermissions(TEST_APP_A);
- assertCreateFilesAs(TEST_APP_A, otherPendingFile);
+ assertCreateFilesAs(APP_A_HAS_RES, otherPendingFile);
// We can't query other apps pending file from FUSE with MATCH_EXCLUDE
try (Cursor c = queryFileExcludingPending(otherPendingFile,
MediaStore.MediaColumns.IS_PENDING)) {
@@ -2289,8 +2253,7 @@
}
} finally {
pendingFile.delete();
- deleteFileAsNoThrow(TEST_APP_A, otherPendingFile.getAbsolutePath());
- uninstallAppNoThrow(TEST_APP_A);
+ deleteFileAsNoThrow(APP_A_HAS_RES, otherPendingFile.getAbsolutePath());
}
}
@@ -2308,17 +2271,13 @@
assertThat(externalMediaPath.createNewFile()).isTrue();
}
- // Install TEST_APP_A with READ_EXTERNAL_STORAGE permission.
- installAppWithStoragePermissions(TEST_APP_A);
-
- // TEST_APP_A should not be able to setattr to other app's files.
+ // APP A should not be able to setattr to other app's files.
assertWithMessage(
"setattr on directory/external media path [%s]", externalMediaPath.getPath())
- .that(setAttrAs(TEST_APP_A, externalMediaPath.getPath()))
+ .that(setAttrAs(APP_A_HAS_RES, externalMediaPath.getPath()))
.isFalse();
} finally {
externalMediaPath.delete();
- uninstallAppNoThrow(TEST_APP_A);
}
}
diff --git a/hostsidetests/scopedstorage/legacy/AndroidManifest.xml b/hostsidetests/scopedstorage/legacy/AndroidManifest.xml
index 07fbfe8..58259b2 100644
--- a/hostsidetests/scopedstorage/legacy/AndroidManifest.xml
+++ b/hostsidetests/scopedstorage/legacy/AndroidManifest.xml
@@ -20,8 +20,6 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application android:requestLegacyExternalStorage="true" >
- <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
- android:exported="true" />
<uses-library android:name="android.test.runner" />
</application>
diff --git a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java
index 60400fb..e9d98c5 100644
--- a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java
+++ b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java
@@ -325,7 +325,7 @@
final String packageName = testApp.getPackageName();
uiAutomation.adoptShellPermissionIdentity(
Manifest.permission.INSTALL_PACKAGES, Manifest.permission.DELETE_PACKAGES);
- if (InstallUtils.getInstalledVersion(packageName) != -1) {
+ if (isAppInstalled(testApp)) {
Uninstall.packages(packageName);
}
Install.single(testApp).commit();
@@ -338,6 +338,10 @@
}
}
+ public static boolean isAppInstalled(TestApp testApp) {
+ return InstallUtils.getInstalledVersion(testApp.getPackageName()) != -1;
+ }
+
/**
* Uninstalls a {@link TestApp}.
*/
@@ -939,7 +943,9 @@
uiAutomation.adoptShellPermissionIdentity(Manifest.permission.FORCE_STOP_PACKAGES);
getContext().getSystemService(ActivityManager.class).forceStopPackage(packageName);
- Thread.sleep(1000);
+ pollForCondition(() -> {
+ return !isProcessRunning(packageName);
+ }, "Timed out while waiting for " + packageName + " to be stopped");
} finally {
uiAutomation.dropShellPermissionIdentity();
}
@@ -1186,4 +1192,14 @@
assertThat(parts[2]).isNotEqualTo("emulated");
}
}
+
+ private static boolean isProcessRunning(String packageName) {
+ try {
+ return getContext().getSystemService(
+ ActivityManager.class).getRunningAppProcesses().stream().anyMatch(
+ p -> packageName.equals(p.processName));
+ } catch (Exception e) {
+ return false;
+ }
+ }
}
diff --git a/hostsidetests/settings/app/DeviceOwnerApp/Android.bp b/hostsidetests/settings/app/DeviceOwnerApp/Android.bp
index 5fd8e6b..0a22b57 100644
--- a/hostsidetests/settings/app/DeviceOwnerApp/Android.bp
+++ b/hostsidetests/settings/app/DeviceOwnerApp/Android.bp
@@ -33,6 +33,7 @@
"androidx.test.rules",
"ub-uiautomator",
"truth-prebuilt",
+ "cts-wm-util",
],
// tag this module as a cts test artifact
test_suites: [
diff --git a/hostsidetests/settings/app/DeviceOwnerApp/src/com/google/android/cts/deviceowner/DeviceOwnerTest.java b/hostsidetests/settings/app/DeviceOwnerApp/src/com/google/android/cts/deviceowner/DeviceOwnerTest.java
index 06b1259..6581614 100644
--- a/hostsidetests/settings/app/DeviceOwnerApp/src/com/google/android/cts/deviceowner/DeviceOwnerTest.java
+++ b/hostsidetests/settings/app/DeviceOwnerApp/src/com/google/android/cts/deviceowner/DeviceOwnerTest.java
@@ -15,6 +15,8 @@
*/
package com.google.android.cts.deviceowner;
+import static android.server.wm.WindowManagerState.STATE_RESUMED;
+
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
@@ -23,6 +25,7 @@
import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.provider.Settings;
+import android.server.wm.WindowManagerStateHelper;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.Until;
@@ -106,8 +109,15 @@
private void launchSettingsPage(Context ctx, String pageName) throws Exception {
Intent intent = new Intent(pageName);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+
+ ComponentName componentName =
+ ctx.getPackageManager()
+ .resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY)
+ .getComponentInfo()
+ .getComponentName();
ctx.startActivity(intent);
- Thread.sleep(TIMEOUT * 2);
+
+ new WindowManagerStateHelper().waitForActivityState(componentName, STATE_RESUMED);
}
private void disableWorkPolicyInfoActivity() {
diff --git a/hostsidetests/stagedinstall/app/AndroidManifest.xml b/hostsidetests/stagedinstall/app/AndroidManifest.xml
index 679c55d..b0f548c 100644
--- a/hostsidetests/stagedinstall/app/AndroidManifest.xml
+++ b/hostsidetests/stagedinstall/app/AndroidManifest.xml
@@ -22,9 +22,6 @@
</queries>
<application>
- <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
- android:exported="true"/>
-
<!-- This activity is necessary to register the test app as the default home activity (i.e.
to receive SESSION_COMMITTED broadcasts.) -->
<activity android:name=".LauncherActivity"
diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/statsd/UidAtomTests.java b/hostsidetests/statsdatom/src/android/cts/statsdatom/statsd/UidAtomTests.java
index 7e97be2..85de6dd 100644
--- a/hostsidetests/statsdatom/src/android/cts/statsdatom/statsd/UidAtomTests.java
+++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/statsd/UidAtomTests.java
@@ -786,36 +786,6 @@
assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.OFF_VALUE);
assertThat(atom.getBytesField().getExperimentIdList()).isEmpty();
}
-
- public void testNotificationReported() throws Exception {
- StatsdConfig.Builder config = ConfigUtils.createConfigBuilder(
- DeviceUtils.STATSD_ATOM_TEST_PKG);
- FieldValueMatcher.Builder fvm = ConfigUtils.createFvm(
- NotificationReported.PACKAGE_NAME_FIELD_NUMBER).setEqString(
- DeviceUtils.STATSD_ATOM_TEST_PKG);
- ConfigUtils.addEventMetric(config, Atom.NOTIFICATION_REPORTED_FIELD_NUMBER,
- Collections.singletonList(fvm));
- ConfigUtils.uploadConfig(getDevice(), config);
-
- DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
- "StatsdCtsForegroundActivity", "action", "action.show_notification");
- Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
- assertThat(data).hasSize(1);
- assertThat(data.get(0).getAtom().hasNotificationReported()).isTrue();
- AtomsProto.NotificationReported n = data.get(0).getAtom().getNotificationReported();
- assertThat(n.getPackageName()).isEqualTo(DeviceUtils.STATSD_ATOM_TEST_PKG);
- assertThat(n.getUid()).isEqualTo(DeviceUtils.getStatsdTestAppUid(getDevice()));
- assertThat(n.getNotificationIdHash()).isEqualTo(1); // smallHash(0x7f080001)
- assertThat(n.getChannelIdHash()).isEqualTo(SmallHash.hash("StatsdCtsChannel"));
- assertThat(n.getGroupIdHash()).isEqualTo(0);
- assertFalse(n.getIsGroupSummary());
- assertThat(n.getCategory()).isEmpty();
- assertThat(n.getStyle()).isEqualTo(0);
- assertThat(n.getNumPeople()).isEqualTo(0);
- }
/*
public void testMobileBytesTransfer() throws Throwable {
final int appUid = getUid();
diff --git a/hostsidetests/systemui/Android.bp b/hostsidetests/systemui/Android.bp
index 506b789..562bc5a 100644
--- a/hostsidetests/systemui/Android.bp
+++ b/hostsidetests/systemui/Android.bp
@@ -20,6 +20,11 @@
"cts-tradefed",
"tradefed",
"compatibility-host-util",
+ "core_cts_test_resources",
+ ],
+
+ static_libs: [
+ "cts-statsd-atom-host-test-utils",
],
// Tag this module as a cts test artifact
test_suites: [
diff --git a/hostsidetests/systemui/app/AndroidManifest.xml b/hostsidetests/systemui/app/AndroidManifest.xml
index 3ca3ef5..0ffc130 100755
--- a/hostsidetests/systemui/app/AndroidManifest.xml
+++ b/hostsidetests/systemui/app/AndroidManifest.xml
@@ -19,6 +19,9 @@
package="android.systemui.cts">
<application>
+ <activity android:name=".TestNotificationActivity"
+ android:exported="true"/>
+
<service android:name=".TestTileService"
android:icon="@android:drawable/ic_delete"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
diff --git a/hostsidetests/systemui/app/res/layout/activity_notification.xml b/hostsidetests/systemui/app/res/layout/activity_notification.xml
new file mode 100644
index 0000000..836f7bb
--- /dev/null
+++ b/hostsidetests/systemui/app/res/layout/activity_notification.xml
@@ -0,0 +1,21 @@
+<!--
+ ~ 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.
+ -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+</RelativeLayout>
\ No newline at end of file
diff --git a/hostsidetests/systemui/app/src/android/systemui/cts/TestNotificationActivity.java b/hostsidetests/systemui/app/src/android/systemui/cts/TestNotificationActivity.java
new file mode 100644
index 0000000..5ebf309
--- /dev/null
+++ b/hostsidetests/systemui/app/src/android/systemui/cts/TestNotificationActivity.java
@@ -0,0 +1,76 @@
+/*
+ * 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.systemui.cts;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+public class TestNotificationActivity extends Activity {
+ private static final String TAG = TestNotificationActivity.class.getSimpleName();
+
+ public static final String KEY_ACTION = "action";
+ public static final String ACTION_SHOW_NOTIFICATION = "action.show_notification";
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+
+ Intent intent = this.getIntent();
+ if (intent == null) {
+ Log.e(TAG, "Intent was null.");
+ finish();
+ }
+
+ String action = intent.getStringExtra(KEY_ACTION);
+ Log.i(TAG, "Starting " + action + " from foreground activity.");
+
+ switch (action) {
+ case ACTION_SHOW_NOTIFICATION:
+ doShowNotification();
+ break;
+ default:
+ Log.e(TAG, "Intent had invalid action " + action);
+ finish();
+ }
+ }
+
+ private void doShowNotification() {
+ final int notificationId = R.layout.activity_notification;
+ final String notificationChannelId = "SystemUiCtsChannel";
+
+ NotificationManager nm = getSystemService(NotificationManager.class);
+ NotificationChannel channel = new NotificationChannel(notificationChannelId, "SystemUi Cts",
+ NotificationManager.IMPORTANCE_DEFAULT);
+ channel.setDescription("SystemUi Cts Channel");
+ nm.createNotificationChannel(channel);
+
+ nm.notify(
+ notificationId,
+ new Notification.Builder(this, notificationChannelId)
+ .setSmallIcon(android.R.drawable.stat_notify_chat)
+ .setContentTitle("SystemUiCts")
+ .setContentText("SystemUiCts")
+ .build());
+ nm.cancel(notificationId);
+ finish();
+ }
+}
diff --git a/hostsidetests/systemui/src/android/host/systemui/StatsdNotificationAtomTest.java b/hostsidetests/systemui/src/android/host/systemui/StatsdNotificationAtomTest.java
new file mode 100644
index 0000000..f82edc4
--- /dev/null
+++ b/hostsidetests/systemui/src/android/host/systemui/StatsdNotificationAtomTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.host.systemui;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.cts.statsdatom.lib.AtomTestUtils;
+import android.cts.statsdatom.lib.ConfigUtils;
+import android.cts.statsdatom.lib.DeviceUtils;
+import android.cts.statsdatom.lib.ReportUtils;
+
+import com.android.internal.os.StatsdConfigProto;
+import com.android.os.AtomsProto;
+import com.android.os.StatsLog;
+import com.android.server.notification.SmallHash;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+
+import java.util.Collections;
+import java.util.List;
+
+public class StatsdNotificationAtomTest extends DeviceTestCase implements IBuildReceiver {
+ private static final String NOTIFICATION_TEST_APK = "CtsSystemUiDeviceApp.apk";
+ private static final String NOTIFICATION_TEST_PKG = "android.systemui.cts";
+
+ private static final String TEST_NOTIFICATION_ACTIVITY = "TestNotificationActivity";
+ private static final String ACTION_KEY = "action";
+ private static final String ACTION_VALUE = "action.show_notification";
+ private static final String NOTIFICATION_CHANNEL_ID = "SystemUiCtsChannel";
+
+ private IBuildInfo mCtsBuild;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ assertThat(mCtsBuild).isNotNull();
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ DeviceUtils.installTestApp(getDevice(), NOTIFICATION_TEST_APK, NOTIFICATION_TEST_PKG,
+ mCtsBuild);
+ Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ DeviceUtils.uninstallTestApp(getDevice(), NOTIFICATION_TEST_PKG);
+ super.tearDown();
+ }
+
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = buildInfo;
+ }
+
+ public void testNotificationReported() throws Exception {
+ StatsdConfigProto.StatsdConfig.Builder config = ConfigUtils.createConfigBuilder(
+ NOTIFICATION_TEST_PKG);
+ StatsdConfigProto.FieldValueMatcher.Builder fvm = ConfigUtils.createFvm(
+ AtomsProto.NotificationReported.PACKAGE_NAME_FIELD_NUMBER).setEqString(
+ NOTIFICATION_TEST_PKG);
+ ConfigUtils.addEventMetric(config, AtomsProto.Atom.NOTIFICATION_REPORTED_FIELD_NUMBER,
+ Collections.singletonList(fvm));
+ ConfigUtils.uploadConfig(getDevice(), config);
+
+ DeviceUtils.runActivity(getDevice(), NOTIFICATION_TEST_PKG,
+ TEST_NOTIFICATION_ACTIVITY, ACTION_KEY, ACTION_VALUE);
+ Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
+
+ // Sorted list of events in order in which they occurred.
+ List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
+ assertThat(data).hasSize(1);
+ assertThat(data.get(0).getAtom().hasNotificationReported()).isTrue();
+ AtomsProto.NotificationReported n = data.get(0).getAtom().getNotificationReported();
+ assertThat(n.getPackageName()).isEqualTo(NOTIFICATION_TEST_PKG);
+ assertThat(n.getUid()).isEqualTo(DeviceUtils.getAppUid(getDevice(), NOTIFICATION_TEST_PKG));
+ assertThat(n.getNotificationIdHash()).isEqualTo(0); // smallHash(0x7F020000)
+ assertThat(n.getChannelIdHash()).isEqualTo(SmallHash.hash(NOTIFICATION_CHANNEL_ID));
+ assertThat(n.getGroupIdHash()).isEqualTo(0);
+ assertFalse(n.getIsGroupSummary());
+ assertThat(n.getCategory()).isEmpty();
+ assertThat(n.getStyle()).isEqualTo(0);
+ assertThat(n.getNumPeople()).isEqualTo(0);
+ }
+}
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/JobInfoTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/JobInfoTest.java
index 819ee53..1a33df0 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/JobInfoTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/JobInfoTest.java
@@ -130,6 +130,13 @@
assertTrue(persistableBundleEquals(pb, ji.getExtras()));
}
+ public void testForegroundJob() {
+ JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent)
+ .setForeground(true)
+ .build();
+ assertTrue(ji.isForegroundJob());
+ }
+
public void testImportantWhileForeground() {
// Assert the default value is false
JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent)
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/JobParametersTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/JobParametersTest.java
index 91b7dc9..8b7ca03 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/JobParametersTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/JobParametersTest.java
@@ -65,6 +65,32 @@
assertTrue(persistableBundleEquals(pb, params.getExtras()));
}
+ public void testForeground() throws Exception {
+ JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent)
+ .setForeground(true)
+ .build();
+
+ kTestEnvironment.setExpectedExecutions(1);
+ mJobScheduler.schedule(ji);
+ runSatisfiedJob(JOB_ID);
+ assertTrue("Job didn't fire immediately", kTestEnvironment.awaitExecution());
+
+ JobParameters params = kTestEnvironment.getLastJobParameters();
+ assertTrue(params.isForegroundJob());
+
+ ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent)
+ .setForeground(false)
+ .build();
+
+ kTestEnvironment.setExpectedExecutions(1);
+ mJobScheduler.schedule(ji);
+ runSatisfiedJob(JOB_ID);
+ assertTrue("Job didn't fire immediately", kTestEnvironment.awaitExecution());
+
+ params = kTestEnvironment.getLastJobParameters();
+ assertFalse(params.isForegroundJob());
+ }
+
public void testJobId() throws Exception {
JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent)
.build();
diff --git a/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java b/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
index c3fcf4c..fe194f2 100644
--- a/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
+++ b/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
@@ -101,7 +101,7 @@
private static final int EXIT_CODE = 123;
private static final int CRASH_SIGNAL = OsConstants.SIGSEGV;
- private static final int WAITFOR_MSEC = 5000;
+ private static final int WAITFOR_MSEC = 10000;
private static final int WAITFOR_SETTLE_DOWN = 2000;
private static final int CMD_PID = 1;
diff --git a/tests/app/src/android/app/cts/DownloadManagerTest.java b/tests/app/src/android/app/cts/DownloadManagerTest.java
index 108f173..9bf0629 100644
--- a/tests/app/src/android/app/cts/DownloadManagerTest.java
+++ b/tests/app/src/android/app/cts/DownloadManagerTest.java
@@ -439,6 +439,16 @@
return process;
}
+ private int getExternalVolumeMediaStoreFilesCount() {
+ Uri rootUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL);
+ String[] projection = {MediaStore.MediaColumns._ID};
+ int count = 0;
+ try (Cursor cursor = mContext.getContentResolver().query(rootUri, projection, null, null)) {
+ count = cursor.getCount();
+ }
+ return count;
+ }
+
@FlakyTest
@Test
public void testProviderAcceptsCleartext() throws Exception {
@@ -641,10 +651,14 @@
int allDownloads = getTotalNumberDownloads();
assertEquals(1, allDownloads);
+ int countBeforeDownload = getExternalVolumeMediaStoreFilesCount();
receiver.waitForDownloadComplete(SHORT_TIMEOUT, id);
assertSuccessfulDownload(id, new File(
Environment.getExternalStoragePublicDirectory(destination), subPath));
+ int countAfterDownload = getExternalVolumeMediaStoreFilesCount();
+ // Asserts that only one row entry is added for 1 download
+ assertEquals((countBeforeDownload + 1), countAfterDownload);
final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(id);
mContext.grantUriPermission("com.android.shell", downloadUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
diff --git a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
index d28520c..1ca6107 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -2777,6 +2777,9 @@
final Rect activeArraySize = mStaticInfo.getActiveArraySizeChecked();
final Rect defaultCropRegion =
new Rect(0, 0, activeArraySize.width(), activeArraySize.height());
+ final Rect zoom2xCropRegion =
+ new Rect(activeArraySize.width()/4, activeArraySize.height()/4,
+ activeArraySize.width()*3/4, activeArraySize.height()*3/4);
MeteringRectangle[][] expectRegions = new MeteringRectangle[ZOOM_STEPS][];
CaptureRequest.Builder requestBuilder =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
@@ -2818,6 +2821,7 @@
Log.v(TAG, "Testing Zoom ratio " + zoomFactor + " Preview size is " + previewSize);
}
requestBuilder.set(CaptureRequest.CONTROL_ZOOM_RATIO, zoomFactor);
+ requestBuilder.set(CaptureRequest.SCALER_CROP_REGION, defaultCropRegion);
CaptureRequest request = requestBuilder.build();
for (int j = 0; j < captureSubmitRepeat; ++j) {
mSession.capture(request, listener, mHandler);
@@ -2894,6 +2898,37 @@
}
previousRatio = resultZoomRatio;
+
+ /*
+ * Set windowboxing cropRegion while zoomRatio is not 1.0x, and make sure the crop
+ * region was overwritten.
+ */
+ if (zoomFactor != 1.0f) {
+ requestBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoom2xCropRegion);
+ CaptureRequest requestWithCrop = requestBuilder.build();
+ for (int j = 0; j < captureSubmitRepeat; ++j) {
+ mSession.capture(requestWithCrop, listener, mHandler);
+ }
+
+ waitForNumResults(listener, captureSubmitRepeat - 1); // Drop first few frames
+ CaptureResult resultWithCrop = listener.getCaptureResultForRequest(
+ requestWithCrop, NUM_RESULTS_WAIT_TIMEOUT);
+ float resultZoomRatioWithCrop = getValueNotNull(resultWithCrop,
+ CaptureResult.CONTROL_ZOOM_RATIO);
+ Rect cropRegionWithCrop = getValueNotNull(resultWithCrop,
+ CaptureResult.SCALER_CROP_REGION);
+
+ mCollector.expectTrue(String.format(
+ "Result zoom ratio should remain the same (activeArrayCrop: %f, " +
+ "zoomedCrop: %f)", resultZoomRatio, resultZoomRatioWithCrop),
+ Math.abs(resultZoomRatio - resultZoomRatioWithCrop) < ZOOM_ERROR_MARGIN);
+
+ if (mStaticInfo.isHardwareLevelAtLeastLimited()) {
+ mCollector.expectRectsAreSimilar(
+ "Result crop region should remain the same with or without crop",
+ cropRegion, cropRegionWithCrop, CROP_REGION_ERROR_PERCENT_DELTA);
+ }
+ }
}
}
diff --git a/tests/framework/base/windowmanager/intent_tests/clearCases/test-no-history-1.json b/tests/framework/base/windowmanager/intent_tests/clearCases/test-no-history-1.json
new file mode 100644
index 0000000..5e90c3f
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/clearCases/test-no-history-1.json
@@ -0,0 +1,44 @@
+{
+ "setup": {
+ "initialIntents": [
+ {
+ "flags": "FLAG_ACTIVITY_NO_HISTORY | FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$RegularActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ],
+ "act": [
+ {
+ "flags": "",
+ "class": "android.server.wm.intent.Activities$SingleTopActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ]
+ },
+ "initialState": {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts/android.server.wm.intent.Activities$RegularActivity",
+ "state": "RESUMED"
+ }
+ ]
+ }
+ ]
+ },
+ "endState": {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts/android.server.wm.intent.Activities$SingleTopActivity",
+ "state": "RESUMED"
+ }
+ ]
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/tests/framework/base/windowmanager/intent_tests/clearCases/test-no-history-2.json b/tests/framework/base/windowmanager/intent_tests/clearCases/test-no-history-2.json
new file mode 100644
index 0000000..0e6e758
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/clearCases/test-no-history-2.json
@@ -0,0 +1,58 @@
+{
+ "setup": {
+ "initialIntents": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$RegularActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ },
+ {
+ "flags": "FLAG_ACTIVITY_NO_HISTORY",
+ "class": "android.server.wm.intent.Activities$SingleTopActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ],
+ "act": [
+ {
+ "flags": "",
+ "class": "android.server.wm.intent.Activities$RegularActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ]
+ },
+ "initialState": {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts/android.server.wm.intent.Activities$SingleTopActivity",
+ "state": "RESUMED"
+ },
+ {
+ "name": "android.server.wm.cts/android.server.wm.intent.Activities$RegularActivity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ]
+ },
+ "endState": {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts/android.server.wm.intent.Activities$RegularActivity",
+ "state": "RESUMED"
+ },
+ {
+ "name": "android.server.wm.cts/android.server.wm.intent.Activities$RegularActivity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/intent/Cases.java b/tests/framework/base/windowmanager/src/android/server/wm/intent/Cases.java
index 3428c15..a5cce2a 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/intent/Cases.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/intent/Cases.java
@@ -21,6 +21,7 @@
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NO_HISTORY;
import static android.content.Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP;
import static android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT;
import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
@@ -67,6 +68,8 @@
"FLAG_ACTIVITY_PREVIOUS_IS_TOP");
public static final IntentFlag REORDER_TO_FRONT = flag(FLAG_ACTIVITY_REORDER_TO_FRONT,
"FLAG_ACTIVITY_REORDER_TO_FRONT");
+ public static final IntentFlag NO_HISTORY = flag(FLAG_ACTIVITY_NO_HISTORY,
+ "FLAG_ACTIVITY_NO_HISTORY");
// Flag only used for parsing intents that contain no flags.
private static final IntentFlag NONE = flag(0, "");
@@ -80,7 +83,8 @@
MULTIPLE_TASK,
RESET_TASK_IF_NEEDED,
PREVIOUS_IS_TOP,
- REORDER_TO_FRONT
+ REORDER_TO_FRONT,
+ NO_HISTORY
);
// Definition of intents used across multiple test cases.
diff --git a/tests/rollback/AndroidManifest.xml b/tests/rollback/AndroidManifest.xml
index 3203f25..4ea3ee7 100644
--- a/tests/rollback/AndroidManifest.xml
+++ b/tests/rollback/AndroidManifest.xml
@@ -19,8 +19,6 @@
package="com.android.cts.rollback" >
<application>
- <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
- android:exported="true" />
<uses-library android:name="android.test.runner" />
</application>
diff --git a/tests/sensor/src/android/hardware/cts/SensorTest.java b/tests/sensor/src/android/hardware/cts/SensorTest.java
index f64bc75..68ab07d 100644
--- a/tests/sensor/src/android/hardware/cts/SensorTest.java
+++ b/tests/sensor/src/android/hardware/cts/SensorTest.java
@@ -89,7 +89,8 @@
mAndroidSensorList = new ArrayList<>();
for (Sensor s : mSensorList) {
- if (s.getType() < Sensor.TYPE_DEVICE_PRIVATE_BASE) {
+ if (s.getType() < Sensor.TYPE_DEVICE_PRIVATE_BASE &&
+ (!context.getPackageManager().isInstantApp() || s.getType() != Sensor.TYPE_HEART_RATE)) {
mAndroidSensorList.add(s);
}
}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/Foo.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/Foo.aidl
index f2b551c..27dddbf 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/Foo.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/Foo.aidl
@@ -31,4 +31,9 @@
test_package.LongEnum[] shouldContainTwoLongFoos;
@nullable String[] g;
@nullable test_package.SimpleUnion u;
+ int shouldSetBit0AndBit2;
+ @nullable test_package.SimpleUnion shouldBeConstS1;
+ const int BIT0 = 1;
+ const int BIT1 = 2;
+ const int BIT2 = 4;
}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/SimpleUnion.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/SimpleUnion.aidl
index 7c1564a..1d896cb 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/SimpleUnion.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/SimpleUnion.aidl
@@ -23,4 +23,5 @@
test_package.ByteEnum d;
test_package.ByteEnum[] e;
@nullable test_package.Bar f;
+ const String S1 = "a string constant";
}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/test_native_aidl_client.cpp b/tests/tests/binder_ndk/libbinder_ndk_test/test_native_aidl_client.cpp
index 85e5599..42e30a6 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/test_native_aidl_client.cpp
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/test_native_aidl_client.cpp
@@ -545,6 +545,8 @@
foo.shouldContainTwoIntFoos = {IntEnum::FOO, IntEnum::FOO};
foo.shouldContainTwoLongFoos = {LongEnum::FOO, LongEnum::FOO};
foo.u = SimpleUnion::make<SimpleUnion::c>("hello");
+ foo.shouldSetBit0AndBit2 = Foo::BIT0 | Foo::BIT2;
+ foo.shouldBeConstS1 = SimpleUnion::S1;
Foo retFoo;
@@ -561,6 +563,8 @@
EXPECT_EQ(foo.shouldContainTwoIntFoos, retFoo.shouldContainTwoIntFoos);
EXPECT_EQ(foo.shouldContainTwoLongFoos, retFoo.shouldContainTwoLongFoos);
EXPECT_EQ(foo.u, retFoo.u);
+ EXPECT_EQ(foo.shouldSetBit0AndBit2, retFoo.shouldSetBit0AndBit2);
+ EXPECT_EQ(foo.shouldBeConstS1, retFoo.shouldBeConstS1);
}
TEST_P(NdkBinderTest_Aidl, RepeatGenericBar) {
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/test_package/Foo.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/test_package/Foo.aidl
index 9ecb97e..6973e6d 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/test_package/Foo.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/test_package/Foo.aidl
@@ -21,4 +21,11 @@
LongEnum[] shouldContainTwoLongFoos;
@nullable String[] g;
@nullable SimpleUnion u;
+
+ const int BIT0 = 0x1;
+ const int BIT1 = 0x1 << 1;
+ const int BIT2 = 0x1 << 2;
+ int shouldSetBit0AndBit2;
+
+ @nullable SimpleUnion shouldBeConstS1;
}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/test_package/SimpleUnion.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/test_package/SimpleUnion.aidl
index 5fa906a..d92f025 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/test_package/SimpleUnion.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/test_package/SimpleUnion.aidl
@@ -10,4 +10,6 @@
ByteEnum d;
ByteEnum[] e;
@nullable Bar f;
+
+ const String S1 = "a string constant";
}
diff --git a/tests/tests/binder_ndk/src/android/binder/cts/JavaClientTest.java b/tests/tests/binder_ndk/src/android/binder/cts/JavaClientTest.java
index 79f231a..2d57d82 100644
--- a/tests/tests/binder_ndk/src/android/binder/cts/JavaClientTest.java
+++ b/tests/tests/binder_ndk/src/android/binder/cts/JavaClientTest.java
@@ -607,6 +607,9 @@
foo.u = SimpleUnion.e(new byte[]{ByteEnum.FOO, ByteEnum.FOO});
+ foo.shouldSetBit0AndBit2 = Foo.BIT0 | Foo.BIT2;
+ foo.shouldBeConstS1 = SimpleUnion.c(SimpleUnion.S1);
+
Foo repeatedFoo = mInterface.repeatFoo(foo);
assertEquals(foo.a, repeatedFoo.a);
@@ -620,6 +623,8 @@
Assert.assertArrayEquals(foo.shouldContainTwoIntFoos, repeatedFoo.shouldContainTwoIntFoos);
Assert.assertArrayEquals(foo.shouldContainTwoLongFoos, repeatedFoo.shouldContainTwoLongFoos);
Assert.assertArrayEquals(foo.u.getE(), repeatedFoo.u.getE());
+ assertEquals(foo.shouldSetBit0AndBit2, repeatedFoo.shouldSetBit0AndBit2);
+ assertEquals(foo.shouldBeConstS1.getC(), repeatedFoo.shouldBeConstS1.getC());
}
@Test
diff --git a/tests/tests/content/AndroidManifest.xml b/tests/tests/content/AndroidManifest.xml
index 2e67edf..3083940 100644
--- a/tests/tests/content/AndroidManifest.xml
+++ b/tests/tests/content/AndroidManifest.xml
@@ -540,9 +540,6 @@
</intent-filter>
</activity>
- <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
- android:exported="true" />
-
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/content/pm/SecureFrp/AndroidManifest.xml b/tests/tests/content/pm/SecureFrp/AndroidManifest.xml
index 9e45d4a..309f4a1 100644
--- a/tests/tests/content/pm/SecureFrp/AndroidManifest.xml
+++ b/tests/tests/content/pm/SecureFrp/AndroidManifest.xml
@@ -21,8 +21,6 @@
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application>
- <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
- android:exported="true" />
<uses-library android:name="android.test.runner" />
</application>
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
index 91ec1a4..cc867e0 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
@@ -73,6 +73,7 @@
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.os.Bundle;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.platform.test.annotations.AppModeFull;
@@ -932,7 +933,7 @@
}
@Test
- public void testSetSystemAppHiddenUntilInstalled() {
+ public void testSetSystemAppHiddenUntilInstalled() throws Exception {
String packageToManipulate = "com.android.cts.ctsshim";
try {
mPackageManager.getPackageInfo(packageToManipulate, MATCH_SYSTEM_ONLY);
@@ -949,11 +950,25 @@
mPackageManager.setSystemAppState(packageToManipulate,
PackageManager.SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN));
- try {
- mPackageManager.getPackageInfo(packageToManipulate, MATCH_SYSTEM_ONLY);
+ // Setting the state to SYSTEM_APP_STATE_UNINSTALLED is an async operation in
+ // PackageManagerService with no way to listen for completion, so poll until the
+ // app is no longer found.
+ int pollingPeriodMs = 100;
+ int timeoutMs = 1000;
+ long startTimeMs = SystemClock.elapsedRealtime();
+ boolean isAppStillVisible = true;
+ while (SystemClock.elapsedRealtime() < startTimeMs + timeoutMs) {
+ try {
+ mPackageManager.getPackageInfo(packageToManipulate, MATCH_SYSTEM_ONLY);
+ } catch (NameNotFoundException e) {
+ // expected, stop polling
+ isAppStillVisible = false;
+ break;
+ }
+ Thread.sleep(pollingPeriodMs);
+ }
+ if (isAppStillVisible) {
fail(packageToManipulate + " should not be found via getPackageInfo.");
- } catch (NameNotFoundException e) {
- // expected
}
} finally {
SystemUtil.runWithShellPermissionIdentity(() ->
diff --git a/tests/tests/graphics/src/android/graphics/cts/FrameRateCtsActivity.java b/tests/tests/graphics/src/android/graphics/cts/FrameRateCtsActivity.java
index 8b51f21..daeb4a4 100644
--- a/tests/tests/graphics/src/android/graphics/cts/FrameRateCtsActivity.java
+++ b/tests/tests/graphics/src/android/graphics/cts/FrameRateCtsActivity.java
@@ -42,6 +42,7 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -345,7 +346,7 @@
super.onCreate(savedInstanceState);
synchronized (mLock) {
mDisplayManager = (DisplayManager) getSystemService(DISPLAY_SERVICE);
- Display.Mode mode = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getMode();
+ Display.Mode mode = getDisplay().getMode();
mDeviceFrameRate = mode.getRefreshRate();
// Insert the initial mode so we have the full display mode history.
mModeChangedEvents.add(mode);
@@ -516,7 +517,7 @@
}
// Returns true if we reached waitUntilNanos, false if some other event occurred.
- private boolean waitForEvents(long waitUntilNanos, ArrayList<TestSurface> surfaces)
+ private boolean waitForEvents(long waitUntilNanos, List<TestSurface> surfaces)
throws InterruptedException {
int numModeChangedEvents = mModeChangedEvents.size();
long nowNanos = System.nanoTime();
@@ -554,7 +555,7 @@
// Set expectedFrameRate to 0.0 to verify only stable frame rate.
private void verifyCompatibleAndStableFrameRate(float expectedFrameRate,
- ArrayList<TestSurface> surfaces) throws InterruptedException {
+ List<TestSurface> surfaces) throws InterruptedException {
Log.i(TAG, "Verifying compatible and stable frame rate");
long nowNanos = System.nanoTime();
long gracePeriodEndTimeNanos =
@@ -616,6 +617,10 @@
void run(Api api) throws InterruptedException;
}
+ private interface OneSurfaceTestInterface {
+ void run(TestSurface surface) throws InterruptedException;
+ }
+
// Runs the given test for each api, waiting for the preconditions to be satisfied before
// running the test. Includes retry logic when the test fails because the preconditions are
// violated. E.g. if we lose the SurfaceHolder's surface, or the activity is paused/resumed,
@@ -690,17 +695,10 @@
private void testExactFrameRateMatch(Api api, boolean shouldBeSeamless)
throws InterruptedException {
- Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
- Display.Mode currentMode = display.getMode();
- float initialRefreshRate = currentMode.getRefreshRate();
-
- TestSurface surface = null;
- try {
- surface = new TestSurface(api, mSurfaceView.getSurfaceControl(), mSurface,
- "testSurface", mSurfaceView.getHolder().getSurfaceFrame(),
- /*visible=*/true, Color.RED);
- ArrayList<TestSurface> surfaces = new ArrayList<>();
- surfaces.add(surface);
+ runOneSurfaceTest(api, (TestSurface surface) -> {
+ Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ Display.Mode currentMode = display.getMode();
+ float initialRefreshRate = currentMode.getRefreshRate();
if (shouldBeSeamless) {
// Seamless rates should be seamlessly achieved with no resolution changes.
@@ -710,7 +708,7 @@
int initialNumEvents = mModeChangedEvents.size();
surface.setFrameRate(frameRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT,
/*shouldBeSeamless*/ true);
- verifyCompatibleAndStableFrameRate(frameRate, surfaces);
+ verifyCompatibleAndStableFrameRate(frameRate, Arrays.asList(surface));
verifyModeSwitchesAreSeamless(initialNumEvents, mModeChangedEvents.size());
verifyModeSwitchesDontChangeResolution(initialNumEvents,
mModeChangedEvents.size());
@@ -719,7 +717,7 @@
surface.setFrameRate(0.f, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT,
/*shouldBeSeamless*/ true);
// Wait for potential mode switches
- verifyCompatibleAndStableFrameRate(initialRefreshRate, surfaces);
+ verifyCompatibleAndStableFrameRate(initialRefreshRate, Arrays.asList(surface));
currentMode = display.getMode();
// Seamed rates should never generate a seamed switch.
@@ -739,18 +737,12 @@
int initialNumEvents = mModeChangedEvents.size();
surface.setFrameRate(frameRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT,
/*shouldBeSeamless*/ false);
- verifyCompatibleAndStableFrameRate(frameRate, surfaces);
+ verifyCompatibleAndStableFrameRate(frameRate, Arrays.asList(surface));
verifyModeSwitchesDontChangeResolution(initialNumEvents,
mModeChangedEvents.size());
}
}
- } finally {
- if (surface != null) {
- surface.setFrameRate(0.f, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT,
- /*shouldBeSeamless*/ false);
- surface.release();
- }
- }
+ });
}
private String modeSwitchesToString(int fromId, int toId) {
@@ -765,7 +757,7 @@
}
private void testFixedSource(Api api, boolean shouldBeSeamless) throws InterruptedException {
- Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ Display display = getDisplay();
float[] incompatibleFrameRates = getIncompatibleFrameRates(display);
if (incompatibleFrameRates == null) {
Log.i(TAG, "No incompatible frame rates to use for testing fixed_source behavior");
@@ -868,6 +860,133 @@
runTestsWithPreconditions(api -> testInvalidParams(api), "invalid params behavior");
}
+ private void runOneSurfaceTest(Api api, OneSurfaceTestInterface test)
+ throws InterruptedException {
+ TestSurface surface = null;
+ try {
+ surface = new TestSurface(api, mSurfaceView.getSurfaceControl(), mSurface,
+ "testSurface", mSurfaceView.getHolder().getSurfaceFrame(),
+ /*visible=*/true, Color.RED);
+
+ ArrayList<TestSurface> surfaces = new ArrayList<>();
+ surfaces.add(surface);
+
+ test.run(surface);
+ } finally {
+ if (surface != null) {
+ surface.release();
+ }
+ }
+ }
+
+ private void testSwitching(TestSurface surface, List<Float> frameRates, boolean expectSwitch,
+ boolean shouldBeSeamless) throws InterruptedException {
+ for (float frameRate : frameRates) {
+ int initialNumEvents = mModeChangedEvents.size();
+ surface.setFrameRate(frameRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT,
+ shouldBeSeamless);
+
+ if (expectSwitch) {
+ verifyCompatibleAndStableFrameRate(frameRate, Arrays.asList(surface));
+ }
+ if (shouldBeSeamless) {
+ verifyModeSwitchesAreSeamless(initialNumEvents, mModeChangedEvents.size());
+ }
+ verifyModeSwitchesDontChangeResolution(initialNumEvents,
+ mModeChangedEvents.size());
+ }
+ }
+
+ private void testMatchContentFramerate_None(Api api) throws InterruptedException {
+ runOneSurfaceTest(api, (TestSurface surface) -> {
+ Display display = getDisplay();
+ Display.Mode currentMode = display.getMode();
+ List<Float> frameRates = getRefreshRates(currentMode, display);
+
+ for (float frameRate : frameRates) {
+ int initialNumEvents = mModeChangedEvents.size();
+ surface.setFrameRate(frameRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT,
+ /*shouldBeSeamless*/ false);
+
+ assertTrue("Mode switches are not expected but these were detected "
+ + modeSwitchesToString(initialNumEvents, mModeChangedEvents.size()),
+ mModeChangedEvents.size() == initialNumEvents);
+ }
+ });
+ }
+
+ public void testMatchContentFramerate_None() throws InterruptedException {
+ runTestsWithPreconditions(api -> testMatchContentFramerate_None(api),
+ "testMatchContentFramerate_None");
+ }
+
+ private void testMatchContentFramerate_Auto(Api api)
+ throws InterruptedException {
+ runOneSurfaceTest(api, (TestSurface surface) -> {
+ Display display = getDisplay();
+ Display.Mode currentMode = display.getMode();
+ float initialRefreshRate = currentMode.getRefreshRate();
+ List<Float> frameRatesToTest = Floats.asList(currentMode.getAlternativeRefreshRates());
+
+ for (float frameRate : frameRatesToTest) {
+ int initialNumEvents = mModeChangedEvents.size();
+ surface.setFrameRate(frameRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT,
+ /*shouldBeSeamless*/ false);
+
+ verifyCompatibleAndStableFrameRate(frameRate, Arrays.asList(surface));
+ verifyModeSwitchesDontChangeResolution(initialNumEvents,
+ mModeChangedEvents.size());
+ }
+
+ // Reset to default
+ surface.setFrameRate(0.f, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT,
+ /*shouldBeSeamless*/ false);
+
+ // Wait for potential mode switches.
+ verifyCompatibleAndStableFrameRate(initialRefreshRate, Arrays.asList(surface));
+
+ currentMode = display.getMode();
+ List<Float> seamedRefreshRates = getSeamedRefreshRates(currentMode, display);
+
+ for (float frameRate : seamedRefreshRates) {
+ int initialNumEvents = mModeChangedEvents.size();
+ surface.setFrameRate(frameRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT,
+ /*shouldBeSeamless*/ false);
+
+ // Mode switches may have occurred, make sure they were all seamless.
+ verifyModeSwitchesAreSeamless(initialNumEvents, mModeChangedEvents.size());
+ verifyModeSwitchesDontChangeResolution(initialNumEvents,
+ mModeChangedEvents.size());
+ }
+ });
+ }
+
+ public void testMatchContentFramerate_Auto() throws InterruptedException {
+ runTestsWithPreconditions(api -> testMatchContentFramerate_Auto(api),
+ "testMatchContentFramerate_Auto");
+ }
+
+ private void testMatchContentFramerate_Always(Api api) throws InterruptedException {
+ runOneSurfaceTest(api, (TestSurface surface) -> {
+ Display display = getDisplay();
+ List<Float> frameRates = getRefreshRates(display.getMode(), display);
+ for (float frameRate : frameRates) {
+ int initialNumEvents = mModeChangedEvents.size();
+ surface.setFrameRate(frameRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT,
+ /*shouldBeSeamless*/ false);
+
+ verifyCompatibleAndStableFrameRate(frameRate, Arrays.asList(surface));
+ verifyModeSwitchesDontChangeResolution(initialNumEvents,
+ mModeChangedEvents.size());
+ }
+ });
+ }
+
+ public void testMatchContentFramerate_Always() throws InterruptedException {
+ runTestsWithPreconditions(api -> testMatchContentFramerate_Always(api),
+ "testMatchContentFramerate_Always");
+ }
+
private static native int nativeWindowSetFrameRate(
Surface surface, float frameRate, int compatibility, boolean shouldBeSeamless);
private static native long nativeSurfaceControlCreate(
diff --git a/tests/tests/graphics/src/android/graphics/cts/MatchContentFrameRateTest.java b/tests/tests/graphics/src/android/graphics/cts/MatchContentFrameRateTest.java
new file mode 100644
index 0000000..25954d8
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/cts/MatchContentFrameRateTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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 androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertEquals;
+
+import android.Manifest;
+import android.hardware.display.DisplayManager;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.AdoptShellPermissionsRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class MatchContentFrameRateTest {
+ private static final int SETTING_PROPAGATION_TIMEOUT_MILLIS = 50;
+
+ @Rule
+ public ActivityTestRule<FrameRateCtsActivity> mActivityRule =
+ new ActivityTestRule<>(FrameRateCtsActivity.class);
+
+ @Rule
+ public final AdoptShellPermissionsRule mShellPermissionsRule =
+ new AdoptShellPermissionsRule(getInstrumentation().getUiAutomation(),
+ Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS,
+ Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE);
+
+ private int mInitialMatchContentFrameRate;
+ private DisplayManager mDisplayManager;
+
+ @Before
+ public void setUp() throws Exception {
+ FrameRateCtsActivity activity = mActivityRule.getActivity();
+
+ // Prevent DisplayManager from limiting the allowed refresh rate range based on
+ // non-app policies (e.g. low battery, user settings, etc).
+ mDisplayManager = activity.getSystemService(DisplayManager.class);
+ mDisplayManager.setShouldAlwaysRespectAppRequestedMode(true);
+
+ mInitialMatchContentFrameRate = mDisplayManager.getRefreshRateSwitchingType();
+ }
+
+ @After
+ public void tearDown() {
+ mDisplayManager.setRefreshRateSwitchingType(mInitialMatchContentFrameRate);
+ mDisplayManager.setShouldAlwaysRespectAppRequestedMode(false);
+ }
+
+ @Test
+ public void testMatchContentFramerate_None() throws InterruptedException {
+ mDisplayManager.setRefreshRateSwitchingType(DisplayManager.SWITCHING_TYPE_NONE);
+ assertEquals(DisplayManager.SWITCHING_TYPE_NONE,
+ mDisplayManager.getRefreshRateSwitchingType());
+
+ FrameRateCtsActivity activity = mActivityRule.getActivity();
+ activity.testMatchContentFramerate_None();
+ }
+
+ @Test
+ public void testMatchContentFramerate_Auto() throws InterruptedException {
+ mDisplayManager.setRefreshRateSwitchingType(DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS);
+ assertEquals(DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS,
+ mDisplayManager.getRefreshRateSwitchingType());
+
+ FrameRateCtsActivity activity = mActivityRule.getActivity();
+ activity.testMatchContentFramerate_Auto();
+ }
+
+ @Test
+ public void testMatchContentFramerate_Always() throws InterruptedException {
+ mDisplayManager.setRefreshRateSwitchingType(
+ DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS);
+ assertEquals(DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS,
+ mDisplayManager.getRefreshRateSwitchingType());
+ FrameRateCtsActivity activity = mActivityRule.getActivity();
+ activity.testMatchContentFramerate_Always();
+ }
+}
diff --git a/tests/tests/hardware/res/raw/google_gamepad_keyevent_media_tests.json b/tests/tests/hardware/res/raw/google_gamepad_keyevent_media_tests.json
new file mode 100644
index 0000000..37b7fb2
--- /dev/null
+++ b/tests/tests/hardware/res/raw/google_gamepad_keyevent_media_tests.json
@@ -0,0 +1,15 @@
+[
+ {
+ "name": "Press BUTTON Play/Pause",
+ "reports": [
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
+ [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "MEDIA_PLAY_PAUSE"},
+ {"action": "UP", "keycode": "MEDIA_PLAY_PAUSE"}
+ ]
+ }
+
+]
diff --git a/tests/tests/hardware/res/raw/google_gamepad_keyeventtests.json b/tests/tests/hardware/res/raw/google_gamepad_keyevent_volume_tests.json
similarity index 66%
rename from tests/tests/hardware/res/raw/google_gamepad_keyeventtests.json
rename to tests/tests/hardware/res/raw/google_gamepad_keyevent_volume_tests.json
index 0ca751d..70f7f24 100644
--- a/tests/tests/hardware/res/raw/google_gamepad_keyeventtests.json
+++ b/tests/tests/hardware/res/raw/google_gamepad_keyevent_volume_tests.json
@@ -1,18 +1,5 @@
[
{
- "name": "Press BUTTON Play/Pause",
- "reports": [
- [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
- [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- ],
- "source": "KEYBOARD | GAMEPAD",
- "events": [
- {"action": "DOWN", "keycode": "MEDIA_PLAY_PAUSE"},
- {"action": "UP", "keycode": "MEDIA_PLAY_PAUSE"}
- ]
- },
-
- {
"name": "Press BUTTON VOLUME_UP",
"reports": [
[0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/UsbVoiceCommandTest.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/UsbVoiceCommandTest.java
index dfb34bf..889e415 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/UsbVoiceCommandTest.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/UsbVoiceCommandTest.java
@@ -18,6 +18,8 @@
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+import static org.junit.Assume.assumeFalse;
+
import android.app.UiAutomation;
import android.content.ComponentName;
import android.content.Intent;
@@ -115,8 +117,18 @@
}
@Test
- public void testAllKeys() {
- testInputEvents(R.raw.google_gamepad_keyeventtests);
+ public void testMediaKeys() {
+ testInputEvents(R.raw.google_gamepad_keyevent_media_tests);
+ }
+
+ @Test
+ public void testVolumeKeys() {
+ // {@link PhoneWindowManager} in interceptKeyBeforeDispatching, on TVs platform,
+ // volume keys never go to the foreground app.
+ // Skip the key test for TV platform.
+ assumeFalse("TV platform doesn't send volume keys to app, test should be skipped",
+ mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK));
+ testInputEvents(R.raw.google_gamepad_keyevent_volume_tests);
}
/**
diff --git a/tests/tests/media/src/android/media/cts/MediaControllerTest.java b/tests/tests/media/src/android/media/cts/MediaControllerTest.java
index f65f9ab..eab4b09 100644
--- a/tests/tests/media/src/android/media/cts/MediaControllerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaControllerTest.java
@@ -18,8 +18,9 @@
import static android.media.cts.Utils.compareRemoteUserInfo;
import static android.media.session.PlaybackState.STATE_PLAYING;
+import static org.junit.Assert.assertNotEquals;
+
import android.content.Intent;
-import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.Rating;
import android.media.VolumeProvider;
@@ -31,6 +32,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
import android.os.ResultReceiver;
@@ -130,12 +132,26 @@
assertEquals(Rating.RATING_5_STARS, mController.getRatingType());
}
- public void testGetSessionToken() throws Exception {
+ public void testGetSessionToken() {
assertEquals(mSession.getSessionToken(), mController.getSessionToken());
+ }
+ public void testGetSessionInfo() {
Bundle sessionInfo = mController.getSessionInfo();
assertNotNull(sessionInfo);
assertEquals(EXTRAS_VALUE, sessionInfo.getString(EXTRAS_KEY));
+
+ Bundle cachedSessionInfo = mController.getSessionInfo();
+ assertEquals(EXTRAS_VALUE, cachedSessionInfo.getString(EXTRAS_KEY));
+ }
+
+ public void testGetSessionInfoReturnsAnEmptyBundleWhenNotSet() {
+ MediaSession session = new MediaSession(getContext(), "test_tag", /*sessionInfo=*/ null);
+ try {
+ assertTrue(session.getController().getSessionInfo().isEmpty());
+ } finally {
+ session.release();
+ }
}
public void testGetTag() {
@@ -158,6 +174,25 @@
}
}
+ public void testSendCommandWithIllegalArgumentsThrowsIAE() {
+ Bundle args = new Bundle();
+ ResultReceiver resultReceiver = new ResultReceiver(mHandler);
+
+ try {
+ mController.sendCommand(/*command=*/ null, args, resultReceiver);
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+
+ try {
+ mController.sendCommand(/*command=*/ "", args, resultReceiver);
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+ }
+
public void testSetPlaybackSpeed() throws Exception {
synchronized (mWaitLock) {
mCallback.reset();
@@ -415,6 +450,185 @@
}
}
+ public void testRegisterCallbackWithNullThrowsIAE() {
+ try {
+ mController.registerCallback(/*handler=*/ null);
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+
+ try {
+ mController.registerCallback(/*handler=*/ null, mHandler);
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+ }
+
+ public void testRegisteringSameCallbackWithDifferentHandlerHasNoEffect() {
+ MediaController.Callback callback = new MediaController.Callback() {};
+ mController.registerCallback(callback, mHandler);
+
+ Handler initialHandler = mController.getHandlerForCallback(callback);
+ assertEquals(mHandler.getLooper(), initialHandler.getLooper());
+
+ // Create a separate handler with a new looper.
+ HandlerThread handlerThread = new HandlerThread("Test thread");
+ handlerThread.start();
+
+ // This call should not change the handler which is previously set.
+ mController.registerCallback(callback, new Handler(handlerThread.getLooper()));
+ Handler currentHandlerInController = mController.getHandlerForCallback(callback);
+
+ // The handler should not have been replaced.
+ assertEquals(initialHandler, currentHandlerInController);
+ assertNotEquals(handlerThread.getLooper(), currentHandlerInController.getLooper());
+
+ handlerThread.quitSafely();
+ }
+
+ public void testUnregisterCallbackWithNull() {
+ try {
+ mController.unregisterCallback(/*handler=*/ null);
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+ }
+
+ public void testUnregisterCallbackShouldRemoveCallback() {
+ MediaController.Callback callback = new MediaController.Callback() {};
+ mController.registerCallback(callback, mHandler);
+ assertEquals(mHandler.getLooper(), mController.getHandlerForCallback(callback).getLooper());
+
+ mController.unregisterCallback(callback);
+ assertNull(mController.getHandlerForCallback(callback));
+ }
+
+ public void testDispatchMediaButtonEventWithNullKeyEvent() {
+ try {
+ mController.dispatchMediaButtonEvent(/*keyEvent=*/ null);
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+ }
+
+ public void testDispatchMediaButtonEventWithNonMediaKeyEventReturnsFalse() {
+ KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_CAPS_LOCK);
+ assertFalse(mController.dispatchMediaButtonEvent(keyEvent));
+ }
+
+ public void testPlaybackInfoCreatorNewArray() {
+ final int arrayLength = 5;
+ MediaController.PlaybackInfo[] playbackInfoArrayInitializedWithNulls
+ = MediaController.PlaybackInfo.CREATOR.newArray(arrayLength);
+ assertNotNull(playbackInfoArrayInitializedWithNulls);
+ assertEquals(arrayLength, playbackInfoArrayInitializedWithNulls.length);
+ for (MediaController.PlaybackInfo playbackInfo : playbackInfoArrayInitializedWithNulls) {
+ assertNull(playbackInfo);
+ }
+ }
+
+ public void testTransportControlsPlayAndPrepareFromMediaIdWithIllegalArgumentsThrowsIAE() {
+ MediaController.TransportControls transportControls = mController.getTransportControls();
+
+ try {
+ transportControls.playFromMediaId(/*mediaId=*/ null, /*extras=*/ new Bundle());
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+
+ try {
+ transportControls.playFromMediaId(/*mediaId=*/ "", /*extras=*/ new Bundle());
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+
+ try {
+ transportControls.prepareFromMediaId(/*mediaId=*/ null, /*extras=*/ new Bundle());
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+
+ try {
+ transportControls.prepareFromMediaId(/*mediaId=*/ "", /*extras=*/ new Bundle());
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+ }
+
+ public void testTransportControlsPlayAndPrepareFromUriWithIllegalArgumentsThrowsIAE() {
+ MediaController.TransportControls transportControls = mController.getTransportControls();
+
+ try {
+ transportControls.playFromUri(/*uri=*/ null, /*extras=*/ new Bundle());
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+
+ try {
+ transportControls.playFromUri(Uri.EMPTY, /*extras=*/ new Bundle());
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+
+ try {
+ transportControls.prepareFromUri(/*uri=*/ null, /*extras=*/ new Bundle());
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+
+ try {
+ transportControls.prepareFromUri(Uri.EMPTY, /*extras=*/ new Bundle());
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+ }
+
+ public void testTransportControlsPlayAndPrepareFromSearchWithNullDoesNotCrash() {
+ MediaController.TransportControls transportControls = mController.getTransportControls();
+
+ // These calls should not crash. Null is accepted on purpose.
+ transportControls.playFromSearch(/*query=*/ null, /*extras=*/ new Bundle());
+ transportControls.prepareFromSearch(/*query=*/ null, /*extras=*/ new Bundle());
+ }
+
+ public void testSendCustomActionWithIllegalArgumentsThrowsIAE() {
+ MediaController.TransportControls transportControls = mController.getTransportControls();
+
+ try {
+ transportControls.sendCustomAction((PlaybackState.CustomAction) null,
+ /*args=*/ new Bundle());
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+
+ try {
+ transportControls.sendCustomAction(/*action=*/ (String) null, /*args=*/ new Bundle());
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+
+ try {
+ transportControls.sendCustomAction(/*action=*/ "", /*args=*/ new Bundle());
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+ }
+
private class MediaSessionCallback extends MediaSession.Callback {
private long mSeekPosition;
private long mQueueItemId;
diff --git a/tests/tests/media/src/android/media/cts/MediaSessionTest.java b/tests/tests/media/src/android/media/cts/MediaSessionTest.java
index f76038f..55f0558 100644
--- a/tests/tests/media/src/android/media/cts/MediaSessionTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaSessionTest.java
@@ -937,6 +937,7 @@
MediaController.PlaybackInfo info = controller.getPlaybackInfo();
assertNotNull(info);
+ info.toString(); // Test that calling PlaybackInfo.toString() does not crash.
assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL, info.getPlaybackType());
AudioAttributes attrs = info.getAudioAttributes();
assertNotNull(attrs);
diff --git a/tests/tests/mediatranscoding/assets/ConflictSupportedValue.xml b/tests/tests/mediatranscoding/assets/ConflictSupportedValue.xml
new file mode 100644
index 0000000..9b2fa3b
--- /dev/null
+++ b/tests/tests/mediatranscoding/assets/ConflictSupportedValue.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+ <format android:name="HEVC" supported="true"/>
+ <format android:name="HEVC" supported="false"/>
+</media-capabilities>
diff --git a/tests/tests/mediatranscoding/assets/EmptyFormat.xml b/tests/tests/mediatranscoding/assets/EmptyFormat.xml
new file mode 100644
index 0000000..5ef5e51
--- /dev/null
+++ b/tests/tests/mediatranscoding/assets/EmptyFormat.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+ <format />
+</media-capabilities>
diff --git a/tests/tests/mediatranscoding/assets/FormatWithoutSupported.xml b/tests/tests/mediatranscoding/assets/FormatWithoutSupported.xml
new file mode 100644
index 0000000..e50c212
--- /dev/null
+++ b/tests/tests/mediatranscoding/assets/FormatWithoutSupported.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+ <format android:name="HEVC"/>
+</media-capabilities>
diff --git a/tests/tests/mediatranscoding/assets/MediaCapabilities.xml b/tests/tests/mediatranscoding/assets/MediaCapabilities.xml
new file mode 100644
index 0000000..3bff61e
--- /dev/null
+++ b/tests/tests/mediatranscoding/assets/MediaCapabilities.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+ <format android:name="HEVC" supported="true"/>
+ <format android:name="HDR10" supported="false"/>
+ <format android:name="SlowMotion" supported="false"/>
+</media-capabilities>
diff --git a/tests/tests/mediatranscoding/assets/SupportAllHdr.xml b/tests/tests/mediatranscoding/assets/SupportAllHdr.xml
new file mode 100644
index 0000000..8c331a0
--- /dev/null
+++ b/tests/tests/mediatranscoding/assets/SupportAllHdr.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+ <format android:name="HEVC" supported="true"/>
+ <format android:name="HDR10" supported="true"/>
+ <format android:name="HDR10Plus" supported="true"/>
+ <format android:name="Dolby-Vision" supported="true"/>
+ <format android:name="HLG" supported="true"/>
+</media-capabilities>
diff --git a/tests/tests/mediatranscoding/assets/SupportHdrWithoutHevc.xml b/tests/tests/mediatranscoding/assets/SupportHdrWithoutHevc.xml
new file mode 100644
index 0000000..309aa1d
--- /dev/null
+++ b/tests/tests/mediatranscoding/assets/SupportHdrWithoutHevc.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+ <format android:name="HDR10" supported="true"/>
+ <format android:name="SlowMotion" supported="false"/>
+</media-capabilities>
diff --git a/tests/tests/mediatranscoding/assets/SupportedWithoutFormat.xml b/tests/tests/mediatranscoding/assets/SupportedWithoutFormat.xml
new file mode 100644
index 0000000..29454fc
--- /dev/null
+++ b/tests/tests/mediatranscoding/assets/SupportedWithoutFormat.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+ <format supported="true"/>
+</media-capabilities>
diff --git a/tests/tests/mediatranscoding/assets/WrongBooleanValue.xml b/tests/tests/mediatranscoding/assets/WrongBooleanValue.xml
new file mode 100644
index 0000000..62de6cd
--- /dev/null
+++ b/tests/tests/mediatranscoding/assets/WrongBooleanValue.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+ <format android:name="HEVC" supported="yes"/>
+ <format android:name="HDR10" supported="false"/>
+ <format android:name="SlowMotion" supported="false"/>
+</media-capabilities>
diff --git a/tests/tests/mediatranscoding/assets/WrongMediaCapabilityTag.xml b/tests/tests/mediatranscoding/assets/WrongMediaCapabilityTag.xml
new file mode 100644
index 0000000..5db42e5
--- /dev/null
+++ b/tests/tests/mediatranscoding/assets/WrongMediaCapabilityTag.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<media-capability xmlns:android="http://schemas.android.com/apk/res/android">
+ <format android:name="HEVC" supported="true"/>
+ <format android:name="HDR10" supported="true"/>
+ <format android:name="SlowMotion" supported="true"/>
+</media-capabilities>
diff --git a/tests/tests/mediatranscoding/assets/WrongMediaCapabilityTag2.xml b/tests/tests/mediatranscoding/assets/WrongMediaCapabilityTag2.xml
new file mode 100644
index 0000000..e924c48
--- /dev/null
+++ b/tests/tests/mediatranscoding/assets/WrongMediaCapabilityTag2.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<MediaCapability xmlns:android="http://schemas.android.com/apk/res/android">
+ <format android:name="HEVC" supported="true"/>
+ <format android:name="HDR10" supported="true"/>
+ <format android:name="SlowMotion" supported="true"/>
+</MediaCapability>
diff --git a/tests/tests/mediatranscoding/res/xml/mediacapabilities.xml b/tests/tests/mediatranscoding/res/xml/mediacapabilities.xml
new file mode 100644
index 0000000..3bff61e
--- /dev/null
+++ b/tests/tests/mediatranscoding/res/xml/mediacapabilities.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+ <format android:name="HEVC" supported="true"/>
+ <format android:name="HDR10" supported="false"/>
+ <format android:name="SlowMotion" supported="false"/>
+</media-capabilities>
diff --git a/tests/tests/mediatranscoding/src/android/media/mediatranscoding/cts/ApplicationMediaCapabilitiesTest.java b/tests/tests/mediatranscoding/src/android/media/mediatranscoding/cts/ApplicationMediaCapabilitiesTest.java
new file mode 100644
index 0000000..dac13f9
--- /dev/null
+++ b/tests/tests/mediatranscoding/src/android/media/mediatranscoding/cts/ApplicationMediaCapabilitiesTest.java
@@ -0,0 +1,264 @@
+/*
+ * 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.media.mediatranscoding.cts;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.res.XmlResourceParser;
+import android.media.ApplicationMediaCapabilities;
+import android.media.MediaFeature;
+import android.media.MediaFormat;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresDevice;
+import android.test.AndroidTestCase;
+import android.util.Xml;
+
+import org.junit.Test;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+@Presubmit
+@AppModeFull(reason = "Instant apps cannot access the SD card")
+public class ApplicationMediaCapabilitiesTest extends AndroidTestCase {
+ private static final String TAG = "ApplicationMediaCapabilitiesTest";
+
+ public void testSetSupportHevc() throws Exception {
+ // Default HEVC support is false.
+ ApplicationMediaCapabilities capability =
+ new ApplicationMediaCapabilities.Builder().build();
+ assertFalse(capability.isVideoMimeTypeSupported(MediaFormat.MIMETYPE_VIDEO_HEVC));
+
+ ApplicationMediaCapabilities capability2 =
+ new ApplicationMediaCapabilities.Builder().addSupportedVideoMimeType(
+ MediaFormat.MIMETYPE_VIDEO_HEVC).build();
+ assertTrue(capability2.isVideoMimeTypeSupported(MediaFormat.MIMETYPE_VIDEO_HEVC));
+ }
+
+ public void testSetSupportHdr() throws Exception {
+ ApplicationMediaCapabilities capability =
+ new ApplicationMediaCapabilities.Builder().addSupportedHdrType(
+ MediaFeature.HdrType.HDR10_PLUS).addSupportedVideoMimeType(
+ MediaFormat.MIMETYPE_VIDEO_HEVC).build();
+ assertEquals(true, capability.isHdrTypeSupported(MediaFeature.HdrType.HDR10_PLUS));
+ }
+
+ // Test supports HDR without supporting hevc, expect exception.
+ public void testSupportHdrWithoutSupportHevc() throws Exception {
+ assertThrows(UnsupportedOperationException.class, () -> {
+ ApplicationMediaCapabilities capability =
+ new ApplicationMediaCapabilities.Builder().addSupportedHdrType(
+ MediaFeature.HdrType.HDR10_PLUS).build();
+ });
+ }
+
+ public void testSetSupportSlowMotion() throws Exception {
+ ApplicationMediaCapabilities capability =
+ new ApplicationMediaCapabilities.Builder().setSlowMotionSupported(true).build();
+ assertTrue(capability.isSlowMotionSupported());
+ }
+
+ // Test builder with all supports are set to true.
+ public void testBuilder() throws Exception {
+ ApplicationMediaCapabilities capability =
+ new ApplicationMediaCapabilities.Builder().addSupportedVideoMimeType(
+ MediaFormat.MIMETYPE_VIDEO_HEVC).addSupportedHdrType(
+ MediaFeature.HdrType.HDR10_PLUS).setSlowMotionSupported(true).build();
+ assertTrue(capability.isVideoMimeTypeSupported(MediaFormat.MIMETYPE_VIDEO_HEVC));
+ assertTrue(capability.isHdrTypeSupported(MediaFeature.HdrType.HDR10_PLUS));
+ assertTrue(capability.isSlowMotionSupported());
+ }
+
+ // Test read the application's xml from res/xml folder using the XmlResourceParser.
+ // <format android:name="HEVC" supported="true"/>
+ // <format android:name="HDR10" supported="false"/>
+ // <format android:name="SlowMotion" supported="false"/>
+ public void testReadMediaCapabilitiesXml() throws Exception {
+ XmlResourceParser parser = mContext.getResources().getXml(R.xml.mediacapabilities);
+ ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml(
+ parser);
+ assertFalse(capability.isHdrTypeSupported(MediaFeature.HdrType.HDR10));
+ assertFalse(capability.isSlowMotionSupported());
+ assertTrue(capability.isVideoMimeTypeSupported(MediaFormat.MIMETYPE_VIDEO_HEVC));
+ }
+
+ // Test read the xml from assets folder using the InputStream.
+ // <format android:name="HEVC" supported="true"/>
+ // <format android:name="HDR10" supported="false"/>
+ // <format android:name="SlowMotion" supported="false"/>
+ public void testReadFromCorrectXmlWithInputStreamInAssets() throws Exception {
+ InputStream xmlIs = mContext.getAssets().open("MediaCapabilities.xml");
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(xmlIs, StandardCharsets.UTF_8.name());
+
+ ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml(
+ parser);
+ assertFalse(capability.isHdrTypeSupported(MediaFeature.HdrType.HDR10));
+ assertFalse(capability.isSlowMotionSupported());
+ assertTrue(capability.isVideoMimeTypeSupported(MediaFormat.MIMETYPE_VIDEO_HEVC));
+ }
+
+ // Test read the application's xml from assets folder using the XmlResourceParser.
+ // <format android:name="HEVC" supported="true"/>
+ // <format android:name="HDR10" supported="true"/>
+ // <format android:name="HDR10Plus" supported="true"/>
+ // <format android:name="Dolby-Vision" supported="true"/>
+ // <format android:name="HLG" supported="true"/>
+ // <format android:name="SlowMotion" supported="true"/>
+ public void testReadMediaCapabilitiesXmlWithSupportAllHdr() throws Exception {
+ InputStream xmlIs = mContext.getAssets().open("SupportAllHdr.xml");
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(xmlIs, StandardCharsets.UTF_8.name());
+
+ ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml(
+ parser);
+ assertTrue(capability.isHdrTypeSupported(MediaFeature.HdrType.HDR10));
+ assertTrue(capability.isHdrTypeSupported(MediaFeature.HdrType.HDR10_PLUS));
+ assertTrue(capability.isHdrTypeSupported(MediaFeature.HdrType.DOLBY_VISION));
+ assertTrue(capability.isHdrTypeSupported(MediaFeature.HdrType.HLG));
+ assertTrue(capability.isVideoMimeTypeSupported(MediaFormat.MIMETYPE_VIDEO_HEVC));
+ }
+
+ // Test parsing invalid xml with wrong tag expect UnsupportedOperationException
+ // MediaCapability does not match MediaCapabilities at the end which will lead to
+ // exception with "Ill-formatted xml file"
+ // <MediaCapability xmlns:android="http://schemas.android.com/apk/res/android">
+ // <format android:name="HEVC" supported="true"/>
+ // <format android:name="HDR10" supported="true"/>
+ // <format android:name="SlowMotion" supported="true"/>
+ // </MediaCapabilities>
+ public void testReadFromWrongMediaCapabilityXml() throws Exception {
+ assertThrows(UnsupportedOperationException.class, () -> {
+ InputStream xmlIs = mContext.getAssets().open("WrongMediaCapabilityTag.xml");
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(xmlIs, StandardCharsets.UTF_8.name());
+ ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml(
+ parser);
+ });
+ }
+
+ // Test invalid xml with wrong tag expect UnsupportedOperationException
+ // MediaCapability is wrong tag.
+ // <MediaCapability xmlns:android="http://schemas.android.com/apk/res/android">
+ // <format android:name="HEVC" supported="true"/>
+ // <format android:name="HDR10" supported="true"/>
+ // <format android:name="SlowMotion" supported="true"/>
+ // </MediaCapability>
+ public void testReadFromWrongMediaCapabilityXml2() throws Exception {
+ assertThrows(UnsupportedOperationException.class, () -> {
+ InputStream xmlIs = mContext.getAssets().open("WrongMediaCapabilityTag2.xml");
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(xmlIs, StandardCharsets.UTF_8.name());
+ ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml(
+ parser);
+ });
+ }
+
+ // Test invalid attribute value of "support" with true->yes expect UnsupportedOperationException
+ // <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+ // <format android:name="HEVC" supported="yes"/>
+ // <format android:name="HDR10" supported="false"/>
+ // <format android:name="SlowMotion" supported="false"/>
+ // </media-capabilities>
+ public void testReadFromXmlWithWrongBoolean() throws Exception {
+ assertThrows(UnsupportedOperationException.class, () -> {
+ InputStream xmlIs = mContext.getAssets().open("WrongBooleanValue.xml");
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(xmlIs, StandardCharsets.UTF_8.name());
+ ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml(
+ parser);
+ });
+ }
+
+ // Test parsing capabilities that support HDR10 but not support HEVC.
+ // Expect UnsupportedOperationException
+ // <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+ // <format android:name="HDR10" supported="true"/>
+ // <format android:name="SlowMotion" supported="false"/>
+ // </media-capabilities>
+ public void testReadXmlSupportHdrWithoutSupportHevc() throws Exception {
+ assertThrows(UnsupportedOperationException.class, () -> {
+ InputStream xmlIs = mContext.getAssets().open("SupportHdrWithoutHevc.xml");
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(xmlIs, StandardCharsets.UTF_8.name());
+ ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml(
+ parser);
+ });
+ }
+
+ // Test parsing capabilities that has conflicted supported value.
+ // Expect UnsupportedOperationException
+ // <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+ // <format android:name="HEVC" supported="true"/>
+ // <format android:name="HEVC" supported="false"/>
+ // </media-capabilities>
+ public void testReadXmlConflictSupportedValue() throws Exception {
+ assertThrows(UnsupportedOperationException.class, () -> {
+ InputStream xmlIs = mContext.getAssets().open("ConflictSupportedValue.xml");
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(xmlIs, StandardCharsets.UTF_8.name());
+ ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml(
+ parser);
+ });
+ }
+
+ // Test parsing capabilities that has empty format.
+ // Expect UnsupportedOperationException
+ // <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+ // <format/>
+ // </media-capabilities>
+ public void testReadXmlWithEmptyFormat() throws Exception {
+ assertThrows(UnsupportedOperationException.class, () -> {
+ InputStream xmlIs = mContext.getAssets().open("EmptyFormat.xml");
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(xmlIs, StandardCharsets.UTF_8.name());
+ ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml(
+ parser);
+ });
+ }
+
+ // Test parsing capabilities that has empty format.
+ // Expect UnsupportedOperationException
+ // <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+ // <format android:name="HEVC"/>
+ // </media-capabilities>
+ public void testReadXmlFormatWithoutSupported() throws Exception {
+ assertThrows(UnsupportedOperationException.class, () -> {
+ InputStream xmlIs = mContext.getAssets().open("FormatWithoutSupported.xml");
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(xmlIs, StandardCharsets.UTF_8.name());
+ ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml(
+ parser);
+ });
+ }
+
+ // Test parsing capabilities that has supported without the format name.
+ // Expect UnsupportedOperationException
+ // <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+ // <format supported="true"/>
+ // </media-capabilities>
+ public void testReadXmlSupportedWithoutFormat() throws Exception {
+ assertThrows(UnsupportedOperationException.class, () -> {
+ InputStream xmlIs = mContext.getAssets().open("SupportedWithoutFormat.xml");
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(xmlIs, StandardCharsets.UTF_8.name());
+ ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml(
+ parser);
+ });
+ }
+}
diff --git a/tests/tests/mediatranscoding/src/android/media/mediatranscoding/cts/MediaTranscodeManagerTest.java b/tests/tests/mediatranscoding/src/android/media/mediatranscoding/cts/MediaTranscodeManagerTest.java
index a4e429a..07b5876 100644
--- a/tests/tests/mediatranscoding/src/android/media/mediatranscoding/cts/MediaTranscodeManagerTest.java
+++ b/tests/tests/mediatranscoding/src/android/media/mediatranscoding/cts/MediaTranscodeManagerTest.java
@@ -24,6 +24,7 @@
import android.media.ApplicationMediaCapabilities;
import android.media.MediaFormat;
import android.media.MediaTranscodeManager;
+import android.media.MediaTranscodeManager.TranscodingRequest;
import android.media.MediaTranscodeManager.TranscodingSession;
import android.net.Uri;
import android.os.Bundle;
@@ -148,8 +149,8 @@
*/
public void testCreateTranscodingRequestWithNullDestinationUri() throws Exception {
assertThrows(IllegalArgumentException.class, () -> {
- MediaTranscodeManager.TranscodingRequest request =
- new MediaTranscodeManager.TranscodingRequest.Builder()
+ TranscodingRequest request =
+ new TranscodingRequest.Builder()
.setSourceUri(mSourceHEVCVideoUri)
.setDestinationUri(null)
.setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
@@ -164,8 +165,8 @@
*/
public void testCreateTranscodingWithInvalidClientPid() throws Exception {
assertThrows(IllegalArgumentException.class, () -> {
- MediaTranscodeManager.TranscodingRequest request =
- new MediaTranscodeManager.TranscodingRequest.Builder()
+ TranscodingRequest request =
+ new TranscodingRequest.Builder()
.setSourceUri(mSourceHEVCVideoUri)
.setDestinationUri(mDestinationUri)
.setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
@@ -181,8 +182,8 @@
*/
public void testCreateTranscodingWithInvalidClientUid() throws Exception {
assertThrows(IllegalArgumentException.class, () -> {
- MediaTranscodeManager.TranscodingRequest request =
- new MediaTranscodeManager.TranscodingRequest.Builder()
+ TranscodingRequest request =
+ new TranscodingRequest.Builder()
.setSourceUri(mSourceHEVCVideoUri)
.setDestinationUri(mDestinationUri)
.setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
@@ -198,8 +199,8 @@
*/
public void testCreateTranscodingRequestWithNullSourceUri() throws Exception {
assertThrows(IllegalArgumentException.class, () -> {
- MediaTranscodeManager.TranscodingRequest request =
- new MediaTranscodeManager.TranscodingRequest.Builder()
+ TranscodingRequest request =
+ new TranscodingRequest.Builder()
.setSourceUri(null)
.setDestinationUri(mDestinationUri)
.setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
@@ -213,8 +214,8 @@
*/
public void testCreateTranscodingRequestWithoutSourceUri() throws Exception {
assertThrows(UnsupportedOperationException.class, () -> {
- MediaTranscodeManager.TranscodingRequest request =
- new MediaTranscodeManager.TranscodingRequest.Builder()
+ TranscodingRequest request =
+ new TranscodingRequest.Builder()
.setDestinationUri(mDestinationUri)
.setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
.setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
@@ -228,8 +229,8 @@
*/
public void testCreateTranscodingRequestWithoutDestinationUri() throws Exception {
assertThrows(UnsupportedOperationException.class, () -> {
- MediaTranscodeManager.TranscodingRequest request =
- new MediaTranscodeManager.TranscodingRequest.Builder()
+ TranscodingRequest request =
+ new TranscodingRequest.Builder()
.setSourceUri(mSourceHEVCVideoUri)
.setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
.setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
@@ -244,8 +245,8 @@
*/
public void testCreateTranscodingRequestWithoutVideoFormat() throws Exception {
assertThrows(UnsupportedOperationException.class, () -> {
- MediaTranscodeManager.TranscodingRequest request =
- new MediaTranscodeManager.TranscodingRequest.Builder()
+ TranscodingRequest request =
+ new TranscodingRequest.Builder()
.setSourceUri(mSourceHEVCVideoUri)
.setDestinationUri(mDestinationUri)
.setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
@@ -258,8 +259,8 @@
throws Exception {
Semaphore transcodeCompleteSemaphore = new Semaphore(0);
- MediaTranscodeManager.TranscodingRequest request =
- new MediaTranscodeManager.TranscodingRequest.Builder()
+ TranscodingRequest request =
+ new TranscodingRequest.Builder()
.setSourceUri(srcUri)
.setDestinationUri(dstUri)
.setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
@@ -268,7 +269,7 @@
.build();
Executor listenerExecutor = Executors.newSingleThreadExecutor();
- MediaTranscodeManager.TranscodingSession session = mMediaTranscodeManager.enqueueRequest(
+ TranscodingSession session = mMediaTranscodeManager.enqueueRequest(
request,
listenerExecutor,
transcodingSession -> {
@@ -287,7 +288,7 @@
}
File dstFile = new File(dstUri.getPath());;
- if (expectedResult == MediaTranscodeManager.TranscodingSession.RESULT_SUCCESS) {
+ if (expectedResult == TranscodingSession.RESULT_SUCCESS) {
// Checks the destination file get generated.
assertTrue("Failed to create destination file", dstFile.exists());
}
@@ -307,7 +308,7 @@
Log.d(TAG, "Transcoding " + invalidSrcUri + "to destination: " + destinationUri);
testTranscodingWithExpectResult(invalidSrcUri, destinationUri,
- MediaTranscodeManager.TranscodingSession.RESULT_ERROR);
+ TranscodingSession.RESULT_ERROR);
}
// Tests transcoding to a uri in res folder and expects failure as test could not write to res
@@ -319,7 +320,7 @@
Log.d(TAG, "Transcoding to destination: " + destinationUri);
testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri,
- MediaTranscodeManager.TranscodingSession.RESULT_ERROR);
+ TranscodingSession.RESULT_ERROR);
}
// Tests transcoding to a uri in internal cache folder and expects success.
@@ -330,7 +331,7 @@
Log.d(TAG, "Transcoding to cache: " + destinationUri);
testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri,
- MediaTranscodeManager.TranscodingSession.RESULT_SUCCESS);
+ TranscodingSession.RESULT_SUCCESS);
}
// Tests transcoding to a uri in internal files directory and expects success.
@@ -340,7 +341,7 @@
Log.i(TAG, "Transcoding to files dir: " + destinationUri);
testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri,
- MediaTranscodeManager.TranscodingSession.RESULT_SUCCESS);
+ TranscodingSession.RESULT_SUCCESS);
}
public void testTranscodingFromHevcToAvc() throws Exception {
@@ -353,8 +354,8 @@
ApplicationMediaCapabilities clientCaps =
new ApplicationMediaCapabilities.Builder().build();
- MediaTranscodeManager.TranscodingRequest.MediaFormatResolver
- resolver = new MediaTranscodeManager.TranscodingRequest.MediaFormatResolver()
+ TranscodingRequest.MediaFormatResolver
+ resolver = new TranscodingRequest.MediaFormatResolver()
.setSourceVideoFormatHint(MediaFormat.createVideoFormat(
MediaFormat.MIMETYPE_VIDEO_HEVC, WIDTH, HEIGHT))
.setClientCapabilities(clientCaps);
@@ -365,8 +366,8 @@
int pid = android.os.Process.myPid();
int uid = android.os.Process.myUid();
- MediaTranscodeManager.TranscodingRequest request =
- new MediaTranscodeManager.TranscodingRequest.Builder()
+ TranscodingRequest request =
+ new TranscodingRequest.Builder()
.setSourceUri(mSourceHEVCVideoUri)
.setDestinationUri(destinationUri)
.setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
@@ -381,7 +382,7 @@
Log.d(TAG, "transcoding to format: " + videoTrackFormat);
- MediaTranscodeManager.TranscodingSession session = mMediaTranscodeManager.enqueueRequest(
+ TranscodingSession session = mMediaTranscodeManager.enqueueRequest(
request,
listenerExecutor,
transcodingSession -> {
@@ -416,8 +417,8 @@
Uri destinationUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
+ mContext.getCacheDir().getAbsolutePath() + "/HevcTranscode.mp4");
- MediaTranscodeManager.TranscodingRequest request =
- new MediaTranscodeManager.TranscodingRequest.Builder()
+ TranscodingRequest request =
+ new TranscodingRequest.Builder()
.setSourceUri(mSourceHEVCVideoUri)
.setDestinationUri(destinationUri)
.setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
@@ -426,7 +427,7 @@
.build();
Executor listenerExecutor = Executors.newSingleThreadExecutor();
- MediaTranscodeManager.TranscodingSession session = mMediaTranscodeManager.enqueueRequest(
+ TranscodingSession session = mMediaTranscodeManager.enqueueRequest(
request,
listenerExecutor,
transcodingSession -> {
@@ -467,8 +468,8 @@
// Use init dameon's pid and uid.
int pid = 1;
int uid = 0;
- MediaTranscodeManager.TranscodingRequest request =
- new MediaTranscodeManager.TranscodingRequest.Builder()
+ TranscodingRequest request =
+ new TranscodingRequest.Builder()
.setSourceUri(mSourceHEVCVideoUri)
.setDestinationUri(mDestinationUri)
.setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
@@ -479,7 +480,7 @@
.build();
Executor listenerExecutor = Executors.newSingleThreadExecutor();
- MediaTranscodeManager.TranscodingSession session =
+ TranscodingSession session =
mMediaTranscodeManager.enqueueRequest(
request,
listenerExecutor,
@@ -489,5 +490,56 @@
});
}
- // TODO(hkuang): Add test for progress update.
+ public void testTranscodingProgressUpdate() throws Exception {
+ Log.d(TAG, "Starting: testTranscodingProgressUpdate");
+
+ Semaphore transcodeCompleteSemaphore = new Semaphore(0);
+
+ // Create a file Uri: file:///data/user/0/android.media.mediatranscoding.cts/cache/HevcTranscode.mp4
+ Uri destinationUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
+ + mContext.getCacheDir().getAbsolutePath() + "/HevcTranscode.mp4");
+
+ TranscodingRequest request =
+ new TranscodingRequest.Builder()
+ .setSourceUri(mSourceHEVCVideoUri)
+ .setDestinationUri(destinationUri)
+ .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+ .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
+ .setVideoTrackFormat(createMediaFormat())
+ .build();
+ Executor listenerExecutor = Executors.newSingleThreadExecutor();
+
+ TranscodingSession session = mMediaTranscodeManager.enqueueRequest(request,
+ listenerExecutor,
+ TranscodingSession -> {
+ Log.d(TAG,
+ "Transcoding completed with result: " + TranscodingSession.getResult());
+ assertEquals(TranscodingSession.RESULT_SUCCESS, TranscodingSession.getResult());
+ transcodeCompleteSemaphore.release();
+ });
+ assertNotNull(session);
+
+ AtomicInteger progressUpdateCount = new AtomicInteger(0);
+
+ // Set progress update executor and use the same executor as result listener.
+ session.setOnProgressUpdateListener(listenerExecutor,
+ new TranscodingSession.OnProgressUpdateListener() {
+ int mPreviousProgress = 0;
+
+ @Override
+ public void onProgressUpdate(TranscodingSession session, int newProgress) {
+ assertTrue("Invalid proress update", newProgress > mPreviousProgress);
+ assertTrue("Invalid proress update", newProgress <= 100);
+ mPreviousProgress = newProgress;
+ progressUpdateCount.getAndIncrement();
+ Log.i(TAG, "Get progress update " + newProgress);
+ }
+ });
+
+ boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
+ TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ assertTrue("Transcode failed to complete in time.", finishedOnTime);
+ assertTrue("Failed to receive at least 10 progress updates",
+ progressUpdateCount.get() > 10);
+ }
}
\ No newline at end of file
diff --git a/tests/tests/packageinstaller/atomicinstall/AndroidManifest.xml b/tests/tests/packageinstaller/atomicinstall/AndroidManifest.xml
index 1f8f283..457b7ef 100644
--- a/tests/tests/packageinstaller/atomicinstall/AndroidManifest.xml
+++ b/tests/tests/packageinstaller/atomicinstall/AndroidManifest.xml
@@ -18,8 +18,6 @@
package="com.android.tests.atomicinstall" >
<application>
- <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
- android:exported="true" />
<uses-library android:name="android.test.runner" />
</application>
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index 71bd0ee..9a175c3 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -3996,6 +3996,14 @@
<permission android:name="android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS"
android:protectionLevel="signature" />
+ <!-- Allows an application to override the display mode requests
+ so the app requested mode will be selected and user settings and display
+ policies will be ignored.
+ @hide
+ @TestApi -->
+ <permission android:name="android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows an application to control VPN.
<p>Not for use by third-party applications.</p>
@hide -->
diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml
index 340fb90..7e4402e 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -92,8 +92,6 @@
</intent-filter>
</activity>
- <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
- android:exported="true"/>
<receiver android:name="android.security.cts.PackageVerificationsBroadcastReceiver"
android:permission="android.permission.BIND_PACKAGE_VERIFIER"
android:exported="true">
diff --git a/tests/tests/tv/src/android/media/tv/cts/BundledTvInputServiceTest.java b/tests/tests/tv/src/android/media/tv/cts/BundledTvInputServiceTest.java
index a50531d..0df3e55 100644
--- a/tests/tests/tv/src/android/media/tv/cts/BundledTvInputServiceTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/BundledTvInputServiceTest.java
@@ -23,6 +23,7 @@
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager;
import android.media.tv.TvView;
+import android.platform.test.annotations.Presubmit;
import android.test.ActivityInstrumentationTestCase2;
import android.util.ArrayMap;
@@ -37,6 +38,7 @@
/**
* Test {@link android.media.tv.TvView}.
*/
+@Presubmit
public class BundledTvInputServiceTest
extends ActivityInstrumentationTestCase2<TvViewStubActivity> {
/** The maximum time to wait for an operation. */
diff --git a/tests/tests/tv/src/android/media/tv/cts/HardwareSessionTest.java b/tests/tests/tv/src/android/media/tv/cts/HardwareSessionTest.java
index c0b5e80..57fb3e7 100644
--- a/tests/tests/tv/src/android/media/tv/cts/HardwareSessionTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/HardwareSessionTest.java
@@ -26,6 +26,7 @@
import android.media.tv.TvView;
import android.media.tv.cts.HardwareSessionTest.HardwareProxyTvInputService.CountingSession;
import android.net.Uri;
+import android.platform.test.annotations.Presubmit;
import android.test.ActivityInstrumentationTestCase2;
import android.tv.cts.R;
@@ -38,6 +39,7 @@
/**
* Test {@link android.media.tv.TvInputService.HardwareSession}.
*/
+@Presubmit
public class HardwareSessionTest extends ActivityInstrumentationTestCase2<TvViewStubActivity> {
/** The maximum time to wait for an operation. */
private static final long TIME_OUT = 15000L;
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvContentRatingTest.java b/tests/tests/tv/src/android/media/tv/cts/TvContentRatingTest.java
index 9acff92..4c0bcc7 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvContentRatingTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvContentRatingTest.java
@@ -17,6 +17,7 @@
package android.media.tv.cts;
import android.media.tv.TvContentRating;
+import android.platform.test.annotations.Presubmit;
import java.util.List;
@@ -25,6 +26,7 @@
/**
* Test for {@link android.media.tv.TvContentRating}.
*/
+@Presubmit
public class TvContentRatingTest extends TestCase {
private static final String DOMAIN = "android.media.tv";
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvInputInfoTest.java b/tests/tests/tv/src/android/media/tv/cts/TvInputInfoTest.java
index 2b2e269..115a9c4 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvInputInfoTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvInputInfoTest.java
@@ -25,12 +25,14 @@
import android.media.tv.TvInputManager;
import android.os.Bundle;
import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
import android.test.AndroidTestCase;
import android.text.TextUtils;
/**
* Test for {@link android.media.tv.TvInputInfo}.
*/
+@Presubmit
public class TvInputInfoTest extends AndroidTestCase {
private TvInputInfo mStubInfo;
private PackageManager mPackageManager;
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvTrackInfoTest.java b/tests/tests/tv/src/android/media/tv/cts/TvTrackInfoTest.java
index dcae3c8..b2c2041 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvTrackInfoTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvTrackInfoTest.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.media.tv.TvTrackInfo;
import android.os.Bundle;
+import android.platform.test.annotations.Presubmit;
import androidx.test.core.os.Parcelables;
@@ -34,13 +35,13 @@
/**
* Test {@link android.media.tv.TvTrackInfo}.
*/
+@Presubmit
public class TvTrackInfoTest {
@Rule
public final RequiredServiceRule requiredServiceRule = new RequiredServiceRule(
Context.TV_INPUT_SERVICE);
-
@Test
public void newAudioTrack_default() {
final TvTrackInfo info = new TvTrackInfo.Builder(TvTrackInfo.TYPE_AUDIO, "default")
diff --git a/tests/tests/util/AndroidManifest.xml b/tests/tests/util/AndroidManifest.xml
index 77ab380..0e71890 100644
--- a/tests/tests/util/AndroidManifest.xml
+++ b/tests/tests/util/AndroidManifest.xml
@@ -21,8 +21,6 @@
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.READ_LOGS" />
<application>
- <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
- android:exported="true" />
<uses-library android:name="android.test.runner" />
</application>
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewSslTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewSslTest.java
index 658fde7..2cf5e3f 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewSslTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewSslTest.java
@@ -824,6 +824,26 @@
assertEquals(callCount + 1, webViewClient.getClientCertRequestCount());
}
+ /**
+ * Loads a url until a specific error code. This is meant to be used when two different errors
+ * can race. Specifically, this is meant to be used to workaround the TLS 1.3 (Android Q and
+ * above) race condition where a server <b>may</b> close the connection at the same time the
+ * client sends the HTTP request, emitting {@code ERROR_CONNECT} instead of {@code
+ * ERROR_FAILED_SSL_HANDSHAKE}.
+ */
+ private void loadUrlUntilError(SslErrorWebViewClient client, String url,
+ int expectedErrorCode) {
+ int maxTries = 40;
+ for (int i = 0; i < maxTries; i++) {
+ mOnUiThread.loadUrlAndWaitForCompletion(url);
+ if (client.onReceivedErrorCode() == expectedErrorCode) {
+ return;
+ }
+ }
+ throw new RuntimeException(
+ "Reached max number of tries and never saw error " + expectedErrorCode);
+ }
+
@FlakyTest(bugId = 172332767)
public void testIgnoreClientCertRequest() throws Throwable {
if (!NullWebViewUtils.isWebViewAvailable()) {
@@ -836,18 +856,23 @@
clearClientCertPreferences();
// Ignore the request. Load should fail.
webViewClient.setAction(ClientCertWebViewClient.IGNORE);
- mOnUiThread.loadUrlAndWaitForCompletion(url);
+ loadUrlUntilError(webViewClient, url, WebViewClient.ERROR_FAILED_SSL_HANDSHAKE);
assertFalse(TestHtmlConstants.HELLO_WORLD_TITLE.equals(mOnUiThread.getTitle()));
- assertFailedHandshakeOrConnectionError(webViewClient.onReceivedErrorCode());
+ // At least one of the loads done by loadUrlUntilError() should produce
+ // onReceivedClientCertRequest.
+ assertTrue("onReceivedClientCertRequest should be called at least once",
+ webViewClient.getClientCertRequestCount() >= 1);
// Load a different page from the same domain, ignoring the request. We should get a callback,
// and load should fail.
int callCount = webViewClient.getClientCertRequestCount();
url = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
- mOnUiThread.loadUrlAndWaitForCompletion(url);
+ loadUrlUntilError(webViewClient, url, WebViewClient.ERROR_FAILED_SSL_HANDSHAKE);
assertFalse(TestHtmlConstants.HTML_URL1_TITLE.equals(mOnUiThread.getTitle()));
- assertFailedHandshakeOrConnectionError(webViewClient.onReceivedErrorCode());
- assertEquals(callCount + 1, webViewClient.getClientCertRequestCount());
+ // At least one of the loads done by loadUrlUntilError() should produce
+ // onReceivedClientCertRequest.
+ assertTrue("onReceivedClientCertRequest should be called at least once for second URL",
+ webViewClient.getClientCertRequestCount() >= callCount + 1);
// Reload, proceeding the request. Load should succeed.
webViewClient.setAction(ClientCertWebViewClient.PROCEED);
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/EasyConnectStatusCallbackTest.java b/tests/tests/wifi/src/android/net/wifi/cts/EasyConnectStatusCallbackTest.java
index 8d8f8eb..e6c5a1d 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/EasyConnectStatusCallbackTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/EasyConnectStatusCallbackTest.java
@@ -247,12 +247,15 @@
// skip the test if Easy Connect is not supported
return;
}
+ UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
try {
+ uiAutomation.adoptShellPermissionIdentity();
mWifiManager.startEasyConnectAsEnrolleeResponder(TEST_WRONG_DEVICE_INFO,
EASY_CONNECT_CRYPTOGRAPHY_CURVE_DEFAULT, mExecutor,
mEasyConnectStatusCallback);
fail("startEasyConnectAsEnrolleeResponder did not throw an IllegalArgumentException"
+ "on passing a wrong device info!");
} catch (IllegalArgumentException expected) {}
+ uiAutomation.dropShellPermissionIdentity();
}
}
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
index 0729b44..7ee1b72 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
@@ -2880,6 +2880,51 @@
}
/**
+ * Verify WifiNetworkSuggestion.Builder.setIsEnhancedMacRandomizationEnabled(true) creates a
+ * WifiConfiguration with macRandomizationSetting == RANDOMIZATION_ENHANCED.
+ * Then verify by default, a WifiConfiguration created by suggestions should have
+ * macRandomizationSetting == RANDOMIZATION_PERSISTENT.
+ * TODO(b/167575586): Wait for S SDK finalization to determine the final minSdkVersion.
+ */
+ @SdkSuppress(minSdkVersion = 31, codeName = "S")
+ public void testSuggestionBuilderEnhancedMacRandomizationSetting() throws Exception {
+ if (!WifiFeature.isWifiSupported(getContext())) {
+ // skip the test if WiFi is not supported
+ return;
+ }
+ WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
+ .setSsid(TEST_SSID).setWpa2Passphrase(TEST_PASSPHRASE)
+ .setIsEnhancedMacRandomizationEnabled(true)
+ .build();
+ assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+ mWifiManager.addNetworkSuggestions(Arrays.asList(suggestion)));
+ verifySuggestionFoundWithMacRandomizationSetting(TEST_SSID,
+ WifiConfiguration.RANDOMIZATION_ENHANCED);
+
+ suggestion = new WifiNetworkSuggestion.Builder()
+ .setSsid(TEST_SSID).setWpa2Passphrase(TEST_PASSPHRASE)
+ .build();
+ assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+ mWifiManager.addNetworkSuggestions(Arrays.asList(suggestion)));
+ verifySuggestionFoundWithMacRandomizationSetting(TEST_SSID,
+ WifiConfiguration.RANDOMIZATION_PERSISTENT);
+ }
+
+ private void verifySuggestionFoundWithMacRandomizationSetting(String ssid,
+ int macRandomizationSetting) {
+ List<WifiNetworkSuggestion> retrievedSuggestions = mWifiManager.getNetworkSuggestions();
+ for (WifiNetworkSuggestion entry : retrievedSuggestions) {
+ if (entry.getSsid().equals(ssid)) {
+ assertEquals(macRandomizationSetting,
+ entry.getWifiConfiguration().macRandomizationSetting);
+ return; // pass test after the MAC randomization setting is verified.
+ }
+ }
+ fail("WifiNetworkSuggestion not found for SSID=" + ssid + ", macRandomizationSetting="
+ + macRandomizationSetting);
+ }
+
+ /**
* Tests {@link WifiManager#getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(List)}
*/
public void testGetAllWifiConfigForMatchedNetworkSuggestion() {