diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 97cc756e3924f..a1f2ac630c04e 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -5121,6 +5121,7 @@ public class ActivityManagerService extends IActivityManager.Stub String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) { synchronized (ActivityManagerService.this) { + mOomAdjuster.mAppCompact.compactAllSystem(); requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false); } } @@ -8614,6 +8615,10 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (this) { final long now = SystemClock.uptimeMillis(); final long timeSinceLastIdle = now - mLastIdleTime; + + // Compact all non-zygote processes to freshen up the page cache. + mOomAdjuster.mAppCompact.compactAllSystem(); + final long lowRamSinceLastIdle = getLowRamTimeSinceIdle(now); mLastIdleTime = now; mLowRamTimeSinceLastIdle = 0; diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/AppCompactor.java index 9216343e05d8d..f58fb95b1ec9a 100644 --- a/services/core/java/com/android/server/am/AppCompactor.java +++ b/services/core/java/com/android/server/am/AppCompactor.java @@ -51,6 +51,8 @@ public final class AppCompactor { @VisibleForTesting static final String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2"; @VisibleForTesting static final String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3"; @VisibleForTesting static final String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4"; + @VisibleForTesting static final String KEY_COMPACT_THROTTLE_5 = "compact_throttle_5"; + @VisibleForTesting static final String KEY_COMPACT_THROTTLE_6 = "compact_throttle_6"; @VisibleForTesting static final String KEY_COMPACT_STATSD_SAMPLE_RATE = "compact_statsd_sample_rate"; @@ -73,6 +75,8 @@ public final class AppCompactor { @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_2 = 10_000; @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_3 = 500; @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_4 = 10_000; + @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_5 = 10 * 60 * 1000; + @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_6 = 10 * 60 * 1000; // The sampling rate to push app compaction events into statsd for upload. @VisibleForTesting static final float DEFAULT_STATSD_SAMPLE_RATE = 0.1f; @@ -85,7 +89,10 @@ public final class AppCompactor { // Handler constants. static final int COMPACT_PROCESS_SOME = 1; static final int COMPACT_PROCESS_FULL = 2; + static final int COMPACT_PROCESS_PERSISTENT = 3; + static final int COMPACT_PROCESS_BFGS = 4; static final int COMPACT_PROCESS_MSG = 1; + static final int COMPACT_SYSTEM_MSG = 2; /** * This thread must be moved to the system background cpuset. @@ -142,6 +149,10 @@ public final class AppCompactor { @GuardedBy("mPhenotypeFlagLock") @VisibleForTesting volatile long mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4; @GuardedBy("mPhenotypeFlagLock") + @VisibleForTesting volatile long mCompactThrottleBFGS = DEFAULT_COMPACT_THROTTLE_5; + @GuardedBy("mPhenotypeFlagLock") + @VisibleForTesting volatile long mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6; + @GuardedBy("mPhenotypeFlagLock") private volatile boolean mUseCompaction = DEFAULT_USE_COMPACTION; private final Random mRandom = new Random(); @@ -224,6 +235,46 @@ public final class AppCompactor { } + @GuardedBy("mAm") + void compactAppPersistent(ProcessRecord app) { + app.reqCompactAction = COMPACT_PROCESS_PERSISTENT; + mPendingCompactionProcesses.add(app); + mCompactionHandler.sendMessage( + mCompactionHandler.obtainMessage( + COMPACT_PROCESS_MSG, app.curAdj, app.setProcState)); + } + + @GuardedBy("mAm") + boolean shouldCompactPersistent(ProcessRecord app, long now) { + return (app.lastCompactTime == 0 + || (now - app.lastCompactTime) > mCompactThrottlePersistent); + } + + @GuardedBy("mAm") + void compactAppBfgs(ProcessRecord app) { + app.reqCompactAction = COMPACT_PROCESS_BFGS; + mPendingCompactionProcesses.add(app); + mCompactionHandler.sendMessage( + mCompactionHandler.obtainMessage( + COMPACT_PROCESS_MSG, app.curAdj, app.setProcState)); + } + + @GuardedBy("mAm") + boolean shouldCompactBFGS(ProcessRecord app, long now) { + return (app.lastCompactTime == 0 + || (now - app.lastCompactTime) > mCompactThrottleBFGS); + } + + @GuardedBy("mAm") + void compactAllSystem() { + if (mUseCompaction) { + mCompactionHandler.sendMessage(mCompactionHandler.obtainMessage( + COMPACT_SYSTEM_MSG)); + } + } + + private native void compactSystem(); + /** * Reads the flag value from DeviceConfig to determine whether app compaction * should be enabled, and starts/stops the compaction thread as needed. @@ -265,10 +316,18 @@ public final class AppCompactor { String throttleFullFullFlag = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_THROTTLE_4); + String throttleBFGSFlag = + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_COMPACT_THROTTLE_5); + String throttlePersistentFlag = + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_COMPACT_THROTTLE_6); if (TextUtils.isEmpty(throttleSomeSomeFlag) || TextUtils.isEmpty(throttleSomeFullFlag) || TextUtils.isEmpty(throttleFullSomeFlag) - || TextUtils.isEmpty(throttleFullFullFlag)) { + || TextUtils.isEmpty(throttleFullFullFlag) + || TextUtils.isEmpty(throttleBFGSFlag) + || TextUtils.isEmpty(throttlePersistentFlag)) { // Set defaults for all if any are not set. useThrottleDefaults = true; } else { @@ -277,6 +336,8 @@ public final class AppCompactor { mCompactThrottleSomeFull = Integer.parseInt(throttleSomeFullFlag); mCompactThrottleFullSome = Integer.parseInt(throttleFullSomeFlag); mCompactThrottleFullFull = Integer.parseInt(throttleFullFullFlag); + mCompactThrottleBFGS = Integer.parseInt(throttleBFGSFlag); + mCompactThrottlePersistent = Integer.parseInt(throttlePersistentFlag); } catch (NumberFormatException e) { useThrottleDefaults = true; } @@ -287,6 +348,8 @@ public final class AppCompactor { mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2; mCompactThrottleFullSome = DEFAULT_COMPACT_THROTTLE_3; mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4; + mCompactThrottleBFGS = DEFAULT_COMPACT_THROTTLE_5; + mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6; } } @@ -332,14 +395,19 @@ public final class AppCompactor { synchronized (mAm) { proc = mPendingCompactionProcesses.remove(0); + pendingAction = proc.reqCompactAction; + // don't compact if the process has returned to perceptible - if (proc.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) { + // and this is only a cached/home/prev compaction + if ((pendingAction == COMPACT_PROCESS_SOME + || pendingAction == COMPACT_PROCESS_FULL) + && (proc.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ)) { return; } pid = proc.pid; name = proc.processName; - pendingAction = proc.reqCompactAction; + lastCompactAction = proc.lastCompactAction; lastCompactTime = proc.lastCompactTime; } @@ -356,31 +424,49 @@ public final class AppCompactor { // Note that we explicitly don't take mPhenotypeFlagLock here as the flags // should very seldom change, and taking the risk of using the wrong action is // preferable to taking the lock for every single compaction action. - if (pendingAction == COMPACT_PROCESS_SOME) { - if ((lastCompactAction == COMPACT_PROCESS_SOME - && (start - lastCompactTime < mCompactThrottleSomeSome)) - || (lastCompactAction == COMPACT_PROCESS_FULL - && (start - lastCompactTime - < mCompactThrottleSomeFull))) { - return; - } - } else { - if ((lastCompactAction == COMPACT_PROCESS_SOME - && (start - lastCompactTime < mCompactThrottleFullSome)) - || (lastCompactAction == COMPACT_PROCESS_FULL - && (start - lastCompactTime - < mCompactThrottleFullFull))) { - return; + if (lastCompactTime != 0) { + if (pendingAction == COMPACT_PROCESS_SOME) { + if ((lastCompactAction == COMPACT_PROCESS_SOME + && (start - lastCompactTime < mCompactThrottleSomeSome)) + || (lastCompactAction == COMPACT_PROCESS_FULL + && (start - lastCompactTime + < mCompactThrottleSomeFull))) { + return; + } + } else if (pendingAction == COMPACT_PROCESS_FULL) { + if ((lastCompactAction == COMPACT_PROCESS_SOME + && (start - lastCompactTime < mCompactThrottleFullSome)) + || (lastCompactAction == COMPACT_PROCESS_FULL + && (start - lastCompactTime + < mCompactThrottleFullFull))) { + return; + } + } else if (pendingAction == COMPACT_PROCESS_PERSISTENT) { + if (start - lastCompactTime < mCompactThrottlePersistent) { + return; + } + } else if (pendingAction == COMPACT_PROCESS_BFGS) { + if (start - lastCompactTime < mCompactThrottleBFGS) { + return; + } } } - - if (pendingAction == COMPACT_PROCESS_SOME) { - action = mCompactActionSome; - } else { - action = mCompactActionFull; + switch (pendingAction) { + case COMPACT_PROCESS_SOME: + action = mCompactActionSome; + break; + // For the time being, treat these as equivalent. + case COMPACT_PROCESS_FULL: + case COMPACT_PROCESS_PERSISTENT: + case COMPACT_PROCESS_BFGS: + action = mCompactActionFull; + break; + default: + action = COMPACT_ACTION_NONE; + break; } - if (action.equals(COMPACT_ACTION_NONE)) { + if (COMPACT_ACTION_NONE.equals(action)) { return; } @@ -422,6 +508,13 @@ public final class AppCompactor { // nothing to do, presumably the process died Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } + break; + } + case COMPACT_SYSTEM_MSG: { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "compactSystem"); + compactSystem(); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + break; } } } diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index aa03de1115b23..c1b9a20d143d6 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1686,9 +1686,10 @@ public final class OomAdjuster { int changes = 0; - if (app.curAdj != app.setAdj) { - // don't compact during bootup - if (mAppCompact.useCompaction() && mService.mBooted) { + // don't compact during bootup + if (mAppCompact.useCompaction() && mService.mBooted) { + // Cached and prev/home compaction + if (app.curAdj != app.setAdj) { // Perform a minor compaction when a perceptible app becomes the prev/home app // Perform a major compaction when any app enters cached // reminder: here, setAdj is previous state, curAdj is upcoming state @@ -1702,7 +1703,23 @@ public final class OomAdjuster { && app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ) { mAppCompact.compactAppFull(app); } + } else if (mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE + && app.setAdj < ProcessList.FOREGROUND_APP_ADJ + // Because these can fire independent of oom_adj/procstate changes, we need + // to throttle the actual dispatch of these requests in addition to the + // processing of the requests. As a result, there is throttling both here + // and in AppCompactor. + && mAppCompact.shouldCompactPersistent(app, now)) { + mAppCompact.compactAppPersistent(app); + } else if (mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE + && app.getCurProcState() + == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE + && mAppCompact.shouldCompactBFGS(app, now)) { + mAppCompact.compactAppBfgs(app); } + } + + if (app.curAdj != app.setAdj) { ProcessList.setOomAdj(app.pid, app.uid, app.curAdj); if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) { String msg = "Set " + app.pid + " " + app.processName + " adj " diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 00b815ae1bfa7..a7423ea40bf0d 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -50,6 +50,7 @@ cc_library_static { "com_android_server_VibratorService.cpp", "com_android_server_PersistentDataBlockService.cpp", "com_android_server_GraphicsStatsService.cpp", + "com_android_server_am_AppCompactor.cpp", "onload.cpp", ], diff --git a/services/core/jni/com_android_server_am_AppCompactor.cpp b/services/core/jni/com_android_server_am_AppCompactor.cpp new file mode 100644 index 0000000000000..de6aa8b3266be --- /dev/null +++ b/services/core/jni/com_android_server_am_AppCompactor.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "AppCompactor" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +using android::base::StringPrintf; +using android::base::WriteStringToFile; + +namespace android { + +// This performs per-process reclaim on all processes belonging to non-app UIDs. +// For the most part, these are non-zygote processes like Treble HALs, but it +// also includes zygote-derived processes that run in system UIDs, like bluetooth +// or potentially some mainline modules. The only process that should definitely +// not be compacted is system_server, since compacting system_server around the +// time of BOOT_COMPLETE could result in perceptible issues. +static void com_android_server_am_AppCompactor_compactSystem(JNIEnv *, jobject) { + std::unique_ptr proc(opendir("/proc"), closedir); + struct dirent* current; + while ((current = readdir(proc.get()))) { + if (current->d_type != DT_DIR) { + continue; + } + + // don't compact system_server, rely on persistent compaction during screen off + // in order to avoid mmap_sem-related stalls + if (atoi(current->d_name) == getpid()) { + continue; + } + + std::string status_name = StringPrintf("/proc/%s/status", current->d_name); + struct stat status_info; + + if (stat(status_name.c_str(), &status_info) != 0) { + // must be some other directory that isn't a pid + continue; + } + + // android.os.Process.FIRST_APPLICATION_UID + if (status_info.st_uid >= 10000) { + continue; + } + + std::string reclaim_path = StringPrintf("/proc/%s/reclaim", current->d_name); + WriteStringToFile(std::string("all"), reclaim_path); + } +} + +static const JNINativeMethod sMethods[] = { + /* name, signature, funcPtr */ + {"compactSystem", "()V", (void*)com_android_server_am_AppCompactor_compactSystem}, +}; + +int register_android_server_am_AppCompactor(JNIEnv* env) +{ + return jniRegisterNativeMethods(env, "com/android/server/am/AppCompactor", + sMethods, NELEM(sMethods)); +} + +} diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index 5ffed03c86aee..2cfaebf4632e3 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -54,6 +54,7 @@ int register_android_server_GraphicsStatsService(JNIEnv* env); int register_android_hardware_display_DisplayViewport(JNIEnv* env); int register_android_server_net_NetworkStatsService(JNIEnv* env); int register_android_server_security_VerityUtils(JNIEnv* env); +int register_android_server_am_AppCompactor(JNIEnv* env); }; using namespace android; @@ -101,5 +102,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_hardware_display_DisplayViewport(env); register_android_server_net_NetworkStatsService(env); register_android_server_security_VerityUtils(env); + register_android_server_am_AppCompactor(env); return JNI_VERSION_1_4; }