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

Commit b6ec069e authored by Jing Ji's avatar Jing Ji
Browse files

Skip service timeout if the host process has been killed.

Bug: 268639079
Test: atest ServiceTimeoutTest
Change-Id: I67058bb5b18e700c9cb2e7b7a5cb59bac7fd6ce2
parent f8bbb00c
Loading
Loading
Loading
Loading
+7 −11
Original line number Diff line number Diff line
@@ -252,12 +252,6 @@ public final class ActiveServices {

    private static final boolean LOG_SERVICE_START_STOP = DEBUG_SERVICE;

    // How long we wait for a service to finish executing.
    static final int SERVICE_TIMEOUT = 20 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;

    // How long we wait for a service to finish executing.
    static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;

    // Foreground service types that always get immediate notification display,
    // expressed in the same bitmask format that ServiceRecord.foregroundServiceType
    // uses.
@@ -6598,13 +6592,15 @@ public final class ActiveServices {
                    return;
                }
                final ProcessServiceRecord psr = proc.mServices;
                if (psr.numberOfExecutingServices() == 0 || proc.getThread() == null) {
                if (psr.numberOfExecutingServices() == 0 || proc.getThread() == null
                        || proc.isKilled()) {
                    return;
                }
                final long now = SystemClock.uptimeMillis();
                final long maxTime =  now
                        - (psr.shouldExecServicesFg()
                        ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
                        ? mAm.mConstants.SERVICE_TIMEOUT
                        : mAm.mConstants.SERVICE_BACKGROUND_TIMEOUT);
                ServiceRecord timeout = null;
                long nextTime = 0;
                for (int i = psr.numberOfExecutingServices() - 1; i >= 0; i--) {
@@ -6635,8 +6631,8 @@ public final class ActiveServices {
                            ActivityManagerService.SERVICE_TIMEOUT_MSG);
                    msg.obj = proc;
                    mAm.mHandler.sendMessageAtTime(msg, psr.shouldExecServicesFg()
                            ? (nextTime + SERVICE_TIMEOUT) :
                            (nextTime + SERVICE_BACKGROUND_TIMEOUT));
                            ? (nextTime + mAm.mConstants.SERVICE_TIMEOUT) :
                            (nextTime + mAm.mConstants.SERVICE_BACKGROUND_TIMEOUT));
                }
            }

