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

Commit 35f72be5 authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Implement issue #10691359: Kill long-running processes

We now have the activity manager kill long-running processes
during idle maintanence.

This involved adding some more information to the activity manager
about the current memory state, so that it could know if it really
should bother killing anything.  While doing this, I also improved
how we determine when memory is getting low by better ignoring cases
where processes are going away for other reasons (such as now idle
maintenance).  We now won't raise our memory state if either a process
is going away because we wanted it gone for another reason or the
total number of processes is not decreasing.

The idle maintanence killing also uses new per-process information
about whether the process has ever gone into the cached state since
the last idle maintenance, and the initial pss and current pss size
over its run time.

Change-Id: Iceaa7ffb2ad2015c33a64133a72a272b56dbad53
parent af672998
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -100,6 +100,7 @@ public class Am extends BaseCommand {
                "       am monitor [--gdb <port>]\n" +
                "       am hang [--allow-restart]\n" +
                "       am restart\n" +
                "       am idle-maintenance\n" +
                "       am screen-compat [on|off] <PACKAGE>\n" +
                "       am to-uri [INTENT]\n" +
                "       am to-intent-uri [INTENT]\n" +
@@ -189,6 +190,8 @@ public class Am extends BaseCommand {
                "\n" +
                "am restart: restart the user-space system.\n" +
                "\n" +
                "am idle-maintenance: perform idle maintenance now.\n" +
                "\n" +
                "am screen-compat: control screen compatibility mode of <PACKAGE>.\n" +
                "\n" +
                "am to-uri: print the given Intent specification as a URI.\n" +
@@ -295,6 +298,8 @@ public class Am extends BaseCommand {
            runHang();
        } else if (op.equals("restart")) {
            runRestart();
        } else if (op.equals("idle-maintenance")) {
            runIdleMaintenance();
        } else if (op.equals("screen-compat")) {
            runScreenCompat();
        } else if (op.equals("to-uri")) {
@@ -1393,6 +1398,20 @@ public class Am extends BaseCommand {
        mAm.restart();
    }

    private void runIdleMaintenance() throws Exception {
        String opt;
        while ((opt=nextOption()) != null) {
            System.err.println("Error: Unknown option: " + opt);
            return;
        }

        System.out.println("Performing idle maintenance...");
        Intent intent = new Intent(
                "com.android.server.IdleMaintenanceService.action.FORCE_IDLE_MAINTENANCE");
        mAm.broadcastIntent(null, intent, null, null, 0, null, null, null,
                android.app.AppOpsManager.OP_NONE, true, false, UserHandle.USER_ALL);
    }

    private void runScreenCompat() throws Exception {
        String mode = nextArgRequired();
        boolean enabled;
+17 −0
Original line number Diff line number Diff line
@@ -1995,6 +1995,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
            reply.writeParcelableArray(uris, 0);
            return true;
        }

        case PERFORM_IDLE_MAINTENANCE_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            performIdleMaintenance();
            reply.writeNoException();
            return true;
        }
        }

        return super.onTransact(code, data, reply, flags);
@@ -4578,5 +4585,15 @@ class ActivityManagerProxy implements IActivityManager
        return uris;
    }

    public void performIdleMaintenance() throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        mRemote.transact(PERFORM_IDLE_MAINTENANCE_TRANSACTION, data, reply, 0);
        reply.readException();
        data.recycle();
        reply.recycle();
    }

    private IBinder mRemote;
}
+3 −0
Original line number Diff line number Diff line
@@ -403,6 +403,8 @@ public interface IActivityManager extends IInterface {
            String sourcePackage, String targetPackage, int modeFlags, int modeMask)
            throws RemoteException;

    public void performIdleMaintenance() throws RemoteException;

    /*
     * Private non-Binder interfaces
     */
