Add an am command to disable service restart backoff policy.
So service restart could happen sooner in testings.
BYPASS_INCLUSIVE_LANGUAGE_REASON=legacy API name
Bug: 170309420
Bug: 182832497
Test: atest FrameworksServicesTests:ServiceRestarterTest
Change-Id: I1180333f628c2bb3d974ae826a1657b0ada00594
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 7f8d944..93cf6ce 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -70,6 +70,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UptimeMillisLong;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityThread;
@@ -237,6 +238,12 @@
private final SparseArray<AppOpCallback> mFgsAppOpCallbacks = new SparseArray<>();
/**
+ * The list of packages with the service restart backoff disabled.
+ */
+ @GuardedBy("mAm")
+ private final ArraySet<String> mRestartBackoffDisabledPackages = new ArraySet<>();
+
+ /**
* For keeping ActiveForegroundApps retaining state while the screen is off.
*/
boolean mScreenOn = true;
@@ -3272,25 +3279,32 @@
}
}
- r.nextRestartTime = now + r.restartDelay;
+ if (isServiceRestartBackoffEnabledLocked(r.packageName)) {
+ r.nextRestartTime = now + r.restartDelay;
- // Make sure that we don't end up restarting a bunch of services
- // all at the same time.
- boolean repeat;
- do {
- repeat = false;
+ // Make sure that we don't end up restarting a bunch of services
+ // all at the same time.
+ boolean repeat;
final long restartTimeBetween = mAm.mConstants.SERVICE_MIN_RESTART_TIME_BETWEEN;
- for (int i=mRestartingServices.size()-1; i>=0; i--) {
- ServiceRecord r2 = mRestartingServices.get(i);
- if (r2 != r && r.nextRestartTime >= (r2.nextRestartTime-restartTimeBetween)
- && r.nextRestartTime < (r2.nextRestartTime+restartTimeBetween)) {
- r.nextRestartTime = r2.nextRestartTime + restartTimeBetween;
- r.restartDelay = r.nextRestartTime - now;
- repeat = true;
- break;
+ do {
+ repeat = false;
+ for (int i = mRestartingServices.size() - 1; i >= 0; i--) {
+ final ServiceRecord r2 = mRestartingServices.get(i);
+ if (r2 != r
+ && r.nextRestartTime >= (r2.nextRestartTime - restartTimeBetween)
+ && r.nextRestartTime < (r2.nextRestartTime + restartTimeBetween)) {
+ r.nextRestartTime = r2.nextRestartTime + restartTimeBetween;
+ r.restartDelay = r.nextRestartTime - now;
+ repeat = true;
+ break;
+ }
}
- }
- } while (repeat);
+ } while (repeat);
+ } else {
+ // It's been forced to ignore the restart backoff, fix the delay here.
+ r.restartDelay = mAm.mConstants.SERVICE_RESTART_DURATION;
+ r.nextRestartTime = now + r.restartDelay;
+ }
} else {
// Persistent processes are immediately restarted, so there is no
@@ -3310,15 +3324,22 @@
cancelForegroundNotificationLocked(r);
+ performScheduleRestartLocked(r, "Scheduling", reason, SystemClock.uptimeMillis());
+
+ return true;
+ }
+
+ @VisibleForTesting
+ @GuardedBy("mAm")
+ void performScheduleRestartLocked(ServiceRecord r, @NonNull String scheduling,
+ @NonNull String reason, @UptimeMillisLong long now) {
mAm.mHandler.removeCallbacks(r.restarter);
mAm.mHandler.postAtTime(r.restarter, r.nextRestartTime);
- r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay;
- Slog.w(TAG, "Scheduling restart of crashed service "
+ r.nextRestartTime = now + r.restartDelay;
+ Slog.w(TAG, scheduling + " restart of crashed service "
+ r.shortInstanceName + " in " + r.restartDelay + "ms for " + reason);
EventLog.writeEvent(EventLogTags.AM_SCHEDULE_SERVICE_RESTART,
r.userId, r.shortInstanceName, r.restartDelay);
-
- return true;
}
final void performServiceRestartLocked(ServiceRecord r) {
@@ -3383,6 +3404,52 @@
}
}
+ /**
+ * Toggle service restart backoff policy, used by {@link ActivityManagerShellCommand}.
+ */
+ @GuardedBy("mAm")
+ void setServiceRestartBackoffEnabledLocked(@NonNull String packageName, boolean enable,
+ @NonNull String reason) {
+ if (!enable) {
+ if (mRestartBackoffDisabledPackages.contains(packageName)) {
+ // Already disabled, do nothing.
+ return;
+ }
+ mRestartBackoffDisabledPackages.add(packageName);
+
+ final long now = SystemClock.uptimeMillis();
+ for (int i = 0, size = mRestartingServices.size(); i < size; i++) {
+ final ServiceRecord r = mRestartingServices.get(i);
+ if (TextUtils.equals(r.packageName, packageName)) {
+ final long remaining = r.nextRestartTime - now;
+ if (remaining > mAm.mConstants.SERVICE_RESTART_DURATION) {
+ r.restartDelay = mAm.mConstants.SERVICE_RESTART_DURATION;
+ r.nextRestartTime = now + r.restartDelay;
+ performScheduleRestartLocked(r, "Rescheduling", reason, now);
+ }
+ }
+ }
+ } else {
+ removeServiceRestartBackoffEnabledLocked(packageName);
+ // For the simplicity, we are not going to reschedule its pending restarts
+ // when we turn the backoff policy back on.
+ }
+ }
+
+ @GuardedBy("mAm")
+ private void removeServiceRestartBackoffEnabledLocked(@NonNull String packageName) {
+ mRestartBackoffDisabledPackages.remove(packageName);
+ }
+
+ /**
+ * @return {@code false} if the given package has been disable from enforcing the service
+ * restart backoff policy, used by {@link ActivityManagerShellCommand}.
+ */
+ @GuardedBy("mAm")
+ boolean isServiceRestartBackoffEnabledLocked(@NonNull String packageName) {
+ return !mRestartBackoffDisabledPackages.contains(packageName);
+ }
+
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
boolean whileRestarting, boolean permissionsReviewRequired, boolean packageFrozen,
boolean enqueueOomAdj)
@@ -4431,6 +4498,7 @@
mPendingBringups.removeAt(i);
}
}
+ removeServiceRestartBackoffEnabledLocked(packageName);
}
void cleanUpServices(int userId, ComponentName component, Intent baseIntent) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0f72c3e..14ca9da 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8436,6 +8436,26 @@
}
}
+ /**
+ * Toggle service restart backoff policy, used by {@link ActivityManagerShellCommand}.
+ */
+ void setServiceRestartBackoffEnabled(@NonNull String packageName, boolean enable,
+ @NonNull String reason) {
+ synchronized (this) {
+ mServices.setServiceRestartBackoffEnabledLocked(packageName, enable, reason);
+ }
+ }
+
+ /**
+ * @return {@code false} if the given package has been disable from enforcing the service
+ * restart backoff policy, used by {@link ActivityManagerShellCommand}.
+ */
+ boolean isServiceRestartBackoffEnabled(@NonNull String packageName) {
+ synchronized (this) {
+ return mServices.isServiceRestartBackoffEnabledLocked(packageName);
+ }
+ }
+
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index b8e06ee..42aac29 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -318,6 +318,8 @@
return runRefreshSettingsCache();
case "memory-factor":
return runMemoryFactor(pw);
+ case "service-restart-backoff":
+ return runServiceRestartBackoff(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -3095,6 +3097,28 @@
}
}
+ private int runServiceRestartBackoff(PrintWriter pw) throws RemoteException {
+ mInternal.enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT,
+ "runServiceRestartBackoff()");
+
+ final String opt = getNextArgRequired();
+ switch (opt) {
+ case "enable":
+ mInternal.setServiceRestartBackoffEnabled(getNextArgRequired(), true, "shell");
+ return 0;
+ case "disable":
+ mInternal.setServiceRestartBackoffEnabled(getNextArgRequired(), false, "shell");
+ return 0;
+ case "show":
+ pw.println(mInternal.isServiceRestartBackoffEnabled(getNextArgRequired())
+ ? "enabled" : "disabled");
+ return 0;
+ default:
+ getErrPrintWriter().println("Error: unknown command '" + opt + "'");
+ return -1;
+ }
+ }
+
private Resources getResources(PrintWriter pw) throws RemoteException {
// system resources does not contain all the device configuration, construct it manually.
Configuration config = mInterface.getConfiguration();
@@ -3418,6 +3442,11 @@
pw.println(" Shows the existing memory pressure factor");
pw.println(" reset");
pw.println(" Removes existing override for memory pressure factor");
+ pw.println(" service-restart-backoff <COMMAND> [...]: sub-commands to toggle service restart backoff policy.");
+ pw.println(" enable|disable <PACKAGE_NAME>");
+ pw.println(" Toggles the restart backoff policy on/off for <PACKAGE_NAME>.");
+ pw.println(" show <PACKAGE_NAME>");
+ pw.println(" Shows the restart backoff policy state for <PACKAGE_NAME>.");
pw.println();
Intent.printIntentArgsHelp(pw, "");
}
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 9513c6e..7c30b45 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -91,6 +91,7 @@
<uses-permission android:name="android.permission.VIBRATE_ALWAYS_ON"/>
<uses-permission android:name="android.permission.CONTROL_DEVICE_STATE"/>
<uses-permission android:name="android.permission.READ_PROJECTION_STATE"/>
+ <uses-permission android:name="android.permission.KILL_UID"/>
<!-- Uses API introduced in O (26) -->
<uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index d34f783..5a0f1ee 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -23,7 +23,9 @@
<option name="test-file-name" value="JobTestApp.apk" />
<option name="test-file-name" value="ConnTestApp.apk" />
<option name="test-file-name" value="SuspendTestApp.apk" />
- <option name="test-file-name" value="SimpleServiceTestApp.apk" />
+ <option name="test-file-name" value="SimpleServiceTestApp1.apk" />
+ <option name="test-file-name" value="SimpleServiceTestApp2.apk" />
+ <option name="test-file-name" value="SimpleServiceTestApp3.apk" />
</target_preparer>
<option name="test-tag" value="FrameworksServicesTests" />
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
index e13597d..e04841b 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
@@ -73,8 +73,9 @@
public class ActivityManagerTest {
private static final String TAG = "ActivityManagerTest";
- private static final String TEST_APP = "com.android.servicestests.apps.simpleservicetestapp";
- private static final String TEST_CLASS = TEST_APP + ".SimpleService";
+ private static final String TEST_APP = "com.android.servicestests.apps.simpleservicetestapp1";
+ private static final String TEST_CLASS =
+ "com.android.servicestests.apps.simpleservicetestapp.SimpleService";
private static final int TEST_LOOPS = 100;
private static final long AWAIT_TIMEOUT = 2000;
private static final long CHECK_INTERVAL = 100;
diff --git a/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java b/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java
new file mode 100644
index 0000000..10f4c05
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2021 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.server.am;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.OnUidImportanceListener;
+import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.Instrumentation;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.os.SystemClock;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:ServiceRestarterTest
+ */
+@RunWith(AndroidJUnit4.class)
+public final class ServiceRestarterTest {
+ private static final String TAG = "ServiceRestarterTest";
+
+ private static final String TEST_PACKAGE1_NAME =
+ "com.android.servicestests.apps.simpleservicetestapp1";
+ private static final String TEST_PACKAGE2_NAME =
+ "com.android.servicestests.apps.simpleservicetestapp2";
+ private static final String TEST_PACKAGE3_NAME =
+ "com.android.servicestests.apps.simpleservicetestapp3";
+ private static final String TEST_SERVICE_NAME =
+ "com.android.servicestests.apps.simpleservicetestapp.SimpleService";
+
+ private static final long WAIT_MS = 5 * 1000;
+ private static final long WAIT_LONG_MS = 30 * 1000;
+
+ private static final int ACTION_START = 1;
+ private static final int ACTION_KILL = 2;
+ private static final int ACTION_WAIT = 4;
+ private static final int ACTION_STOPPKG = 8;
+ private static final int ACTION_ALL = ACTION_START | ACTION_KILL | ACTION_WAIT | ACTION_STOPPKG;
+
+ private Context mContext;
+ private Instrumentation mInstrumentation;
+ private int mTestPackage1Uid;
+ private int mTestPackage2Uid;
+ private int mTestPackage3Uid;
+
+ @Before
+ public void setUp() throws Exception {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mContext = InstrumentationRegistry.getContext();
+ ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(TEST_PACKAGE1_NAME, 0);
+ mTestPackage1Uid = ai.uid;
+ ai = mContext.getPackageManager().getApplicationInfo(TEST_PACKAGE2_NAME, 0);
+ mTestPackage2Uid = ai.uid;
+ ai = mContext.getPackageManager().getApplicationInfo(TEST_PACKAGE3_NAME, 0);
+ mTestPackage3Uid = ai.uid;
+ }
+
+ @LargeTest
+ @Test
+ public void testDisableServiceRestartBackoff() throws Exception {
+ final ActivityManager am = mContext.getSystemService(ActivityManager.class);
+ final MyUidImportanceListener uid1Listener1 = new MyUidImportanceListener(mTestPackage1Uid);
+ final MyUidImportanceListener uid1Listener2 = new MyUidImportanceListener(mTestPackage1Uid);
+ final MyUidImportanceListener uid2Listener1 = new MyUidImportanceListener(mTestPackage2Uid);
+ final MyUidImportanceListener uid2Listener2 = new MyUidImportanceListener(mTestPackage2Uid);
+ final MyUidImportanceListener uid3Listener1 = new MyUidImportanceListener(mTestPackage3Uid);
+ final MyUidImportanceListener uid3Listener2 = new MyUidImportanceListener(mTestPackage3Uid);
+ try {
+ am.addOnUidImportanceListener(uid1Listener1, RunningAppProcessInfo.IMPORTANCE_SERVICE);
+ am.addOnUidImportanceListener(uid1Listener2, RunningAppProcessInfo.IMPORTANCE_GONE);
+ am.addOnUidImportanceListener(uid2Listener1, RunningAppProcessInfo.IMPORTANCE_SERVICE);
+ am.addOnUidImportanceListener(uid2Listener2, RunningAppProcessInfo.IMPORTANCE_GONE);
+ am.addOnUidImportanceListener(uid3Listener1, RunningAppProcessInfo.IMPORTANCE_SERVICE);
+ am.addOnUidImportanceListener(uid3Listener2, RunningAppProcessInfo.IMPORTANCE_GONE);
+ executeShellCmd("cmd deviceidle whitelist +" + TEST_PACKAGE1_NAME);
+ executeShellCmd("cmd deviceidle whitelist +" + TEST_PACKAGE2_NAME);
+ executeShellCmd("cmd deviceidle whitelist +" + TEST_PACKAGE3_NAME);
+
+ // Issue the command to enable service backoff policy for app2.
+ executeShellCmd("am service-restart-backoff enable " + TEST_PACKAGE2_NAME);
+ // Test restarts in normal case
+ final long[] ts1 = startKillAndRestart(am, ACTION_START | ACTION_KILL | ACTION_WAIT,
+ uid1Listener1, uid1Listener2, uid2Listener1, uid2Listener2,
+ uid3Listener1, uid3Listener2, Long.MAX_VALUE);
+ assertTrue("app1 restart should be before app2", ts1[1] < ts1[2]);
+ assertTrue("app2 restart should be before app3", ts1[2] < ts1[3]);
+
+ // Issue the command to disable service backoff policy for app2.
+ executeShellCmd("am service-restart-backoff disable " + TEST_PACKAGE2_NAME);
+ // Test restarts again.
+ final long[] ts2 = startKillAndRestart(am, ACTION_KILL | ACTION_WAIT | ACTION_STOPPKG,
+ uid1Listener1, uid1Listener2, uid2Listener1,
+ uid2Listener2, uid3Listener1, uid3Listener2, Long.MAX_VALUE);
+ assertTrue("app2 restart should be before app1", ts2[2] < ts2[1]);
+ assertTrue("app1 restart should be before app3", ts2[1] < ts2[3]);
+ assertTrue("app2 should be restart in a very short moment", ts2[2] - ts2[0] < WAIT_MS);
+
+ // Issue the command to enable service backoff policy for app2.
+ executeShellCmd("am service-restart-backoff enable " + TEST_PACKAGE2_NAME);
+ // Test restarts again.
+ final long[] ts3 = startKillAndRestart(am, ACTION_ALL, uid1Listener1, uid1Listener2,
+ uid2Listener1, uid2Listener2, uid3Listener1, uid3Listener2, Long.MAX_VALUE);
+ assertTrue("app1 restart should be before app2", ts3[1] < ts3[2]);
+ assertTrue("app2 restart should be before app3", ts3[2] < ts3[3]);
+
+ } finally {
+ executeShellCmd("cmd deviceidle whitelist -" + TEST_PACKAGE1_NAME);
+ executeShellCmd("cmd deviceidle whitelist -" + TEST_PACKAGE2_NAME);
+ executeShellCmd("cmd deviceidle whitelist -" + TEST_PACKAGE3_NAME);
+ executeShellCmd("am service-restart-backoff enable " + TEST_PACKAGE2_NAME);
+ am.removeOnUidImportanceListener(uid1Listener1);
+ am.removeOnUidImportanceListener(uid1Listener2);
+ am.removeOnUidImportanceListener(uid2Listener1);
+ am.removeOnUidImportanceListener(uid2Listener2);
+ am.removeOnUidImportanceListener(uid3Listener1);
+ am.removeOnUidImportanceListener(uid3Listener2);
+ am.forceStopPackage(TEST_PACKAGE1_NAME);
+ am.forceStopPackage(TEST_PACKAGE2_NAME);
+ am.forceStopPackage(TEST_PACKAGE3_NAME);
+ }
+ }
+
+ private long[] startKillAndRestart(ActivityManager am, int action,
+ MyUidImportanceListener uid1Listener1, MyUidImportanceListener uid1Listener2,
+ MyUidImportanceListener uid2Listener1, MyUidImportanceListener uid2Listener2,
+ MyUidImportanceListener uid3Listener1, MyUidImportanceListener uid3Listener2,
+ long waitDuration) throws Exception {
+ final long[] res = new long[4];
+ // Test restarts in normal condition.
+ if ((action & ACTION_START) != 0) {
+ startServiceAndWait(TEST_PACKAGE1_NAME, uid1Listener1, WAIT_MS);
+ startServiceAndWait(TEST_PACKAGE2_NAME, uid2Listener1, WAIT_MS);
+ startServiceAndWait(TEST_PACKAGE3_NAME, uid3Listener1, WAIT_MS);
+ }
+
+ if ((action & ACTION_KILL) != 0) {
+ final long now = res[0] = SystemClock.uptimeMillis();
+ killUidAndWait(am, mTestPackage1Uid, uid1Listener2, WAIT_MS);
+ killUidAndWait(am, mTestPackage2Uid, uid2Listener2, WAIT_MS);
+ killUidAndWait(am, mTestPackage3Uid, uid3Listener2, WAIT_MS);
+ }
+
+ if ((action & ACTION_WAIT) != 0) {
+ assertTrue("Timed out to restart " + TEST_PACKAGE1_NAME, uid1Listener1.waitFor(
+ RunningAppProcessInfo.IMPORTANCE_SERVICE, waitDuration));
+ assertTrue("Timed out to restart " + TEST_PACKAGE2_NAME, uid2Listener1.waitFor(
+ RunningAppProcessInfo.IMPORTANCE_SERVICE, waitDuration));
+ assertTrue("Timed out to restart " + TEST_PACKAGE3_NAME, uid3Listener1.waitFor(
+ RunningAppProcessInfo.IMPORTANCE_SERVICE, waitDuration));
+ res[1] = uid1Listener1.mCurrentTimestamp;
+ res[2] = uid2Listener1.mCurrentTimestamp;
+ res[3] = uid3Listener1.mCurrentTimestamp;
+ }
+
+ if ((action & ACTION_STOPPKG) != 0) {
+ // Force stop these packages to reset the backoff delays.
+ am.forceStopPackage(TEST_PACKAGE1_NAME);
+ am.forceStopPackage(TEST_PACKAGE2_NAME);
+ am.forceStopPackage(TEST_PACKAGE3_NAME);
+ assertTrue("Timed out to force-stop " + mTestPackage1Uid,
+ uid1Listener2.waitFor(RunningAppProcessInfo.IMPORTANCE_GONE, WAIT_MS));
+ assertTrue("Timed out to force-stop " + mTestPackage2Uid,
+ uid2Listener2.waitFor(RunningAppProcessInfo.IMPORTANCE_GONE, WAIT_MS));
+ assertTrue("Timed out to force-stop " + mTestPackage3Uid,
+ uid3Listener2.waitFor(RunningAppProcessInfo.IMPORTANCE_GONE, WAIT_MS));
+ }
+ return res;
+ }
+
+ private void startServiceAndWait(String pkgName, MyUidImportanceListener uidListener,
+ long timeout) throws Exception {
+ final Intent intent = new Intent();
+ final ComponentName cn = ComponentName.unflattenFromString(
+ pkgName + "/" + TEST_SERVICE_NAME);
+ intent.setComponent(cn);
+ Log.i(TAG, "Starting service " + cn);
+ assertNotNull(mContext.startService(intent));
+ assertTrue("Timed out to start service " + cn,
+ uidListener.waitFor(RunningAppProcessInfo.IMPORTANCE_SERVICE, timeout));
+ }
+
+ private void killUidAndWait(ActivityManager am, int uid, MyUidImportanceListener uidListener,
+ long timeout) throws Exception {
+ am.killUid(uid, "test service restart");
+ assertTrue("Timed out to kill " + uid,
+ uidListener.waitFor(RunningAppProcessInfo.IMPORTANCE_GONE, timeout));
+ }
+
+ private String executeShellCmd(String cmd) throws Exception {
+ final String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
+ Log.d(TAG, String.format("Output for '%s': %s", cmd, result));
+ return result;
+ }
+
+ private static class MyUidImportanceListener implements OnUidImportanceListener {
+ final CountDownLatch[] mLatchHolder = new CountDownLatch[1];
+ private final int mExpectedUid;
+ private int mExpectedImportance;
+ private int mCurrentImportance = RunningAppProcessInfo.IMPORTANCE_GONE;
+ long mCurrentTimestamp;
+
+ MyUidImportanceListener(int uid) {
+ mExpectedUid = uid;
+ }
+
+ @Override
+ public void onUidImportance(int uid, int importance) {
+ if (uid == mExpectedUid) {
+ mCurrentTimestamp = SystemClock.uptimeMillis();
+ synchronized (this) {
+ if (importance == mExpectedImportance && mLatchHolder[0] != null) {
+ mLatchHolder[0].countDown();
+ }
+ mCurrentImportance = importance;
+ }
+ Log.i(TAG, "uid " + uid + " importance: " + importance);
+ }
+ }
+
+ boolean waitFor(int expectedImportance, long timeout) throws Exception {
+ synchronized (this) {
+ mExpectedImportance = expectedImportance;
+ if (mCurrentImportance == expectedImportance) {
+ return true;
+ }
+ mLatchHolder[0] = new CountDownLatch(1);
+ }
+ return mLatchHolder[0].await(timeout, TimeUnit.MILLISECONDS);
+ }
+ }
+}
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp b/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp
index 50b89d4..5550f7d 100644
--- a/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp
@@ -22,7 +22,7 @@
}
android_test_helper_app {
- name: "SimpleServiceTestApp",
+ name: "SimpleServiceTestApp1",
test_suites: ["device-tests"],
@@ -36,4 +36,47 @@
optimize: {
enabled: false,
},
+ aaptflags: [
+ "--rename-manifest-package com.android.servicestests.apps.simpleservicetestapp1",
+ ],
+}
+
+android_test_helper_app {
+ name: "SimpleServiceTestApp2",
+
+ test_suites: ["device-tests"],
+
+ srcs: ["**/*.java"],
+
+ platform_apis: true,
+ certificate: "platform",
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+ aaptflags: [
+ "--rename-manifest-package com.android.servicestests.apps.simpleservicetestapp2",
+ ],
+}
+
+android_test_helper_app {
+ name: "SimpleServiceTestApp3",
+
+ test_suites: ["device-tests"],
+
+ srcs: ["**/*.java"],
+
+ platform_apis: true,
+ certificate: "platform",
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+ aaptflags: [
+ "--rename-manifest-package com.android.servicestests.apps.simpleservicetestapp3",
+ ],
}
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java
index 75f71d6..674ce8a 100644
--- a/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java
@@ -21,8 +21,11 @@
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.Process;
+import android.util.Log;
public class SimpleService extends Service {
+ private static final String TAG = "SimpleService";
+
private final IRemoteCallback.Stub mBinder = new IRemoteCallback.Stub() {
@Override
public void sendResult(Bundle bundle) {
@@ -31,6 +34,12 @@
};
@Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.i(TAG, "onStartCommand");
+ return START_STICKY;
+ }
+
+ @Override
public IBinder onBind(Intent intent) {
return mBinder;
}