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:
Jing Ji
2021-12-09 01:35:19 +00:00
committed by Automerger Merge Worker
6 changed files with 151 additions and 11 deletions

View File

@@ -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");

View File

@@ -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);

View File

@@ -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" />

View File

@@ -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();

View File

@@ -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"

View File

@@ -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;
}
}