Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 53ff2501 authored by Jing Ji's avatar Jing Ji
Browse files

Allow service auto-restarts when its dependency is force-stopped

While force-stopping a package, if it's being loaded as dependency
of another package, previously the processes of the latter package
will be stopped too, now retain the auto-restartable services in
the latter package and restart them later.

Bug: 190749907
Bug: 200137455
Test: atest FrameworksServicesTests#ServiceRestarterTest
Change-Id: Ieef1fcee01f3009251f5a8e147063665cedaae90
parent 17115827
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -4275,7 +4275,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,
@@ -7358,6 +7358,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");
@@ -7379,6 +7380,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");
+31 −10
Original line number Diff line number Diff line
@@ -135,7 +135,6 @@ import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerService.ProcessChangeItem;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.wm.ActivityServiceConnectionsHolder;
import com.android.server.wm.WindowManagerService;
@@ -2794,8 +2793,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")
@@ -2828,9 +2827,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
@@ -2847,7 +2847,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;
                }
@@ -2862,6 +2873,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) {
@@ -2883,9 +2896,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!
@@ -2895,13 +2915,14 @@ 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,
            final Pair<ProcessRecord, Boolean> proc = procs.get(i);
            removeProcessLocked(proc.first, callerWillRestart, allowRestart || proc.second,
                    reasonCode, subReason, reason);
        }
        killAppZygotesLocked(packageName, appId, userId, false /* force */);
+7 −0
Original line number Diff line number Diff line
@@ -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" />
+88 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW
import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

@@ -30,7 +31,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.provider.DeviceConfig;
import android.provider.Settings;
@@ -50,6 +53,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:
@@ -83,6 +88,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 SettingsSession<String> mAMConstantsSettings;
    private DeviceConfigSession<String> mExtraDelaysDeviceConfig;
    private DeviceConfigSession<Boolean> mEnableExtraDelaysDeviceConfig;
@@ -348,6 +359,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();
+1 −0
Original line number Diff line number Diff line
@@ -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"
Loading