Merge "Allow service auto-restarts when its dependency is force-stopped" into sc-v2-dev am: df6ae85d24
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/16387368 Change-Id: Ieb518c1bbf1a270065724104312b3e9abf25d4b7
This commit is contained in:
@@ -4190,7 +4190,7 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
|
||||
didSomething |= mProcessList.killPackageProcessesLSP(packageName, appId, userId,
|
||||
ProcessList.INVALID_ADJ, callerWillRestart, false /* allowRestart */, doit,
|
||||
evenPersistent, true /* setRemoved */,
|
||||
evenPersistent, true /* setRemoved */, uninstalling,
|
||||
packageName == null ? ApplicationExitInfo.REASON_USER_STOPPED
|
||||
: ApplicationExitInfo.REASON_USER_REQUESTED,
|
||||
ApplicationExitInfo.SUBREASON_UNKNOWN,
|
||||
@@ -7227,6 +7227,7 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
ProcessList.PERSISTENT_PROC_ADJ, false /* callerWillRestart */,
|
||||
true /* callerWillRestart */, true /* doit */,
|
||||
true /* evenPersistent */, false /* setRemoved */,
|
||||
false /* uninstalling */,
|
||||
ApplicationExitInfo.REASON_OTHER,
|
||||
ApplicationExitInfo.SUBREASON_KILL_UID,
|
||||
reason != null ? reason : "kill uid");
|
||||
@@ -7248,6 +7249,7 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
ProcessList.PERSISTENT_PROC_ADJ, false /* callerWillRestart */,
|
||||
true /* callerWillRestart */, true /* doit */,
|
||||
true /* evenPersistent */, false /* setRemoved */,
|
||||
false /* uninstalling */,
|
||||
ApplicationExitInfo.REASON_PERMISSION_CHANGE,
|
||||
ApplicationExitInfo.SUBREASON_UNKNOWN,
|
||||
reason != null ? reason : "kill uid");
|
||||
|
||||
@@ -2751,8 +2751,8 @@ public final class ProcessList {
|
||||
int reasonCode, int subReason, String reason) {
|
||||
return killPackageProcessesLSP(packageName, appId, userId, minOomAdj,
|
||||
false /* callerWillRestart */, true /* allowRestart */, true /* doit */,
|
||||
false /* evenPersistent */, false /* setRemoved */, reasonCode,
|
||||
subReason, reason);
|
||||
false /* evenPersistent */, false /* setRemoved */, false /* uninstalling */,
|
||||
reasonCode, subReason, reason);
|
||||
}
|
||||
|
||||
@GuardedBy("mService")
|
||||
@@ -2785,9 +2785,10 @@ public final class ProcessList {
|
||||
@GuardedBy({"mService", "mProcLock"})
|
||||
boolean killPackageProcessesLSP(String packageName, int appId,
|
||||
int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
|
||||
boolean doit, boolean evenPersistent, boolean setRemoved, int reasonCode,
|
||||
int subReason, String reason) {
|
||||
ArrayList<ProcessRecord> procs = new ArrayList<>();
|
||||
boolean doit, boolean evenPersistent, boolean setRemoved, boolean uninstalling,
|
||||
int reasonCode, int subReason, String reason) {
|
||||
final PackageManagerInternal pm = mService.getPackageManagerInternal();
|
||||
final ArrayList<Pair<ProcessRecord, Boolean>> procs = new ArrayList<>();
|
||||
|
||||
// Remove all processes this package may have touched: all with the
|
||||
// same UID (except for the system or root user), and all whose name
|
||||
@@ -2804,7 +2805,18 @@ public final class ProcessList {
|
||||
}
|
||||
if (app.isRemoved()) {
|
||||
if (doit) {
|
||||
procs.add(app);
|
||||
boolean shouldAllowRestart = false;
|
||||
if (!uninstalling && packageName != null) {
|
||||
// This package has a dependency on the given package being stopped,
|
||||
// while it's not being frozen nor uninstalled, allow to restart it.
|
||||
shouldAllowRestart = !app.getPkgList().containsKey(packageName)
|
||||
&& app.getPkgDeps() != null
|
||||
&& app.getPkgDeps().contains(packageName)
|
||||
&& app.info != null
|
||||
&& !pm.isPackageFrozen(app.info.packageName, app.uid,
|
||||
app.userId);
|
||||
}
|
||||
procs.add(new Pair<>(app, shouldAllowRestart));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -2819,6 +2831,8 @@ public final class ProcessList {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean shouldAllowRestart = false;
|
||||
|
||||
// If no package is specified, we call all processes under the
|
||||
// give user id.
|
||||
if (packageName == null) {
|
||||
@@ -2840,9 +2854,16 @@ public final class ProcessList {
|
||||
if (userId != UserHandle.USER_ALL && app.userId != userId) {
|
||||
continue;
|
||||
}
|
||||
if (!app.getPkgList().containsKey(packageName) && !isDep) {
|
||||
final boolean isInPkgList = app.getPkgList().containsKey(packageName);
|
||||
if (!isInPkgList && !isDep) {
|
||||
continue;
|
||||
}
|
||||
if (!isInPkgList && isDep && !uninstalling && app.info != null
|
||||
&& !pm.isPackageFrozen(app.info.packageName, app.uid, app.userId)) {
|
||||
// This package has a dependency on the given package being stopped,
|
||||
// while it's not being frozen nor uninstalled, allow to restart it.
|
||||
shouldAllowRestart = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Process has passed all conditions, kill it!
|
||||
@@ -2852,14 +2873,15 @@ public final class ProcessList {
|
||||
if (setRemoved) {
|
||||
app.setRemoved(true);
|
||||
}
|
||||
procs.add(app);
|
||||
procs.add(new Pair<>(app, shouldAllowRestart));
|
||||
}
|
||||
}
|
||||
|
||||
int N = procs.size();
|
||||
for (int i=0; i<N; i++) {
|
||||
removeProcessLocked(procs.get(i), callerWillRestart, allowRestart,
|
||||
reasonCode, subReason, reason);
|
||||
final Pair<ProcessRecord, Boolean> proc = procs.get(i);
|
||||
removeProcessLocked(proc.first, callerWillRestart, allowRestart || proc.second,
|
||||
reasonCode, subReason, reason);
|
||||
}
|
||||
killAppZygotesLocked(packageName, appId, userId, false /* force */);
|
||||
mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END);
|
||||
|
||||
@@ -16,6 +16,13 @@
|
||||
<configuration description="Runs Frameworks Services Tests.">
|
||||
<option name="test-suite-tag" value="apct" />
|
||||
<option name="test-suite-tag" value="apct-instrumentation" />
|
||||
|
||||
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
|
||||
<option name="cleanup" value="true" />
|
||||
<option name="push-file" key="SimpleServiceTestApp3.apk"
|
||||
value="/data/local/tmp/cts/content/SimpleServiceTestApp3.apk" />
|
||||
</target_preparer>
|
||||
|
||||
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
|
||||
<option name="cleanup-apks" value="true" />
|
||||
<option name="install-arg" value="-t" />
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.server.am;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@@ -26,7 +27,9 @@ import android.app.Instrumentation;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.os.IBinder;
|
||||
import android.os.SystemClock;
|
||||
import android.test.suitebuilder.annotation.LargeTest;
|
||||
import android.util.Log;
|
||||
@@ -42,6 +45,8 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Build/Install/Run:
|
||||
@@ -69,6 +74,12 @@ public final class ServiceRestarterTest {
|
||||
private static final int ACTION_STOPPKG = 8;
|
||||
private static final int ACTION_ALL = ACTION_START | ACTION_KILL | ACTION_WAIT | ACTION_STOPPKG;
|
||||
|
||||
private static final String LOCAL_APK_BASE_PATH = "/data/local/tmp/cts/content/";
|
||||
private static final String TEST_PACKAGE3_APK = "SimpleServiceTestApp3.apk";
|
||||
private static final String ACTION_SERVICE_WITH_DEP_PKG =
|
||||
"com.android.servicestests.apps.simpleservicetestapp.ACTION_SERVICE_WITH_DEP_PKG";
|
||||
private static final String EXTRA_TARGET_PACKAGE = "target_package";
|
||||
|
||||
private Context mContext;
|
||||
private Instrumentation mInstrumentation;
|
||||
private int mTestPackage1Uid;
|
||||
@@ -199,6 +210,83 @@ public final class ServiceRestarterTest {
|
||||
return res;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServiceWithDepPkgStopped() throws Exception {
|
||||
final CountDownLatch[] latchHolder = new CountDownLatch[1];
|
||||
final ServiceConnection conn = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
latchHolder[0].countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
latchHolder[0].countDown();
|
||||
}
|
||||
};
|
||||
|
||||
final long timeout = 5_000;
|
||||
final long shortTimeout = 2_000;
|
||||
final Intent intent = new Intent(ACTION_SERVICE_WITH_DEP_PKG);
|
||||
final String testPkg = TEST_PACKAGE2_NAME;
|
||||
final String libPkg = TEST_PACKAGE3_NAME;
|
||||
final String apkPath = LOCAL_APK_BASE_PATH + TEST_PACKAGE3_APK;
|
||||
final ActivityManager am = mContext.getSystemService(ActivityManager.class);
|
||||
|
||||
intent.setComponent(ComponentName.unflattenFromString(testPkg + "/" + TEST_SERVICE_NAME));
|
||||
intent.putExtra(EXTRA_TARGET_PACKAGE, libPkg);
|
||||
try {
|
||||
executeShellCmd("am service-restart-backoff disable " + testPkg);
|
||||
|
||||
latchHolder[0] = new CountDownLatch(1);
|
||||
assertTrue("Unable to bind to test service in " + testPkg,
|
||||
mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE));
|
||||
assertTrue("Timed out to bind service in " + testPkg,
|
||||
latchHolder[0].await(timeout, TimeUnit.MILLISECONDS));
|
||||
|
||||
Thread.sleep(shortTimeout);
|
||||
assertTrue(libPkg + " should be a dependency package of " + testPkg,
|
||||
isPackageDependency(testPkg, libPkg));
|
||||
|
||||
// Force-stop lib package, the test service should be restarted.
|
||||
latchHolder[0] = new CountDownLatch(2);
|
||||
am.forceStopPackage(libPkg);
|
||||
assertTrue("Test service in didn't restart in " + testPkg,
|
||||
latchHolder[0].await(timeout, TimeUnit.MILLISECONDS));
|
||||
|
||||
Thread.sleep(shortTimeout);
|
||||
|
||||
// Re-install the lib package, the test service should be restarted.
|
||||
latchHolder[0] = new CountDownLatch(2);
|
||||
assertTrue("Unable to install package " + apkPath, installPackage(apkPath));
|
||||
assertTrue("Test service in didn't restart in " + testPkg,
|
||||
latchHolder[0].await(timeout, TimeUnit.MILLISECONDS));
|
||||
|
||||
Thread.sleep(shortTimeout);
|
||||
|
||||
// Force-stop the service package, the test service should not be restarted.
|
||||
latchHolder[0] = new CountDownLatch(2);
|
||||
am.forceStopPackage(testPkg);
|
||||
assertFalse("Test service should not be restarted in " + testPkg,
|
||||
latchHolder[0].await(timeout * 2, TimeUnit.MILLISECONDS));
|
||||
} finally {
|
||||
executeShellCmd("am service-restart-backoff enable " + testPkg);
|
||||
mContext.unbindService(conn);
|
||||
am.forceStopPackage(testPkg);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPackageDependency(String pkgName, String libPackage) throws Exception {
|
||||
final String output = SystemUtil.runShellCommand("dumpsys activity processes " + pkgName);
|
||||
final Matcher matcher = Pattern.compile("packageDependencies=\\{.*?\\b"
|
||||
+ libPackage + "\\b.*?\\}").matcher(output);
|
||||
return matcher.find();
|
||||
}
|
||||
|
||||
private boolean installPackage(String apkPath) throws Exception {
|
||||
return executeShellCmd("pm install -t " + apkPath).equals("Success\n");
|
||||
}
|
||||
|
||||
private void startServiceAndWait(String pkgName, MyUidImportanceListener uidListener,
|
||||
long timeout) throws Exception {
|
||||
final Intent intent = new Intent();
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package="com.android.servicestests.apps.simpleservicetestapp">
|
||||
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||
|
||||
<application>
|
||||
<service android:name=".SimpleService"
|
||||
|
||||
@@ -17,8 +17,10 @@ package com.android.servicestests.apps.simpleservicetestapp;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.IRemoteCallback;
|
||||
@@ -33,6 +35,9 @@ public class SimpleService extends Service {
|
||||
private static final String TEST_CLASS =
|
||||
"com.android.servicestests.apps.simpleservicetestapp.SimpleService";
|
||||
|
||||
private static final String ACTION_SERVICE_WITH_DEP_PKG =
|
||||
"com.android.servicestests.apps.simpleservicetestapp.ACTION_SERVICE_WITH_DEP_PKG";
|
||||
|
||||
private static final String EXTRA_CALLBACK = "callback";
|
||||
private static final String EXTRA_COMMAND = "command";
|
||||
private static final String EXTRA_FLAGS = "flags";
|
||||
@@ -118,6 +123,21 @@ public class SimpleService extends Service {
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
if (ACTION_SERVICE_WITH_DEP_PKG.equals(intent.getAction())) {
|
||||
final String targetPkg = intent.getStringExtra(EXTRA_TARGET_PACKAGE);
|
||||
Log.i(TAG, "SimpleService.onBind: " + ACTION_SERVICE_WITH_DEP_PKG + " " + targetPkg);
|
||||
if (targetPkg != null) {
|
||||
Context pkgContext = null;
|
||||
try {
|
||||
pkgContext = createPackageContext(targetPkg,
|
||||
Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(TAG, "Unable to create package context for " + pkgContext, e);
|
||||
}
|
||||
// This effectively loads the target package as a dependency.
|
||||
pkgContext.getClassLoader();
|
||||
}
|
||||
}
|
||||
return mBinder;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user