diff options
| author | 2021-03-26 00:50:09 -0700 | |
|---|---|---|
| committer | 2021-03-29 14:44:41 -0700 | |
| commit | f45997c08795670a9bd5952012004bb1a9e92f16 (patch) | |
| tree | 346b77b5ae7f8b9551272e6160f163e7e4a83c75 | |
| parent | 6c67db7ba2496bee4aa890ec5ab3eed4c7de303c (diff) | |
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
9 files changed, 460 insertions, 24 deletions
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 7f8d944cf668..93cf6ce43d8c 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 static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NA 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } } - 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } } + /** + * 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 @@ public final class ActiveServices { 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 0f72c3ef9f97..14ca9dae27cd 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } + /** + * 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 b8e06ee51754..42aac295c027 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 @@ final class ActivityManagerShellCommand extends ShellCommand { return runRefreshSettingsCache(); case "memory-factor": return runMemoryFactor(pw); + case "service-restart-backoff": + return runServiceRestartBackoff(pw); default: return handleDefaultCommands(cmd); } @@ -3095,6 +3097,28 @@ final class ActivityManagerShellCommand extends ShellCommand { } } + 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 @@ final class ActivityManagerShellCommand extends ShellCommand { 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 9513c6e276b1..7c30b4553786 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 d34f78353e71..5a0f1ee963a2 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 e13597dc6982..e04841bf2335 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 @@ import java.util.concurrent.TimeUnit; 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 000000000000..10f4c05eb6d8 --- /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 50b89d484ee8..5550f7d0ef28 100644 --- a/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp +++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp @@ -22,7 +22,7 @@ package { } android_test_helper_app { - name: "SimpleServiceTestApp", + name: "SimpleServiceTestApp1", test_suites: ["device-tests"], @@ -36,4 +36,47 @@ android_test_helper_app { 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 75f71d609a13..674ce8aade4c 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.Bundle; 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 @@ public class SimpleService extends Service { }; @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; } |