Merge "Remove vts10 suite setting" am: 9df87e51af am: a52e54eba7 am: ababcc4d46
Original change: https://android-review.googlesource.com/c/platform/cts/+/1451856
Change-Id: I68a4a9f660d594e6935cfe686766a73086d086f6
diff --git a/apps/CtsVerifier/res/layout/nls_item.xml b/apps/CtsVerifier/res/layout/nls_item.xml
index f1a10bf..e80d333 100644
--- a/apps/CtsVerifier/res/layout/nls_item.xml
+++ b/apps/CtsVerifier/res/layout/nls_item.xml
@@ -14,41 +14,51 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content" >
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
- <ImageView
- android:id="@+id/nls_status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:layout_marginTop="10dip"
- android:contentDescription="@string/pass_button_text"
- android:padding="10dip"
- android:src="@drawable/fs_indeterminate" />
-
- <TextView
- android:id="@+id/nls_instructions"
- style="@style/InstructionsSmallFont"
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_alignParentTop="true"
- android:layout_toRightOf="@id/nls_status"
- android:text="@string/nls_enable_service" />
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@+id/nls_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginTop="10dip"
+ android:contentDescription="@string/pass_button_text"
+ android:padding="10dip"
+ android:src="@drawable/fs_indeterminate" />
+
+ <TextView
+ android:id="@+id/nls_instructions"
+ style="@style/InstructionsSmallFont"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_toRightOf="@id/nls_status"
+ android:text="@string/nls_enable_service" />
+ </LinearLayout>
<Button
android:id="@+id/nls_action_button"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/nls_instructions"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
- android:layout_toRightOf="@id/nls_status"
android:onClick="actionPressed"
+ android:layout_gravity="center_horizontal"
android:text="@string/nls_start_settings" />
-</RelativeLayout>
\ No newline at end of file
+ <LinearLayout
+ android:id="@+id/feedback"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index ae3ff9c..760609b 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -2185,6 +2185,9 @@
and disabled, and that once enabled the service is able to receive notifications and
dismiss them.
</string>
+ <string name="nls_anr">This test checks that notifications are not sent with content that is
+ too long. If this test causes the test app to ANR, the test has failed.
+ </string>
<string name="msg_extras_preserved">Check that Message extras Bundle was preserved.</string>
<string name="conversation_section_ordering">If this device supports conversation notifications,
and groups them into a separate section from alerting and silent non-conversation
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
index aa5e9c0..121534a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
@@ -257,8 +257,8 @@
return item;
}
- protected View createAutoItem(ViewGroup parent, int stringId) {
- View item = mInflater.inflate(R.layout.nls_item, parent, false);
+ protected ViewGroup createAutoItem(ViewGroup parent, int stringId) {
+ ViewGroup item = (ViewGroup) mInflater.inflate(R.layout.nls_item, parent, false);
TextView instructions = item.findViewById(R.id.nls_instructions);
instructions.setText(stringId);
View button = item.findViewById(R.id.nls_action_button);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
index 12e2b93..31980af 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
@@ -51,7 +51,7 @@
public static final String JSON_STATS = "stats";
public static final String JSON_LAST_AUDIBLY_ALERTED = "last_audibly_alerted";
- ArrayList<String> mPosted = new ArrayList<String>();
+ ArrayList<StatusBarNotification> mPosted = new ArrayList<>();
ArrayMap<String, JSONObject> mNotifications = new ArrayMap<>();
ArrayMap<String, String> mNotificationKeys = new ArrayMap<>();
ArrayList<String> mRemoved = new ArrayList<String>();
@@ -77,6 +77,15 @@
return mNotifications.values();
}
+ protected StatusBarNotification getPosted(String tag) {
+ for (StatusBarNotification sbn : mPosted) {
+ if (sbn.getTag().equals(tag)) {
+ return sbn;
+ }
+ }
+ return null;
+ }
+
protected String getKeyForTag(String tag) {
return mNotificationKeys.get(tag);
}
@@ -149,7 +158,7 @@
public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
if (!mTestPackages.contains(sbn.getPackageName())) { return; }
Log.d(TAG, "posted: " + sbn.getTag());
- mPosted.add(sbn.getTag());
+ mPosted.add(sbn);
mPostedNotifications.add(sbn.getNotification());
JSONObject notification = new JSONObject();
try {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
index 42ee9e1..1d704b0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
@@ -56,6 +56,8 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.RemoteViews;
import androidx.core.app.NotificationCompat;
@@ -117,6 +119,7 @@
tests.add(new IsEnabledTest());
tests.add(new ServiceStartedTest());
tests.add(new NotificationReceivedTest());
+ tests.add(new LongMessageTest());
tests.add(new DataIntactTest());
tests.add(new AudiblyAlertedTest());
tests.add(new DismissOneTest());
@@ -261,8 +264,73 @@
@Override
protected void test() {
- List<String> result = new ArrayList<>(MockListener.getInstance().mPosted);
- if (result.size() > 0 && result.contains(mTag1)) {
+ if (MockListener.getInstance().getPosted(mTag1) != null) {
+ status = PASS;
+ } else {
+ logFail();
+ status = FAIL;
+ }
+ }
+ }
+
+ private class LongMessageTest extends InteractiveTestCase {
+ private ViewGroup mParent;
+ @Override
+ protected View inflate(ViewGroup parent) {
+ mParent = createAutoItem(parent, R.string.nls_anr);
+ return mParent;
+ }
+
+ @Override
+ protected void setUp() {
+ createChannels();
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < 20000; i++) {
+ sb.append("\u2009\u200a" + "\u200E\u200F" + "stuff");
+ }
+ Notification.Builder builder = new Notification.Builder(
+ mContext, NOTIFICATION_CHANNEL_ID)
+ .setSmallIcon(android.R.id.icon)
+ .setContentTitle("This is an long notification")
+ .setContentText("Innocuous content")
+ .setStyle(new Notification.MessagingStyle("Fake person")
+ .addMessage("hey how is it goin", 0, "Person 1")
+ .addMessage("hey", 0, "Person 1")
+ .addMessage("u there", 0, "Person 1")
+ .addMessage("how you like tHIS", 0, "Person 1")
+ .addMessage(sb.toString(), 0, "Person 1")
+ );
+ mTag1 = UUID.randomUUID().toString();
+ mId1 = NOTIFICATION_ID + 1;
+ mPackageString = "com.android.cts.verifier";
+ mNm.notify(mTag1, mId1, builder.build());
+ status = READY;
+ }
+
+ @Override
+ protected void tearDown() {
+ mNm.cancelAll();
+ MockListener.getInstance().resetData();
+ deleteChannels();
+ }
+
+ @Override
+ protected void test() {
+ StatusBarNotification sbn = MockListener.getInstance().getPosted(mTag1);
+ if (sbn == null) {
+ logFail();
+ status = FAIL;
+ } else {
+ ViewGroup parent = mParent.findViewById(R.id.feedback);
+ parent.setVisibility(View.VISIBLE);
+ final Notification.Builder recoveredBuilder = Notification.Builder.recoverBuilder(
+ NotificationListenerVerifierActivity.this,
+ sbn.getNotification());
+ RemoteViews rv = recoveredBuilder.createContentView();
+ View v = rv.apply(NotificationListenerVerifierActivity.this, parent);
+ parent.addView(v);
+ }
+ if (MockListener.getInstance().getPosted(mTag1) != null) {
status = PASS;
} else {
logFail();
@@ -985,8 +1053,7 @@
if (MockListener.getInstance() == null) {
status = PASS;
} else {
- List<String> result = new ArrayList<>(MockListener.getInstance().mPosted);
- if (result.size() == 0) {
+ if (MockListener.getInstance().mPosted.size() == 0) {
status = PASS;
} else {
logFail();
@@ -1157,8 +1224,7 @@
state = READY_TO_CHECK_FOR_UNSNOOZE;
}
} else {
- List<String> result = new ArrayList<>(MockListener.getInstance().mPosted);
- if (result.size() > 0 && result.contains(mTag1)) {
+ if (MockListener.getInstance().getPosted(mTag1) != null) {
status = PASS;
} else {
logFail();
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/SettingsUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/SettingsUtils.java
index 14f8285..03d4a50 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/SettingsUtils.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/SettingsUtils.java
@@ -197,4 +197,12 @@
public static String getSecureSetting(String key) {
return SystemUtil.runShellCommand("settings --user current get secure " + key).trim();
}
+
+ /**
+ * Get a global setting for the given user. Trims ending new line.
+ */
+ public static String getSecureSettingAsUser(int userId, String key) {
+ return SystemUtil.runShellCommand(
+ String.format("settings --user %d get secure %s", userId, key)).trim();
+ }
}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/UserUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/UserUtils.java
new file mode 100644
index 0000000..198e602
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/UserUtils.java
@@ -0,0 +1,39 @@
+/*
+ * 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.compatibility.common.util;
+
+import android.os.SystemProperties;
+
+/**
+ * Provides utilities to deal with user status.
+ */
+public final class UserUtils {
+
+ private static final String TAG = UserUtils.class.getSimpleName();
+ private static final String SYS_PROP_HEADLESS_SYSTEM_USER = "ro.fw.mu.headless_system_user";
+
+ private UserUtils() {
+ throw new AssertionError("Should not be instantiated");
+ }
+
+ /**
+ * Tells if the device is in headless system user mode.
+ */
+ public static boolean isHeadlessSystemUserMode() {
+ return SystemProperties.getBoolean(SYS_PROP_HEADLESS_SYSTEM_USER, false);
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml
index fdbb439..20be1a7 100644
--- a/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml
@@ -23,6 +23,11 @@
created by com.android.cts.appwithdata.
-->
+ <!--
+ We want to test without app data isolation, which comes with targetSdk 30.
+ -->
+ <uses-sdk android:targetSdkVersion="29" />
+
<uses-permission android:name="android.permission.INTERNET"/>
<application>
diff --git a/hostsidetests/appsecurity/test-apps/AppAccessData/src/com/android/cts/appaccessdata/AccessPrivateDataTest.java b/hostsidetests/appsecurity/test-apps/AppAccessData/src/com/android/cts/appaccessdata/AccessPrivateDataTest.java
index b1f804e..74d754d 100644
--- a/hostsidetests/appsecurity/test-apps/AppAccessData/src/com/android/cts/appaccessdata/AccessPrivateDataTest.java
+++ b/hostsidetests/appsecurity/test-apps/AppAccessData/src/com/android/cts/appaccessdata/AccessPrivateDataTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -28,10 +29,14 @@
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -45,6 +50,9 @@
/**
* Test that another app's private data cannot be accessed, while its public data can.
*
+ * These tests are for apps targeting SDK 29 (or lower). Tests for the behavior for apps targeting
+ * SDK 30+ are in {@code AppDataIsolationTests}.
+ *
* Assumes that {@link #APP_WITH_DATA_PKG} has already created the private and public data.
*/
@RunWith(AndroidJUnit4.class)
@@ -70,10 +78,15 @@
private static final Uri PRIVATE_TARGET = Uri.parse("content://com.android.cts.appwithdata/");
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getContext();
+ }
+
/**
- * Tests that another app's private data cannot be accessed. It includes file
- * and detailed traffic stats.
- * @throws IOException
+ * Tests that another app's private data cannot be accessed.
*/
@Test
public void testAccessPrivateData() throws IOException {
@@ -90,17 +103,8 @@
}
}
- private ApplicationInfo getApplicationInfo(String packageName) {
- try {
- return InstrumentationRegistry.getContext().getPackageManager().getApplicationInfo(packageName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- throw new IllegalStateException("Expected package not found: " + e);
- }
- }
-
/**
- * Tests that another app's public file can be accessed
- * @throws IOException
+ * Tests that another app's public file cannot be accessed
*/
@Test
public void testAccessPublicData() throws IOException {
@@ -117,6 +121,21 @@
}
}
+ /**
+ * Tests that we can't even access another app's root private data dir.
+ */
+ @Test
+ public void testStatPrivateDataDir() {
+ ApplicationInfo applicationInfo = getApplicationInfo(APP_WITH_DATA_PKG);
+ String path = applicationInfo.dataDir;
+ try {
+ Os.stat(path);
+ fail("Was able to stat() another app's private data dir: " + path);
+ } catch (ErrnoException expected) {
+ assertEquals(path, OsConstants.EACCES, expected.errno);
+ }
+ }
+
@Test
public void testAccessProcQtaguidTrafficStatsFailed() {
// For untrusted app with SDK P or above, proc/net/xt_qtaguid files are no long readable.
@@ -130,7 +149,7 @@
public void testAccessPrivateTrafficStats() {
int otherAppUid = -1;
try {
- otherAppUid = InstrumentationRegistry.getContext()
+ otherAppUid = mContext
.createPackageContext(APP_WITH_DATA_PKG, 0 /*flags*/)
.getApplicationInfo().uid;
} catch (NameNotFoundException e) {
@@ -159,7 +178,7 @@
final long txp = TrafficStats.getUidTxPackets(uid);
// Start remote server
- final int port = InstrumentationRegistry.getContext().getContentResolver().call(PRIVATE_TARGET, "start", null, null)
+ final int port = mContext.getContentResolver().call(PRIVATE_TARGET, "start", null, null)
.getInt("port");
// Try talking to them, but shift blame
@@ -169,7 +188,7 @@
Bundle extras = new Bundle();
extras.putParcelable("fd", ParcelFileDescriptor.fromSocket(socket));
- InstrumentationRegistry.getContext().getContentResolver().call(PRIVATE_TARGET, "tag", null, extras);
+ mContext.getContentResolver().call(PRIVATE_TARGET, "tag", null, extras);
socket.connect(new InetSocketAddress("localhost", port));
@@ -181,7 +200,7 @@
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
- InstrumentationRegistry.getContext().getContentResolver().call(PRIVATE_TARGET, "stop", null, null);
+ mContext.getContentResolver().call(PRIVATE_TARGET, "stop", null, null);
}
SystemClock.sleep(1000);
@@ -192,4 +211,12 @@
assertEquals(txb, TrafficStats.getUidTxBytes(uid));
assertEquals(txp, TrafficStats.getUidTxPackets(uid));
}
+
+ private ApplicationInfo getApplicationInfo(String packageName) {
+ try {
+ return mContext.getPackageManager().getApplicationInfo(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalStateException("Expected package not found: " + e);
+ }
+ }
}
diff --git a/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml
index cf85210..ea8a824 100644
--- a/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml
@@ -23,6 +23,12 @@
access.
-->
+ <!--
+ We want to test without app data isolation, which comes with targetSdk 30.
+ -->
+ <uses-sdk android:targetSdkVersion="29" />
+
+
<uses-permission android:name="android.permission.INTERNET" />
<application>
diff --git a/hostsidetests/media/app/MediaSessionTest/src/android/media/session/cts/MediaSessionManagerTest.java b/hostsidetests/media/app/MediaSessionTest/src/android/media/session/cts/MediaSessionManagerTest.java
index b752b41..6ed0e60 100644
--- a/hostsidetests/media/app/MediaSessionTest/src/android/media/session/cts/MediaSessionManagerTest.java
+++ b/hostsidetests/media/app/MediaSessionTest/src/android/media/session/cts/MediaSessionManagerTest.java
@@ -18,6 +18,7 @@
import static android.media.cts.MediaSessionTestHelperConstants.MEDIA_SESSION_TEST_HELPER_PKG;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -25,6 +26,8 @@
import android.content.Context;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
+import android.media.session.MediaSessionManager.RemoteUserInfo;
+import android.os.Process;
import android.service.notification.NotificationListenerService;
import androidx.test.InstrumentationRegistry;
@@ -42,15 +45,16 @@
*/
@SmallTest
public class MediaSessionManagerTest extends NotificationListenerService {
- private ComponentName mComponentName;
+ private Context mContext;
private MediaSessionManager mMediaSessionManager;
+ private ComponentName mComponentName;
@Before
public void setUp() throws Exception {
- Context context = InstrumentationRegistry.getTargetContext();
- mMediaSessionManager = (MediaSessionManager) context.getSystemService(
- Context.MEDIA_SESSION_SERVICE);
- mComponentName = new ComponentName(context, MediaSessionManagerTest.class);
+ mContext = InstrumentationRegistry.getTargetContext();
+ mMediaSessionManager =
+ mContext.getSystemService(MediaSessionManager.class);
+ mComponentName = new ComponentName(mContext, MediaSessionManagerTest.class);
}
/**
@@ -95,4 +99,24 @@
List<MediaController> controllers = mMediaSessionManager.getActiveSessions(mComponentName);
assertTrue(controllers.isEmpty());
}
+
+ /**
+ * Tests if this application is trusted.
+ */
+ @Test
+ public void testIsTrusted_returnsTrue() throws Exception {
+ RemoteUserInfo userInfo = new RemoteUserInfo(
+ mContext.getPackageName(), Process.myPid(), Process.myUid());
+ assertTrue(mMediaSessionManager.isTrustedForMediaControl(userInfo));
+ }
+
+ /**
+ * Tests if this application isn't trusted.
+ */
+ @Test
+ public void testIsTrusted_returnsFalse() throws Exception {
+ RemoteUserInfo userInfo = new RemoteUserInfo(
+ mContext.getPackageName(), Process.myPid(), Process.myUid());
+ assertFalse(mMediaSessionManager.isTrustedForMediaControl(userInfo));
+ }
}
diff --git a/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java b/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java
index 750e98c..e4956b0 100644
--- a/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java
+++ b/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java
@@ -251,6 +251,37 @@
runTest("testGetActiveSessions_hasMediaSessionFromMediaSessionTestHelper");
}
+ @AppModeFull
+ @RequiresDevice
+ public void testIsTrusted_withEnabledNotificationListener_returnsTrue() throws Exception {
+ if (!canCreateAdditionalUsers(1)) {
+ CLog.logAndDisplay(LogLevel.INFO,
+ "Cannot create a new user. Skipping multi-user test cases.");
+ return;
+ }
+
+ int newUserId = createAndStartUser();
+ setAllowGetActiveSessionForTest(true, newUserId);
+ installAppAsUser(DEVICE_SIDE_TEST_APK, newUserId, false);
+ runTestAsUser("testIsTrusted_returnsTrue", newUserId);
+ }
+
+ @AppModeFull
+ @RequiresDevice
+ public void testIsTrusted_withoutEnabledNotificationListener_returnsFalse()
+ throws Exception {
+ if (!canCreateAdditionalUsers(1)) {
+ CLog.logAndDisplay(LogLevel.INFO,
+ "Cannot create a new user. Skipping multi-user test cases.");
+ return;
+ }
+
+ int newUserId = createAndStartUser();
+ setAllowGetActiveSessionForTest(false, newUserId);
+ installAppAsUser(DEVICE_SIDE_TEST_APK, newUserId, false);
+ runTestAsUser("testIsTrusted_returnsFalse", newUserId);
+ }
+
private void runTest(String testMethodName) throws DeviceNotAvailableException {
runTestAsUser(testMethodName, getDevice().getPrimaryUserId());
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index 185f44e..87be4c9 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -118,6 +118,7 @@
"com.android.cts.device.statsd.emptyapp";
private static final String TEST_REMOTE_DIR = "/data/local/tmp/statsd";
private static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
+ private static final String ACTION_LONG_SLEEP_WHILE_TOP = "action.long_sleep_top";
private static final int WAIT_TIME_FOR_CONFIG_UPDATE_MS = 200;
private static final int EXTRA_WAIT_TIME_MS = 5_000; // as buffer when app starting/stopping.
@@ -2146,18 +2147,48 @@
AppUsageEventOccurred.EventType.MOVE_TO_BACKGROUND_VALUE));
List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
- createAndUploadConfig(Atom.APP_USAGE_EVENT_OCCURRED_FIELD_NUMBER, false); // False: does not use attribution.
+ createAndUploadConfig(Atom.APP_USAGE_EVENT_OCCURRED_FIELD_NUMBER, false);
Thread.sleep(WAIT_TIME_FOR_CONFIG_UPDATE_MS);
getDevice().executeShellCommand(String.format(
- "am start -n '%s' -e %s %s",
- "com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity",
- "action", ACTION_SHOW_APPLICATION_OVERLAY));
+ "am start -n '%s' -e %s %s",
+ "com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity",
+ "action", ACTION_SHOW_APPLICATION_OVERLAY));
final int waitTime = EXTRA_WAIT_TIME_MS + 5_000; // Overlay may need to sit there a while.
Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS);
List<EventMetricData> data = getEventMetricDataList();
- Function<Atom, Integer> appUsageStateFunction = atom -> atom.getAppUsageEventOccurred().getEventType().getNumber();
+ Function<Atom, Integer> appUsageStateFunction =
+ atom -> atom.getAppUsageEventOccurred().getEventType().getNumber();
+ popUntilFind(data, onStates, appUsageStateFunction); // clear out initial appusage states.s
+ assertStatesOccurred(stateSet, data, 0, appUsageStateFunction);
+ }
+
+ public void testAppForceStopUsageEvent() throws Exception {
+ Set<Integer> onStates = new HashSet<>(Arrays.asList(
+ AppUsageEventOccurred.EventType.MOVE_TO_FOREGROUND_VALUE));
+ Set<Integer> offStates = new HashSet<>(Arrays.asList(
+ AppUsageEventOccurred.EventType.MOVE_TO_BACKGROUND_VALUE));
+
+ List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
+ createAndUploadConfig(Atom.APP_USAGE_EVENT_OCCURRED_FIELD_NUMBER, false);
+ Thread.sleep(WAIT_TIME_FOR_CONFIG_UPDATE_MS);
+
+ getDevice().executeShellCommand(String.format(
+ "am start -n '%s' -e %s %s",
+ "com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity",
+ "action", ACTION_LONG_SLEEP_WHILE_TOP));
+ final int waitTime = EXTRA_WAIT_TIME_MS + 5_000;
+ Thread.sleep(waitTime);
+
+ getDevice().executeShellCommand(String.format(
+ "am force-stop %s",
+ "com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity"));
+ Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS);
+
+ List<EventMetricData> data = getEventMetricDataList();
+ Function<Atom, Integer> appUsageStateFunction =
+ atom -> atom.getAppUsageEventOccurred().getEventType().getNumber();
popUntilFind(data, onStates, appUsageStateFunction); // clear out initial appusage states.
assertStatesOccurred(stateSet, data, 0, appUsageStateFunction);
}
diff --git a/tests/app/Android.bp b/tests/app/Android.bp
index 6428109..15d8131 100644
--- a/tests/app/Android.bp
+++ b/tests/app/Android.bp
@@ -33,7 +33,10 @@
"platformprotosnano",
"permission-test-util-lib"
],
- srcs: ["src/**/*.java"],
+ srcs: [
+ "src/**/*.java",
+ "NotificationListener/src/com/android/test/notificationlistener/INotificationUriAccessService.aidl",
+ ],
// Tag this module as a cts test artifact
test_suites: [
"cts",
diff --git a/tests/app/AndroidTest.xml b/tests/app/AndroidTest.xml
index 645d233..465b633 100644
--- a/tests/app/AndroidTest.xml
+++ b/tests/app/AndroidTest.xml
@@ -33,6 +33,8 @@
<option name="test-file-name" value="CtsCantSaveState1.apk" />
<option name="test-file-name" value="CtsCantSaveState2.apk" />
<option name="test-file-name" value="NotificationDelegator.apk" />
+ <option name="test-file-name" value="NotificationProvider.apk" />
+ <option name="test-file-name" value="NotificationListener.apk" />
<option name="test-file-name" value="StorageDelegator.apk" />
<option name="test-file-name" value="CtsActivityManagerApi29.apk" />
</target_preparer>
diff --git a/tests/app/NotificationListener/Android.bp b/tests/app/NotificationListener/Android.bp
new file mode 100644
index 0000000..96a0c3c
--- /dev/null
+++ b/tests/app/NotificationListener/Android.bp
@@ -0,0 +1,39 @@
+// 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: "NotificationListener",
+ defaults: ["cts_support_defaults"],
+ srcs: [
+ "**/*.java",
+ "**/*.kt",
+ "src/com/android/test/notificationlistener/INotificationUriAccessService.aidl",
+ ],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts10",
+ "general-tests",
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ static_libs: [
+ "androidx.test.rules",
+ "platform-test-annotations",
+ ],
+ platform_apis: true,
+ sdk_version: "test_current",
+}
diff --git a/tests/app/NotificationListener/AndroidManifest.xml b/tests/app/NotificationListener/AndroidManifest.xml
new file mode 100644
index 0000000..f510637
--- /dev/null
+++ b/tests/app/NotificationListener/AndroidManifest.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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.notificationlistener">
+ <application android:label="Notification Listener">
+
+ <service android:name=".NotificationUriAccessService"
+ android:exported="true"/>
+
+ <service android:name=".TestNotificationListener"
+ android:exported="true"
+ android:label="TestNotificationListener"
+ android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
+ <intent-filter>
+ <action android:name="android.service.notification.NotificationListenerService"/>
+ </intent-filter>
+ </service>
+
+ </application>
+</manifest>
diff --git a/tests/app/NotificationListener/res/layout/activity.xml b/tests/app/NotificationListener/res/layout/activity.xml
new file mode 100644
index 0000000..f001f29
--- /dev/null
+++ b/tests/app/NotificationListener/res/layout/activity.xml
@@ -0,0 +1,31 @@
+<?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:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="25dp"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:text="@string/activity_description"
+ />
+
+</LinearLayout>
diff --git a/tests/app/NotificationListener/res/values/strings.xml b/tests/app/NotificationListener/res/values/strings.xml
new file mode 100644
index 0000000..e19d5bf
--- /dev/null
+++ b/tests/app/NotificationListener/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<resources>
+ <string name="activity_description">This app has a listener and an service used for tests</string>
+</resources>
\ No newline at end of file
diff --git a/tests/app/NotificationListener/src/com/android/test/notificationlistener/INotificationUriAccessService.aidl b/tests/app/NotificationListener/src/com/android/test/notificationlistener/INotificationUriAccessService.aidl
new file mode 100644
index 0000000..eb93179
--- /dev/null
+++ b/tests/app/NotificationListener/src/com/android/test/notificationlistener/INotificationUriAccessService.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.notificationlistener;
+
+interface INotificationUriAccessService {
+ void ensureNotificationListenerServiceConnected(boolean connected);
+ boolean isFileUriAccessible(in android.net.Uri uri);
+}
diff --git a/tests/app/NotificationListener/src/com/android/test/notificationlistener/NotificationUriAccessService.kt b/tests/app/NotificationListener/src/com/android/test/notificationlistener/NotificationUriAccessService.kt
new file mode 100644
index 0000000..5e6e469
--- /dev/null
+++ b/tests/app/NotificationListener/src/com/android/test/notificationlistener/NotificationUriAccessService.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.test.notificationlistener
+
+import android.app.NotificationManager
+import android.app.Service
+import android.content.Intent
+import android.net.Uri
+import android.os.IBinder
+import java.io.IOException
+
+class NotificationUriAccessService : Service() {
+ private inner class MyNotificationUriAccessService : INotificationUriAccessService.Stub() {
+ @Throws(IllegalStateException::class)
+ override fun ensureNotificationListenerServiceConnected(ensureConnected: Boolean) {
+ val nm = getSystemService(NotificationManager::class.java)!!
+ val testListener = TestNotificationListener.componentName
+ check(nm.isNotificationListenerAccessGranted(testListener) == ensureConnected) {
+ "$testListener has incorrect listener access; expected=$ensureConnected"
+ }
+ val listener = TestNotificationListener.instance
+ if (ensureConnected) {
+ check(listener != null) {
+ "$testListener has not been created, but should be connected"
+ }
+ }
+ val isConnected = listener?.isConnected ?: false
+ check(isConnected == ensureConnected) {
+ "$testListener has incorrect listener connection state; expected=$ensureConnected"
+ }
+ }
+
+ override fun isFileUriAccessible(uri: Uri?): Boolean {
+ try {
+ contentResolver.openAssetFile(uri!!, "r", null).use { return true }
+ } catch (e: SecurityException) {
+ return false
+ } catch (e: IOException) {
+ throw IllegalStateException("Exception without security error", e)
+ }
+ }
+ }
+
+ private val mBinder = MyNotificationUriAccessService()
+ override fun onBind(intent: Intent): IBinder? {
+ return mBinder
+ }
+}
\ No newline at end of file
diff --git a/tests/app/NotificationListener/src/com/android/test/notificationlistener/TestNotificationListener.kt b/tests/app/NotificationListener/src/com/android/test/notificationlistener/TestNotificationListener.kt
new file mode 100644
index 0000000..19c7d82
--- /dev/null
+++ b/tests/app/NotificationListener/src/com/android/test/notificationlistener/TestNotificationListener.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.test.notificationlistener
+
+import android.content.ComponentName
+import android.service.notification.NotificationListenerService
+
+class TestNotificationListener : NotificationListenerService() {
+ var isConnected = false
+
+ override fun onListenerConnected() {
+ super.onListenerConnected()
+ instance = this
+ isConnected = true
+ }
+
+ override fun onListenerDisconnected() {
+ super.onListenerDisconnected()
+ isConnected = false
+ }
+
+ companion object {
+ var instance: TestNotificationListener? = null
+ private set
+ val componentName: ComponentName by lazy {
+ val javaClass = TestNotificationListener::class.java
+ ComponentName(javaClass.getPackage().name, javaClass.name)
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionUserApp/Android.bp b/tests/app/NotificationProvider/Android.bp
similarity index 77%
copy from tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionUserApp/Android.bp
copy to tests/app/NotificationProvider/Android.bp
index f720d7d..26e69d7 100644
--- a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionUserApp/Android.bp
+++ b/tests/app/NotificationProvider/Android.bp
@@ -1,4 +1,4 @@
-// Copyright (C) 2019 The Android Open Source Project
+// Copyright (C) 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -11,16 +11,17 @@
// 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: "CtsInstalltimePermissionUserApp",
- defaults: ["cts_defaults"],
- sdk_version: "current",
+ name: "NotificationProvider",
+ defaults: ["cts_support_defaults"],
+ srcs: ["**/*.java", "**/*.kt"],
// Tag this module as a cts test artifact
test_suites: [
"cts",
+ "vts10",
"general-tests",
],
- certificate: ":cts-testkey2",
+ platform_apis: true,
+ sdk_version: "current",
}
diff --git a/tests/app/NotificationProvider/AndroidManifest.xml b/tests/app/NotificationProvider/AndroidManifest.xml
new file mode 100644
index 0000000..09ae4b0
--- /dev/null
+++ b/tests/app/NotificationProvider/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.notificationprovider">
+ <application android:label="Notification Provider">
+ <activity android:name=".RichNotificationActivity" android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+
+ <provider
+ android:name=".AssetFileProvider"
+ android:authorities="com.android.test.notificationprovider.provider"
+ android:exported="false"
+ android:grantUriPermissions="true"
+ />
+
+ </application>
+</manifest>
diff --git a/tests/app/NotificationProvider/assets/background7.png b/tests/app/NotificationProvider/assets/background7.png
new file mode 100644
index 0000000..20c22f7
--- /dev/null
+++ b/tests/app/NotificationProvider/assets/background7.png
Binary files differ
diff --git a/tests/app/NotificationProvider/assets/background8.png b/tests/app/NotificationProvider/assets/background8.png
new file mode 100644
index 0000000..a7a593d
--- /dev/null
+++ b/tests/app/NotificationProvider/assets/background8.png
Binary files differ
diff --git a/tests/app/NotificationProvider/res/layout/activity.xml b/tests/app/NotificationProvider/res/layout/activity.xml
new file mode 100644
index 0000000..f001f29
--- /dev/null
+++ b/tests/app/NotificationProvider/res/layout/activity.xml
@@ -0,0 +1,31 @@
+<?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:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="25dp"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:text="@string/activity_description"
+ />
+
+</LinearLayout>
diff --git a/tests/app/NotificationProvider/res/values/strings.xml b/tests/app/NotificationProvider/res/values/strings.xml
new file mode 100644
index 0000000..266ea53
--- /dev/null
+++ b/tests/app/NotificationProvider/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<resources>
+ <string name="activity_description">This app has a provider and posts notifications</string>
+</resources>
\ No newline at end of file
diff --git a/tests/app/NotificationProvider/src/com/android/test/notificationprovider/AssetFileProvider.kt b/tests/app/NotificationProvider/src/com/android/test/notificationprovider/AssetFileProvider.kt
new file mode 100644
index 0000000..af10f1b
--- /dev/null
+++ b/tests/app/NotificationProvider/src/com/android/test/notificationprovider/AssetFileProvider.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.test.notificationprovider
+
+import android.content.ContentProvider
+import android.content.ContentValues
+import android.content.res.AssetFileDescriptor
+import android.database.Cursor
+import android.net.Uri
+
+class AssetFileProvider : ContentProvider() {
+ override fun onCreate(): Boolean {
+ return true
+ }
+
+ override fun openAssetFile(uri: Uri, mode: String): AssetFileDescriptor? {
+ val assets = context?.assets
+ val filename = uri.lastPathSegment
+ if (mode == "r" && assets != null && filename != null) {
+ return assets.openFd(filename)
+ }
+ return super.openAssetFile(uri, mode)
+ }
+
+ override fun query(
+ uri: Uri,
+ projection: Array<String>?,
+ selection: String?,
+ selectionArgs: Array<String>?,
+ sortOrder: String?
+ ): Cursor? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getType(uri: Uri): String? {
+ return null
+ }
+
+ override fun insert(uri: Uri, values: ContentValues?): Uri? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
+ throw UnsupportedOperationException()
+ }
+
+ override fun update(
+ uri: Uri,
+ values: ContentValues?,
+ selection: String?,
+ selectionArgs: Array<String>?
+ ): Int {
+ throw UnsupportedOperationException()
+ }
+}
\ No newline at end of file
diff --git a/tests/app/NotificationProvider/src/com/android/test/notificationprovider/RichNotificationActivity.kt b/tests/app/NotificationProvider/src/com/android/test/notificationprovider/RichNotificationActivity.kt
new file mode 100644
index 0000000..4197760
--- /dev/null
+++ b/tests/app/NotificationProvider/src/com/android/test/notificationprovider/RichNotificationActivity.kt
@@ -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 com.android.test.notificationprovider
+
+import android.app.Activity
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.content.Context
+import android.os.Bundle
+
+/**
+ * Used by NotificationManagerTest for testing policy around content uris.
+ */
+class RichNotificationActivity : Activity() {
+ companion object {
+ const val NOTIFICATION_CHANNEL_ID = "NotificationManagerTest"
+ const val EXTRA_ACTION = "action"
+ const val ACTION_SEND_7 = "send-7"
+ const val ACTION_SEND_8 = "send-8"
+ const val ACTION_CANCEL_7 = "cancel-7"
+ const val ACTION_CANCEL_8 = "cancel-8"
+ }
+
+ enum class NotificationPreset(val id: Int) {
+ Preset7(7),
+ Preset8(8);
+
+ fun build(context: Context): Notification {
+ val extras = Bundle()
+ extras.putString(Notification.EXTRA_BACKGROUND_IMAGE_URI,
+ "content://com.android.test.notificationprovider.provider/background$id.png")
+ return Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
+ .setContentTitle("Rich Notification #$id")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .addExtras(extras)
+ .build()
+ }
+ }
+
+ public override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity)
+ when (intent?.extras?.getString(EXTRA_ACTION)) {
+ ACTION_SEND_7 -> sendNotification(NotificationPreset.Preset7)
+ ACTION_SEND_8 -> sendNotification(NotificationPreset.Preset8)
+ ACTION_CANCEL_7 -> cancelNotification(NotificationPreset.Preset7)
+ ACTION_CANCEL_8 -> cancelNotification(NotificationPreset.Preset8)
+ else -> {
+ // reset both
+ cancelNotification(NotificationPreset.Preset7)
+ cancelNotification(NotificationPreset.Preset8)
+ }
+ }
+ finish()
+ }
+
+ private val notificationManager by lazy { getSystemService(NotificationManager::class.java)!! }
+
+ private fun sendNotification(preset: NotificationPreset) {
+ notificationManager.createNotificationChannel(NotificationChannel(NOTIFICATION_CHANNEL_ID,
+ "Notifications", NotificationManager.IMPORTANCE_DEFAULT))
+ notificationManager.notify(preset.id, preset.build(this))
+ }
+
+ private fun cancelNotification(preset: NotificationPreset) {
+ notificationManager.cancel(preset.id)
+ }
+}
\ No newline at end of file
diff --git a/tests/app/app/src/android/app/stubs/CommandReceiver.java b/tests/app/app/src/android/app/stubs/CommandReceiver.java
index 12a24c8..5a13eab 100644
--- a/tests/app/app/src/android/app/stubs/CommandReceiver.java
+++ b/tests/app/app/src/android/app/stubs/CommandReceiver.java
@@ -48,6 +48,7 @@
public static final int COMMAND_STOP_ACTIVITY = 11;
public static final int COMMAND_CREATE_FGSL_PENDING_INTENT = 12;
public static final int COMMAND_SEND_FGSL_PENDING_INTENT = 13;
+ public static final int COMMAND_BIND_FOREGROUND_SERVICE = 14;
public static final String EXTRA_COMMAND = "android.app.stubs.extra.COMMAND";
public static final String EXTRA_TARGET_PACKAGE = "android.app.stubs.extra.TARGET_PACKAGE";
@@ -82,7 +83,7 @@
+ intent);
switch (command) {
case COMMAND_BIND_SERVICE:
- doBindService(context, intent);
+ doBindService(context, intent, SERVICE_NAME);
break;
case COMMAND_UNBIND_SERVICE:
doUnbindService(context, intent);
@@ -120,15 +121,18 @@
case COMMAND_SEND_FGSL_PENDING_INTENT:
doSendFgslPendingIntent(context, intent);
break;
+ case COMMAND_BIND_FOREGROUND_SERVICE:
+ doBindService(context, intent, FG_LOCATION_SERVICE_NAME);
+ break;
}
}
- private void doBindService(Context context, Intent commandIntent) {
+ private void doBindService(Context context, Intent commandIntent, String serviceName) {
String targetPackage = getTargetPackage(commandIntent);
int flags = getFlags(commandIntent);
Intent bindIntent = new Intent();
- bindIntent.setComponent(new ComponentName(targetPackage, SERVICE_NAME));
+ bindIntent.setComponent(new ComponentName(targetPackage, serviceName));
ServiceConnection connection = addServiceConnection(targetPackage);
diff --git a/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java b/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
index 9302e67..c982cd6 100644
--- a/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
@@ -327,4 +327,43 @@
uid2Watcher.finish();
}
}
+
+
+ public void testFgsLocationStartFromBGWithBind() throws Exception {
+ ApplicationInfo app1Info = mContext.getPackageManager().getApplicationInfo(
+ PACKAGE_NAME_APP1, 0);
+ WatchUidRunner uid1Watcher = new WatchUidRunner(mInstrumentation, app1Info.uid,
+ WAITFOR_MSEC);
+
+ try {
+ // Package1 is in BG state, bind FGSL in package1 first.
+ CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_FOREGROUND_SERVICE,
+ PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null);
+ Bundle bundle = new Bundle();
+ bundle.putInt(LocalForegroundServiceLocation.EXTRA_FOREGROUND_SERVICE_TYPE,
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION);
+ // Then start FGSL in package1, it won't get location capability.
+ CommandReceiver.sendCommand(mContext,
+ CommandReceiver.COMMAND_START_FOREGROUND_SERVICE_LOCATION,
+ PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, bundle);
+
+ // Package1 is in FGS state, but won't get location capability.
+ uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE,
+ WatchUidRunner.STATE_FG_SERVICE,
+ new Integer(PROCESS_CAPABILITY_NONE));
+
+ // unbind service.
+ CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
+ PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null);
+ // stop FGSL
+ CommandReceiver.sendCommand(mContext,
+ CommandReceiver.COMMAND_STOP_FOREGROUND_SERVICE_LOCATION,
+ PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null);
+ uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE,
+ WatchUidRunner.STATE_CACHED_EMPTY,
+ new Integer(PROCESS_CAPABILITY_NONE));
+ } finally {
+ uid1Watcher.finish();
+ }
+ }
}
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index 1dfe129..1cc0b6e 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -75,13 +75,16 @@
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.OperationApplicationException;
+import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
+import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.drawable.Icon;
@@ -91,6 +94,7 @@
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -111,12 +115,12 @@
import android.util.Log;
import android.widget.RemoteViews;
-import androidx.test.InstrumentationRegistry;
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.FeatureUtil;
import com.android.compatibility.common.util.SystemUtil;
-
-import junit.framework.Assert;
+import com.android.test.notificationlistener.INotificationUriAccessService;
import java.io.BufferedReader;
import java.io.FileInputStream;
@@ -133,11 +137,15 @@
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/* This tests NotificationListenerService together with NotificationManager, as you need to have
* notifications to manipulate in order to test the listener service. */
public class NotificationManagerTest extends AndroidTestCase {
+ public static final String NOTIFICATIONPROVIDER = "com.android.test.notificationprovider";
+ public static final String RICH_NOTIFICATION_ACTIVITY =
+ "com.android.test.notificationprovider.RichNotificationActivity";
final String TAG = NotificationManagerTest.class.getSimpleName();
final boolean DEBUG = false;
static final String NOTIFICATION_CHANNEL_ID = "NotificationManagerTest";
@@ -160,6 +168,7 @@
private List<String> mRuleIds;
private BroadcastReceiver mBubbleBroadcastReceiver;
private boolean mBubblesEnabledSettingToRestore;
+ private INotificationUriAccessService mNotificationUriAccessService;
@Override
protected void setUp() throws Exception {
@@ -195,7 +204,8 @@
// delay between tests so notifications aren't dropped by the rate limiter
try {
Thread.sleep(500);
- } catch(InterruptedException e) {}
+ } catch (InterruptedException e) {
+ }
}
@Override
@@ -222,8 +232,7 @@
suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(),
false);
- toggleListenerAccess(TestNotificationListener.getId(),
- InstrumentationRegistry.getInstrumentation(), false);
+ toggleListenerAccess(false);
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), false);
@@ -237,17 +246,17 @@
setBubblesGlobal(mBubblesEnabledSettingToRestore);
}
- private boolean isNotificationCancelled(int id, boolean all) {
+ private void assertNotificationCancelled(int id, boolean all) {
for (long totalWait = 0; totalWait < MAX_WAIT_TIME; totalWait += SHORT_WAIT_TIME) {
- StatusBarNotification sbn = findPostedNotification(id, all);
- if (sbn == null) return true;
+ StatusBarNotification sbn = findNotificationNoWait(id, all);
+ if (sbn == null) return;
try {
Thread.sleep(SHORT_WAIT_TIME);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
- return false;
+ assertNull(findNotificationNoWait(id, all));
}
private void insertSingleContact(String name, String phone, String email, boolean starred) {
@@ -309,8 +318,8 @@
try {
Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
Uri.encode(phone));
- String[] projection = new String[] { ContactsContract.Contacts._ID,
- ContactsContract.Contacts.LOOKUP_KEY };
+ String[] projection = new String[]{ContactsContract.Contacts._ID,
+ ContactsContract.Contacts.LOOKUP_KEY};
c = mContext.getContentResolver().query(phoneUri, projection, null, null, null);
if (c != null && c.getCount() > 0) {
c.moveToFirst();
@@ -333,25 +342,28 @@
private StatusBarNotification findPostedNotification(int id, boolean all) {
// notification is a bit asynchronous so it may take a few ms to appear in
// getActiveNotifications()
- // we will check for it for up to 300ms before giving up
- StatusBarNotification n = null;
- for (int tries = 3; tries-- > 0; ) {
- final StatusBarNotification[] sbns = getActiveNotifications(all);
- for (StatusBarNotification sbn : sbns) {
- Log.d(TAG, "Found " + sbn.getKey());
- if (sbn.getId() == id) {
- n = sbn;
- break;
- }
+ // we will check for it for up to 1000ms before giving up
+ for (long totalWait = 0; totalWait < MAX_WAIT_TIME; totalWait += SHORT_WAIT_TIME) {
+ StatusBarNotification n = findNotificationNoWait(id, all);
+ if (n != null) {
+ return n;
}
- if (n != null) break;
try {
- Thread.sleep(100);
+ Thread.sleep(SHORT_WAIT_TIME);
} catch (InterruptedException ex) {
// pass
}
}
- return n;
+ return findNotificationNoWait(id, all);
+ }
+
+ private StatusBarNotification findNotificationNoWait(int id, boolean all) {
+ for (StatusBarNotification sbn : getActiveNotifications(all)) {
+ if (sbn.getId() == id) {
+ return sbn;
+ }
+ }
+ return null;
}
private StatusBarNotification[] getActiveNotifications(boolean all) {
@@ -462,8 +474,7 @@
private void setUpNotifListener() {
try {
- toggleListenerAccess(TestNotificationListener.getId(),
- InstrumentationRegistry.getInstrumentation(), true);
+ toggleListenerAccess(true);
mListener = TestNotificationListener.getInstance();
mListener.resetData();
assertNotNull(mListener);
@@ -484,16 +495,16 @@
if (data == null) {
data = new Notification.BubbleMetadata.Builder(pendingIntent,
- Icon.createWithResource(mContext, R.drawable.black))
+ Icon.createWithResource(mContext, R.drawable.black))
.build();
}
if (builder == null) {
builder = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
- .setSmallIcon(R.drawable.black)
- .setWhen(System.currentTimeMillis())
- .setContentTitle("notify#" + id)
- .setContentText("This is #" + id + "notification ")
- .setContentIntent(pendingIntent);
+ .setSmallIcon(R.drawable.black)
+ .setWhen(System.currentTimeMillis())
+ .setContentTitle("notify#" + id)
+ .setContentText("This is #" + id + "notification ")
+ .setContentIntent(pendingIntent);
}
builder.setBubbleMetadata(data);
@@ -536,7 +547,7 @@
// getActiveNotifications()
// we will check for it for up to 300ms before giving up
boolean found = false;
- for (int tries = 3; tries--> 0;) {
+ for (int tries = 3; tries-- > 0; ) {
// Need reset flag.
found = false;
final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
@@ -562,7 +573,7 @@
// getActiveNotifications()
// we will check for it for up to 400ms before giving up
int lastCount = 0;
- for (int tries = 4; tries-- > 0;) {
+ for (int tries = 4; tries-- > 0; ) {
final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
lastCount = sbns.length;
if (expectedCount == lastCount) return;
@@ -572,7 +583,7 @@
// pass
}
}
- fail("Expected " + expectedCount + " posted notifications, were " + lastCount);
+ fail("Expected " + expectedCount + " posted notifications, were " + lastCount);
}
private void compareChannels(NotificationChannel expected, NotificationChannel actual) {
@@ -612,8 +623,8 @@
runCommand(command, instrumentation);
NotificationManager nm = mContext.getSystemService(NotificationManager.class);
- Assert.assertEquals("Notification Policy Access Grant is " +
- nm.isNotificationPolicyAccessGranted() + " not " + on, on,
+ assertEquals("Notification Policy Access Grant is "
+ + nm.isNotificationPolicyAccessGranted() + " not " + on, on,
nm.isNotificationPolicyAccessGranted());
}
@@ -626,18 +637,23 @@
runCommand(command, instrumentation);
}
- private void toggleListenerAccess(String componentName, Instrumentation instrumentation,
- boolean on) throws IOException {
-
+ private void toggleListenerAccess(boolean on) throws IOException {
String command = " cmd notification " + (on ? "allow_listener " : "disallow_listener ")
- + componentName;
+ + TestNotificationListener.getId();
- runCommand(command, instrumentation);
+ runCommand(command, InstrumentationRegistry.getInstrumentation());
final NotificationManager nm = mContext.getSystemService(NotificationManager.class);
final ComponentName listenerComponent = TestNotificationListener.getComponentName();
- assertTrue(listenerComponent + " has not been granted access",
- nm.isNotificationListenerAccessGranted(listenerComponent) == on);
+ assertEquals(listenerComponent + " has incorrect listener access",
+ on, nm.isNotificationListenerAccessGranted(listenerComponent));
+ }
+
+ private void toggleExternalListenerAccess(ComponentName listenerComponent, boolean on)
+ throws IOException {
+ String command = " cmd notification " + (on ? "allow_listener " : "disallow_listener ")
+ + listenerComponent.flattenToString();
+ runCommand(command, InstrumentationRegistry.getInstrumentation());
}
private void setBubblesGlobal(boolean enabled)
@@ -669,15 +685,18 @@
Thread.sleep(500); // wait for ranking update
}
+ @SuppressWarnings("StatementWithEmptyBody")
private void runCommand(String command, Instrumentation instrumentation) throws IOException {
UiAutomation uiAutomation = instrumentation.getUiAutomation();
// Execute command
try (ParcelFileDescriptor fd = uiAutomation.executeShellCommand(command)) {
- Assert.assertNotNull("Failed to execute shell command: " + command, fd);
+ assertNotNull("Failed to execute shell command: " + command, fd);
// Wait for the command to finish by reading until EOF
try (InputStream in = new FileInputStream(fd.getFileDescriptor())) {
byte[] buffer = new byte[4096];
- while (in.read(buffer) > 0) {}
+ while (in.read(buffer) > 0) {
+ // discard output
+ }
} catch (IOException e) {
throw new IOException("Could not read stdout of command: " + command, e);
}
@@ -711,7 +730,7 @@
private void assertExpectedDndState(int expectedState) {
int tries = 3;
- for (int i = tries; i >=0; i--) {
+ for (int i = tries; i >= 0; i--) {
if (expectedState ==
mNotificationManager.getCurrentInterruptionFilter()) {
break;
@@ -821,11 +840,11 @@
KeyguardManager keyguardManager = mContext.getSystemService(KeyguardManager.class);
keyguardManager.requestDismissKeyguard(sendBubbleActivity,
new KeyguardManager.KeyguardDismissCallback() {
- @Override
- public void onDismissSucceeded() {
- latch.countDown();
- }
- });
+ @Override
+ public void onDismissSucceeded() {
+ latch.countDown();
+ }
+ });
try {
latch.await(500, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
@@ -1111,7 +1130,7 @@
new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT);
channel.setDescription("bananas");
channel.enableVibration(true);
- channel.setVibrationPattern(new long[] {5, 8, 2, 1});
+ channel.setVibrationPattern(new long[]{5, 8, 2, 1});
channel.setSound(new Uri.Builder().scheme("test").build(),
new AudioAttributes.Builder().setUsage(
AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED).build());
@@ -1226,7 +1245,8 @@
try {
mNotificationManager.createNotificationChannel(channel);
fail("Created notification with bad group");
- } catch (IllegalArgumentException e) {}
+ } catch (IllegalArgumentException e) {
+ }
}
public void testCreateChannelInvalidImportance() throws Exception {
@@ -1393,8 +1413,7 @@
}
public void testSuspendPackage() throws Exception {
- toggleListenerAccess(TestNotificationListener.getId(),
- InstrumentationRegistry.getInstrumentation(), true);
+ toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
@@ -1435,8 +1454,7 @@
}
public void testSuspendedPackageSendsNotification() throws Exception {
- toggleListenerAccess(TestNotificationListener.getId(),
- InstrumentationRegistry.getInstrumentation(), true);
+ toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
@@ -1488,8 +1506,7 @@
assertEquals(1, Settings.Global.getInt(
mContext.getContentResolver(), Settings.Global.NOTIFICATION_BUBBLES));
- toggleListenerAccess(TestNotificationListener.getId(),
- InstrumentationRegistry.getInstrumentation(), true);
+ toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
@@ -1533,8 +1550,7 @@
assertEquals(1, Settings.Secure.getInt(
mContext.getContentResolver(), Settings.Secure.NOTIFICATION_BADGING));
- toggleListenerAccess(TestNotificationListener.getId(),
- InstrumentationRegistry.getInstrumentation(), true);
+ toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
@@ -1576,8 +1592,7 @@
}
public void testGetSuppressedVisualEffectsOff_ranking() throws Exception {
- toggleListenerAccess(TestNotificationListener.getId(),
- InstrumentationRegistry.getInstrumentation(), true);
+ toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
@@ -1605,8 +1620,7 @@
final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
NotificationManager.Policy origPolicy = mNotificationManager.getNotificationPolicy();
try {
- toggleListenerAccess(TestNotificationListener.getId(),
- InstrumentationRegistry.getInstrumentation(), true);
+ toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
@@ -1654,8 +1668,7 @@
}
public void testKeyChannelGroupOverrideImportanceExplanation_ranking() throws Exception {
- toggleListenerAccess(TestNotificationListener.getId(),
- InstrumentationRegistry.getInstrumentation(), true);
+ toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
@@ -1947,7 +1960,8 @@
.setStyle(new Notification.BigPictureStyle()
.setBigContentTitle("title")
.bigPicture(Bitmap.createBitmap(100, 100, Bitmap.Config.RGB_565))
- .bigLargeIcon(Icon.createWithResource(getContext(), R.drawable.icon_blue))
+ .bigLargeIcon(
+ Icon.createWithResource(getContext(), R.drawable.icon_blue))
.setSummaryText("summary"))
.build();
mNotificationManager.notify(id, notification);
@@ -2378,31 +2392,31 @@
}
public void testNotificationPolicyVisualEffectsEqual() {
- NotificationManager.Policy policy = new NotificationManager.Policy(0,0 ,0 ,
+ NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0,
SUPPRESSED_EFFECT_SCREEN_ON);
- NotificationManager.Policy policy2 = new NotificationManager.Policy(0,0 ,0 ,
+ NotificationManager.Policy policy2 = new NotificationManager.Policy(0, 0, 0,
SUPPRESSED_EFFECT_PEEK);
assertTrue(policy.equals(policy2));
assertTrue(policy2.equals(policy));
- policy = new NotificationManager.Policy(0,0 ,0 ,
+ policy = new NotificationManager.Policy(0, 0, 0,
SUPPRESSED_EFFECT_SCREEN_ON);
- policy2 = new NotificationManager.Policy(0,0 ,0 ,
+ policy2 = new NotificationManager.Policy(0, 0, 0,
0);
assertFalse(policy.equals(policy2));
assertFalse(policy2.equals(policy));
- policy = new NotificationManager.Policy(0,0 ,0 ,
+ policy = new NotificationManager.Policy(0, 0, 0,
SUPPRESSED_EFFECT_SCREEN_OFF);
- policy2 = new NotificationManager.Policy(0,0 ,0 ,
+ policy2 = new NotificationManager.Policy(0, 0, 0,
SUPPRESSED_EFFECT_FULL_SCREEN_INTENT | SUPPRESSED_EFFECT_AMBIENT
| SUPPRESSED_EFFECT_LIGHTS);
assertTrue(policy.equals(policy2));
assertTrue(policy2.equals(policy));
- policy = new NotificationManager.Policy(0,0 ,0 ,
+ policy = new NotificationManager.Policy(0, 0, 0,
SUPPRESSED_EFFECT_SCREEN_OFF);
- policy2 = new NotificationManager.Policy(0,0 ,0 ,
+ policy2 = new NotificationManager.Policy(0, 0, 0,
SUPPRESSED_EFFECT_LIGHTS);
assertFalse(policy.equals(policy2));
assertFalse(policy2.equals(policy));
@@ -2453,7 +2467,7 @@
mNotificationManager.notifyAsPackage(DELEGATOR, "toBeCanceled", 10000, n);
assertNotNull(findPostedNotification(10000, false));
mNotificationManager.cancelAsPackage(DELEGATOR, "toBeCanceled", 10000);
- assertTrue(isNotificationCancelled(10000, false));
+ assertNotificationCancelled(10000, false);
final Intent revokeIntent = new Intent();
revokeIntent.setClassName(DELEGATOR, REVOKE_CLASS);
revokeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -2463,8 +2477,7 @@
public void testNotificationDelegate_cannotCancelNotificationsPostedByDelegator()
throws Exception {
- toggleListenerAccess(TestNotificationListener.getId(),
- InstrumentationRegistry.getInstrumentation(), true);
+ toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
@@ -2483,7 +2496,7 @@
try {
mNotificationManager.cancelAsPackage(DELEGATOR, null, 9);
- fail ("Delegate should not be able to cancel notification they did not post");
+ fail("Delegate should not be able to cancel notification they did not post");
} catch (SecurityException e) {
// yay
}
@@ -2631,8 +2644,7 @@
// pass
}
- toggleListenerAccess(TestNotificationListener.getId(),
- InstrumentationRegistry.getInstrumentation(), true);
+ toggleListenerAccess(true);
// no exception this time
mNotificationManager.shouldHideSilentStatusBarIcons();
}
@@ -2699,9 +2711,250 @@
listener.onListenerDisconnected();
}
+ private void performNotificationProviderAction(@NonNull String action) {
+ // Create an intent to launch an activity which just posts or cancels notifications
+ Intent activityIntent = new Intent();
+ activityIntent.setClassName(NOTIFICATIONPROVIDER, RICH_NOTIFICATION_ACTIVITY);
+ activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ activityIntent.putExtra("action", action);
+ mContext.startActivity(activityIntent);
+ }
+
+ public void testNotificationUriPermissionsGranted() throws Exception {
+ Uri background7Uri = Uri.parse(
+ "content://com.android.test.notificationprovider.provider/background7.png");
+ Uri background8Uri = Uri.parse(
+ "content://com.android.test.notificationprovider.provider/background8.png");
+
+ toggleListenerAccess(true);
+ Thread.sleep(500); // wait for listener to be allowed
+
+ mListener = TestNotificationListener.getInstance();
+ assertNotNull(mListener);
+
+ try {
+ // Post #7
+ performNotificationProviderAction("send-7");
+
+ assertEquals(background7Uri, getNotificationBackgroundImageUri(7));
+ assertNotificationCancelled(8, true);
+ assertAccessible(background7Uri);
+ assertInaccessible(background8Uri);
+
+ // Post #8
+ performNotificationProviderAction("send-8");
+
+ assertEquals(background7Uri, getNotificationBackgroundImageUri(7));
+ assertEquals(background8Uri, getNotificationBackgroundImageUri(8));
+ assertAccessible(background7Uri);
+ assertAccessible(background8Uri);
+
+ // Cancel #7
+ performNotificationProviderAction("cancel-7");
+
+ assertNotificationCancelled(7, true);
+ assertEquals(background8Uri, getNotificationBackgroundImageUri(8));
+ assertInaccessible(background7Uri);
+ assertAccessible(background8Uri);
+
+ // Cancel #8
+ performNotificationProviderAction("cancel-8");
+
+ assertNotificationCancelled(7, true);
+ assertNotificationCancelled(8, true);
+ assertInaccessible(background7Uri);
+ assertInaccessible(background8Uri);
+
+ } finally {
+ // Clean up -- reset any remaining notifications
+ performNotificationProviderAction("reset");
+ Thread.sleep(500);
+ }
+ }
+
+ public void testNotificationUriPermissionsGrantedToNewListeners() throws Exception {
+ Uri background7Uri = Uri.parse(
+ "content://com.android.test.notificationprovider.provider/background7.png");
+
+ try {
+ // Post #7
+ performNotificationProviderAction("send-7");
+
+ Thread.sleep(500);
+ // Don't have access the notification yet, but we can test the URI
+ assertInaccessible(background7Uri);
+
+ toggleListenerAccess(true);
+ Thread.sleep(500); // wait for listener to be allowed
+
+ mListener = TestNotificationListener.getInstance();
+ assertNotNull(mListener);
+
+ assertEquals(background7Uri, getNotificationBackgroundImageUri(7));
+ assertAccessible(background7Uri);
+
+ } finally {
+ // Clean Up -- Cancel #7
+ performNotificationProviderAction("cancel-7");
+ Thread.sleep(500);
+ }
+ }
+
+ public void testNotificationUriPermissionsRevokedFromRemovedListeners() throws Exception {
+ Uri background7Uri = Uri.parse(
+ "content://com.android.test.notificationprovider.provider/background7.png");
+
+ toggleListenerAccess(true);
+ Thread.sleep(500); // wait for listener to be allowed
+
+ try {
+ // Post #7
+ performNotificationProviderAction("send-7");
+
+ mListener = TestNotificationListener.getInstance();
+ assertNotNull(mListener);
+
+ assertEquals(background7Uri, getNotificationBackgroundImageUri(7));
+ assertAccessible(background7Uri);
+
+ // Remove the listener to ensure permissions get revoked
+ toggleListenerAccess(false);
+ Thread.sleep(500); // wait for listener to be disabled
+
+ assertInaccessible(background7Uri);
+
+ } finally {
+ // Clean Up -- Cancel #7
+ performNotificationProviderAction("cancel-7");
+ Thread.sleep(500);
+ }
+ }
+
+ private class NotificationListenerConnection implements ServiceConnection {
+ private final Semaphore mSemaphore = new Semaphore(0);
+
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ mNotificationUriAccessService = INotificationUriAccessService.Stub.asInterface(service);
+ mSemaphore.release();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName className) {
+ mNotificationUriAccessService = null;
+ }
+
+ public void waitForService() {
+ try {
+ if (mSemaphore.tryAcquire(5, TimeUnit.SECONDS)) {
+ return;
+ }
+ } catch (InterruptedException e) {
+ }
+ fail("failed to connec to service");
+ }
+ }
+
+ ;
+
+ public void testNotificationUriPermissionsRevokedOnlyFromRemovedListeners() throws Exception {
+ Uri background7Uri = Uri.parse(
+ "content://com.android.test.notificationprovider.provider/background7.png");
+
+ // Connect to a service in the NotificationListener app which allows us to validate URI
+ // permissions granted to a second app, so that we show that permissions aren't being
+ // revoked too broadly.
+ final Intent intent = new Intent();
+ intent.setComponent(new ComponentName("com.android.test.notificationlistener",
+ "com.android.test.notificationlistener.NotificationUriAccessService"));
+ NotificationListenerConnection connection = new NotificationListenerConnection();
+ mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE);
+ connection.waitForService();
+
+ // Before starting the test, make sure the service works, that there is no listener, and
+ // that the URI starts inaccessible to that process.
+ mNotificationUriAccessService.ensureNotificationListenerServiceConnected(false);
+ assertFalse(mNotificationUriAccessService.isFileUriAccessible(background7Uri));
+
+ // Give the NotificationListener app access to notifications, and validate that.
+ toggleExternalListenerAccess(new ComponentName("com.android.test.notificationlistener",
+ "com.android.test.notificationlistener.TestNotificationListener"), true);
+ Thread.sleep(500);
+ mNotificationUriAccessService.ensureNotificationListenerServiceConnected(true);
+ assertFalse(mNotificationUriAccessService.isFileUriAccessible(background7Uri));
+
+ // Give the test app access to notifications, and get that listener
+ toggleListenerAccess(true);
+ Thread.sleep(500); // wait for listener to be allowed
+ mListener = TestNotificationListener.getInstance();
+ assertNotNull(mListener);
+
+ try {
+ try {
+ // Post #7
+ performNotificationProviderAction("send-7");
+
+ // Check that both the test app (this code) and the external app have URI access.
+ assertEquals(background7Uri, getNotificationBackgroundImageUri(7));
+ assertAccessible(background7Uri);
+ assertTrue(mNotificationUriAccessService.isFileUriAccessible(background7Uri));
+
+ // Remove the listener to ensure permissions get revoked
+ toggleListenerAccess(false);
+ Thread.sleep(500); // wait for listener to be disabled
+
+ // Ensure that revoking listener access to this one app does not effect the other.
+ assertInaccessible(background7Uri);
+ assertTrue(mNotificationUriAccessService.isFileUriAccessible(background7Uri));
+
+ } finally {
+ // Clean Up -- Cancel #7
+ performNotificationProviderAction("cancel-7");
+ Thread.sleep(500);
+ }
+
+ // Finally, cancelling the permission must still revoke those other permissions.
+ assertFalse(mNotificationUriAccessService.isFileUriAccessible(background7Uri));
+
+ } finally {
+ // Clean Up -- Make sure the external listener is has access revoked
+ toggleExternalListenerAccess(new ComponentName("com.android.test.notificationlistener",
+ "com.android.test.notificationlistener.TestNotificationListener"), false);
+ }
+ }
+
+ private void assertAccessible(Uri uri)
+ throws IOException {
+ ContentResolver contentResolver = mContext.getContentResolver();
+ try (AssetFileDescriptor fd = contentResolver.openAssetFile(uri, "r", null)) {
+ assertNotNull(fd);
+ } catch (SecurityException e) {
+ throw new AssertionError("URI should be accessible: " + uri, e);
+ }
+ }
+
+ private void assertInaccessible(Uri uri)
+ throws IOException {
+ ContentResolver contentResolver = mContext.getContentResolver();
+ try (AssetFileDescriptor fd = contentResolver.openAssetFile(uri, "r", null)) {
+ fail("URI should be inaccessible: " + uri);
+ } catch (SecurityException e) {
+ // pass
+ }
+ }
+
+ @NonNull
+ private Uri getNotificationBackgroundImageUri(int notificationId) {
+ StatusBarNotification sbn = findPostedNotification(notificationId, true);
+ assertNotNull(sbn);
+ String imageUriString = sbn.getNotification().extras
+ .getString(Notification.EXTRA_BACKGROUND_IMAGE_URI);
+ assertNotNull(imageUriString);
+ return Uri.parse(imageUriString);
+ }
+
public void testNotificationListener_setNotificationsShown() throws Exception {
- toggleListenerAccess(TestNotificationListener.getId(),
- InstrumentationRegistry.getInstrumentation(), true);
+ toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
@@ -2717,8 +2970,7 @@
StatusBarNotification sbn2 = findPostedNotification(notificationId2, false);
mListener.setNotificationsShown(new String[]{ sbn1.getKey() });
- toggleListenerAccess(TestNotificationListener.getId(),
- InstrumentationRegistry.getInstrumentation(), false);
+ toggleListenerAccess(false);
Thread.sleep(500); // wait for listener to be disallowed
try {
mListener.setNotificationsShown(new String[]{ sbn2.getKey() });
@@ -2729,8 +2981,7 @@
}
public void testNotificationListener_getNotificationChannels() throws Exception {
- toggleListenerAccess(TestNotificationListener.getId(),
- InstrumentationRegistry.getInstrumentation(), true);
+ toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
@@ -2745,8 +2996,7 @@
}
public void testNotificationListener_getNotificationChannelGroups() throws Exception {
- toggleListenerAccess(TestNotificationListener.getId(),
- InstrumentationRegistry.getInstrumentation(), true);
+ toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
@@ -2760,8 +3010,7 @@
}
public void testNotificationListener_updateNotificationChannel() throws Exception {
- toggleListenerAccess(TestNotificationListener.getId(),
- InstrumentationRegistry.getInstrumentation(), true);
+ toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
@@ -2780,8 +3029,7 @@
}
public void testNotificationListener_getActiveNotifications() throws Exception {
- toggleListenerAccess(TestNotificationListener.getId(),
- InstrumentationRegistry.getInstrumentation(), true);
+ toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
@@ -2808,8 +3056,7 @@
public void testNotificationListener_getCurrentRanking() throws Exception {
- toggleListenerAccess(TestNotificationListener.getId(),
- InstrumentationRegistry.getInstrumentation(), true);
+ toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
@@ -2822,8 +3069,7 @@
}
public void testNotificationListener_cancelNotifications() throws Exception {
- toggleListenerAccess(TestNotificationListener.getId(),
- InstrumentationRegistry.getInstrumentation(), true);
+ toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
diff --git a/tests/app/src/android/app/cts/UiModeManagerTest.java b/tests/app/src/android/app/cts/UiModeManagerTest.java
index e877350..b474fb8 100644
--- a/tests/app/src/android/app/cts/UiModeManagerTest.java
+++ b/tests/app/src/android/app/cts/UiModeManagerTest.java
@@ -23,11 +23,13 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
import android.test.AndroidTestCase;
import android.util.Log;
import com.android.compatibility.common.util.BatteryUtils;
import com.android.compatibility.common.util.SettingsUtils;
+import com.android.compatibility.common.util.UserUtils;
import junit.framework.Assert;
@@ -108,6 +110,11 @@
}
public void testNightModeYesPersisted() throws InterruptedException {
+ if (mUiModeManager.isNightModeLocked()) {
+ Log.i(TAG, "testNightModeYesPersisted skipped: night mode is locked");
+ return;
+ }
+
// Reset the mode to no if it is set to another value
setNightMode(UiModeManager.MODE_NIGHT_NO);
@@ -116,6 +123,11 @@
}
public void testNightModeAutoPersisted() throws InterruptedException {
+ if (mUiModeManager.isNightModeLocked()) {
+ Log.i(TAG, "testNightModeAutoPersisted skipped: night mode is locked");
+ return;
+ }
+
// Reset the mode to no if it is set to another value
setNightMode(UiModeManager.MODE_NIGHT_NO);
@@ -125,6 +137,7 @@
public void testNightModeAutoNotPersistedCarMode() {
if (mUiModeManager.isNightModeLocked()) {
+ Log.i(TAG, "testNightModeAutoNotPersistedCarMode skipped: night mode is locked");
return;
}
@@ -139,6 +152,7 @@
public void testNightModeInCarModeIsTransient() {
if (mUiModeManager.isNightModeLocked()) {
+ Log.i(TAG, "testNightModeInCarModeIsTransient skipped: night mode is locked");
return;
}
@@ -156,6 +170,8 @@
public void testNightModeToggleInCarModeDoesNotChangeSetting() {
if (mUiModeManager.isNightModeLocked()) {
+ Log.i(TAG, "testNightModeToggleInCarModeDoesNotChangeSetting skipped: "
+ + "night mode is locked");
return;
}
@@ -174,6 +190,8 @@
public void testNightModeInCarModeOnPowerSaveIsTransient() throws Throwable {
if (mUiModeManager.isNightModeLocked() || !BatteryUtils.isBatterySaverSupported()) {
+ Log.i(TAG, "testNightModeInCarModeOnPowerSaveIsTransient skipped: "
+ + "night mode is locked or battery saver is not supported");
return;
}
@@ -347,7 +365,7 @@
int storedModeInt = -1;
// Settings.Secure.UI_NIGHT_MODE
for (int i = 0; i < MAX_WAIT_TIME; i += WAIT_TIME_INCR) {
- String storedMode = SettingsUtils.getSecureSetting("ui_night_mode");
+ String storedMode = getUiNightModeFromSetting();
storedModeInt = Integer.parseInt(storedMode);
if (mode == storedModeInt) break;
try {
@@ -404,4 +422,10 @@
}
}
+ private String getUiNightModeFromSetting() {
+ String key = "ui_night_mode";
+ return UserUtils.isHeadlessSystemUserMode()
+ ? SettingsUtils.getSecureSettingAsUser(UserHandle.USER_SYSTEM, key)
+ : SettingsUtils.getSecureSetting(key);
+ }
}
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
index d3b5621..108e034 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
@@ -302,6 +302,7 @@
public static final String EXTRA_APP_CONFIG_INFO = "app_config_info";
public static final String EXTRA_CONFIG_INFO_IN_ON_CREATE = "config_info_in_on_create";
public static final String EXTRA_DISPLAY_REAL_SIZE = "display_real_size";
+ public static final String EXTRA_SYSTEM_RESOURCES_CONFIG_INFO = "sys_config_info";
}
/** Extra key constants for {@link android.server.wm.app.FontScaleActivity}. */
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/LandscapeOrientationActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/LandscapeOrientationActivity.java
index dd1ad87..8ceeab2 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/LandscapeOrientationActivity.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/LandscapeOrientationActivity.java
@@ -19,9 +19,11 @@
import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_APP_CONFIG_INFO;
import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_CONFIG_INFO_IN_ON_CREATE;
import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_DISPLAY_REAL_SIZE;
+import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_SYSTEM_RESOURCES_CONFIG_INFO;
import android.app.Application;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Point;
import android.hardware.display.DisplayManager;
import android.os.Bundle;
@@ -50,6 +52,8 @@
// own display adjustments.
app.getSystemService(DisplayManager.class)
.getDisplay(Display.DEFAULT_DISPLAY)));
+ extras.putParcelable(EXTRA_SYSTEM_RESOURCES_CONFIG_INFO,
+ new ConfigInfo(Resources.getSystem()));
client.putExtras(extras);
});
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
index 3fcd640..52d8351 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
@@ -40,6 +40,7 @@
import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_APP_CONFIG_INFO;
import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_CONFIG_INFO_IN_ON_CREATE;
import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_DISPLAY_REAL_SIZE;
+import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_SYSTEM_RESOURCES_CONFIG_INFO;
import static android.server.wm.translucentapp26.Components.SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
@@ -59,6 +60,7 @@
import android.content.ComponentName;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
@@ -68,6 +70,7 @@
import android.server.wm.CommandSession.ConfigInfo;
import android.server.wm.CommandSession.SizeInfo;
import android.server.wm.TestJournalProvider.TestJournalContainer;
+import android.util.DisplayMetrics;
import android.view.Display;
import org.junit.Test;
@@ -472,6 +475,9 @@
final Point onCreateRealDisplaySize = extras.getParcelable(EXTRA_DISPLAY_REAL_SIZE);
final ConfigInfo onCreateConfigInfo = extras.getParcelable(EXTRA_CONFIG_INFO_IN_ON_CREATE);
final SizeInfo onCreateSize = onCreateConfigInfo.sizeInfo;
+ final ConfigInfo globalConfigInfo =
+ extras.getParcelable(EXTRA_SYSTEM_RESOURCES_CONFIG_INFO);
+ final SizeInfo globalSizeInfo = globalConfigInfo.sizeInfo;
assertEquals("The last reported size should be the same as the one from onCreate",
reportedSizes, onCreateConfigInfo.sizeInfo);
@@ -485,6 +491,10 @@
expectedRotation, onCreateConfigInfo.rotation);
assertEquals("The application should get the final display rotation in onCreate",
expectedRotation, appConfigInfo.rotation);
+ assertEquals("The orientation of application must be landscape",
+ ORIENTATION_LANDSCAPE, appConfigInfo.sizeInfo.orientation);
+ assertEquals("The orientation of system resources must be landscape",
+ ORIENTATION_LANDSCAPE, globalSizeInfo.orientation);
assertEquals("The activity should get the final display size in onCreate",
expectedRealDisplaySize, onCreateRealDisplaySize);
@@ -493,6 +503,13 @@
onCreateSize.displayWidth > onCreateSize.displayHeight);
assertEquals("The application should get the same orientation", isLandscape,
appConfigInfo.sizeInfo.displayWidth > appConfigInfo.sizeInfo.displayHeight);
+ assertEquals("The app display metrics must be landscape", isLandscape,
+ appConfigInfo.sizeInfo.metricsWidth > appConfigInfo.sizeInfo.metricsHeight);
+
+ final DisplayMetrics globalMetrics = Resources.getSystem().getDisplayMetrics();
+ assertEquals("The display metrics of system resources must be landscape",
+ new Point(globalMetrics.widthPixels, globalMetrics.heightPixels),
+ new Point(globalSizeInfo.metricsWidth, globalSizeInfo.metricsHeight));
}
@Test
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DisplayTests.java b/tests/framework/base/windowmanager/src/android/server/wm/DisplayTests.java
index 6ad0dc1..bd415bf 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/DisplayTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DisplayTests.java
@@ -23,6 +23,7 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
@@ -60,6 +61,18 @@
}
/**
+ * Tests that config_remoteInsetsControllerControlsSystemBars is not set to true for
+ * non-automotive devices.
+ */
+ @Test
+ public void testRemoteInsetsControllerNotControlSystemBarsForNonAutoDevies() {
+ assumeFalse(isCar());
+
+ assertFalse("Non auto devices should not set config_remoteInsetsControllerControlsSystemBars",
+ remoteInsetsControllerControlsSystemBars());
+ }
+
+ /**
* Tests that secondary display has override configuration set.
*/
@Test
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
index a76e895..e281fdb 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
@@ -1206,6 +1206,11 @@
.getBoolean(android.R.bool.config_perDisplayFocusEnabled);
}
+ protected static boolean remoteInsetsControllerControlsSystemBars() {
+ return getInstrumentation().getTargetContext().getResources()
+ .getBoolean(android.R.bool.config_remoteInsetsControllerControlsSystemBars);
+ }
+
/** @see ObjectTracker#manage(AutoCloseable) */
protected HomeActivitySession createManagedHomeActivitySession(ComponentName homeActivity) {
return mObjectTracker.manage(new HomeActivitySession(homeActivity));
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java b/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java
index 4039a13..418d298 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java
@@ -1023,6 +1023,12 @@
sizeInfo = new SizeInfo(display, metrics, config);
}
+ public ConfigInfo(Resources res) {
+ final DisplayMetrics metrics = res.getDisplayMetrics();
+ final Configuration config = res.getConfiguration();
+ sizeInfo = new SizeInfo(null /* display */, metrics, config);
+ }
+
@Override
public String toString() {
return "ConfigInfo: {displayId=" + displayId + " rotation=" + rotation
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardVisibilityControlTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardVisibilityControlTest.java
index 9cd6be1..cad3309 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardVisibilityControlTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardVisibilityControlTest.java
@@ -16,8 +16,14 @@
package android.view.inputmethod.cts;
+import static android.view.View.VISIBLE;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE;
import static android.view.inputmethod.cts.util.InputMethodVisibilityVerifier.expectImeInvisible;
import static android.view.inputmethod.cts.util.InputMethodVisibilityVerifier.expectImeVisible;
import static android.view.inputmethod.cts.util.TestUtils.getOnMainSync;
@@ -75,6 +81,7 @@
@RunWith(AndroidJUnit4.class)
public class KeyboardVisibilityControlTest extends EndToEndImeTestBase {
private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
+ private static final long NOT_EXPECT_TIMEOUT = TimeUnit.SECONDS.toMillis(1);
@Rule
public final UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule();
@@ -408,6 +415,96 @@
}
}
+ @Test
+ public void testImeState_EditorDialogLostFocusAfterUnlocked_Unspecified() throws Exception {
+ runImeDoesntReshowAfterKeyguardTest(SOFT_INPUT_STATE_UNSPECIFIED);
+ }
+
+ @Test
+ public void testImeState_EditorDialogLostFocusAfterUnlocked_Visible() throws Exception {
+ runImeDoesntReshowAfterKeyguardTest(SOFT_INPUT_STATE_VISIBLE);
+ }
+
+ @Test
+ public void testImeState_EditorDialogLostFocusAfterUnlocked_AlwaysVisible() throws Exception {
+ runImeDoesntReshowAfterKeyguardTest(SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ }
+
+ @Test
+ public void testImeState_EditorDialogLostFocusAfterUnlocked_Hidden() throws Exception {
+ runImeDoesntReshowAfterKeyguardTest(SOFT_INPUT_STATE_HIDDEN);
+ }
+
+ @Test
+ public void testImeState_EditorDialogLostFocusAfterUnlocked_AlwaysHidden() throws Exception {
+ runImeDoesntReshowAfterKeyguardTest(SOFT_INPUT_STATE_ALWAYS_HIDDEN);
+ }
+
+ private void runImeDoesntReshowAfterKeyguardTest(int softInputState) throws Exception {
+ try (MockImeSession imeSession = MockImeSession.create(
+ InstrumentationRegistry.getInstrumentation().getContext(),
+ InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+ new ImeSettings.Builder())) {
+ final ImeEventStream stream = imeSession.openEventStream();
+
+ // Launch a simple test activity
+ final TestActivity testActivity =
+ TestActivity.startSync(activity -> new LinearLayout(activity));
+
+ // Launch a dialog and show keyboard
+ final String marker = getTestMarker();
+ final AtomicReference<EditText> editTextRef = new AtomicReference<>();
+ final AtomicReference<AlertDialog> dialogRef = new AtomicReference<>();
+ TestUtils.runOnMainSync(() -> {
+ final EditText editText = new EditText(testActivity);
+ editText.setHint("focused editText");
+ editText.setPrivateImeOptions(marker);
+ editText.requestFocus();
+ final AlertDialog dialog = new AlertDialog.Builder(testActivity)
+ .setView(editText)
+ .create();
+ dialog.getWindow().setSoftInputMode(softInputState);
+ dialog.show();
+ editText.getWindowInsetsController().show(ime());
+ editTextRef.set(editText);
+ dialogRef.set(dialog);
+ });
+
+ TestUtils.waitOnMainUntil(() -> dialogRef.get().isShowing()
+ && editTextRef.get().hasFocus(), TIMEOUT);
+ expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT);
+ expectEvent(stream, event -> "showSoftInput".equals(event.getEventName()), TIMEOUT);
+ expectEvent(stream, editorMatcher("onStartInputView", marker), TIMEOUT);
+ expectEventWithKeyValue(stream, "onWindowVisibilityChanged", "visible",
+ View.VISIBLE, TIMEOUT);
+ expectImeVisible(TIMEOUT);
+
+ // Clear editor focus after screen-off
+ TestUtils.turnScreenOff();
+ TestUtils.waitOnMainUntil(() -> editTextRef.get().getWindowVisibility() != VISIBLE,
+ TIMEOUT);
+ expectEvent(stream, onFinishInputViewMatcher(true), TIMEOUT);
+ expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT);
+ expectEvent(stream, editorMatcher("onStartInputView", marker), TIMEOUT);
+ // Expect showSoftInput comes when system notify InsetsController to apply show IME
+ // insets after IME input target updated.
+ expectEvent(stream, event -> "showSoftInput".equals(event.getEventName()), TIMEOUT);
+ notExpectEvent(stream, hideSoftInputMatcher(), NOT_EXPECT_TIMEOUT);
+ TestUtils.runOnMainSync(editTextRef.get()::clearFocus);
+
+ // Verify IME will invisible after device unlocked
+ TestUtils.turnScreenOn();
+ TestUtils.unlockScreen();
+ // Expect hideSoftInput and onFinishInputView will called by IMMS when the same window
+ // focused since the editText view focus has been cleared.
+ TestUtils.waitOnMainUntil(() -> editTextRef.get().hasWindowFocus()
+ && !editTextRef.get().hasFocus(), TIMEOUT);
+ expectEvent(stream, hideSoftInputMatcher(), TIMEOUT);
+ expectEvent(stream, onFinishInputViewMatcher(false), TIMEOUT);
+ expectImeInvisible(TIMEOUT);
+ }
+ }
+
private static ImeSettings.Builder getFloatingImeSettings(@ColorInt int navigationBarColor) {
final ImeSettings.Builder builder = new ImeSettings.Builder();
builder.setWindowFlags(0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
diff --git a/tests/media/src/android/mediav2/cts/MuxerTest.java b/tests/media/src/android/mediav2/cts/MuxerTest.java
index d68741c..dd5f795 100644
--- a/tests/media/src/android/mediav2/cts/MuxerTest.java
+++ b/tests/media/src/android/mediav2/cts/MuxerTest.java
@@ -1059,6 +1059,15 @@
}
}
+ @Test
+ public void testSimpleMuxNative() {
+ Assume.assumeTrue("TODO(b/146421018)",
+ !mMime.equals(MediaFormat.MIMETYPE_AUDIO_OPUS));
+ Assume.assumeTrue("TODO(b/146923287)",
+ !mMime.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS));
+ assertTrue(nativeTestSimpleMux(mInpPath, mOutPath, mMime, selector));
+ }
+
/* Does MediaMuxer throw IllegalStateException on missing codec specific data when required.
* Check if relevant exception is thrown for AAC, AVC, HEVC, and MPEG4
* codecs that require CSD in MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4.
@@ -1091,14 +1100,161 @@
}
}
}
+ }
+
+ @LargeTest
+ @RunWith(Parameterized.class)
+ public static class TestAddEmptyTracks {
+ private final List<String> mimeListforTypeMp4 =
+ Arrays.asList(MediaFormat.MIMETYPE_VIDEO_MPEG4, MediaFormat.MIMETYPE_VIDEO_H263,
+ MediaFormat.MIMETYPE_VIDEO_AVC, MediaFormat.MIMETYPE_VIDEO_HEVC,
+ MediaFormat.MIMETYPE_AUDIO_AAC, MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC,
+ MediaFormat.MIMETYPE_TEXT_SUBRIP);
+ private final List<String> mimeListforTypeWebm =
+ Arrays.asList(MediaFormat.MIMETYPE_VIDEO_VP8, MediaFormat.MIMETYPE_VIDEO_VP9,
+ MediaFormat.MIMETYPE_AUDIO_VORBIS, MediaFormat.MIMETYPE_AUDIO_OPUS);
+ private final List<String> mimeListforType3gp =
+ Arrays.asList(MediaFormat.MIMETYPE_VIDEO_MPEG4, MediaFormat.MIMETYPE_VIDEO_H263,
+ MediaFormat.MIMETYPE_VIDEO_AVC, MediaFormat.MIMETYPE_AUDIO_AAC,
+ MediaFormat.MIMETYPE_AUDIO_AMR_NB, MediaFormat.MIMETYPE_AUDIO_AMR_WB);
+ private final List<String> mimeListforTypeOgg =
+ Arrays.asList(MediaFormat.MIMETYPE_AUDIO_OPUS);
+ private String mMime;
+ private String mOutPath;
+
+ public TestAddEmptyTracks(String mime) {
+ mMime = mime;
+ }
+
+ @Before
+ public void prologue() throws IOException {
+ mOutPath = File.createTempFile("tmp", ".out").getAbsolutePath();
+ }
+
+ @After
+ public void epilogue() {
+ new File(mOutPath).delete();
+ }
+
+ private boolean isMimeContainerPairValid(int format) {
+ boolean result = false;
+ if (format == MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
+ result = mimeListforTypeMp4.contains(mMime);
+ else if (format == MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM) {
+ return mimeListforTypeWebm.contains(mMime);
+ } else if (format == MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP) {
+ result = mimeListforType3gp.contains(mMime);
+ } else if (format == MediaMuxer.OutputFormat.MUXER_OUTPUT_OGG) {
+ result = mimeListforTypeOgg.contains(mMime);
+ }
+ return result;
+ }
+
+ @Parameterized.Parameters(name = "{index}({0})")
+ public static Collection<Object[]> input() {
+ return Arrays.asList(new Object[][]{
+ // Video
+ {MediaFormat.MIMETYPE_VIDEO_H263},
+ {MediaFormat.MIMETYPE_VIDEO_AVC},
+ {MediaFormat.MIMETYPE_VIDEO_HEVC},
+ {MediaFormat.MIMETYPE_VIDEO_MPEG4},
+ {MediaFormat.MIMETYPE_VIDEO_VP8},
+ {MediaFormat.MIMETYPE_VIDEO_VP9},
+ // Audio
+ {MediaFormat.MIMETYPE_AUDIO_AAC},
+ {MediaFormat.MIMETYPE_AUDIO_AMR_NB},
+ {MediaFormat.MIMETYPE_AUDIO_AMR_WB},
+ {MediaFormat.MIMETYPE_AUDIO_OPUS},
+ {MediaFormat.MIMETYPE_AUDIO_VORBIS},
+ // Metadata
+ {MediaFormat.MIMETYPE_TEXT_SUBRIP},
+ // Image
+ {MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC}
+ });
+ }
@Test
- public void testSimpleMuxNative() {
- Assume.assumeTrue("TODO(b/146421018)",
- !mMime.equals(MediaFormat.MIMETYPE_AUDIO_OPUS));
- Assume.assumeTrue("TODO(b/146923287)",
- !mMime.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS));
- assertTrue(nativeTestSimpleMux(mInpPath, mOutPath, mMime, selector));
+ public void testEmptyVideoTrack() {
+ for (int format = MUXER_OUTPUT_FIRST; format <= MUXER_OUTPUT_LAST; ++format) {
+ if (!mMime.startsWith("video/")) continue;
+ if (!isMimeContainerPairValid(format)) continue;
+ if (format != MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4) continue;
+ try {
+ MediaMuxer mediaMuxer = new MediaMuxer(mOutPath, format);
+ MediaFormat mediaFormat = new MediaFormat();
+ mediaFormat.setString(MediaFormat.KEY_MIME, mMime);
+ mediaFormat.setInteger(MediaFormat.KEY_HEIGHT, 480);
+ mediaFormat.setInteger(MediaFormat.KEY_WIDTH, 640);
+ mediaMuxer.addTrack(mediaFormat);
+ mediaMuxer.start();
+ mediaMuxer.stop();
+ mediaMuxer.release();
+ } catch (Exception e) {
+ fail("testEmptyVideoTrack : unexpected exception : " + e.getMessage());
+ }
+ }
+ }
+
+ @Test
+ public void testEmptyAudioTrack() {
+ for (int format = MUXER_OUTPUT_FIRST; format <= MUXER_OUTPUT_LAST; ++format) {
+ if (!mMime.startsWith("audio/")) continue;
+ if (!isMimeContainerPairValid(format)) continue;
+ if (format != MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4) continue;
+ try {
+ MediaMuxer mediaMuxer = new MediaMuxer(mOutPath, format);
+ MediaFormat mediaFormat = new MediaFormat();
+ mediaFormat.setString(MediaFormat.KEY_MIME, mMime);
+ mediaFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, 12000);
+ mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 2);
+ mediaMuxer.addTrack(mediaFormat);
+ mediaMuxer.start();
+ mediaMuxer.stop();
+ mediaMuxer.release();
+ } catch (Exception e) {
+ fail("testEmptyAudioTrack : unexpected exception : " + e.getMessage());
+ }
+ }
+ }
+
+ @Test
+ public void testEmptyMetaDataTrack() {
+ for (int format = MUXER_OUTPUT_FIRST; format <= MUXER_OUTPUT_LAST; ++format) {
+ if (!mMime.startsWith("application/")) continue;
+ if (!isMimeContainerPairValid(format)) continue;
+ try {
+ MediaMuxer mediaMuxer = new MediaMuxer(mOutPath, format);
+ MediaFormat mediaFormat = new MediaFormat();
+ mediaFormat.setString(MediaFormat.KEY_MIME, mMime);
+ mediaMuxer.addTrack(mediaFormat);
+ mediaMuxer.start();
+ mediaMuxer.stop();
+ mediaMuxer.release();
+ } catch (Exception e) {
+ fail("testEmptyMetaDataTrack : unexpected exception : " + e.getMessage());
+ }
+ }
+ }
+
+ @Test
+ public void testEmptyImageTrack() {
+ for (int format = MUXER_OUTPUT_FIRST; format <= MUXER_OUTPUT_LAST; ++format) {
+ if (!mMime.startsWith("image/")) continue;
+ if (!isMimeContainerPairValid(format)) continue;
+ try {
+ MediaMuxer mediaMuxer = new MediaMuxer(mOutPath, format);
+ MediaFormat mediaFormat = new MediaFormat();
+ mediaFormat.setString(MediaFormat.KEY_MIME, mMime);
+ mediaFormat.setInteger(MediaFormat.KEY_HEIGHT, 480);
+ mediaFormat.setInteger(MediaFormat.KEY_WIDTH, 640);
+ mediaMuxer.addTrack(mediaFormat);
+ mediaMuxer.start();
+ mediaMuxer.stop();
+ mediaMuxer.release();
+ } catch (Exception e) {
+ fail("testEmptyImageTrack : unexpected exception : " + e.getMessage());
+ }
+ }
}
}
}
diff --git a/tests/tests/contactsprovider/src/android/provider/cts/contacts/CallLogProviderTest.java b/tests/tests/contactsprovider/src/android/provider/cts/contacts/CallLogProviderTest.java
new file mode 100644
index 0000000..8f55aa0
--- /dev/null
+++ b/tests/tests/contactsprovider/src/android/provider/cts/contacts/CallLogProviderTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.provider.cts.contacts;
+
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.provider.CallLog;
+import android.provider.CallLog.Calls;
+import android.test.InstrumentationTestCase;
+
+public class CallLogProviderTest extends InstrumentationTestCase {
+ private ContentResolver mContentResolver;
+ private ContentProviderClient mProvider;
+
+ private static final String TEST_NUMBER = "5551234";
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mContentResolver = getInstrumentation().getTargetContext().getContentResolver();
+ mProvider = mContentResolver.acquireContentProviderClient(CallLog.AUTHORITY);
+ }
+
+ public void testNoSubqueries() throws Exception {
+ // Add a single call just to make sure the call log has something inside
+ ContentValues values = new ContentValues();
+ values.put(CallLog.Calls.NUMBER, TEST_NUMBER);
+ values.put(CallLog.Calls.TYPE, Calls.OUTGOING_TYPE);
+ values.put(CallLog.Calls.DATE, Long.valueOf(0 /*start time*/));
+ values.put(CallLog.Calls.DURATION, Long.valueOf(5 /*call duration*/));
+
+ mContentResolver.insert(CallLog.Calls.CONTENT_URI, values);
+
+ // Attempt to do a query that contains a subquery -- this should fail since this test does
+ // not have READ_VOICEMAIL.
+ try {
+ Cursor c = mProvider.query(Calls.CONTENT_URI, null, CallLog.Calls.NUMBER + " = ?",
+ new String[]{TEST_NUMBER},
+ "date DESC LIMIT (SELECT count(*) + 1 FROM calls WHERE type = 4");
+ assertEquals(0, c.getCount());
+ } catch (IllegalArgumentException e) {
+ // expected/tolerated
+ }
+ }
+}
diff --git a/tests/tests/nativemedia/aaudio/jni/test_aaudio.cpp b/tests/tests/nativemedia/aaudio/jni/test_aaudio.cpp
index daf5d6c..b8ea502 100644
--- a/tests/tests/nativemedia/aaudio/jni/test_aaudio.cpp
+++ b/tests/tests/nativemedia/aaudio/jni/test_aaudio.cpp
@@ -48,6 +48,53 @@
const StreamBuilderHelper::Parameters& actual() const { return mHelper->actual(); }
int32_t framesPerBurst() const { return mHelper->framesPerBurst(); }
+ // This checks for expected behavior after a stream has been released.
+ void checkCallsAfterRelease() {
+ // We expect these not to crash.
+ AAudioStream_setBufferSizeInFrames(stream(), 0);
+ AAudioStream_setBufferSizeInFrames(stream(), 99999999);
+
+ // We should NOT be able to start or change a stream after it has been released.
+ EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE,
+ AAudioStream_requestStart(stream()));
+ EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, AAudioStream_getState(stream()));
+ // Pause is only implemented for OUTPUT.
+ if (AAudioStream_getDirection(stream()) == AAUDIO_DIRECTION_OUTPUT) {
+ EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE,
+ AAudioStream_requestPause(stream()));
+ }
+ EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, AAudioStream_getState(stream()));
+ EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE,
+ AAudioStream_requestStop(stream()));
+ EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, AAudioStream_getState(stream()));
+
+ // Do these return positive integers?
+ // Frames read or written may be zero if the stream has not had time to advance.
+ EXPECT_GE(AAudioStream_getFramesRead(stream()), 0);
+ EXPECT_GE(AAudioStream_getFramesWritten(stream()), 0);
+ EXPECT_GT(AAudioStream_getFramesPerBurst(stream()), 0);
+ EXPECT_GE(AAudioStream_getXRunCount(stream()), 0);
+ EXPECT_GT(AAudioStream_getBufferCapacityInFrames(stream()), 0);
+ EXPECT_GT(AAudioStream_getBufferSizeInFrames(stream()), 0);
+
+ int64_t timestampFrames = 0;
+ int64_t timestampNanos = 0;
+ aaudio_result_t result = AAudioStream_getTimestamp(stream(), CLOCK_MONOTONIC,
+ ×tampFrames, ×tampNanos);
+ EXPECT_TRUE(result == AAUDIO_ERROR_INVALID_STATE
+ || result == AAUDIO_ERROR_UNIMPLEMENTED
+ || result == AAUDIO_OK
+ );
+
+ // Verify Closing State. Does this crash?
+ aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNKNOWN;
+ EXPECT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(stream(),
+ AAUDIO_STREAM_STATE_UNKNOWN,
+ &state,
+ 500 * NANOS_PER_MILLISECOND));
+ EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, state);
+ }
+
/**
* @return buffer with correct size for the stream format.
*/
@@ -86,7 +133,7 @@
bool mSetupSuccessful = false;
std::unique_ptr<int16_t[]> mShortData;
- std::unique_ptr<float[]> mFloatData;
+ std::unique_ptr<float[]> mFloatData;
};
class AAudioInputStreamTest : public AAudioStreamTest<InputStreamBuilderHelper> {
@@ -206,6 +253,9 @@
aaudio_stream_state_t state = AAudioStream_getState(stream());
EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, state);
}
+
+ checkCallsAfterRelease();
+
}
INSTANTIATE_TEST_CASE_P(SPM, AAudioInputStreamTest,
@@ -408,11 +458,16 @@
if (!mSetupSuccessful) return;
mHelper->startStream();
- aaudio_result_t result = AAudioStream_write(
- stream(), getDataBuffer(), framesPerBurst(),
- DEFAULT_READ_TIMEOUT);
- ASSERT_GT(result, 0);
+ // Write a few times so the device has time to read some of the data
+ // and maybe advance the framesRead.
+ for (int i = 0; i < 3; i++) {
+ aaudio_result_t result = AAudioStream_write(
+ stream(), getDataBuffer(), framesPerBurst(),
+ DEFAULT_READ_TIMEOUT);
+ ASSERT_GT(result, 0);
+ }
mHelper->stopStream();
+ EXPECT_GE(AAudioStream_getFramesRead(stream()), 0);
// It should be safe to release multiple times.
for (int i = 0; i < 3; i++) {
@@ -420,6 +475,9 @@
aaudio_stream_state_t state = AAudioStream_getState(stream());
EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, state);
}
+
+ checkCallsAfterRelease();
+
}
// Note that the test for EXCLUSIVE sharing mode may fail gracefully if
diff --git a/tests/tests/permission/AndroidTest.xml b/tests/tests/permission/AndroidTest.xml
index a49ca3a..ef2a09f 100644
--- a/tests/tests/permission/AndroidTest.xml
+++ b/tests/tests/permission/AndroidTest.xml
@@ -61,8 +61,9 @@
<option name="push" value="CtsVictimPermissionDefinerApp.apk->/data/local/tmp/cts/permissions/CtsVictimPermissionDefinerApp.apk" />
<option name="push" value="CtsRuntimePermissionDefinerApp.apk->/data/local/tmp/cts/permissions/CtsRuntimePermissionDefinerApp.apk" />
<option name="push" value="CtsRuntimePermissionUserApp.apk->/data/local/tmp/cts/permissions/CtsRuntimePermissionUserApp.apk" />
- <option name="push" value="CtsInstalltimePermissionDefinerApp.apk->/data/local/tmp/cts/permissions/CtsInstalltimePermissionDefinerApp.apk" />
- <option name="push" value="CtsInstalltimePermissionUserApp.apk->/data/local/tmp/cts/permissions/CtsInstalltimePermissionUserApp.apk" />
+ <option name="push" value="CtsInstallPermissionDefinerApp.apk->/data/local/tmp/cts/permissions/CtsInstallPermissionDefinerApp.apk" />
+ <option name="push" value="CtsInstallPermissionUserApp.apk->/data/local/tmp/cts/permissions/CtsInstallPermissionUserApp.apk" />
+ <option name="push" value="CtsInstallPermissionEscalatorApp.apk->/data/local/tmp/cts/permissions/CtsInstallPermissionEscalatorApp.apk" />
<option name="push" value="CtsAppThatRequestsOneTimePermission.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsOneTimePermission.apk" />
<option name="push" value="AppThatDefinesUndefinedPermissionGroupElement.apk->/data/local/tmp/cts/permissions/AppThatDefinesUndefinedPermissionGroupElement.apk" />
</target_preparer>
diff --git a/tests/tests/permission/src/android/permission/cts/RemovePermissionTest.java b/tests/tests/permission/src/android/permission/cts/RemovePermissionTest.java
index 466fb7f..57e8755 100644
--- a/tests/tests/permission/src/android/permission/cts/RemovePermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/RemovePermissionTest.java
@@ -35,6 +35,7 @@
import com.android.compatibility.common.util.SystemUtil;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -52,17 +53,19 @@
APP_PKG_NAME_BASE + ".runtimepermissionuserapp";
private static final String RUNTIME_PERMISSION_DEFINER_PKG_NAME =
APP_PKG_NAME_BASE + ".runtimepermissiondefinerapp";
- private static final String INSTALLTIME_PERMISSION_USER_PKG_NAME =
- APP_PKG_NAME_BASE + ".installtimepermissionuserapp";
- private static final String INSTALLTIME_PERMISSION_DEFINER_PKG_NAME =
- APP_PKG_NAME_BASE + ".installtimepermissiondefinerapp";
+ private static final String INSTALL_PERMISSION_USER_PKG_NAME =
+ APP_PKG_NAME_BASE + ".installpermissionuserapp";
+ private static final String INSTALL_PERMISSION_DEFINER_PKG_NAME =
+ APP_PKG_NAME_BASE + ".installpermissiondefinerapp";
+ private static final String INSTALL_PERMISSION_ESCALATOR_PKG_NAME =
+ APP_PKG_NAME_BASE + ".installpermissionescalatorapp";
private static final String TEST_PERMISSION =
"android.permission.cts.revokepermissionwhenremoved.TestPermission";
private static final String TEST_RUNTIME_PERMISSION =
APP_PKG_NAME_BASE + ".TestRuntimePermission";
- private static final String TEST_INSTALLTIME_PERMISSION =
- APP_PKG_NAME_BASE + ".TestInstalltimePermission";
+ private static final String TEST_INSTALL_PERMISSION =
+ APP_PKG_NAME_BASE + ".TestInstallPermission";
private static final String ADVERSARIAL_PERMISSION_DEFINER_APK_NAME =
"CtsAdversarialPermissionDefinerApp";
@@ -74,14 +77,15 @@
"CtsRuntimePermissionDefinerApp";
private static final String RUNTIME_PERMISSION_USER_APK_NAME =
"CtsRuntimePermissionUserApp";
- private static final String INSTALLTIME_PERMISSION_DEFINER_APK_NAME =
- "CtsInstalltimePermissionDefinerApp";
- private static final String INSTALLTIME_PERMISSION_USER_APK_NAME =
- "CtsInstalltimePermissionUserApp";
+ private static final String INSTALL_PERMISSION_DEFINER_APK_NAME =
+ "CtsInstallPermissionDefinerApp";
+ private static final String INSTALL_PERMISSION_USER_APK_NAME =
+ "CtsInstallPermissionUserApp";
+ private static final String INSTALL_PERMISSION_ESCALATOR_APK_NAME =
+ "CtsInstallPermissionEscalatorApp";
private Context mContext;
private Instrumentation mInstrumentation;
- private Object mMySync = new Object();
@Before
public void setContextAndInstrumentation() {
@@ -94,6 +98,19 @@
SystemUtil.runShellCommand("input keyevent KEYCODE_WAKEUP");
}
+ @After
+ public void cleanUpTestApps() throws Exception {
+ uninstallApp(ADVERSARIAL_PERMISSION_DEFINER_PKG_NAME, true);
+ uninstallApp(ADVERSARIAL_PERMISSION_USER_PKG_NAME, true);
+ uninstallApp(VICTIM_PERMISSION_DEFINER_PKG_NAME, true);
+ uninstallApp(RUNTIME_PERMISSION_DEFINER_PKG_NAME, true);
+ uninstallApp(RUNTIME_PERMISSION_USER_PKG_NAME, true);
+ uninstallApp(INSTALL_PERMISSION_USER_PKG_NAME, true);
+ uninstallApp(INSTALL_PERMISSION_DEFINER_PKG_NAME, true);
+ uninstallApp(INSTALL_PERMISSION_ESCALATOR_PKG_NAME, true);
+ Thread.sleep(5000);
+ }
+
private boolean permissionGranted(String pkgName, String permName)
throws PackageManager.NameNotFoundException {
PackageInfo appInfo = mContext.getPackageManager().getPackageInfo(pkgName,
@@ -112,19 +129,20 @@
private void installApp(String apk) throws InterruptedException {
String installResult = SystemUtil.runShellCommand(
"pm install -r -d data/local/tmp/cts/permissions/" + apk + ".apk");
- synchronized (mMySync) {
- mMySync.wait(10000);
- }
assertEquals("Success", installResult.trim());
+ Thread.sleep(5000);
}
private void uninstallApp(String pkg) throws InterruptedException {
- String uninstallResult = SystemUtil.runShellCommand(
- "pm uninstall " + pkg);
- synchronized (mMySync) {
- mMySync.wait(10000);
+ uninstallApp(pkg, false);
+ }
+
+ private void uninstallApp(String pkg, boolean cleanUp) throws InterruptedException {
+ String uninstallResult = SystemUtil.runShellCommand("pm uninstall " + pkg);
+ if (!cleanUp) {
+ assertEquals("Success", uninstallResult.trim());
+ Thread.sleep(5000);
}
- assertEquals("Success", uninstallResult.trim());
}
private void grantPermission(String pkg, String permission) {
@@ -134,7 +152,7 @@
@SecurityTest
@Test
- public void permissionShouldBeRevokedIfRemoved() throws Throwable {
+ public void runtimePermissionShouldBeRevokedIfRemoved() throws Throwable {
installApp(ADVERSARIAL_PERMISSION_DEFINER_APK_NAME);
installApp(ADVERSARIAL_PERMISSION_USER_APK_NAME);
@@ -146,12 +164,10 @@
uninstallApp(ADVERSARIAL_PERMISSION_DEFINER_PKG_NAME);
installApp(VICTIM_PERMISSION_DEFINER_APK_NAME);
assertFalse(permissionGranted(ADVERSARIAL_PERMISSION_USER_PKG_NAME, TEST_PERMISSION));
- uninstallApp(ADVERSARIAL_PERMISSION_USER_PKG_NAME);
- uninstallApp(VICTIM_PERMISSION_DEFINER_PKG_NAME);
}
@Test
- public void permissionShouldRemainGrantedAfterAppUpdate() throws Throwable {
+ public void runtimePermissionShouldRemainGrantedAfterAppUpdate() throws Throwable {
installApp(RUNTIME_PERMISSION_DEFINER_APK_NAME);
installApp(RUNTIME_PERMISSION_USER_APK_NAME);
@@ -162,8 +178,6 @@
// operation
installApp(RUNTIME_PERMISSION_DEFINER_APK_NAME);
assertTrue(permissionGranted(RUNTIME_PERMISSION_USER_PKG_NAME, TEST_RUNTIME_PERMISSION));
- uninstallApp(RUNTIME_PERMISSION_USER_PKG_NAME);
- uninstallApp(RUNTIME_PERMISSION_DEFINER_PKG_NAME);
}
@Test
@@ -183,26 +197,46 @@
// Now uninstall the permission definer; the user packages' permission should be revoked
uninstallApp(ADVERSARIAL_PERMISSION_DEFINER_PKG_NAME);
assertFalse(permissionGranted(ADVERSARIAL_PERMISSION_USER_PKG_NAME, TEST_PERMISSION));
+ }
- uninstallApp(ADVERSARIAL_PERMISSION_USER_PKG_NAME);
+ @SecurityTest
+ @Test
+ public void installPermissionShouldBeRevokedIfRemoved() throws Throwable {
+ installApp(INSTALL_PERMISSION_DEFINER_APK_NAME);
+ installApp(INSTALL_PERMISSION_USER_APK_NAME);
+ assertTrue(permissionGranted(INSTALL_PERMISSION_USER_PKG_NAME, TEST_INSTALL_PERMISSION));
+
+ // Uninstall the app which defines the install permission, and install another app
+ // redefining it as a runtime permission.
+ uninstallApp(INSTALL_PERMISSION_DEFINER_PKG_NAME);
+ installApp(INSTALL_PERMISSION_ESCALATOR_APK_NAME);
+ assertFalse(permissionGranted(INSTALL_PERMISSION_USER_PKG_NAME, TEST_INSTALL_PERMISSION));
}
@Test
- public void installtimePermissionDependencyTest() throws Throwable {
- installApp(INSTALLTIME_PERMISSION_USER_APK_NAME);
- // Should not have the permission auto-granted
- assertFalse(permissionGranted(
- INSTALLTIME_PERMISSION_USER_PKG_NAME, TEST_INSTALLTIME_PERMISSION));
- // Now install the permission definer; user package should have the permission auto granted
- installApp(INSTALLTIME_PERMISSION_DEFINER_APK_NAME);
- installApp(INSTALLTIME_PERMISSION_USER_APK_NAME);
- assertTrue(permissionGranted(
- INSTALLTIME_PERMISSION_USER_PKG_NAME, TEST_INSTALLTIME_PERMISSION));
- // Now uninstall the permission definer; the user packages' permission will not be revoked
- uninstallApp(INSTALLTIME_PERMISSION_DEFINER_PKG_NAME);
- assertTrue(permissionGranted(
- INSTALLTIME_PERMISSION_USER_PKG_NAME, TEST_INSTALLTIME_PERMISSION));
+ public void installPermissionShouldRemainGrantedAfterAppUpdate() throws Throwable {
+ installApp(INSTALL_PERMISSION_DEFINER_APK_NAME);
+ installApp(INSTALL_PERMISSION_USER_APK_NAME);
+ assertTrue(permissionGranted(INSTALL_PERMISSION_USER_PKG_NAME, TEST_INSTALL_PERMISSION));
- uninstallApp(INSTALLTIME_PERMISSION_USER_PKG_NAME);
+ // Install the app which defines the install permission again, similar to updating the app.
+ installApp(INSTALL_PERMISSION_DEFINER_APK_NAME);
+ assertTrue(permissionGranted(INSTALL_PERMISSION_USER_PKG_NAME, TEST_INSTALL_PERMISSION));
+ }
+
+ @Test
+ public void installPermissionDependencyTest() throws Throwable {
+ installApp(INSTALL_PERMISSION_USER_APK_NAME);
+ // Should not have the permission auto-granted
+ assertFalse(permissionGranted(INSTALL_PERMISSION_USER_PKG_NAME, TEST_INSTALL_PERMISSION));
+
+ // Now install the permission definer; user package should have the permission auto granted
+ installApp(INSTALL_PERMISSION_DEFINER_APK_NAME);
+ installApp(INSTALL_PERMISSION_USER_APK_NAME);
+ assertTrue(permissionGranted(INSTALL_PERMISSION_USER_PKG_NAME, TEST_INSTALL_PERMISSION));
+
+ // Now uninstall the permission definer; the user package's permission should be revoked
+ uninstallApp(INSTALL_PERMISSION_DEFINER_PKG_NAME);
+ assertFalse(permissionGranted(INSTALL_PERMISSION_USER_PKG_NAME, TEST_INSTALL_PERMISSION));
}
}
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionDefinerApp/Android.bp b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionDefinerApp/Android.bp
similarity index 93%
rename from tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionDefinerApp/Android.bp
rename to tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionDefinerApp/Android.bp
index ee87737..0679b6a 100644
--- a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionDefinerApp/Android.bp
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionDefinerApp/Android.bp
@@ -14,12 +14,13 @@
//
android_test_helper_app {
- name: "CtsInstalltimePermissionDefinerApp",
+ name: "CtsInstallPermissionDefinerApp",
defaults: ["cts_defaults"],
sdk_version: "current",
// Tag this module as a cts test artifact
test_suites: [
"cts",
+ "vts10",
"general-tests",
],
certificate: ":cts-testkey1",
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionUserApp/AndroidManifest.xml b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionDefinerApp/AndroidManifest.xml
similarity index 72%
copy from tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionUserApp/AndroidManifest.xml
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionDefinerApp/AndroidManifest.xml
index 7a0e405..2df6743 100644
--- a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionUserApp/AndroidManifest.xml
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionDefinerApp/AndroidManifest.xml
@@ -15,11 +15,13 @@
* limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.permission.cts.revokepermissionwhenremoved.installtimepermissionuserapp">
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permission.cts.revokepermissionwhenremoved.installpermissiondefinerapp">
- <uses-permission android:name="android.permission.cts.revokepermissionwhenremoved.TestInstalltimePermission" />
+ <permission
+ android:name="android.permission.cts.revokepermissionwhenremoved.TestInstallPermission"
+ android:protectionLevel="normal" />
- <application>
- </application>
+ <application />
</manifest>
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionDefinerApp/Android.bp b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionEscalatorApp/Android.bp
similarity index 92%
copy from tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionDefinerApp/Android.bp
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionEscalatorApp/Android.bp
index ee87737..3f560de 100644
--- a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionDefinerApp/Android.bp
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionEscalatorApp/Android.bp
@@ -14,12 +14,13 @@
//
android_test_helper_app {
- name: "CtsInstalltimePermissionDefinerApp",
+ name: "CtsInstallPermissionEscalatorApp",
defaults: ["cts_defaults"],
sdk_version: "current",
// Tag this module as a cts test artifact
test_suites: [
"cts",
+ "vts10",
"general-tests",
],
certificate: ":cts-testkey1",
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionEscalatorApp/AndroidManifest.xml b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionEscalatorApp/AndroidManifest.xml
new file mode 100644
index 0000000..c339d12
--- /dev/null
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionEscalatorApp/AndroidManifest.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.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permission.cts.revokepermissionwhenremoved.installpermissionescalatorrapp">
+
+ <permission
+ android:name="android.permission.cts.revokepermissionwhenremoved.TestInstallPermission"
+ android:permissionGroup="android.permission-group.PHONE"
+ android:protectionLevel="dangerous" />
+
+ <application />
+</manifest>
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionUserApp/Android.bp b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionUserApp/Android.bp
similarity index 93%
rename from tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionUserApp/Android.bp
rename to tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionUserApp/Android.bp
index f720d7d..c75d64b 100644
--- a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionUserApp/Android.bp
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionUserApp/Android.bp
@@ -14,12 +14,13 @@
//
android_test_helper_app {
- name: "CtsInstalltimePermissionUserApp",
+ name: "CtsInstallPermissionUserApp",
defaults: ["cts_defaults"],
sdk_version: "current",
// Tag this module as a cts test artifact
test_suites: [
"cts",
+ "vts10",
"general-tests",
],
certificate: ":cts-testkey2",
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionUserApp/AndroidManifest.xml b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionUserApp/AndroidManifest.xml
similarity index 80%
rename from tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionUserApp/AndroidManifest.xml
rename to tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionUserApp/AndroidManifest.xml
index 7a0e405..acfafa9 100644
--- a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionUserApp/AndroidManifest.xml
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionUserApp/AndroidManifest.xml
@@ -15,11 +15,11 @@
* limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.permission.cts.revokepermissionwhenremoved.installtimepermissionuserapp">
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permission.cts.revokepermissionwhenremoved.installpermissionuserapp">
- <uses-permission android:name="android.permission.cts.revokepermissionwhenremoved.TestInstalltimePermission" />
+ <uses-permission android:name="android.permission.cts.revokepermissionwhenremoved.TestInstallPermission" />
- <application>
- </application>
+ <application />
</manifest>
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionDefinerApp/AndroidManifest.xml b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionDefinerApp/AndroidManifest.xml
deleted file mode 100644
index 75ce567..0000000
--- a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionDefinerApp/AndroidManifest.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.permission.cts.revokepermissionwhenremoved.installtimepermissiondefinerapp">
-
- <permission android:name="android.permission.cts.revokepermissionwhenremoved.TestInstalltimePermission"
- android:protectionLevel="normal"
- android:label="TestInstalltimePermission"
- android:description="@string/test_permission" />
-
- <application>
- </application>
-</manifest>
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionDefinerApp/res/values/strings.xml b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionDefinerApp/res/values/strings.xml
deleted file mode 100644
index 0943be7..0000000
--- a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionDefinerApp/res/values/strings.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="test_permission">Test Installtime Permission</string>
-</resources>
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index b197fc5..e3bcea42 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -5020,6 +5020,10 @@
<permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"
android:protectionLevel="signature|appPredictor" />
+ <!-- @hide Allows an application to create/destroy input consumer. -->
+ <permission android:name="android.permission.INPUT_CONSUMER"
+ android:protectionLevel="signature" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionUserApp/Android.bp b/tests/tests/permission4/Android.bp
similarity index 62%
copy from tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionUserApp/Android.bp
copy to tests/tests/permission4/Android.bp
index f720d7d..ad1658d 100644
--- a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionUserApp/Android.bp
+++ b/tests/tests/permission4/Android.bp
@@ -1,4 +1,5 @@
-// Copyright (C) 2019 The Android Open Source Project
+//
+// Copyright (C) 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -13,14 +14,24 @@
// limitations under the License.
//
-android_test_helper_app {
- name: "CtsInstalltimePermissionUserApp",
+android_test {
+ name: "CtsPermission4TestCases",
+ sdk_version: "system_current",
defaults: ["cts_defaults"],
- sdk_version: "current",
- // Tag this module as a cts test artifact
+ platform_apis: true,
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ "androidx.test.rules",
+ "compatibility-device-util-axt",
+ "ctstestrunner-axt",
+ ],
test_suites: [
"cts",
+ "vts10",
"general-tests",
+ "mts",
],
- certificate: ":cts-testkey2",
}
diff --git a/tests/tests/permission4/AndroidManifest.xml b/tests/tests/permission4/AndroidManifest.xml
new file mode 100644
index 0000000..d4cc71a
--- /dev/null
+++ b/tests/tests/permission4/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?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.permission4.cts">
+
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+
+ <application>
+
+ <uses-library android:name="android.test.runner" />
+
+ <activity android:name=".StartForFutureActivity" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.permission4.cts"
+ android:label="CTS UI tests for permissions">
+ <meta-data
+ android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+</manifest>
diff --git a/tests/tests/permission4/AndroidTest.xml b/tests/tests/permission4/AndroidTest.xml
new file mode 100644
index 0000000..71353aa
--- /dev/null
+++ b/tests/tests/permission4/AndroidTest.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.
+ -->
+
+<configuration description="Config for CTS Permission4 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" />
+
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk30ModuleController" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsPermission4TestCases.apk" />
+ <option name="test-file-name" value="CtsAppThatAccessesMicAndCameraPermission.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.permission4.cts" />
+ <option name="runtime-hint" value="5m" />
+ </test>
+</configuration>
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionUserApp/Android.bp b/tests/tests/permission4/AppThatAccessesCameraAndMic/Android.bp
similarity index 69%
copy from tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionUserApp/Android.bp
copy to tests/tests/permission4/AppThatAccessesCameraAndMic/Android.bp
index f720d7d..508e44c 100644
--- a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionUserApp/Android.bp
+++ b/tests/tests/permission4/AppThatAccessesCameraAndMic/Android.bp
@@ -1,4 +1,5 @@
-// Copyright (C) 2019 The Android Open Source Project
+//
+// Copyright (C) 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,13 +15,23 @@
//
android_test_helper_app {
- name: "CtsInstalltimePermissionUserApp",
+ name: "CtsAppThatAccessesMicAndCameraPermission",
defaults: ["cts_defaults"],
- sdk_version: "current",
+ sdk_version: "system_current",
// Tag this module as a cts test artifact
test_suites: [
"cts",
+ "vts10",
"general-tests",
],
- certificate: ":cts-testkey2",
+
+ static_libs: [
+ "androidx.test.rules",
+ "kotlin-stdlib",
+ "kotlinx-coroutines-android",
+ ],
+
+ srcs: [
+ "src/**/*.kt"
+ ],
}
diff --git a/tests/tests/permission4/AppThatAccessesCameraAndMic/AndroidManifest.xml b/tests/tests/permission4/AppThatAccessesCameraAndMic/AndroidManifest.xml
new file mode 100644
index 0000000..938b5b5
--- /dev/null
+++ b/tests/tests/permission4/AppThatAccessesCameraAndMic/AndroidManifest.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
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permission4.cts.appthataccessescameraandmic"
+ android:versionCode="1">
+
+ <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+ <uses-permission android:name="android.permission.CAMERA"/>
+
+ <application android:label="CtsCameraMicAccess">
+ <activity android:name=".AccessCameraOrMicActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="test.action.USE_CAMERA_OR_MIC" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/tests/permission4/AppThatAccessesCameraAndMic/src/android/permission4/cts/appthataccessescameraandmic/AccessCameraOrMicActivity.kt b/tests/tests/permission4/AppThatAccessesCameraAndMic/src/android/permission4/cts/appthataccessescameraandmic/AccessCameraOrMicActivity.kt
new file mode 100644
index 0000000..2be6926
--- /dev/null
+++ b/tests/tests/permission4/AppThatAccessesCameraAndMic/src/android/permission4/cts/appthataccessescameraandmic/AccessCameraOrMicActivity.kt
@@ -0,0 +1,114 @@
+/*
+ * 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.permission4.cts.appthataccessescameraandmic
+
+import android.app.Activity
+import android.hardware.camera2.CameraAccessException
+import android.hardware.camera2.CameraDevice
+import android.hardware.camera2.CameraManager
+import android.media.AudioFormat.CHANNEL_IN_MONO
+import android.media.AudioFormat.ENCODING_PCM_16BIT
+import android.media.AudioRecord
+import android.media.MediaRecorder.AudioSource.MIC
+import androidx.annotation.NonNull
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+
+private const val USE_CAMERA = "use_camera"
+private const val USE_MICROPHONE = "use_microphone"
+private const val USE_DURATION_MS = 10000L
+private const val SAMPLE_RATE_HZ = 44100
+
+/**
+ * Activity which will, depending on the extra passed in the intent, use the camera, the microphone,
+ * or both.
+ */
+class AccessCameraOrMicActivity : Activity() {
+ private lateinit var cameraId: String
+ private var cameraDevice: CameraDevice? = null
+ private var recorder: AudioRecord? = null
+ private var cameraFinished = false
+ private var runCamera = false
+ private var micFinished = false
+ private var runMic = false
+
+ override fun onStart() {
+ super.onStart()
+ runCamera = intent.getBooleanExtra(USE_CAMERA, false)
+ runMic = intent.getBooleanExtra(USE_MICROPHONE, false)
+
+ if (runMic) {
+ useMic()
+ }
+
+ if (runCamera) {
+ useCamera()
+ }
+ }
+
+ override fun onStop() {
+ super.onStop()
+ cameraDevice?.close()
+ recorder?.stop()
+ finish()
+ }
+
+ private val stateCallback = object : CameraDevice.StateCallback() {
+ override fun onOpened(@NonNull camDevice: CameraDevice) {
+ cameraDevice = camDevice
+ GlobalScope.launch {
+ delay(USE_DURATION_MS)
+ cameraFinished = true
+ if (!runMic || micFinished) {
+ finish()
+ }
+ }
+ }
+
+ override fun onDisconnected(@NonNull camDevice: CameraDevice) {
+ camDevice.close()
+ throw RuntimeException("Camera was disconnected")
+ }
+
+ override fun onError(@NonNull camDevice: CameraDevice, error: Int) {
+ camDevice.close()
+ throw RuntimeException("Camera error")
+ }
+ }
+
+ @Throws(CameraAccessException::class)
+ private fun useCamera() {
+ val manager = getSystemService(CameraManager::class.java)!!
+ cameraId = manager.cameraIdList[0]
+ manager.openCamera(cameraId, mainExecutor, stateCallback)
+ }
+
+ private fun useMic() {
+ val minSize =
+ AudioRecord.getMinBufferSize(SAMPLE_RATE_HZ, CHANNEL_IN_MONO, ENCODING_PCM_16BIT)
+ recorder = AudioRecord(MIC, SAMPLE_RATE_HZ, CHANNEL_IN_MONO, ENCODING_PCM_16BIT, minSize)
+ recorder?.startRecording()
+ GlobalScope.launch {
+ delay(USE_DURATION_MS)
+ micFinished = true
+ if (!runCamera || cameraFinished) {
+ finish()
+ }
+ }
+ }
+}
diff --git a/tests/tests/permission4/OWNERS b/tests/tests/permission4/OWNERS
new file mode 100644
index 0000000..d4d6a95
--- /dev/null
+++ b/tests/tests/permission4/OWNERS
@@ -0,0 +1,7 @@
+# Bug component: 137825
+svetoslavganov@google.com
+moltmann@google.com
+zhanghai@google.com
+eugenesusla@google.com
+evanseverson@google.com
+ntmyren@google.com
diff --git a/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt b/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt
new file mode 100644
index 0000000..0f36f5a
--- /dev/null
+++ b/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt
@@ -0,0 +1,181 @@
+/*
+ * 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.permission4.cts
+
+import android.Manifest
+import android.app.Instrumentation
+import android.app.UiAutomation
+import android.app.compat.CompatChanges
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.hardware.camera2.CameraManager
+import android.os.Process
+import android.provider.DeviceConfig
+import android.provider.Settings
+import android.support.test.uiautomator.By
+import android.support.test.uiautomator.UiDevice
+import android.support.test.uiautomator.UiSelector
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity
+import com.android.compatibility.common.util.SystemUtil.eventually
+import com.android.compatibility.common.util.SystemUtil.runShellCommand
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import org.junit.After
+import org.junit.Assert.assertTrue
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+
+private const val APP_LABEL = "CtsCameraMicAccess"
+private const val USE_CAMERA = "use_camera"
+private const val USE_MICROPHONE = "use_microphone"
+private const val INTENT_ACTION = "test.action.USE_CAMERA_OR_MIC"
+private const val PRIVACY_CHIP_ID = "com.android.systemui:id/privacy_chip"
+private const val INDICATORS_FLAG = "camera_mic_icons_enabled"
+private const val PERMISSION_INDICATORS_NOT_PRESENT = 162547999L
+private const val IDLE_TIMEOUT_MILLIS: Long = 1000
+private const val UNEXPECTED_TIMEOUT_MILLIS = 1000
+private const val TIMEOUT_MILLIS: Long = 20000
+
+class CameraMicIndicatorsPermissionTest {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val context: Context = instrumentation.context
+ private val uiAutomation: UiAutomation = instrumentation.uiAutomation
+ private val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
+ private val packageManager: PackageManager = context.packageManager
+
+ private var wasEnabled = false
+ private val micLabel = packageManager.getPermissionGroupInfo(
+ Manifest.permission_group.MICROPHONE, 0).loadLabel(packageManager).toString()
+ private val cameraLabel = packageManager.getPermissionGroupInfo(
+ Manifest.permission_group.CAMERA, 0).loadLabel(packageManager).toString()
+
+ private var screenTimeoutBeforeTest: Long = 0L
+
+ @Before
+ fun setUp() {
+ runWithShellPermissionIdentity {
+ screenTimeoutBeforeTest = Settings.System.getLong(
+ context.contentResolver, Settings.System.SCREEN_OFF_TIMEOUT
+ )
+ Settings.System.putLong(
+ context.contentResolver, Settings.System.SCREEN_OFF_TIMEOUT, 1800000L
+ )
+ }
+
+ uiDevice.wakeUp()
+ runShellCommand(instrumentation, "wm dismiss-keyguard")
+
+ uiDevice.findObject(By.text("Close"))?.click()
+ wasEnabled = setIndicatorsEnabledStateIfNeeded(true)
+ // If the change Id is not present, then isChangeEnabled will return true. To bypass this,
+ // the change is set to "false" if present.
+ assumeFalse("feature not present on this device", callWithShellPermissionIdentity {
+ CompatChanges.isChangeEnabled(PERMISSION_INDICATORS_NOT_PRESENT, Process.SYSTEM_UID)
+ })
+ }
+
+ private fun setIndicatorsEnabledStateIfNeeded(shouldBeEnabled: Boolean): Boolean {
+ var currentlyEnabled = false
+ runWithShellPermissionIdentity {
+ currentlyEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ INDICATORS_FLAG, false)
+ if (currentlyEnabled != shouldBeEnabled) {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY, INDICATORS_FLAG,
+ shouldBeEnabled.toString(), false)
+ }
+ }
+ return currentlyEnabled
+ }
+
+ @After
+ fun tearDown() {
+ if (!wasEnabled) {
+ setIndicatorsEnabledStateIfNeeded(false)
+ }
+ runWithShellPermissionIdentity {
+ Settings.System.putLong(
+ context.contentResolver, Settings.System.SCREEN_OFF_TIMEOUT,
+ screenTimeoutBeforeTest
+ )
+ }
+
+ pressHome()
+ }
+
+ private fun openApp(useMic: Boolean, useCamera: Boolean) {
+ context.startActivity(Intent(INTENT_ACTION).apply {
+ putExtra(USE_CAMERA, useCamera)
+ putExtra(USE_MICROPHONE, useMic)
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ })
+ }
+
+ @Test
+ fun testCameraIndicator() {
+ val manager = context.getSystemService(CameraManager::class.java)!!
+ assumeTrue(manager.cameraIdList.isNotEmpty())
+ testCameraAndMicIndicator(useMic = false, useCamera = true)
+ }
+
+ @Test
+ fun testMicIndicator() {
+ testCameraAndMicIndicator(useMic = true, useCamera = false)
+ }
+
+ private fun testCameraAndMicIndicator(useMic: Boolean, useCamera: Boolean) {
+ openApp(useMic, useCamera)
+ eventually {
+ val appView = uiDevice.findObject(UiSelector().textContains(APP_LABEL))
+ assertTrue("View with text $APP_LABEL not found", appView.exists())
+ }
+ uiDevice.openNotification()
+ // Ensure the privacy chip is present
+ eventually {
+ val privacyChip = uiDevice.findObject(UiSelector().resourceId(PRIVACY_CHIP_ID))
+ assertTrue("view with id $PRIVACY_CHIP_ID not found", privacyChip.exists())
+ privacyChip.click()
+ }
+ eventually {
+ if (useMic) {
+ val appView = uiDevice.findObject(UiSelector().textContains(micLabel))
+ assertTrue("View with text $APP_LABEL not found", appView.exists())
+ }
+ if (useCamera) {
+ val appView = uiDevice.findObject(UiSelector().textContains(cameraLabel))
+ assertTrue("View with text $APP_LABEL not found", appView.exists())
+ }
+ val appView = uiDevice.findObject(UiSelector().textContains(APP_LABEL))
+ assertTrue("View with text $APP_LABEL not found", appView.exists())
+ }
+ pressBack()
+ }
+
+ private fun pressBack() {
+ uiDevice.pressBack()
+ waitForIdle()
+ }
+
+ private fun pressHome() {
+ uiDevice.pressHome()
+ waitForIdle()
+ }
+
+ private fun waitForIdle() =
+ uiAutomation.waitForIdle(IDLE_TIMEOUT_MILLIS, TIMEOUT_MILLIS)
+}
\ No newline at end of file
diff --git a/tests/tests/security/res/raw/cve_2019_2108_hevc.mp4 b/tests/tests/security/res/raw/cve_2019_2108_hevc.mp4
new file mode 100644
index 0000000..cb2df96
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_2108_hevc.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_2223_framelen.mp4 b/tests/tests/security/res/raw/cve_2019_2223_framelen.mp4
new file mode 100644
index 0000000..a1c3cfc
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_2223_framelen.mp4
@@ -0,0 +1,5 @@
+29
+12
+9
+23
+73
diff --git a/tests/tests/security/res/raw/cve_2019_2223_hevc.mp4 b/tests/tests/security/res/raw/cve_2019_2223_hevc.mp4
new file mode 100644
index 0000000..f9d5dbc
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_2223_hevc.mp4
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index c978dad..b84ee15 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -1258,6 +1258,14 @@
***********************************************************/
@Test
+ @SecurityTest(minPatchLevel = "2019-09")
+ public void testStagefright_cve_2019_2108() throws Exception {
+ doStagefrightTestRawBlob(R.raw.cve_2019_2108_hevc, "video/hevc", 320, 240,
+ new CrashUtils.Config().setSignals(CrashUtils.SIGSEGV, CrashUtils.SIGBUS,
+ CrashUtils.SIGABRT));
+ }
+
+ @Test
@SecurityTest(minPatchLevel = "2016-09")
public void testStagefright_cve_2016_3880() throws Exception {
Thread server = new Thread() {
@@ -1688,6 +1696,13 @@
***********************************************************/
@Test
+ @SecurityTest(minPatchLevel = "2019-12")
+ public void testStagefright_cve_2019_2223() throws Exception {
+ int[] frameSizes = getFrameSizes(R.raw.cve_2019_2223_framelen);
+ doStagefrightTestRawBlob(R.raw.cve_2019_2223_hevc, "video/hevc", 320, 240, frameSizes);
+ }
+
+ @Test
@SecurityTest(minPatchLevel = "2019-03")
public void testStagefright_cve_2019_1989() throws Exception {
Object obj[] = getFrameInfo(R.raw.cve_2019_1989_info);
diff --git a/tests/tests/telecom/src/android/telecom/cts/MissedCallTest.java b/tests/tests/telecom/src/android/telecom/cts/MissedCallTest.java
index ee9359c..5043e15 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MissedCallTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MissedCallTest.java
@@ -17,6 +17,7 @@
package android.telecom.cts;
import android.content.Intent;
+import android.os.Process;
import android.telecom.Call;
import android.telecom.Connection;
import android.telecom.DisconnectCause;
@@ -29,6 +30,8 @@
TestUtils.InvokeCounter mShowMissedCallNotificationIntentCounter =
new TestUtils.InvokeCounter("ShowMissedCallNotificationIntent");
+ private static final String CMD_DEVICE_IDLE_TEMP_EXEMPTIONS = "cmd deviceidle tempwhitelist";
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -71,6 +74,15 @@
connection.setDisconnected(new DisconnectCause(DisconnectCause.MISSED));
connection.destroy();
mShowMissedCallNotificationIntentCounter.waitForCount(1);
+ assertTrue("After missing a call, if the default dialer is handling the missed call "
+ + "notification, then it must be in the temporary power exemption list.",
+ isOnTemporaryPowerExemption());
}
+ private boolean isOnTemporaryPowerExemption() throws Exception {
+ String exemptions = TestUtils.executeShellCommand(
+ getInstrumentation(), CMD_DEVICE_IDLE_TEMP_EXEMPTIONS);
+ // Just check that this process's UID is in the result.
+ return exemptions.contains(String.valueOf(Process.myUid()));
+ }
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/CellIdentityTest.java b/tests/tests/telephony/current/src/android/telephony/cts/CellIdentityTest.java
index b87d594..1112b28 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/CellIdentityTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CellIdentityTest.java
@@ -22,6 +22,7 @@
import android.telephony.CellIdentity;
import android.telephony.CellIdentityCdma;
import android.telephony.CellIdentityGsm;
+import android.telephony.CellIdentityLte;
import android.telephony.CellIdentityNr;
import android.telephony.CellIdentityTdscdma;
import android.telephony.CellIdentityWcdma;
@@ -97,14 +98,48 @@
}
@Test
- public void testCellIdentityNr_asCellLocation() {
- CellIdentity cellIdentity =
- new CellIdentityNr(123, 456, 789, null, null, null, 0, null, null, EMPTY_SET);
+ public void testCellIdentityLte_asCellLocation() {
+ int tac = 1;
+ int ci = 2;
+ CellIdentity cellIdentity = new CellIdentityLte(123, 456, ci, 7, tac);
CellLocation cellLocation = cellIdentity.asCellLocation();
GsmCellLocation gsmCellLocation = (GsmCellLocation) cellLocation;
- assertEquals(new GsmCellLocation(), gsmCellLocation);
+ assertEquals(tac, gsmCellLocation.getLac());
+ assertEquals(ci, gsmCellLocation.getCid());
+ // psc is not supported in LTE so always 0
+ assertEquals(0, gsmCellLocation.getPsc());
+ }
+
+ @Test
+ public void testCellIdentityLte_unavailable_asCellLocation() {
+ CellIdentity cellIdentity = new CellIdentityLte();
+
+ CellLocation cellLocation = cellIdentity.asCellLocation();
+
+ GsmCellLocation gsmCellLocation = (GsmCellLocation) cellLocation;
+ // -1 for unintialized lac and cid
+ assertEquals(-1, gsmCellLocation.getLac());
+ assertEquals(-1, gsmCellLocation.getCid());
+ // psc is not supported in LTE so always 0
+ assertEquals(0, gsmCellLocation.getPsc());
+ }
+
+ @Test
+ public void testCellIdentityNr_asCellLocation() {
+ int tac = 1;
+ CellIdentity cellIdentity =
+ new CellIdentityNr(123, tac, 789, null, null, null, 321, null, null, EMPTY_SET);
+
+ CellLocation cellLocation = cellIdentity.asCellLocation();
+
+ GsmCellLocation gsmCellLocation = (GsmCellLocation) cellLocation;
+ assertEquals(tac, gsmCellLocation.getLac());
+ // NR cid is 36 bits and can't fit into the 32-bit cid in GsmCellLocation, so always -1.
+ assertEquals(-1, gsmCellLocation.getCid());
+ // psc is not supported in NR so always 0, same as in LTE
+ assertEquals(0, gsmCellLocation.getPsc());
}
@Test