diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java index a5d64b2c89dee..912c465b5c162 100644 --- a/services/java/com/android/server/am/ActiveServices.java +++ b/services/java/com/android/server/am/ActiveServices.java @@ -1015,7 +1015,7 @@ public final class ActiveServices { Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app); if (app != null && app.thread != null) { try { - app.addPackage(r.appInfo.packageName); + app.addPackage(r.appInfo.packageName, mAm.mProcessTracker); realStartServiceLocked(r, app); return null; } catch (RemoteException e) { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 38fe5e6a65cfb..2124095502f3f 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -817,10 +817,10 @@ public final class ActivityManagerService extends ActivityManagerNative int mNumNonCachedProcs = 0; /** - * Keep track of the number of cached procs, to balance oom adj + * Keep track of the number of cached hidden procs, to balance oom adj * distribution between those and empty procs. */ - int mNumCachedProcs = 0; + int mNumCachedHiddenProcs = 0; /** * Keep track of the number of service processes we last found, to @@ -2151,7 +2151,7 @@ public final class ActivityManagerService extends ActivityManagerNative // come up (we have a pid but not yet its thread), so keep it. if (DEBUG_PROCESSES) Slog.v(TAG, "App already running: " + app); // If this is a new package in the process, add the package to the list - app.addPackage(info.packageName); + app.addPackage(info.packageName, mProcessTracker); return app; } @@ -2206,7 +2206,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } else { // If this is a new package in the process, add the package to the list - app.addPackage(info.packageName); + app.addPackage(info.packageName, mProcessTracker); } // If the system is not ready yet, then hold off on starting this @@ -3039,10 +3039,8 @@ public final class ActivityManagerService extends ActivityManagerNative proc = p; break; } - for (String str : p.pkgList) { - if (str.equals(packageName)) { - proc = p; - } + if (p.pkgList.containsKey(packageName)) { + proc = p; } } } @@ -4014,7 +4012,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (userId != UserHandle.USER_ALL && app.userId != userId) { continue; } - if (!app.pkgList.contains(packageName)) { + if (!app.pkgList.containsKey(packageName)) { continue; } } @@ -6220,7 +6218,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (proc.userId != tr.userId) { continue; } - if (!proc.pkgList.contains(pkg)) { + if (!proc.pkgList.containsKey(pkg)) { continue; } procs.add(proc); @@ -6577,7 +6575,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (DEBUG_MU) Slog.v(TAG_MU, "generateApplicationProvidersLocked, cpi.uid = " + cpr.uid); app.pubProviders.put(cpi.name, cpr); - app.addPackage(cpi.applicationInfo.packageName); + app.addPackage(cpi.applicationInfo.packageName, mProcessTracker); ensurePackageDexOpt(cpi.applicationInfo.packageName); } } @@ -7316,7 +7314,7 @@ public final class ActivityManagerService extends ActivityManagerNative ps = stats.getProcessStatsLocked(info.uid, proc); } return new ProcessRecord(ps, thread, info, proc, uid, - mProcessTracker.getStateLocked(info.processName, info.uid)); + mProcessTracker.getProcessStateLocked(info.packageName, uid, proc)); } final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated) { @@ -8895,7 +8893,8 @@ public final class ActivityManagerService extends ActivityManagerNative int flags = process.info.flags; IPackageManager pm = AppGlobals.getPackageManager(); sb.append("Flags: 0x").append(Integer.toString(flags, 16)).append("\n"); - for (String pkg : process.pkgList) { + for (int ip=0; ip= ProcessList.FOREGROUND_APP_ADJ) { - app.tracker.setState(mProcessList.adjToTrackedState(app.setAdj), - SystemClock.uptimeMillis()); + app.setAdjChanged = true; + if (!doingAll) { + app.setProcessTrackerState(TOP_APP, mProcessTracker.getMemFactor(), + now, mProcessList); } } else { success = false; @@ -13997,7 +13997,7 @@ public final class ActivityManagerService extends ActivityManagerNative mAdjSeq++; boolean success = updateOomAdjLocked(app, app.cachedAdj, app.clientCachedAdj, - app.emptyAdj, TOP_APP, false); + app.emptyAdj, TOP_APP, false, SystemClock.uptimeMillis()); final boolean nowCached = app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ && app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ; if (nowCached != wasCached) { @@ -14011,7 +14011,8 @@ public final class ActivityManagerService extends ActivityManagerNative final void updateOomAdjLocked() { final ActivityRecord TOP_ACT = resumedAppLocked(); final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null; - final long oldTime = SystemClock.uptimeMillis() - ProcessList.MAX_EMPTY_TIME; + final long now = SystemClock.uptimeMillis(); + final long oldTime = now - ProcessList.MAX_EMPTY_TIME; if (false) { RuntimeException e = new RuntimeException(); @@ -14040,7 +14041,7 @@ public final class ActivityManagerService extends ActivityManagerNative // them. int numSlots = (ProcessList.CACHED_APP_MAX_ADJ - ProcessList.CACHED_APP_MIN_ADJ + 1) / 2; - int numEmptyProcs = mLruProcesses.size()- mNumNonCachedProcs - mNumCachedProcs; + int numEmptyProcs = mLruProcesses.size()- mNumNonCachedProcs - mNumCachedHiddenProcs; if (numEmptyProcs > cachedProcessLimit) { // If there are more empty processes than our limit on cached // processes, then use the cached process limit for the factor. @@ -14052,7 +14053,7 @@ public final class ActivityManagerService extends ActivityManagerNative } int emptyFactor = numEmptyProcs/numSlots; if (emptyFactor < 1) emptyFactor = 1; - int cachedFactor = (mNumCachedProcs > 0 ? mNumCachedProcs : 1)/numSlots; + int cachedFactor = (mNumCachedHiddenProcs > 0 ? mNumCachedHiddenProcs : 1)/numSlots; if (cachedFactor < 1) cachedFactor = 1; int stepCached = 0; int stepEmpty = 0; @@ -14061,7 +14062,7 @@ public final class ActivityManagerService extends ActivityManagerNative int numTrimming = 0; mNumNonCachedProcs = 0; - mNumCachedProcs = 0; + mNumCachedHiddenProcs = 0; // First update the OOM adjustment for each of the // application processes based on their current state. @@ -14071,16 +14072,20 @@ public final class ActivityManagerService extends ActivityManagerNative int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ; int nextEmptyAdj = curEmptyAdj+2; int curClientCachedAdj = curEmptyAdj; + boolean changed = false; while (i > 0) { i--; ProcessRecord app = mLruProcesses.get(i); //Slog.i(TAG, "OOM " + app + ": cur cached=" + curCachedAdj); - updateOomAdjLocked(app, curCachedAdj, curClientCachedAdj, curEmptyAdj, TOP_APP, true); + app.setAdjChanged = false; + updateOomAdjLocked(app, curCachedAdj, curClientCachedAdj, curEmptyAdj, TOP_APP, + true, now); + changed |= app.setAdjChanged; if (!app.killedBackground) { if (app.curRawAdj == curCachedAdj && app.hasActivities) { // This process was assigned as a cached process... step the // cached level. - mNumCachedProcs++; + mNumCachedHiddenProcs++; if (curCachedAdj != nextCachedAdj) { stepCached++; if (stepCached >= cachedFactor) { @@ -14188,6 +14193,7 @@ 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. + int memFactor = ProcessTracker.STATE_MEM_FACTOR_NORMAL_ADJ; if (numCached <= ProcessList.TRIM_CACHED_APPS && numEmpty <= ProcessList.TRIM_EMPTY_APPS) { final int numCachedAndEmpty = numCached + numEmpty; @@ -14201,10 +14207,13 @@ public final class ActivityManagerService extends ActivityManagerNative int fgTrimLevel; if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) { fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL; + memFactor = ProcessTracker.STATE_MEM_FACTOR_CRITIAL_ADJ; } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) { fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW; + memFactor = ProcessTracker.STATE_MEM_FACTOR_LOW_ADJ; } else { fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE; + memFactor = ProcessTracker.STATE_MEM_FACTOR_MODERATE_ADJ; } int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE; for (i=0; i=0; i--) { + ProcessRecord app = mLruProcesses.get(i); + if (allChanged || app.setAdjChanged) { + app.setProcessTrackerState(TOP_APP, memFactor, now, mProcessList); + } + } + } + + if (DEBUG_OOM_ADJ) { + Slog.d(TAG, "Did OOM ADJ in " + (SystemClock.uptimeMillis()-now) + "ms"); + } } final void trimApplications() { diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java index 6fe28f4c884ea..ba8de39f9b938 100644 --- a/services/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/java/com/android/server/am/ActivityStackSupervisor.java @@ -987,7 +987,7 @@ public final class ActivityStackSupervisor { if (app != null && app.thread != null) { try { - app.addPackage(r.info.packageName); + app.addPackage(r.info.packageName, mService.mProcessTracker); realStartActivityLocked(r, app, andResume, checkConfig); return; } catch (RemoteException e) { diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java index c7e6010cf445d..0e7513c926a9d 100644 --- a/services/java/com/android/server/am/BroadcastQueue.java +++ b/services/java/com/android/server/am/BroadcastQueue.java @@ -795,7 +795,7 @@ public final class BroadcastQueue { info.activityInfo.applicationInfo.uid); if (app != null && app.thread != null) { try { - app.addPackage(info.activityInfo.packageName); + app.addPackage(info.activityInfo.packageName, mService.mProcessTracker); processCurBroadcastLocked(r, app); return; } catch (RemoteException e) { diff --git a/services/java/com/android/server/am/CompatModePackages.java b/services/java/com/android/server/am/CompatModePackages.java index f56371c266394..59e678738444f 100644 --- a/services/java/com/android/server/am/CompatModePackages.java +++ b/services/java/com/android/server/am/CompatModePackages.java @@ -300,7 +300,7 @@ public final class CompatModePackages { // Tell all processes that loaded this package about the change. for (int i=mService.mLruProcesses.size()-1; i>=0; i--) { ProcessRecord app = mService.mLruProcesses.get(i); - if (!app.pkgList.contains(packageName)) { + if (!app.pkgList.containsKey(packageName)) { continue; } try { diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java index e937d3521ddc3..b5d783d341e86 100644 --- a/services/java/com/android/server/am/ProcessList.java +++ b/services/java/com/android/server/am/ProcessList.java @@ -244,7 +244,7 @@ final class ProcessList { } int adjToTrackedState(int adj) { - return mAdjToTrackedState[adj]; + return adj >= FOREGROUND_APP_ADJ ? mAdjToTrackedState[adj] : ProcessTracker.STATE_NOTHING; } private void writeFile(String path, String data) { diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 6cae67c07d730..cc0a5a3e4e18b 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -51,9 +51,10 @@ final class ProcessRecord { final int uid; // uid of process; may be different from 'info' if isolated final int userId; // user of process. final String processName; // name of the process - final ProcessTracker.ProcessState tracker; // tracking execution of process + final ProcessTracker.ProcessState baseProcessTracker; // List of packages running in the process - final HashSet pkgList = new HashSet(); + final ArrayMap pkgList + = new ArrayMap(); IApplicationThread thread; // the actual proc... may be null only if // 'persistent' is true (in which case we // are in the process of launching the app) @@ -87,6 +88,7 @@ final class ProcessRecord { 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 setAdjChanged; // 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 int adjSeq; // Sequence id for identifying oom_adj assignment cycles @@ -327,15 +329,15 @@ final class ProcessRecord { ProcessRecord(BatteryStatsImpl.Uid.Proc _batteryStats, IApplicationThread _thread, ApplicationInfo _info, String _processName, int _uid, - ProcessTracker.ProcessState _tracker) { + ProcessTracker.ProcessState tracker) { batteryStats = _batteryStats; info = _info; isolated = _info.uid != _uid; uid = _uid; userId = UserHandle.getUserId(_uid); processName = _processName; - tracker = _tracker; - pkgList.add(_info.packageName); + baseProcessTracker = tracker; + pkgList.put(_info.packageName, tracker); thread = _thread; maxAdj = ProcessList.CACHED_APP_MAX_ADJ; cachedAdj = clientCachedAdj = emptyAdj = ProcessList.CACHED_APP_MIN_ADJ; @@ -437,20 +439,33 @@ final class ProcessRecord { /* * Return true if package has been added false if not */ - public boolean addPackage(String pkg) { - if (!pkgList.contains(pkg)) { - pkgList.add(pkg); + public boolean addPackage(String pkg, ProcessTracker tracker) { + if (!pkgList.containsKey(pkg)) { + pkgList.put(pkg, tracker.getProcessStateLocked(pkg, info.uid, processName)); return true; } return false; } - + + public void setProcessTrackerState(ProcessRecord TOP_APP, int memFactor, long now, + ProcessList plist) { + int state = this == TOP_APP ? ProcessTracker.STATE_TOP + : plist.adjToTrackedState(setAdj); + for (int ip=pkgList.size()-1; ip>=0; ip--) { + pkgList.valueAt(ip).setState(state, memFactor, now); + } + } + /* * Delete all packages from list except the package indicated in info */ public void resetPackageList() { + long now = SystemClock.uptimeMillis(); + for (int i=0; i mProcesses = new ArrayMap(); + final ArrayMap mServices = new ArrayMap(); + final int mUid; + + public PackageState(int uid) { + mUid = uid; } } static final class State { - final ProcessMap mProcesses = new ProcessMap(); + final ProcessMap mPackages = new ProcessMap(); + final long[] mMemFactorDurations = new long[STATE_COUNT/STATE_MEM_FACTOR_MOD]; + int mMemFactor = STATE_NOTHING; + long mStartTime; } final State mState = new State(); @@ -67,44 +98,198 @@ public final class ProcessTracker { public ProcessTracker() { } - public ProcessState getStateLocked(String name, int uid) { - ProcessState ps = mState.mProcesses.get(name, uid); + private PackageState getPackageStateLocked(String packageName, int uid) { + PackageState as = mState.mPackages.get(packageName, uid); + if (as != null) { + return as; + } + as = new PackageState(uid); + mState.mPackages.put(packageName, uid, as); + return as; + } + + public ProcessState getProcessStateLocked(String packageName, int uid, String processName) { + final PackageState as = getPackageStateLocked(packageName, uid); + ProcessState ps = as.mProcesses.get(processName); if (ps != null) { return ps; } ps = new ProcessState(); - mState.mProcesses.put(name, uid, ps); + as.mProcesses.put(processName, ps); return ps; } + public ServiceState getServiceStateLocked(String packageName, int uid, String className) { + final PackageState as = getPackageStateLocked(packageName, uid); + ServiceState ss = as.mServices.get(className); + if (ss != null) { + return ss; + } + ss = new ServiceState(); + as.mServices.put(className, ss); + return ss; + } + + public boolean setMemFactor(int memFactor, boolean screenOn, long now) { + if (screenOn) { + memFactor += STATE_SCREEN_ON_MOD; + } + if (memFactor != mState.mMemFactor) { + if (mState.mMemFactor != STATE_NOTHING) { + mState.mMemFactorDurations[mState.mMemFactor/STATE_MEM_FACTOR_MOD] + += now - mState.mStartTime; + } + mState.mMemFactor = memFactor; + mState.mStartTime = now; + return true; + } + return false; + } + + public int getMemFactor() { + return mState.mMemFactor != STATE_NOTHING ? mState.mMemFactor : 0; + } + + private void printScreenLabel(PrintWriter pw, int offset) { + switch (offset) { + case STATE_NOTHING: + pw.print(" "); + break; + case STATE_SCREEN_OFF_ADJ: + pw.print("Screen Off / "); + break; + case STATE_SCREEN_ON_ADJ: + pw.print("Screen On / "); + break; + default: + pw.print("?????????? / "); + break; + } + } + + private void printMemLabel(PrintWriter pw, int offset) { + switch (offset) { + case STATE_NOTHING: + pw.print(" "); + break; + case STATE_MEM_FACTOR_NORMAL_ADJ: + pw.print("Norm / "); + break; + case STATE_MEM_FACTOR_MODERATE_ADJ: + pw.print("Mod / "); + break; + case STATE_MEM_FACTOR_LOW_ADJ: + pw.print("Low / "); + break; + case STATE_MEM_FACTOR_CRITIAL_ADJ: + pw.print("Crit / "); + break; + default: + pw.print("???? / "); + break; + } + } + public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) { final long now = SystemClock.uptimeMillis(); - ArrayMap> pmap = mState.mProcesses.getMap(); + ArrayMap> pmap = mState.mPackages.getMap(); + pw.println("Process Stats:"); for (int ip=0; ip procs = pmap.valueAt(ip); + SparseArray procs = pmap.valueAt(ip); for (int iu=0; iu= 0) { + time += now - svc.mStartedTime; + } + if (time != 0) { + pw.print(" Started: "); + TimeUtils.formatDuration(time, pw); pw.println(); + } } } } + pw.println(); + pw.println("Run time Stats:"); + long totalTime = 0; + int printedScreen = -1; + for (int iscreen=0; iscreen