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

Commit 1b62a825 authored by Jing Ji's avatar Jing Ji Committed by Android (Google) Code Review
Browse files

Merge "Handle the potential recursions within updateOomAdj" into sc-dev

parents 3b1ded17 64f88fa1
Loading
Loading
Loading
Loading
+121 −8
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ import static com.android.server.am.AppProfiler.TAG_PSS;
import static com.android.server.am.ProcessList.TAG_PROCESS_OBSERVERS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;

import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ApplicationExitInfo;
@@ -229,6 +230,22 @@ public final class OomAdjuster {
    private final ArrayDeque<ProcessRecord> mTmpQueue;
    private final ArraySet<ProcessRecord> mPendingProcessSet = new ArraySet<>();

    /**
     * Flag to mark if there is an ongoing oomAdjUpdate: potentially the oomAdjUpdate
     * could be called recursively because of the indirect calls during the update;
     * however the oomAdjUpdate itself doesn't support recursion - in this case we'd
     * have to queue up the new targets found during the update, and perform another
     * round of oomAdjUpdate at the end of last update.
     */
    @GuardedBy("mService")
    private boolean mOomAdjUpdateOngoing = false;

    /**
     * Flag to mark if there is a pending full oomAdjUpdate.
     */
    @GuardedBy("mService")
    private boolean mPendingFullOomAdjUpdate = false;

    private final PlatformCompatCache mPlatformCompatCache;

    private static class PlatformCompatCache {
@@ -439,6 +456,23 @@ public final class OomAdjuster {
        if (oomAdjAll && mConstants.OOMADJ_UPDATE_QUICK) {
            return updateOomAdjLSP(app, oomAdjReason);
        }
        if (checkAndEnqueueOomAdjTargetLocked(app)) {
            // Simply return true as there is an oomAdjUpdate ongoing
            return true;
        }
        try {
            mOomAdjUpdateOngoing = true;
            return performUpdateOomAdjLSP(app, oomAdjAll, oomAdjReason);
        } finally {
            // Kick off the handling of any pending targets enqueued during the above update
            mOomAdjUpdateOngoing = false;
            updateOomAdjPendingTargetsLocked(oomAdjReason);
        }
    }

    @GuardedBy({"mService", "mProcLock"})
    private boolean performUpdateOomAdjLSP(ProcessRecord app, boolean oomAdjAll,
            String oomAdjReason) {
        final ProcessRecord topApp = mService.getTopApp();
        final ProcessStateRecord state = app.mState;
        final boolean wasCached = state.isCached();
@@ -453,20 +487,21 @@ public final class OomAdjuster {
                ? state.getCurRawAdj() : ProcessList.UNKNOWN_ADJ;
        // Check if this process is in the pending list too, remove from pending list if so.
        mPendingProcessSet.remove(app);
        boolean success = updateOomAdjLSP(app, cachedAdj, topApp, false,
        boolean success = performUpdateOomAdjLSP(app, cachedAdj, topApp, false,
                SystemClock.uptimeMillis());
        if (oomAdjAll
                && (wasCached != state.isCached()
                    || state.getCurRawAdj() == ProcessList.UNKNOWN_ADJ)) {
            // Changed to/from cached state, so apps after it in the LRU
            // list may also be changed.
            updateOomAdjLSP(oomAdjReason);
            performUpdateOomAdjLSP(oomAdjReason);
        }

        return success;
    }

    @GuardedBy({"mService", "mProcLock"})
    private boolean updateOomAdjLSP(ProcessRecord app, int cachedAdj,
    private boolean performUpdateOomAdjLSP(ProcessRecord app, int cachedAdj,
            ProcessRecord TOP_APP, boolean doingAll, long now) {
        if (app.getThread() == null) {
            return false;
@@ -519,6 +554,22 @@ public final class OomAdjuster {

    @GuardedBy({"mService", "mProcLock"})
    private void updateOomAdjLSP(String oomAdjReason) {
        if (checkAndEnqueueOomAdjTargetLocked(null)) {
            // Simply return as there is an oomAdjUpdate ongoing
            return;
        }
        try {
            mOomAdjUpdateOngoing = true;
            performUpdateOomAdjLSP(oomAdjReason);
        } finally {
            // Kick off the handling of any pending targets enqueued during the above update
            mOomAdjUpdateOngoing = false;
            updateOomAdjPendingTargetsLocked(oomAdjReason);
        }
    }

    @GuardedBy({"mService", "mProcLock"})
    private void performUpdateOomAdjLSP(String oomAdjReason) {
        final ProcessRecord topApp = mService.getTopApp();
        // Clear any pending ones because we are doing a full update now.
        mPendingProcessSet.clear();
@@ -548,6 +599,23 @@ public final class OomAdjuster {
            return true;
        }

        if (checkAndEnqueueOomAdjTargetLocked(app)) {
            // Simply return true as there is an oomAdjUpdate ongoing
            return true;
        }

        try {
            mOomAdjUpdateOngoing = true;
            return performUpdateOomAdjLSP(app, oomAdjReason);
        } finally {
            // Kick off the handling of any pending targets enqueued during the above update
            mOomAdjUpdateOngoing = false;
            updateOomAdjPendingTargetsLocked(oomAdjReason);
        }
    }

    @GuardedBy({"mService", "mProcLock"})
    private boolean performUpdateOomAdjLSP(ProcessRecord app, String oomAdjReason) {
        final ProcessRecord topApp = mService.getTopApp();

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason);
@@ -567,7 +635,7 @@ public final class OomAdjuster {
        state.resetCachedInfo();
        // Check if this process is in the pending list too, remove from pending list if so.
        mPendingProcessSet.remove(app);
        boolean success = updateOomAdjLSP(app, cachedAdj, topApp, false,
        boolean success = performUpdateOomAdjLSP(app, cachedAdj, topApp, false,
                SystemClock.uptimeMillis());
        if (!success || (wasCached == state.isCached() && oldAdj != ProcessList.INVALID_ADJ
                && wasBackground == ActivityManager.isProcStateBackground(
@@ -695,15 +763,60 @@ public final class OomAdjuster {
        }
    }

    /**
     * Check if there is an ongoing oomAdjUpdate, enqueue the given process record
     * to {@link #mPendingProcessSet} if there is one.
     *
     * @param app The target app to get an oomAdjUpdate, or a full oomAdjUpdate if it's null.
     * @return {@code true} if there is an ongoing oomAdjUpdate.
     */
    @GuardedBy("mService")
    private boolean checkAndEnqueueOomAdjTargetLocked(@Nullable ProcessRecord app) {
        if (!mOomAdjUpdateOngoing) {
            return false;
        }
        if (app != null) {
            mPendingProcessSet.add(app);
        } else {
            mPendingFullOomAdjUpdate = true;
        }
        return true;
    }

    /**
     * Kick off an oom adj update pass for the pending targets which are enqueued via
     * {@link #enqueueOomAdjTargetLocked}.
     */
    @GuardedBy("mService")
    void updateOomAdjPendingTargetsLocked(String oomAdjReason) {
        // First check if there is pending full update
        if (mPendingFullOomAdjUpdate) {
            mPendingFullOomAdjUpdate = false;
            mPendingProcessSet.clear();
            updateOomAdjLocked(oomAdjReason);
            return;
        }
        if (mPendingProcessSet.isEmpty()) {
            return;
        }

        if (mOomAdjUpdateOngoing) {
            // There's another oomAdjUpdate ongoing, return from here now;
            // that ongoing update would call us again at the end of it.
            return;
        }
        try {
            mOomAdjUpdateOngoing = true;
            performUpdateOomAdjPendingTargetsLocked(oomAdjReason);
        } finally {
            // Kick off the handling of any pending targets enqueued during the above update
            mOomAdjUpdateOngoing = false;
            updateOomAdjPendingTargetsLocked(oomAdjReason);
        }
    }

    @GuardedBy("mService")
    private void performUpdateOomAdjPendingTargetsLocked(String oomAdjReason) {
        final ProcessRecord topApp = mService.getTopApp();

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason);
@@ -1846,8 +1959,8 @@ public final class OomAdjuster {
                        computeOomAdjLSP(client, cachedAdj, topApp, doingAll, now,
                                cycleReEval, true);
                    } else {
                        cstate.setCurRawAdj(cstate.getSetAdj());
                        cstate.setCurRawProcState(cstate.getSetProcState());
                        cstate.setCurRawAdj(cstate.getCurAdj());
                        cstate.setCurRawProcState(cstate.getCurProcState());
                    }

                    int clientAdj = cstate.getCurRawAdj();
@@ -2130,8 +2243,8 @@ public final class OomAdjuster {
                if (computeClients) {
                    computeOomAdjLSP(client, cachedAdj, topApp, doingAll, now, cycleReEval, true);
                } else {
                    cstate.setCurRawAdj(cstate.getSetAdj());
                    cstate.setCurRawProcState(cstate.getSetProcState());
                    cstate.setCurRawAdj(cstate.getCurAdj());
                    cstate.setCurRawProcState(cstate.getCurProcState());
                }

                if (shouldSkipDueToCycle(state, cstate, procState, adj, cycleReEval)) {
+155 −7
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.AdditionalAnswers.answer;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.doAnswer;
@@ -73,6 +74,8 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;

import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
import android.content.ComponentName;
@@ -178,11 +181,20 @@ public class MockingOomAdjusterTests {
        setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler);
        setFieldValue(ActivityManagerService.class, sService, "mProcLock",
                new ActivityManagerProcLock());
        setFieldValue(ActivityManagerService.class, sService, "mServices",
                spy(new ActiveServices(sService)));
        setFieldValue(ActivityManagerService.class, sService, "mInternal",
                mock(ActivityManagerService.LocalService.class));
        setFieldValue(ActivityManagerService.class, sService, "mBatteryStatsService",
                mock(BatteryStatsService.class));
        doReturn(mock(AppOpsManager.class)).when(sService).getAppOpsManager();
        doCallRealMethod().when(sService).enqueueOomAdjTargetLocked(any(ProcessRecord.class));
        doCallRealMethod().when(sService).updateOomAdjPendingTargetsLocked(any(String.class));
        setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object());
        doReturn(new ActivityManagerService.ProcessChangeItem()).when(pr)
                .enqueueProcessChangeItemLocked(anyInt(), anyInt());
        sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList,
                mock(ActiveUids.class));
                new ActiveUids(sService, false));
        sService.mOomAdjuster.mAdjSeq = 10000;
        sService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE);
    }
@@ -1313,6 +1325,115 @@ public class MockingOomAdjusterTests {
                SCHED_GROUP_DEFAULT);
    }

    @SuppressWarnings("GuardedBy")
    @Test
    public void testUpdateOomAdj_UidIdle_StopService() {
        final ProcessRecord app1 = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
        final ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
        final ProcessRecord client1 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
        final ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP3_UID,
                MOCKAPP4_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
        final ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
                MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
        final UidRecord app1UidRecord = new UidRecord(MOCKAPP_UID, sService);
        final UidRecord app2UidRecord = new UidRecord(MOCKAPP2_UID, sService);
        final UidRecord app3UidRecord = new UidRecord(MOCKAPP5_UID, sService);
        final UidRecord clientUidRecord = new UidRecord(MOCKAPP3_UID, sService);
        app1.setUidRecord(app1UidRecord);
        app2.setUidRecord(app2UidRecord);
        app3.setUidRecord(app3UidRecord);
        client1.setUidRecord(clientUidRecord);
        client2.setUidRecord(clientUidRecord);

        client1.mServices.setHasForegroundServices(true, 0);
        client2.mState.setForcingToImportant(new Object());
        ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
        lru.clear();
        lru.add(app1);
        lru.add(app2);
        lru.add(app3);
        lru.add(client1);
        lru.add(client2);
        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);

        final ComponentName cn1 = ComponentName.unflattenFromString(
                MOCKAPP_PACKAGENAME + "/.TestService");
        final ServiceRecord s1 = bindService(app1, client1, null, 0, mock(IBinder.class));
        setFieldValue(ServiceRecord.class, s1, "name", cn1);
        s1.startRequested = true;

        final ComponentName cn2 = ComponentName.unflattenFromString(
                MOCKAPP2_PACKAGENAME + "/.TestService");
        final ServiceRecord s2 = bindService(app2, client2, null, 0, mock(IBinder.class));
        setFieldValue(ServiceRecord.class, s2, "name", cn2);
        s2.startRequested = true;

        final ComponentName cn3 = ComponentName.unflattenFromString(
                MOCKAPP5_PACKAGENAME + "/.TestService");
        final ServiceRecord s3 = bindService(app3, client1, null, 0, mock(IBinder.class));
        setFieldValue(ServiceRecord.class, s3, "name", cn3);
        s3.startRequested = true;

        final ComponentName cn4 = ComponentName.unflattenFromString(
                MOCKAPP3_PACKAGENAME + "/.TestService");
        final ServiceRecord c2s = makeServiceRecord(client2);
        setFieldValue(ServiceRecord.class, c2s, "name", cn4);
        c2s.startRequested = true;

        try {
            sService.mOomAdjuster.mActiveUids.put(MOCKAPP_UID, app1UidRecord);
            sService.mOomAdjuster.mActiveUids.put(MOCKAPP2_UID, app2UidRecord);
            sService.mOomAdjuster.mActiveUids.put(MOCKAPP5_UID, app3UidRecord);
            sService.mOomAdjuster.mActiveUids.put(MOCKAPP3_UID, clientUidRecord);

            setServiceMap(s1, MOCKAPP_UID, cn1);
            setServiceMap(s2, MOCKAPP2_UID, cn2);
            setServiceMap(s3, MOCKAPP5_UID, cn3);
            setServiceMap(c2s, MOCKAPP3_UID, cn4);
            app2UidRecord.setIdle(false);
            sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);

            assertProcStates(app1, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
                    SCHED_GROUP_DEFAULT);
            assertProcStates(app3, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
                    SCHED_GROUP_DEFAULT);
            assertProcStates(client1, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
                    SCHED_GROUP_DEFAULT);
            assertEquals(PROCESS_STATE_TRANSIENT_BACKGROUND, app2.mState.getSetProcState());
            assertEquals(PROCESS_STATE_TRANSIENT_BACKGROUND, client2.mState.getSetProcState());

            client1.mServices.setHasForegroundServices(false, 0);
            client2.mState.setForcingToImportant(null);
            app1UidRecord.reset();
            app2UidRecord.reset();
            app3UidRecord.reset();
            clientUidRecord.reset();
            app1UidRecord.setIdle(true);
            app2UidRecord.setIdle(true);
            app3UidRecord.setIdle(true);
            clientUidRecord.setIdle(true);
            doReturn(ActivityManager.APP_START_MODE_DELAYED).when(sService)
                    .getAppStartModeLOSP(anyInt(), any(String.class), anyInt(),
                    anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
            doNothing().when(sService.mServices)
                    .scheduleServiceTimeoutLocked(any(ProcessRecord.class));
            sService.mOomAdjuster.updateOomAdjLocked(client1, OomAdjuster.OOM_ADJ_REASON_NONE);

            assertEquals(PROCESS_STATE_CACHED_EMPTY, client1.mState.getSetProcState());
            assertEquals(PROCESS_STATE_SERVICE, app1.mState.getSetProcState());
            assertEquals(PROCESS_STATE_SERVICE, client2.mState.getSetProcState());
        } finally {
            doCallRealMethod().when(sService)
                    .getAppStartModeLOSP(anyInt(), any(String.class), anyInt(),
                    anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
            sService.mServices.mServiceMap.clear();
            sService.mOomAdjuster.mActiveUids.clear();
        }
    }

    @SuppressWarnings("GuardedBy")
    @Test
    public void testUpdateOomAdj_DoAll_Unbound() {
@@ -1815,15 +1936,42 @@ public class MockingOomAdjusterTests {
        return app;
    }

    private ServiceRecord bindService(ProcessRecord service, ProcessRecord client,
            ServiceRecord record, int bindFlags, IBinder binder) {
        if (record == null) {
            record = mock(ServiceRecord.class);
            record.app = service;
    private ServiceRecord makeServiceRecord(ProcessRecord app) {
        final ServiceRecord record = mock(ServiceRecord.class);
        record.app = app;
        setFieldValue(ServiceRecord.class, record, "connections",
                new ArrayMap<IBinder, ArrayList<ConnectionRecord>>());
            service.mServices.startService(record);
        doCallRealMethod().when(record).getConnections();
        setFieldValue(ServiceRecord.class, record, "packageName", app.info.packageName);
        app.mServices.startService(record);
        record.appInfo = app.info;
        setFieldValue(ServiceRecord.class, record, "bindings", new ArrayMap<>());
        setFieldValue(ServiceRecord.class, record, "pendingStarts", new ArrayList<>());
        return record;
    }

    private void setServiceMap(ServiceRecord s, int uid, ComponentName cn) {
        ActiveServices.ServiceMap serviceMap = sService.mServices.mServiceMap.get(
                UserHandle.getUserId(uid));
        if (serviceMap == null) {
            serviceMap = mock(ActiveServices.ServiceMap.class);
            setFieldValue(ActiveServices.ServiceMap.class, serviceMap, "mServicesByInstanceName",
                    new ArrayMap<>());
            setFieldValue(ActiveServices.ServiceMap.class, serviceMap, "mActiveForegroundApps",
                    new ArrayMap<>());
            setFieldValue(ActiveServices.ServiceMap.class, serviceMap, "mServicesByIntent",
                    new ArrayMap<>());
            setFieldValue(ActiveServices.ServiceMap.class, serviceMap, "mDelayedStartList",
                    new ArrayList<>());
            sService.mServices.mServiceMap.put(UserHandle.getUserId(uid), serviceMap);
        }
        serviceMap.mServicesByInstanceName.put(cn, s);
    }

    private ServiceRecord bindService(ProcessRecord service, ProcessRecord client,
            ServiceRecord record, int bindFlags, IBinder binder) {
        if (record == null) {
            record = makeServiceRecord(service);
        }
        AppBindRecord binding = new AppBindRecord(record, null, client);
        ConnectionRecord cr = spy(new ConnectionRecord(binding,