Merge "Use DE storage for writing shared prefs during the test."
diff --git a/apps/CameraITS/tests/scene2_e/test_continuous_picture.py b/apps/CameraITS/tests/scene2_e/test_continuous_picture.py
new file mode 100644
index 0000000..3f64e3a
--- /dev/null
+++ b/apps/CameraITS/tests/scene2_e/test_continuous_picture.py
@@ -0,0 +1,98 @@
+# Copyright 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os.path
+import its.caps
+import its.device
+import its.image
+import its.objects
+
+CONTINUOUS_PICTURE_MODE = 4
+CONVERGED_3A = [[2, 2, 2], [2, 5, 2], [2, 6, 2]] # [AE, AF, AWB]
+# AE_STATES: {0: INACTIVE, 1: SEARCHING, 2: CONVERGED, 3: LOCKED,
+# 4: FLASH_REQ, 5: PRECAPTURE}
+# AF_STATES: {0: INACTIVE, 1: PASSIVE_SCAN, 2: PASSIVE_FOCUSED,
+# 3: PASSIVE_UNFOCUSED, 4: ACTIVE_SCAN, 5: FOCUS_LOCKED,
+# 6: NOT_FOCUSED_LOCKED}
+# AWB_STATES: {0: INACTIVE, 1: SEARCHING, 2: CONVERGED, 3: LOCKED}
+NAME = os.path.basename(__file__).split('.')[0]
+NUM_FRAMES = 50
+W, H = 640, 480
+
+
+def capture_frames(cam, debug):
+ """Capture frames."""
+ cap_data_list = []
+ req = its.objects.auto_capture_request()
+ req['android.control.afMode'] = CONTINUOUS_PICTURE_MODE
+ fmt = {'format': 'yuv', 'width': W, 'height': H}
+ caps = cam.do_capture([req]*NUM_FRAMES, fmt)
+
+ # extract frame metadata and frame
+ for i, cap in enumerate(caps):
+ cap_data = {}
+ md = cap['metadata']
+ exp = md['android.sensor.exposureTime']
+ iso = md['android.sensor.sensitivity']
+ fd = md['android.lens.focalLength']
+ ae_state = md['android.control.aeState']
+ af_state = md['android.control.afState']
+ awb_state = md['android.control.awbState']
+ fd_str = 'infinity'
+ if fd != 0.0:
+ fd_str = str(round(1.0E2/fd, 2)) + 'cm'
+ img = its.image.convert_capture_to_rgb_image(cap)
+ tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
+ g = its.image.compute_image_means(tile)[1]
+ print '%d, iso: %d, exp: %.2fms, fd: %s, avg: %.3f' % (
+ i, iso, exp*1E-6, fd_str, g),
+ print '[ae,af,awb]: [%d,%d,%d]' % (ae_state, af_state, awb_state)
+ cap_data['exp'] = exp
+ cap_data['iso'] = iso
+ cap_data['fd'] = fd
+ cap_data['3a_state'] = [ae_state, af_state, awb_state]
+ cap_data['avg'] = g
+ cap_data_list.append(cap_data)
+ if debug:
+ its.image.write_image(img, '%s_%d.jpg' % (NAME, i))
+ return cap_data_list
+
+
+def main():
+ """Test 3A converges in CONTINUOUS_PICTURE mode.
+
+ Set camera into CONTINUOUS_PICTURE mode and do NUM_FRAMES capture.
+ By the end of NUM_FRAMES capture, 3A should be in converged state.
+ """
+
+ # check for skip conditions and do 3a up front
+ with its.device.ItsSession() as cam:
+ props = cam.get_camera_properties()
+ props = cam.override_with_hidden_physical_camera_props(props)
+ its.caps.skip_unless(its.caps.continuous_picture(props) and
+ its.caps.read_3a(props))
+ debug = its.caps.debug_mode()
+ cam.do_3a()
+
+ # ensure 3a settles in CONTINUOUS_PICTURE mode with no scene change
+ cap_data = capture_frames(cam, debug)
+ final_3a = cap_data[NUM_FRAMES-1]['3a_state']
+ msg = '\n Last frame [ae, af, awb] state: [%d, %d, %d]' % (
+ final_3a[0], final_3a[1], final_3a[2])
+ msg += '\n Converged states:' + str(CONVERGED_3A)
+ assert final_3a in CONVERGED_3A, msg
+
+
+if __name__ == '__main__':
+ main()
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index 20fd98b..021a324 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -113,7 +113,8 @@
['test_num_faces', 30]
],
'scene2_e': [
- ['test_num_faces', 30]
+ ['test_num_faces', 30],
+ ['test_continuous_picture', 30]
],
'scene3': [],
'scene4': [],
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/AndroidManifest.xml b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/AndroidManifest.xml
index 02b480a..5564c3a 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/AndroidManifest.xml
@@ -42,6 +42,8 @@
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
+
+ <activity android:name=".CrossProfileSameTaskLauncherActivity" android:exported="true"/>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/res/layout/cross_profile_same_task_launcher.xml b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/res/layout/cross_profile_same_task_launcher.xml
new file mode 100644
index 0000000..8d9cf94
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/res/layout/cross_profile_same_task_launcher.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.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="CrossProfileSameTaskLauncherActivity"
+ />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsStartActivityTest.java b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsStartActivityTest.java
index 87878a1..be5c4b1 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsStartActivityTest.java
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsStartActivityTest.java
@@ -24,6 +24,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
+import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -36,7 +37,6 @@
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.Until;
-import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -104,7 +104,7 @@
intent.setComponent(MainActivity.getComponentName(mContext));
ShellIdentityUtils.dropShellPermissionIdentity();
- mCrossProfileApps.startActivity(intent, mTargetUser);
+ mCrossProfileApps.startActivity(intent, mTargetUser, /* callingActivity= */ null);
}
@Test
@@ -113,8 +113,11 @@
intent.setComponent(MainActivity.getComponentName(mContext));
ShellIdentityUtils.dropShellPermissionIdentity();
- ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
- crossProfileApps -> crossProfileApps.startActivity(intent, mTargetUser), INTERACT_ACROSS_PROFILES);
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+ mCrossProfileApps,
+ crossProfileApps -> crossProfileApps.startActivity(
+ intent, mTargetUser, /* callingActivity= */ null),
+ INTERACT_ACROSS_PROFILES);
// Look for the text view to verify that MainActivity is started.
UiObject2 textView = mDevice.wait(Until.findObject(By.res(ID_USER_TEXTVIEW)),
@@ -130,8 +133,11 @@
intent.setComponent(MainActivity.getComponentName(mContext));
ShellIdentityUtils.dropShellPermissionIdentity();
- ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
- crossProfileApps -> crossProfileApps.startActivity(intent, mTargetUser), INTERACT_ACROSS_USERS);
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+ mCrossProfileApps,
+ crossProfileApps -> crossProfileApps.startActivity(
+ intent, mTargetUser, /* callingActivity= */ null),
+ INTERACT_ACROSS_USERS);
// Look for the text view to verify that MainActivity is started.
UiObject2 textView = mDevice.wait(Until.findObject(By.res(ID_USER_TEXTVIEW)),
@@ -147,8 +153,11 @@
intent.setComponent(MainActivity.getComponentName(mContext));
ShellIdentityUtils.dropShellPermissionIdentity();
- ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
- crossProfileApps -> crossProfileApps.startActivity(intent, mTargetUser), INTERACT_ACROSS_USERS_FULL);
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+ mCrossProfileApps,
+ crossProfileApps -> crossProfileApps.startActivity(
+ intent, mTargetUser, /* callingActivity= */ null),
+ INTERACT_ACROSS_USERS_FULL);
// Look for the text view to verify that MainActivity is started.
UiObject2 textView = mDevice.wait(Until.findObject(By.res(ID_USER_TEXTVIEW)),
@@ -164,9 +173,10 @@
Intent nonMainActivityImplicitIntent = new Intent();
nonMainActivityImplicitIntent.setAction(Intent.ACTION_VIEW);
- ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+ mCrossProfileApps,
crossProfileApps -> crossProfileApps.startActivity(
- nonMainActivityImplicitIntent, mTargetUser));
+ nonMainActivityImplicitIntent, mTargetUser, /* callingActivity= */ null));
}
@Test
@@ -175,9 +185,10 @@
mainActivityIntent.setComponent(MainActivity.getComponentName(mContext));
try {
- ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+ mCrossProfileApps,
crossProfileApps -> mCrossProfileApps.startActivity(
- mainActivityIntent, mTargetUser));
+ mainActivityIntent, mTargetUser, /* callingActivity= */ null));
// Look for the text view to verify that MainActivity is started.
UiObject2 textView = mDevice.wait(Until.findObject(By.res(ID_USER_TEXTVIEW)),
@@ -196,9 +207,10 @@
nonMainActivityIntent.setComponent(NonMainActivity.getComponentName(mContext));
try {
- ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+ mCrossProfileApps,
crossProfileApps -> mCrossProfileApps.startActivity(
- nonMainActivityIntent, mTargetUser));
+ nonMainActivityIntent, mTargetUser, /* callingActivity= */ null));
// Look for the text view to verify that NonMainActivity is started.
UiObject2 textView = mDevice.wait(Until.findObject(By.res(ID_USER_TEXTVIEW2)),
@@ -212,7 +224,33 @@
}
/**
- * Calls {@link CrossProfileApps#startActivity(Intent, UserHandle)}. This can then be used by
+ * Starts an activity in the same task in the target user. Asserts that the activity is
+ * correctly started in the correct user, but the host-side test should verify that the tasks
+ * are the same using the log messages printed by each activity.
+ */
+ @Test
+ public void testStartActivityIntent_sameTaskByDefault() throws Exception {
+ try {
+ final Intent crossProfileSameTaskCheckerIntent = new Intent();
+ crossProfileSameTaskCheckerIntent.setComponent(
+ CrossProfileSameTaskLauncherActivity.getComponentName(mContext));
+ crossProfileSameTaskCheckerIntent.putExtra(
+ CrossProfileSameTaskLauncherActivity.TARGET_USER_EXTRA, mTargetUser);
+ mContext.startActivity(crossProfileSameTaskCheckerIntent);
+
+ // Look for the text view to verify that NonMainActivity is started.
+ UiObject2 textView = mDevice.wait(Until.findObject(By.res(ID_USER_TEXTVIEW2)),
+ TIMEOUT_WAIT_UI);
+ assertNotNull("Failed to start non-main activity in target user", textView);
+ assertEquals("Non-Main Activity is started in wrong user",
+ String.valueOf(mUserSerialNumber), textView.getText());
+ } catch (Exception e) {
+ fail("unable to start cross-profile activity in the same task: " + e);
+ }
+ }
+
+ /**
+ * Calls {@link CrossProfileApps#startActivity(Intent, UserHandle, Activity)}. This can then be used by
* host-side tests.
*/
@Test
@@ -222,8 +260,8 @@
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
mCrossProfileApps,
- crossProfileApps ->
- mCrossProfileApps.startActivity(nonMainActivityIntent, mTargetUser));
+ crossProfileApps -> mCrossProfileApps.startActivity(
+ nonMainActivityIntent, mTargetUser, /* callingActivity= */ null));
}
@Test
@@ -267,8 +305,10 @@
Intent nonExportedActivityIntent = new Intent();
nonExportedActivityIntent.setComponent(NonExportedActivity.getComponentName(mContext));
- ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCrossProfileApps,
- crossProfileApps -> mCrossProfileApps.startActivity(nonExportedActivityIntent, mTargetUser));
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+ mCrossProfileApps,
+ crossProfileApps -> mCrossProfileApps.startActivity(
+ nonExportedActivityIntent, mTargetUser, /* callingActivity= */ null));
// Look for the text view to verify that NonExportedActivity is started.
UiObject2 textView = mDevice.wait(Until.findObject(By.res(ID_USER_TEXTVIEW2)),
@@ -290,9 +330,8 @@
otherPackageIntent.setComponent(new ComponentName(
"com.android.cts.launcherapps.simpleapp",
"com.android.cts.launcherapps.simpleapp.SimpleActivity"));
- mCrossProfileApps.startActivity(otherPackageIntent,
- mTargetUser
- );
+ mCrossProfileApps.startActivity(
+ otherPackageIntent, mTargetUser, /* callingActivity= */ null);
}
@Test(expected = SecurityException.class)
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileSameTaskLauncherActivity.java b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileSameTaskLauncherActivity.java
new file mode 100644
index 0000000..9d1f209
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileSameTaskLauncherActivity.java
@@ -0,0 +1,66 @@
+/*
+ * 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.crossprofileappstest;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.CrossProfileApps;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.android.compatibility.common.util.ShellIdentityUtils;
+
+/**
+ * An activity that launches the {@link NonMainActivity} in a different user within the same task.
+ *
+ * <p>Logs its task ID with the following format:
+ * "CrossProfileSameTaskLauncherActivity#taskId#[taskId]#", where [taskId] is the actual task ID,
+ * such as NonMainActivity#taskId#4#.
+ */
+public class CrossProfileSameTaskLauncherActivity extends Activity {
+ static final String TARGET_USER_EXTRA = "TARGET_USER";
+
+ private static final String LOG_TAG = "CrossProfileSameTaskChe"; // 23 chars max
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final Intent intent = getIntent();
+ if (!intent.hasExtra(TARGET_USER_EXTRA)) {
+ throw new IllegalStateException(
+ "ActivityForwarder started without the extra: " + TARGET_USER_EXTRA);
+ }
+ setContentView(R.layout.cross_profile_same_task_launcher);
+ Log.w(LOG_TAG, "CrossProfileSameTaskLauncherActivity#taskId#" + getTaskId() + "#");
+ final UserHandle targetUser = intent.getParcelableExtra(TARGET_USER_EXTRA);
+ final Intent nonMainActivityIntent = new Intent();
+ nonMainActivityIntent.setComponent(NonMainActivity.getComponentName(this));
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+ getSystemService(CrossProfileApps.class),
+ crossProfileApps
+ -> crossProfileApps.startActivity(nonMainActivityIntent, targetUser, this));
+ }
+
+ static ComponentName getComponentName(Context context) {
+ return new ComponentName(context, CrossProfileSameTaskLauncherActivity.class);
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonMainActivity.java b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonMainActivity.java
index 4131a98..c2ad97c 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonMainActivity.java
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonMainActivity.java
@@ -21,14 +21,23 @@
import android.os.Bundle;
import android.os.Process;
import android.os.UserManager;
+import android.util.Log;
import android.widget.TextView;
+/**
+ * An activity that is not the main activity of the application.
+ *
+ * <p>Logs its task ID with the following format: "NonMainActivity#taskId#[taskId]#", where [taskId]
+ * is the actual task ID, such as NonMainActivity#taskId#4#.
+ */
public class NonMainActivity extends Activity {
+ private static final String LOG_TAG = "NonMainActivity";
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.non_main);
+ Log.w(LOG_TAG, "NonMainActivity#taskId#" + getTaskId() + "#");
}
@Override
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
index eff3ce7..d427645 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
@@ -7,17 +7,25 @@
import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
import android.platform.test.annotations.FlakyTest;
import android.platform.test.annotations.LargeTest;
import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.result.InputStreamSource;
+import com.android.tradefed.util.StreamUtil;
import org.junit.Test;
import java.io.FileNotFoundException;
import java.util.Collections;
import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import javax.annotation.Nullable;
@@ -158,6 +166,33 @@
@LargeTest
@Test
+ public void testStartActivityIntent_sameTaskByDefault() throws Exception {
+ if (!mHasManagedUserFeature) {
+ return;
+ }
+ getDevice().clearLogcat();
+ verifyCrossProfileAppsApi(
+ mProfileId,
+ mPrimaryUserId,
+ START_ACTIVITY_TEST_CLASS,
+ "testStartActivityIntent_sameTaskByDefault");
+ assertThat(findTaskId("CrossProfileSameTaskLauncherActivity"))
+ .isEqualTo(findTaskId("NonMainActivity"));
+ }
+
+ private int findTaskId(String className) throws Exception {
+ final Matcher matcher =
+ Pattern.compile(className + "#taskId#" + "(.*?)" + "#").matcher(readLogcat());
+ boolean isFound = matcher.find();
+ if (!isFound) {
+ fail("Task not found for " + className);
+ return -1;
+ }
+ return Integer.parseInt(matcher.group(1));
+ }
+
+ @LargeTest
+ @Test
public void testPrimaryUserToSecondaryUser() throws Exception {
if (!mCanTestMultiUser) {
return;
@@ -257,4 +292,14 @@
return Collections.singletonMap(PARAM_TARGET_USER,
Integer.toString(getUserSerialNumber(targetUserId)));
}
+
+ private String readLogcat() throws Exception {
+ getDevice().stopLogcat();
+ final String logcat;
+ try (InputStreamSource logcatStream = getDevice().getLogcat()) {
+ logcat = StreamUtil.getStringFromSource(logcatStream);
+ }
+ getDevice().startLogcat();
+ return logcat;
+ }
}
diff --git a/hostsidetests/hdmicec/OWNERS b/hostsidetests/hdmicec/OWNERS
index 254c58d..e7f5c32 100644
--- a/hostsidetests/hdmicec/OWNERS
+++ b/hostsidetests/hdmicec/OWNERS
@@ -1,3 +1,3 @@
-# Bug component: 141606867
+# Bug component: 114946
nchalko@google.com
amyjojo@google.com
diff --git a/hostsidetests/packagemanager/dynamicmime/Android.bp b/hostsidetests/packagemanager/dynamicmime/Android.bp
new file mode 100644
index 0000000..e23c99a
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+java_test_host {
+ name: "CtsDynamicMimeHostTestCases",
+ defaults: ["cts_defaults"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ libs: [
+ "cts-tradefed",
+ "tradefed",
+ "compatibility-host-util",
+ "truth-prebuilt",
+ ],
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/AndroidTest.xml b/hostsidetests/packagemanager/dynamicmime/AndroidTest.xml
new file mode 100644
index 0000000..56ab6ca
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/AndroidTest.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<configuration description="Config for the CTS dynamic MIME tests">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
+ <option name="test-file-name" value="CtsDynamicMimeHelperApp.apk" />
+ <option name="test-file-name" value="CtsDynamicMimeTestApp.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="mkdir -p /data/local/tmp/dynamic-mime-test" />
+ <option name="teardown-command" value="rm -rf /data/local/tmp/dynamic-mime-test"/>
+ </target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="CtsDynamicMimeUpdateAppFirstGroup.apk->/data/local/tmp/dynamic-mime-test/CtsDynamicMimeUpdateAppFirstGroup.apk"/>
+ <option name="push" value="CtsDynamicMimeUpdateAppSecondGroup.apk->/data/local/tmp/dynamic-mime-test/CtsDynamicMimeUpdateAppSecondGroup.apk"/>
+ <option name="push" value="CtsDynamicMimeUpdateAppBothGroups.apk->/data/local/tmp/dynamic-mime-test/CtsDynamicMimeUpdateAppBothGroups.apk"/>
+ <option name="push" value="CtsDynamicMimePreferredApp.apk->/data/local/tmp/dynamic-mime-test/CtsDynamicMimePreferredApp.apk"/>
+ </target_preparer>
+
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="CtsDynamicMimeHostTestCases.jar" />
+ </test>
+
+</configuration>
diff --git a/hostsidetests/packagemanager/dynamicmime/OWNERS b/hostsidetests/packagemanager/dynamicmime/OWNERS
new file mode 100644
index 0000000..af9eee9
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 136635677
+tantoshchuk@google.com
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/app/Android.bp b/hostsidetests/packagemanager/dynamicmime/app/Android.bp
new file mode 100644
index 0000000..1e983e1
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/app/Android.bp
@@ -0,0 +1,88 @@
+// 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.
+
+android_test_helper_app {
+ name: "CtsDynamicMimeHelperApp",
+ defaults: ["cts_support_defaults"],
+ static_libs: ["CtsDynamicMimeCommon"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+ manifest: "manifests/AndroidManifest_helper.xml",
+}
+
+android_test_helper_app {
+ name: "CtsDynamicMimeUpdateAppFirstGroup",
+ defaults: ["cts_support_defaults"],
+ static_libs: ["CtsDynamicMimeCommon"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+ manifest: "manifests/AndroidManifest_update_firstGroup.xml",
+}
+
+android_test_helper_app {
+ name: "CtsDynamicMimeUpdateAppSecondGroup",
+ defaults: ["cts_support_defaults"],
+ static_libs: ["CtsDynamicMimeCommon"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+ manifest: "manifests/AndroidManifest_update_secondGroup.xml",
+}
+
+android_test_helper_app {
+ name: "CtsDynamicMimeUpdateAppBothGroups",
+ defaults: ["cts_support_defaults"],
+ static_libs: ["CtsDynamicMimeCommon"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+ manifest: "manifests/AndroidManifest_update_bothGroups.xml",
+}
+
+android_test_helper_app {
+ name: "CtsDynamicMimePreferredApp",
+ defaults: ["cts_support_defaults"],
+ static_libs: ["CtsDynamicMimeCommon"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+ manifest: "manifests/AndroidManifest_preferred.xml",
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_helper.xml b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_helper.xml
new file mode 100644
index 0000000..1373430
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_helper.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.dynamicmime.helper">
+ <application android:testOnly="true">
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.dynamicmime.common.activity.FirstActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.helper.FILTER_INFO_HOOK_group_first"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeGroup="group_first"/>
+ </intent-filter>
+ </activity>
+ <activity android:name="android.dynamicmime.common.activity.SecondActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.helper.FILTER_INFO_HOOK_group_second"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeGroup="group_second"/>
+ </intent-filter>
+ </activity>
+
+ <receiver android:name="android.dynamicmime.app.AppMimeGroupsReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.dynamicmime.UPDATE_MIME_GROUP_REQUEST"/>
+ </intent-filter>
+ </receiver>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.dynamicmime.helper" >
+ </instrumentation>
+</manifest>
diff --git a/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_preferred.xml b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_preferred.xml
new file mode 100644
index 0000000..83cdf0e
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_preferred.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.dynamicmime.preferred">
+ <application android:testOnly="true"
+ android:label="TestApp.Application">
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.dynamicmime.common.activity.FirstActivity">
+ <intent-filter android:label="TestApp.FirstActivity">
+ <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.preferred.FILTER_INFO_HOOK_group_first"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeGroup="group_first"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:name="android.dynamicmime.common.activity.TwoGroupsActivity">
+ <intent-filter android:label="TestApp.TwoGroupsActivity">
+ <action android:name="android.intent.action.SEND"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <action android:name="android.dynamicmime.preferred.FILTER_INFO_HOOK_group_both"/>
+ <data android:mimeGroup="group_third"/>
+ <data android:mimeGroup="group_second"/>
+ </intent-filter>
+ </activity>
+
+ <receiver android:name="android.dynamicmime.app.AppMimeGroupsReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.dynamicmime.UPDATE_MIME_GROUP_REQUEST"/>
+ </intent-filter>
+ </receiver>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.dynamicmime.preferred">
+ </instrumentation>
+</manifest>
diff --git a/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_bothGroups.xml b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_bothGroups.xml
new file mode 100644
index 0000000..802d13c
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_bothGroups.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.dynamicmime.update">
+ <application android:testOnly="true">
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.dynamicmime.common.activity.FirstActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.update.FILTER_INFO_HOOK_group_first"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeGroup="group_first"/>
+ </intent-filter>
+ </activity>
+ <activity android:name="android.dynamicmime.common.activity.SecondActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.update.FILTER_INFO_HOOK_group_second"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeGroup="group_second"/>
+ </intent-filter>
+ </activity>
+
+ <receiver android:name="android.dynamicmime.app.AppMimeGroupsReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.dynamicmime.UPDATE_MIME_GROUP_REQUEST"/>
+ </intent-filter>
+ </receiver>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.dynamicmime.update" >
+ </instrumentation>
+</manifest>
diff --git a/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_firstGroup.xml b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_firstGroup.xml
new file mode 100644
index 0000000..adc93cf
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_firstGroup.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.dynamicmime.update">
+ <application android:testOnly="true">
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.dynamicmime.common.activity.FirstActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.update.FILTER_INFO_HOOK_group_first"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeGroup="group_first"/>
+ </intent-filter>
+ </activity>
+ <activity android:name="android.dynamicmime.common.activity.SecondActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.update.FILTER_INFO_HOOK_group_second"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+
+ <receiver android:name="android.dynamicmime.app.AppMimeGroupsReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.dynamicmime.UPDATE_MIME_GROUP_REQUEST"/>
+ </intent-filter>
+ </receiver>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.dynamicmime.update" >
+ </instrumentation>
+</manifest>
diff --git a/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_secondGroup.xml b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_secondGroup.xml
new file mode 100644
index 0000000..e93a3d5
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_update_secondGroup.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.dynamicmime.update">
+ <application android:testOnly="true">
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.dynamicmime.common.activity.FirstActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.update.FILTER_INFO_HOOK_group_first"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+ <activity android:name="android.dynamicmime.common.activity.SecondActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.update.FILTER_INFO_HOOK_group_second"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeGroup="group_second"/>
+ </intent-filter>
+ </activity>
+
+ <receiver android:name="android.dynamicmime.app.AppMimeGroupsReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.dynamicmime.UPDATE_MIME_GROUP_REQUEST"/>
+ </intent-filter>
+ </receiver>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.dynamicmime.update" >
+ </instrumentation>
+</manifest>
diff --git a/hostsidetests/packagemanager/dynamicmime/app/src/android/dynamicmime/app/AppMimeGroupsReceiver.java b/hostsidetests/packagemanager/dynamicmime/app/src/android/dynamicmime/app/AppMimeGroupsReceiver.java
new file mode 100644
index 0000000..7c79fef
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/app/src/android/dynamicmime/app/AppMimeGroupsReceiver.java
@@ -0,0 +1,74 @@
+/*
+ * 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.dynamicmime.app;
+
+import static android.dynamicmime.common.Constants.ACTION_REQUEST;
+import static android.dynamicmime.common.Constants.ACTION_RESPONSE;
+import static android.dynamicmime.common.Constants.EXTRA_GROUP;
+import static android.dynamicmime.common.Constants.EXTRA_MIMES;
+import static android.dynamicmime.common.Constants.EXTRA_REQUEST;
+import static android.dynamicmime.common.Constants.EXTRA_RESPONSE;
+import static android.dynamicmime.common.Constants.REQUEST_CLEAR;
+import static android.dynamicmime.common.Constants.REQUEST_GET;
+import static android.dynamicmime.common.Constants.REQUEST_SET;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.ArraySet;
+
+import java.util.Set;
+
+/**
+ * Receiver of {@link android.dynamicmime.testapp.util.AppMimeGroups} commands
+ */
+public class AppMimeGroupsReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!ACTION_REQUEST.equals(intent.getAction())) {
+ return;
+ }
+
+ String mimeGroup = intent.getStringExtra(EXTRA_GROUP);
+ String[] mimeTypes = intent.getStringArrayExtra(EXTRA_MIMES);
+ int requestCode = intent.getIntExtra(EXTRA_REQUEST, -1);
+
+ Intent response = new Intent(ACTION_RESPONSE);
+ switch (requestCode) {
+ case REQUEST_SET:
+ context.getPackageManager().setMimeGroup(mimeGroup, new ArraySet<>(mimeTypes));
+ break;
+ case REQUEST_CLEAR:
+ context.getPackageManager().clearMimeGroup(mimeGroup);
+ break;
+ case REQUEST_GET:
+ response.putExtra(EXTRA_RESPONSE, getMimeGroup(context, mimeGroup));
+ break;
+ default:
+ //do not respond with broadcast to indicate that something is wrong
+ return;
+ }
+
+ context.sendBroadcast(response);
+ }
+
+ private String[] getMimeGroup(Context context, String mimeGroup) {
+ Set<String> mimeTypes = context.getPackageManager().getMimeGroup(mimeGroup);
+ return mimeTypes != null ? mimeTypes.toArray(new String[0]) : null;
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/common/Android.bp b/hostsidetests/packagemanager/dynamicmime/common/Android.bp
new file mode 100644
index 0000000..a80aba7
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/common/Android.bp
@@ -0,0 +1,20 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_library_static {
+ name: "CtsDynamicMimeCommon",
+ defaults: ["cts_defaults"],
+ sdk_version: "test_current",
+ srcs: ["src/**/*.java"],
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/Constants.java b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/Constants.java
new file mode 100644
index 0000000..e3289ce
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/Constants.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.dynamicmime.common;
+
+public class Constants {
+ public static final String ACTION_REQUEST = "android.dynamicmime.UPDATE_MIME_GROUP_REQUEST";
+ public static final String ACTION_RESPONSE = "android.dynamicmime.UPDATE_MIME_GROUP_RESPONSE";
+
+ public static final String EXTRA_GROUP = "EXTRA_GROUP";
+ public static final String EXTRA_MIMES = "EXTRA_MIME";
+ public static final String EXTRA_REQUEST = "EXTRA_REQUEST";
+ public static final String EXTRA_RESPONSE = "EXTRA_RESPONSE";
+
+ public static final String GROUP_FIRST = "group_first";
+ public static final String GROUP_SECOND = "group_second";
+ public static final String GROUP_THIRD = "group_third";
+ public static final String GROUP_UNDEFINED = "undefined";
+
+ public static final String ALIAS_BOTH_GROUPS = "group_both";
+ public static final String ALIAS_BOTH_GROUPS_AND_TYPE = "groups_and_type";
+
+ public static final String[] GROUPS = {
+ GROUP_FIRST, GROUP_SECOND, GROUP_UNDEFINED
+ };
+
+ public static final int REQUEST_CLEAR = 1;
+ public static final int REQUEST_SET = 2;
+ public static final int REQUEST_GET = 3;
+
+ public static final String MIME_TEXT_PLAIN = "text/plain";
+ public static final String MIME_TEXT_XML = "text/xml";
+ public static final String MIME_TEXT_ANY = "text/*";
+ public static final String MIME_IMAGE_PNG = "image/png";
+ public static final String MIME_IMAGE_JPEG = "image/jpeg";
+ public static final String MIME_IMAGE_ANY = "image/*";
+ public static final String MIME_ANY = "*/*";
+
+ public static final String PACKAGE_TEST_APP = "android.dynamicmime.testapp";
+ public static final String PACKAGE_ACTIVITIES = "android.dynamicmime.common.activity";
+ public static final String PACKAGE_HELPER_APP = "android.dynamicmime.helper";
+ public static final String PACKAGE_UPDATE_APP = "android.dynamicmime.update";
+ public static final String PACKAGE_PREFERRED_APP = "android.dynamicmime.preferred";
+
+ public static final String ACTIVITY_FIRST = ".FirstActivity";
+ public static final String ACTIVITY_SECOND = ".SecondActivity";
+ public static final String ACTIVITY_BOTH = ".TwoGroupsActivity";
+ public static final String ACTIVITY_BOTH_AND_TYPE = ".TwoGroupsAndTypeActivity";
+
+ public static final String DATA_DIR = "/data/local/tmp/dynamic-mime-test/";
+
+ public static final String APK_PREFERRED_APP = DATA_DIR + "CtsDynamicMimePreferredApp.apk";
+ public static final String APK_BOTH_GROUPS = DATA_DIR + "CtsDynamicMimeUpdateAppBothGroups.apk";
+ public static final String APK_FIRST_GROUP = DATA_DIR + "CtsDynamicMimeUpdateAppFirstGroup.apk";
+ public static final String APK_SECOND_GROUP = DATA_DIR +
+ "CtsDynamicMimeUpdateAppSecondGroup.apk";
+
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/FirstActivity.java b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/FirstActivity.java
new file mode 100644
index 0000000..94aeb8f
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/FirstActivity.java
@@ -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.
+ */
+package android.dynamicmime.common.activity;
+
+import android.app.Activity;
+
+public class FirstActivity extends Activity {
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/SecondActivity.java b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/SecondActivity.java
new file mode 100644
index 0000000..3ce0ff3
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/SecondActivity.java
@@ -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.
+ */
+package android.dynamicmime.common.activity;
+
+import android.app.Activity;
+
+public class SecondActivity extends Activity {
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/TwoGroupsActivity.java b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/TwoGroupsActivity.java
new file mode 100644
index 0000000..9171f0b
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/TwoGroupsActivity.java
@@ -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.
+ */
+package android.dynamicmime.common.activity;
+
+import android.app.Activity;
+
+public class TwoGroupsActivity extends Activity {
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/TwoGroupsAndTypeActivity.java b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/TwoGroupsAndTypeActivity.java
new file mode 100644
index 0000000..0a2f04d
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/TwoGroupsAndTypeActivity.java
@@ -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.
+ */
+package android.dynamicmime.common.activity;
+
+import android.app.Activity;
+
+public class TwoGroupsAndTypeActivity extends Activity {
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/PreferredActivitiesTestCases.java b/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/PreferredActivitiesTestCases.java
new file mode 100644
index 0000000..25ac458
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/PreferredActivitiesTestCases.java
@@ -0,0 +1,75 @@
+/*
+ * 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.dynamicmime.cts;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Preferred Activities test cases
+ *
+ * Verifies that preferred activity is removed after any change to any MIME group
+ * declared by preferred activity package
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class PreferredActivitiesTestCases extends BaseHostJUnit4Test {
+ private static final String PACKAGE_TEST_APP = "android.dynamicmime.testapp";
+
+ @Before
+ public void setUp() throws DeviceNotAvailableException {
+ // wake up and unlock device
+ getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP");
+ getDevice().disableKeyguard();
+ }
+
+ @Test
+ public void testRemoveFromGroup() throws DeviceNotAvailableException {
+ runDeviceTest("testRemoveFromGroup");
+ }
+
+ @Test
+ public void testAddToGroup() throws DeviceNotAvailableException {
+ runDeviceTest("testAddToGroup");
+ }
+
+ @Test
+ public void testClearGroup() throws DeviceNotAvailableException {
+ runDeviceTest("testClearGroup");
+ }
+
+ @Test
+ public void testModifyGroupWithoutActualGroupChanges() throws DeviceNotAvailableException {
+ runDeviceTest("testModifyGroupWithoutActualGroupChanges");
+ }
+
+ @Test
+ public void testModifyGroupWithoutActualIntentFilterChanges()
+ throws DeviceNotAvailableException {
+ runDeviceTest("testModifyGroupWithoutActualIntentFilterChanges");
+ }
+
+ private void runDeviceTest(String testMethodName) throws DeviceNotAvailableException {
+ runDeviceTests(PACKAGE_TEST_APP, PACKAGE_TEST_APP + ".preferred.PreferredActivitiesTest",
+ testMethodName);
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/RebootTestCases.java b/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/RebootTestCases.java
new file mode 100644
index 0000000..26ac19f
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/RebootTestCases.java
@@ -0,0 +1,266 @@
+/*
+ * 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.dynamicmime.cts;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Reboot test cases
+ *
+ * Reuses existing test cases from {@link android.dynamicmime.testapp.ComplexFilterTest}
+ * and {@link android.dynamicmime.testapp.SingleAppTest}
+ * by "inserting" device reboot between setup part (MIME group commands) and verification part
+ * (MIME group assertions) in each test case
+ *
+ * @see android.dynamicmime.testapp.reboot.PreRebootSingleAppTest
+ * @see android.dynamicmime.testapp.reboot.PostRebootSingleAppTest
+ * @see #runTestWithReboot(String, String)
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class RebootTestCases extends BaseHostJUnit4Test {
+ private static final String PACKAGE_TEST_APP = "android.dynamicmime.testapp";
+ private static final String PACKAGE_REBOOT_TESTS = PACKAGE_TEST_APP + ".reboot";
+
+ @Test
+ public void testGroupWithExactType() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testGroupWithExactType");
+ }
+
+ @Test
+ public void testGroupWithWildcardType() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testGroupWithWildcardType");
+ }
+
+ @Test
+ public void testGroupWithAnyType() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testGroupWithAnyType");
+ }
+
+ @Test
+ public void testAddSimilarTypes() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testAddSimilarTypes");
+ }
+
+ @Test
+ public void testAddDifferentTypes() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testAddDifferentTypes");
+ }
+
+ @Test
+ public void testRemoveExactType() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testRemoveExactType");
+ }
+
+ @Test
+ public void testRemoveDifferentType() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testRemoveDifferentType");
+ }
+
+ @Test
+ public void testRemoveSameBaseType_keepSpecific() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testRemoveSameBaseType_keepSpecific");
+ }
+
+ @Test
+ public void testRemoveSameBaseType_keepWildcard() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testRemoveSameBaseType_keepWildcard");
+ }
+
+ @Test
+ public void testRemoveAnyType_keepSpecific() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testRemoveAnyType_keepSpecific");
+ }
+
+ @Test
+ public void testRemoveAnyType_keepWildcard() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testRemoveAnyType_keepWildcard");
+ }
+
+ @Test
+ public void testResetWithoutIntersection() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testResetWithoutIntersection");
+ }
+
+ @Test
+ public void testResetWithIntersection() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testResetWithIntersection");
+ }
+
+ @Test
+ public void testResetToEmpty() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testResetToEmpty");
+ }
+
+ @Test
+ public void testClear() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testClear");
+ }
+
+ @Test
+ public void testUndefinedGroupIsNullAfterAdd() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testUndefinedGroupIsNullAfterAdd");
+ }
+
+ @Test
+ public void testUndefinedGroupIsNullAfterSet() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testUndefinedGroupIsNullAfterSet");
+ }
+
+ @Test
+ public void testUndefinedGroupIsNullAfterRemove() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testUndefinedGroupIsNullAfterRemove");
+ }
+
+ @Test
+ public void testUndefinedGroupIsNullAfterClear() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testUndefinedGroupIsNullAfterClear");
+ }
+
+ @Test
+ public void testDefinedGroupNotNullAfterRemove() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testDefinedGroupNotNullAfterRemove");
+ }
+
+ @Test
+ public void testDefinedGroupNotNullAfterClear() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testDefinedGroupNotNullAfterClear");
+ }
+
+ @Test
+ public void testDefinedGroupNotNullAfterSetEmpty() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testDefinedGroupNotNullAfterSetEmpty");
+ }
+
+ @Test
+ public void testDefinedGroupNotNullAfterSetNull() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testDefinedGroupNotNullAfterSetNull");
+ }
+
+ @Test
+ public void testMimeGroupsIndependentAdd() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testMimeGroupsIndependentAdd");
+ }
+
+ @Test
+ public void testMimeGroupsIndependentAddToBoth() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testMimeGroupsIndependentAddToBoth");
+ }
+
+ @Test
+ public void testMimeGroupsIndependentRemove() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testMimeGroupsIndependentRemove");
+ }
+
+ @Test
+ public void testMimeGroupsIndependentClear() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testMimeGroupsIndependentClear");
+ }
+
+ @Test
+ public void testMimeGroupsIndependentClearBoth() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testMimeGroupsIndependentClearBoth");
+ }
+
+ @Test
+ public void testMimeGroupsIndependentSet() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testMimeGroupsIndependentSet");
+ }
+
+ @Test
+ public void testMimeGroupsIndependentSetBoth() throws DeviceNotAvailableException {
+ runTestWithReboot("SingleAppTest", "testMimeGroupsIndependentSetBoth");
+ }
+
+ @Test
+ public void testMimeGroupsNotIntersect() throws DeviceNotAvailableException {
+ runTestWithReboot("ComplexFilterTest", "testMimeGroupsNotIntersect");
+ }
+
+ @Test
+ public void testMimeGroupsIntersect() throws DeviceNotAvailableException {
+ runTestWithReboot("ComplexFilterTest", "testMimeGroupsIntersect");
+ }
+
+ @Test
+ public void testRemoveTypeFromIntersection() throws DeviceNotAvailableException {
+ runTestWithReboot("ComplexFilterTest", "testRemoveTypeFromIntersection");
+ }
+
+ @Test
+ public void testRemoveIntersectionFromBothGroups() throws DeviceNotAvailableException {
+ runTestWithReboot("ComplexFilterTest", "testRemoveIntersectionFromBothGroups");
+ }
+
+ @Test
+ public void testClearGroupWithoutIntersection() throws DeviceNotAvailableException {
+ runTestWithReboot("ComplexFilterTest", "testClearGroupWithoutIntersection");
+ }
+
+ @Test
+ public void testClearGroupWithIntersection() throws DeviceNotAvailableException {
+ runTestWithReboot("ComplexFilterTest", "testClearGroupWithIntersection");
+ }
+
+ @Test
+ public void testMimeGroupNotIntersectWithStaticType() throws DeviceNotAvailableException {
+ runTestWithReboot("ComplexFilterTest", "testMimeGroupNotIntersectWithStaticType");
+ }
+
+ @Test
+ public void testMimeGroupIntersectWithStaticType() throws DeviceNotAvailableException {
+ runTestWithReboot("ComplexFilterTest", "testMimeGroupIntersectWithStaticType");
+ }
+
+ @Test
+ public void testRemoveStaticTypeFromMimeGroup() throws DeviceNotAvailableException {
+ runTestWithReboot("ComplexFilterTest", "testRemoveStaticTypeFromMimeGroup");
+ }
+
+ @Test
+ public void testClearGroupContainsStaticType() throws DeviceNotAvailableException {
+ runTestWithReboot("ComplexFilterTest", "testClearGroupContainsStaticType");
+ }
+
+ @Test
+ public void testClearGroupNotContainStaticType() throws DeviceNotAvailableException {
+ runTestWithReboot("ComplexFilterTest", "testClearGroupNotContainStaticType");
+ }
+
+ private void runTestWithReboot(String testClassName, String testMethodName)
+ throws DeviceNotAvailableException {
+ runPreReboot(testClassName, testMethodName);
+ getDevice().reboot();
+ runPostReboot(testClassName, testMethodName);
+ }
+
+ private void runPostReboot(String testClassName, String testMethodName)
+ throws DeviceNotAvailableException {
+ runDeviceTests(PACKAGE_TEST_APP, PACKAGE_REBOOT_TESTS + ".PostReboot" + testClassName,
+ testMethodName);
+ }
+
+ private void runPreReboot(String testClassName, String testMethodName)
+ throws DeviceNotAvailableException {
+ runDeviceTests(PACKAGE_TEST_APP, PACKAGE_REBOOT_TESTS + ".PreReboot" + testClassName,
+ testMethodName);
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/TestCases.java b/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/TestCases.java
new file mode 100644
index 0000000..d3a2e89
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/TestCases.java
@@ -0,0 +1,62 @@
+/*
+ * 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.dynamicmime.cts;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Invokes device-side tests as is, no need for any host-side setup
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class TestCases extends BaseHostJUnit4Test {
+ private static final String PACKAGE_TEST_APP = "android.dynamicmime.testapp";
+
+ @Test
+ public void testDynamicMimeWithSingleApp() throws DeviceNotAvailableException {
+ runDeviceTests("SingleAppTest");
+ }
+
+ @Test
+ public void testDynamicMimeWithMultipleApps() throws DeviceNotAvailableException {
+ runDeviceTests("MultipleAppsTest");
+ }
+
+ @Test
+ public void testDynamicMimeWithMultipleGroupsPerFilter() throws DeviceNotAvailableException {
+ runDeviceTests("ComplexFilterTest");
+ }
+
+ @Test
+ public void testDynamicMimeWithAppUpdate() throws DeviceNotAvailableException {
+ runDeviceTests("update.SameGroupsTest");
+ }
+
+ @Test
+ public void testDynamicMimeWithChangedGroupsAppUpdate() throws DeviceNotAvailableException {
+ runDeviceTests("update.ChangedGroupsTest");
+ }
+
+ private void runDeviceTests(String className)
+ throws DeviceNotAvailableException {
+ runDeviceTests(PACKAGE_TEST_APP, PACKAGE_TEST_APP + "." + className);
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/Android.bp b/hostsidetests/packagemanager/dynamicmime/test/Android.bp
new file mode 100644
index 0000000..207f050
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/Android.bp
@@ -0,0 +1,35 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+android_test_helper_app {
+ name: "CtsDynamicMimeTestApp",
+ defaults: ["cts_defaults"],
+ static_libs: [
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "compatibility-device-util-axt",
+ "CtsDynamicMimeCommon",
+ "hamcrest-library",
+ "android-support-test",
+ "androidx.test.uiautomator_uiautomator",
+ ],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+ platform_apis: true,
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/test/AndroidManifest.xml b/hostsidetests/packagemanager/dynamicmime/test/AndroidManifest.xml
new file mode 100644
index 0000000..d31dcf3
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/AndroidManifest.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.dynamicmime.testapp">
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.dynamicmime.common.activity.FirstActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.testapp.FILTER_INFO_HOOK_group_first"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeGroup="group_first"/>
+ </intent-filter>
+ </activity>
+ <activity android:name="android.dynamicmime.common.activity.SecondActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.testapp.FILTER_INFO_HOOK_group_second"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeGroup="group_second"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:name="android.dynamicmime.common.activity.TwoGroupsActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <action android:name="android.dynamicmime.testapp.FILTER_INFO_HOOK_group_both"/>
+ <data android:mimeGroup="group_first"/>
+ <data android:mimeGroup="group_second"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:name="android.dynamicmime.common.activity.TwoGroupsAndTypeActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <action android:name="android.dynamicmime.testapp.FILTER_INFO_HOOK_groups_and_type"/>
+ <data android:mimeType="text/plain"/>
+ <data android:mimeGroup="group_first"/>
+ <data android:mimeGroup="group_second"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.dynamicmime.testapp">
+ </instrumentation>
+</manifest>
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/BaseDynamicMimeTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/BaseDynamicMimeTest.java
new file mode 100644
index 0000000..ab16c74
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/BaseDynamicMimeTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.dynamicmime.testapp;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+import android.dynamicmime.testapp.util.MimeGroupOperations;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+
+/**
+ * Base class for dynamic MIME handlers feature test cases
+ */
+public abstract class BaseDynamicMimeTest extends MimeGroupOperations {
+ public BaseDynamicMimeTest(MimeGroupCommands commands, MimeGroupAssertions assertions) {
+ super(commands, assertions);
+ }
+
+ protected static Context context() {
+ return instrumentation().getContext();
+ }
+
+ protected static Context targetContext() {
+ return instrumentation().getTargetContext();
+ }
+
+ protected static Instrumentation instrumentation() {
+ return InstrumentationRegistry.getInstrumentation();
+ }
+
+ @After
+ public void tearDown() {
+ clearGroups();
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/ComplexFilterTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/ComplexFilterTest.java
new file mode 100644
index 0000000..0ad981c
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/ComplexFilterTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.dynamicmime.testapp;
+
+import static android.dynamicmime.common.Constants.ALIAS_BOTH_GROUPS;
+import static android.dynamicmime.common.Constants.ALIAS_BOTH_GROUPS_AND_TYPE;
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.GROUP_SECOND;
+import static android.dynamicmime.common.Constants.MIME_IMAGE_PNG;
+import static android.dynamicmime.common.Constants.MIME_TEXT_ANY;
+import static android.dynamicmime.common.Constants.MIME_TEXT_PLAIN;
+
+import android.dynamicmime.testapp.assertions.AssertionsByIntentResolution;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+
+import org.junit.Test;
+
+/**
+ * Test cases for:
+ * - multiple MIME groups per intent filter
+ * - MIME groups and android:mimeType attribute in one filter
+ */
+public class ComplexFilterTest extends BaseDynamicMimeTest {
+ public ComplexFilterTest() {
+ super(MimeGroupCommands.testApp(context()),
+ AssertionsByIntentResolution.testApp(context()));
+ }
+
+ @Test
+ public void testMimeGroupsNotIntersect() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG);
+ setMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+
+ assertBothGroups(MIME_IMAGE_PNG, MIME_TEXT_ANY);
+ }
+
+ @Test
+ public void testMimeGroupsIntersect() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_IMAGE_PNG);
+ setMimeGroup(GROUP_SECOND, MIME_IMAGE_PNG, MIME_TEXT_ANY);
+
+ assertBothGroups(MIME_TEXT_PLAIN, MIME_TEXT_ANY, MIME_IMAGE_PNG);
+ }
+
+ @Test
+ public void testRemoveTypeFromIntersection() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_IMAGE_PNG);
+ setMimeGroup(GROUP_SECOND, MIME_IMAGE_PNG, MIME_TEXT_ANY);
+ removeMimeTypeFromGroup(GROUP_FIRST, MIME_IMAGE_PNG);
+
+ assertBothGroups(MIME_TEXT_PLAIN, MIME_TEXT_ANY, MIME_IMAGE_PNG);
+ }
+
+ @Test
+ public void testRemoveIntersectionFromBothGroups() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_IMAGE_PNG);
+ setMimeGroup(GROUP_SECOND, MIME_IMAGE_PNG, MIME_TEXT_ANY);
+ removeMimeTypeFromGroup(GROUP_FIRST, MIME_IMAGE_PNG);
+ removeMimeTypeFromGroup(GROUP_SECOND, MIME_IMAGE_PNG);
+
+ assertBothGroups(MIME_TEXT_PLAIN, MIME_TEXT_ANY);
+ }
+
+ @Test
+ public void testClearGroupWithoutIntersection() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ setMimeGroup(GROUP_SECOND, MIME_IMAGE_PNG);
+ clearMimeGroup(GROUP_FIRST);
+
+ assertBothGroups(MIME_IMAGE_PNG);
+ }
+
+ @Test
+ public void testClearGroupWithIntersection() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_IMAGE_PNG);
+ setMimeGroup(GROUP_SECOND, MIME_IMAGE_PNG, MIME_TEXT_ANY);
+ clearMimeGroup(GROUP_FIRST);
+
+ assertBothGroups(MIME_IMAGE_PNG, MIME_TEXT_ANY);
+ }
+
+ @Test
+ public void testMimeGroupNotIntersectWithStaticType() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG);
+
+ assertGroupsAndType(MIME_IMAGE_PNG, MIME_TEXT_PLAIN);
+ }
+
+ @Test
+ public void testMimeGroupIntersectWithStaticType() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+
+ assertGroupsAndType(MIME_TEXT_PLAIN);
+ }
+
+ @Test
+ public void testRemoveStaticTypeFromMimeGroup() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG, MIME_TEXT_PLAIN);
+ removeMimeTypeFromGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+
+ assertGroupsAndType(MIME_IMAGE_PNG, MIME_TEXT_PLAIN);
+ }
+
+ @Test
+ public void testClearGroupContainsStaticType() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG, MIME_TEXT_PLAIN);
+ clearMimeGroup(GROUP_FIRST);
+
+ assertGroupsAndType(MIME_TEXT_PLAIN);
+ }
+
+ @Test
+ public void testClearGroupNotContainStaticType() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG, MIME_TEXT_ANY);
+ clearMimeGroup(GROUP_FIRST);
+
+ assertGroupsAndType(MIME_TEXT_PLAIN);
+ }
+
+ private void assertGroupsAndType(String... expectedTypes) {
+ assertMimeGroup(ALIAS_BOTH_GROUPS_AND_TYPE, expectedTypes);
+ }
+
+ private void assertBothGroups(String... expectedTypes) {
+ assertMimeGroup(ALIAS_BOTH_GROUPS, expectedTypes);
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/MultipleAppsTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/MultipleAppsTest.java
new file mode 100644
index 0000000..fa5876d
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/MultipleAppsTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.dynamicmime.testapp;
+
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.MIME_IMAGE_ANY;
+import static android.dynamicmime.common.Constants.MIME_TEXT_ANY;
+
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+import android.dynamicmime.testapp.util.MimeGroupOperations;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test cases that verify independence of MIME groups from different apps
+ */
+@RunWith(AndroidJUnit4.class)
+public class MultipleAppsTest extends BaseDynamicMimeTest {
+ public MultipleAppsTest() {
+ super(MimeGroupCommands.testApp(context()), MimeGroupAssertions.testApp(context()));
+ }
+
+ @After
+ @Override
+ public void tearDown() {
+ helperApp().clearGroups();
+ super.tearDown();
+ }
+
+ @Test
+ public void testMimeGroupsIndependentSet() {
+ helperApp().setMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ helperApp().assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+
+ assertMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+ helperApp().assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+ }
+
+ @Test
+ public void testMimeGroupsIndependentReset() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+ helperApp().setMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+ removeMimeTypeFromGroup(GROUP_FIRST, MIME_TEXT_ANY);
+ helperApp().removeMimeTypeFromGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+
+ assertMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+ helperApp().assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+ }
+
+ @Test
+ public void testMimeGroupsIndependentClear() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY, MIME_TEXT_ANY);
+ helperApp().setMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+ clearMimeGroup(GROUP_FIRST);
+
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ helperApp().assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+ helperApp().clearMimeGroup(GROUP_FIRST);
+
+ assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+ helperApp().assertMimeGroupIsEmpty(GROUP_FIRST);
+ }
+
+ /**
+ * {@link MimeGroupOperations} implementation to interact with sample app MIME groups
+ */
+ private MimeGroupOperations helperApp() {
+ return new MimeGroupOperations(MimeGroupCommands.helperApp(context()),
+ MimeGroupAssertions.helperApp(context()));
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/SingleAppTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/SingleAppTest.java
new file mode 100644
index 0000000..84eab63
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/SingleAppTest.java
@@ -0,0 +1,294 @@
+/*
+ * 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.dynamicmime.testapp;
+
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.GROUP_SECOND;
+import static android.dynamicmime.common.Constants.GROUP_UNDEFINED;
+import static android.dynamicmime.common.Constants.MIME_ANY;
+import static android.dynamicmime.common.Constants.MIME_IMAGE_ANY;
+import static android.dynamicmime.common.Constants.MIME_IMAGE_JPEG;
+import static android.dynamicmime.common.Constants.MIME_IMAGE_PNG;
+import static android.dynamicmime.common.Constants.MIME_TEXT_ANY;
+import static android.dynamicmime.common.Constants.MIME_TEXT_PLAIN;
+import static android.dynamicmime.common.Constants.MIME_TEXT_XML;
+
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test cases for single app and one MIME group per intent filter scenario
+ */
+@RunWith(AndroidJUnit4.class)
+public class SingleAppTest extends BaseDynamicMimeTest {
+ public SingleAppTest() {
+ super(MimeGroupCommands.testApp(context()), MimeGroupAssertions.testApp(context()));
+ }
+
+ @Test
+ public void testGroupWithExactType() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+
+ assertMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ assertMatchedByMimeGroup(GROUP_FIRST, MIME_TEXT_ANY, MIME_ANY);
+ assertNotMatchedByMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+ }
+
+ @Test
+ public void testGroupWithWildcardType() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+ assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+ assertMatchedByMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_ANY);
+ assertNotMatchedByMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+ }
+
+ @Test
+ public void testGroupWithAnyType() {
+ setMimeGroup(GROUP_FIRST, MIME_ANY);
+
+ assertMimeGroup(GROUP_FIRST, MIME_ANY);
+ assertMatchedByMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_TEXT_ANY, MIME_IMAGE_ANY);
+ }
+
+ @Test
+ public void testAddSimilarTypes() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ addMimeTypeToGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+ assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY, MIME_TEXT_PLAIN);
+ }
+
+ @Test
+ public void testAddDifferentTypes() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ addMimeTypeToGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+
+ assertMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY, MIME_TEXT_PLAIN);
+ }
+
+ @Test
+ public void testRemoveExactType() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ removeMimeTypeFromGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ assertNotMatchedByMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ }
+
+ @Test
+ public void testRemoveDifferentType() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ removeMimeTypeFromGroup(GROUP_FIRST, MIME_TEXT_ANY);
+
+ assertMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ }
+
+ @Test
+ public void testRemoveSameBaseType_keepSpecific() {
+ testKeepAndRemoveSimilarTypes(MIME_TEXT_PLAIN, MIME_TEXT_ANY);
+ }
+
+ @Test
+ public void testRemoveSameBaseType_keepWildcard() {
+ testKeepAndRemoveSimilarTypes(MIME_TEXT_ANY, MIME_TEXT_PLAIN);
+ }
+
+ @Test
+ public void testRemoveAnyType_keepSpecific() {
+ testKeepAndRemoveSimilarTypes(MIME_TEXT_PLAIN, MIME_ANY);
+ }
+
+ @Test
+ public void testRemoveAnyType_keepWildcard() {
+ testKeepAndRemoveSimilarTypes(MIME_ANY, MIME_TEXT_PLAIN);
+ }
+
+ @Test
+ public void testResetWithoutIntersection() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_TEXT_XML);
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_JPEG, MIME_IMAGE_PNG);
+
+ assertMimeGroup(GROUP_FIRST, MIME_IMAGE_JPEG, MIME_IMAGE_PNG);
+ }
+
+ @Test
+ public void testResetWithIntersection() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_TEXT_XML);
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_IMAGE_PNG);
+
+ assertMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_IMAGE_PNG);
+ }
+
+ @Test
+ public void testResetToEmpty() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_TEXT_XML);
+ setMimeGroup(GROUP_FIRST, (String[]) null);
+
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ }
+
+ @Test
+ public void testClear() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN, MIME_IMAGE_PNG);
+ clearMimeGroup(GROUP_FIRST);
+
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ }
+
+ @Test
+ public void testUndefinedGroupIsNullAfterAdd() {
+ addMimeTypeToGroup(GROUP_UNDEFINED, MIME_TEXT_PLAIN);
+
+ assertMimeGroupIsNull(GROUP_UNDEFINED);
+ }
+
+ @Test
+ public void testUndefinedGroupIsNullAfterSet() {
+ setMimeGroup(GROUP_UNDEFINED, MIME_TEXT_PLAIN);
+
+ assertMimeGroupIsNull(GROUP_UNDEFINED);
+ }
+
+ @Test
+ public void testUndefinedGroupIsNullAfterRemove() {
+ removeMimeTypeFromGroup(GROUP_UNDEFINED, MIME_TEXT_PLAIN);
+
+ assertMimeGroupIsNull(GROUP_UNDEFINED);
+ }
+
+ @Test
+ public void testUndefinedGroupIsNullAfterClear() {
+ clearMimeGroup(GROUP_UNDEFINED);
+
+ assertMimeGroupIsNull(GROUP_UNDEFINED);
+ }
+
+ @Test
+ public void testDefinedGroupNotNullAfterRemove() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ removeMimeTypeFromGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ }
+
+ @Test
+ public void testDefinedGroupNotNullAfterClear() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ clearMimeGroup(GROUP_FIRST);
+
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ }
+
+ @Test
+ public void testDefinedGroupNotNullAfterSetEmpty() {
+ setMimeGroup(GROUP_FIRST, (String[]) null);
+ clearMimeGroup(GROUP_FIRST);
+
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ }
+
+ @Test
+ public void testDefinedGroupNotNullAfterSetNull() {
+ setMimeGroup(GROUP_FIRST, (String[]) null);
+ clearMimeGroup(GROUP_FIRST);
+
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ }
+
+ @Test
+ public void testMimeGroupsIndependentAdd() {
+ addMimeTypeToGroup(GROUP_SECOND, MIME_TEXT_ANY);
+
+ assertMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ }
+
+ @Test
+ public void testMimeGroupsIndependentAddToBoth() {
+ addMimeTypeToGroup(GROUP_SECOND, MIME_TEXT_ANY);
+ addMimeTypeToGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+
+ assertMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+ assertMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+ }
+
+ @Test
+ public void testMimeGroupsIndependentRemove() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+ setMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+
+ removeMimeTypeFromGroup(GROUP_FIRST, MIME_TEXT_ANY);
+ removeMimeTypeFromGroup(GROUP_SECOND, MIME_IMAGE_ANY);
+
+ assertMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY);
+ assertMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+ }
+
+ @Test
+ public void testMimeGroupsIndependentClear() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY, MIME_TEXT_ANY);
+ setMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+
+ clearMimeGroup(GROUP_FIRST);
+
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ assertMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+ }
+
+ @Test
+ public void testMimeGroupsIndependentClearBoth() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_ANY, MIME_TEXT_ANY);
+ setMimeGroup(GROUP_SECOND, MIME_TEXT_ANY);
+ clearMimeGroup(GROUP_FIRST);
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+ clearMimeGroup(GROUP_SECOND);
+
+ assertMimeGroup(GROUP_FIRST, MIME_TEXT_ANY);
+ assertMimeGroupIsEmpty(GROUP_SECOND);
+ }
+
+ @Test
+ public void testMimeGroupsIndependentSet() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG, MIME_IMAGE_ANY);
+
+ assertMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG, MIME_IMAGE_ANY);
+ assertMimeGroupIsEmpty(GROUP_SECOND);
+ }
+
+ @Test
+ public void testMimeGroupsIndependentSetBoth() {
+ setMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG, MIME_IMAGE_ANY);
+ setMimeGroup(GROUP_SECOND, MIME_TEXT_PLAIN, MIME_TEXT_ANY);
+
+ assertMimeGroup(GROUP_FIRST, MIME_IMAGE_PNG, MIME_IMAGE_ANY);
+ assertMimeGroup(GROUP_SECOND, MIME_TEXT_PLAIN, MIME_TEXT_ANY);
+ }
+
+ private void testKeepAndRemoveSimilarTypes(String mimeTypeToKeep, String mimeTypeToRemove) {
+ addMimeTypeToGroup(GROUP_FIRST, mimeTypeToKeep, mimeTypeToRemove);
+ removeMimeTypeFromGroup(GROUP_FIRST, mimeTypeToRemove);
+
+ assertMimeGroup(GROUP_FIRST, mimeTypeToKeep);
+ assertMatchedByMimeGroup(GROUP_FIRST, mimeTypeToRemove);
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AppAssertionsByGroupData.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AppAssertionsByGroupData.java
new file mode 100644
index 0000000..95eaa12
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AppAssertionsByGroupData.java
@@ -0,0 +1,37 @@
+/*
+ * 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.dynamicmime.testapp.assertions;
+
+import android.content.Context;
+import android.dynamicmime.testapp.util.AppMimeGroups;
+import android.util.ArraySet;
+
+import java.util.Set;
+
+public class AppAssertionsByGroupData extends AssertionsByGroupData {
+ private final AppMimeGroups mAppMimeGroups;
+
+ AppAssertionsByGroupData(Context context, String targetPackage) {
+ mAppMimeGroups = AppMimeGroups.with(context, targetPackage);
+ }
+
+ @Override
+ protected Set<String> getMimeGroup(String mimeGroup) {
+ String[] mimeTypes = mAppMimeGroups.get(mimeGroup);
+ return mimeTypes != null ? new ArraySet<>(mimeTypes) : null;
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AssertionsByGroupData.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AssertionsByGroupData.java
new file mode 100644
index 0000000..eb74b3d
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AssertionsByGroupData.java
@@ -0,0 +1,57 @@
+/*
+ * 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.dynamicmime.testapp.assertions;
+
+import static android.dynamicmime.common.Constants.PACKAGE_HELPER_APP;
+import static android.dynamicmime.common.Constants.PACKAGE_UPDATE_APP;
+
+import android.content.Context;
+
+import org.junit.Assert;
+
+import java.util.Set;
+
+public abstract class AssertionsByGroupData extends MimeGroupAssertions {
+ @Override
+ public void assertMatchedByMimeGroup(String mimeGroup, String mimeType) {
+ // We cannot check if type is matched by group via group info from PackageManager
+ }
+
+ @Override
+ public void assertNotMatchedByMimeGroup(String mimeGroup, String mimeType) {
+ // We cannot check if type is matched by group via group info from PackageManager
+ }
+
+ @Override
+ public void assertMimeGroupInternal(String mimeGroup, Set<String> mimeTypes) {
+ Assert.assertEquals(getMimeGroup(mimeGroup), mimeTypes);
+ }
+
+ protected abstract Set<String> getMimeGroup(String mimeGroup);
+
+ public static MimeGroupAssertions testApp(Context context) {
+ return new TestAppAssertionsByGroupData(context);
+ }
+
+ public static MimeGroupAssertions helperApp(Context context) {
+ return new AppAssertionsByGroupData(context, PACKAGE_HELPER_APP);
+ }
+
+ public static MimeGroupAssertions appWithUpdates(Context context) {
+ return new AppAssertionsByGroupData(context, PACKAGE_UPDATE_APP);
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AssertionsByIntentResolution.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AssertionsByIntentResolution.java
new file mode 100644
index 0000000..13f4d4d
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/AssertionsByIntentResolution.java
@@ -0,0 +1,150 @@
+/*
+ * 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.dynamicmime.testapp.assertions;
+
+import static android.dynamicmime.common.Constants.ACTIVITY_BOTH;
+import static android.dynamicmime.common.Constants.ACTIVITY_BOTH_AND_TYPE;
+import static android.dynamicmime.common.Constants.ACTIVITY_FIRST;
+import static android.dynamicmime.common.Constants.ACTIVITY_SECOND;
+import static android.dynamicmime.common.Constants.ALIAS_BOTH_GROUPS;
+import static android.dynamicmime.common.Constants.ALIAS_BOTH_GROUPS_AND_TYPE;
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.GROUP_SECOND;
+import static android.dynamicmime.common.Constants.PACKAGE_HELPER_APP;
+import static android.dynamicmime.common.Constants.PACKAGE_TEST_APP;
+import static android.dynamicmime.common.Constants.PACKAGE_UPDATE_APP;
+import static android.dynamicmime.testapp.util.IntentsResolutionHelper.resolve;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.ArraySet;
+
+import java.util.Set;
+
+public class AssertionsByIntentResolution extends MimeGroupAssertions {
+ private final Context mContext;
+ private final String mPackageName;
+
+ private AssertionsByIntentResolution(String packageName, Context context) {
+ mContext = context;
+ mPackageName = packageName;
+ }
+
+ @Override
+ public void assertMatchedByMimeGroup(String mimeGroup, String mimeType) {
+ resolve(mContext, intentSend(mimeType))
+ .assertMatched(mPackageName, activity(mimeGroup));
+ }
+
+ @Override
+ public void assertNotMatchedByMimeGroup(String mimeGroup, String mimeType) {
+ resolve(mContext, intentSend(mimeType))
+ .assertNotMatched(mPackageName, activity(mimeGroup));
+ }
+
+ @Override
+ public void assertMimeGroupInternal(String mimeGroup, Set<String> mimeTypes) {
+ if (mimeTypes == null) {
+ // We can't distinguish empty group from null group via intent resolution
+ return;
+ }
+
+ // Check MIME group types retrieved from declared intent filter
+ assertEquals("Mismatch for " + mimeGroup + " MIME types",
+ mimeTypes, getMimeGroup(mimeGroup, mimeTypes));
+
+ // Additionally we check if all types are matched by this MIME group
+ for (String mimeType: mimeTypes) {
+ assertMatchedByMimeGroup(mimeGroup, mimeType);
+ }
+ }
+
+ public static MimeGroupAssertions testApp(Context context) {
+ return new AssertionsByIntentResolution(PACKAGE_TEST_APP, context);
+ }
+
+ public static MimeGroupAssertions helperApp(Context context) {
+ return new AssertionsByIntentResolution(PACKAGE_HELPER_APP, context);
+ }
+
+ public static MimeGroupAssertions appWithUpdates(Context context) {
+ return new AssertionsByIntentResolution(PACKAGE_UPDATE_APP, context);
+ }
+
+ private Set<String> getMimeGroup(String mimeGroup, Set<String> mimeTypes) {
+ IntentFilter filter = resolve(mContext, intentGroup(mimeGroup, mimeTypes))
+ .getAny()
+ .filter;
+
+ Set<String> actualTypes = new ArraySet<>(filter.countDataTypes());
+
+ for (int i = 0; i < filter.countDataTypes(); i++) {
+ actualTypes.add(appendWildcard(filter.getDataType(i)));
+ }
+
+ return actualTypes;
+ }
+
+ private Intent intentGroup(String mimeGroup, Set<String> mimeTypes) {
+ Intent intent = new Intent(action(mimeGroup));
+
+ if (mimeTypes != null && !mimeTypes.isEmpty()) {
+ intent.setType(mimeTypes.iterator().next());
+ }
+
+ return intent;
+ }
+
+ private static Intent intentSend(String mimeType) {
+ Intent sendIntent = new Intent();
+ sendIntent.setAction(Intent.ACTION_SEND);
+ sendIntent.setType(mimeType);
+ return sendIntent;
+ }
+
+ private static String appendWildcard(String actualType) {
+ int slashpos = actualType.indexOf('/');
+
+ if (slashpos < 0) {
+ return actualType + "/*";
+ } else {
+ return actualType;
+ }
+ }
+
+ private static String activity(String mimeGroup) {
+ switch (mimeGroup) {
+ case GROUP_FIRST:
+ return ACTIVITY_FIRST;
+ case GROUP_SECOND:
+ return ACTIVITY_SECOND;
+ case ALIAS_BOTH_GROUPS:
+ return ACTIVITY_BOTH;
+ case ALIAS_BOTH_GROUPS_AND_TYPE:
+ return ACTIVITY_BOTH_AND_TYPE;
+ default:
+ throw new IllegalArgumentException("Unexpected MIME group " + mimeGroup);
+ }
+ }
+
+ private String action(String mimeGroup) {
+ return mPackageName + ".FILTER_INFO_HOOK_"+ mimeGroup;
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/MimeGroupAssertions.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/MimeGroupAssertions.java
new file mode 100644
index 0000000..105e6eb
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/MimeGroupAssertions.java
@@ -0,0 +1,127 @@
+/*
+ * 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.dynamicmime.testapp.assertions;
+
+import android.content.Context;
+import android.util.ArraySet;
+
+import java.util.Collections;
+import java.util.Set;
+
+public abstract class MimeGroupAssertions {
+ public abstract void assertMatchedByMimeGroup(String mimeGroup, String mimeType);
+
+ public abstract void assertNotMatchedByMimeGroup(String mimeGroup, String mimeType);
+
+ public abstract void assertMimeGroupInternal(String mimeGroup, Set<String> mimeTypes);
+
+ public final void assertMimeGroup(String mimeGroup, String... mimeTypes) {
+ assertMimeGroupInternal(mimeGroup, new ArraySet<>(mimeTypes));
+ }
+
+ public final void assertMimeGroupIsEmpty(String mimeGroup) {
+ assertMimeGroupInternal(mimeGroup, Collections.emptySet());
+ }
+
+ public final void assertMimeGroupIsNull(String mimeGroup) {
+ assertMimeGroupInternal(mimeGroup, null);
+ }
+
+ public final void assertMatchedByMimeGroup(String mimeGroup, String... mimeTypes) {
+ for (String mimeType: mimeTypes) {
+ assertMatchedByMimeGroup(mimeGroup, mimeType);
+ }
+ }
+
+ public final void assertNotMatchedByMimeGroup(String mimeGroup, String... mimeTypes) {
+ for (String mimeType: mimeTypes) {
+ assertNotMatchedByMimeGroup(mimeGroup, mimeType);
+ }
+ }
+
+ public static MimeGroupAssertions testApp(Context context) {
+ return AssertionsByGroupData.testApp(context)
+ .mergeWith(AssertionsByIntentResolution.testApp(context));
+ }
+
+ public static MimeGroupAssertions helperApp(Context context) {
+ return AssertionsByGroupData.helperApp(context)
+ .mergeWith(AssertionsByIntentResolution.helperApp(context));
+ }
+
+ public static MimeGroupAssertions appWithUpdates(Context context) {
+ return AssertionsByGroupData.appWithUpdates(context)
+ .mergeWith(AssertionsByIntentResolution.appWithUpdates(context));
+ }
+
+ public static MimeGroupAssertions noOp() {
+ return new MimeGroupAssertions() {
+ @Override
+ public void assertMatchedByMimeGroup(String mimeGroup, String mimeType) {
+ }
+
+ @Override
+ public void assertNotMatchedByMimeGroup(String mimeGroup, String mimeType) {
+ }
+
+ @Override
+ public void assertMimeGroupInternal(String mimeGroup, Set<String> mimeTypes) {
+ }
+ };
+ }
+
+ public static MimeGroupAssertions notUsed() {
+ return new MimeGroupAssertions() {
+ @Override
+ public void assertMatchedByMimeGroup(String group, String type) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void assertNotMatchedByMimeGroup(String group, String type) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void assertMimeGroupInternal(String group, Set<String> types) {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ protected final MimeGroupAssertions mergeWith(MimeGroupAssertions other) {
+ return new MimeGroupAssertions() {
+ @Override
+ public void assertMatchedByMimeGroup(String mimeGroup, String mimeType) {
+ other.assertMatchedByMimeGroup(mimeGroup, mimeType);
+ MimeGroupAssertions.this.assertMatchedByMimeGroup(mimeGroup, mimeType);
+ }
+
+ @Override
+ public void assertNotMatchedByMimeGroup(String mimeGroup, String mimeType) {
+ other.assertNotMatchedByMimeGroup(mimeGroup, mimeType);
+ MimeGroupAssertions.this.assertNotMatchedByMimeGroup(mimeGroup, mimeType);
+ }
+
+ @Override
+ public void assertMimeGroupInternal(String mimeGroup, Set<String> mimeTypes) {
+ other.assertMimeGroupInternal(mimeGroup, mimeTypes);
+ MimeGroupAssertions.this.assertMimeGroupInternal(mimeGroup, mimeTypes);
+ }
+ };
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/TestAppAssertionsByGroupData.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/TestAppAssertionsByGroupData.java
new file mode 100644
index 0000000..017fa44
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/assertions/TestAppAssertionsByGroupData.java
@@ -0,0 +1,34 @@
+/*
+ * 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.dynamicmime.testapp.assertions;
+
+import android.content.Context;
+
+import java.util.Set;
+
+class TestAppAssertionsByGroupData extends AssertionsByGroupData {
+ private final Context mContext;
+
+ TestAppAssertionsByGroupData(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ protected Set<String> getMimeGroup(String mimeGroup) {
+ return mContext.getPackageManager().getMimeGroup(mimeGroup);
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/AppCommands.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/AppCommands.java
new file mode 100644
index 0000000..83400f4
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/AppCommands.java
@@ -0,0 +1,49 @@
+/*
+ * 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.dynamicmime.testapp.commands;
+
+import android.content.Context;
+import android.dynamicmime.testapp.util.AppMimeGroups;
+import android.util.ArraySet;
+
+import androidx.annotation.Nullable;
+
+import java.util.Set;
+
+public class AppCommands implements MimeGroupCommands {
+ private final AppMimeGroups mAppMimeGroups;
+
+ AppCommands(Context context, String targetPackage) {
+ mAppMimeGroups = AppMimeGroups.with(context, targetPackage);
+ }
+
+ @Override
+ public void setMimeGroup(String mimeGroup, Set<String> mimeTypes) {
+ mAppMimeGroups.set(mimeGroup, mimeTypes.toArray(new String[0]));
+ }
+
+ @Override
+ public void clearMimeGroup(String mimeGroup) {
+ mAppMimeGroups.clear(mimeGroup);
+ }
+
+ @Nullable
+ @Override
+ public Set<String> getMimeGroupInternal(String mimeGroup) {
+ return new ArraySet<>(mAppMimeGroups.get(mimeGroup));
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/MimeGroupCommands.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/MimeGroupCommands.java
new file mode 100644
index 0000000..27a9cb2
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/MimeGroupCommands.java
@@ -0,0 +1,102 @@
+/*
+ * 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.dynamicmime.testapp.commands;
+
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.GROUP_SECOND;
+import static android.dynamicmime.common.Constants.GROUP_THIRD;
+import static android.dynamicmime.common.Constants.GROUP_UNDEFINED;
+import static android.dynamicmime.common.Constants.PACKAGE_HELPER_APP;
+import static android.dynamicmime.common.Constants.PACKAGE_PREFERRED_APP;
+import static android.dynamicmime.common.Constants.PACKAGE_UPDATE_APP;
+
+import android.content.Context;
+import android.util.ArraySet;
+
+import androidx.annotation.Nullable;
+
+import java.util.Set;
+
+public interface MimeGroupCommands {
+ void setMimeGroup(String mimeGroup, Set<String> mimeTypes);
+ void clearMimeGroup(String mimeGroup);
+
+ @Nullable
+ Set<String> getMimeGroupInternal(String mimeGroup);
+
+ default void addMimeTypeToGroup(String mimeGroup, String... mimeTypes) {
+ ArraySet<String> typesToAdd = new ArraySet<>(mimeTypes);
+
+ Set<String> newMimeTypes = new ArraySet<>(getMimeGroupInternal(mimeGroup));
+ newMimeTypes.addAll(typesToAdd);
+
+ setMimeGroup(mimeGroup, newMimeTypes);
+ }
+
+ default void removeMimeTypeFromGroup(String mimeGroup, String... mimeTypes) {
+ ArraySet<String> typesToRemove = new ArraySet<>(mimeTypes);
+
+ Set<String> newMimeTypes = new ArraySet<>(getMimeGroupInternal(mimeGroup));
+ newMimeTypes.removeAll(typesToRemove);
+
+ setMimeGroup(mimeGroup, newMimeTypes);
+ }
+
+ default void setMimeGroup(String mimeGroup, String... mimeTypes) {
+ setMimeGroup(mimeGroup, new ArraySet<>(mimeTypes));
+ }
+
+ default void clearGroups() {
+ clearMimeGroup(GROUP_FIRST);
+ clearMimeGroup(GROUP_SECOND);
+ clearMimeGroup(GROUP_THIRD);
+ clearMimeGroup(GROUP_UNDEFINED);
+ }
+
+ static MimeGroupCommands testApp(Context context) {
+ return new TestAppCommands(context);
+ }
+
+ static MimeGroupCommands helperApp(Context context) {
+ return new AppCommands(context, PACKAGE_HELPER_APP);
+ }
+
+ static MimeGroupCommands appWithUpdates(Context context) {
+ return new AppCommands(context, PACKAGE_UPDATE_APP);
+ }
+
+ static MimeGroupCommands preferredApp(Context context) {
+ return new AppCommands(context, PACKAGE_PREFERRED_APP);
+ }
+
+ static MimeGroupCommands noOp() {
+ return new MimeGroupCommands() {
+ @Override
+ public void setMimeGroup(String mimeGroup, Set<String> mimeTypes) {
+ }
+
+ @Override
+ public void clearMimeGroup(String mimeGroup) {
+ }
+
+ @Override
+ public Set<String> getMimeGroupInternal(String mimeGroup) {
+ return null;
+ }
+ };
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/TestAppCommands.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/TestAppCommands.java
new file mode 100644
index 0000000..82dcb8853
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/commands/TestAppCommands.java
@@ -0,0 +1,48 @@
+/*
+ * 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.dynamicmime.testapp.commands;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import androidx.annotation.Nullable;
+
+import java.util.Set;
+
+public class TestAppCommands implements MimeGroupCommands {
+ private PackageManager mPM;
+
+ TestAppCommands(Context context) {
+ mPM = context.getPackageManager();
+ }
+
+ @Override
+ public void setMimeGroup(String mimeGroup, Set<String> mimeTypes) {
+ mPM.setMimeGroup(mimeGroup, mimeTypes);
+ }
+
+ @Override
+ public void clearMimeGroup(String mimeGroup) {
+ mPM.clearMimeGroup(mimeGroup);
+ }
+
+ @Nullable
+ @Override
+ public Set<String> getMimeGroupInternal(String mimeGroup) {
+ return mPM.getMimeGroup(mimeGroup);
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/preferred/PreferredActivitiesTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/preferred/PreferredActivitiesTest.java
new file mode 100644
index 0000000..5ef052ef
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/preferred/PreferredActivitiesTest.java
@@ -0,0 +1,323 @@
+/*
+ * 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.dynamicmime.testapp.preferred;
+
+import static android.dynamicmime.common.Constants.ACTIVITY_BOTH;
+import static android.dynamicmime.common.Constants.ACTIVITY_FIRST;
+import static android.dynamicmime.common.Constants.APK_PREFERRED_APP;
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.GROUP_SECOND;
+import static android.dynamicmime.common.Constants.GROUP_THIRD;
+import static android.dynamicmime.common.Constants.MIME_IMAGE_PNG;
+import static android.dynamicmime.common.Constants.MIME_TEXT_PLAIN;
+import static android.dynamicmime.common.Constants.PACKAGE_PREFERRED_APP;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.content.Intent;
+import android.dynamicmime.testapp.BaseDynamicMimeTest;
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+import android.dynamicmime.testapp.util.Utils;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.Direction;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class PreferredActivitiesTest extends BaseDynamicMimeTest {
+ private static final BySelector BUTTON_ALWAYS = By.res("android:id/button_always");
+ private static final BySelector RESOLVER_DIALOG = By.res("android:id/contentPanel");
+
+ private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
+
+ private TestStrategy mTest;
+
+ public PreferredActivitiesTest() {
+ super(MimeGroupCommands.preferredApp(context()), MimeGroupAssertions.notUsed());
+ }
+
+ @Before
+ public void setUp() {
+ Utils.installApk(APK_PREFERRED_APP);
+ }
+
+ @After
+ public void tearDown() {
+ super.tearDown();
+ Utils.uninstallApp(PACKAGE_PREFERRED_APP);
+ }
+
+ @Test
+ public void testRemoveFromGroup() {
+ setStrategyAndRun(new TestStrategy() {
+ @Override
+ public void prepareMimeGroups() {
+ addMimeTypeToGroup(GROUP_FIRST, mimeType());
+ }
+
+ @Override
+ public void changeMimeGroups() {
+ removeMimeTypeFromGroup(GROUP_FIRST, mimeType());
+ }
+
+ @Override
+ public String preferredActivity() {
+ return ACTIVITY_FIRST;
+ }
+ });
+ }
+
+ @Test
+ public void testAddToGroup() {
+ setStrategyAndRun(new TestStrategy() {
+ @Override
+ public void prepareMimeGroups() {
+ addMimeTypeToGroup(GROUP_FIRST, mimeType());
+ }
+
+ @Override
+ public void changeMimeGroups() {
+ addMimeTypeToGroup(GROUP_FIRST, MIME_IMAGE_PNG);
+ }
+
+ @Override
+ public void revertMimeGroupsChange() {
+ removeMimeTypeFromGroup(GROUP_FIRST, MIME_IMAGE_PNG);
+ }
+
+ @Override
+ public String preferredActivity() {
+ return ACTIVITY_FIRST;
+ }
+ });
+ }
+
+ @Test
+ public void testClearGroup() {
+ setStrategyAndRun(new TestStrategy() {
+ @Override
+ public void prepareMimeGroups() {
+ addMimeTypeToGroup(GROUP_FIRST, mimeType());
+ }
+
+ @Override
+ public void changeMimeGroups() {
+ clearMimeGroup(GROUP_FIRST);
+ }
+
+ @Override
+ public String preferredActivity() {
+ return ACTIVITY_FIRST;
+ }
+ });
+ }
+
+ @Test
+ public void testModifyGroupWithoutActualGroupChanges() {
+ setStrategyAndRun(new TestStrategy() {
+ @Override
+ public void prepareMimeGroups() {
+ addMimeTypeToGroup(GROUP_FIRST, mimeType());
+ }
+
+ @Override
+ public void changeMimeGroups() {
+ addMimeTypeToGroup(GROUP_FIRST, mimeType());
+ }
+
+ @Override
+ public String preferredActivity() {
+ return ACTIVITY_FIRST;
+ }
+
+ @Override
+ public boolean isActivityPreferredAfterRevert() {
+ return true;
+ }
+
+ @Override
+ public boolean isActivityPreferredAfterChange() {
+ return true;
+ }
+ });
+ }
+
+ @Test
+ public void testModifyGroupWithoutActualIntentFilterChanges() {
+ setStrategyAndRun(new TestStrategy() {
+ @Override
+ public void prepareMimeGroups() {
+ addMimeTypeToGroup(GROUP_THIRD, mimeType());
+ addMimeTypeToGroup(GROUP_SECOND, mimeType());
+ }
+
+ @Override
+ public void changeMimeGroups() {
+ removeMimeTypeFromGroup(GROUP_THIRD, mimeType());
+ }
+
+ @Override
+ public void revertMimeGroupsChange() {
+ addMimeTypeToGroup(GROUP_THIRD, mimeType());
+ }
+
+ @Override
+ public String preferredActivity() {
+ return ACTIVITY_BOTH;
+ }
+
+ @Override
+ public boolean isActivityPreferredAfterRevert() {
+ return true;
+ }
+
+ @Override
+ public boolean isActivityPreferredAfterChange() {
+ return true;
+ }
+ });
+ }
+
+ private void setStrategyAndRun(TestStrategy test) {
+ mTest = test;
+ runTest();
+ }
+
+ private void runTest() {
+ mTest.prepareMimeGroups();
+ setPreferredActivity();
+
+ mTest.changeMimeGroups();
+ checkPreferredActivityAfterChange();
+
+ mTest.revertMimeGroupsChange();
+ checkPreferredActivityAfterRevert();
+
+ getUiDevice().pressHome();
+ }
+
+ private void setPreferredActivity() {
+ triggerResolutionDialog();
+
+ verifyDialogIsShown(true);
+
+ chooseActivity("TestApp" + mTest.preferredActivity());
+ }
+
+ private void checkPreferredActivityAfterChange() {
+ checkPreferredActivity(mTest.isActivityPreferredAfterChange());
+ }
+
+ private void checkPreferredActivityAfterRevert() {
+ checkPreferredActivity(mTest.isActivityPreferredAfterRevert());
+ }
+
+ private void checkPreferredActivity(boolean hasPreferredActivity) {
+ triggerResolutionDialog();
+ verifyResolutionDialog(hasPreferredActivity);
+ }
+
+ private void triggerResolutionDialog() {
+ getUiDevice().pressHome();
+ sendIntent(mTest.mimeType());
+ }
+
+ private void verifyResolutionDialog(boolean shouldLaunchActivity) {
+ verifyDialogIsShown(!shouldLaunchActivity);
+ getUiDevice().pressBack();
+ }
+
+ private void verifyDialogIsShown(boolean shouldBeShown) {
+ UiObject2 buttonAlways = getUiDevice().wait(Until.findObject(BUTTON_ALWAYS), TIMEOUT);
+
+ if (shouldBeShown) {
+ assertNotNull(buttonAlways);
+ } else {
+ assertNull(buttonAlways);
+ }
+ }
+
+ private void chooseActivity(String label) {
+ findActivityInDialog(label).click();
+ chooseUseAlways();
+
+ getUiDevice().pressBack();
+ }
+
+ private UiObject2 findActivityInDialog(String label) {
+ getUiDevice()
+ .wait(Until.findObject(RESOLVER_DIALOG), TIMEOUT)
+ .swipe(Direction.UP, 1f);
+
+ return getUiDevice().findObject(By.text(label));
+ }
+
+ private void chooseUseAlways() {
+ getUiDevice()
+ .findObject(BUTTON_ALWAYS)
+ .click();
+ }
+
+ private interface TestStrategy {
+ void prepareMimeGroups();
+
+ void changeMimeGroups();
+
+ default void revertMimeGroupsChange() {
+ prepareMimeGroups();
+ }
+
+ default String mimeType() {
+ return MIME_TEXT_PLAIN;
+ }
+
+ String preferredActivity();
+
+ default boolean isActivityPreferredAfterChange() {
+ return false;
+ }
+
+ default boolean isActivityPreferredAfterRevert() {
+ return false;
+ }
+ }
+
+ private static UiDevice getUiDevice() {
+ return UiDevice.getInstance(instrumentation());
+ }
+
+ private static void sendIntent(String mimeType) {
+ Intent sendIntent = new Intent();
+ sendIntent.setAction(Intent.ACTION_SEND);
+ sendIntent.setType(mimeType);
+ sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ targetContext().startActivity(sendIntent, null);
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PostRebootComplexFilterTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PostRebootComplexFilterTest.java
new file mode 100644
index 0000000..19f8bab
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PostRebootComplexFilterTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.dynamicmime.testapp.reboot;
+
+import android.dynamicmime.testapp.ComplexFilterTest;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+/**
+ * Post-reboot part of {@link ComplexFilterTest}
+ *
+ * {@link MimeGroupCommands} are disabled during test body execution
+ */
+@RunWith(AndroidJUnit4.class)
+public class PostRebootComplexFilterTest extends ComplexFilterTest {
+ private MimeGroupCommands mCommands;
+
+ public PostRebootComplexFilterTest() {
+ super();
+ mCommands = getCommands();
+ }
+
+ @Before
+ public void setUp() {
+ setCommands(MimeGroupCommands.noOp());
+ }
+
+ @After
+ @Override
+ public void tearDown() {
+ setCommands(mCommands);
+ super.tearDown();
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PostRebootSingleAppTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PostRebootSingleAppTest.java
new file mode 100644
index 0000000..3e4c86d
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PostRebootSingleAppTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.dynamicmime.testapp.reboot;
+
+import android.dynamicmime.testapp.SingleAppTest;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+/**
+ * Post-reboot part of {@link SingleAppTest}
+ *
+ * {@link MimeGroupCommands} are disabled during test body execution
+ */
+@RunWith(AndroidJUnit4.class)
+public class PostRebootSingleAppTest extends SingleAppTest {
+ private MimeGroupCommands mCommands;
+
+ public PostRebootSingleAppTest() {
+ super();
+ mCommands = getCommands();
+ }
+
+ @Before
+ public void setUp() {
+ setCommands(MimeGroupCommands.noOp());
+ }
+
+ @After
+ @Override
+ public void tearDown() {
+ setCommands(mCommands);
+ super.tearDown();
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PreRebootComplexFilterTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PreRebootComplexFilterTest.java
new file mode 100644
index 0000000..9f290e3
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PreRebootComplexFilterTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.dynamicmime.testapp.reboot;
+
+import android.dynamicmime.testapp.ComplexFilterTest;
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+/**
+ * Pre-reboot part of {@link ComplexFilterTest}
+ *
+ * {@link MimeGroupAssertions} are disabled
+ * {@link MimeGroupCommands} are disabled during tear down
+ */
+@RunWith(AndroidJUnit4.class)
+public class PreRebootComplexFilterTest extends ComplexFilterTest {
+ private MimeGroupCommands mCommands;
+
+ public PreRebootComplexFilterTest() {
+ super();
+ setAssertions(MimeGroupAssertions.noOp());
+ mCommands = getCommands();
+ }
+
+ @Before
+ public void setUp() {
+ setCommands(mCommands);
+ }
+
+ @After
+ @Override
+ public void tearDown() {
+ setCommands(MimeGroupCommands.noOp());
+ super.tearDown();
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PreRebootSingleAppTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PreRebootSingleAppTest.java
new file mode 100644
index 0000000..5a65df7
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/reboot/PreRebootSingleAppTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.dynamicmime.testapp.reboot;
+
+import android.dynamicmime.testapp.SingleAppTest;
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+/**
+ * Pre-reboot part of {@link SingleAppTest}
+ *
+ * {@link MimeGroupAssertions} are disabled
+ * {@link MimeGroupCommands} are disabled during tear down
+ */
+@RunWith(AndroidJUnit4.class)
+public class PreRebootSingleAppTest extends SingleAppTest {
+ private MimeGroupCommands mCommands;
+
+ public PreRebootSingleAppTest() {
+ super();
+ setAssertions(MimeGroupAssertions.noOp());
+ mCommands = getCommands();
+ }
+
+ @Before
+ public void setUp() {
+ setCommands(mCommands);
+ }
+
+ @After
+ @Override
+ public void tearDown() {
+ setCommands(MimeGroupCommands.noOp());
+ super.tearDown();
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/BaseUpdateTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/BaseUpdateTest.java
new file mode 100644
index 0000000..43f590a
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/BaseUpdateTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.dynamicmime.testapp.update;
+
+import static android.dynamicmime.common.Constants.PACKAGE_UPDATE_APP;
+
+import android.dynamicmime.testapp.BaseDynamicMimeTest;
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+import android.dynamicmime.testapp.util.Utils;
+
+import org.junit.After;
+import org.junit.Before;
+
+import java.io.IOException;
+
+abstract class BaseUpdateTest extends BaseDynamicMimeTest {
+ BaseUpdateTest() {
+ super(MimeGroupCommands.appWithUpdates(context()),
+ MimeGroupAssertions.appWithUpdates(context()));
+ }
+
+ @Before
+ public void setUp() throws IOException {
+ Utils.installApk(installApkPath());
+ }
+
+ @After
+ @Override
+ public void tearDown() {
+ super.tearDown();
+ Utils.uninstallApp(PACKAGE_UPDATE_APP);
+ }
+
+ void updateApp() {
+ Utils.updateApp(updateApkPath());
+ }
+
+ abstract String installApkPath();
+ abstract String updateApkPath();
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/ChangedGroupsTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/ChangedGroupsTest.java
new file mode 100644
index 0000000..b209699
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/ChangedGroupsTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.dynamicmime.testapp.update;
+
+import static android.dynamicmime.common.Constants.APK_FIRST_GROUP;
+import static android.dynamicmime.common.Constants.APK_SECOND_GROUP;
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.GROUP_SECOND;
+import static android.dynamicmime.common.Constants.GROUP_UNDEFINED;
+import static android.dynamicmime.common.Constants.MIME_TEXT_PLAIN;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test cases for app update with MIME groups changes
+ */
+@RunWith(AndroidJUnit4.class)
+public class ChangedGroupsTest extends BaseUpdateTest {
+ @Test
+ public void testUpdateRemoveEmptyGroup() {
+ assertMimeGroupIsEmpty(GROUP_FIRST);
+ assertMimeGroupIsNull(GROUP_SECOND);
+ assertMimeGroupIsNull(GROUP_UNDEFINED);
+
+ updateApp();
+
+ assertMimeGroupIsNull(GROUP_FIRST);
+ assertMimeGroupIsEmpty(GROUP_SECOND);
+ assertMimeGroupIsNull(GROUP_UNDEFINED);
+ }
+
+ @Test
+ public void testUpdateRemoveNonEmptyGroup() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+
+ assertMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ assertMimeGroupIsNull(GROUP_SECOND);
+ assertMimeGroupIsNull(GROUP_UNDEFINED);
+
+ updateApp();
+
+ assertMimeGroupIsNull(GROUP_FIRST);
+ assertMimeGroupIsEmpty(GROUP_SECOND);
+ assertMimeGroupIsNull(GROUP_UNDEFINED);
+ }
+
+ @Override
+ String installApkPath() {
+ return APK_FIRST_GROUP;
+ }
+
+ @Override
+ String updateApkPath() {
+ return APK_SECOND_GROUP;
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/SameGroupsTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/SameGroupsTest.java
new file mode 100644
index 0000000..0c3349a
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/update/SameGroupsTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.dynamicmime.testapp.update;
+
+import static android.dynamicmime.common.Constants.APK_BOTH_GROUPS;
+import static android.dynamicmime.common.Constants.GROUP_FIRST;
+import static android.dynamicmime.common.Constants.GROUP_SECOND;
+import static android.dynamicmime.common.Constants.GROUP_UNDEFINED;
+import static android.dynamicmime.common.Constants.MIME_TEXT_PLAIN;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test cases for app update, without MIME groups changes
+ */
+@RunWith(AndroidJUnit4.class)
+public class SameGroupsTest extends BaseUpdateTest {
+ @Test
+ public void testUpdate() {
+ setMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+
+ updateApp();
+
+ assertMimeGroup(GROUP_FIRST, MIME_TEXT_PLAIN);
+ assertMimeGroupIsEmpty(GROUP_SECOND);
+ assertMimeGroupIsNull(GROUP_UNDEFINED);
+ }
+
+ @Override
+ String installApkPath() {
+ return APK_BOTH_GROUPS;
+ }
+
+ @Override
+ String updateApkPath() {
+ return APK_BOTH_GROUPS;
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/AppMimeGroups.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/AppMimeGroups.java
new file mode 100644
index 0000000..b034946
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/AppMimeGroups.java
@@ -0,0 +1,99 @@
+/*
+ * 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.dynamicmime.testapp.util;
+
+import static android.dynamicmime.common.Constants.ACTION_REQUEST;
+import static android.dynamicmime.common.Constants.ACTION_RESPONSE;
+import static android.dynamicmime.common.Constants.EXTRA_GROUP;
+import static android.dynamicmime.common.Constants.EXTRA_MIMES;
+import static android.dynamicmime.common.Constants.EXTRA_REQUEST;
+import static android.dynamicmime.common.Constants.EXTRA_RESPONSE;
+import static android.dynamicmime.common.Constants.REQUEST_CLEAR;
+import static android.dynamicmime.common.Constants.REQUEST_GET;
+import static android.dynamicmime.common.Constants.REQUEST_SET;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.content.Context;
+import android.content.Intent;
+
+import com.android.compatibility.common.util.BlockingBroadcastReceiver;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Class that dispatches MIME groups related commands to another app
+ * Target app should register {@link android.dynamicmime.app.AppMimeGroupsReceiver}
+ * to handler requests
+ */
+public class AppMimeGroups {
+ private final Context mContext;
+ private final String mTargetPackage;
+
+ private AppMimeGroups(Context context, String targetPackage) {
+ mContext = context;
+ mTargetPackage = targetPackage;
+ }
+
+ public static AppMimeGroups with(Context context, String targetPackage) {
+ return new AppMimeGroups(context, targetPackage);
+ }
+
+ public void set(String mimeGroup, String[] mimeTypes) {
+ sendRequestAndAwait(mimeGroup, REQUEST_SET, mimeTypes);
+ }
+
+ public void clear(String mimeGroup) {
+ sendRequestAndAwait(mimeGroup, REQUEST_CLEAR);
+ }
+
+ public String[] get(String mimeGroup) {
+ return sendRequestAndAwait(mimeGroup, REQUEST_GET)
+ .getStringArrayExtra(EXTRA_RESPONSE);
+ }
+
+ private Intent sendRequestAndAwait(String mimeGroup, int requestSet) {
+ return sendRequestAndAwait(mimeGroup, requestSet, null);
+ }
+
+ private Intent sendRequestAndAwait(String mimeGroup, int request, String[] mimeTypes) {
+ BlockingBroadcastReceiver receiver =
+ new BlockingBroadcastReceiver(mContext, ACTION_RESPONSE);
+ receiver.register();
+
+ mContext.sendBroadcast(getRequestIntent(mimeGroup, mimeTypes, request));
+
+ Intent response = receiver.awaitForBroadcast(TimeUnit.SECONDS.toMillis(5L));
+ assertNotNull(response);
+
+ mContext.unregisterReceiver(receiver);
+
+ return response;
+ }
+
+ private Intent getRequestIntent(String mimeGroup, String[] mimeTypes, int request) {
+ Intent intent = new Intent(ACTION_REQUEST);
+ intent.setPackage(mTargetPackage);
+ intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+
+ intent.putExtra(EXTRA_GROUP, mimeGroup);
+ intent.putExtra(EXTRA_MIMES, mimeTypes);
+ intent.putExtra(EXTRA_REQUEST, request);
+
+ return intent;
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/IntentsResolutionHelper.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/IntentsResolutionHelper.java
new file mode 100644
index 0000000..fceb48f
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/IntentsResolutionHelper.java
@@ -0,0 +1,91 @@
+/*
+ * 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.dynamicmime.testapp.util;
+
+import static android.dynamicmime.common.Constants.PACKAGE_ACTIVITIES;
+
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.hasItem;
+import static org.junit.Assert.assertThat;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+
+import java.util.List;
+
+/**
+ * Helper class for {@link PackageManager#queryIntentActivities} results verification
+ */
+public class IntentsResolutionHelper {
+ private final List<ResolveInfo> resolvedActivities;
+ private final Intent mIntent;
+
+ public static IntentsResolutionHelper resolve(Context context, Intent intent) {
+ return new IntentsResolutionHelper(context, intent);
+ }
+
+ private IntentsResolutionHelper(Context context, Intent intent) {
+ mIntent = intent;
+ this.resolvedActivities = context.getPackageManager().queryIntentActivities(mIntent, PackageManager.GET_RESOLVED_FILTER);
+ }
+
+ public void assertMatched(String packageName, String simpleClassName) {
+ assertThat("Missing expected match for " + mIntent, resolvedActivities,
+ hasItem(activity(packageName, simpleClassName)));
+ }
+
+ public void assertNotMatched(String packageName, String simpleClassName) {
+ assertThat("Unexpected match for " + mIntent, resolvedActivities,
+ not(hasItem(activity(packageName, simpleClassName))));
+ }
+
+ public ResolveInfo getAny() {
+ assertThat("Missing match for " + mIntent, resolvedActivities, not(empty()));
+
+ return resolvedActivities
+ .stream()
+ .findAny()
+ .orElse(null);
+ }
+
+ private Matcher<ResolveInfo> activity(String packageName, String simpleClassName) {
+ return new BaseMatcher<ResolveInfo>() {
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("packageName=" + packageName + ", simpleClassName=" + simpleClassName);
+ }
+
+ @Override
+ public boolean matches(Object item) {
+ if (item == null) {
+ return false;
+ }
+ ResolveInfo info = (ResolveInfo) item;
+
+ return info.activityInfo.packageName.equals(packageName)
+ && info.activityInfo.name.equals(PACKAGE_ACTIVITIES + simpleClassName);
+ }
+ };
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/MimeGroupOperations.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/MimeGroupOperations.java
new file mode 100644
index 0000000..ebd4cc4
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/MimeGroupOperations.java
@@ -0,0 +1,85 @@
+/*
+ * 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.dynamicmime.testapp.util;
+
+import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
+import android.dynamicmime.testapp.commands.MimeGroupCommands;
+
+import androidx.annotation.Nullable;
+
+import java.util.Set;
+
+/**
+ * Base class for commands/queries related to MIME groups
+ * Allows independent implementations for commands and assertions
+ */
+public class MimeGroupOperations extends MimeGroupAssertions implements MimeGroupCommands {
+ private MimeGroupCommands mCommands;
+ private MimeGroupAssertions mAssertions;
+
+ public MimeGroupOperations(MimeGroupCommands commands, MimeGroupAssertions assertions) {
+ mCommands = commands;
+ mAssertions = assertions;
+ }
+
+ @Override
+ public final void assertMatchedByMimeGroup(String mimeGroup, String mimeType) {
+ mAssertions.assertMatchedByMimeGroup(mimeGroup, mimeType);
+ }
+
+ @Override
+ public final void assertNotMatchedByMimeGroup(String mimeGroup, String mimeType) {
+ mAssertions.assertNotMatchedByMimeGroup(mimeGroup, mimeType);
+ }
+
+ @Override
+ public final void assertMimeGroupInternal(String mimeGroup, Set<String> mimeTypes) {
+ mAssertions.assertMimeGroupInternal(mimeGroup, mimeTypes);
+ }
+
+ @Override
+ public void setMimeGroup(String mimeGroup, Set<String> mimeTypes) {
+ mCommands.setMimeGroup(mimeGroup, mimeTypes);
+ }
+
+ @Override
+ public final void clearMimeGroup(String mimeGroup) {
+ mCommands.clearMimeGroup(mimeGroup);
+ }
+
+ @Nullable
+ @Override
+ public Set<String> getMimeGroupInternal(String mimeGroup) {
+ return mCommands.getMimeGroupInternal(mimeGroup);
+ }
+
+ public MimeGroupCommands getCommands() {
+ return mCommands;
+ }
+
+ public MimeGroupAssertions getAssertions() {
+ return mAssertions;
+ }
+
+ public void setCommands(MimeGroupCommands commands) {
+ mCommands = commands;
+ }
+
+ public void setAssertions(MimeGroupAssertions assertions) {
+ mAssertions = assertions;
+ }
+}
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/Utils.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/Utils.java
new file mode 100644
index 0000000..fce0c98f
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/util/Utils.java
@@ -0,0 +1,72 @@
+/*
+ * 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.dynamicmime.testapp.util;
+
+import static org.junit.Assert.assertTrue;
+
+import android.os.ParcelFileDescriptor;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class Utils {
+ public static void installApk(String pathToApk) {
+ executeShellCommand("pm install -t " + pathToApk);
+ }
+
+ public static void updateApp(String pathToApk) {
+ executeShellCommand("pm install -t -r " + pathToApk);
+ }
+
+ public static void uninstallApp(String appPackage) {
+ executeShellCommand("pm uninstall " + appPackage);
+ }
+
+ private static void executeShellCommand(String command) {
+ ParcelFileDescriptor pfd = InstrumentationRegistry
+ .getInstrumentation()
+ .getUiAutomation()
+ .executeShellCommand(command);
+ InputStream is = new FileInputStream(pfd.getFileDescriptor());
+
+ try {
+ readFully(is);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static void readFully(InputStream in) throws IOException {
+ try {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int count;
+ while ((count = in.read(buffer)) != -1) {
+ bytes.write(buffer, 0, count);
+ }
+
+ String result = new String(bytes.toByteArray());
+ assertTrue(result, result.isEmpty() || result.contains("Success"));
+ } finally {
+ in.close();
+ }
+ }
+}
diff --git a/hostsidetests/stagedinstall/app/AndroidManifest.xml b/hostsidetests/stagedinstall/app/AndroidManifest.xml
index 067e1cc..bc8bb0b 100644
--- a/hostsidetests/stagedinstall/app/AndroidManifest.xml
+++ b/hostsidetests/stagedinstall/app/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.tests.stagedinstall" >
- <uses-permission android:name="QUERY_ALL_PACKAGES" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application>
<receiver android:name="com.android.cts.install.lib.LocalIntentSender"
android:exported="true" />
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
index d0a05f4..b3ab63c 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
@@ -21,6 +21,7 @@
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.content.Intent;
import android.graphics.Color;
@@ -44,6 +45,7 @@
public static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
public static final String ACTION_SHOW_NOTIFICATION = "action.show_notification";
public static final String ACTION_CRASH = "action.crash";
+ public static final String ACTION_CREATE_CHANNEL_GROUP = "action.create_channel_group";
public static final int SLEEP_OF_ACTION_SLEEP_WHILE_TOP = 2_000;
public static final int SLEEP_OF_ACTION_SHOW_APPLICATION_OVERLAY = 2_000;
@@ -81,6 +83,9 @@
case ACTION_CRASH:
doCrash();
break;
+ case ACTION_CREATE_CHANNEL_GROUP:
+ doCreateChannelGroup();
+ break;
default:
Log.e(TAG, "Intent had invalid action " + action);
finish();
@@ -135,15 +140,17 @@
private void doShowNotification() {
final int notificationId = R.layout.activity_main;
- final String notificationChannel = "StatsdCtsChannel";
+ final String notificationChannelId = "StatsdCtsChannel";
NotificationManager nm = getSystemService(NotificationManager.class);
- nm.createNotificationChannel(new NotificationChannel(notificationChannel, "Statsd Cts",
- NotificationManager.IMPORTANCE_DEFAULT));
+ NotificationChannel channel = new NotificationChannel(notificationChannelId, "Statsd Cts",
+ NotificationManager.IMPORTANCE_DEFAULT);
+ channel.setDescription("Statsd Cts Channel");
+ nm.createNotificationChannel(channel);
nm.notify(
notificationId,
- new Notification.Builder(this, notificationChannel)
+ new Notification.Builder(this, notificationChannelId)
.setSmallIcon(android.R.drawable.stat_notify_chat)
.setContentTitle("StatsdCts")
.setContentText("StatsdCts")
@@ -152,6 +159,15 @@
finish();
}
+ private void doCreateChannelGroup() {
+ NotificationManager nm = getSystemService(NotificationManager.class);
+ NotificationChannelGroup channelGroup = new NotificationChannelGroup("StatsdCtsGroup",
+ "Statsd Cts Group");
+ channelGroup.setDescription("StatsdCtsGroup Description");
+ nm.createNotificationChannelGroup(channelGroup);
+ finish();
+ }
+
@SuppressWarnings("ConstantOverflow")
private void doCrash() {
Log.e(TAG, "About to crash the app with 1/0 " + (long)1/0);
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index 5e9e2ba..1ac8a18 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -48,6 +48,9 @@
import com.android.os.AtomsProto.LmkKillOccurred;
import com.android.os.AtomsProto.MediaCodecStateChanged;
import com.android.os.AtomsProto.OverlayStateChanged;
+import com.android.os.AtomsProto.PackageNotificationPreferences;
+import com.android.os.AtomsProto.PackageNotificationChannelPreferences;
+import com.android.os.AtomsProto.PackageNotificationChannelGroupPreferences;
import com.android.os.AtomsProto.PictureInPictureStateChanged;
import com.android.os.AtomsProto.ProcessMemoryHighWaterMark;
import com.android.os.AtomsProto.ProcessMemorySnapshot;
@@ -1531,4 +1534,130 @@
assertThat(atom.getBytesField().getExperimentIdList()).isEmpty();
}
+ public void testNotificationPackagePreferenceExtraction() throws Exception {
+ if (statsdDisabled()) {
+ return;
+ }
+
+ StatsdConfig.Builder config = getPulledConfig();
+ addGaugeAtomWithDimensions(config,
+ Atom.PACKAGE_NOTIFICATION_PREFERENCES_FIELD_NUMBER,
+ null);
+ uploadConfig(config);
+ Thread.sleep(WAIT_TIME_SHORT);
+ runActivity("StatsdCtsForegroundActivity", "action", "action.show_notification");
+ Thread.sleep(WAIT_TIME_SHORT);
+ setAppBreadcrumbPredicate();
+ Thread.sleep(WAIT_TIME_SHORT);
+
+ List<PackageNotificationPreferences> allPreferences = new ArrayList<>();
+ for (Atom atom : getGaugeMetricDataList()){
+ if(atom.hasPackageNotificationPreferences()) {
+ allPreferences.add(atom.getPackageNotificationPreferences());
+ }
+ }
+ assertThat(allPreferences.size()).isGreaterThan(0);
+
+ boolean foundTestPackagePreferences = false;
+ int uid = getUid();
+ for (PackageNotificationPreferences pref : allPreferences) {
+ assertThat(pref.getUid()).isGreaterThan(0);
+ assertTrue(pref.hasImportance());
+ assertTrue(pref.hasVisibility());
+ assertTrue(pref.hasUserLockedFields());
+ if(pref.getUid() == uid){
+ assertThat(pref.getImportance()).isEqualTo(-1000); //UNSPECIFIED_IMPORTANCE
+ assertThat(pref.getVisibility()).isEqualTo(-1000); //UNSPECIFIED_VISIBILITY
+ foundTestPackagePreferences = true;
+ }
+ }
+ assertTrue(foundTestPackagePreferences);
+ }
+
+ public void testNotificationChannelPreferencesExtraction() throws Exception {
+ if (statsdDisabled()) {
+ return;
+ }
+
+ StatsdConfig.Builder config = getPulledConfig();
+ addGaugeAtomWithDimensions(config,
+ Atom.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES_FIELD_NUMBER,
+ null);
+ uploadConfig(config);
+ Thread.sleep(WAIT_TIME_SHORT);
+ runActivity("StatsdCtsForegroundActivity", "action", "action.show_notification");
+ Thread.sleep(WAIT_TIME_SHORT);
+ setAppBreadcrumbPredicate();
+ Thread.sleep(WAIT_TIME_SHORT);
+
+ List<PackageNotificationChannelPreferences> allChannelPreferences = new ArrayList<>();
+ for(Atom atom : getGaugeMetricDataList()) {
+ if (atom.hasPackageNotificationChannelPreferences()) {
+ allChannelPreferences.add(atom.getPackageNotificationChannelPreferences());
+ }
+ }
+ assertThat(allChannelPreferences.size()).isGreaterThan(0);
+
+ boolean foundTestPackagePreferences = false;
+ int uid = getUid();
+ for (PackageNotificationChannelPreferences pref : allChannelPreferences) {
+ assertThat(pref.getUid()).isGreaterThan(0);
+ assertTrue(pref.hasChannelId());
+ assertTrue(pref.hasChannelName());
+ assertTrue(pref.hasDescription());
+ assertTrue(pref.hasImportance());
+ assertTrue(pref.hasUserLockedFields());
+ assertTrue(pref.hasIsDeleted());
+ if(uid == pref.getUid() && pref.getChannelId().equals("StatsdCtsChannel")) {
+ assertThat(pref.getChannelName()).isEqualTo("Statsd Cts");
+ assertThat(pref.getDescription()).isEqualTo("Statsd Cts Channel");
+ assertThat(pref.getImportance()).isEqualTo(3); // IMPORTANCE_DEFAULT
+ foundTestPackagePreferences = true;
+ }
+ }
+ assertTrue(foundTestPackagePreferences);
+ }
+
+ public void testNotificationChannelGroupPreferencesExtraction() throws Exception {
+ if (statsdDisabled()) {
+ return;
+ }
+
+ StatsdConfig.Builder config = getPulledConfig();
+ addGaugeAtomWithDimensions(config,
+ Atom.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES_FIELD_NUMBER,
+ null);
+ uploadConfig(config);
+ Thread.sleep(WAIT_TIME_SHORT);
+ runActivity("StatsdCtsForegroundActivity", "action", "action.create_channel_group");
+ Thread.sleep(WAIT_TIME_SHORT);
+ setAppBreadcrumbPredicate();
+ Thread.sleep(WAIT_TIME_SHORT);
+
+ List<PackageNotificationChannelGroupPreferences> allGroupPreferences = new ArrayList<>();
+ for(Atom atom : getGaugeMetricDataList()) {
+ if (atom.hasPackageNotificationChannelGroupPreferences()) {
+ allGroupPreferences.add(atom.getPackageNotificationChannelGroupPreferences());
+ }
+ }
+ assertThat(allGroupPreferences.size()).isGreaterThan(0);
+
+ boolean foundTestPackagePreferences = false;
+ int uid = getUid();
+ for(PackageNotificationChannelGroupPreferences pref : allGroupPreferences) {
+ assertThat(pref.getUid()).isGreaterThan(0);
+ assertTrue(pref.hasGroupId());
+ assertTrue(pref.hasGroupName());
+ assertTrue(pref.hasDescription());
+ assertTrue(pref.hasIsBlocked());
+ assertTrue(pref.hasUserLockedFields());
+ if(uid == pref.getUid() && pref.getGroupId().equals("StatsdCtsGroup")) {
+ assertThat(pref.getGroupName()).isEqualTo("Statsd Cts Group");
+ assertThat(pref.getDescription()).isEqualTo("StatsdCtsGroup Description");
+ assertThat(pref.getIsBlocked()).isFalse();
+ foundTestPackagePreferences = true;
+ }
+ }
+ assertTrue(foundTestPackagePreferences);
+ }
}
diff --git a/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java b/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java
index 8dcc4d3..0b4882a 100644
--- a/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java
+++ b/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java
@@ -162,5 +162,7 @@
} finally {
in.close();
}
+ //Re-enable Wi-Fi as part of CTS Pre-conditions
+ device.executeShellCommand("svc wifi enable; sleep 1");
}
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
index 9042d62..bc30c7d4 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
@@ -68,8 +68,10 @@
import android.content.pm.PackageManager;
import android.graphics.Point;
import android.graphics.PointF;
+import android.graphics.Region;
import android.platform.test.annotations.AppModeFull;
import android.util.DisplayMetrics;
+import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
@@ -353,8 +355,7 @@
dispatch(doubleTap(mTapLocation));
mHoverListener.assertNonePropagated();
mTouchListener.assertNonePropagated();
- mService.assertPropagated(
- TYPE_TOUCH_INTERACTION_START, TYPE_TOUCH_INTERACTION_END);
+ mService.assertPropagated(TYPE_TOUCH_INTERACTION_START, TYPE_TOUCH_INTERACTION_END);
mService.clearEvents();
mLongClickListener.assertNoneLongClicked();
}
@@ -470,6 +471,84 @@
ACTION_DOWN, ACTION_POINTER_DOWN, ACTION_MOVE, ACTION_POINTER_UP, ACTION_UP);
}
+ /**
+ * Test the gesture detection passthrough by performing a fast swipe in the passthrough region.
+ * It should bypass the gesture detector entirely.
+ */
+ @Test
+ @AppModeFull
+ public void testGestureDetectionPassthrough_initiatesTouchExploration() {
+ if (!mHasTouchscreen || !mScreenBigEnough) return;
+ setRightSideOfScreenGestureDetectionPassthrough();
+ // Swipe in the passthrough region. This should generate hover events.
+ dispatch(swipe(mTapLocation, add(mTapLocation, mSwipeDistance, 0)));
+ mHoverListener.assertPropagated(ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_END,
+ TYPE_TOUCH_INTERACTION_END);
+ mService.clearEvents();
+ // Swipe starting inside the passthrough region but ending outside of it. This should still
+ // behave as a passthrough interaction.
+ dispatch(swipe(mTapLocation, add(mTapLocation, -mSwipeDistance, 0)));
+ mHoverListener.assertPropagated(ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_END,
+ TYPE_TOUCH_INTERACTION_END);
+ mService.clearEvents();
+ // Swipe outside the passthrough region. This should not generate hover events.
+ dispatch(swipe(add(mTapLocation, -1, 0), add(mTapLocation, -mSwipeDistance, 0)));
+ mHoverListener.assertNonePropagated();
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START,
+ TYPE_GESTURE_DETECTION_START,
+ TYPE_GESTURE_DETECTION_END,
+ TYPE_TOUCH_INTERACTION_END);
+ mService.clearEvents();
+ // There should be no touch events in this test.
+ mTouchListener.assertNonePropagated();
+ clearPassthroughRegions();
+ }
+
+ /**
+ * Test the touch exploration passthrough by performing a fast swipe in the passthrough region.
+ * It should generate touch events.
+ */
+ @Test
+ @AppModeFull
+ public void testTouchExplorationPassthrough_sendsTouchEvents() {
+ if (!mHasTouchscreen || !mScreenBigEnough) return;
+ setRightSideOfScreenTouchExplorationPassthrough();
+ // Swipe in the passthrough region. This should generate touch events.
+ dispatch(swipe(mTapLocation, add(mTapLocation, mSwipeDistance, 0)));
+ mTouchListener.assertPropagated(ACTION_DOWN, ACTION_MOVE, ACTION_UP);
+ // We still want accessibility events to tell us when the gesture starts and ends.
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START, TYPE_TOUCH_INTERACTION_END, TYPE_VIEW_CLICKED);
+ mService.clearEvents();
+ // Swipe starting inside the passthrough region but ending outside of it. This should still
+ // behave as a passthrough interaction.
+ dispatch(swipe(mTapLocation, add(mTapLocation, -mSwipeDistance, 0)));
+ mTouchListener.assertPropagated(ACTION_DOWN, ACTION_MOVE, ACTION_UP);
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START, TYPE_TOUCH_INTERACTION_END, TYPE_VIEW_CLICKED);
+ mService.clearEvents();
+ // Swipe outside the passthrough region. This should not generate touch events.
+ dispatch(swipe(add(mTapLocation, -1, 0), add(mTapLocation, -mSwipeDistance, 0)));
+ mTouchListener.assertNonePropagated();
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START,
+ TYPE_GESTURE_DETECTION_START,
+ TYPE_GESTURE_DETECTION_END,
+ TYPE_TOUCH_INTERACTION_END);
+ // There should be no hover events in this test.
+ mHoverListener.assertNonePropagated();
+ clearPassthroughRegions();
+ }
+
public void dispatch(StrokeDescription firstStroke, StrokeDescription... rest) {
GestureDescription.Builder builder =
new GestureDescription.Builder().addStroke(firstStroke);
@@ -493,4 +572,44 @@
.performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
});
}
+
+ private void setRightSideOfScreenGestureDetectionPassthrough() {
+ Region region = getRightSideOfScreenRegion();
+ mService.runOnServiceSync(
+ () -> {
+ mService.setGestureDetectionPassthroughRegion(Display.DEFAULT_DISPLAY, region);
+ });
+ }
+
+ private void setRightSideOfScreenTouchExplorationPassthrough() {
+ Region region = getRightSideOfScreenRegion();
+ mService.runOnServiceSync(
+ () -> {
+ mService.setTouchExplorationPassthroughRegion(Display.DEFAULT_DISPLAY, region);
+ });
+ }
+
+ private void clearPassthroughRegions() {
+ mService.runOnServiceSync(
+ () -> {
+ mService.setGestureDetectionPassthroughRegion(
+ Display.DEFAULT_DISPLAY, new Region());
+ mService.setTouchExplorationPassthroughRegion(
+ Display.DEFAULT_DISPLAY, new Region());
+ });
+ }
+
+ private Region getRightSideOfScreenRegion() {
+ WindowManager windowManager =
+ (WindowManager)
+ mInstrumentation.getContext().getSystemService(Context.WINDOW_SERVICE);
+ final DisplayMetrics metrics = new DisplayMetrics();
+ windowManager.getDefaultDisplay().getRealMetrics(metrics);
+ int top = 0;
+ int left = metrics.widthPixels / 2;
+ int right = metrics.widthPixels;
+ int bottom = metrics.heightPixels;
+ Region region = new Region(left, top, right, bottom);
+ return region;
+ }
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/InlinePresentationTest.java b/tests/autofillservice/src/android/autofillservice/cts/InlinePresentationTest.java
new file mode 100644
index 0000000..e50b772
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/InlinePresentationTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.autofillservice.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.app.slice.Slice;
+import android.app.slice.SliceSpec;
+import android.net.Uri;
+import android.os.Parcel;
+import android.service.autofill.InlinePresentation;
+import android.util.Size;
+import android.view.inline.InlinePresentationSpec;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InlinePresentationTest {
+
+ @Test
+ public void testNullInlinePresentationSpecsThrowsException() {
+ assertThrows(NullPointerException.class,
+ () -> createInlinePresentation(/* createSlice */true, /* createSpec */ false));
+ }
+
+ @Test
+ public void testNullSliceThrowsException() {
+ assertThrows(NullPointerException.class,
+ () -> createInlinePresentation(/* createSlice */false, /* createSpec */ true));
+ }
+
+ @Test
+ public void testInlinePresentationValues() {
+ InlinePresentation presentation =
+ createInlinePresentation(/* createSlice */true, /* createSpec */ true);
+
+ assertThat(presentation.isPinned()).isFalse();
+ assertThat(presentation.getInlinePresentationSpec()).isNotNull();
+ assertThat(presentation.getSlice()).isNotNull();
+ assertThat(presentation.getSlice().getItems().size()).isEqualTo(0);
+ }
+
+ @Test
+ public void testtInlinePresentationParcelizeDeparcelize() {
+ InlinePresentation presentation =
+ createInlinePresentation(/* createSlice */true, /* createSpec */ true);
+
+ Parcel p = Parcel.obtain();
+ presentation.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ InlinePresentation targetPresentation = InlinePresentation.CREATOR.createFromParcel(p);
+ p.recycle();
+
+ assertThat(targetPresentation.isPinned()).isEqualTo(presentation.isPinned());
+ assertThat(targetPresentation.getInlinePresentationSpec()).isEqualTo(
+ presentation.getInlinePresentationSpec());
+ assertThat(targetPresentation.getSlice().getUri()).isEqualTo(
+ presentation.getSlice().getUri());
+ assertThat(targetPresentation.getSlice().getSpec()).isEqualTo(
+ presentation.getSlice().getSpec());
+ }
+
+ private InlinePresentation createInlinePresentation(boolean createSlice, boolean createSpec) {
+ Slice slice = createSlice ? new Slice.Builder(Uri.parse("testuri"),
+ new SliceSpec("type", 1)).build() : null;
+ InlinePresentationSpec spec = createSpec ? new InlinePresentationSpec.Builder(
+ new Size(100, 100), new Size(400, 100)).build() : null;
+ return new InlinePresentation(slice, spec, /* pined */ false);
+ }
+}
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
index 929eae0..18ab33e 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
@@ -131,6 +131,7 @@
private static final String ALPHA_TAG_B = "tagB";
private static final String NUMBER_A = "1234567890";
private static final String NUMBER_B = "0987654321";
+ private static final String TESTING_PLMN = "12345";
private static final int DSDS_PHONE_COUNT = 2;
@@ -485,6 +486,7 @@
mTelephonyManager.getVoiceMailAlphaTag();
mTelephonyManager.getForbiddenPlmns();
mTelephonyManager.getServiceState();
+ mTelephonyManager.getManualNetworkSelectionPlmn();
mTelephonyManager.setForbiddenPlmns(new ArrayList<String>());
} catch (SecurityException e) {
failMessage();
@@ -553,6 +555,19 @@
static final int CARRIER_PRIVILEGE_LISTENERS =
READ_PHONE_STATE_LISTENERS | READ_PRECISE_PHONE_STATE_LISTENERS;
+ public void testGetManualNetworkSelectionPlmnPersisted() throws Exception {
+ if (!hasCellular) return;
+
+ try {
+ mTelephonyManager.setNetworkSelectionModeManual(
+ TESTING_PLMN/* operatorNumeric */, true /* persistSelection */);
+ String plmn = mTelephonyManager.getManualNetworkSelectionPlmn();
+ assertEquals(TESTING_PLMN, plmn);
+ } finally {
+ mTelephonyManager.setNetworkSelectionModeAutomatic();
+ }
+ }
+
public void testPhoneStateListener() throws Exception {
if (!hasCellular) return;
PhoneStateListener psl = new PhoneStateListener((Runnable r) -> { });
@@ -563,6 +578,16 @@
}
}
+ public void testIsManualNetworkSelectionAllowed() throws Exception {
+ if (!hasCellular) return;
+
+ try {
+ assertTrue(mTelephonyManager.isManualNetworkSelectionAllowed());
+ } catch (SecurityException e) {
+ failMessage();
+ }
+ }
+
public void testSubscriptionInfoChangeListener() throws Exception {
if (!hasCellular) return;
final AtomicReference<SecurityException> error = new AtomicReference<>();
diff --git a/tests/tests/content/OWNERS b/tests/tests/content/OWNERS
new file mode 100644
index 0000000..12c955a
--- /dev/null
+++ b/tests/tests/content/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 36137
+toddke@google.com
+patb@google.com
+schfan@google.com
+alexbuy@google.com
diff --git a/tests/tests/content/res/xml/intentfilter.xml b/tests/tests/content/res/xml/intentfilter.xml
index 7ed3a53..9041513 100644
--- a/tests/tests/content/res/xml/intentfilter.xml
+++ b/tests/tests/content/res/xml/intentfilter.xml
@@ -20,6 +20,8 @@
<action name="testAction"/>
<cat name="testCategory" />
<type name="vnd.android.cursor.dir/person"/>
+ <staticType name="text/plain"/>
+ <group name="testMimeGroup"/>
<scheme name="testScheme"/>
<auth host="testHost" port="80"/>
<path name="testPath" literal="test"/>
diff --git a/tests/tests/content/src/android/content/cts/IntentFilterTest.java b/tests/tests/content/src/android/content/cts/IntentFilterTest.java
index 80f47f9..6248044 100644
--- a/tests/tests/content/src/android/content/cts/IntentFilterTest.java
+++ b/tests/tests/content/src/android/content/cts/IntentFilterTest.java
@@ -20,18 +20,6 @@
import static android.os.PatternMatcher.PATTERN_PREFIX;
import static android.os.PatternMatcher.PATTERN_SIMPLE_GLOB;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Intent;
@@ -52,14 +40,28 @@
import com.android.internal.util.FastXmlSerializer;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
public class IntentFilterTest extends AndroidTestCase {
private IntentFilter mIntentFilter;
private static final String ACTION = "testAction";
private static final String CATEGORY = "testCategory";
- private static final String DATA_TYPE = "vnd.android.cursor.dir/person";
+ private static final String DATA_DYNAMIC_TYPE = "type/dynamic";
+ private static final String DATA_STATIC_TYPE = "vnd.android.cursor.dir/person";
private static final String DATA_SCHEME = "testDataSchemes.";
+ private static final String MIME_GROUP = "mime_group";
private static final String SSP = "testSsp";
private static final String HOST = "testHost";
private static final int PORT = 80;
@@ -80,11 +82,11 @@
filter = new IntentFilter(ACTION);
verifyContent(filter, ACTION, null);
- final IntentFilter actionTypeFilter = new IntentFilter(ACTION, DATA_TYPE);
- verifyContent(actionTypeFilter, ACTION, DATA_TYPE);
+ final IntentFilter actionTypeFilter = new IntentFilter(ACTION, DATA_STATIC_TYPE);
+ verifyContent(actionTypeFilter, ACTION, DATA_STATIC_TYPE);
filter = new IntentFilter(actionTypeFilter);
- verifyContent(filter, ACTION, DATA_TYPE);
+ verifyContent(filter, ACTION, DATA_STATIC_TYPE);
final String dataType = "testdataType";
try {
@@ -110,8 +112,10 @@
if (dataType != null) {
assertEquals(1, filter.countDataTypes());
assertEquals(dataType, filter.getDataType(0));
+ assertEquals(1, filter.countStaticDataTypes());
} else {
assertEquals(0, filter.countDataTypes());
+ assertEquals(0, filter.countStaticDataTypes());
}
}
@@ -215,6 +219,125 @@
null), });
}
+ public void testDynamicMimeTypes() {
+ IntentFilter filter = new Match()
+ .addDynamicMimeTypes(new String[] { "which1/what1" });
+ checkMatches(filter, new MatchCondition[] {
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, null, null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what1",
+ null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "*/*", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/what2", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/*", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which1/what2", null),
+ });
+
+ filter = new Match()
+ .addDynamicMimeTypes(new String[] { "which1/what1", "which2/what2" });
+ checkMatches(filter, new MatchCondition[] {
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, null, null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what1",
+ null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "*/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which2/what2",
+ null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which2/*", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which1/what2", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which3/what3", null),
+ });
+
+ filter = new Match()
+ .addDynamicMimeTypes(new String[] { "which1/*" });
+ checkMatches(filter, new MatchCondition[] {
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, null, null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what1",
+ null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "*/*", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/what2", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what2",
+ null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which3/what3", null),
+ });
+
+ filter = new Match()
+ .addDynamicMimeTypes(new String[] { "*/*" });
+ checkMatches(filter, new MatchCondition[] {
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, null, null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what1",
+ null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "*/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which2/what2",
+ null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which2/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what2",
+ null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which3/what3",
+ null), });
+ }
+
+ public void testClearDynamicMimeTypesWithStaticType() {
+ IntentFilter filter = new Match()
+ .addMimeTypes(new String[] {"which1/what1"})
+ .addDynamicMimeTypes(new String[] { "which2/what2" });
+
+ checkMatches(filter, new MatchCondition[] {
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, null, null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what1",
+ null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "*/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which2/what2",
+ null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which2/*", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which1/what2", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which3/what3", null),
+ });
+
+ filter.clearDynamicDataTypes();
+
+ checkMatches(filter, new MatchCondition[] {
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, null, null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what1",
+ null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "*/*", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/what2", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/*", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which1/what2", null),
+ });
+ }
+
+ public void testClearDynamicMimeTypesWithAction() {
+ IntentFilter filter = new Match()
+ .addActions(new String[] {"action1"})
+ .addDynamicMimeTypes(new String[] { "which1/what1" });
+
+ checkMatches(filter, new MatchCondition[] {
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, null, null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/what1",
+ null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "which1/*", null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null, "*/*", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/what2", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which2/*", null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null, "which1/what2", null),
+ });
+
+ filter.clearDynamicDataTypes();
+
+ checkMatches(filter, new MatchCondition[] {
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_EMPTY, null, null, null, null),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_EMPTY, "action1", null, null, null),
+ new MatchCondition(IntentFilter.NO_MATCH_TYPE, "action1", null, "which2/what2",
+ null),
+ });
+ }
+
public void testAccessPriority() {
final int expected = 1;
mIntentFilter.setPriority(expected);
@@ -255,9 +378,9 @@
}
public void testCreate() {
- IntentFilter filter = IntentFilter.create(ACTION, DATA_TYPE);
+ IntentFilter filter = IntentFilter.create(ACTION, DATA_STATIC_TYPE);
assertNotNull(filter);
- verifyContent(filter, ACTION, DATA_TYPE);
+ verifyContent(filter, ACTION, DATA_STATIC_TYPE);
}
@@ -492,18 +615,83 @@
public void testDataTypes() throws MalformedMimeTypeException {
for (int i = 0; i < 10; i++) {
- mIntentFilter.addDataType(DATA_TYPE + i);
+ mIntentFilter.addDataType(DATA_STATIC_TYPE + i);
}
assertEquals(10, mIntentFilter.countDataTypes());
+ assertEquals(10, mIntentFilter.countStaticDataTypes());
final Iterator<String> iter = mIntentFilter.typesIterator();
+ String actual;
+ int i = 0;
+ while (iter.hasNext()) {
+ actual = iter.next();
+ assertEquals(DATA_STATIC_TYPE + i, actual);
+ assertEquals(DATA_STATIC_TYPE + i, mIntentFilter.getDataType(i));
+ assertTrue(mIntentFilter.hasDataType(DATA_STATIC_TYPE + i));
+ assertFalse(mIntentFilter.hasDataType(DATA_STATIC_TYPE + i + 10));
+ i++;
+ }
+ }
+
+ public void testDynamicDataTypes() throws MalformedMimeTypeException {
+ for (int i = 0; i < 10; i++) {
+ mIntentFilter.addDynamicDataType(DATA_DYNAMIC_TYPE + i);
+ }
+ assertEquals(10, mIntentFilter.countDataTypes());
+ assertEquals(0, mIntentFilter.countStaticDataTypes());
+
+ final Iterator<String> iter = mIntentFilter.typesIterator();
+ String actual;
+ int i = 0;
+ while (iter.hasNext()) {
+ actual = iter.next();
+ assertEquals(DATA_DYNAMIC_TYPE + i, actual);
+ assertEquals(DATA_DYNAMIC_TYPE + i, mIntentFilter.getDataType(i));
+ assertTrue(mIntentFilter.hasDataType(DATA_DYNAMIC_TYPE + i));
+ assertFalse(mIntentFilter.hasDataType(DATA_DYNAMIC_TYPE + i + 10));
+ i++;
+ }
+ }
+
+ public void testClearDynamicDataTypes() throws MalformedMimeTypeException {
+ for (int i = 0; i < 10; i++) {
+ mIntentFilter.addDataType(DATA_STATIC_TYPE + i);
+ mIntentFilter.addDynamicDataType(DATA_DYNAMIC_TYPE + i);
+ }
+ assertEquals(20, mIntentFilter.countDataTypes());
+ assertEquals(10, mIntentFilter.countStaticDataTypes());
+
+ mIntentFilter.clearDynamicDataTypes();
+
+ assertEquals(10, mIntentFilter.countDataTypes());
+ assertEquals(10, mIntentFilter.countStaticDataTypes());
+
+ final Iterator<String> iter = mIntentFilter.typesIterator();
+ String actual;
+ int i = 0;
+ while (iter.hasNext()) {
+ actual = iter.next();
+ assertEquals(DATA_STATIC_TYPE + i, actual);
+ assertEquals(DATA_STATIC_TYPE + i, mIntentFilter.getDataType(i));
+ assertTrue(mIntentFilter.hasDataType(DATA_STATIC_TYPE + i));
+ assertFalse(mIntentFilter.hasDataType(DATA_DYNAMIC_TYPE + i));
+ i++;
+ }
+ }
+
+ public void testMimeGroups() {
+ for (int i = 0; i < 10; i++) {
+ mIntentFilter.addMimeGroup(MIME_GROUP + i);
+ }
+ assertEquals(10, mIntentFilter.countMimeGroups());
+ final Iterator<String> iter = mIntentFilter.mimeGroupsIterator();
String actual = null;
int i = 0;
while (iter.hasNext()) {
actual = iter.next();
- assertEquals(DATA_TYPE + i, actual);
- assertEquals(DATA_TYPE + i, mIntentFilter.getDataType(i));
- assertTrue(mIntentFilter.hasDataType(DATA_TYPE + i));
- assertFalse(mIntentFilter.hasDataType(DATA_TYPE + i + 10));
+ assertEquals(MIME_GROUP + i, actual);
+ assertEquals(MIME_GROUP + i, mIntentFilter.getMimeGroup(i));
+ assertTrue(mIntentFilter.hasMimeGroup(MIME_GROUP + i));
+ assertFalse(mIntentFilter.hasMimeGroup(MIME_GROUP + i + 10));
i++;
}
}
@@ -514,22 +702,23 @@
assertEquals(expected, mIntentFilter.matchData(null, DATA_SCHEME, null));
assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(null, DATA_SCHEME, URI));
- assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(DATA_TYPE, DATA_SCHEME,
- URI));
+ assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(DATA_STATIC_TYPE,
+ DATA_SCHEME, URI));
mIntentFilter.addDataScheme(DATA_SCHEME);
- assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(DATA_TYPE,
+ assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(DATA_STATIC_TYPE,
"mDataSchemestest", URI));
- assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(DATA_TYPE, "", URI));
+ assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(DATA_STATIC_TYPE, "",
+ URI));
expected = IntentFilter.MATCH_CATEGORY_SCHEME + IntentFilter.MATCH_ADJUSTMENT_NORMAL;
assertEquals(expected, mIntentFilter.matchData(null, DATA_SCHEME, URI));
- assertEquals(IntentFilter.NO_MATCH_TYPE, mIntentFilter.matchData(DATA_TYPE, DATA_SCHEME,
- URI));
+ assertEquals(IntentFilter.NO_MATCH_TYPE, mIntentFilter.matchData(DATA_STATIC_TYPE,
+ DATA_SCHEME, URI));
- mIntentFilter.addDataType(DATA_TYPE);
+ mIntentFilter.addDataType(DATA_STATIC_TYPE);
assertEquals(IntentFilter.MATCH_CATEGORY_TYPE + IntentFilter.MATCH_ADJUSTMENT_NORMAL,
- mIntentFilter.matchData(DATA_TYPE, DATA_SCHEME, URI));
+ mIntentFilter.matchData(DATA_STATIC_TYPE, DATA_SCHEME, URI));
mIntentFilter.addDataAuthority(HOST, String.valueOf(PORT));
assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(null, DATA_SCHEME, URI));
@@ -584,7 +773,9 @@
mIntentFilter.addDataAuthority(HOST, String.valueOf(PORT));
mIntentFilter.addDataPath(DATA_PATH, 1);
mIntentFilter.addDataScheme(DATA_SCHEME);
- mIntentFilter.addDataType(DATA_TYPE);
+ mIntentFilter.addDataType(DATA_STATIC_TYPE);
+ mIntentFilter.addDynamicDataType(DATA_DYNAMIC_TYPE);
+ mIntentFilter.addMimeGroup(MIME_GROUP);
mIntentFilter.writeToXml(xml);
xml.flush();
final XmlPullParser parser = Xml.newPullParser();
@@ -594,7 +785,9 @@
intentFilter.readFromXml(parser);
assertEquals(ACTION, intentFilter.getAction(0));
assertEquals(CATEGORY, intentFilter.getCategory(0));
- assertEquals(DATA_TYPE, intentFilter.getDataType(0));
+ assertTrue(intentFilter.hasExactStaticDataType(DATA_STATIC_TYPE));
+ assertTrue(intentFilter.hasExactDynamicDataType(DATA_DYNAMIC_TYPE));
+ assertEquals(MIME_GROUP, intentFilter.getMimeGroup(0));
assertEquals(DATA_SCHEME, intentFilter.getDataScheme(0));
assertEquals(DATA_PATH, intentFilter.getDataPath(0).getPath());
assertEquals(HOST, intentFilter.getDataAuthority(0).getHost());
@@ -655,7 +848,11 @@
assertEquals("testAction", mIntentFilter.getAction(0));
assertEquals("testCategory", mIntentFilter.getCategory(0));
- assertEquals("vnd.android.cursor.dir/person", mIntentFilter.getDataType(0));
+
+ assertTrue(mIntentFilter.hasExactDynamicDataType("vnd.android.cursor.dir/person"));
+ assertTrue(mIntentFilter.hasExactStaticDataType("text/plain"));
+
+ assertEquals("testMimeGroup", mIntentFilter.getMimeGroup(0));
assertEquals("testScheme", mIntentFilter.getDataScheme(0));
assertEquals("testHost", mIntentFilter.getDataAuthority(0).getHost());
assertEquals(80, mIntentFilter.getDataAuthority(0).getPort());
@@ -781,10 +978,10 @@
assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(resolver, intent, true, null));
mIntentFilter.addDataAuthority(HOST, String.valueOf(PORT));
assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(resolver, intent, true, null));
- intent.setType(DATA_TYPE);
+ intent.setType(DATA_STATIC_TYPE);
assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(resolver, intent, true, null));
- mIntentFilter.addDataType(DATA_TYPE);
+ mIntentFilter.addDataType(DATA_STATIC_TYPE);
assertEquals(IntentFilter.MATCH_CATEGORY_TYPE + IntentFilter.MATCH_ADJUSTMENT_NORMAL,
mIntentFilter.match(resolver, intent, true, null));
@@ -797,7 +994,7 @@
assertEquals(IntentFilter.MATCH_CATEGORY_TYPE + IntentFilter.MATCH_ADJUSTMENT_NORMAL,
mIntentFilter.match(resolver, intent, true, null));
- intent.setDataAndType(uri, DATA_TYPE);
+ intent.setDataAndType(uri, DATA_STATIC_TYPE);
assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(resolver, intent, true, null));
}
@@ -815,18 +1012,18 @@
assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.matchData(null, DATA_SCHEME, URI));
- assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE,
+ assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
DATA_SCHEME, URI, null, null));
mIntentFilter.addDataScheme(DATA_SCHEME);
- assertEquals(IntentFilter.NO_MATCH_TYPE, mIntentFilter.match(ACTION, DATA_TYPE,
+ assertEquals(IntentFilter.NO_MATCH_TYPE, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
DATA_SCHEME, URI, null, null));
- assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE, "", URI,
- null, null));
- mIntentFilter.addDataType(DATA_TYPE);
+ assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE, "",
+ URI, null, null));
+ mIntentFilter.addDataType(DATA_STATIC_TYPE);
assertEquals(IntentFilter.MATCH_CATEGORY_TYPE + IntentFilter.MATCH_ADJUSTMENT_NORMAL,
- mIntentFilter.match(ACTION, DATA_TYPE, DATA_SCHEME, URI, null, null));
+ mIntentFilter.match(ACTION, DATA_STATIC_TYPE, DATA_SCHEME, URI, null, null));
assertEquals(IntentFilter.NO_MATCH_TYPE, mIntentFilter.match(ACTION, null, DATA_SCHEME,
URI, null, null));
@@ -835,29 +1032,29 @@
URI, cat, null));
cat.add(CATEGORY);
- assertEquals(IntentFilter.NO_MATCH_CATEGORY, mIntentFilter.match(ACTION, DATA_TYPE,
+ assertEquals(IntentFilter.NO_MATCH_CATEGORY, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
DATA_SCHEME, URI, cat, null));
cat = new HashSet<String>();
mIntentFilter.addDataAuthority(HOST, String.valueOf(PORT));
assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, null, DATA_SCHEME,
URI, null, null));
- assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE,
+ assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
DATA_SCHEME, URI, null, null));
final Uri uri = Uri.parse(DATA_SCHEME + "://" + HOST + ":" + PORT);
mIntentFilter.addDataPath(DATA_PATH, PatternMatcher.PATTERN_LITERAL);
- assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE,
+ assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
DATA_SCHEME, uri, null, null));
- assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE,
+ assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
DATA_SCHEME, URI, null, null));
- assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE,
+ assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
DATA_SCHEME, URI, cat, null));
cat.add(CATEGORY);
- assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE,
+ assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
DATA_SCHEME, URI, cat, null));
mIntentFilter.addCategory(CATEGORY);
- assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_TYPE,
+ assertEquals(IntentFilter.NO_MATCH_DATA, mIntentFilter.match(ACTION, DATA_STATIC_TYPE,
DATA_SCHEME, URI, cat, null));
}
@@ -867,7 +1064,9 @@
mIntentFilter.addDataAuthority(HOST, String.valueOf(PORT));
mIntentFilter.addDataPath(DATA_PATH, 1);
mIntentFilter.addDataScheme(DATA_SCHEME);
- mIntentFilter.addDataType(DATA_TYPE);
+ mIntentFilter.addDataType(DATA_STATIC_TYPE);
+ mIntentFilter.addDynamicDataType(DATA_DYNAMIC_TYPE);
+ mIntentFilter.addMimeGroup(MIME_GROUP);
Parcel parcel = Parcel.obtain();
mIntentFilter.writeToParcel(parcel, 1);
parcel.setDataPosition(0);
@@ -880,6 +1079,11 @@
target.getDataAuthority(0).getPort());
assertEquals(mIntentFilter.getDataPath(0).getPath(), target.getDataPath(0).getPath());
assertEquals(mIntentFilter.getDataScheme(0), target.getDataScheme(0));
+ assertEquals(mIntentFilter.getDataType(0), target.getDataType(0));
+ assertEquals(mIntentFilter.getDataType(1), target.getDataType(1));
+ assertEquals(mIntentFilter.countStaticDataTypes(), target.countStaticDataTypes());
+ assertEquals(mIntentFilter.countDataTypes(), target.countDataTypes());
+ assertEquals(mIntentFilter.getMimeGroup(0), target.getMimeGroup(0));
}
public void testAddDataType() throws MalformedMimeTypeException {
@@ -890,17 +1094,18 @@
// expected
}
- mIntentFilter.addDataType(DATA_TYPE);
- assertEquals(DATA_TYPE, mIntentFilter.getDataType(0));
+ mIntentFilter.addDataType(DATA_STATIC_TYPE);
+ assertEquals(DATA_STATIC_TYPE, mIntentFilter.getDataType(0));
}
private static class Match extends IntentFilter {
+ Match() {
+ }
+
Match(String[] actions, String[] categories, String[] mimeTypes, String[] schemes,
String[] authorities, String[] ports) {
if (actions != null) {
- for (int i = 0; i < actions.length; i++) {
- addAction(actions[i]);
- }
+ addActions(actions);
}
if (categories != null) {
for (int i = 0; i < categories.length; i++) {
@@ -948,6 +1153,35 @@
}
}
}
+
+ Match addDynamicMimeTypes(String[] dynamicMimeTypes) {
+ for (int i = 0; i < dynamicMimeTypes.length; i++) {
+ try {
+ addDynamicDataType(dynamicMimeTypes[i]);
+ } catch (IntentFilter.MalformedMimeTypeException e) {
+ throw new RuntimeException("Bad mime type", e);
+ }
+ }
+ return this;
+ }
+
+ Match addMimeTypes(String[] mimeTypes) {
+ for (int i = 0; i < mimeTypes.length; i++) {
+ try {
+ addDataType(mimeTypes[i]);
+ } catch (IntentFilter.MalformedMimeTypeException e) {
+ throw new RuntimeException("Bad mime type", e);
+ }
+ }
+ return this;
+ }
+
+ Match addActions(String[] actions) {
+ for (int i = 0; i < actions.length; i++) {
+ addAction(actions[i]);
+ }
+ return this;
+ }
}
private static class MatchCondition {
@@ -1179,7 +1413,7 @@
public void testDump() throws MalformedMimeTypeException {
TestPrinter printer = new TestPrinter();
String prefix = "TestIntentFilter";
- IntentFilter filter = new IntentFilter(ACTION, DATA_TYPE);
+ IntentFilter filter = new IntentFilter(ACTION, DATA_STATIC_TYPE);
filter.dump(printer, prefix);
assertTrue(printer.isPrintlnCalled);
}
diff --git a/tests/tests/content/src/android/content/om/cts/OverlayInfoTest.java b/tests/tests/content/src/android/content/om/cts/OverlayInfoTest.java
index d2e1998..3eed1b2 100644
--- a/tests/tests/content/src/android/content/om/cts/OverlayInfoTest.java
+++ b/tests/tests/content/src/android/content/om/cts/OverlayInfoTest.java
@@ -108,6 +108,6 @@
assertEquals(info.state, copyInfo.state);
assertEquals(info.userId, copyInfo.userId);
assertEquals(info.priority, copyInfo.priority);
- assertEquals(info.isStatic, copyInfo.isStatic);
+ assertEquals(info.isMutable, copyInfo.isMutable);
}
}
\ No newline at end of file
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
index a78069c..36ba232 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
@@ -74,8 +74,7 @@
@Parameters
public static Iterable<Object> initParameters() {
- return Arrays.asList(DATA_LOADER_TYPE_NONE, DATA_LOADER_TYPE_STREAMING,
- DATA_LOADER_TYPE_INCREMENTAL);
+ return Arrays.asList(DATA_LOADER_TYPE_NONE);
}
private boolean mStreaming = false;
diff --git a/tests/tests/graphics/src/android/graphics/cts/VulkanDeqpLevelTest.java b/tests/tests/graphics/src/android/graphics/cts/VulkanDeqpLevelTest.java
index 0374082..3f8c011 100644
--- a/tests/tests/graphics/src/android/graphics/cts/VulkanDeqpLevelTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/VulkanDeqpLevelTest.java
@@ -29,6 +29,7 @@
import com.android.compatibility.common.util.CddTest;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -69,6 +70,7 @@
}
@CddTest(requirement = "7.1.4.2/C-1-8")
+ @Ignore("b/149464764: Test disabled until certain targets get the new feature flag.")
@Test
public void testVulkanDeqpLevel() {
if (mVulkanHardwareVersion.version >= VULKAN_1_0) {
diff --git a/tests/tests/identity/src/android/security/identity/cts/AttestationTest.java b/tests/tests/identity/src/android/security/identity/cts/AttestationTest.java
new file mode 100644
index 0000000..34b0cf2
--- /dev/null
+++ b/tests/tests/identity/src/android/security/identity/cts/AttestationTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity.cts;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+
+import android.security.identity.IdentityCredential;
+import android.security.identity.IdentityCredentialStore;
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Test;
+import org.junit.Ignore;
+
+import com.google.common.primitives.Bytes;
+
+import java.security.SecureRandom;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+
+import java.util.Optional;
+
+public class AttestationTest {
+ private static final String TAG = "AttestationTest";
+
+ // The subset of Keymaster tags we care about. See
+ //
+ // https://source.android.com/security/keystore/tags
+ //
+ // for a list of known tags.
+ //
+ public static final int KM_TAG_ATTESTATION_APPLICATION_ID = 709;
+ public static final int KM_TAG_IDENTITY_CREDENTIAL_KEY = 721;
+
+ @Ignore("Not ready until SW implementation produces correct attestations")
+ @Test
+ public void attestationTest() throws Exception {
+ Context appContext = InstrumentationRegistry.getTargetContext();
+ IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+
+ // Use a random challenge of length between 16 and 32.
+ SecureRandom random = new SecureRandom();
+ int challengeLength = 16 + random.nextInt(16);
+ byte[] challenge = new byte[challengeLength];
+ random.nextBytes(challenge);
+
+ String credentialName = "test";
+
+ store.deleteCredentialByName(credentialName);
+ Collection<X509Certificate> certChain = ProvisioningTest.createCredentialWithChallenge(
+ store, credentialName, challenge);
+ store.deleteCredentialByName("test");
+
+ // Check that each certificate in the chain isn't expired and also that it's signed by the
+ // next one.
+ assertTrue(verifyCertificateChain(certChain));
+
+ // Parse the attestation record... Identity Credential uses standard KeyMaster-style
+ // attestation with only a few differences:
+ //
+ // - Tag::IDENTITY_CREDENTIAL_KEY (boolean) must be set to identify the key as an Identity
+ // Credential key.
+ //
+ // - the KeyMaster version and security-level should be set to that of Identity Credential
+ //
+ // In this test we only test for these two things and a) the attestationApplicationId
+ // matches the calling application; and b) the given challenge matches what was sent.
+ //
+ // We could test for all the other expected attestation records but currently we assume
+ // this is already tested by Keymaster/Android Keystore tests since the TA is expected to
+ // re-use the same infrastructure.
+ //
+ X509Certificate cert = certChain.iterator().next();
+ ParsedAttestationRecord record = new ParsedAttestationRecord(cert);
+
+ // Need at least attestation version 3 and attestations to be done in either the TEE
+ // or in a StrongBox.
+ assertTrue(record.getAttestationVersion() >= 3);
+
+ // Also per the IC HAL, the keymasterSecurityLevel field is used as the securityLevel of
+ // the IC TA and this must be either TEE or in a StrongBox... except we specifically
+ // allow our software implementation to report Software here.
+ //
+ // Since we cannot get the implementation name or author at this layer, we can't test for
+ // it. This can be tested for in the VTS test, however.
+
+ // As per the IC HAL, the keymasterVersion field should be the version of the Identity
+ // Credential HAL - 1.0 - and this is encoded as major*10 + minor. This field is used by
+ // Keymaster which is known to report integers less than or equal to 4 (for KM up to 4.0)
+ // and integers greater or equal than 41 (for KM starting with 4.1).
+ //
+ // Since we won't get to version 4.0 of the IC HAL for a while, let's also check that a KM
+ // version isn't errornously returned.
+ assertTrue(record.getKeymasterVersion() >= 10);
+ assertTrue(record.getKeymasterVersion() < 40);
+
+ // Check that the challenge we passed in, is in fact in the attestation record.
+ assertArrayEquals(challenge, record.getAttestationChallenge());
+
+ // Tag::ATTESTATION_APPLICATION_ID is used to identify the set of possible applications of
+ // which one has initiated a key attestation. This is complicated ASN.1 which we don't want
+ // to parse and we don't need to [1].. we can however easily check that our applicationId
+ // is appearing as a substring somewhere in this blob.
+ //
+ // [1] : and the point of this test isn't to verify that attestations are done correctly,
+ // that's tested elsewhere in e.g. KM and Android Keystore.
+ //
+ Optional<byte[]> attestationApplicationId =
+ record.getSoftwareAuthorizationByteString(KM_TAG_ATTESTATION_APPLICATION_ID);
+ assertTrue(attestationApplicationId.isPresent());
+ String appId = appContext.getPackageName();
+ assertTrue(Bytes.indexOf(attestationApplicationId.get(), appId.getBytes()) != -1);
+
+ // Tag::IDENTITY_CREDENTIAL_KEY is used in attestations produced by the Identity Credential
+ // HAL when that HAL attests to Credential Keys.
+ boolean isIdentityCredentialKey =
+ record.getTeeAuthorizationBoolean(KM_TAG_IDENTITY_CREDENTIAL_KEY);
+ assertTrue(isIdentityCredentialKey);
+ }
+
+ // This only verifies each cert hasn't expired and signed by the next one.
+ private static boolean verifyCertificateChain(Collection<X509Certificate> certChain) {
+ X509Certificate[] certs = new X509Certificate[certChain.size()];
+ certs = certChain.toArray(certs);
+ X509Certificate parent = certs[certs.length - 1];
+ for (int i = certs.length - 1; i >= 0; i--) {
+ X509Certificate cert = certs[i];
+ try {
+ cert.checkValidity();
+ cert.verify(parent.getPublicKey());
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ parent = cert;
+ }
+ return true;
+ }
+}
diff --git a/tests/tests/identity/src/android/security/identity/cts/ParsedAttestationRecord.java b/tests/tests/identity/src/android/security/identity/cts/ParsedAttestationRecord.java
new file mode 100644
index 0000000..e4297d7
--- /dev/null
+++ b/tests/tests/identity/src/android/security/identity/cts/ParsedAttestationRecord.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity.cts;
+
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.Optional;
+
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Boolean;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Enumerated;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+
+// This code is loosely based on https://github.com/google/android-key-attestation
+
+class ParsedAttestationRecord {
+ private static final String KEY_DESCRIPTION_OID = "1.3.6.1.4.1.11129.2.1.17";
+
+ enum SecurityLevel {
+ SOFTWARE,
+ TRUSTED_ENVIRONMENT,
+ STRONG_BOX
+ }
+
+ private static final int ATTESTATION_VERSION_INDEX = 0;
+ private static final int ATTESTATION_SECURITY_LEVEL_INDEX = 1;
+ private static final int KEYMASTER_VERSION_INDEX = 2;
+ private static final int KEYMASTER_SECURITY_LEVEL_INDEX = 3;
+ private static final int ATTESTATION_CHALLENGE_INDEX = 4;
+ private static final int UNIQUE_ID_INDEX = 5;
+ private static final int SW_ENFORCED_INDEX = 6;
+ private static final int TEE_ENFORCED_INDEX = 7;
+
+ // Some security values. The complete list is in this AOSP file:
+ // hardware/libhardware/include/hardware/keymaster_defs.h
+ private static final int KM_SECURITY_LEVEL_SOFTWARE = 0;
+ private static final int KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT = 1;
+ private static final int KM_SECURITY_LEVEL_STRONG_BOX = 2;
+
+ private int attestationVersion;
+ private SecurityLevel attestationSecurityLevel;
+ private int keymasterVersion;
+ private SecurityLevel keymasterSecurityLevel;
+ private byte[] attestationChallenge;
+ private byte[] uniqueId;
+
+ private Map<Integer, ASN1Primitive> softwareEnforcedAuthorizations;
+ private Map<Integer, ASN1Primitive> teeEnforcedAuthorizations;
+
+ public int getAttestationVersion() {
+ return attestationVersion;
+ }
+
+ public SecurityLevel getAttestationSecurityLevel() {
+ return attestationSecurityLevel;
+ }
+
+ public int getKeymasterVersion() {
+ return keymasterVersion;
+ }
+
+ public SecurityLevel getKeymasterSecurityLevel() {
+ return attestationSecurityLevel;
+ }
+
+ public byte[] getAttestationChallenge() {
+ return attestationChallenge;
+ }
+
+ public byte[] getUniqueId() {
+ return uniqueId;
+ }
+
+ public Set<Integer> getSoftwareEnforcedAuthorizationTags() {
+ return softwareEnforcedAuthorizations.keySet();
+ }
+
+ public Set<Integer> getTeeEnforcedAuthorizationTags() {
+ return teeEnforcedAuthorizations.keySet();
+ }
+
+ private static ASN1Primitive findAuthorizationListEntry(
+ Map<Integer, ASN1Primitive> authorizationMap, int tag) {
+ return authorizationMap.getOrDefault(tag, null);
+ }
+
+ public Optional<Integer> getSoftwareAuthorizationInteger(int tag) {
+ ASN1Primitive entry = findAuthorizationListEntry(softwareEnforcedAuthorizations, tag);
+ return Optional.ofNullable(entry).map(ParsedAttestationRecord::getIntegerFromAsn1);
+ }
+
+ public Optional<Integer> getTeeAuthorizationInteger(int tag) {
+ ASN1Primitive entry = findAuthorizationListEntry(teeEnforcedAuthorizations, tag);
+ return Optional.ofNullable(entry).map(ParsedAttestationRecord::getIntegerFromAsn1);
+ }
+ public boolean getSoftwareAuthorizationBoolean(int tag) {
+ ASN1Primitive entry = findAuthorizationListEntry(softwareEnforcedAuthorizations, tag);
+ return entry != null;
+ }
+
+ public boolean getTeeAuthorizationBoolean(int tag) {
+ ASN1Primitive entry = findAuthorizationListEntry(teeEnforcedAuthorizations, tag);
+ return entry != null;
+ }
+
+ public Optional<byte[]> getSoftwareAuthorizationByteString(int tag) {
+ ASN1OctetString entry = (ASN1OctetString) findAuthorizationListEntry(softwareEnforcedAuthorizations, tag);
+ return Optional.ofNullable(entry).map(ASN1OctetString::getOctets);
+ }
+
+ public Optional<byte[]> getTeeAuthorizationByteString(int tag) {
+ ASN1OctetString entry = (ASN1OctetString) findAuthorizationListEntry(teeEnforcedAuthorizations, tag);
+ return Optional.ofNullable(entry).map(ASN1OctetString::getOctets);
+ }
+
+ private static boolean getBooleanFromAsn1(ASN1Encodable asn1Value) {
+ if (asn1Value instanceof ASN1Boolean) {
+ return ((ASN1Boolean) asn1Value).isTrue();
+ } else {
+ throw new RuntimeException(
+ "Boolean value expected; found " + asn1Value.getClass().getName() + " instead.");
+ }
+ }
+
+ private static int getIntegerFromAsn1(ASN1Encodable asn1Value) {
+ if (asn1Value instanceof ASN1Integer) {
+ return ((ASN1Integer) asn1Value).getValue().intValue();
+ } else if (asn1Value instanceof ASN1Enumerated) {
+ return ((ASN1Enumerated) asn1Value).getValue().intValue();
+ } else {
+ throw new IllegalArgumentException(
+ "Integer value expected; found " + asn1Value.getClass().getName() + " instead.");
+ }
+ }
+
+ private static Map<Integer, ASN1Primitive> getAuthorizationMap(
+ ASN1Encodable[] authorizationList) {
+ Map<Integer, ASN1Primitive> authorizationMap = new HashMap<>();
+ for (ASN1Encodable entry : authorizationList) {
+ ASN1TaggedObject taggedEntry = (ASN1TaggedObject) entry;
+ authorizationMap.put(taggedEntry.getTagNo(), taggedEntry.getObject());
+ }
+ return authorizationMap;
+ }
+
+ public ParsedAttestationRecord(X509Certificate cert) throws IOException {
+ byte[] attestationExtensionBytes = cert.getExtensionValue(KEY_DESCRIPTION_OID);
+ if (attestationExtensionBytes == null || attestationExtensionBytes.length == 0) {
+ throw new IllegalArgumentException("Couldn't find keystore attestation extension.");
+ }
+
+ ASN1Sequence seq;
+ try (ASN1InputStream asn1InputStream = new ASN1InputStream(attestationExtensionBytes)) {
+ // The extension contains one object, a sequence, in the
+ // Distinguished Encoding Rules (DER)-encoded form. Get the DER
+ // bytes.
+ byte[] derSequenceBytes = ((ASN1OctetString) asn1InputStream.readObject()).getOctets();
+ // Decode the bytes as an ASN1 sequence object.
+ try (ASN1InputStream seqInputStream = new ASN1InputStream(derSequenceBytes)) {
+ seq = (ASN1Sequence) seqInputStream.readObject();
+ }
+ }
+
+ this.attestationVersion = getIntegerFromAsn1(seq.getObjectAt(ATTESTATION_VERSION_INDEX));
+ this.attestationSecurityLevel =
+ securityLevelToEnum(getIntegerFromAsn1(
+ seq.getObjectAt(ATTESTATION_SECURITY_LEVEL_INDEX)));
+ this.keymasterVersion = getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_VERSION_INDEX));
+ this.keymasterSecurityLevel = securityLevelToEnum(
+ getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_SECURITY_LEVEL_INDEX)));
+ this.attestationChallenge =
+ ((ASN1OctetString) seq.getObjectAt(ATTESTATION_CHALLENGE_INDEX)).getOctets();
+ this.uniqueId = ((ASN1OctetString) seq.getObjectAt(UNIQUE_ID_INDEX)).getOctets();
+
+ this.softwareEnforcedAuthorizations = getAuthorizationMap(
+ ((ASN1Sequence) seq.getObjectAt(SW_ENFORCED_INDEX)).toArray());
+
+ this.teeEnforcedAuthorizations = getAuthorizationMap(
+ ((ASN1Sequence) seq.getObjectAt(TEE_ENFORCED_INDEX)).toArray());
+ }
+
+ private static SecurityLevel securityLevelToEnum(int securityLevel) {
+ switch (securityLevel) {
+ case KM_SECURITY_LEVEL_SOFTWARE:
+ return SecurityLevel.SOFTWARE;
+ case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT:
+ return SecurityLevel.TRUSTED_ENVIRONMENT;
+ case KM_SECURITY_LEVEL_STRONG_BOX:
+ return SecurityLevel.STRONG_BOX;
+ default:
+ throw new IllegalArgumentException("Invalid security level.");
+ }
+ }
+}
diff --git a/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java b/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java
index 8269234..48cc405 100644
--- a/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java
+++ b/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java
@@ -107,11 +107,17 @@
static Collection<X509Certificate> createCredential(IdentityCredentialStore store,
String credentialName) throws IdentityCredentialException {
+ return createCredentialWithChallenge(store, credentialName, "SomeChallenge".getBytes());
+ }
+
+ static Collection<X509Certificate> createCredentialWithChallenge(IdentityCredentialStore store,
+ String credentialName,
+ byte[] challenge) throws IdentityCredentialException {
WritableIdentityCredential wc = null;
wc = store.createCredential(credentialName, "org.iso.18013-5.2019.mdl");
Collection<X509Certificate> certificateChain =
- wc.getCredentialKeyCertificateChain("SomeChallenge".getBytes());
+ wc.getCredentialKeyCertificateChain(challenge);
// TODO: inspect cert-chain
// Profile 0 (no authentication)
diff --git a/tests/tests/match_flags/Android.bp b/tests/tests/match_flags/Android.bp
new file mode 100644
index 0000000..7debb8a
--- /dev/null
+++ b/tests/tests/match_flags/Android.bp
@@ -0,0 +1,32 @@
+// 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.
+
+android_test {
+ name: "CtsMatchFlagTestCases",
+ defaults: ["cts_defaults"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ static_libs: [
+ "ctstestrunner-axt",
+ "compatibility-device-util-axt",
+ "androidx.test.ext.junit",
+ ],
+
+ srcs: ["src/**/*.java"],
+ sdk_version: "test_current",
+}
diff --git a/tests/tests/match_flags/AndroidManifest.xml b/tests/tests/match_flags/AndroidManifest.xml
new file mode 100644
index 0000000..1b6cb42
--- /dev/null
+++ b/tests/tests/match_flags/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.matchflags.cts">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.matchflags.cts"
+ android:label="CTS tests for match flags">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+</manifest>
+
diff --git a/tests/tests/match_flags/AndroidTest.xml b/tests/tests/match_flags/AndroidTest.xml
new file mode 100644
index 0000000..a5fadab
--- /dev/null
+++ b/tests/tests/match_flags/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<configuration description="Config for match flag CTS test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsMatchFlagTestCases.apk" />
+ <option name="test-file-name" value="CtsMatchFlagsUniqueAndSharedUri.apk" />
+ <option name="test-file-name" value="CtsMatchFlagsSharedUri.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.matchflags.cts" />
+ <option name="runtime-hint" value="12m30s" />
+ </test>
+</configuration>
diff --git a/tests/tests/match_flags/OWNERS b/tests/tests/match_flags/OWNERS
new file mode 100644
index 0000000..8a44fb2
--- /dev/null
+++ b/tests/tests/match_flags/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 36137
+patb@google.com
+toddke@google.com
+chiuwinson@google.com
+rtmitchell@google.com
diff --git a/tests/tests/match_flags/app/a/Android.bp b/tests/tests/match_flags/app/a/Android.bp
new file mode 100644
index 0000000..9a5db56
--- /dev/null
+++ b/tests/tests/match_flags/app/a/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2015 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.
+
+android_test_helper_app {
+ name: "CtsMatchFlagsUniqueAndSharedUri",
+ manifest: "AndroidManifest.xml",
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+}
\ No newline at end of file
diff --git a/tests/tests/match_flags/app/a/AndroidManifest.xml b/tests/tests/match_flags/app/a/AndroidManifest.xml
new file mode 100644
index 0000000..8ad54de
--- /dev/null
+++ b/tests/tests/match_flags/app/a/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.matchflags.app.uniqueandshared">
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name="android.matchflags.cts.app.TestActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW"/>
+ <category android:name="android.intent.category.BROWSABLE"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:scheme="https"
+ android:host="unique-5gle2bs6woovjn8xabwyb3js01xl0ducci3gd3fpe622h48lyg.com"/>
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.matchflags.app.UNIQUE_ACTION"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.matchflags.app.SHARED_ACTION"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/tests/match_flags/app/a/src/android/matchflags/cts/app/TestActivity.java b/tests/tests/match_flags/app/a/src/android/matchflags/cts/app/TestActivity.java
new file mode 100644
index 0000000..e9046eb
--- /dev/null
+++ b/tests/tests/match_flags/app/a/src/android/matchflags/cts/app/TestActivity.java
@@ -0,0 +1,35 @@
+/*
+ * 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.matchflags.cts.app;
+
+import static android.content.Intent.EXTRA_RETURN_RESULT;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.util.Random;
+
+public class TestActivity extends Activity {
+}
\ No newline at end of file
diff --git a/tests/tests/match_flags/app/b/Android.bp b/tests/tests/match_flags/app/b/Android.bp
new file mode 100644
index 0000000..8c49047
--- /dev/null
+++ b/tests/tests/match_flags/app/b/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2015 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.
+
+android_test_helper_app {
+ name: "CtsMatchFlagsSharedUri",
+ manifest: "AndroidManifest.xml",
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+}
\ No newline at end of file
diff --git a/tests/tests/match_flags/app/b/AndroidManifest.xml b/tests/tests/match_flags/app/b/AndroidManifest.xml
new file mode 100644
index 0000000..6d2c9d0
--- /dev/null
+++ b/tests/tests/match_flags/app/b/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.matchflags.app.shared">
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name="android.matchflags.cts.app.TestActivity">
+ <intent-filter>
+ <action android:name="android.matchflags.app.SHARED_ACTION"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/tests/match_flags/app/b/src/android/matchflags/cts/app/TestActivity.java b/tests/tests/match_flags/app/b/src/android/matchflags/cts/app/TestActivity.java
new file mode 100644
index 0000000..e9046eb
--- /dev/null
+++ b/tests/tests/match_flags/app/b/src/android/matchflags/cts/app/TestActivity.java
@@ -0,0 +1,35 @@
+/*
+ * 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.matchflags.cts.app;
+
+import static android.content.Intent.EXTRA_RETURN_RESULT;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.util.Random;
+
+public class TestActivity extends Activity {
+}
\ No newline at end of file
diff --git a/tests/tests/match_flags/src/android/matchflags/cts/MatchFlagTests.java b/tests/tests/match_flags/src/android/matchflags/cts/MatchFlagTests.java
new file mode 100644
index 0000000..da7e212
--- /dev/null
+++ b/tests/tests/match_flags/src/android/matchflags/cts/MatchFlagTests.java
@@ -0,0 +1,134 @@
+/*
+ * 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.matchflags.cts;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.RemoteCallback;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.hamcrest.core.IsNull;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+@RunWith(AndroidJUnit4.class)
+public class MatchFlagTests {
+
+ private static final String ONLY_BROWSER_URI =
+ "https://nohandler-02xgpcssu1v7xvpek0skc905glnyu7ihjtza3eufox0mauqyri.com";
+ private static final String UNIQUE_URI =
+ "https://unique-5gle2bs6woovjn8xabwyb3js01xl0ducci3gd3fpe622h48lyg.com";
+ @Rule
+ public TestName name = new TestName();
+
+ @BeforeClass
+ public static void setup() {
+
+ }
+
+ @Test
+ public void startNoBrowserIntentWithNoMatchingApps() throws Exception {
+ Intent onlyBrowserIntent = new Intent(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .setData(Uri.parse(ONLY_BROWSER_URI));
+
+ startActivity(onlyBrowserIntent);
+
+ Intent noBrowserWithBrowserOnlyIntent = new Intent(onlyBrowserIntent)
+ .addFlags(Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER);
+
+ try {
+ startActivity(noBrowserWithBrowserOnlyIntent);
+ fail("Should not have started a browser-only intent with NON_BROWSER flag set");
+ } catch (ActivityNotFoundException e) {
+ // hooray!
+ }
+ }
+
+ @Test
+ public void startRequireDefaultWithNoDefault() throws Exception {
+ Intent sharedIntent = new Intent("android.matchflags.app.SHARED_ACTION");
+
+ startActivity(sharedIntent);
+
+ Intent sharedIntentRequireDefault = new Intent(sharedIntent)
+ .addFlags(Intent.FLAG_ACTIVITY_REQUIRE_DEFAULT);
+
+ try {
+ startActivity(sharedIntentRequireDefault);
+ fail("Should have started intent with no default when default required");
+ } catch (ActivityNotFoundException e) {
+ // hooray!
+ }
+ }
+
+ @Test
+ public void startRequireDefaultWithSingleMatch() throws Exception {
+ Intent uniqueIntent = new Intent("android.matchflags.app.UNIQUE_ACTION")
+ .addFlags(Intent.FLAG_ACTIVITY_REQUIRE_DEFAULT);
+ startActivity(uniqueIntent);
+ }
+
+ @Test
+ public void startNoBrowserRequireDefault() throws Exception {
+ Intent uniqueUriIntent = new Intent(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .setData(Uri.parse(UNIQUE_URI));
+
+ startActivity(uniqueUriIntent);
+
+ Intent uniqueUriIntentNoBrowserRequireDefault = new Intent(uniqueUriIntent)
+ .addFlags(Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER)
+ .addFlags(Intent.FLAG_ACTIVITY_REQUIRE_DEFAULT);
+
+ startActivity(uniqueUriIntentNoBrowserRequireDefault);
+ }
+
+ private static void startActivity(Intent onlyBrowserIntent) {
+ InstrumentationRegistry.getInstrumentation().getContext().startActivity(
+ onlyBrowserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ }
+
+}
diff --git a/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java b/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java
index ee23442..7b00c9a 100644
--- a/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java
@@ -42,7 +42,6 @@
import android.media.projection.MediaProjection;
import android.os.Handler;
import android.os.Looper;
-import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import androidx.test.rule.ActivityTestRule;
@@ -78,7 +77,6 @@
private AudioManager mAudioManager;
private boolean mPlaybackBeforeCapture;
private int mUid; //< UID of this test
- private int mUserId; // userId of this test
private AudioPlaybackCaptureActivity mActivity;
private MediaProjection mMediaProjection;
@Rule
@@ -90,8 +88,6 @@
public @AttributeUsage int[] excludeUsages;
public int[] matchingUids;
public int[] excludeUids;
- public int[] matchingUserIds;
- public int[] excludeUserIds;
private AudioPlaybackCaptureConfiguration build(MediaProjection projection)
throws Exception {
AudioPlaybackCaptureConfiguration.Builder apccBuilder =
@@ -117,16 +113,6 @@
apccBuilder.excludeUid(uid);
}
}
- if (matchingUserIds != null) {
- for (int userId : matchingUserIds) {
- apccBuilder.addMatchingUserId(userId);
- }
- }
- if (excludeUserIds != null) {
- for (int userId : excludeUserIds) {
- apccBuilder.excludeUserId(userId);
- }
- }
AudioPlaybackCaptureConfiguration config = apccBuilder.build();
assertCorreclyBuilt(config);
return config;
@@ -137,8 +123,6 @@
assertEqualNullIsEmpty("excludeUsages", excludeUsages, config.getExcludeUsages());
assertEqualNullIsEmpty("matchingUids", matchingUids, config.getMatchingUids());
assertEqualNullIsEmpty("excludeUids", excludeUids, config.getExcludeUids());
- assertEqualNullIsEmpty("matchingUserIds", matchingUserIds, config.getMatchingUserIds());
- assertEqualNullIsEmpty("excludeUserIds", excludeUserIds, config.getExcludeUserIds());
}
private void assertEqualNullIsEmpty(String msg, int[] expected, int[] found) {
@@ -158,8 +142,6 @@
mActivity = mActivityRule.getActivity();
mAudioManager = mActivity.getSystemService(AudioManager.class);
mUid = mActivity.getApplicationInfo().uid;
- mUserId = UserHandle.getUserHandleForUid(mActivity.getApplicationInfo().uid)
- .getIdentifier();
mMediaProjection = mActivity.waitForMediaProjection();
}
@@ -339,13 +321,6 @@
testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_UNKNOWN, EXPECT_SILENCE);
}
- @Test(expected = IllegalStateException.class)
- public void testCombineUserId() throws Exception {
- mAPCTestConfig.matchingUserIds = new int[]{ mUserId };
- mAPCTestConfig.excludeUserIds = new int[]{ mUserId + 1 };
- testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_UNKNOWN, EXPECT_SILENCE);
- }
-
@Test
public void testCaptureMatchingAllowedUsage() throws Exception {
for (int usage : ALLOWED_USAGES) {
@@ -396,17 +371,6 @@
}
@Test
- public void testCaptureMatchingUserId() throws Exception {
- mAPCTestConfig.matchingUserIds = new int[]{ mUserId };
- testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_GAME, EXPECT_DATA);
- testPlaybackCapture(OPT_OUT, AudioAttributes.USAGE_GAME, EXPECT_SILENCE);
- testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_VOICE_COMMUNICATION, EXPECT_SILENCE);
-
- mAPCTestConfig.matchingUserIds = new int[]{ mUserId + 1 };
- testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_GAME, EXPECT_SILENCE);
- }
-
- @Test
public void testCaptureExcludeUid() throws Exception {
mAPCTestConfig.excludeUids = new int[]{ 0 };
testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_GAME, EXPECT_DATA);
@@ -417,17 +381,6 @@
testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_GAME, EXPECT_SILENCE);
}
- @Test
- public void testCaptureExcludeUserId() throws Exception {
- mAPCTestConfig.excludeUserIds = new int[]{ mUserId + 1 };
- testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_GAME, EXPECT_DATA);
- testPlaybackCapture(OPT_OUT, AudioAttributes.USAGE_UNKNOWN, EXPECT_SILENCE);
- testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_VOICE_COMMUNICATION, EXPECT_SILENCE);
-
- mAPCTestConfig.excludeUserIds = new int[]{ mUserId };
- testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_GAME, EXPECT_SILENCE);
- }
-
@Test(expected = UnsupportedOperationException.class)
public void testStoppedMediaProjection() throws Exception {
mMediaProjection.stop();
diff --git a/tests/tests/media/src/android/media/cts/CodecState.java b/tests/tests/media/src/android/media/cts/CodecState.java
index 5f1d322..2384704 100644
--- a/tests/tests/media/src/android/media/cts/CodecState.java
+++ b/tests/tests/media/src/android/media/cts/CodecState.java
@@ -19,6 +19,7 @@
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
+import android.os.Handler;
import android.util.Log;
import android.view.Surface;
import java.nio.ByteBuffer;
@@ -50,6 +51,7 @@
private MediaFormat mFormat;
private MediaFormat mOutputFormat;
private NonBlockingAudioTrack mAudioTrack;
+ private OnFrameRenderedListener mOnFrameRenderedListener;
/**
* Manages audio and video playback using MediaCodec and AudioTrack.
@@ -84,6 +86,11 @@
String mime = mFormat.getString(MediaFormat.KEY_MIME);
Log.d(TAG, "CodecState::CodecState " + mime);
mIsAudio = mime.startsWith("audio/");
+
+ if (mTunneled && !mIsAudio) {
+ mOnFrameRenderedListener = new OnFrameRenderedListener();
+ codec.setOnFrameRenderedListener(mOnFrameRenderedListener, new Handler());
+ }
}
public void release() {
@@ -100,6 +107,11 @@
mAvailableOutputBufferIndices = null;
mAvailableOutputBufferInfos = null;
+ if (mOnFrameRenderedListener != null) {
+ mCodec.setOnFrameRenderedListener(null, null);
+ mOnFrameRenderedListener = null;
+ }
+
mCodec.release();
mCodec = null;
@@ -218,11 +230,6 @@
Log.d(TAG, "sampleSize: " + sampleSize + " trackIndex:" + trackIndex +
" sampleTime:" + sampleTime + " sampleFlags:" + sampleFlags);
mSawInputEOS = true;
- // FIX-ME: in tunneled mode we currently use input EOS as output EOS indicator
- // we should use stream duration
- if (mTunneled && !mIsAudio) {
- mSawOutputEOS = true;
- }
return false;
}
@@ -231,9 +238,6 @@
mSampleBaseTimeUs = sampleTime;
}
sampleTime -= mSampleBaseTimeUs;
- // FIX-ME: in tunneled mode we currently use input buffer time
- // as video presentation time. This is not accurate and should be fixed
- mPresentationTimeUs = sampleTime;
}
if ((sampleFlags & MediaExtractor.SAMPLE_FLAG_ENCRYPTED) != 0) {
@@ -255,11 +259,6 @@
Log.d(TAG, "saw input EOS on track " + mTrackIndex);
mSawInputEOS = true;
- // FIX-ME: in tunneled mode we currently use input EOS as output EOS indicator
- // we should use stream duration
- if (mTunneled && !mIsAudio) {
- mSawOutputEOS = true;
- }
mCodec.queueInputBuffer(
index, 0 /* offset */, 0 /* sampleSize */,
@@ -375,6 +374,23 @@
}
}
+ /** Callback called by the renderer in tunneling mode. */
+ private class OnFrameRenderedListener implements MediaCodec.OnFrameRenderedListener {
+ private static final long TUNNELING_EOS_PRESENTATION_TIME_US = Long.MAX_VALUE;
+
+ @Override
+ public void onFrameRendered(MediaCodec codec, long presentationTimeUs, long nanoTime) {
+ if (this != mOnFrameRenderedListener) {
+ return; // stale event
+ }
+ if (presentationTimeUs == TUNNELING_EOS_PRESENTATION_TIME_US) {
+ mSawOutputEOS = true;
+ } else {
+ mPresentationTimeUs = presentationTimeUs;
+ }
+ }
+ }
+
public long getAudioTimeUs() {
if (mAudioTrack == null) {
return 0;
diff --git a/tests/tests/media/src/android/media/cts/MediaCasTest.java b/tests/tests/media/src/android/media/cts/MediaCasTest.java
index f5e5027..27a8879 100644
--- a/tests/tests/media/src/android/media/cts/MediaCasTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCasTest.java
@@ -252,7 +252,7 @@
MediaDescrambler descrambler = null;
try {
- mediaCas = new MediaCas(sClearKeySystemId, "TIS_Session_1",
+ mediaCas = new MediaCas(getContext(), sClearKeySystemId, "TIS_Session_1",
android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE);
descrambler = new MediaDescrambler(sClearKeySystemId);
@@ -266,9 +266,7 @@
fail("Can't open session for program");
}
- if (!PropertyUtil.isVendorApiLevelNewerThan(API_LEVEL_BEFORE_CAS_SESSION + 1)) {
- Log.d(TAG, "Session Id = " + Arrays.toString(session.getSessionId()));
- }
+ Log.d(TAG, "Session Id = " + Arrays.toString(session.getSessionId()));
session.setPrivateData(pvtData);
diff --git a/tests/tests/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java b/tests/tests/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
new file mode 100644
index 0000000..0a80047
--- /dev/null
+++ b/tests/tests/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.net.cts;
+
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.net.ConnectivityDiagnosticsManager;
+import android.net.NetworkRequest;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Executor;
+
+@RunWith(AndroidJUnit4.class)
+public class ConnectivityDiagnosticsManagerTest {
+ private static final Executor INLINE_EXECUTOR = x -> x.run();
+ private static final NetworkRequest DEFAULT_REQUEST = new NetworkRequest.Builder().build();
+
+ private Context mContext;
+ private ConnectivityDiagnosticsManager mCdm;
+ private ConnectivityDiagnosticsCallback mCallback;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ mCdm = mContext.getSystemService(ConnectivityDiagnosticsManager.class);
+
+ mCallback = new ConnectivityDiagnosticsCallback() {};
+ }
+
+ @Test
+ public void testRegisterConnectivityDiagnosticsCallback() {
+ mCdm.registerConnectivityDiagnosticsCallback(DEFAULT_REQUEST, INLINE_EXECUTOR, mCallback);
+ }
+
+ @Test
+ public void testRegisterDuplicateConnectivityDiagnosticsCallback() {
+ mCdm.registerConnectivityDiagnosticsCallback(DEFAULT_REQUEST, INLINE_EXECUTOR, mCallback);
+
+ try {
+ mCdm.registerConnectivityDiagnosticsCallback(
+ DEFAULT_REQUEST, INLINE_EXECUTOR, mCallback);
+ fail("Registering the same callback twice should throw an IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testUnregisterConnectivityDiagnosticsCallback() {
+ mCdm.registerConnectivityDiagnosticsCallback(DEFAULT_REQUEST, INLINE_EXECUTOR, mCallback);
+ mCdm.unregisterConnectivityDiagnosticsCallback(mCallback);
+ }
+
+ @Test
+ public void testUnregisterUnknownConnectivityDiagnosticsCallback() {
+ // Expected to silently ignore the unregister() call
+ mCdm.unregisterConnectivityDiagnosticsCallback(mCallback);
+ }
+}
diff --git a/tests/tests/permission/telephony/OWNERS b/tests/tests/permission/telephony/OWNERS
index 423a16b..b19c963 100644
--- a/tests/tests/permission/telephony/OWNERS
+++ b/tests/tests/permission/telephony/OWNERS
@@ -1,4 +1,2 @@
set noparent
-amitmahajan@google.com
-fionaxu@google.com
-jminjie@google.com
\ No newline at end of file
+include ../../telephony/OWNERS
\ No newline at end of file
diff --git a/tests/tests/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java b/tests/tests/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java
index b050ee0..805847a 100644
--- a/tests/tests/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java
+++ b/tests/tests/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java
@@ -200,6 +200,38 @@
}
}
+ /**
+ * Tests that isManualNetworkSelectionAllowed requires permission
+ * Expects a security exception since the caller does not have carrier privileges.
+ */
+ @Test
+ public void testIsManualNetworkSelectionAllowedWithoutPermission() {
+ if (!mHasTelephony) {
+ return;
+ }
+ try {
+ mTelephonyManager.isManualNetworkSelectionAllowed();
+ fail("Expected SecurityException. App does not have carrier privileges.");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Tests that getManualNetworkSelectionPlmn requires permission
+ * Expects a security exception since the caller does not have carrier privileges.
+ */
+ @Test
+ public void testGetManualNetworkSelectionPlmnWithoutPermission() {
+ if (!mHasTelephony) {
+ return;
+ }
+ try {
+ mTelephonyManager.getManualNetworkSelectionPlmn();
+ fail("Expected SecurityException. App does not have carrier privileges.");
+ } catch (SecurityException expected) {
+ }
+ }
+
/**
* Verify that Telephony related broadcasts are protected.
*/
diff --git a/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java b/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java
index 25a343d..eeb8312 100644
--- a/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java
+++ b/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java
@@ -29,27 +29,6 @@
}
}
- protected static void noExecuteOnly() throws IOException {
- String[] maps = {"^[0-9a-z]+-[0-9a-z]+\\s+--xp.*\\/apex\\/com\\.android\\.runtime\\/.*",
- "^[0-9a-z]+-[0-9a-z]+\\s+--xp.*\\/system\\/.*"};
- for (String map : maps) {
- final Pattern mapsPattern = Pattern.compile(map);
- BufferedReader reader = new BufferedReader(new FileReader("/proc/self/maps"));
- String line;
- try {
- while ((line = reader.readLine()) != null) {
- Matcher m = mapsPattern.matcher(line);
- assertFalse("System provided libraries should be not be marked execute-only " +
- "for apps with targetSdkVersion<Q, but an execute-only segment was " +
- "found:\n" + line, m.matches());
- }
-
- } finally {
- reader.close();
- }
- }
- }
-
protected static String getProperty(String property)
throws IOException {
Process process = new ProcessBuilder("getprop", property).start();
diff --git a/tests/tests/selinux/selinuxTargetSdk25/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxTargetSdk25/src/android/security/SELinuxTargetSdkTest.java
index dddd3d9..ec2af2d 100644
--- a/tests/tests/selinux/selinuxTargetSdk25/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxTargetSdk25/src/android/security/SELinuxTargetSdkTest.java
@@ -68,8 +68,4 @@
public void testDex2oat() throws Exception {
checkDex2oatAccess(true);
}
-
- public void testNoExecuteOnly() throws IOException {
- noExecuteOnly();
- }
}
diff --git a/tests/tests/selinux/selinuxTargetSdk27/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxTargetSdk27/src/android/security/SELinuxTargetSdkTest.java
index e7c0e67..0394a18 100644
--- a/tests/tests/selinux/selinuxTargetSdk27/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxTargetSdk27/src/android/security/SELinuxTargetSdkTest.java
@@ -64,8 +64,4 @@
public void testDex2oat() throws Exception {
checkDex2oatAccess(true);
}
-
- public void testNoExecuteOnly() throws IOException {
- noExecuteOnly();
- }
}
diff --git a/tests/tests/selinux/selinuxTargetSdk28/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxTargetSdk28/src/android/security/SELinuxTargetSdkTest.java
index 1ab9706..92e1070 100644
--- a/tests/tests/selinux/selinuxTargetSdk28/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxTargetSdk28/src/android/security/SELinuxTargetSdkTest.java
@@ -64,8 +64,4 @@
public void testDex2oat() throws Exception {
checkDex2oatAccess(true);
}
-
- public void testNoExecuteOnly() throws IOException {
- noExecuteOnly();
- }
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
index 4492561..9550526 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -168,6 +168,7 @@
private static final int MIN_FPLMN_NUM = 3;
private static final String TEST_FORWARD_NUMBER = "54321";
+ private static final String TESTING_PLMN = "12345";
static {
EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>();
@@ -512,6 +513,11 @@
(tm) -> tm.getImei());
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getImei(mTelephonyManager.getSlotIndex()));
+ ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ (tm) -> tm.isManualNetworkSelectionAllowed());
+ ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ (tm) -> tm.getManualNetworkSelectionPlmn());
+
mTelephonyManager.getPhoneCount();
mTelephonyManager.getDataEnabled();
mTelephonyManager.getNetworkSpecifier();
@@ -1764,6 +1770,52 @@
}
/**
+ * Tests that the device properly reports the contents of ManualNetworkSelectionPlmn
+ * The setting is not persisted selection
+ */
+ @Test
+ public void testGetManualNetworkSelectionPlmnNonPersisted() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+
+ try {
+ ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ (tm) -> tm.setNetworkSelectionModeManual(
+ TESTING_PLMN/* operatorNumeric */, false /* persistSelection */));
+ String plmn = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ (tm) -> tm.getManualNetworkSelectionPlmn());
+ assertEquals(TESTING_PLMN, plmn);
+ } finally {
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+ (tm) -> tm.setNetworkSelectionModeAutomatic());
+ }
+ }
+
+ /**
+ * Tests that the device properly reports the contents of ManualNetworkSelectionPlmn
+ * The setting is persisted selection
+ */
+ @Test
+ public void testGetManualNetworkSelectionPlmnPersisted() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+
+ try {
+ ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ (tm) -> tm.setNetworkSelectionModeManual(
+ TESTING_PLMN/* operatorNumeric */, true /* persistSelection */));
+ String plmn = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ (tm) -> tm.getManualNetworkSelectionPlmn());
+ assertEquals(TESTING_PLMN, plmn);
+ } finally {
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+ (tm) -> tm.setNetworkSelectionModeAutomatic());
+ }
+ }
+
+ /**
* Verify that TelephonyManager.getCardIdForDefaultEuicc returns a positive value or either
* UNINITIALIZED_CARD_ID or UNSUPPORTED_CARD_ID.
*/
@@ -1855,6 +1907,19 @@
}
/**
+ * Tests that the device properly check whether selection mode was manual.
+ */
+ @Test
+ public void testIsManualNetworkSelectionAllowed() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ Log.d(TAG, "Skipping test that requires FEATURE_TELEPHONY");
+ return;
+ }
+ assertTrue(ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ (tm) -> tm.isManualNetworkSelectionAllowed()));
+ }
+
+ /**
* Construct a CallAttributes object and test getters.
*/
@Test
diff --git a/tests/tests/widget29/AndroidTest.xml b/tests/tests/widget29/AndroidTest.xml
index 39d3911..f867df3 100644
--- a/tests/tests/widget29/AndroidTest.xml
+++ b/tests/tests/widget29/AndroidTest.xml
@@ -18,6 +18,7 @@
<option name="config-descriptor:metadata" key="component" value="uitoolkit" />
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsWidgetTestCases29.apk" />