@@ -6732,7 +6728,7 @@ public final class ActiveServices {
                ActivityManagerService.SERVICE_TIMEOUT_MSG);
        msg.obj = proc;
        mAm.mHandler.sendMessageDelayed(msg, proc.mServices.shouldExecServicesFg()
                ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
                ? mAm.mConstants.SERVICE_TIMEOUT : mAm.mConstants.SERVICE_BACKGROUND_TIMEOUT);
    }

    void scheduleServiceForegroundTransitionTimeoutLocked(ServiceRecord r) {
+16 −1
Original line number Diff line number Diff line
@@ -248,7 +248,16 @@ final class ActivityManagerConstants extends ContentObserver {

    private static final long DEFAULT_SERVICE_BIND_ALMOST_PERCEPTIBLE_TIMEOUT_MS = 15 * 1000;

    // Flag stored in the DeviceConfig API.
    /**
     * Default value to {@link #SERVICE_TIMEOUT}.
     */
    private static final long DEFAULT_SERVICE_TIMEOUT = 20 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;

    /**
     * Default value to {@link #SERVICE_BACKGROUND_TIMEOUT}.
     */
    private static final long DEFAULT_SERVICE_BACKGROUND_TIMEOUT = DEFAULT_SERVICE_TIMEOUT * 10;

    /**
     * Maximum number of cached processes.
     */
@@ -506,6 +515,12 @@ final class ActivityManagerConstants extends ContentObserver {
    // to restart less than this amount of time from the last one.
    public long SERVICE_MIN_RESTART_TIME_BETWEEN = DEFAULT_SERVICE_MIN_RESTART_TIME_BETWEEN;

    // How long we wait for a service to finish executing.
    long SERVICE_TIMEOUT = DEFAULT_SERVICE_TIMEOUT;

    // How long we wait for a service to finish executing.
    long SERVICE_BACKGROUND_TIMEOUT = DEFAULT_SERVICE_BACKGROUND_TIMEOUT;

    // Maximum amount of time for there to be no activity on a service before
    // we consider it non-essential and allow its process to go on the
    // LRU background list.
+8 −1
Original line number Diff line number Diff line
@@ -989,9 +989,16 @@ public class ApplicationExitInfoTest {
    private ProcessRecord makeProcessRecord(int pid, int uid, int packageUid, Integer definingUid,
            int connectionGroup, int procState, long pss, long rss,
            String processName, String packageName) {
        return makeProcessRecord(pid, uid, packageUid, definingUid, connectionGroup,
                procState, pss, rss, processName, packageName, mAms);
    }

    static ProcessRecord makeProcessRecord(int pid, int uid, int packageUid, Integer definingUid,
            int connectionGroup, int procState, long pss, long rss,
            String processName, String packageName, ActivityManagerService ams) {
        ApplicationInfo ai = new ApplicationInfo();
        ai.packageName = packageName;
        ProcessRecord app = new ProcessRecord(mAms, ai, processName, uid);
        ProcessRecord app = new ProcessRecord(ams, ai, processName, uid);
        app.setPid(pid);
        app.info.uid = packageUid;
        if (definingUid != null) {
+204 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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 android.app.ActivityManager.PROCESS_STATE_SERVICE;

import static com.android.server.am.ApplicationExitInfoTest.makeProcessRecord;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.after;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import android.app.IApplicationThread;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;

import androidx.test.platform.app.InstrumentationRegistry;

import com.android.server.DropBoxManagerInternal;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService.Injector;
import com.android.server.am.ApplicationExitInfoTest.ServiceThreadRule;
import com.android.server.appop.AppOpsService;
import com.android.server.wm.ActivityTaskManagerService;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.io.File;

/**
 * Test class for the service timeout.
 *
 * Build/Install/Run:
 *  atest ServiceTimeoutTest
 */
@Presubmit
public final class ServiceTimeoutTest {
    private static final String TAG = ServiceTimeoutTest.class.getSimpleName();
    private static final long DEFAULT_SERVICE_TIMEOUT = 2000;

    @Rule
    public final ServiceThreadRule mServiceThreadRule = new ServiceThreadRule();
    private Context mContext;
    private HandlerThread mHandlerThread;

    @Mock
    private AppOpsService mAppOpsService;
    @Mock
    private DropBoxManagerInternal mDropBoxManagerInt;
    @Mock
    private PackageManagerInternal mPackageManagerInt;
    @Mock
    private UsageStatsManagerInternal mUsageStatsManagerInt;

    private ActivityManagerService mAms;
    private ProcessList mProcessList;
    private ActiveServices mActiveServices;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);

        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();

        mHandlerThread = new HandlerThread(TAG);
        mHandlerThread.start();
        mProcessList = spy(new ProcessList());

        LocalServices.removeServiceForTest(DropBoxManagerInternal.class);
        LocalServices.addService(DropBoxManagerInternal.class, mDropBoxManagerInt);
        LocalServices.removeServiceForTest(PackageManagerInternal.class);
        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
        doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();

        final ActivityManagerService realAms = new ActivityManagerService(
                new TestInjector(mContext), mServiceThreadRule.getThread());
        realAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
        realAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
        realAms.mAtmInternal = spy(realAms.mActivityTaskManager.getAtmInternal());
        realAms.mOomAdjuster.mCachedAppOptimizer = spy(realAms.mOomAdjuster.mCachedAppOptimizer);
        realAms.mPackageManagerInt = mPackageManagerInt;
        realAms.mUsageStatsService = mUsageStatsManagerInt;
        realAms.mProcessesReady = true;
        realAms.mConstants.SERVICE_TIMEOUT = DEFAULT_SERVICE_TIMEOUT;
        realAms.mConstants.SERVICE_BACKGROUND_TIMEOUT = DEFAULT_SERVICE_TIMEOUT;
        mAms = spy(realAms);
    }

    @After
    public void tearDown() throws Exception {
        LocalServices.removeServiceForTest(DropBoxManagerInternal.class);
        LocalServices.removeServiceForTest(PackageManagerInternal.class);
        mHandlerThread.quit();
    }

    @SuppressWarnings("GuardedBy")
    @Test
    public void testServiceTimeoutAndProcessKill() throws Exception {
        final int pid = 12345;
        final int uid = 10123;
        final String name = "com.example.foo";
        final ProcessRecord app = makeProcessRecord(
                pid,                   // pid
                uid,                   // uid
                uid,                   // packageUid
                null,                  // definingUid
                0,                     // connectionGroup
                PROCESS_STATE_SERVICE, // procstate
                0,                     // pss
                0,                     // rss
                name,                  // processName
                name,                  // packageName
                mAms);
        app.makeActive(mock(IApplicationThread.class), mAms.mProcessStats);
        mProcessList.updateLruProcessLocked(app, false, null);

        final long now = SystemClock.uptimeMillis();
        final ServiceRecord sr = spy(ServiceRecord.newEmptyInstanceForTest(mAms));
        doNothing().when(sr).dump(any(), anyString());
        sr.startRequested = true;
        sr.executingStart = now;

        app.mServices.startExecutingService(sr);
        mActiveServices.scheduleServiceTimeoutLocked(app);

        verify(mActiveServices, timeout(DEFAULT_SERVICE_TIMEOUT * 2).times(1))
                .serviceTimeout(eq(app));

        clearInvocations(mActiveServices);

        app.mServices.startExecutingService(sr);
        mActiveServices.scheduleServiceTimeoutLocked(app);

        app.killLocked(TAG, 42, false);
        mAms.removeLruProcessLocked(app);

        verify(mActiveServices, after(DEFAULT_SERVICE_TIMEOUT * 4)
                .times(1)).serviceTimeout(eq(app));
    }

    private class TestInjector extends Injector {
        TestInjector(Context context) {
            super(context);
        }

        @Override
        public AppOpsService getAppOpsService(File recentAccessesFile, File storageFile,
                Handler handler) {
            return mAppOpsService;
        }

        @Override
        public Handler getUiHandler(ActivityManagerService service) {
            return mHandlerThread.getThreadHandler();
        }

        @Override
        public ProcessList getProcessList(ActivityManagerService service) {
            return mProcessList;
        }

        @Override
        public ActiveServices getActiveServices(ActivityManagerService service) {
            if (mActiveServices == null) {
                mActiveServices = spy(new ActiveServices(service));
            }
            return mActiveServices;
        }
    }
}