diff options
-rw-r--r-- | cmds/am/src/com/android/commands/am/Am.java | 19 | ||||
-rw-r--r-- | core/java/android/app/ActivityManagerNative.java | 17 | ||||
-rw-r--r-- | core/java/android/app/IActivityManager.java | 3 | ||||
-rw-r--r-- | core/java/android/content/Context.java | 21 | ||||
-rw-r--r-- | services/java/com/android/server/IdleMaintenanceService.java | 69 | ||||
-rw-r--r-- | services/java/com/android/server/SystemServer.java | 3 | ||||
-rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 351 | ||||
-rw-r--r-- | services/java/com/android/server/am/ProcessRecord.java | 14 |
8 files changed, 325 insertions, 172 deletions
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index d78572b..c18f542 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -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; diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 370db31..a727b07 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -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; } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index b2ae298..25c02df 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -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; } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 92a9c7c..860b12c 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -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 */ @@ -2353,17 +2347,6 @@ public abstract class Context { /** * 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. * * @see #getSystemService diff --git a/services/java/com/android/server/IdleMaintenanceService.java b/services/java/com/android/server/IdleMaintenanceService.java index 584d4bc..b0a1aca 100644 --- a/services/java/com/android/server/IdleMaintenanceService.java +++ b/services/java/com/android/server/IdleMaintenanceService.java @@ -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 diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index d38756f..ef50df7 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -217,8 +217,7 @@ class ServerThread { ServiceManager.addService("telephony.registry", telephonyRegistry); Slog.i(TAG, "Scheduling Policy"); - ServiceManager.addService(Context.SCHEDULING_POLICY_SERVICE, - new SchedulingPolicyService()); + ServiceManager.addService("scheduling_policy", new SchedulingPolicyService()); AttributeCache.init(context); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index e208f10..2bac96e 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -172,7 +172,6 @@ import android.view.WindowManager; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; -import java.io.BufferedReader; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; @@ -850,10 +849,39 @@ public final class ActivityManagerService extends ActivityManagerNative int mNewNumServiceProcs = 0; /** - * System monitoring: number of processes that died since the last - * N procs were started. + * Allow the current computed overall memory level of the system to go down? + * This is set to false when we are killing processes for reasons other than + * memory management, so that the now smaller process list will not be taken as + * an indication that memory is tighter. */ - int[] mProcDeaths = new int[20]; + boolean mAllowLowerMemLevel = false; + + /** + * The last computed memory level, for holding when we are in a state that + * processes are going away for other reasons. + */ + int mLastMemoryLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL; + + /** + * The last total number of process we have, to determine if changes actually look + * like a shrinking number of process due to lower RAM. + */ + int mLastNumProcesses; + + /** + * The uptime of the last time we performed idle maintenance. + */ + long mLastIdleTime = SystemClock.uptimeMillis(); + + /** + * Total time spent with RAM that has been added in the past since the last idle time. + */ + long mLowRamTimeSinceLastIdle = 0; + + /** + * If RAM is currently low, when that horrible situatin started. + */ + long mLowRamStartTime = 0; /** * This is set if we had to do a delayed dexopt of an app before launching @@ -978,17 +1006,18 @@ public final class ActivityManagerService extends ActivityManagerNative static final int CANCEL_HEAVY_NOTIFICATION_MSG = 25; static final int SHOW_STRICT_MODE_VIOLATION_MSG = 26; static final int CHECK_EXCESSIVE_WAKE_LOCKS_MSG = 27; - static final int CLEAR_DNS_CACHE = 28; - static final int UPDATE_HTTP_PROXY = 29; + static final int CLEAR_DNS_CACHE_MSG = 28; + static final int UPDATE_HTTP_PROXY_MSG = 29; static final int SHOW_COMPAT_MODE_DIALOG_MSG = 30; static final int DISPATCH_PROCESSES_CHANGED = 31; static final int DISPATCH_PROCESS_DIED = 32; - static final int REPORT_MEM_USAGE = 33; + static final int REPORT_MEM_USAGE_MSG = 33; static final int REPORT_USER_SWITCH_MSG = 34; static final int CONTINUE_USER_SWITCH_MSG = 35; static final int USER_SWITCH_TIMEOUT_MSG = 36; static final int IMMERSIVE_MODE_LOCK_MSG = 37; - static final int PERSIST_URI_GRANTS = 38; + static final int PERSIST_URI_GRANTS_MSG = 38; + static final int REQUEST_ALL_PSS_MSG = 39; static final int FIRST_ACTIVITY_STACK_MSG = 100; static final int FIRST_BROADCAST_QUEUE_MSG = 200; @@ -1169,7 +1198,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } } break; - case CLEAR_DNS_CACHE: { + case CLEAR_DNS_CACHE_MSG: { synchronized (ActivityManagerService.this) { for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) { ProcessRecord r = mLruProcesses.get(i); @@ -1183,7 +1212,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } } break; - case UPDATE_HTTP_PROXY: { + case UPDATE_HTTP_PROXY_MSG: { ProxyProperties proxy = (ProxyProperties)msg.obj; String host = ""; String port = ""; @@ -1369,7 +1398,7 @@ public final class ActivityManagerService extends ActivityManagerNative dispatchProcessDied(pid, uid); break; } - case REPORT_MEM_USAGE: { + case REPORT_MEM_USAGE_MSG: { final ArrayList<ProcessMemInfo> memInfos = (ArrayList<ProcessMemInfo>)msg.obj; Thread thread = new Thread() { @Override public void run() { @@ -1583,10 +1612,14 @@ public final class ActivityManagerService extends ActivityManagerNative } break; } - case PERSIST_URI_GRANTS: { + case PERSIST_URI_GRANTS_MSG: { writeGrantedUriPermissions(); break; } + case REQUEST_ALL_PSS_MSG: { + requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false); + break; + } } } }; @@ -1630,6 +1663,10 @@ public final class ActivityManagerService extends ActivityManagerNative num++; proc.lastPssTime = SystemClock.uptimeMillis(); proc.baseProcessTracker.addPss(pss, tmp[0], true); + if (proc.initialIdlePss == 0) { + proc.initialIdlePss = pss; + } + proc.lastPss = pss; } } } @@ -2250,8 +2287,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - final void updateLruProcessLocked(ProcessRecord app, - boolean oomAdj) { + final void updateLruProcessLocked(ProcessRecord app, boolean oomAdj) { mLruSeq++; updateLruProcessInternalLocked(app, 0); @@ -2417,9 +2453,6 @@ public final class ActivityManagerService extends ActivityManagerNative updateCpuStats(); - System.arraycopy(mProcDeaths, 0, mProcDeaths, 1, mProcDeaths.length-1); - mProcDeaths[0] = 0; - try { int uid = app.uid; @@ -3428,7 +3461,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } if (doReport) { - Message msg = mHandler.obtainMessage(REPORT_MEM_USAGE, memInfos); + Message msg = mHandler.obtainMessage(REPORT_MEM_USAGE_MSG, memInfos); mHandler.sendMessage(msg); } scheduleAppGcsLocked(); @@ -3438,8 +3471,6 @@ public final class ActivityManagerService extends ActivityManagerNative final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread) { - mProcDeaths[0]++; - BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); synchronized (stats) { stats.noteProcessDiedLocked(app.info.uid, pid); @@ -3448,17 +3479,27 @@ public final class ActivityManagerService extends ActivityManagerNative // Clean up already done if the process has been re-started. if (app.pid == pid && app.thread != null && app.thread.asBinder() == thread.asBinder()) { - if (!app.killedBackground) { + boolean doLowMem = app.instrumentationClass == null; + boolean doOomAdj = doLowMem; + if (!app.killedByAm) { Slog.i(TAG, "Process " + app.processName + " (pid " + pid + ") has died."); + mAllowLowerMemLevel = true; + } else { + // Note that we always want to do oom adj to update our state with the + // new number of procs. + mAllowLowerMemLevel = false; + doLowMem = false; } EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.userId, app.pid, app.processName); if (DEBUG_CLEANUP) Slog.v( TAG, "Dying app: " + app + ", pid: " + pid + ", thread: " + thread.asBinder()); - boolean doLowMem = app.instrumentationClass == null; handleAppDiedLocked(app, false, true); + if (doOomAdj) { + updateOomAdjLocked(); + } if (doLowMem) { doLowMemReportIfNeededLocked(app); } @@ -3791,10 +3832,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) { - Slog.w(TAG, "Killing " + app + ": background ANR"); - EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, - app.processName, app.setAdj, "background ANR"); - Process.killProcessQuiet(app.pid); + killUnneededProcessLocked(app, "background ANR"); return; } @@ -3979,6 +4017,7 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=0; i<N; i++) { removeProcessLocked(procs.get(i), false, true, "kill all background"); } + mAllowLowerMemLevel = true; updateOomAdjLocked(); doLowMemReportIfNeededLocked(null); } @@ -4478,10 +4517,9 @@ public final class ActivityManagerService extends ActivityManagerNative mPidsSelfLocked.remove(pid); mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); } - Slog.i(TAG, "Killing proc " + app.toShortString() + ": " + reason); + killUnneededProcessLocked(app, reason); handleAppDiedLocked(app, true, allowRestart); mLruProcesses.remove(app); - Process.killProcessQuiet(pid); if (app.persistent && !app.isolated) { if (!callerWillRestart) { @@ -4523,9 +4561,7 @@ public final class ActivityManagerService extends ActivityManagerNative checkAppInLaunchingProvidersLocked(app, true); // Take care of any services that are waiting for the process. mServices.processStartTimedOutLocked(app); - EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, pid, - app.processName, app.setAdj, "start timeout"); - Process.killProcessQuiet(pid); + killUnneededProcessLocked(app, "start timeout"); if (mBackupTarget != null && mBackupTarget.app.pid == pid) { Slog.w(TAG, "Unattached app died before backup, skipping"); try { @@ -5769,8 +5805,8 @@ public final class ActivityManagerService extends ActivityManagerNative pi.packageName, targetPkg, targetUid, uri); final boolean persistChanged = perm.grantModes(modeFlags, persist, owner); if (persistChanged) { - mHandler.removeMessages(PERSIST_URI_GRANTS); - mHandler.obtainMessage(PERSIST_URI_GRANTS).sendToTarget(); + mHandler.removeMessages(PERSIST_URI_GRANTS_MSG); + mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG).sendToTarget(); } } @@ -5994,8 +6030,8 @@ public final class ActivityManagerService extends ActivityManagerNative } if (persistChanged) { - mHandler.removeMessages(PERSIST_URI_GRANTS); - mHandler.obtainMessage(PERSIST_URI_GRANTS).sendToTarget(); + mHandler.removeMessages(PERSIST_URI_GRANTS_MSG); + mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG).sendToTarget(); } } @@ -6079,8 +6115,8 @@ public final class ActivityManagerService extends ActivityManagerNative } if (persistChanged) { - mHandler.removeMessages(PERSIST_URI_GRANTS); - mHandler.obtainMessage(PERSIST_URI_GRANTS).sendToTarget(); + mHandler.removeMessages(PERSIST_URI_GRANTS_MSG); + mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG).sendToTarget(); } } @@ -6508,6 +6544,16 @@ public final class ActivityManagerService extends ActivityManagerNative } } + private void killUnneededProcessLocked(ProcessRecord pr, String reason) { + if (!pr.killedByAm) { + Slog.i(TAG, "Killing " + pr.toShortString() + " (adj " + pr.setAdj + "): " + reason); + EventLog.writeEvent(EventLogTags.AM_KILL, pr.userId, pr.pid, + pr.processName, pr.setAdj, reason); + pr.killedByAm = true; + Process.killProcessQuiet(pr.pid); + } + } + private void cleanUpRemovedTaskLocked(TaskRecord tr, int flags) { mRecentTasks.remove(tr); mStackSupervisor.removeTask(tr); @@ -6546,11 +6592,7 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=0; i<procs.size(); i++) { ProcessRecord pr = procs.get(i); if (pr.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) { - Slog.i(TAG, "Killing " + pr.toShortString() + ": remove task"); - EventLog.writeEvent(EventLogTags.AM_KILL, pr.userId, pr.pid, - pr.processName, pr.setAdj, "remove task"); - pr.killedBackground = true; - Process.killProcessQuiet(pr.pid); + killUnneededProcessLocked(pr, "remove task"); } else { pr.waitingToKill = "remove task"; } @@ -8401,13 +8443,9 @@ public final class ActivityManagerService extends ActivityManagerNative continue; } int adj = proc.setAdj; - if (adj >= worstType && !proc.killedBackground) { - Slog.w(TAG, "Killing " + proc + " (adj " + adj + "): " + reason); - EventLog.writeEvent(EventLogTags.AM_KILL, proc.userId, proc.pid, - proc.processName, adj, reason); + if (adj >= worstType && !proc.killedByAm) { + killUnneededProcessLocked(proc, reason); killed = true; - proc.killedBackground = true; - Process.killProcessQuiet(pids[i]); } } } @@ -8449,13 +8487,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (proc == null) continue; final int adj = proc.setAdj; - if (adj > belowAdj && !proc.killedBackground) { - Slog.w(TAG, "Killing " + proc + " (adj " + adj + "): " + reason); - EventLog.writeEvent(EventLogTags.AM_KILL, proc.userId, - proc.pid, proc.processName, adj, reason); + if (adj > belowAdj && !proc.killedByAm) { + killUnneededProcessLocked(proc, reason); killed = true; - proc.killedBackground = true; - Process.killProcessQuiet(pid); } } } @@ -8533,6 +8567,61 @@ public final class ActivityManagerService extends ActivityManagerNative br.onReceive(mContext, intent); } + private long getLowRamTimeSinceIdle(long now) { + return mLowRamTimeSinceLastIdle + (mLowRamStartTime > 0 ? (now-mLowRamStartTime) : 0); + } + + @Override + public void performIdleMaintenance() { + if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires permission " + + android.Manifest.permission.SET_ACTIVITY_WATCHER); + } + + synchronized (this) { + final long now = SystemClock.uptimeMillis(); + final long timeSinceLastIdle = now - mLastIdleTime; + final long lowRamSinceLastIdle = getLowRamTimeSinceIdle(now); + mLastIdleTime = now; + mLowRamTimeSinceLastIdle = 0; + if (mLowRamStartTime != 0) { + mLowRamStartTime = now; + } + + StringBuilder sb = new StringBuilder(128); + sb.append("Idle maintenance over "); + TimeUtils.formatDuration(timeSinceLastIdle, sb); + sb.append(" low RAM for "); + TimeUtils.formatDuration(lowRamSinceLastIdle, sb); + Slog.i(TAG, sb.toString()); + + // If at least 1/3 of our time since the last idle period has been spent + // with RAM low, then we want to kill processes. + boolean doKilling = lowRamSinceLastIdle > (timeSinceLastIdle/3); + + for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) { + ProcessRecord proc = mLruProcesses.get(i); + if (proc.notCachedSinceIdle) { + if (proc.setProcState > ActivityManager.PROCESS_STATE_TOP + && proc.setProcState <= ActivityManager.PROCESS_STATE_SERVICE) { + if (doKilling && proc.initialIdlePss != 0 + && proc.lastPss > ((proc.initialIdlePss*3)/2)) { + killUnneededProcessLocked(proc, "idle maint (pss " + proc.lastPss + + " from " + proc.initialIdlePss + ")"); + } + } + } else if (proc.setProcState < ActivityManager.PROCESS_STATE_HOME) { + proc.notCachedSinceIdle = true; + proc.initialIdlePss = 0; + } + } + + mHandler.removeMessages(REQUEST_ALL_PSS_MSG); + mHandler.sendEmptyMessageDelayed(REQUEST_ALL_PSS_MSG, 2*60*1000); + } + } + public final void startRunning(String pkg, String cls, String action, String data) { synchronized(this) { @@ -8967,10 +9056,7 @@ public final class ActivityManagerService extends ActivityManagerNative } if (app.pid > 0 && app.pid != MY_PID) { handleAppCrashLocked(app); - Slog.i(ActivityManagerService.TAG, "Killing " + app + ": user's request"); - EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, - app.processName, app.setAdj, "user's request after error"); - Process.killProcessQuiet(app.pid); + killUnneededProcessLocked(app, "user request after error"); } } } @@ -10444,6 +10530,15 @@ public final class ActivityManagerService extends ActivityManagerNative + " mNumCachedHiddenProcs=" + mNumCachedHiddenProcs + " mNumServiceProcs=" + mNumServiceProcs + " mNewNumServiceProcs=" + mNewNumServiceProcs); + pw.println(" mAllowLowerMemLevel=" + mAllowLowerMemLevel + + " mLastMemoryLevel" + mLastMemoryLevel + + " mLastNumProcesses" + mLastNumProcesses); + long now = SystemClock.uptimeMillis(); + pw.print(" mLastIdleTime="); + TimeUtils.formatDuration(now, mLastIdleTime, pw); + pw.print(" mLowRamSinceLastIdle="); + TimeUtils.formatDuration(getLowRamTimeSinceIdle(now), pw); + pw.println(); } } @@ -11704,13 +11799,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (!capp.persistent && capp.thread != null && capp.pid != 0 && capp.pid != MY_PID) { - Slog.i(TAG, "Kill " + capp.processName - + " (pid " + capp.pid + "): provider " + cpr.info.name - + " in dying process " + (proc != null ? proc.processName : "??")); - EventLog.writeEvent(EventLogTags.AM_KILL, capp.userId, capp.pid, - capp.processName, capp.setAdj, "dying provider " - + cpr.name.toShortString()); - Process.killProcessQuiet(capp.pid); + killUnneededProcessLocked(capp, "depends on provider " + + cpr.name.flattenToShortString() + + " in dying proc " + (proc != null ? proc.processName : "??")); } } else if (capp.thread != null && conn.provider.provider != null) { try { @@ -12791,12 +12882,12 @@ public final class ActivityManagerService extends ActivityManagerNative } if (intent.ACTION_CLEAR_DNS_CACHE.equals(intent.getAction())) { - mHandler.sendEmptyMessage(CLEAR_DNS_CACHE); + mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG); } if (Proxy.PROXY_CHANGE_ACTION.equals(intent.getAction())) { ProxyProperties proxy = intent.getParcelableExtra("proxy"); - mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY, proxy)); + mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy)); } // Add to the sticky list if requested. @@ -14527,26 +14618,18 @@ public final class ActivityManagerService extends ActivityManagerNative stats.reportExcessiveWakeLocked(app.info.uid, app.processName, realtimeSince, wtimeUsed); } - Slog.w(TAG, "Excessive wake lock in " + app.processName - + " (pid " + app.pid + "): held " + wtimeUsed + killUnneededProcessLocked(app, "excessive wake held " + wtimeUsed + " during " + realtimeSince); - EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, - app.processName, app.setAdj, "excessive wake lock"); app.baseProcessTracker.reportExcessiveWake(app.pkgList); - Process.killProcessQuiet(app.pid); } else if (doCpuKills && uptimeSince > 0 && ((cputimeUsed*100)/uptimeSince) >= 50) { synchronized (stats) { stats.reportExcessiveCpuLocked(app.info.uid, app.processName, uptimeSince, cputimeUsed); } - Slog.w(TAG, "Excessive CPU in " + app.processName - + " (pid " + app.pid + "): used " + cputimeUsed + killUnneededProcessLocked(app, "excessive cpu " + cputimeUsed + " during " + uptimeSince); - EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, - app.processName, app.setAdj, "excessive cpu"); app.baseProcessTracker.reportExcessiveCpu(app.pkgList); - Process.killProcessQuiet(app.pid); } else { app.lastWakeTime = wtime; app.lastCpuTime = app.curCpuTime; @@ -14593,11 +14676,7 @@ public final class ActivityManagerService extends ActivityManagerNative + " to " + app.curSchedGroup); if (app.waitingToKill != null && app.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) { - Slog.i(TAG, "Killing " + app.toShortString() + ": " + app.waitingToKill); - EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, - app.processName, app.setAdj, app.waitingToKill); - app.killedBackground = true; - Process.killProcessQuiet(app.pid); + killUnneededProcessLocked(app, app.waitingToKill); success = false; } else { if (true) { @@ -14657,6 +14736,9 @@ public final class ActivityManagerService extends ActivityManagerNative "Proc state change of " + app.processName + " to " + app.curProcState); app.setProcState = app.curProcState; + if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) { + app.notCachedSinceIdle = false; + } if (!doingAll) { setProcessTrackerState(app, mProcessStats.getMemFactorLocked(), now); } else { @@ -14783,7 +14865,7 @@ public final class ActivityManagerService extends ActivityManagerNative int nextEmptyAdj = curEmptyAdj+2; for (int i=N-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); - if (!app.killedBackground && app.thread != null) { + if (!app.killedByAm && app.thread != null) { app.procStateChanged = false; final boolean wasKeeping = app.keeping; computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now); @@ -14858,34 +14940,19 @@ public final class ActivityManagerService extends ActivityManagerNative mNumCachedHiddenProcs++; numCached++; if (numCached > cachedProcessLimit) { - Slog.i(TAG, "No longer want " + app.processName - + " (pid " + app.pid + "): cached #" + numCached); - EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, - app.processName, app.setAdj, "too many background"); - app.killedBackground = true; - Process.killProcessQuiet(app.pid); + killUnneededProcessLocked(app, "cached #" + numCached); } break; case ActivityManager.PROCESS_STATE_CACHED_EMPTY: if (numEmpty > ProcessList.TRIM_EMPTY_APPS && app.lastActivityTime < oldTime) { - Slog.i(TAG, "No longer want " + app.processName - + " (pid " + app.pid + "): empty for " - + ((oldTime+ProcessList.MAX_EMPTY_TIME-app.lastActivityTime) - / 1000) + "s"); - EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, - app.processName, app.setAdj, "old background process"); - app.killedBackground = true; - Process.killProcessQuiet(app.pid); + killUnneededProcessLocked(app, "empty for " + + ((oldTime + ProcessList.MAX_EMPTY_TIME - app.lastActivityTime) + / 1000) + "s"); } else { numEmpty++; if (numEmpty > emptyProcessLimit) { - Slog.i(TAG, "No longer want " + app.processName - + " (pid " + app.pid + "): empty #" + numEmpty); - EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, - app.processName, app.setAdj, "too many background"); - app.killedBackground = true; - Process.killProcessQuiet(app.pid); + killUnneededProcessLocked(app, "empty #" + numEmpty); } } break; @@ -14901,16 +14968,11 @@ public final class ActivityManagerService extends ActivityManagerNative // definition not re-use the same process again, and it is // good to avoid having whatever code was running in them // left sitting around after no longer needed. - Slog.i(TAG, "Isolated process " + app.processName - + " (pid " + app.pid + ") no longer needed"); - EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, - app.processName, app.setAdj, "isolated not needed"); - app.killedBackground = true; - Process.killProcessQuiet(app.pid); + killUnneededProcessLocked(app, "isolated not needed"); } if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME - && !app.killedBackground) { + && !app.killedByAm) { numTrimming++; } } @@ -14924,31 +14986,60 @@ public final class ActivityManagerService extends ActivityManagerNative // are managing to keep around is less than half the maximum we desire; // if we are keeping a good number around, we'll let them use whatever // memory they want. - boolean allChanged; + final int numCachedAndEmpty = numCached + numEmpty; + int memFactor; if (numCached <= ProcessList.TRIM_CACHED_APPS && numEmpty <= ProcessList.TRIM_EMPTY_APPS) { - final int numCachedAndEmpty = numCached + numEmpty; - int factor = numTrimming/3; - int minFactor = 2; - if (mHomeProcess != null) minFactor++; - if (mPreviousProcess != null) minFactor++; - if (factor < minFactor) factor = minFactor; - int step = 0; - int fgTrimLevel; - int memFactor; if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) { - fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL; memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL; } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) { - fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW; memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW; } else { - fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE; memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE; } + } else { + memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL; + } + // We always allow the memory level to go up (better). We only allow it to go + // down if we are in a state where that is allowed, *and* the total number of processes + // has gone down since last time. + if (DEBUG_OOM_ADJ) Slog.d(TAG, "oom: memFactor=" + memFactor + " last=" + mLastMemoryLevel + + " allowLow=" + mAllowLowerMemLevel + " numProcs=" + mLruProcesses.size() + + " last=" + mLastNumProcesses); + if (memFactor > mLastMemoryLevel) { + if (!mAllowLowerMemLevel || mLruProcesses.size() >= mLastNumProcesses) { + memFactor = mLastMemoryLevel; + if (DEBUG_OOM_ADJ) Slog.d(TAG, "Keeping last mem factor!"); + } + } + mLastMemoryLevel = memFactor; + mLastNumProcesses = mLruProcesses.size(); + boolean allChanged = mProcessStats.setMemFactorLocked( + ProcessStats.ADJ_MEM_FACTOR_NORMAL, !mSleeping, now); + final int trackerMemFactor = mProcessStats.getMemFactorLocked(); + if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) { + if (mLowRamStartTime == 0) { + mLowRamStartTime = now; + } + int step = 0; + int fgTrimLevel; + switch (memFactor) { + case ProcessStats.ADJ_MEM_FACTOR_CRITICAL: + fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL; + break; + case ProcessStats.ADJ_MEM_FACTOR_LOW: + fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW; + break; + default: + fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE; + break; + } + int factor = numTrimming/3; + int minFactor = 2; + if (mHomeProcess != null) minFactor++; + if (mPreviousProcess != null) minFactor++; + if (factor < minFactor) factor = minFactor; int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE; - allChanged = mProcessStats.setMemFactorLocked(memFactor, !mSleeping, now); - final int trackerMemFactor = mProcessStats.getMemFactorLocked(); for (int i=N-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); if (allChanged || app.procStateChanged) { @@ -14956,7 +15047,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.procStateChanged = false; } if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME - && !app.killedBackground) { + && !app.killedByAm) { if (app.trimMemoryLevel < curLevel && app.thread != null) { try { if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG, @@ -15036,9 +15127,10 @@ public final class ActivityManagerService extends ActivityManagerNative } } } else { - allChanged = mProcessStats.setMemFactorLocked( - ProcessStats.ADJ_MEM_FACTOR_NORMAL, !mSleeping, now); - final int trackerMemFactor = mProcessStats.getMemFactorLocked(); + if (mLowRamStartTime != 0) { + mLowRamTimeSinceLastIdle += now - mLowRamStartTime; + mLowRamStartTime = 0; + } for (int i=N-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); if (allChanged || app.procStateChanged) { @@ -15107,6 +15199,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (app.pid > 0 && app.pid != MY_PID) { EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, app.processName, app.setAdj, "empty"); + app.killedByAm = true; Process.killProcessQuiet(app.pid); } else { try { diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 35e06b6..4fdacb3 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -66,6 +66,8 @@ final class ProcessRecord { long lastPssTime; // Last time we retrieved PSS data long nextPssTime; // Next time we want to request PSS data long lastStateTime; // Last time setProcState changed + long initialIdlePss; // Initial memory pss of process for idle maintenance. + long lastPss; // Last computed memory pss. int maxAdj; // Maximum OOM adjustment for this process int curRawAdj; // Current OOM unlimited adjustment for this process int setRawAdj; // Last set OOM unlimited adjustment for this process @@ -82,6 +84,7 @@ final class ProcessRecord { boolean serviceb; // Process currently is on the service B list boolean keeping; // Actively running code so don't kill due to that? boolean setIsForeground; // Running foreground UI when last set? + boolean notCachedSinceIdle; // Has this process not been in a cached state since last idle? boolean hasActivities; // Are there any activities running in this process? boolean hasClientActivities; // Are there any client services with activities? boolean hasStartedServices; // Are there any started services running in this process? @@ -92,7 +95,7 @@ final class ProcessRecord { boolean pendingUiClean; // Want to clean up resources from showing UI? boolean hasAboveClient; // Bound using BIND_ABOVE_CLIENT, so want to be lower boolean bad; // True if disabled in the bad process list - boolean killedBackground; // True when proc has been killed due to too many bg + boolean killedByAm; // True when proc has been killed by activity manager, not for RAM boolean procStateChanged; // Keep track of whether we changed 'setAdj'. String waitingToKill; // Process is waiting to be killed when in the bg; reason IBinder forcingToForeground;// Token that is forcing this process to be foreground @@ -216,6 +219,11 @@ final class ProcessRecord { pw.print(" keeping="); pw.print(keeping); pw.print(" cached="); pw.print(cached); pw.print(" empty="); pw.println(empty); + if (notCachedSinceIdle) { + pw.print(prefix); pw.print("notCachedSinceIdle="); pw.print(notCachedSinceIdle); + pw.print(" initialIdlePss="); pw.print(initialIdlePss); + pw.print(" lastPss="); pw.println(lastPss); + } pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj); pw.print(" curRaw="); pw.print(curRawAdj); pw.print(" setRaw="); pw.print(setRawAdj); @@ -280,8 +288,8 @@ final class ProcessRecord { pw.print(" lastLowMemory="); TimeUtils.formatDuration(lastLowMemory, now, pw); pw.print(" reportLowMemory="); pw.println(reportLowMemory); - if (killedBackground || waitingToKill != null) { - pw.print(prefix); pw.print("killedBackground="); pw.print(killedBackground); + if (killedByAm || waitingToKill != null) { + pw.print(prefix); pw.print("killedByAm="); pw.print(killedByAm); pw.print(" waitingToKill="); pw.println(waitingToKill); } if (debugging || crashing || crashDialog != null || notResponding |