@@ -685,4 +687,5 @@ public interface IActivityManager extends IInterface {
    int REPORT_ACTIVITY_FULLY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+176;
    int RESTART_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+177;
    int GET_GRANTED_URI_PERMISSIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+178;
    int PERFORM_IDLE_MAINTENANCE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+179;
}
+2 −19
Original line number Diff line number Diff line
@@ -2132,13 +2132,7 @@ public abstract class Context {
    public static final String UPDATE_LOCK_SERVICE = "updatelock";

    /**
     * Use with {@link #getSystemService} to retrieve a {@link
     * android.net.NetworkManagementService} for handling management of
     * system network services
     *
     * @hide
     * @see #getSystemService
     * @see android.net.NetworkManagementService
     * Constant for the internal network management service, not really a Context service.
     */
    public static final String NETWORKMANAGEMENT_SERVICE = "network_management";

@@ -2327,7 +2321,7 @@ public abstract class Context {
     * android.hardware.SerialManager} for access to serial ports.
     *
     * @see #getSystemService
     * @see android.harware.SerialManager
     * @see android.hardware.SerialManager
     *
     * @hide
     */
@@ -2351,17 +2345,6 @@ public abstract class Context {
     */
    public static final String DISPLAY_SERVICE = "display";

    /**
     * Use with {@link #getSystemService} to retrieve a
     * {@link android.os.SchedulingPolicyService} for managing scheduling policy.
     *
     * @see #getSystemService
     * @see android.os.SchedulingPolicyService
     *
     * @hide
     */
    public static final String SCHEDULING_POLICY_SERVICE = "scheduling_policy";

    /**
     * Use with {@link #getSystemService} to retrieve a
     * {@link android.os.UserManager} for managing users on devices that support multiple users.
+50 −19
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server;

import android.app.Activity;
import android.app.ActivityManagerNative;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -24,12 +25,13 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;

/**
 * This service observes the device state and when applicable sends
@@ -69,6 +71,9 @@ public class IdleMaintenanceService extends BroadcastReceiver {
    private static final String ACTION_UPDATE_IDLE_MAINTENANCE_STATE =
        "com.android.server.IdleMaintenanceService.action.UPDATE_IDLE_MAINTENANCE_STATE";

    private static final String ACTION_FORCE_IDLE_MAINTENANCE =
        "com.android.server.IdleMaintenanceService.action.FORCE_IDLE_MAINTENANCE";

    private static final Intent sIdleMaintenanceStartIntent;
    static {
        sIdleMaintenanceStartIntent = new Intent(Intent.ACTION_IDLE_MAINTENANCE_START);
@@ -115,10 +120,10 @@ public class IdleMaintenanceService extends BroadcastReceiver {
        mUpdateIdleMaintenanceStatePendingIntent = PendingIntent.getBroadcast(mContext, 0,
                intent, PendingIntent.FLAG_UPDATE_CURRENT);

        register(mContext.getMainLooper());
        register(mHandler);
    }

    public void register(Looper looper) {
    public void register(Handler handler) {
        IntentFilter intentFilter = new IntentFilter();

        // Alarm actions.
@@ -136,7 +141,12 @@ public class IdleMaintenanceService extends BroadcastReceiver {
        intentFilter.addAction(Intent.ACTION_DREAMING_STOPPED);

        mContext.registerReceiverAsUser(this, UserHandle.ALL,
                intentFilter, null, new Handler(looper));
                intentFilter, null, mHandler);

        intentFilter = new IntentFilter();
        intentFilter.addAction(ACTION_FORCE_IDLE_MAINTENANCE);
        mContext.registerReceiverAsUser(this, UserHandle.ALL,
                intentFilter, android.Manifest.permission.SET_ACTIVITY_WATCHER, mHandler);
    }

    private void scheduleUpdateIdleMaintenanceState(long delayMillis) {
@@ -149,7 +159,7 @@ public class IdleMaintenanceService extends BroadcastReceiver {
        mAlarmService.cancel(mUpdateIdleMaintenanceStatePendingIntent);
    }

    private void updateIdleMaintenanceState() {
    private void updateIdleMaintenanceState(boolean noisy) {
        if (mIdleMaintenanceStarted) {
            // Idle maintenance can be interrupted by user activity, or duration
            // time out, or low battery.
@@ -170,9 +180,9 @@ public class IdleMaintenanceService extends BroadcastReceiver {
                            getNextIdleMaintenanceIntervalStartFromNow());
                }
            }
        } else if (deviceStatePermitsIdleMaintenanceStart()
                && lastUserActivityPermitsIdleMaintenanceStart()
                && lastRunPermitsIdleMaintenanceStart()) {
        } else if (deviceStatePermitsIdleMaintenanceStart(noisy)
                && lastUserActivityPermitsIdleMaintenanceStart(noisy)
                && lastRunPermitsIdleMaintenanceStart(noisy)) {
            // Now that we started idle maintenance, we should schedule another
            // update for the moment when the idle maintenance times out.
            scheduleUpdateIdleMaintenanceState(MAX_IDLE_MAINTENANCE_DURATION);
@@ -182,8 +192,8 @@ public class IdleMaintenanceService extends BroadcastReceiver {
                    isBatteryCharging() ? 1 : 0);
            mLastIdleMaintenanceStartTimeMillis = SystemClock.elapsedRealtime();
            sendIdleMaintenanceStartIntent();
        } else if (lastUserActivityPermitsIdleMaintenanceStart()) {
             if (lastRunPermitsIdleMaintenanceStart()) {
        } else if (lastUserActivityPermitsIdleMaintenanceStart(noisy)) {
             if (lastRunPermitsIdleMaintenanceStart(noisy)) {
                // The user does not use the device and we did not run maintenance in more
                // than the min interval between runs, so schedule an update - maybe the
                // battery will be charged latter.
@@ -204,6 +214,10 @@ public class IdleMaintenanceService extends BroadcastReceiver {

    private void sendIdleMaintenanceStartIntent() {
        mWakeLock.acquire();
        try {
            ActivityManagerNative.getDefault().performIdleMaintenance();
        } catch (RemoteException e) {
        }
        mContext.sendOrderedBroadcastAsUser(sIdleMaintenanceStartIntent, UserHandle.ALL,
                null, this, mHandler, Activity.RESULT_OK, null, null);
    }
@@ -214,25 +228,37 @@ public class IdleMaintenanceService extends BroadcastReceiver {
                null, this, mHandler, Activity.RESULT_OK, null, null);
    }

    private boolean deviceStatePermitsIdleMaintenanceStart() {
    private boolean deviceStatePermitsIdleMaintenanceStart(boolean noisy) {
        final int minBatteryLevel = isBatteryCharging()
                ? MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING
                : MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING;
        return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
        boolean allowed = (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
                && mBatteryService.getBatteryLevel() > minBatteryLevel);
        if (!allowed && noisy) {
            Slog.i("IdleMaintenance", "Idle maintenance not allowed due to power");
        }
        return allowed;
    }

    private boolean lastUserActivityPermitsIdleMaintenanceStart() {
    private boolean lastUserActivityPermitsIdleMaintenanceStart(boolean noisy) {
        // The last time the user poked the device is above the threshold.
        return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
        boolean allowed = (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
                && SystemClock.elapsedRealtime() - mLastUserActivityElapsedTimeMillis
                    > MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
        if (!allowed && noisy) {
            Slog.i("IdleMaintenance", "Idle maintenance not allowed due to last user activity");
        }
        return allowed;
    }

    private boolean lastRunPermitsIdleMaintenanceStart() {
    private boolean lastRunPermitsIdleMaintenanceStart(boolean noisy) {
        // Enough time passed since the last maintenance run.
        return SystemClock.elapsedRealtime() - mLastIdleMaintenanceStartTimeMillis
        boolean allowed = SystemClock.elapsedRealtime() - mLastIdleMaintenanceStartTimeMillis
                > MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS;
        if (!allowed && noisy) {
            Slog.i("IdleMaintenance", "Idle maintenance not allowed due time since last");
        }
        return allowed;
    }

    private boolean lastUserActivityPermitsIdleMaintenanceRunning() {
@@ -266,7 +292,7 @@ public class IdleMaintenanceService extends BroadcastReceiver {
            // next release. The only client for this for now is internal an holds
            // a wake lock correctly.
            if (mIdleMaintenanceStarted) {
                updateIdleMaintenanceState();
                updateIdleMaintenanceState(false);
            }
        } else if (Intent.ACTION_SCREEN_ON.equals(action)
                || Intent.ACTION_DREAMING_STOPPED.equals(action)) {
@@ -276,7 +302,7 @@ public class IdleMaintenanceService extends BroadcastReceiver {
            unscheduleUpdateIdleMaintenanceState();
            // If the screen went on/stopped dreaming, we know the user is using the
            // device which means that idle maintenance should be stopped if running.
            updateIdleMaintenanceState();
            updateIdleMaintenanceState(false);
        } else if (Intent.ACTION_SCREEN_OFF.equals(action)
                || Intent.ACTION_DREAMING_STARTED.equals(action)) {
            mLastUserActivityElapsedTimeMillis = SystemClock.elapsedRealtime();
@@ -285,7 +311,12 @@ public class IdleMaintenanceService extends BroadcastReceiver {
            // this timeout elapses since the device may go to sleep by then.
            scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
        } else if (ACTION_UPDATE_IDLE_MAINTENANCE_STATE.equals(action)) {
            updateIdleMaintenanceState();
            updateIdleMaintenanceState(false);
        } else if (ACTION_FORCE_IDLE_MAINTENANCE.equals(action)) {
            long now = SystemClock.elapsedRealtime() - 1;
            mLastUserActivityElapsedTimeMillis = now - MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START;
            mLastIdleMaintenanceStartTimeMillis = now - MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS;
            updateIdleMaintenanceState(true);
        } else if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)
                || Intent.ACTION_IDLE_MAINTENANCE_END.equals(action)) {
            // We were holding a wake lock while broadcasting the idle maintenance
Loading