CTS change for process capability.
In ActivityManagerProcessStateTest.java, in foreground service with
location type test cases, when process state changes,
also wait for process capability change, especially watch for
PROCESS_CAPABILITY_FOREGROUND_LOCAION.
Create a test app ActivityManagerApi29 with targetSdkVersion set to 29.
In ActivityManagerApi29Test.java, create test case testFgsLocationWithAppOps()
to test PROCESS_CAPABILITY_FOREGROUND_LOCAION is sent to AppOpsService and
AppOpsService can use it to allow or deny OP_COARSE_LOCATION based on
the uid's process state and capability.
Bug: 136274596
Test: atest android.app.cts.ActivityManagerApi29Test
Change-Id: I437c6772112840b6e9ba917c38cc0972d2139221
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 926b808..29f027e 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -11,6 +11,7 @@
-fw apps/CtsVerifier/src/com/android/cts/verifier/usb/
apps/CtsVerifierUSBCompanion/
libs/
+ tests/app/
tests/autofillservice/
tests/contentcaptureservice/
tests/tests/animation/
diff --git a/tests/app/ActivityManagerApi29Test/Android.bp b/tests/app/ActivityManagerApi29Test/Android.bp
new file mode 100644
index 0000000..ed3a613
--- /dev/null
+++ b/tests/app/ActivityManagerApi29Test/Android.bp
@@ -0,0 +1,26 @@
+// 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.
+
+android_test_helper_app {
+ name: "CtsActivityManagerApi29",
+ defaults: ["cts_support_defaults"],
+ srcs: ["**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "current",
+}
diff --git a/tests/app/ActivityManagerApi29Test/AndroidManifest.xml b/tests/app/ActivityManagerApi29Test/AndroidManifest.xml
new file mode 100644
index 0000000..2ae7497
--- /dev/null
+++ b/tests/app/ActivityManagerApi29Test/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?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.app.cts.activitymanager.api29">
+ <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="29" />
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+
+ <application android:usesCleartextTraffic="true">
+ <uses-library android:name="android.test.runner" />
+ <uses-library android:name="org.apache.http.legacy" android:required="false" />
+ <activity android:name=".SimpleActivity" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <service android:name="LocationForegroundService"
+ android:foregroundServiceType="location"
+ android:exported="true">
+ </service>
+ </application>
+</manifest>
diff --git a/tests/app/ActivityManagerApi29Test/res/values/colors.xml b/tests/app/ActivityManagerApi29Test/res/values/colors.xml
new file mode 100644
index 0000000..d67d56a
--- /dev/null
+++ b/tests/app/ActivityManagerApi29Test/res/values/colors.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<resources>
+ <drawable name="black">#77ffffff</drawable>
+</resources>
diff --git a/tests/app/ActivityManagerApi29Test/src/android/app/cts/LocationForegroundService.java b/tests/app/ActivityManagerApi29Test/src/android/app/cts/LocationForegroundService.java
new file mode 100644
index 0000000..86ea295
--- /dev/null
+++ b/tests/app/ActivityManagerApi29Test/src/android/app/cts/LocationForegroundService.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.cts.activitymanager.api29;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+public class LocationForegroundService extends Service {
+ private static final String NOTIFICATION_CHANNEL_ID =
+ LocationForegroundService.class.getSimpleName();
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ getSystemService(NotificationManager.class).createNotificationChannel(
+ new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_ID,
+ NotificationManager.IMPORTANCE_DEFAULT));
+ Notification status = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
+ .setContentTitle("LocalForegroundService running")
+ .setSmallIcon(R.drawable.black)
+ .build();
+ startForeground(1, status);
+ return START_STICKY;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
+
diff --git a/tests/app/ActivityManagerApi29Test/src/android/app/cts/SimpleActivity.java b/tests/app/ActivityManagerApi29Test/src/android/app/cts/SimpleActivity.java
new file mode 100644
index 0000000..aa28b1f
--- /dev/null
+++ b/tests/app/ActivityManagerApi29Test/src/android/app/cts/SimpleActivity.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.cts.activitymanager.api29;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * A simple activity to install for various users to test LauncherApps.
+ */
+public class SimpleActivity extends Activity {
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ if (intent.getExtras().getBoolean("finish")) {
+ finish();
+ }
+ }
+}
diff --git a/tests/app/Android.bp b/tests/app/Android.bp
index 9e25fea..13adccf 100644
--- a/tests/app/Android.bp
+++ b/tests/app/Android.bp
@@ -30,6 +30,7 @@
"androidx.test.rules",
"platform-test-annotations",
"platformprotosnano",
+ "permission-test-util-lib"
],
srcs: ["src/**/*.java"],
// Tag this module as a cts test artifact
diff --git a/tests/app/AndroidTest.xml b/tests/app/AndroidTest.xml
index 5c2e4cf..36a283e 100644
--- a/tests/app/AndroidTest.xml
+++ b/tests/app/AndroidTest.xml
@@ -33,6 +33,11 @@
<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="CtsActivityManagerApi29.apk" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
+ <option name="run-command" value="wm dismiss-keyguard" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
diff --git a/tests/app/src/android/app/cts/ActivityManagerApi29Test.java b/tests/app/src/android/app/cts/ActivityManagerApi29Test.java
new file mode 100644
index 0000000..8553698
--- /dev/null
+++ b/tests/app/src/android/app/cts/ActivityManagerApi29Test.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.cts;
+
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
+import static android.app.AppOpsManager.OP_FLAGS_ALL;
+import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
+
+import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.HistoricalOp;
+import android.app.AppOpsManager.HistoricalOps;
+import android.app.AppOpsManager.HistoricalOpsRequest;
+import android.app.Instrumentation;
+import android.app.cts.android.app.cts.tools.WatchUidRunner;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.permission.cts.PermissionUtils;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.Arrays;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * AppOpsManager.MODE_FOREGROUND is introduced in API level 29. This test class specifically tests
+ * ActivityManagerService's interaction with AppOpsService regarding MODE_FOREGROUND operation.
+ * If an operation's mode is MODE_FOREGROUND, this operation is allowed only when the process is in
+ * one of the foreground state (including foreground_service state), this operation will be denied
+ * when the process is in background state.
+ */
+@RunWith(AndroidJUnit4.class)
+public class ActivityManagerApi29Test {
+ private static final String PACKAGE_NAME = "android.app.cts.activitymanager.api29";
+ private static final String SIMPLE_ACTIVITY = ".SimpleActivity";
+ private static final String SERVICE_NAME = ".LocationForegroundService";
+ private static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled";
+ private static final int WAITFOR_MSEC = 5000;
+ private static final int NOTEOP_COUNT = 5;
+ private static Instrumentation sInstrumentation = InstrumentationRegistry.getInstrumentation();
+ private static Context sContext = sInstrumentation.getContext();
+ private static AppOpsManager sAppOps =
+ (AppOpsManager) sContext.getSystemService(AppOpsManager.class);
+ private static Intent sServiceIntent = new Intent().setClassName(
+ PACKAGE_NAME, PACKAGE_NAME + SERVICE_NAME);
+ private static int sUid;
+ static {
+ try {
+ sUid = sContext.getPackageManager().getApplicationInfo(PACKAGE_NAME, 0).uid;
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException("NameNotFoundException:" + e);
+ }
+ }
+
+ private String mOldAppOpsSettings;
+ private boolean mWasPermissionsHubEnabled = false;
+ private WatchUidRunner mUidWatcher;
+
+ @Before
+ public void setUp() throws Exception {
+ // PACKAGE_NAME's targetSdkVersion is 29, when ACCESS_COARSE_LOCATION is granted, appOp is
+ // MODE_FOREGROUND (In API level lower than 29, appOp is MODE_ALLOWED).
+ assertEquals(AppOpsManager.MODE_FOREGROUND,
+ PermissionUtils.getAppOp(PACKAGE_NAME, ACCESS_COARSE_LOCATION));
+ runWithShellPermissionIdentity(()-> {
+ mOldAppOpsSettings = Settings.Global.getString(sContext.getContentResolver(),
+ Settings.Global.APP_OPS_CONSTANTS);
+ Settings.Global.putString(sContext.getContentResolver(),
+ Settings.Global.APP_OPS_CONSTANTS,
+ "top_state_settle_time=0,fg_service_state_settle_time=0,"
+ + "bg_state_settle_time=0");
+ mWasPermissionsHubEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_PERMISSIONS_HUB_ENABLED, false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_PERMISSIONS_HUB_ENABLED, Boolean.toString(true), false);
+ sAppOps.clearHistory();
+ sAppOps.resetHistoryParameters(); }
+ );
+ mUidWatcher = new WatchUidRunner(sInstrumentation, sUid, WAITFOR_MSEC);
+ }
+
+ @After
+ public void tearDown() {
+ runWithShellPermissionIdentity(() -> {
+ // restore old AppOps settings.
+ Settings.Global.putString(sContext.getContentResolver(),
+ Settings.Global.APP_OPS_CONSTANTS, mOldAppOpsSettings);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_PERMISSIONS_HUB_ENABLED, Boolean.toString(mWasPermissionsHubEnabled),
+ false);
+ sAppOps.clearHistory();
+ sAppOps.resetHistoryParameters(); }
+ );
+ mUidWatcher.finish();
+ }
+
+ /**
+ * This tests app in PROCESS_STATE_TOP state can have location access.
+ * The app's permission is AppOpsManager.MODE_FOREGROUND. If the process is in PROCESS_STATE_TOP
+ * , even its capability is zero, it still has location access.
+ * @throws Exception
+ */
+ @Test
+ public void testTopActivityWithAppOps() throws Exception {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setClassName(PACKAGE_NAME, PACKAGE_NAME + SIMPLE_ACTIVITY);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ sContext.startActivity(intent);
+ // TOP process has all capabilities.
+ mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP,
+ new Integer(PROCESS_CAPABILITY_ALL));
+
+ // AppOps location access should be allowed.
+ assertEquals(AppOpsManager.MODE_ALLOWED, noteOp());
+
+ // Tell the activity to finalize.
+ intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ intent.putExtra("finish", true);
+ sContext.startActivity(intent);
+ mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_RECENT,
+ new Integer(PROCESS_CAPABILITY_NONE));
+
+ // AppOps location access should be denied.
+ assertEquals(AppOpsManager.MODE_IGNORED, noteOp());
+ }
+
+ /**
+ * When ActivityManagerService process states and capability changes, it updates AppOpsService.
+ * This test starts a foreground service with location type, it updates AppOpsService with
+ * PROCESS_STATE_FOREGROUND_SERVICE and PROCESS_CAPABILITY_FOREGROUND_LOCATION, then check if
+ * AppOpsManager allow ACCESS_COARSE_LOCATION of MODE_FOREGROUND.
+ *
+ * The "android.app.cts.activitymanager.api29" package's targetSdkVersion is 29.
+ * @throws Exception
+ */
+ @Test
+ public void testFgsLocationWithAppOps() throws Exception {
+ // Start a foreground service with location
+ sContext.startForegroundService(sServiceIntent);
+ // Wait for state and capability change.
+ mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE,
+ new Integer(PROCESS_CAPABILITY_FOREGROUND_LOCATION));
+
+ // AppOps location access should be allowed.
+ assertEquals(AppOpsManager.MODE_ALLOWED, noteOp());
+
+ // Stop the foreground service.
+ sContext.stopService(sServiceIntent);
+ // Wait for proc state and capability change.
+ mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY,
+ new Integer(PROCESS_CAPABILITY_NONE));
+
+ // AppOps location access should be denied.
+ assertEquals(AppOpsManager.MODE_IGNORED, noteOp());
+ }
+
+ /**
+ * After calling AppOpsManager.noteOp() interface multiple times in different process states,
+ * this test calls AppOpsManager.getHistoricalOps() and check the access count and reject count
+ * in HistoricalOps.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testAppOpsHistoricalOps() throws Exception {
+ // Start a foreground service with location
+ sContext.startForegroundService(sServiceIntent);
+ // Wait for state and capability change.
+ mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE,
+ new Integer(PROCESS_CAPABILITY_FOREGROUND_LOCATION));
+
+ runWithShellPermissionIdentity(
+ () -> sAppOps.setHistoryParameters(AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE,
+ 1000, 10)
+ );
+ for (int i = 0; i < NOTEOP_COUNT; i++) {
+ noteOp();
+ }
+
+ // Stop the foreground service.
+ sContext.stopService(sServiceIntent);
+ // Wait for proc state and capability change.
+ mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY,
+ new Integer(PROCESS_CAPABILITY_NONE));
+
+ for (int i = 0; i < NOTEOP_COUNT; i++) {
+ noteOp();
+ }
+ runWithShellPermissionIdentity(() -> {
+ CompletableFuture<HistoricalOps> ops = new CompletableFuture<>();
+ HistoricalOpsRequest histOpsRequest = new HistoricalOpsRequest.Builder(
+ Instant.now().minus(1, ChronoUnit.HOURS).toEpochMilli(),
+ Long.MAX_VALUE)
+ .setUid(sUid)
+ .setPackageName(PACKAGE_NAME)
+ .setOpNames(Arrays.asList(AppOpsManager.OPSTR_COARSE_LOCATION))
+ .setFlags(OP_FLAGS_ALL)
+ .build();
+ sAppOps.getHistoricalOps(histOpsRequest, sContext.getMainExecutor(), ops::complete);
+ HistoricalOp hOp = ops.get(5000, TimeUnit.MILLISECONDS)
+ .getUidOps(sUid).getPackageOps(PACKAGE_NAME)
+ .getOp(AppOpsManager.OPSTR_COARSE_LOCATION);
+ // granted access one time in UID_STATE_FOREGROUND_SERVICE.
+ assertEquals(NOTEOP_COUNT, hOp.getAccessCount(UID_STATE_FOREGROUND_SERVICE,
+ UID_STATE_FOREGROUND_SERVICE, AppOpsManager.OP_FLAGS_ALL));
+ assertEquals(NOTEOP_COUNT, hOp.getForegroundAccessCount(AppOpsManager.OP_FLAGS_ALL));
+ assertEquals(0, hOp.getForegroundRejectCount(AppOpsManager.OP_FLAGS_ALL));
+ assertEquals(0, hOp.getBackgroundAccessCount(AppOpsManager.OP_FLAGS_ALL));
+ // denied access one time in background.
+ assertEquals(NOTEOP_COUNT, hOp.getBackgroundRejectCount(AppOpsManager.OP_FLAGS_ALL)); }
+ );
+ }
+
+ private int noteOp() throws Exception {
+ return callWithShellPermissionIdentity(
+ () -> sAppOps.noteOp(AppOpsManager.OPSTR_COARSE_LOCATION, sUid, PACKAGE_NAME,
+ "Op OPSTR_COARSE_LOCATION", ""));
+ }
+}
diff --git a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
index 410ae34..6a23c47 100644
--- a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
@@ -16,6 +16,9 @@
package android.app.cts;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
@@ -59,8 +62,6 @@
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
-import androidx.test.InstrumentationRegistry;
-
import com.android.compatibility.common.util.CommonTestUtils;
import com.android.compatibility.common.util.SystemUtil;
@@ -193,7 +194,7 @@
}
private String executeShellCmd(String cmd) throws Exception {
- final String result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ final String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
Log.d(TAG, String.format("Output for '%s': %s", cmd, result));
return result;
}
@@ -235,7 +236,7 @@
}
private void startActivityAndWaitForShow(final Intent intent) throws Exception {
- getInstrumentation().getUiAutomation().executeAndWaitForEvent(
+ mInstrumentation.getUiAutomation().executeAndWaitForEvent(
() -> {
try {
mContext.startActivity(intent);
@@ -272,7 +273,7 @@
UidImportanceListener uidForegroundListener = new UidImportanceListener(mContext,
appInfo.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE, WAIT_TIME);
- InstrumentationRegistry.getInstrumentation().getUiAutomation().revokeRuntimePermission(
+ mInstrumentation.getUiAutomation().revokeRuntimePermission(
STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS);
boolean gotException = false;
try {
@@ -282,12 +283,12 @@
}
assertTrue("Expected SecurityException thrown", gotException);
- InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ mInstrumentation.getUiAutomation().grantRuntimePermission(
STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS);
/*
Log.d("XXXX", "Invoke: " + cmd);
Log.d("XXXX", "Result: " + result);
- Log.d("XXXX", SystemUtil.runShellCommand(getInstrumentation(), "dumpsys package "
+ Log.d("XXXX", SystemUtil.runShellCommand(mInstrumentation, "dumpsys package "
+ STUB_PACKAGE_NAME));
*/
uidForegroundListener.register();
@@ -296,7 +297,7 @@
appInfo.uid, IMPORTANCE_CACHED, WAIT_TIME);
uidGoneListener.register();
- WatchUidRunner uidWatcher = new WatchUidRunner(getInstrumentation(), appInfo.uid,
+ WatchUidRunner uidWatcher = new WatchUidRunner(mInstrumentation, appInfo.uid,
WAIT_TIME);
try {
@@ -440,12 +441,12 @@
ActivityManager am = mContext.getSystemService(ActivityManager.class);
- InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ mInstrumentation.getUiAutomation().grantRuntimePermission(
STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS);
/*
Log.d("XXXX", "Invoke: " + cmd);
Log.d("XXXX", "Result: " + result);
- Log.d("XXXX", SystemUtil.runShellCommand(getInstrumentation(), "dumpsys package "
+ Log.d("XXXX", SystemUtil.runShellCommand(mInstrumentation, "dumpsys package "
+ STUB_PACKAGE_NAME));
*/
@@ -459,7 +460,7 @@
appInfo.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY, WAIT_TIME);
uidGoneListener.register();
- WatchUidRunner uidWatcher = new WatchUidRunner(getInstrumentation(), appInfo.uid,
+ WatchUidRunner uidWatcher = new WatchUidRunner(mInstrumentation, appInfo.uid,
WAIT_TIME);
// First kill the process to start out in a stable state.
@@ -482,7 +483,7 @@
uidWatcher.waitFor(WatchUidRunner.CMD_GONE, null);
String cmd = "appops set " + SIMPLE_PACKAGE_NAME + " RUN_IN_BACKGROUND deny";
- String result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
// This is a side-effect of the app op command.
uidWatcher.expect(WatchUidRunner.CMD_IDLE, null);
@@ -490,11 +491,11 @@
// We don't want to wait for the uid to actually go idle, we can force it now.
cmd = "am make-uid-idle " + SIMPLE_PACKAGE_NAME;
- result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ result = SystemUtil.runShellCommand(mInstrumentation, cmd);
// Make sure app is not yet on whitelist
cmd = "cmd deviceidle whitelist -" + SIMPLE_PACKAGE_NAME;
- result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ result = SystemUtil.runShellCommand(mInstrumentation, cmd);
// We will use this to monitor when the service is running.
conn.startMonitoring();
@@ -514,7 +515,7 @@
// Put app on temporary whitelist to see if this allows the service start.
cmd = String.format("cmd deviceidle tempwhitelist -d %d %s",
TEMP_WHITELIST_DURATION_MS, SIMPLE_PACKAGE_NAME);
- result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ result = SystemUtil.runShellCommand(mInstrumentation, cmd);
// Try starting the service now that the app is whitelisted... should work!
mContext.startService(serviceIntent);
@@ -540,7 +541,7 @@
// We don't want to wait for the uid to actually go idle, we can force it now.
cmd = "am make-uid-idle " + SIMPLE_PACKAGE_NAME;
- result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ result = SystemUtil.runShellCommand(mInstrumentation, cmd);
uidWatcher.expect(WatchUidRunner.CMD_IDLE, null);
uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
@@ -558,7 +559,7 @@
// Now put app on whitelist, should allow service to run.
cmd = "cmd deviceidle whitelist +" + SIMPLE_PACKAGE_NAME;
- result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ result = SystemUtil.runShellCommand(mInstrumentation, cmd);
// Try starting the service now that the app is whitelisted... should work!
mContext.startService(serviceIntent);
@@ -581,9 +582,9 @@
uidWatcher.finish();
cmd = "appops set " + SIMPLE_PACKAGE_NAME + " RUN_IN_BACKGROUND allow";
- result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ result = SystemUtil.runShellCommand(mInstrumentation, cmd);
cmd = "cmd deviceidle whitelist -" + SIMPLE_PACKAGE_NAME;
- result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ result = SystemUtil.runShellCommand(mInstrumentation, cmd);
uidGoneListener.unregister();
uidForegroundListener.unregister();
@@ -605,12 +606,12 @@
ActivityManager am = mContext.getSystemService(ActivityManager.class);
- InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ mInstrumentation.getUiAutomation().grantRuntimePermission(
STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS);
/*
Log.d("XXXX", "Invoke: " + cmd);
Log.d("XXXX", "Result: " + result);
- Log.d("XXXX", SystemUtil.runShellCommand(getInstrumentation(), "dumpsys package "
+ Log.d("XXXX", SystemUtil.runShellCommand(mInstrumentation, "dumpsys package "
+ STUB_PACKAGE_NAME));
*/
@@ -624,7 +625,7 @@
appInfo.uid, IMPORTANCE_CACHED, WAIT_TIME);
uidGoneListener.register();
- WatchUidRunner uidWatcher = new WatchUidRunner(getInstrumentation(), appInfo.uid,
+ WatchUidRunner uidWatcher = new WatchUidRunner(mInstrumentation, appInfo.uid,
WAIT_TIME);
// First kill the process to start out in a stable state.
@@ -656,7 +657,7 @@
uidWatcher.waitFor(WatchUidRunner.CMD_GONE, null, WAIT_TIME);
String cmd = "appops set " + SIMPLE_PACKAGE_NAME + " RUN_IN_BACKGROUND deny";
- String result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
// This is a side-effect of the app op command.
uidWatcher.expect(WatchUidRunner.CMD_IDLE, null);
@@ -664,11 +665,11 @@
// We don't want to wait for the uid to actually go idle, we can force it now.
cmd = "am make-uid-idle " + SIMPLE_PACKAGE_NAME;
- result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ result = SystemUtil.runShellCommand(mInstrumentation, cmd);
// Make sure app is not yet on whitelist
cmd = "cmd deviceidle whitelist -" + SIMPLE_PACKAGE_NAME;
- result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ result = SystemUtil.runShellCommand(mInstrumentation, cmd);
// We will use this to monitor when the service is running.
conn.startMonitoring();
@@ -726,7 +727,7 @@
// Force app to go idle now
cmd = "am make-uid-idle " + SIMPLE_PACKAGE_NAME;
- result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ result = SystemUtil.runShellCommand(mInstrumentation, cmd);
// Wait for services to be stopped by system.
uidServiceListener.waitForValue(IMPORTANCE_CACHED,
@@ -752,9 +753,9 @@
uidWatcher.finish();
cmd = "appops set " + SIMPLE_PACKAGE_NAME + " RUN_IN_BACKGROUND allow";
- result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ result = SystemUtil.runShellCommand(mInstrumentation, cmd);
cmd = "cmd deviceidle whitelist -" + SIMPLE_PACKAGE_NAME;
- result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ result = SystemUtil.runShellCommand(mInstrumentation, cmd);
uidGoneListener.unregister();
uidServiceListener.unregister();
@@ -774,7 +775,7 @@
SIMPLE_PACKAGE_NAME + SIMPLE_RECEIVER_START_SERVICE);
final ServiceProcessController controller = new ServiceProcessController(mContext,
- getInstrumentation(), STUB_PACKAGE_NAME, mAllProcesses, WAIT_TIME);
+ mInstrumentation, STUB_PACKAGE_NAME, mAllProcesses, WAIT_TIME);
final ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext,
mServiceIntent, WAIT_TIME);
final WatchUidRunner uidWatcher = controller.getUidWatcher();
@@ -904,7 +905,7 @@
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final ServiceProcessController controller = new ServiceProcessController(mContext,
- getInstrumentation(), STUB_PACKAGE_NAME, mAllProcesses, WAIT_TIME);
+ mInstrumentation, STUB_PACKAGE_NAME, mAllProcesses, WAIT_TIME);
final ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext,
mServiceIntent, WAIT_TIME);
final WatchUidRunner uidWatcher = controller.getUidWatcher();
@@ -986,7 +987,7 @@
public void testForegroundServiceAppOp() throws Exception {
// Use default timeout value 5000
final ServiceProcessController controller = new ServiceProcessController(mContext,
- getInstrumentation(), STUB_PACKAGE_NAME, mAllProcesses);
+ mInstrumentation, STUB_PACKAGE_NAME, mAllProcesses);
// Use default timeout value 5000
final ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext,
mServiceIntent);
@@ -1248,12 +1249,12 @@
ActivityManager am = mContext.getSystemService(ActivityManager.class);
- InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ mInstrumentation.getUiAutomation().grantRuntimePermission(
STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS);
// We don't want to wait for the uid to actually go idle, we can force it now.
String cmd = "am make-uid-idle " + CANT_SAVE_STATE_1_PACKAGE_NAME;
- String result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo(
CANT_SAVE_STATE_1_PACKAGE_NAME, 0);
@@ -1273,10 +1274,10 @@
WAIT_TIME);
uidCachedListener.register();
- WatchUidRunner uidWatcher = new WatchUidRunner(getInstrumentation(), appInfo.uid,
+ WatchUidRunner uidWatcher = new WatchUidRunner(mInstrumentation, appInfo.uid,
WAIT_TIME);
- UiDevice device = UiDevice.getInstance(getInstrumentation());
+ UiDevice device = UiDevice.getInstance(mInstrumentation);
try {
// Start the heavy-weight app, should launch like a normal app.
@@ -1314,7 +1315,7 @@
// While in background, should go in to normal idle state.
// Force app to go idle now
cmd = "am make-uid-idle " + CANT_SAVE_STATE_1_PACKAGE_NAME;
- result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ result = SystemUtil.runShellCommand(mInstrumentation, cmd);
uidWatcher.expect(WatchUidRunner.CMD_IDLE, null);
// Switch back to heavy-weight app to see if it correctly returns to foreground.
@@ -1336,7 +1337,7 @@
device.waitForIdle();
// Exit activity, check to see if we are now cached.
- getInstrumentation().getUiAutomation().performGlobalAction(
+ mInstrumentation.getUiAutomation().performGlobalAction(
AccessibilityService.GLOBAL_ACTION_BACK);
// Wait for process to become cached
@@ -1352,7 +1353,7 @@
// While in background, should go in to normal idle state.
// Force app to go idle now
cmd = "am make-uid-idle " + CANT_SAVE_STATE_1_PACKAGE_NAME;
- result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ result = SystemUtil.runShellCommand(mInstrumentation, cmd);
uidWatcher.expect(WatchUidRunner.CMD_IDLE, null);
} finally {
@@ -1389,25 +1390,25 @@
homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ActivityManager am = mContext.getSystemService(ActivityManager.class);
- UiDevice device = UiDevice.getInstance(getInstrumentation());
+ UiDevice device = UiDevice.getInstance(mInstrumentation);
- InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ mInstrumentation.getUiAutomation().grantRuntimePermission(
STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS);
// We don't want to wait for the uid to actually go idle, we can force it now.
String cmd = "am make-uid-idle " + CANT_SAVE_STATE_1_PACKAGE_NAME;
- String result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
cmd = "am make-uid-idle " + CANT_SAVE_STATE_2_PACKAGE_NAME;
- result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ result = SystemUtil.runShellCommand(mInstrumentation, cmd);
ApplicationInfo app1Info = mContext.getPackageManager().getApplicationInfo(
CANT_SAVE_STATE_1_PACKAGE_NAME, 0);
- WatchUidRunner uid1Watcher = new WatchUidRunner(getInstrumentation(), app1Info.uid,
+ WatchUidRunner uid1Watcher = new WatchUidRunner(mInstrumentation, app1Info.uid,
WAIT_TIME);
ApplicationInfo app2Info = mContext.getPackageManager().getApplicationInfo(
CANT_SAVE_STATE_2_PACKAGE_NAME, 0);
- WatchUidRunner uid2Watcher = new WatchUidRunner(getInstrumentation(), app2Info.uid,
+ WatchUidRunner uid2Watcher = new WatchUidRunner(mInstrumentation, app2Info.uid,
WAIT_TIME);
try {
@@ -1463,7 +1464,7 @@
// Make sure the original app is idle for cleanliness
cmd = "am make-uid-idle " + CANT_SAVE_STATE_1_PACKAGE_NAME;
- result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ result = SystemUtil.runShellCommand(mInstrumentation, cmd);
uid1Watcher.expect(WatchUidRunner.CMD_IDLE, null);
// Return to home.
@@ -1501,16 +1502,16 @@
// Exit activity, check to see if we are now cached.
waitForAppFocus(CANT_SAVE_STATE_1_PACKAGE_NAME, WAIT_TIME);
device.waitForIdle();
- getInstrumentation().getUiAutomation().performGlobalAction(
+ mInstrumentation.getUiAutomation().performGlobalAction(
AccessibilityService.GLOBAL_ACTION_BACK);
uid1Watcher.expect(WatchUidRunner.CMD_CACHED, null);
uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_RECENT);
// Make both apps idle for cleanliness.
cmd = "am make-uid-idle " + CANT_SAVE_STATE_1_PACKAGE_NAME;
- result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ result = SystemUtil.runShellCommand(mInstrumentation, cmd);
cmd = "am make-uid-idle " + CANT_SAVE_STATE_2_PACKAGE_NAME;
- result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ result = SystemUtil.runShellCommand(mInstrumentation, cmd);
} finally {
uid2Watcher.finish();
@@ -1743,8 +1744,8 @@
/**
* Test process states for foreground service with and without location type in the manifest.
- * When running a foreground service with location type, the process should go to
- * PROCESS_STATE_FOREGROUND_SERVICE_LOCATION.
+ * When running a foreground service with location type, the process will have
+ * PROCESS_CAPABILITY_FOREGROUND_LOCATION.
* @throws Exception
*/
public void testFgsLocation() throws Exception {
@@ -1758,7 +1759,9 @@
CommandReceiver.sendCommand(mContext,
CommandReceiver.COMMAND_START_FOREGROUND_SERVICE,
PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null);
- uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE);
+ uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE,
+ WatchUidRunner.STATE_FG_SERVICE,
+ new Integer(PROCESS_CAPABILITY_NONE));
// Try to elevate to foreground service location
Bundle bundle = new Bundle();
@@ -1768,13 +1771,16 @@
CommandReceiver.COMMAND_START_FOREGROUND_SERVICE_LOCATION,
PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, bundle);
uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE,
- WatchUidRunner.STATE_FG_SERVICE_LOCATION);
+ WatchUidRunner.STATE_FG_SERVICE,
+ new Integer(PROCESS_CAPABILITY_FOREGROUND_LOCATION));
// Back down to foreground service
CommandReceiver.sendCommand(mContext,
CommandReceiver.COMMAND_STOP_FOREGROUND_SERVICE_LOCATION,
PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null);
- uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE);
+ uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE,
+ WatchUidRunner.STATE_FG_SERVICE,
+ new Integer(PROCESS_CAPABILITY_NONE));
try {
uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE,
@@ -1787,8 +1793,9 @@
CommandReceiver.sendCommand(mContext,
CommandReceiver.COMMAND_STOP_FOREGROUND_SERVICE,
PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null);
- uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
-
+ uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE,
+ WatchUidRunner.STATE_CACHED_EMPTY,
+ new Integer(PROCESS_CAPABILITY_NONE));
} finally {
uid1Watcher.finish();
}
@@ -1796,8 +1803,11 @@
/**
* Test process states for foreground service binding to another app, with and without
- * BIND_INCLUDE_CAPABILITIES. Bound app should either go to FGS or FGSL, depending on the
- * flag.
+ * BIND_INCLUDE_CAPABILITIES.
+ * With BIND_INCLUDE_CAPABILITIES flag, PROCESS_CAPABILITY_FOREGROUND_LOCATION can be passed
+ * from client to service.
+ * Without BIND_INCLUDE_CAPABILITIES flag, PROCESS_CAPABILITY_FOREGROUND_LOCATION can not be
+ * passed from client to service.
* @throws Exception
*/
public void testFgsLocationBind() throws Exception {
@@ -1808,7 +1818,9 @@
CommandReceiver.sendCommand(mContext,
CommandReceiver.COMMAND_START_FOREGROUND_SERVICE,
mAppInfo[0].packageName, mAppInfo[0].packageName, 0, null);
- mWatchers[0].waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE);
+ mWatchers[0].waitFor(WatchUidRunner.CMD_PROCSTATE,
+ WatchUidRunner.STATE_FG_SERVICE,
+ new Integer(PROCESS_CAPABILITY_NONE));
// Try to elevate to foreground service location
Bundle bundle = new Bundle();
@@ -1817,34 +1829,45 @@
CommandReceiver.sendCommand(mContext,
CommandReceiver.COMMAND_START_FOREGROUND_SERVICE_LOCATION,
mAppInfo[0].packageName, mAppInfo[0].packageName, 0, bundle);
- // Verify moved to FGSL
+ // Verify app0 has FOREGROUND_LOCATION capability.
mWatchers[0].waitFor(WatchUidRunner.CMD_PROCSTATE,
- WatchUidRunner.STATE_FG_SERVICE_LOCATION);
+ WatchUidRunner.STATE_FG_SERVICE,
+ new Integer(PROCESS_CAPABILITY_FOREGROUND_LOCATION));
- // Bind App 0 -> App 1, verify doesn't include capabilities (only FGS, not FGSL)
+ // Bind App 0 -> App 1, verify doesn't include capability.
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE,
mAppInfo[0].packageName, mAppInfo[1].packageName, 0, null);
- mWatchers[1].waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE);
+ // Verify app1 does NOT have FOREGROUND_LOCATION capability.
+ mWatchers[1].waitFor(WatchUidRunner.CMD_PROCSTATE,
+ WatchUidRunner.STATE_FG_SERVICE,
+ new Integer(PROCESS_CAPABILITY_NONE));
- // Bind App 0 -> App 2, include capabilities (FGSL)
+ // Bind App 0 -> App 2, include capability.
bundle = new Bundle();
bundle.putInt(CommandReceiver.EXTRA_FLAGS, Context.BIND_INCLUDE_CAPABILITIES);
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE,
mAppInfo[0].packageName, mAppInfo[2].packageName, 0, bundle);
+ // Verify app2 has FOREGROUND_LOCATION capability.
mWatchers[2].waitFor(WatchUidRunner.CMD_PROCSTATE,
- WatchUidRunner.STATE_FG_SERVICE_LOCATION);
+ WatchUidRunner.STATE_FG_SERVICE,
+ new Integer(PROCESS_CAPABILITY_FOREGROUND_LOCATION));
// Back down to foreground service
CommandReceiver.sendCommand(mContext,
CommandReceiver.COMMAND_STOP_FOREGROUND_SERVICE_LOCATION,
mAppInfo[0].packageName, mAppInfo[0].packageName, 0, null);
- mWatchers[0].waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE);
+ // Verify app0 does NOT have FOREGROUND_LOCATION capability.
+ mWatchers[0].waitFor(WatchUidRunner.CMD_PROCSTATE,
+ WatchUidRunner.STATE_FG_SERVICE,
+ new Integer(PROCESS_CAPABILITY_NONE));
// Remove foreground service as well
CommandReceiver.sendCommand(mContext,
CommandReceiver.COMMAND_STOP_FOREGROUND_SERVICE,
mAppInfo[0].packageName, mAppInfo[0].packageName, 0, null);
- mWatchers[0].waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
+ mWatchers[0].waitFor(WatchUidRunner.CMD_PROCSTATE,
+ WatchUidRunner.STATE_CACHED_EMPTY,
+ new Integer(PROCESS_CAPABILITY_NONE));
// Clean up: unbind services to avoid from interferences with other tests
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
@@ -1870,18 +1893,19 @@
// This will start an activity in App0
activity = startSubActivity(ScreenOnActivity.class);
- // Bind Stub -> App 0, verify doesn't include capabilities (only BTOP, not TOP)
+ // Bind Stub -> App 0, verify doesn't include capability (only BTOP, not TOP)
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE,
STUB_PACKAGE_NAME, mAppInfo[0].packageName, 0, null);
- mWatchers[0].waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_BOUND_TOP);
+ mWatchers[0].waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_BOUND_TOP,
+ new Integer(0));
- // Bind Stub -> App 1, include capabilities (TOP)
+ // Bind Stub -> App 1, include capability (TOP)
Bundle bundle = new Bundle();
bundle.putInt(CommandReceiver.EXTRA_FLAGS, Context.BIND_INCLUDE_CAPABILITIES);
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE,
STUB_PACKAGE_NAME, mAppInfo[1].packageName, 0, bundle);
- mWatchers[1].waitFor(WatchUidRunner.CMD_PROCSTATE,
- WatchUidRunner.STATE_TOP);
+ mWatchers[1].waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_BOUND_TOP,
+ new Integer(PROCESS_CAPABILITY_ALL));
// Clean up: unbind services to avoid from interferences with other tests
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
diff --git a/tests/app/src/android/app/cts/android/app/cts/tools/ServiceProcessController.java b/tests/app/src/android/app/cts/android/app/cts/tools/ServiceProcessController.java
index 01235ae..80c3225 100644
--- a/tests/app/src/android/app/cts/android/app/cts/tools/ServiceProcessController.java
+++ b/tests/app/src/android/app/cts/android/app/cts/tools/ServiceProcessController.java
@@ -215,6 +215,6 @@
throw new IllegalStateException("Unexpected importance after killing process: "
+ importance);
}
- mUidWatcher.waitFor(WatchUidRunner.CMD_GONE, null, timeout);
+ mUidWatcher.waitFor(WatchUidRunner.CMD_GONE, timeout);
}
}
diff --git a/tests/app/src/android/app/cts/android/app/cts/tools/WatchUidRunner.java b/tests/app/src/android/app/cts/android/app/cts/tools/WatchUidRunner.java
index e6e844f..8e631cb 100644
--- a/tests/app/src/android/app/cts/android/app/cts/tools/WatchUidRunner.java
+++ b/tests/app/src/android/app/cts/android/app/cts/tools/WatchUidRunner.java
@@ -47,6 +47,7 @@
public static final int CMD_UNCACHED = 3;
public static final int CMD_CACHED = 4;
public static final int CMD_GONE = 5;
+ public static final int CMD_CAPABILITY = 6;
public static final String STATE_PERSISTENT = "PER";
public static final String STATE_PERSISTENT_UI = "PERU";
@@ -72,7 +73,7 @@
public static final String STATE_NONEXISTENT = "NONE";
static final String[] COMMAND_TO_STRING = new String[] {
- "procstate", "active", "idle", "uncached", "cached", "gone"
+ "procstate", "active", "idle", "uncached", "cached", "gone", "capability"
};
final Instrumentation mInstrumentation;
@@ -133,7 +134,7 @@
public void expect(int cmd, String procState, long timeout) {
long waitUntil = SystemClock.uptimeMillis() + timeout;
- String[] line = waitForNextLine(waitUntil, cmd, procState);
+ String[] line = waitForNextLine(waitUntil, cmd, procState, 0);
if (!COMMAND_TO_STRING[cmd].equals(line[1])) {
String msg = "Expected cmd " + COMMAND_TO_STRING[cmd]
+ " uid " + mUid + " but next report was " + Arrays.toString(line);
@@ -151,26 +152,61 @@
Log.d(TAG, "Got expected: " + Arrays.toString(line));
}
+ public void waitFor(int cmd) {
+ waitFor(cmd, null, null, mDefaultWaitTime);
+ }
+
+ public void waitFor(int cmd, long timeout) {
+ waitFor(cmd, null, null, timeout);
+ }
+
public void waitFor(int cmd, String procState) {
- waitFor(cmd, procState, mDefaultWaitTime);
+ waitFor(cmd, procState, null, mDefaultWaitTime);
+ }
+
+ public void waitFor(int cmd, String procState, Integer capability) {
+ waitFor(cmd, procState, capability, mDefaultWaitTime);
}
public void waitFor(int cmd, String procState, long timeout) {
+ waitFor(cmd, procState, null, timeout);
+ }
+
+ public void waitFor(int cmd, String procState, Integer capability, long timeout) {
long waitUntil = SystemClock.uptimeMillis() + timeout;
while (true) {
- String[] line = waitForNextLine(waitUntil, cmd, procState);
+ String[] line = waitForNextLine(waitUntil, cmd, procState, capability);
if (COMMAND_TO_STRING[cmd].equals(line[1])) {
- if (procState == null) {
+ if (procState == null && capability == null) {
Log.d(TAG, "Waited for: " + Arrays.toString(line));
return;
}
- if (line.length >= 3 && procState.equals(line[2])) {
- Log.d(TAG, "Waited for: " + Arrays.toString(line));
- return;
+ if (cmd == CMD_PROCSTATE) {
+ if (procState != null && capability != null) {
+ if (procState.equals(line[2]) && capability.toString().equals(line[6])) {
+ Log.d(TAG, "Waited for: " + Arrays.toString(line));
+ return;
+ }
+ } else if (procState != null) {
+ if (procState.equals(line[2])) {
+ Log.d(TAG, "Waited for: " + Arrays.toString(line));
+ return;
+ }
+ } else if (capability != null) {
+ if (capability.toString().equals(line[6])) {
+ Log.d(TAG, "Waited for: " + Arrays.toString(line));
+ return;
+ }
+ }
} else {
- Log.d(TAG, "Skipping because procstate not " + procState + ": "
- + Arrays.toString(line));
+ if (procState != null
+ && procState.equals(line[2])) {
+ Log.d(TAG, "Waited for: " + Arrays.toString(line));
+ return;
+ }
}
+ Log.d(TAG, "Skipping because procstate not " + procState + ": "
+ + Arrays.toString(line));
} else {
Log.d(TAG, "Skipping because not " + COMMAND_TO_STRING[cmd] + ": "
+ Arrays.toString(line));
@@ -191,14 +227,15 @@
}
}
- String[] waitForNextLine(long waitUntil, int cmd, String procState) {
+ String[] waitForNextLine(long waitUntil, int cmd, String procState, Integer capability) {
synchronized (mPendingLines) {
while (true) {
while (mPendingLines.size() == 0) {
long now = SystemClock.uptimeMillis();
if (now >= waitUntil) {
- String msg = "Timed out waiting for next line: "
- + "cmd=" + COMMAND_TO_STRING[cmd] + " procState=" + procState;
+ String msg = "Timed out waiting for next line: uid=" + mUidStr
+ + " cmd=" + COMMAND_TO_STRING[cmd] + " procState=" + procState
+ + " capability=" + capability;
Log.d(TAG, msg);
throw new IllegalStateException(msg);
}