summaryrefslogtreecommitdiff
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/core/java/com/android/server/GestureLauncherService.java61
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java1
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java30
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java11
-rw-r--r--services/core/java/com/android/server/am/BroadcastConstants.java6
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java1273
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.md25
-rw-r--r--services/core/java/com/android/server/am/OomAdjusterModernImpl.java1125
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java5
-rw-r--r--services/core/java/com/android/server/am/ProcessServiceRecord.java44
-rw-r--r--services/core/java/com/android/server/am/ProcessStateRecord.java31
-rw-r--r--services/core/java/com/android/server/display/VirtualDisplayAdapter.java12
-rw-r--r--services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java5
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java6
-rw-r--r--services/midi/java/com/android/server/midi/MidiService.java5
-rw-r--r--services/permission/java/com/android/server/permission/access/immutable/IndexedList.kt6
-rw-r--r--services/permission/java/com/android/server/permission/access/immutable/IndexedListSet.kt6
-rw-r--r--services/permission/java/com/android/server/permission/access/immutable/IndexedMap.kt6
-rw-r--r--services/permission/java/com/android/server/permission/access/immutable/IndexedReferenceMap.kt10
-rw-r--r--services/permission/java/com/android/server/permission/access/immutable/IndexedSet.kt6
-rw-r--r--services/permission/java/com/android/server/permission/access/immutable/IntMap.kt6
-rw-r--r--services/permission/java/com/android/server/permission/access/immutable/IntReferenceMap.kt10
-rw-r--r--services/permission/java/com/android/server/permission/access/immutable/IntSet.kt6
-rw-r--r--services/permission/java/com/android/server/permission/access/immutable/MutableReference.kt29
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java14
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java8
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java101
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java64
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/utils/OWNERS1
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/utils/PackageUtilsTest.java128
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt270
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java24
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java16
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java2
35 files changed, 2669 insertions, 686 deletions
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index f19f7f212513..1741593ba671 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -19,12 +19,10 @@ package com.android.server;
import android.app.ActivityManager;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.Sensor;
@@ -40,8 +38,6 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
-import android.os.VibrationEffect;
-import android.os.Vibrator;
import android.provider.Settings;
import android.util.MutableBoolean;
import android.util.Slog;
@@ -113,19 +109,6 @@ public class GestureLauncherService extends SystemService {
*/
private static final int CAMERA_POWER_TAP_COUNT_THRESHOLD = 2;
- /** Action for starting emergency alerts on Wear OS. */
- private static final String WEAR_LAUNCH_EMERGENCY_ACTION =
- "com.android.systemui.action.LAUNCH_EMERGENCY";
-
- /** Action for starting emergency alerts in retail mode on Wear OS. */
- private static final String WEAR_LAUNCH_EMERGENCY_RETAIL_ACTION =
- "com.android.systemui.action.LAUNCH_EMERGENCY_RETAIL";
-
- /**
- * Boolean extra for distinguishing intents coming from power button gesture.
- */
- private static final String EXTRA_LAUNCH_EMERGENCY_VIA_GESTURE = "launch_emergency_via_gesture";
-
/** The listener that receives the gesture event. */
private final GestureEventListener mGestureListener = new GestureEventListener();
private final CameraLiftTriggerEventListener mCameraLiftTriggerListener =
@@ -198,7 +181,6 @@ public class GestureLauncherService extends SystemService {
private final UiEventLogger mUiEventLogger;
private boolean mHasFeatureWatch;
- private long mVibrateMilliSecondsForPanicGesture;
@VisibleForTesting
public enum GestureLauncherEvent implements UiEventLogger.UiEventEnum {
@@ -268,13 +250,6 @@ public class GestureLauncherService extends SystemService {
mHasFeatureWatch =
mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
- mVibrateMilliSecondsForPanicGesture =
- resources.getInteger(
- com.android
- .internal
- .R
- .integer
- .config_mashPressVibrateTimeOnPowerButton);
}
}
@@ -714,11 +689,6 @@ public class GestureLauncherService extends SystemService {
userSetupComplete));
}
- if (mHasFeatureWatch) {
- onEmergencyGestureDetectedOnWatch();
- return true;
- }
-
StatusBarManagerInternal service = LocalServices.getService(
StatusBarManagerInternal.class);
service.onEmergencyActionLaunchGestureDetected();
@@ -728,37 +698,6 @@ public class GestureLauncherService extends SystemService {
}
}
- private void onEmergencyGestureDetectedOnWatch() {
- Intent emergencyIntent =
- new Intent(
- isInRetailMode()
- ? WEAR_LAUNCH_EMERGENCY_RETAIL_ACTION
- : WEAR_LAUNCH_EMERGENCY_ACTION);
- PackageManager pm = mContext.getPackageManager();
- ResolveInfo resolveInfo = pm.resolveActivity(emergencyIntent, /*flags=*/0);
- if (resolveInfo == null) {
- Slog.w(TAG, "Couldn't find an app to process the emergency intent "
- + emergencyIntent.getAction());
- return;
- }
-
- Vibrator vibrator = mContext.getSystemService(Vibrator.class);
- vibrator.vibrate(VibrationEffect.createOneShot(mVibrateMilliSecondsForPanicGesture,
- VibrationEffect.DEFAULT_AMPLITUDE));
-
- emergencyIntent.setComponent(
- new ComponentName(
- resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name));
- emergencyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- emergencyIntent.putExtra(EXTRA_LAUNCH_EMERGENCY_VIA_GESTURE, true);
- mContext.startActivityAsUser(emergencyIntent, new UserHandle(mUserId));
- }
-
- private boolean isInRetailMode() {
- return Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.DEVICE_DEMO_MODE, 0) == 1;
- }
-
private boolean isUserSetupComplete() {
return Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index dc83125503a9..383bb2542199 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -6434,6 +6434,7 @@ public final class ActiveServices {
}
updateServiceConnectionActivitiesLocked(psr);
psr.removeAllConnections();
+ psr.removeAllSdkSandboxConnections();
psr.mAllowlistManager = false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index bef53c7f31ee..faf1900a6609 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -22,6 +22,7 @@ import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_NONE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;
import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY;
import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_TARGET_T_ONLY;
+import static com.android.server.am.BroadcastConstants.getDeviceConfigBoolean;
import android.annotation.NonNull;
import android.app.ActivityThread;
@@ -153,6 +154,11 @@ final class ActivityManagerConstants extends ContentObserver {
static final String KEY_TIERED_CACHED_ADJ_DECAY_TIME = "tiered_cached_adj_decay_time";
static final String KEY_USE_MODERN_TRIM = "use_modern_trim";
+ /**
+ * Whether or not to enable the new oom adjuster implementation.
+ */
+ static final String KEY_ENABLE_NEW_OOMADJ = "enable_new_oom_adj";
+
private static final int DEFAULT_MAX_CACHED_PROCESSES = 1024;
private static final boolean DEFAULT_PRIORITIZE_ALARM_BROADCASTS = true;
private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000;
@@ -216,6 +222,11 @@ final class ActivityManagerConstants extends ContentObserver {
private static final boolean DEFAULT_USE_MODERN_TRIM = true;
/**
+ * The default value to {@link #KEY_ENABLE_NEW_OOMADJ}.
+ */
+ private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = false;
+
+ /**
* Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
*/
private static final int
@@ -1051,6 +1062,9 @@ final class ActivityManagerConstants extends ContentObserver {
/** @see #KEY_USE_MODERN_TRIM */
public boolean USE_MODERN_TRIM = DEFAULT_USE_MODERN_TRIM;
+ /** @see #KEY_ENABLE_NEW_OOMADJ */
+ public boolean ENABLE_NEW_OOMADJ = DEFAULT_ENABLE_NEW_OOM_ADJ;
+
private final OnPropertiesChangedListener mOnDeviceConfigChangedListener =
new OnPropertiesChangedListener() {
@Override
@@ -1308,6 +1322,7 @@ final class ActivityManagerConstants extends ContentObserver {
CUR_TRIM_CACHED_PROCESSES = (Integer.min(CUR_MAX_CACHED_PROCESSES, MAX_CACHED_PROCESSES)
- rawMaxEmptyProcesses) / 3;
+ loadNativeBootDeviceConfigConstants();
}
public void start(ContentResolver resolver) {
@@ -1347,6 +1362,11 @@ final class ActivityManagerConstants extends ContentObserver {
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_COMPONENT_ALIAS));
}
+ private void loadNativeBootDeviceConfigConstants() {
+ ENABLE_NEW_OOMADJ = getDeviceConfigBoolean(KEY_ENABLE_NEW_OOMADJ,
+ DEFAULT_ENABLE_NEW_OOM_ADJ);
+ }
+
public void setOverrideMaxCachedProcesses(int value) {
mOverrideMaxCachedProcesses = value;
updateMaxCachedProcesses();
@@ -1997,6 +2017,13 @@ final class ActivityManagerConstants extends ContentObserver {
DEFAULT_USE_MODERN_TRIM);
}
+ private void updateEnableNewOomAdj() {
+ ENABLE_NEW_OOMADJ = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
+ KEY_ENABLE_NEW_OOMADJ,
+ DEFAULT_ENABLE_NEW_OOM_ADJ);
+ }
+
private void updateFGSPermissionEnforcementFlagsIfNecessary(@NonNull String name) {
ForegroundServiceTypePolicy.getDefaultPolicy()
.updatePermissionEnforcementFlagIfNecessary(name);
@@ -2187,6 +2214,9 @@ final class ActivityManagerConstants extends ContentObserver {
pw.print(" "); pw.print(KEY_TIERED_CACHED_ADJ_DECAY_TIME);
pw.print("="); pw.println(TIERED_CACHED_ADJ_DECAY_TIME);
+ pw.print(" "); pw.print(KEY_ENABLE_NEW_OOMADJ);
+ pw.print("="); pw.println(ENABLE_NEW_OOMADJ);
+
pw.println();
if (mOverrideMaxCachedProcesses >= 0) {
pw.print(" mOverrideMaxCachedProcesses="); pw.println(mOverrideMaxCachedProcesses);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a0fae26fcac1..c1f2f6731e43 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2029,6 +2029,7 @@ public class ActivityManagerService extends IActivityManager.Stub
app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
app.mProfile.addHostingComponentType(HOSTING_COMPONENT_TYPE_SYSTEM);
addPidLocked(app);
+ mOomAdjuster.onProcessBeginLocked(app);
updateLruProcessLocked(app, false, null);
updateOomAdjLocked(OOM_ADJ_REASON_SYSTEM_INIT);
}
@@ -2422,7 +2423,9 @@ public class ActivityManagerService extends IActivityManager.Stub
mProcessList.init(this, activeUids, mPlatformCompat);
mAppProfiler = new AppProfiler(this, BackgroundThread.getHandler().getLooper(), null);
mPhantomProcessList = new PhantomProcessList(this);
- mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids, handlerThread);
+ mOomAdjuster = mConstants.ENABLE_NEW_OOMADJ
+ ? new OomAdjusterModernImpl(this, mProcessList, activeUids, handlerThread)
+ : new OomAdjuster(this, mProcessList, activeUids, handlerThread);
mIntentFirewall = null;
mProcessStats = new ProcessStatsService(this, mContext.getCacheDir());
@@ -2483,7 +2486,9 @@ public class ActivityManagerService extends IActivityManager.Stub
mAppProfiler = new AppProfiler(this, BackgroundThread.getHandler().getLooper(),
new LowMemDetector(this));
mPhantomProcessList = new PhantomProcessList(this);
- mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids);
+ mOomAdjuster = mConstants.ENABLE_NEW_OOMADJ
+ ? new OomAdjusterModernImpl(this, mProcessList, activeUids)
+ : new OomAdjuster(this, mProcessList, activeUids);
// Broadcast policy parameters
final BroadcastConstants foreConstants = new BroadcastConstants(
@@ -4595,6 +4600,7 @@ public class ActivityManagerService extends IActivityManager.Stub
EventLogTags.writeAmProcBound(app.userId, pid, app.processName);
synchronized (mProcLock) {
+ mOomAdjuster.onProcessBeginLocked(app);
mOomAdjuster.setAttachingProcessStatesLSP(app);
clearProcessForegroundLocked(app);
app.setDebugging(false);
@@ -6980,6 +6986,7 @@ public class ActivityManagerService extends IActivityManager.Stub
sdkSandboxClientAppPackage,
new HostingRecord(HostingRecord.HOSTING_TYPE_ADDED_APPLICATION,
customProcess != null ? customProcess : info.processName));
+ mOomAdjuster.onProcessBeginLocked(app);
updateLruProcessLocked(app, false, null);
updateOomAdjLocked(app, OOM_ADJ_REASON_PROCESS_BEGIN);
}
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index 8c1fd516028e..2fff79b3fb26 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -373,7 +373,7 @@ public class BroadcastConstants {
* Return the {@link SystemProperty} name for the given key in our
* {@link DeviceConfig} namespace.
*/
- private @NonNull String propertyFor(@NonNull String key) {
+ private static @NonNull String propertyFor(@NonNull String key) {
return "persist.device_config." + NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT + "." + key;
}
@@ -382,11 +382,11 @@ public class BroadcastConstants {
* {@link DeviceConfig} namespace, but with a different prefix that can be
* used to locally override the {@link DeviceConfig} value.
*/
- private @NonNull String propertyOverrideFor(@NonNull String key) {
+ private static @NonNull String propertyOverrideFor(@NonNull String key) {
return "persist.sys." + NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT + "." + key;
}
- private boolean getDeviceConfigBoolean(@NonNull String key, boolean def) {
+ static boolean getDeviceConfigBoolean(@NonNull String key, boolean def) {
return SystemProperties.getBoolean(propertyOverrideFor(key),
SystemProperties.getBoolean(propertyFor(key), def));
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 459c6ff3504a..1f9e89e30782 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -41,6 +41,7 @@ import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI;
import static android.app.ActivityManager.PROCESS_STATE_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
+import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_ACTIVITY;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_ALLOWLIST;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_BACKUP;
@@ -124,6 +125,7 @@ import static com.android.server.am.ProcessList.UNKNOWN_ADJ;
import static com.android.server.am.ProcessList.VISIBLE_APP_ADJ;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal.OomAdjReason;
@@ -369,20 +371,21 @@ public class OomAdjuster {
*/
private final Handler mProcessGroupHandler;
- private final int[] mTmpSchedGroup = new int[1];
+ protected final int[] mTmpSchedGroup = new int[1];
- private final ActivityManagerService mService;
- private final ProcessList mProcessList;
- private final ActivityManagerGlobalLock mProcLock;
+ final ActivityManagerService mService;
+ final ProcessList mProcessList;
+ final ActivityManagerGlobalLock mProcLock;
private final int mNumSlots;
- private final ArrayList<ProcessRecord> mTmpProcessList = new ArrayList<ProcessRecord>();
- private final ArrayList<UidRecord> mTmpBecameIdle = new ArrayList<UidRecord>();
- private final ActiveUids mTmpUidRecords;
- private final ArrayDeque<ProcessRecord> mTmpQueue;
- private final ArraySet<ProcessRecord> mTmpProcessSet = new ArraySet<>();
- private final ArraySet<ProcessRecord> mPendingProcessSet = new ArraySet<>();
- private final ArraySet<ProcessRecord> mProcessesInCycle = new ArraySet<>();
+ protected final ArrayList<ProcessRecord> mTmpProcessList = new ArrayList<ProcessRecord>();
+ protected final ArrayList<ProcessRecord> mTmpProcessList2 = new ArrayList<ProcessRecord>();
+ protected final ArrayList<UidRecord> mTmpBecameIdle = new ArrayList<UidRecord>();
+ protected final ActiveUids mTmpUidRecords;
+ protected final ArrayDeque<ProcessRecord> mTmpQueue;
+ protected final ArraySet<ProcessRecord> mTmpProcessSet = new ArraySet<>();
+ protected final ArraySet<ProcessRecord> mPendingProcessSet = new ArraySet<>();
+ protected final ArraySet<ProcessRecord> mProcessesInCycle = new ArraySet<>();
/**
* Flag to mark if there is an ongoing oomAdjUpdate: potentially the oomAdjUpdate
@@ -412,7 +415,7 @@ public class OomAdjuster {
this(service, processList, activeUids, createAdjusterThread());
}
- private static ServiceThread createAdjusterThread() {
+ static ServiceThread createAdjusterThread() {
// The process group is usually critical to the response time of foreground app, so the
// setter should apply it as soon as possible.
final ServiceThread adjusterThread =
@@ -532,7 +535,7 @@ public class OomAdjuster {
mPendingProcessSet.remove(app);
mProcessesInCycle.clear();
- computeOomAdjLSP(app, cachedAdj, topApp, false, now, false, true);
+ computeOomAdjLSP(app, cachedAdj, topApp, false, now, false, true, oomAdjReason, true);
if (!mProcessesInCycle.isEmpty()) {
// We can't use the score here if there is a cycle, abort.
for (int i = mProcessesInCycle.size() - 1; i >= 0; i--) {
@@ -550,7 +553,7 @@ public class OomAdjuster {
&& (uidRec.getSetProcState() != uidRec.getCurProcState()
|| uidRec.getSetCapability() != uidRec.getCurCapability()
|| uidRec.isSetAllowListed() != uidRec.isCurAllowListed())) {
- ActiveUids uids = mTmpUidRecords;
+ final ActiveUids uids = mTmpUidRecords;
uids.clear();
uids.put(uidRec.getUid(), uidRec);
updateUidsLSP(uids, SystemClock.elapsedRealtime());
@@ -633,19 +636,20 @@ public class OomAdjuster {
}
@GuardedBy({"mService", "mProcLock"})
- private boolean performUpdateOomAdjLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) {
+ protected boolean performUpdateOomAdjLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) {
final ProcessRecord topApp = mService.getTopApp();
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
mService.mOomAdjProfiler.oomAdjStarted();
mAdjSeq++;
- // Firstly, try to see if the importance of itself gets changed
final ProcessStateRecord state = app.mState;
final boolean wasCached = state.isCached();
final int oldAdj = state.getCurRawAdj();
final int cachedAdj = oldAdj >= CACHED_APP_MIN_ADJ
? oldAdj : UNKNOWN_ADJ;
+
+ // Firstly, try to see if the importance of itself gets changed
final boolean wasBackground = ActivityManager.isProcStateBackground(
state.getSetProcState());
final int oldCap = state.getSetCapability();
@@ -693,8 +697,6 @@ public class OomAdjuster {
mPendingProcessSet.clear();
if (!containsCycle) {
- // Reset the flag
- state.setReachable(false);
// Remove this app from the return list because we've done the computation on it.
processes.remove(app);
}
@@ -718,8 +720,13 @@ public class OomAdjuster {
return true;
}
+ /**
+ * Collect the reachable processes from the given {@code apps}, the result will be
+ * returned in the given {@code processes}, which will include the processes from
+ * the given {@code apps}.
+ */
@GuardedBy("mService")
- private boolean collectReachableProcessesLocked(ArraySet<ProcessRecord> apps,
+ protected boolean collectReachableProcessesLocked(ArraySet<ProcessRecord> apps,
ArrayList<ProcessRecord> processes, ActiveUids uids) {
final ArrayDeque<ProcessRecord> queue = mTmpQueue;
queue.clear();
@@ -824,11 +831,15 @@ public class OomAdjuster {
if (size > 0) {
// Reverse the process list, since the updateOomAdjInnerLSP scans from the end of it.
for (int l = 0, r = size - 1; l < r; l++, r--) {
- ProcessRecord t = processes.get(l);
- processes.set(l, processes.get(r));
+ final ProcessRecord t = processes.get(l);
+ final ProcessRecord u = processes.get(r);
+ t.mState.setReachable(false);
+ u.mState.setReachable(false);
+ processes.set(l, u);
processes.set(r, t);
}
}
+
return containsCycle;
}
@@ -928,24 +939,18 @@ public class OomAdjuster {
* Update OomAdj for all processes within the given list (could be partial), or the whole LRU
* list if the given list is null; when it's partial update, each process's client proc won't
* get evaluated recursively here.
+ *
+ * <p>Note: If the given {@code processes} is not null, the expectation to it is, the caller
+ * must have called {@link collectReachableProcessesLocked} on it.
*/
@GuardedBy({"mService", "mProcLock"})
- private void updateOomAdjInnerLSP(@OomAdjReason int oomAdjReason, final ProcessRecord topApp,
+ protected void updateOomAdjInnerLSP(@OomAdjReason int oomAdjReason, final ProcessRecord topApp,
ArrayList<ProcessRecord> processes, ActiveUids uids, boolean potentialCycles,
boolean startProfiling) {
- if (startProfiling) {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
- mService.mOomAdjProfiler.oomAdjStarted();
- }
- final long now = SystemClock.uptimeMillis();
- final long nowElapsed = SystemClock.elapsedRealtime();
- final long oldTime = now - mConstants.mMaxEmptyTimeMillis;
final boolean fullUpdate = processes == null;
+ final ArrayList<ProcessRecord> activeProcesses = fullUpdate
+ ? mProcessList.getLruProcessesLOSP() : processes;
ActiveUids activeUids = uids;
- ArrayList<ProcessRecord> activeProcesses = fullUpdate ? mProcessList.getLruProcessesLOSP()
- : processes;
- final int numProc = activeProcesses.size();
-
if (activeUids == null) {
final int numUids = mActiveUids.size();
activeUids = mTmpUidRecords;
@@ -956,14 +961,14 @@ public class OomAdjuster {
}
}
- // Reset state in all uid records.
- for (int i = activeUids.size() - 1; i >= 0; i--) {
- final UidRecord uidRec = activeUids.valueAt(i);
- if (DEBUG_UID_OBSERVERS) {
- Slog.i(TAG_UID_OBSERVERS, "Starting update of " + uidRec);
- }
- uidRec.reset();
+ if (startProfiling) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
+ mService.mOomAdjProfiler.oomAdjStarted();
}
+ final long now = SystemClock.uptimeMillis();
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ final long oldTime = now - mConstants.mMaxEmptyTimeMillis;
+ final int numProc = activeProcesses.size();
mAdjSeq++;
if (fullUpdate) {
@@ -971,6 +976,9 @@ public class OomAdjuster {
mNewNumAServiceProcs = 0;
}
+ // Reset state in all uid records.
+ resetUidRecordsLsp(activeUids);
+
boolean retryCycles = false;
boolean computeClients = fullUpdate || potentialCycles;
@@ -996,8 +1004,9 @@ public class OomAdjuster {
if (!app.isKilledByAm() && app.getThread() != null) {
state.setProcStateChanged(false);
app.mOptRecord.setLastOomAdjChangeReason(oomAdjReason);
+ // It won't enter cycle if not computing clients.
computeOomAdjLSP(app, UNKNOWN_ADJ, topApp, fullUpdate, now, false,
- computeClients); // It won't enter cycle if not computing clients.
+ computeClients, oomAdjReason, true);
// if any app encountered a cycle, we need to perform an additional loop later
retryCycles |= state.containsCycle();
// Keep the completedAdjSeq to up to date.
@@ -1034,7 +1043,7 @@ public class OomAdjuster {
final ProcessStateRecord state = app.mState;
if (!app.isKilledByAm() && app.getThread() != null && state.containsCycle()) {
if (computeOomAdjLSP(app, UNKNOWN_ADJ, topApp, true, now,
- true, true)) {
+ true, true, oomAdjReason, true)) {
retryCycles = true;
}
}
@@ -1045,10 +1054,33 @@ public class OomAdjuster {
assignCachedAdjIfNecessary(mProcessList.getLruProcessesLOSP());
+ postUpdateOomAdjInnerLSP(oomAdjReason, activeUids, now, nowElapsed, oldTime);
+
+ if (startProfiling) {
+ mService.mOomAdjProfiler.oomAdjEnded();
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ private void resetUidRecordsLsp(@NonNull ActiveUids activeUids) {
+ // Reset state in all uid records.
+ for (int i = activeUids.size() - 1; i >= 0; i--) {
+ final UidRecord uidRec = activeUids.valueAt(i);
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "Starting update of " + uidRec);
+ }
+ uidRec.reset();
+ }
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ protected void postUpdateOomAdjInnerLSP(@OomAdjReason int oomAdjReason, ActiveUids activeUids,
+ long now, long nowElapsed, long oldTime) {
mNumNonCachedProcs = 0;
mNumCachedHiddenProcs = 0;
- boolean allChanged = updateAndTrimProcessLSP(now, nowElapsed, oldTime, activeUids,
+ final boolean allChanged = updateAndTrimProcessLSP(now, nowElapsed, oldTime, activeUids,
oomAdjReason);
mNumServiceProcs = mNewNumServiceProcs;
@@ -1085,14 +1117,10 @@ public class OomAdjuster {
Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms");
}
}
- if (startProfiling) {
- mService.mOomAdjProfiler.oomAdjEnded();
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- }
}
@GuardedBy({"mService", "mProcLock"})
- private void assignCachedAdjIfNecessary(ArrayList<ProcessRecord> lruList) {
+ protected void assignCachedAdjIfNecessary(ArrayList<ProcessRecord> lruList) {
final int numLru = lruList.size();
if (mConstants.USE_TIERED_CACHED_ADJ) {
final long now = SystemClock.uptimeMillis();
@@ -1413,7 +1441,7 @@ public class OomAdjuster {
}
@GuardedBy({"mService", "mProcLock"})
- private void updateAppUidRecIfNecessaryLSP(final ProcessRecord app) {
+ protected void updateAppUidRecIfNecessaryLSP(final ProcessRecord app) {
if (!app.isKilledByAm() && app.getThread() != null) {
if (app.isolated && app.mServices.numberOfRunningServices() <= 0
&& app.getIsolatedEntryPoint() == null) {
@@ -1442,7 +1470,7 @@ public class OomAdjuster {
}
@GuardedBy({"mService", "mProcLock"})
- private void updateUidsLSP(ActiveUids activeUids, final long nowElapsed) {
+ protected void updateUidsLSP(ActiveUids activeUids, final long nowElapsed) {
// This compares previously set procstate to the current procstate in regards to whether
// or not the app's network access will be blocked. So, this needs to be called before
// we update the UidRecord's procstate by calling {@link UidRecord#setSetProcState}.
@@ -1580,7 +1608,7 @@ public class OomAdjuster {
return true;
}
- private final ComputeOomAdjWindowCallback mTmpComputeOomAdjWindowCallback =
+ protected final ComputeOomAdjWindowCallback mTmpComputeOomAdjWindowCallback =
new ComputeOomAdjWindowCallback();
/** These methods are called inline during computeOomAdjLSP(), on the same thread */
@@ -1719,24 +1747,30 @@ public class OomAdjuster {
}
@GuardedBy({"mService", "mProcLock"})
- private boolean computeOomAdjLSP(ProcessRecord app, int cachedAdj,
+ protected boolean computeOomAdjLSP(ProcessRecord app, int cachedAdj,
ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,
- boolean computeClients) {
+ boolean computeClients, int oomAdjReason, boolean couldRecurse) {
final ProcessStateRecord state = app.mState;
- if (mAdjSeq == state.getAdjSeq()) {
- if (state.getAdjSeq() == state.getCompletedAdjSeq()) {
- // This adjustment has already been computed successfully.
- return false;
- } else {
- // The process is being computed, so there is a cycle. We cannot
- // rely on this process's state.
- state.setContainsCycle(true);
- mProcessesInCycle.add(app);
+ if (couldRecurse) {
+ if (mAdjSeq == state.getAdjSeq()) {
+ if (state.getAdjSeq() == state.getCompletedAdjSeq()) {
+ // This adjustment has already been computed successfully.
+ return false;
+ } else {
+ // The process is being computed, so there is a cycle. We cannot
+ // rely on this process's state.
+ state.setContainsCycle(true);
+ mProcessesInCycle.add(app);
- return false;
+ return false;
+ }
}
}
+ int prevAppAdj = getInitialAdj(app);
+ int prevProcState = getInitialProcState(app);
+ int prevCapability = getInitialCapability(app);
+
if (app.getThread() == null) {
state.setAdjSeq(mAdjSeq);
state.setCurrentSchedulingGroup(SCHED_GROUP_BACKGROUND);
@@ -1745,6 +1779,8 @@ public class OomAdjuster {
state.setCurRawAdj(CACHED_APP_MAX_ADJ);
state.setCompletedAdjSeq(state.getAdjSeq());
state.setCurCapability(PROCESS_CAPABILITY_NONE);
+ onProcessStateChanged(app, prevProcState);
+ onProcessOomAdjChanged(app, prevAppAdj);
return false;
}
@@ -1753,7 +1789,7 @@ public class OomAdjuster {
state.setAdjTarget(null);
state.setEmpty(false);
state.setCached(false);
- if (!cycleReEval) {
+ if (!couldRecurse || !cycleReEval) {
// Don't reset this flag when doing cycles re-evaluation.
state.setNoKillOnBgRestrictedAndIdle(false);
// If this UID is currently allowlisted, it should not be frozen.
@@ -1764,9 +1800,6 @@ public class OomAdjuster {
final int appUid = app.info.uid;
final int logUid = mService.mCurOomAdjUid;
- int prevAppAdj = state.getCurAdj();
- int prevProcState = state.getCurProcState();
- int prevCapability = state.getCurCapability();
final ProcessServiceRecord psr = app.mServices;
if (state.getMaxAdj() <= FOREGROUND_APP_ADJ) {
@@ -1812,6 +1845,8 @@ public class OomAdjuster {
state.setCurRawProcState(state.getCurProcState());
state.setCurAdj(state.getMaxAdj());
state.setCompletedAdjSeq(state.getAdjSeq());
+ onProcessStateChanged(app, prevProcState);
+ onProcessOomAdjChanged(app, prevAppAdj);
// if curAdj is less than prevAppAdj, then this process was promoted
return state.getCurAdj() < prevAppAdj || state.getCurProcState() < prevProcState;
}
@@ -1825,7 +1860,7 @@ public class OomAdjuster {
int adj;
int schedGroup;
int procState;
- int capability = cycleReEval ? app.mState.getCurCapability() : 0;
+ int capability = cycleReEval ? getInitialCapability(app) : 0;
boolean foregroundActivities = false;
boolean hasVisibleActivities = false;
@@ -1904,7 +1939,7 @@ public class OomAdjuster {
// value that the caller wants us to.
adj = cachedAdj;
procState = PROCESS_STATE_CACHED_EMPTY;
- if (!state.containsCycle()) {
+ if (!couldRecurse || !state.containsCycle()) {
state.setCached(true);
state.setEmpty(true);
state.setAdjType("cch-empty");
@@ -2169,8 +2204,10 @@ public class OomAdjuster {
}
}
- boolean boundByNonBgRestricted = state.isCurBoundByNonBgRestrictedApp();
- boolean scheduleLikeTopApp = false;
+ state.setCurBoundByNonBgRestrictedApp(getInitialIsCurBoundByNonBgRestrictedApp(app));
+
+ state.setScheduleLikeTopApp(false);
+
for (int is = psr.numberOfRunningServices() - 1;
is >= 0 && (adj > FOREGROUND_APP_ADJ
|| schedGroup == SCHED_GROUP_BACKGROUND
@@ -2243,6 +2280,18 @@ public class OomAdjuster {
}
}
+ if (!couldRecurse) {
+ // We're entering recursive functions below, if we're told it's not a recursive
+ // loop, abort here.
+ continue;
+ }
+
+
+ state.setCurRawAdj(adj);
+ state.setCurRawProcState(procState);
+ state.setCurrentSchedulingGroup(schedGroup);
+ state.setCurCapability(capability);
+
ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections = s.getConnections();
for (int conni = serviceConnections.size() - 1;
conni >= 0 && (adj > FOREGROUND_APP_ADJ
@@ -2263,335 +2312,13 @@ public class OomAdjuster {
continue;
}
- boolean trackedProcState = false;
-
- ProcessRecord client = cr.binding.client;
- if (app.isSdkSandbox && cr.binding.attributedClient != null) {
- // For SDK sandboxes, use the attributed client (eg the app that
- // requested the sandbox)
- client = cr.binding.attributedClient;
- }
- final ProcessStateRecord cstate = client.mState;
- if (computeClients) {
- computeOomAdjLSP(client, cachedAdj, topApp, doingAll, now,
- cycleReEval, true);
- } else {
- cstate.setCurRawAdj(cstate.getCurAdj());
- cstate.setCurRawProcState(cstate.getCurProcState());
- }
-
- int clientAdj = cstate.getCurRawAdj();
- int clientProcState = cstate.getCurRawProcState();
-
- final boolean clientIsSystem = clientProcState < PROCESS_STATE_TOP;
-
- boundByNonBgRestricted |= cstate.isCurBoundByNonBgRestrictedApp()
- || clientProcState <= PROCESS_STATE_BOUND_TOP
- || (clientProcState == PROCESS_STATE_FOREGROUND_SERVICE
- && !cstate.isBackgroundRestricted());
-
- if (client.mOptRecord.shouldNotFreeze()) {
- // Propagate the shouldNotFreeze flag down the bindings.
- app.mOptRecord.setShouldNotFreeze(true);
- }
-
- // We always propagate PROCESS_CAPABILITY_BFSL over bindings here,
- // but, right before actually setting it to the process,
- // we check the final procstate, and remove it if the procsate is below BFGS.
- capability |= getBfslCapabilityFromClient(client);
-
- if (cr.notHasFlag(Context.BIND_WAIVE_PRIORITY)) {
- if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
- capability |= cstate.getCurCapability();
- }
-
- // If an app has network capability by default
- // (by having procstate <= BFGS), then the apps it binds to will get
- // elevated to a high enough procstate anyway to get network unless they
- // request otherwise, so don't propagate the network capability by default
- // in this case unless they explicitly request it.
- if ((cstate.getCurCapability()
- & PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK) != 0) {
- if (clientProcState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
- // This is used to grant network access to Expedited Jobs.
- if (cr.hasFlag(Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS)) {
- capability |= PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK;
- }
- } else {
- capability |= PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK;
- }
- }
- if ((cstate.getCurCapability()
- & PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK) != 0) {
- if (clientProcState <= PROCESS_STATE_IMPORTANT_FOREGROUND) {
- // This is used to grant network access to User Initiated Jobs.
- if (cr.hasFlag(Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS)) {
- capability |= PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK;
- }
- }
- }
-
- if (shouldSkipDueToCycle(app, cstate, procState, adj, cycleReEval)) {
- continue;
- }
+ computeServiceHostOomAdjLSP(cr, app, cr.binding.client, now, topApp, doingAll,
+ cycleReEval, computeClients, oomAdjReason, cachedAdj, true);
- if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
- // If the other app is cached for any reason, for purposes here
- // we are going to consider it empty. The specific cached state
- // doesn't propagate except under certain conditions.
- clientProcState = PROCESS_STATE_CACHED_EMPTY;
- }
- String adjType = null;
- if (cr.hasFlag(Context.BIND_ALLOW_OOM_MANAGEMENT)) {
- // Similar to BIND_WAIVE_PRIORITY, keep it unfrozen.
- if (clientAdj < CACHED_APP_MIN_ADJ) {
- app.mOptRecord.setShouldNotFreeze(true);
- }
- // Not doing bind OOM management, so treat
- // this guy more like a started service.
- if (state.hasShownUi() && !state.getCachedIsHomeProcess()) {
- // If this process has shown some UI, let it immediately
- // go to the LRU list because it may be pretty heavy with
- // UI stuff. We'll tag it with a label just to help
- // debug and understand what is going on.
- if (adj > clientAdj) {
- adjType = "cch-bound-ui-services";
- }
- state.setCached(false);
- clientAdj = adj;
- clientProcState = procState;
- } else {
- if (now >= (s.lastActivity
- + mConstants.MAX_SERVICE_INACTIVITY)) {
- // This service has not seen activity within
- // recent memory, so allow it to drop to the
- // LRU list if there is no other reason to keep
- // it around. We'll also tag it with a label just
- // to help debug and undertand what is going on.
- if (adj > clientAdj) {
- adjType = "cch-bound-services";
- }
- clientAdj = adj;
- }
- }
- }
- if (adj > clientAdj) {
- // If this process has recently shown UI, and
- // the process that is binding to it is less
- // important than being visible, then we don't
- // care about the binding as much as we care
- // about letting this process get into the LRU
- // list to be killed and restarted if needed for
- // memory.
- if (state.hasShownUi() && !state.getCachedIsHomeProcess()
- && clientAdj > PERCEPTIBLE_APP_ADJ) {
- if (adj >= CACHED_APP_MIN_ADJ) {
- adjType = "cch-bound-ui-services";
- }
- } else {
- int newAdj;
- int lbAdj = VISIBLE_APP_ADJ; // lower bound of adj.
- if (cr.hasFlag(Context.BIND_ABOVE_CLIENT
- | Context.BIND_IMPORTANT)) {
- if (clientAdj >= PERSISTENT_SERVICE_ADJ) {
- newAdj = clientAdj;
- } else {
- // make this service persistent
- newAdj = PERSISTENT_SERVICE_ADJ;
- schedGroup = SCHED_GROUP_DEFAULT;
- procState = ActivityManager.PROCESS_STATE_PERSISTENT;
- cr.trackProcState(procState, mAdjSeq);
- trackedProcState = true;
- }
- } else if (cr.hasFlag(Context.BIND_NOT_PERCEPTIBLE)
- && clientAdj <= PERCEPTIBLE_APP_ADJ
- && adj >= (lbAdj = PERCEPTIBLE_LOW_APP_ADJ)) {
- newAdj = PERCEPTIBLE_LOW_APP_ADJ;
- } else if (cr.hasFlag(Context.BIND_ALMOST_PERCEPTIBLE)
- && cr.notHasFlag(Context.BIND_NOT_FOREGROUND)
- && clientAdj < PERCEPTIBLE_APP_ADJ
- && adj >= (lbAdj = PERCEPTIBLE_APP_ADJ)) {
- // This is for user-initiated jobs.
- // We use APP_ADJ + 1 here, so we can tell them apart from FGS.
- newAdj = PERCEPTIBLE_APP_ADJ + 1;
- } else if (cr.hasFlag(Context.BIND_ALMOST_PERCEPTIBLE)
- && cr.hasFlag(Context.BIND_NOT_FOREGROUND)
- && clientAdj < PERCEPTIBLE_APP_ADJ
- && adj >= (lbAdj = (PERCEPTIBLE_MEDIUM_APP_ADJ + 2))) {
- // This is for expedited jobs.
- // We use MEDIUM_APP_ADJ + 2 here, so we can tell apart
- // EJ and short-FGS.
- newAdj = PERCEPTIBLE_MEDIUM_APP_ADJ + 2;
- } else if (cr.hasFlag(Context.BIND_NOT_VISIBLE)
- && clientAdj < PERCEPTIBLE_APP_ADJ
- && adj >= (lbAdj = PERCEPTIBLE_APP_ADJ)) {
- newAdj = PERCEPTIBLE_APP_ADJ;
- } else if (clientAdj >= PERCEPTIBLE_APP_ADJ) {
- newAdj = clientAdj;
- } else if (cr.hasFlag(BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE)
- && clientAdj <= VISIBLE_APP_ADJ
- && adj > VISIBLE_APP_ADJ) {
- newAdj = VISIBLE_APP_ADJ;
- } else {
- if (adj > VISIBLE_APP_ADJ) {
- // TODO: Is this too limiting for apps bound from TOP?
- newAdj = Math.max(clientAdj, lbAdj);
- } else {
- newAdj = adj;
- }
- }
- if (!cstate.isCached()) {
- state.setCached(false);
- }
- if (adj > newAdj) {
- adj = newAdj;
- state.setCurRawAdj(adj);
- adjType = "service";
- }
- }
- }
- if (cr.notHasFlag(Context.BIND_NOT_FOREGROUND
- | Context.BIND_IMPORTANT_BACKGROUND)) {
- // This will treat important bound services identically to
- // the top app, which may behave differently than generic
- // foreground work.
- final int curSchedGroup = cstate.getCurrentSchedulingGroup();
- if (curSchedGroup > schedGroup) {
- if (cr.hasFlag(Context.BIND_IMPORTANT)) {
- schedGroup = curSchedGroup;
- } else {
- schedGroup = SCHED_GROUP_DEFAULT;
- }
- }
- if (clientProcState < PROCESS_STATE_TOP) {
- // Special handling for above-top states (persistent
- // processes). These should not bring the current process
- // into the top state, since they are not on top. Instead
- // give them the best bound state after that.
- if (cr.hasFlag(BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE)) {
- clientProcState = PROCESS_STATE_FOREGROUND_SERVICE;
- } else if (cr.hasFlag(Context.BIND_FOREGROUND_SERVICE)) {
- clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
- } else if (mService.mWakefulness.get()
- == PowerManagerInternal.WAKEFULNESS_AWAKE
- && cr.hasFlag(Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE))
- {
- clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
- } else {
- clientProcState =
- PROCESS_STATE_IMPORTANT_FOREGROUND;
- }
- } else if (clientProcState == PROCESS_STATE_TOP) {
- // Go at most to BOUND_TOP, unless requested to elevate
- // to client's state.
- clientProcState = PROCESS_STATE_BOUND_TOP;
- final boolean enabled = cstate.getCachedCompatChange(
- CACHED_COMPAT_CHANGE_PROCESS_CAPABILITY);
- if (enabled) {
- if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
- // TOP process passes all capabilities to the service.
- capability |= cstate.getCurCapability();
- } else {
- // TOP process passes no capability to the service.
- }
- } else {
- // TOP process passes all capabilities to the service.
- capability |= cstate.getCurCapability();
- }
- }
- } else if (cr.notHasFlag(Context.BIND_IMPORTANT_BACKGROUND)) {
- if (clientProcState <
- PROCESS_STATE_TRANSIENT_BACKGROUND) {
- clientProcState =
- PROCESS_STATE_TRANSIENT_BACKGROUND;
- }
- } else {
- if (clientProcState <
- PROCESS_STATE_IMPORTANT_BACKGROUND) {
- clientProcState =
- PROCESS_STATE_IMPORTANT_BACKGROUND;
- }
- }
-
- if (schedGroup < SCHED_GROUP_TOP_APP
- && cr.hasFlag(Context.BIND_SCHEDULE_LIKE_TOP_APP)
- && clientIsSystem) {
- schedGroup = SCHED_GROUP_TOP_APP;
- scheduleLikeTopApp = true;
- }
-
- if (!trackedProcState) {
- cr.trackProcState(clientProcState, mAdjSeq);
- }
-
- if (procState > clientProcState) {
- procState = clientProcState;
- state.setCurRawProcState(procState);
- if (adjType == null) {
- adjType = "service";
- }
- }
- if (procState < PROCESS_STATE_IMPORTANT_BACKGROUND
- && cr.hasFlag(Context.BIND_SHOWING_UI)) {
- app.setPendingUiClean(true);
- }
- if (adjType != null) {
- state.setAdjType(adjType);
- state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
- .REASON_SERVICE_IN_USE);
- state.setAdjSource(client);
- state.setAdjSourceProcState(clientProcState);
- state.setAdjTarget(s.instanceName);
- if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
- reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
- + ": " + app + ", due to " + client
- + " adj=" + adj + " procState="
- + ProcessList.makeProcStateString(procState));
- }
- }
- } else { // BIND_WAIVE_PRIORITY == true
- // BIND_WAIVE_PRIORITY bindings are special when it comes to the
- // freezer. Processes bound via WPRI are expected to be running,
- // but they are not promoted in the LRU list to keep them out of
- // cached. As a result, they can freeze based on oom_adj alone.
- // Normally, bindToDeath would fire when a cached app would die
- // in the background, but nothing will fire when a running process
- // pings a frozen process. Accordingly, any cached app that is
- // bound by an unfrozen app via a WPRI binding has to remain
- // unfrozen.
- if (clientAdj < CACHED_APP_MIN_ADJ) {
- app.mOptRecord.setShouldNotFreeze(true);
- }
- }
- if (cr.hasFlag(Context.BIND_TREAT_LIKE_ACTIVITY)) {
- psr.setTreatLikeActivity(true);
- }
- final ActivityServiceConnectionsHolder a = cr.activity;
- if (cr.hasFlag(Context.BIND_ADJUST_WITH_ACTIVITY)) {
- if (a != null && adj > FOREGROUND_APP_ADJ
- && a.isActivityVisible()) {
- adj = FOREGROUND_APP_ADJ;
- state.setCurRawAdj(adj);
- if (cr.notHasFlag(Context.BIND_NOT_FOREGROUND)) {
- if (cr.hasFlag(Context.BIND_IMPORTANT)) {
- schedGroup = SCHED_GROUP_TOP_APP_BOUND;
- } else {
- schedGroup = SCHED_GROUP_DEFAULT;
- }
- }
- state.setCached(false);
- state.setAdjType("service");
- state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
- .REASON_SERVICE_IN_USE);
- state.setAdjSource(a);
- state.setAdjSourceProcState(procState);
- state.setAdjTarget(s.instanceName);
- if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
- reportOomAdjMessageLocked(TAG_OOM_ADJ,
- "Raise to service w/activity: " + app);
- }
- }
- }
+ adj = state.getCurRawAdj();
+ procState = state.getCurRawProcState();
+ schedGroup = state.getCurrentSchedulingGroup();
+ capability = state.getCurCapability();
}
}
}
@@ -2603,97 +2330,27 @@ public class OomAdjuster {
|| procState > PROCESS_STATE_TOP);
provi--) {
ContentProviderRecord cpr = ppr.getProviderAt(provi);
- for (int i = cpr.connections.size() - 1;
- i >= 0 && (adj > FOREGROUND_APP_ADJ
- || schedGroup == SCHED_GROUP_BACKGROUND
- || procState > PROCESS_STATE_TOP);
- i--) {
- ContentProviderConnection conn = cpr.connections.get(i);
- ProcessRecord client = conn.client;
- final ProcessStateRecord cstate = client.mState;
- if (client == app) {
- // Being our own client is not interesting.
- continue;
- }
- if (computeClients) {
- computeOomAdjLSP(client, cachedAdj, topApp, doingAll, now, cycleReEval, true);
- } else {
- cstate.setCurRawAdj(cstate.getCurAdj());
- cstate.setCurRawProcState(cstate.getCurProcState());
- }
-
- if (shouldSkipDueToCycle(app, cstate, procState, adj, cycleReEval)) {
- continue;
- }
-
- int clientAdj = cstate.getCurRawAdj();
- int clientProcState = cstate.getCurRawProcState();
-
- // We always propagate PROCESS_CAPABILITY_BFSL to providers here,
- // but, right before actually setting it to the process,
- // we check the final procstate, and remove it if the procsate is below BFGS.
- capability |= getBfslCapabilityFromClient(client);
-
- if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
- // If the other app is cached for any reason, for purposes here
- // we are going to consider it empty.
- clientProcState = PROCESS_STATE_CACHED_EMPTY;
- }
- if (client.mOptRecord.shouldNotFreeze()) {
- // Propagate the shouldNotFreeze flag down the bindings.
- app.mOptRecord.setShouldNotFreeze(true);
- }
-
- boundByNonBgRestricted |= cstate.isCurBoundByNonBgRestrictedApp()
- || clientProcState <= PROCESS_STATE_BOUND_TOP
- || (clientProcState == PROCESS_STATE_FOREGROUND_SERVICE
- && !cstate.isBackgroundRestricted());
-
- String adjType = null;
- if (adj > clientAdj) {
- if (state.hasShownUi() && !state.getCachedIsHomeProcess()
- && clientAdj > PERCEPTIBLE_APP_ADJ) {
- adjType = "cch-ui-provider";
- } else {
- adj = Math.max(clientAdj, FOREGROUND_APP_ADJ);
- state.setCurRawAdj(adj);
- adjType = "provider";
- }
- state.setCached(state.isCached() & cstate.isCached());
- }
-
- if (clientProcState <= PROCESS_STATE_FOREGROUND_SERVICE) {
- if (adjType == null) {
- adjType = "provider";
- }
- if (clientProcState == PROCESS_STATE_TOP) {
- clientProcState = PROCESS_STATE_BOUND_TOP;
- } else {
- clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
- }
- }
+ if (couldRecurse) {
+ // We're entering recursive functions below.
+ state.setCurRawAdj(adj);
+ state.setCurRawProcState(procState);
+ state.setCurrentSchedulingGroup(schedGroup);
+ state.setCurCapability(capability);
+
+ for (int i = cpr.connections.size() - 1;
+ i >= 0 && (adj > FOREGROUND_APP_ADJ
+ || schedGroup == SCHED_GROUP_BACKGROUND
+ || procState > PROCESS_STATE_TOP);
+ i--) {
+ ContentProviderConnection conn = cpr.connections.get(i);
+ ProcessRecord client = conn.client;
+ computeProviderHostOomAdjLSP(conn, app, client, now, topApp, doingAll,
+ cycleReEval, computeClients, oomAdjReason, cachedAdj, true);
- conn.trackProcState(clientProcState, mAdjSeq);
- if (procState > clientProcState) {
- procState = clientProcState;
- state.setCurRawProcState(procState);
- }
- if (cstate.getCurrentSchedulingGroup() > schedGroup) {
- schedGroup = SCHED_GROUP_DEFAULT;
- }
- if (adjType != null) {
- state.setAdjType(adjType);
- state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
- .REASON_PROVIDER_IN_USE);
- state.setAdjSource(client);
- state.setAdjSourceProcState(clientProcState);
- state.setAdjTarget(cpr.name);
- if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
- reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
- + ": " + app + ", due to " + client
- + " adj=" + adj + " procState="
- + ProcessList.makeProcStateString(procState));
- }
+ adj = state.getCurRawAdj();
+ procState = state.getCurRawProcState();
+ schedGroup = state.getCurrentSchedulingGroup();
+ capability = state.getCurCapability();
}
}
// If the provider has external (non-framework) process
@@ -2799,7 +2456,7 @@ public class OomAdjuster {
// restrictions on screen off
if (procState >= PROCESS_STATE_BOUND_FOREGROUND_SERVICE
&& mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE
- && !scheduleLikeTopApp) {
+ && !state.shouldScheduleLikeTopApp()) {
if (schedGroup > SCHED_GROUP_RESTRICTED) {
schedGroup = SCHED_GROUP_RESTRICTED;
}
@@ -2817,6 +2474,7 @@ public class OomAdjuster {
capability &= ~PROCESS_CAPABILITY_BFSL;
}
+ state.setHasForegroundActivities(foregroundActivities);
if (app.isPendingFinishAttach()) {
// If the app is still starting up. We reset the computations to the
@@ -2834,22 +2492,580 @@ public class OomAdjuster {
// it when computing the final cached adj later. Note that we don't need to
// worry about this for max adj above, since max adj will always be used to
// keep it out of the cached vaues.
- state.setCurAdj(adj);
state.setCurCapability(capability);
- state.setCurrentSchedulingGroup(schedGroup);
- state.setCurProcState(procState);
- state.setCurRawProcState(procState);
state.updateLastInvisibleTime(hasVisibleActivities);
- state.setHasForegroundActivities(foregroundActivities);
state.setCompletedAdjSeq(mAdjSeq);
- state.setCurBoundByNonBgRestrictedApp(boundByNonBgRestricted);
+
+ schedGroup = setIntermediateAdjLSP(app, adj, prevAppAdj, schedGroup);
+ setIntermediateProcStateLSP(app, procState, prevProcState);
+ setIntermediateSchedGroupLSP(state, schedGroup);
// if curAdj or curProcState improved, then this process was promoted
return state.getCurAdj() < prevAppAdj || state.getCurProcState() < prevProcState
|| state.getCurCapability() != prevCapability;
}
- private int getDefaultCapability(ProcessRecord app, int procState) {
+ /**
+ * @return The proposed change to the schedGroup.
+ */
+ @GuardedBy({"mService", "mProcLock"})
+ protected int setIntermediateAdjLSP(ProcessRecord app, int adj, int prevRawAppAdj,
+ int schedGroup) {
+ final ProcessStateRecord state = app.mState;
+ state.setCurRawAdj(adj);
+
+ adj = app.mServices.modifyRawOomAdj(adj);
+ if (adj > state.getMaxAdj()) {
+ adj = state.getMaxAdj();
+ if (adj <= PERCEPTIBLE_LOW_APP_ADJ) {
+ schedGroup = SCHED_GROUP_DEFAULT;
+ }
+ }
+
+ state.setCurAdj(adj);
+
+ return schedGroup;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ protected void setIntermediateProcStateLSP(ProcessRecord app, int procState,
+ int prevProcState) {
+ final ProcessStateRecord state = app.mState;
+ state.setCurProcState(procState);
+ state.setCurRawProcState(procState);
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ protected void setIntermediateSchedGroupLSP(ProcessStateRecord state, int schedGroup) {
+ // Put bound foreground services in a special sched group for additional
+ // restrictions on screen off
+ if (state.getCurProcState() >= PROCESS_STATE_BOUND_FOREGROUND_SERVICE
+ && mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE
+ && !state.shouldScheduleLikeTopApp()) {
+ if (schedGroup > SCHED_GROUP_RESTRICTED) {
+ schedGroup = SCHED_GROUP_RESTRICTED;
+ }
+ }
+
+ state.setCurrentSchedulingGroup(schedGroup);
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ protected void computeServiceHostOomAdjLSP(ConnectionRecord cr, ProcessRecord app,
+ ProcessRecord client, long now, ProcessRecord topApp, boolean doingAll,
+ boolean cycleReEval, boolean computeClients, int oomAdjReason, int cachedAdj,
+ boolean couldRecurse) {
+ if (app.isPendingFinishAttach()) {
+ // We've set the attaching process state in the computeInitialOomAdjLSP. Skip it here.
+ return;
+ }
+
+ final ProcessStateRecord state = app.mState;
+ ProcessStateRecord cstate = client.mState;
+
+ if (couldRecurse) {
+ if (app.isSdkSandbox && cr.binding.attributedClient != null) {
+ // For SDK sandboxes, use the attributed client (eg the app that
+ // requested the sandbox)
+ client = cr.binding.attributedClient;
+ cstate = client.mState;
+ }
+ if (computeClients) {
+ computeOomAdjLSP(client, cachedAdj, topApp, doingAll, now, cycleReEval, true,
+ oomAdjReason, true);
+ } else {
+ cstate.setCurRawAdj(cstate.getCurAdj());
+ cstate.setCurRawProcState(cstate.getCurProcState());
+ }
+ }
+
+ int clientAdj = cstate.getCurRawAdj();
+ int clientProcState = cstate.getCurRawProcState();
+
+ final boolean clientIsSystem = clientProcState < PROCESS_STATE_TOP;
+
+ int adj = state.getCurRawAdj();
+ int procState = state.getCurRawProcState();
+ int schedGroup = state.getCurrentSchedulingGroup();
+ int capability = state.getCurCapability();
+
+ final int prevRawAdj = adj;
+ final int prevProcState = procState;
+ final int prevSchedGroup = schedGroup;
+
+ final int appUid = app.info.uid;
+ final int logUid = mService.mCurOomAdjUid;
+
+ state.setCurBoundByNonBgRestrictedApp(state.isCurBoundByNonBgRestrictedApp()
+ || cstate.isCurBoundByNonBgRestrictedApp()
+ || clientProcState <= PROCESS_STATE_BOUND_TOP
+ || (clientProcState == PROCESS_STATE_FOREGROUND_SERVICE
+ && !cstate.isBackgroundRestricted()));
+
+ if (client.mOptRecord.shouldNotFreeze()) {
+ // Propagate the shouldNotFreeze flag down the bindings.
+ app.mOptRecord.setShouldNotFreeze(true);
+ }
+
+ boolean trackedProcState = false;
+
+ // We always propagate PROCESS_CAPABILITY_BFSL over bindings here,
+ // but, right before actually setting it to the process,
+ // we check the final procstate, and remove it if the procsate is below BFGS.
+ capability |= getBfslCapabilityFromClient(client);
+
+ if (cr.notHasFlag(Context.BIND_WAIVE_PRIORITY)) {
+ if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
+ capability |= cstate.getCurCapability();
+ }
+
+ // If an app has network capability by default
+ // (by having procstate <= BFGS), then the apps it binds to will get
+ // elevated to a high enough procstate anyway to get network unless they
+ // request otherwise, so don't propagate the network capability by default
+ // in this case unless they explicitly request it.
+ if ((cstate.getCurCapability()
+ & PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK) != 0) {
+ if (clientProcState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
+ // This is used to grant network access to Expedited Jobs.
+ if (cr.hasFlag(Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS)) {
+ capability |= PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK;
+ }
+ } else {
+ capability |= PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK;
+ }
+ }
+ if ((cstate.getCurCapability()
+ & PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK) != 0) {
+ if (clientProcState <= PROCESS_STATE_IMPORTANT_FOREGROUND) {
+ // This is used to grant network access to User Initiated Jobs.
+ if (cr.hasFlag(Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS)) {
+ capability |= PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK;
+ }
+ }
+ }
+
+ if (couldRecurse && shouldSkipDueToCycle(app, cstate, procState, adj, cycleReEval)) {
+ return;
+ }
+
+ if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
+ // If the other app is cached for any reason, for purposes here
+ // we are going to consider it empty. The specific cached state
+ // doesn't propagate except under certain conditions.
+ clientProcState = PROCESS_STATE_CACHED_EMPTY;
+ }
+ String adjType = null;
+ if (cr.hasFlag(Context.BIND_ALLOW_OOM_MANAGEMENT)) {
+ // Similar to BIND_WAIVE_PRIORITY, keep it unfrozen.
+ if (clientAdj < CACHED_APP_MIN_ADJ) {
+ app.mOptRecord.setShouldNotFreeze(true);
+ }
+ // Not doing bind OOM management, so treat
+ // this guy more like a started service.
+ if (state.hasShownUi() && !state.getCachedIsHomeProcess()) {
+ // If this process has shown some UI, let it immediately
+ // go to the LRU list because it may be pretty heavy with
+ // UI stuff. We'll tag it with a label just to help
+ // debug and understand what is going on.
+ if (adj > clientAdj) {
+ adjType = "cch-bound-ui-services";
+ }
+ state.setCached(false);
+ clientAdj = adj;
+ clientProcState = procState;
+ } else {
+ if (now >= (cr.binding.service.lastActivity
+ + mConstants.MAX_SERVICE_INACTIVITY)) {
+ // This service has not seen activity within
+ // recent memory, so allow it to drop to the
+ // LRU list if there is no other reason to keep
+ // it around. We'll also tag it with a label just
+ // to help debug and undertand what is going on.
+ if (adj > clientAdj) {
+ adjType = "cch-bound-services";
+ }
+ clientAdj = adj;
+ }
+ }
+ }
+ if (adj > clientAdj) {
+ // If this process has recently shown UI, and
+ // the process that is binding to it is less
+ // important than being visible, then we don't
+ // care about the binding as much as we care
+ // about letting this process get into the LRU
+ // list to be killed and restarted if needed for
+ // memory.
+ if (state.hasShownUi() && !state.getCachedIsHomeProcess()
+ && clientAdj > PERCEPTIBLE_APP_ADJ) {
+ if (adj >= CACHED_APP_MIN_ADJ) {
+ adjType = "cch-bound-ui-services";
+ }
+ } else {
+ int newAdj;
+ int lbAdj = VISIBLE_APP_ADJ; // lower bound of adj.
+ if (cr.hasFlag(Context.BIND_ABOVE_CLIENT
+ | Context.BIND_IMPORTANT)) {
+ if (clientAdj >= PERSISTENT_SERVICE_ADJ) {
+ newAdj = clientAdj;
+ } else {
+ // make this service persistent
+ newAdj = PERSISTENT_SERVICE_ADJ;
+ schedGroup = SCHED_GROUP_DEFAULT;
+ procState = ActivityManager.PROCESS_STATE_PERSISTENT;
+ cr.trackProcState(procState, mAdjSeq);
+ trackedProcState = true;
+ }
+ } else if (cr.hasFlag(Context.BIND_NOT_PERCEPTIBLE)
+ && clientAdj <= PERCEPTIBLE_APP_ADJ
+ && adj >= (lbAdj = PERCEPTIBLE_LOW_APP_ADJ)) {
+ newAdj = PERCEPTIBLE_LOW_APP_ADJ;
+ } else if (cr.hasFlag(Context.BIND_ALMOST_PERCEPTIBLE)
+ && cr.notHasFlag(Context.BIND_NOT_FOREGROUND)
+ && clientAdj < PERCEPTIBLE_APP_ADJ
+ && adj >= (lbAdj = PERCEPTIBLE_APP_ADJ)) {
+ // This is for user-initiated jobs.
+ // We use APP_ADJ + 1 here, so we can tell them apart from FGS.
+ newAdj = PERCEPTIBLE_APP_ADJ + 1;
+ } else if (cr.hasFlag(Context.BIND_ALMOST_PERCEPTIBLE)
+ && cr.hasFlag(Context.BIND_NOT_FOREGROUND)
+ && clientAdj < PERCEPTIBLE_APP_ADJ
+ && adj >= (lbAdj = (PERCEPTIBLE_MEDIUM_APP_ADJ + 2))) {
+ // This is for expedited jobs.
+ // We use MEDIUM_APP_ADJ + 2 here, so we can tell apart
+ // EJ and short-FGS.
+ newAdj = PERCEPTIBLE_MEDIUM_APP_ADJ + 2;
+ } else if (cr.hasFlag(Context.BIND_NOT_VISIBLE)
+ && clientAdj < PERCEPTIBLE_APP_ADJ
+ && adj >= (lbAdj = PERCEPTIBLE_APP_ADJ)) {
+ newAdj = PERCEPTIBLE_APP_ADJ;
+ } else if (clientAdj >= PERCEPTIBLE_APP_ADJ) {
+ newAdj = clientAdj;
+ } else if (cr.hasFlag(BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE)
+ && clientAdj <= VISIBLE_APP_ADJ
+ && adj > VISIBLE_APP_ADJ) {
+ newAdj = VISIBLE_APP_ADJ;
+ } else {
+ if (adj > VISIBLE_APP_ADJ) {
+ // TODO: Is this too limiting for apps bound from TOP?
+ newAdj = Math.max(clientAdj, lbAdj);
+ } else {
+ newAdj = adj;
+ }
+ }
+ if (!cstate.isCached()) {
+ state.setCached(false);
+ }
+ if (adj > newAdj) {
+ adj = newAdj;
+ state.setCurRawAdj(adj);
+ adjType = "service";
+ }
+ }
+ }
+ if (cr.notHasFlag(Context.BIND_NOT_FOREGROUND
+ | Context.BIND_IMPORTANT_BACKGROUND)) {
+ // This will treat important bound services identically to
+ // the top app, which may behave differently than generic
+ // foreground work.
+ final int curSchedGroup = cstate.getCurrentSchedulingGroup();
+ if (curSchedGroup > schedGroup) {
+ if (cr.hasFlag(Context.BIND_IMPORTANT)) {
+ schedGroup = curSchedGroup;
+ } else {
+ schedGroup = SCHED_GROUP_DEFAULT;
+ }
+ }
+ if (clientProcState < PROCESS_STATE_TOP) {
+ // Special handling for above-top states (persistent
+ // processes). These should not bring the current process
+ // into the top state, since they are not on top. Instead
+ // give them the best bound state after that.
+ if (cr.hasFlag(BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE)) {
+ clientProcState = PROCESS_STATE_FOREGROUND_SERVICE;
+ } else if (cr.hasFlag(Context.BIND_FOREGROUND_SERVICE)) {
+ clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+ } else if (mService.mWakefulness.get()
+ == PowerManagerInternal.WAKEFULNESS_AWAKE
+ && cr.hasFlag(Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE)) {
+ clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+ } else {
+ clientProcState =
+ PROCESS_STATE_IMPORTANT_FOREGROUND;
+ }
+ } else if (clientProcState == PROCESS_STATE_TOP) {
+ // Go at most to BOUND_TOP, unless requested to elevate
+ // to client's state.
+ clientProcState = PROCESS_STATE_BOUND_TOP;
+ final boolean enabled = cstate.getCachedCompatChange(
+ CACHED_COMPAT_CHANGE_PROCESS_CAPABILITY);
+ if (enabled) {
+ if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
+ // TOP process passes all capabilities to the service.
+ capability |= cstate.getCurCapability();
+ } else {
+ // TOP process passes no capability to the service.
+ }
+ } else {
+ // TOP process passes all capabilities to the service.
+ capability |= cstate.getCurCapability();
+ }
+ }
+ } else if (cr.notHasFlag(Context.BIND_IMPORTANT_BACKGROUND)) {
+ if (clientProcState < PROCESS_STATE_TRANSIENT_BACKGROUND) {
+ clientProcState =
+ PROCESS_STATE_TRANSIENT_BACKGROUND;
+ }
+ } else {
+ if (clientProcState < PROCESS_STATE_IMPORTANT_BACKGROUND) {
+ clientProcState =
+ PROCESS_STATE_IMPORTANT_BACKGROUND;
+ }
+ }
+
+ if (schedGroup < SCHED_GROUP_TOP_APP
+ && cr.hasFlag(Context.BIND_SCHEDULE_LIKE_TOP_APP)
+ && clientIsSystem) {
+ schedGroup = SCHED_GROUP_TOP_APP;
+ state.setScheduleLikeTopApp(true);
+ }
+
+ if (!trackedProcState) {
+ cr.trackProcState(clientProcState, mAdjSeq);
+ }
+
+ if (procState > clientProcState) {
+ procState = clientProcState;
+ state.setCurRawProcState(procState);
+ if (adjType == null) {
+ adjType = "service";
+ }
+ }
+ if (procState < PROCESS_STATE_IMPORTANT_BACKGROUND
+ && cr.hasFlag(Context.BIND_SHOWING_UI)) {
+ app.setPendingUiClean(true);
+ }
+ if (adjType != null) {
+ state.setAdjType(adjType);
+ state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
+ .REASON_SERVICE_IN_USE);
+ state.setAdjSource(client);
+ state.setAdjSourceProcState(clientProcState);
+ state.setAdjTarget(cr.binding.service.instanceName);
+ if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+ reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
+ + ": " + app + ", due to " + client
+ + " adj=" + adj + " procState="
+ + ProcessList.makeProcStateString(procState));
+ }
+ }
+ } else { // BIND_WAIVE_PRIORITY == true
+ // BIND_WAIVE_PRIORITY bindings are special when it comes to the
+ // freezer. Processes bound via WPRI are expected to be running,
+ // but they are not promoted in the LRU list to keep them out of
+ // cached. As a result, they can freeze based on oom_adj alone.
+ // Normally, bindToDeath would fire when a cached app would die
+ // in the background, but nothing will fire when a running process
+ // pings a frozen process. Accordingly, any cached app that is
+ // bound by an unfrozen app via a WPRI binding has to remain
+ // unfrozen.
+ if (clientAdj < CACHED_APP_MIN_ADJ) {
+ app.mOptRecord.setShouldNotFreeze(true);
+ }
+ }
+ if (cr.hasFlag(Context.BIND_TREAT_LIKE_ACTIVITY)) {
+ app.mServices.setTreatLikeActivity(true);
+ if (clientProcState <= PROCESS_STATE_CACHED_ACTIVITY
+ && procState > PROCESS_STATE_CACHED_ACTIVITY) {
+ // This is a cached process, but somebody wants us to treat it like it has
+ // an activity, okay!
+ procState = PROCESS_STATE_CACHED_ACTIVITY;
+ state.setAdjType("cch-as-act");
+ }
+ }
+ final ActivityServiceConnectionsHolder a = cr.activity;
+ if (cr.hasFlag(Context.BIND_ADJUST_WITH_ACTIVITY)) {
+ if (a != null && adj > FOREGROUND_APP_ADJ
+ && a.isActivityVisible()) {
+ adj = FOREGROUND_APP_ADJ;
+ state.setCurRawAdj(adj);
+ if (cr.notHasFlag(Context.BIND_NOT_FOREGROUND)) {
+ if (cr.hasFlag(Context.BIND_IMPORTANT)) {
+ schedGroup = SCHED_GROUP_TOP_APP_BOUND;
+ } else {
+ schedGroup = SCHED_GROUP_DEFAULT;
+ }
+ }
+ state.setCached(false);
+ state.setAdjType("service");
+ state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
+ .REASON_SERVICE_IN_USE);
+ state.setAdjSource(a);
+ state.setAdjSourceProcState(procState);
+ state.setAdjTarget(cr.binding.service.instanceName);
+ if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+ reportOomAdjMessageLocked(TAG_OOM_ADJ,
+ "Raise to service w/activity: " + app);
+ }
+ }
+ }
+
+ capability |= getDefaultCapability(app, procState);
+
+ // Procstates below BFGS should never have this capability.
+ if (procState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
+ capability &= ~PROCESS_CAPABILITY_BFSL;
+ }
+
+ if (adj < prevRawAdj) {
+ schedGroup = setIntermediateAdjLSP(app, adj, prevRawAdj, schedGroup);
+ }
+ if (procState < prevProcState) {
+ setIntermediateProcStateLSP(app, procState, prevProcState);
+ }
+ if (schedGroup > prevSchedGroup) {
+ setIntermediateSchedGroupLSP(state, schedGroup);
+ }
+ state.setCurCapability(capability);
+
+ state.setEmpty(false);
+ }
+
+ protected void computeProviderHostOomAdjLSP(ContentProviderConnection conn, ProcessRecord app,
+ ProcessRecord client, long now, ProcessRecord topApp, boolean doingAll,
+ boolean cycleReEval, boolean computeClients, int oomAdjReason, int cachedAdj,
+ boolean couldRecurse) {
+ if (app.isPendingFinishAttach()) {
+ // We've set the attaching process state in the computeInitialOomAdjLSP. Skip it here.
+ return;
+ }
+
+ final ProcessStateRecord state = app.mState;
+ final ProcessStateRecord cstate = client.mState;
+
+ if (client == app) {
+ // Being our own client is not interesting.
+ return;
+ }
+ if (couldRecurse) {
+ if (computeClients) {
+ computeOomAdjLSP(client, cachedAdj, topApp, doingAll, now, cycleReEval, true,
+ oomAdjReason, true);
+ } else if (couldRecurse) {
+ cstate.setCurRawAdj(cstate.getCurAdj());
+ cstate.setCurRawProcState(cstate.getCurProcState());
+ }
+
+ if (shouldSkipDueToCycle(app, cstate, state.getCurRawProcState(), state.getCurRawAdj(),
+ cycleReEval)) {
+ return;
+ }
+ }
+
+ int clientAdj = cstate.getCurRawAdj();
+ int clientProcState = cstate.getCurRawProcState();
+
+ int adj = state.getCurRawAdj();
+ int procState = state.getCurRawProcState();
+ int schedGroup = state.getCurrentSchedulingGroup();
+ int capability = state.getCurCapability();
+
+ final int prevRawAdj = adj;
+ final int prevProcState = procState;
+ final int prevSchedGroup = schedGroup;
+
+ final int appUid = app.info.uid;
+ final int logUid = mService.mCurOomAdjUid;
+
+ // We always propagate PROCESS_CAPABILITY_BFSL to providers here,
+ // but, right before actually setting it to the process,
+ // we check the final procstate, and remove it if the procsate is below BFGS.
+ capability |= getBfslCapabilityFromClient(client);
+
+ if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
+ // If the other app is cached for any reason, for purposes here
+ // we are going to consider it empty.
+ clientProcState = PROCESS_STATE_CACHED_EMPTY;
+ }
+ if (client.mOptRecord.shouldNotFreeze()) {
+ // Propagate the shouldNotFreeze flag down the bindings.
+ app.mOptRecord.setShouldNotFreeze(true);
+ }
+
+ state.setCurBoundByNonBgRestrictedApp(state.isCurBoundByNonBgRestrictedApp()
+ || cstate.isCurBoundByNonBgRestrictedApp()
+ || clientProcState <= PROCESS_STATE_BOUND_TOP
+ || (clientProcState == PROCESS_STATE_FOREGROUND_SERVICE
+ && !cstate.isBackgroundRestricted()));
+
+ String adjType = null;
+ if (adj > clientAdj) {
+ if (state.hasShownUi() && !state.getCachedIsHomeProcess()
+ && clientAdj > PERCEPTIBLE_APP_ADJ) {
+ adjType = "cch-ui-provider";
+ } else {
+ adj = Math.max(clientAdj, FOREGROUND_APP_ADJ);
+ state.setCurRawAdj(adj);
+ adjType = "provider";
+ }
+ state.setCached(state.isCached() & cstate.isCached());
+ }
+
+ if (clientProcState <= PROCESS_STATE_FOREGROUND_SERVICE) {
+ if (adjType == null) {
+ adjType = "provider";
+ }
+ if (clientProcState == PROCESS_STATE_TOP) {
+ clientProcState = PROCESS_STATE_BOUND_TOP;
+ } else {
+ clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+ }
+ }
+
+ conn.trackProcState(clientProcState, mAdjSeq);
+ if (procState > clientProcState) {
+ procState = clientProcState;
+ state.setCurRawProcState(procState);
+ }
+ if (cstate.getCurrentSchedulingGroup() > schedGroup) {
+ schedGroup = SCHED_GROUP_DEFAULT;
+ }
+ if (adjType != null) {
+ state.setAdjType(adjType);
+ state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
+ .REASON_PROVIDER_IN_USE);
+ state.setAdjSource(client);
+ state.setAdjSourceProcState(clientProcState);
+ state.setAdjTarget(conn.provider.name);
+ if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+ reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
+ + ": " + app + ", due to " + client
+ + " adj=" + adj + " procState="
+ + ProcessList.makeProcStateString(procState));
+ }
+ }
+
+ // Procstates below BFGS should never have this capability.
+ if (procState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
+ capability &= ~PROCESS_CAPABILITY_BFSL;
+ }
+
+ if (adj < prevRawAdj) {
+ schedGroup = setIntermediateAdjLSP(app, adj, prevRawAdj, schedGroup);
+ }
+ if (procState < prevProcState) {
+ setIntermediateProcStateLSP(app, procState, prevProcState);
+ }
+ if (schedGroup > prevSchedGroup) {
+ setIntermediateSchedGroupLSP(state, schedGroup);
+ }
+ state.setCurCapability(capability);
+
+ state.setEmpty(false);
+ }
+
+ protected int getDefaultCapability(ProcessRecord app, int procState) {
final int networkCapabilities =
NetworkPolicyManager.getDefaultProcessNetworkCapabilities(procState);
final int baseCapabilities;
@@ -2882,7 +3098,7 @@ public class OomAdjuster {
/**
* @return the BFSL capability from a client (of a service binding or provider).
*/
- int getBfslCapabilityFromClient(ProcessRecord client) {
+ protected int getBfslCapabilityFromClient(ProcessRecord client) {
// Procstates above FGS should always have this flag. We shouldn't need this logic,
// but let's do it just in case.
if (client.mState.getCurProcState() < PROCESS_STATE_FOREGROUND_SERVICE) {
@@ -2967,7 +3183,7 @@ public class OomAdjuster {
/** Inform the oomadj observer of changes to oomadj. Used by tests. */
@GuardedBy("mService")
- private void reportOomAdjMessageLocked(String tag, String msg) {
+ protected void reportOomAdjMessageLocked(String tag, String msg) {
Slog.d(tag, msg);
synchronized (mService.mOomAdjObserverLock) {
if (mService.mCurOomAdjObserver != null) {
@@ -2983,7 +3199,7 @@ public class OomAdjuster {
/** Applies the computed oomadj, procstate and sched group values and freezes them in set* */
@GuardedBy({"mService", "mProcLock"})
- private boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now,
+ protected boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now,
long nowElapsed, @OomAdjReason int oomAdjReson) {
boolean success = true;
final ProcessStateRecord state = app.mState;
@@ -3272,6 +3488,8 @@ public class OomAdjuster {
int initialCapability = PROCESS_CAPABILITY_NONE;
boolean initialCached = true;
final ProcessStateRecord state = app.mState;
+ final int prevProcState = PROCESS_STATE_UNKNOWN;
+ final int prevAdj = UNKNOWN_ADJ;
// If the process has been marked as foreground, it is starting as the top app (with
// Zygote#START_AS_TOP_APP_ARG), so boost the thread priority of its default UI thread.
if (state.hasForegroundActivities()) {
@@ -3306,6 +3524,9 @@ public class OomAdjuster {
state.setCurRawAdj(ProcessList.FOREGROUND_APP_ADJ);
state.setForcingToImportant(null);
state.setHasShownUi(false);
+
+ onProcessStateChanged(app, prevProcState);
+ onProcessOomAdjChanged(app, prevAdj);
}
// ONLY used for unit testing in OomAdjusterTests.java
@@ -3553,4 +3774,56 @@ public class OomAdjuster {
}
processes.clear();
}
+
+ @GuardedBy("mService")
+ void onProcessBeginLocked(@NonNull ProcessRecord app) {
+ // Empty, the OomAdjusterModernImpl will have an implementation.
+ }
+
+ @GuardedBy("mService")
+ void onProcessEndLocked(@NonNull ProcessRecord app) {
+ // Empty, the OomAdjusterModernImpl will have an implementation.
+ }
+
+ /**
+ * Called when the process state is changed outside of the OomAdjuster.
+ */
+ @GuardedBy("mService")
+ void onProcessStateChanged(@NonNull ProcessRecord app, int prevProcState) {
+ // Empty, the OomAdjusterModernImpl will have an implementation.
+ }
+
+ /**
+ * Called when the oom adj is changed outside of the OomAdjuster.
+ */
+ @GuardedBy("mService")
+ void onProcessOomAdjChanged(@NonNull ProcessRecord app, int prevAdj) {
+ // Empty, the OomAdjusterModernImpl will have an implementation.
+ }
+
+ @VisibleForTesting
+ void resetInternal() {
+ // Empty, the OomAdjusterModernImpl will have an implementation.
+ }
+
+ @GuardedBy("mService")
+ protected int getInitialAdj(@NonNull ProcessRecord app) {
+ return app.mState.getCurAdj();
+ }
+
+ @GuardedBy("mService")
+ protected int getInitialProcState(@NonNull ProcessRecord app) {
+ return app.mState.getCurProcState();
+ }
+
+ @GuardedBy("mService")
+ protected int getInitialCapability(@NonNull ProcessRecord app) {
+ return app.mState.getCurCapability();
+ }
+
+ @GuardedBy("mService")
+ protected boolean getInitialIsCurBoundByNonBgRestrictedApp(@NonNull ProcessRecord app) {
+ // The caller will set the initial value in this implementation.
+ return app.mState.isCurBoundByNonBgRestrictedApp();
+ }
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.md b/services/core/java/com/android/server/am/OomAdjuster.md
index 16091d1c162d..da5e12ef21fa 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.md
+++ b/services/core/java/com/android/server/am/OomAdjuster.md
@@ -130,3 +130,28 @@ The Oom Adjuster maintains a global sequence ID `mAdjSeq` to track the current O
* Iterate the processes from least important to most important ones.
* A maximum retries of 10 is enforced, while in practice, the maximum retries could reach only 2 to 3.
+## The Modern Implementation
+
+As aforementioned, the OomAdjuster makes the computation in a recursive way, while this is inefficient in dealing with the cycles. The overall code complexity should be around **O((1 + num(retries)) * num(procs) * num(binding connections))**. In addition, depending on the ordering of the input, the algorithm may produce different results and sometimes it's wrong.
+
+The new "Modern Implementation" is based on the rationale that, apps can't promote the service/provider it connects to, to a higher bucket than itself. We are introducing a bucket based, breadth first search algorithm, as illustrated below:
+
+```
+for all processes in the process list
+ compute the state of each process, but, excluding its clients
+ put each process to the corresponding bucket according to the state value
+done
+
+for each bucket, starting from the top most to the bottom most
+ for each process in the bucket
+ for each process it binds to
+ if the state of the bindee process could be elevated because of the binding; then
+ move the bindee process to the higher bucket
+ fi
+ done
+ done
+done
+```
+
+The overall code complexity should be around **O(num(procs) * num(binding connections))**, which saves the retry time from the existing algorithm.
+
diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
new file mode 100644
index 000000000000..b852ef56fceb
--- /dev/null
+++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
@@ -0,0 +1,1125 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+package com.android.server.am;
+
+import static android.app.ActivityManager.PROCESS_STATE_BACKUP;
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_RECENT;
+import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
+import static android.app.ActivityManager.PROCESS_STATE_HOME;
+import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
+import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT;
+import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI;
+import static android.app.ActivityManager.PROCESS_STATE_RECEIVER;
+import static android.app.ActivityManager.PROCESS_STATE_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_TOP;
+import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING;
+import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
+import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
+
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
+import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
+import static com.android.server.am.ProcessList.BACKUP_APP_ADJ;
+import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ;
+import static com.android.server.am.ProcessList.FOREGROUND_APP_ADJ;
+import static com.android.server.am.ProcessList.HEAVY_WEIGHT_APP_ADJ;
+import static com.android.server.am.ProcessList.HOME_APP_ADJ;
+import static com.android.server.am.ProcessList.NATIVE_ADJ;
+import static com.android.server.am.ProcessList.PERCEPTIBLE_APP_ADJ;
+import static com.android.server.am.ProcessList.PERCEPTIBLE_LOW_APP_ADJ;
+import static com.android.server.am.ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ;
+import static com.android.server.am.ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ;
+import static com.android.server.am.ProcessList.PERSISTENT_PROC_ADJ;
+import static com.android.server.am.ProcessList.PERSISTENT_SERVICE_ADJ;
+import static com.android.server.am.ProcessList.PREVIOUS_APP_ADJ;
+import static com.android.server.am.ProcessList.SCHED_GROUP_BACKGROUND;
+import static com.android.server.am.ProcessList.SERVICE_ADJ;
+import static com.android.server.am.ProcessList.SERVICE_B_ADJ;
+import static com.android.server.am.ProcessList.SYSTEM_ADJ;
+import static com.android.server.am.ProcessList.UNKNOWN_ADJ;
+import static com.android.server.am.ProcessList.VISIBLE_APP_ADJ;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal.OomAdjReason;
+import android.content.pm.ServiceInfo;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.ServiceThread;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.function.Consumer;
+
+/**
+ * A modern implementation of the oom adjuster.
+ */
+public class OomAdjusterModernImpl extends OomAdjuster {
+ static final String TAG = "OomAdjusterModernImpl";
+
+ // The ADJ_SLOT_INVALID is NOT an actual slot.
+ static final int ADJ_SLOT_INVALID = -1;
+ static final int ADJ_SLOT_NATIVE = 0;
+ static final int ADJ_SLOT_SYSTEM = 1;
+ static final int ADJ_SLOT_PERSISTENT_PROC = 2;
+ static final int ADJ_SLOT_PERSISTENT_SERVICE = 3;
+ static final int ADJ_SLOT_FOREGROUND_APP = 4;
+ static final int ADJ_SLOT_PERCEPTIBLE_RECENT_FOREGROUND_APP = 5;
+ static final int ADJ_SLOT_VISIBLE_APP = 6;
+ static final int ADJ_SLOT_PERCEPTIBLE_APP = 7;
+ static final int ADJ_SLOT_PERCEPTIBLE_MEDIUM_APP = 8;
+ static final int ADJ_SLOT_PERCEPTIBLE_LOW_APP = 9;
+ static final int ADJ_SLOT_BACKUP_APP = 10;
+ static final int ADJ_SLOT_HEAVY_WEIGHT_APP = 11;
+ static final int ADJ_SLOT_SERVICE = 12;
+ static final int ADJ_SLOT_HOME_APP = 13;
+ static final int ADJ_SLOT_PREVIOUS_APP = 14;
+ static final int ADJ_SLOT_SERVICE_B = 15;
+ static final int ADJ_SLOT_CACHED_APP = 16;
+ static final int ADJ_SLOT_UNKNOWN = 17;
+
+ @IntDef(prefix = { "ADJ_SLOT_" }, value = {
+ ADJ_SLOT_INVALID,
+ ADJ_SLOT_NATIVE,
+ ADJ_SLOT_SYSTEM,
+ ADJ_SLOT_PERSISTENT_PROC,
+ ADJ_SLOT_PERSISTENT_SERVICE,
+ ADJ_SLOT_FOREGROUND_APP,
+ ADJ_SLOT_PERCEPTIBLE_RECENT_FOREGROUND_APP,
+ ADJ_SLOT_VISIBLE_APP,
+ ADJ_SLOT_PERCEPTIBLE_APP,
+ ADJ_SLOT_PERCEPTIBLE_MEDIUM_APP,
+ ADJ_SLOT_PERCEPTIBLE_LOW_APP,
+ ADJ_SLOT_BACKUP_APP,
+ ADJ_SLOT_HEAVY_WEIGHT_APP,
+ ADJ_SLOT_SERVICE,
+ ADJ_SLOT_HOME_APP,
+ ADJ_SLOT_PREVIOUS_APP,
+ ADJ_SLOT_SERVICE_B,
+ ADJ_SLOT_CACHED_APP,
+ ADJ_SLOT_UNKNOWN,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface AdjSlot{}
+
+ static final int[] ADJ_SLOT_VALUES = new int[] {
+ NATIVE_ADJ,
+ SYSTEM_ADJ,
+ PERSISTENT_PROC_ADJ,
+ PERSISTENT_SERVICE_ADJ,
+ FOREGROUND_APP_ADJ,
+ PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ,
+ VISIBLE_APP_ADJ,
+ PERCEPTIBLE_APP_ADJ,
+ PERCEPTIBLE_MEDIUM_APP_ADJ,
+ PERCEPTIBLE_LOW_APP_ADJ,
+ BACKUP_APP_ADJ,
+ HEAVY_WEIGHT_APP_ADJ,
+ SERVICE_ADJ,
+ HOME_APP_ADJ,
+ PREVIOUS_APP_ADJ,
+ SERVICE_B_ADJ,
+ CACHED_APP_MIN_ADJ,
+ UNKNOWN_ADJ,
+ };
+
+ /**
+ * Note: Always use the raw adj to call this API.
+ */
+ static @AdjSlot int adjToSlot(int adj) {
+ if (adj >= ADJ_SLOT_VALUES[0] && adj <= ADJ_SLOT_VALUES[ADJ_SLOT_VALUES.length - 1]) {
+ // Conduct a binary search, in most of the cases it'll get a hit.
+ final int index = Arrays.binarySearch(ADJ_SLOT_VALUES, adj);
+ if (index >= 0) {
+ return index;
+ }
+ // If not found, the returned index above should be (-(insertion point) - 1),
+ // let's return the first slot that's less than the adj value.
+ return -(index + 1) - 1;
+ }
+ return ADJ_SLOT_VALUES.length - 1;
+ }
+
+ static final int[] PROC_STATE_SLOTS = new int[] {
+ PROCESS_STATE_PERSISTENT, // 0
+ PROCESS_STATE_PERSISTENT_UI,
+ PROCESS_STATE_TOP,
+ PROCESS_STATE_BOUND_TOP,
+ PROCESS_STATE_FOREGROUND_SERVICE,
+ PROCESS_STATE_BOUND_FOREGROUND_SERVICE,
+ PROCESS_STATE_IMPORTANT_FOREGROUND,
+ PROCESS_STATE_IMPORTANT_BACKGROUND,
+ PROCESS_STATE_TRANSIENT_BACKGROUND,
+ PROCESS_STATE_BACKUP,
+ PROCESS_STATE_SERVICE,
+ PROCESS_STATE_RECEIVER,
+ PROCESS_STATE_TOP_SLEEPING,
+ PROCESS_STATE_HEAVY_WEIGHT,
+ PROCESS_STATE_HOME,
+ PROCESS_STATE_LAST_ACTIVITY,
+ PROCESS_STATE_CACHED_ACTIVITY,
+ PROCESS_STATE_CACHED_ACTIVITY_CLIENT,
+ PROCESS_STATE_CACHED_RECENT,
+ PROCESS_STATE_CACHED_EMPTY,
+ PROCESS_STATE_UNKNOWN, // -1
+ };
+
+ static int processStateToSlot(@ActivityManager.ProcessState int state) {
+ if (state >= PROCESS_STATE_PERSISTENT && state <= PROCESS_STATE_CACHED_EMPTY) {
+ return state;
+ }
+ return PROC_STATE_SLOTS.length - 1;
+ }
+
+ /**
+ * A container node in the {@link LinkedProcessRecordList},
+ * holding the references to {@link ProcessRecord}.
+ */
+ static class ProcessRecordNode {
+ static final int NODE_TYPE_PROC_STATE = 0;
+ static final int NODE_TYPE_ADJ = 1;
+
+ @IntDef(prefix = { "NODE_TYPE_" }, value = {
+ NODE_TYPE_PROC_STATE,
+ NODE_TYPE_ADJ,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface NodeType {}
+
+ static final int NUM_NODE_TYPE = NODE_TYPE_ADJ + 1;
+
+ @Nullable ProcessRecordNode mPrev;
+ @Nullable ProcessRecordNode mNext;
+ final @Nullable ProcessRecord mApp;
+
+ ProcessRecordNode(@Nullable ProcessRecord app) {
+ mApp = app;
+ }
+
+ void unlink() {
+ if (mPrev != null) {
+ mPrev.mNext = mNext;
+ }
+ if (mNext != null) {
+ mNext.mPrev = mPrev;
+ }
+ mPrev = mNext = null;
+ }
+
+ boolean isLinked() {
+ return mPrev != null && mNext != null;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("ProcessRecordNode{");
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ sb.append(' ');
+ sb.append(mApp);
+ sb.append(' ');
+ sb.append(mApp != null ? mApp.mState.getCurProcState() : PROCESS_STATE_UNKNOWN);
+ sb.append(' ');
+ sb.append(mApp != null ? mApp.mState.getCurAdj() : UNKNOWN_ADJ);
+ sb.append(' ');
+ sb.append(Integer.toHexString(System.identityHashCode(mPrev)));
+ sb.append(' ');
+ sb.append(Integer.toHexString(System.identityHashCode(mNext)));
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+
+ private class ProcessRecordNodes {
+ private final @ProcessRecordNode.NodeType int mType;
+
+ private final LinkedProcessRecordList[] mProcessRecordNodes;
+ // The last node besides the tail.
+ private final ProcessRecordNode[] mLastNode;
+
+ ProcessRecordNodes(@ProcessRecordNode.NodeType int type, int size) {
+ mType = type;
+ mProcessRecordNodes = new LinkedProcessRecordList[size];
+ for (int i = 0; i < size; i++) {
+ mProcessRecordNodes[i] = new LinkedProcessRecordList(type);
+ }
+ mLastNode = new ProcessRecordNode[size];
+ }
+
+ int size() {
+ return mProcessRecordNodes.length;
+ }
+
+ @VisibleForTesting
+ void reset() {
+ for (int i = 0; i < mProcessRecordNodes.length; i++) {
+ mProcessRecordNodes[i].reset();
+ mLastNode[i] = null;
+ }
+ }
+
+ void resetLastNodes() {
+ for (int i = 0; i < mProcessRecordNodes.length; i++) {
+ mLastNode[i] = mProcessRecordNodes[i].getLastNodeBeforeTail();
+ }
+ }
+
+ void setLastNodeToHead(int slot) {
+ mLastNode[slot] = mProcessRecordNodes[slot].HEAD;
+ }
+
+ void forEachNewNode(int slot, @NonNull Consumer<OomAdjusterArgs> callback) {
+ ProcessRecordNode node = mLastNode[slot].mNext;
+ final ProcessRecordNode tail = mProcessRecordNodes[slot].TAIL;
+ while (node != tail) {
+ mTmpOomAdjusterArgs.mApp = node.mApp;
+ // Save the next before calling callback, since that may change the node.mNext.
+ final ProcessRecordNode next = node.mNext;
+ callback.accept(mTmpOomAdjusterArgs);
+ // There are couple of cases:
+ // a) The current node is moved to another slot
+ // - for this case, we'd need to keep using the "next" node.
+ // b) There are one or more new nodes being appended to this slot
+ // - for this case, we'd need to make sure we scan the new node too.
+ // Based on the assumption that case a) is only possible with
+ // the computeInitialOomAdjLSP(), where the movings are for single node only,
+ // we may safely assume that, if the "next" used to be the "tail" here, and it's
+ // now a new tail somewhere else, that's case a); otherwise, it's case b);
+ node = next == tail && node.mNext != null && node.mNext.mNext != null
+ ? node.mNext : next;
+ }
+ }
+
+ int getNumberOfSlots() {
+ return mProcessRecordNodes.length;
+ }
+
+ void moveAppTo(@NonNull ProcessRecord app, int prevSlot, int newSlot) {
+ final ProcessRecordNode node = app.mLinkedNodes[mType];
+ if (prevSlot != ADJ_SLOT_INVALID) {
+ if (mLastNode[prevSlot] == node) {
+ mLastNode[prevSlot] = node.mPrev;
+ }
+ node.unlink();
+ }
+ mProcessRecordNodes[newSlot].append(node);
+ }
+
+ void moveAllNodesTo(int fromSlot, int toSlot) {
+ final LinkedProcessRecordList fromList = mProcessRecordNodes[fromSlot];
+ final LinkedProcessRecordList toList = mProcessRecordNodes[toSlot];
+ if (fromSlot != toSlot && fromList.HEAD.mNext != fromList.TAIL) {
+ fromList.moveTo(toList);
+ mLastNode[fromSlot] = fromList.getLastNodeBeforeTail();
+ }
+ }
+
+ void moveAppToTail(ProcessRecord app) {
+ final ProcessRecordNode node = app.mLinkedNodes[mType];
+ int slot;
+ switch (mType) {
+ case ProcessRecordNode.NODE_TYPE_PROC_STATE:
+ slot = processStateToSlot(app.mState.getCurProcState());
+ if (mLastNode[slot] == node) {
+ mLastNode[slot] = node.mPrev;
+ }
+ mProcessRecordNodes[slot].moveNodeToTail(node);
+ break;
+ case ProcessRecordNode.NODE_TYPE_ADJ:
+ slot = adjToSlot(app.mState.getCurRawAdj());
+ if (mLastNode[slot] == node) {
+ mLastNode[slot] = node.mPrev;
+ }
+ mProcessRecordNodes[slot].moveNodeToTail(node);
+ break;
+ default:
+ return;
+ }
+
+ }
+
+ void reset(int slot) {
+ mProcessRecordNodes[slot].reset();
+ }
+
+ void unlink(@NonNull ProcessRecord app) {
+ final ProcessRecordNode node = app.mLinkedNodes[mType];
+ final int slot = getCurrentSlot(app);
+ if (slot != ADJ_SLOT_INVALID) {
+ if (mLastNode[slot] == node) {
+ mLastNode[slot] = node.mPrev;
+ }
+ }
+ node.unlink();
+ }
+
+ void append(@NonNull ProcessRecord app) {
+ append(app, getCurrentSlot(app));
+ }
+
+ void append(@NonNull ProcessRecord app, int targetSlot) {
+ final ProcessRecordNode node = app.mLinkedNodes[mType];
+ mProcessRecordNodes[targetSlot].append(node);
+ }
+
+ private int getCurrentSlot(@NonNull ProcessRecord app) {
+ switch (mType) {
+ case ProcessRecordNode.NODE_TYPE_PROC_STATE:
+ return processStateToSlot(app.mState.getCurProcState());
+ case ProcessRecordNode.NODE_TYPE_ADJ:
+ return adjToSlot(app.mState.getCurRawAdj());
+ }
+ return ADJ_SLOT_INVALID;
+ }
+
+ String toString(int slot, int logUid) {
+ return "lastNode=" + mLastNode[slot] + " " + mProcessRecordNodes[slot].toString(logUid);
+ }
+
+ /**
+ * A simple version of {@link java.util.LinkedList}, as here we don't allocate new node
+ * while adding an object to it.
+ */
+ private static class LinkedProcessRecordList {
+ // Sentinel head/tail, to make bookkeeping work easier.
+ final ProcessRecordNode HEAD = new ProcessRecordNode(null);
+ final ProcessRecordNode TAIL = new ProcessRecordNode(null);
+ final @ProcessRecordNode.NodeType int mNodeType;
+
+ LinkedProcessRecordList(@ProcessRecordNode.NodeType int nodeType) {
+ HEAD.mNext = TAIL;
+ TAIL.mPrev = HEAD;
+ mNodeType = nodeType;
+ }
+
+ void append(@NonNull ProcessRecordNode node) {
+ node.mNext = TAIL;
+ node.mPrev = TAIL.mPrev;
+ TAIL.mPrev.mNext = node;
+ TAIL.mPrev = node;
+ }
+
+ void moveTo(@NonNull LinkedProcessRecordList toList) {
+ if (HEAD.mNext != TAIL) {
+ toList.TAIL.mPrev.mNext = HEAD.mNext;
+ HEAD.mNext.mPrev = toList.TAIL.mPrev;
+ toList.TAIL.mPrev = TAIL.mPrev;
+ TAIL.mPrev.mNext = toList.TAIL;
+ HEAD.mNext = TAIL;
+ TAIL.mPrev = HEAD;
+ }
+ }
+
+ void moveNodeToTail(@NonNull ProcessRecordNode node) {
+ node.unlink();
+ append(node);
+ }
+
+ @NonNull ProcessRecordNode getLastNodeBeforeTail() {
+ return TAIL.mPrev;
+ }
+
+ @VisibleForTesting
+ void reset() {
+ HEAD.mNext = TAIL;
+ TAIL.mPrev = HEAD;
+ }
+
+ String toString(int logUid) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("LinkedProcessRecordList{");
+ sb.append(HEAD);
+ sb.append(' ');
+ sb.append(TAIL);
+ sb.append('[');
+ ProcessRecordNode node = HEAD.mNext;
+ while (node != TAIL) {
+ if (node.mApp != null && node.mApp.uid == logUid) {
+ sb.append(node);
+ sb.append(',');
+ }
+ node = node.mNext;
+ }
+ sb.append(']');
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+ }
+
+ /**
+ * A data class for holding the parameters in computing oom adj.
+ */
+ private class OomAdjusterArgs {
+ ProcessRecord mApp;
+ ProcessRecord mTopApp;
+ long mNow;
+ int mCachedAdj;
+ @OomAdjReason int mOomAdjReason;
+ @NonNull ActiveUids mUids;
+ boolean mFullUpdate;
+
+ void update(ProcessRecord topApp, long now, int cachedAdj,
+ @OomAdjReason int oomAdjReason, @NonNull ActiveUids uids, boolean fullUpdate) {
+ mTopApp = topApp;
+ mNow = now;
+ mCachedAdj = cachedAdj;
+ mOomAdjReason = oomAdjReason;
+ mUids = uids;
+ mFullUpdate = fullUpdate;
+ }
+ }
+
+ OomAdjusterModernImpl(ActivityManagerService service, ProcessList processList,
+ ActiveUids activeUids) {
+ this(service, processList, activeUids, createAdjusterThread());
+ }
+
+ OomAdjusterModernImpl(ActivityManagerService service, ProcessList processList,
+ ActiveUids activeUids, ServiceThread adjusterThread) {
+ super(service, processList, activeUids, adjusterThread);
+ }
+
+ private final ProcessRecordNodes mProcessRecordProcStateNodes = new ProcessRecordNodes(
+ ProcessRecordNode.NODE_TYPE_PROC_STATE, PROC_STATE_SLOTS.length);
+ private final ProcessRecordNodes mProcessRecordAdjNodes = new ProcessRecordNodes(
+ ProcessRecordNode.NODE_TYPE_ADJ, ADJ_SLOT_VALUES.length);
+ private final OomAdjusterArgs mTmpOomAdjusterArgs = new OomAdjusterArgs();
+
+ void linkProcessRecordToList(@NonNull ProcessRecord app) {
+ mProcessRecordProcStateNodes.append(app);
+ mProcessRecordAdjNodes.append(app);
+ }
+
+ void unlinkProcessRecordFromList(@NonNull ProcessRecord app) {
+ mProcessRecordProcStateNodes.unlink(app);
+ mProcessRecordAdjNodes.unlink(app);
+ }
+
+ @Override
+ @VisibleForTesting
+ void resetInternal() {
+ mProcessRecordProcStateNodes.reset();
+ mProcessRecordAdjNodes.reset();
+ }
+
+ @GuardedBy("mService")
+ @Override
+ void onProcessBeginLocked(@NonNull ProcessRecord app) {
+ // Check one type should be good enough.
+ if (app.mLinkedNodes[ProcessRecordNode.NODE_TYPE_PROC_STATE] == null) {
+ for (int i = 0; i < app.mLinkedNodes.length; i++) {
+ app.mLinkedNodes[i] = new ProcessRecordNode(app);
+ }
+ }
+ if (!app.mLinkedNodes[ProcessRecordNode.NODE_TYPE_PROC_STATE].isLinked()) {
+ linkProcessRecordToList(app);
+ }
+ }
+
+ @GuardedBy("mService")
+ @Override
+ void onProcessEndLocked(@NonNull ProcessRecord app) {
+ if (app.mLinkedNodes[ProcessRecordNode.NODE_TYPE_PROC_STATE] != null
+ && app.mLinkedNodes[ProcessRecordNode.NODE_TYPE_PROC_STATE].isLinked()) {
+ unlinkProcessRecordFromList(app);
+ }
+ }
+
+ @GuardedBy("mService")
+ @Override
+ void onProcessStateChanged(@NonNull ProcessRecord app, int prevProcState) {
+ updateProcStateSlotIfNecessary(app, prevProcState);
+ }
+
+ @GuardedBy("mService")
+ void onProcessOomAdjChanged(@NonNull ProcessRecord app, int prevAdj) {
+ updateAdjSlotIfNecessary(app, prevAdj);
+ }
+
+ @GuardedBy("mService")
+ @Override
+ protected int getInitialAdj(@NonNull ProcessRecord app) {
+ return UNKNOWN_ADJ;
+ }
+
+ @GuardedBy("mService")
+ @Override
+ protected int getInitialProcState(@NonNull ProcessRecord app) {
+ return PROCESS_STATE_UNKNOWN;
+ }
+
+ @GuardedBy("mService")
+ @Override
+ protected int getInitialCapability(@NonNull ProcessRecord app) {
+ return 0;
+ }
+
+ @GuardedBy("mService")
+ @Override
+ protected boolean getInitialIsCurBoundByNonBgRestrictedApp(@NonNull ProcessRecord app) {
+ return false;
+ }
+
+ private void updateAdjSlotIfNecessary(ProcessRecord app, int prevRawAdj) {
+ if (app.mState.getCurRawAdj() != prevRawAdj) {
+ final int slot = adjToSlot(app.mState.getCurRawAdj());
+ final int prevSlot = adjToSlot(prevRawAdj);
+ if (slot != prevSlot && slot != ADJ_SLOT_INVALID) {
+ mProcessRecordAdjNodes.moveAppTo(app, prevSlot, slot);
+ }
+ }
+ }
+
+ private void updateProcStateSlotIfNecessary(ProcessRecord app, int prevProcState) {
+ if (app.mState.getCurProcState() != prevProcState) {
+ final int slot = processStateToSlot(app.mState.getCurProcState());
+ final int prevSlot = processStateToSlot(prevProcState);
+ if (slot != prevSlot) {
+ mProcessRecordProcStateNodes.moveAppTo(app, prevSlot, slot);
+ }
+ }
+ }
+
+ @Override
+ protected boolean performUpdateOomAdjLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) {
+ final ProcessRecord topApp = mService.getTopApp();
+
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
+ mService.mOomAdjProfiler.oomAdjStarted();
+ mAdjSeq++;
+
+ final ProcessStateRecord state = app.mState;
+ final int oldAdj = state.getCurRawAdj();
+ final int cachedAdj = oldAdj >= CACHED_APP_MIN_ADJ
+ ? oldAdj : UNKNOWN_ADJ;
+
+ final ActiveUids uids = mTmpUidRecords;
+ final ArraySet<ProcessRecord> targetProcesses = mTmpProcessSet;
+ final ArrayList<ProcessRecord> reachableProcesses = mTmpProcessList;
+ final long now = SystemClock.uptimeMillis();
+ final long nowElapsed = SystemClock.elapsedRealtime();
+
+ uids.clear();
+ targetProcesses.clear();
+ targetProcesses.add(app);
+ reachableProcesses.clear();
+
+ // Find out all reachable processes from this app.
+ collectReachableProcessesLocked(targetProcesses, reachableProcesses, uids);
+
+ // Copy all of the reachable processes into the target process set.
+ targetProcesses.addAll(reachableProcesses);
+ reachableProcesses.clear();
+
+ final boolean result = performNewUpdateOomAdjLSP(oomAdjReason,
+ topApp, targetProcesses, uids, false, now, cachedAdj);
+
+ reachableProcesses.addAll(targetProcesses);
+ assignCachedAdjIfNecessary(reachableProcesses);
+ for (int i = uids.size() - 1; i >= 0; i--) {
+ final UidRecord uidRec = uids.valueAt(i);
+ uidRec.forEachProcess(this::updateAppUidRecIfNecessaryLSP);
+ }
+ updateUidsLSP(uids, nowElapsed);
+ for (int i = 0, size = targetProcesses.size(); i < size; i++) {
+ applyOomAdjLSP(targetProcesses.valueAt(i), false, now, nowElapsed, oomAdjReason);
+ }
+ targetProcesses.clear();
+ reachableProcesses.clear();
+
+ mService.mOomAdjProfiler.oomAdjEnded();
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ return result;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ @Override
+ protected void updateOomAdjInnerLSP(@OomAdjReason int oomAdjReason, final ProcessRecord topApp,
+ ArrayList<ProcessRecord> processes, ActiveUids uids, boolean potentialCycles,
+ boolean startProfiling) {
+ final boolean fullUpdate = processes == null;
+ final ArrayList<ProcessRecord> activeProcesses = fullUpdate
+ ? mProcessList.getLruProcessesLOSP() : processes;
+ ActiveUids activeUids = uids;
+ if (activeUids == null) {
+ final int numUids = mActiveUids.size();
+ activeUids = mTmpUidRecords;
+ activeUids.clear();
+ for (int i = 0; i < numUids; i++) {
+ UidRecord uidRec = mActiveUids.valueAt(i);
+ activeUids.put(uidRec.getUid(), uidRec);
+ }
+ }
+
+ if (startProfiling) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
+ mService.mOomAdjProfiler.oomAdjStarted();
+ }
+ final long now = SystemClock.uptimeMillis();
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ final long oldTime = now - mConstants.mMaxEmptyTimeMillis;
+ final int numProc = activeProcesses.size();
+
+ mAdjSeq++;
+ if (fullUpdate) {
+ mNewNumServiceProcs = 0;
+ mNewNumAServiceProcs = 0;
+ }
+
+ final ArraySet<ProcessRecord> targetProcesses = mTmpProcessSet;
+ targetProcesses.clear();
+ if (!fullUpdate) {
+ targetProcesses.addAll(activeProcesses);
+ }
+
+ performNewUpdateOomAdjLSP(oomAdjReason, topApp, targetProcesses, activeUids,
+ fullUpdate, now, UNKNOWN_ADJ);
+
+ if (fullUpdate) {
+ assignCachedAdjIfNecessary(mProcessList.getLruProcessesLOSP());
+ postUpdateOomAdjInnerLSP(oomAdjReason, activeUids, now, nowElapsed, oldTime);
+ } else {
+ activeProcesses.clear();
+ activeProcesses.addAll(targetProcesses);
+ assignCachedAdjIfNecessary(activeProcesses);
+
+ for (int i = activeUids.size() - 1; i >= 0; i--) {
+ final UidRecord uidRec = activeUids.valueAt(i);
+ uidRec.forEachProcess(this::updateAppUidRecIfNecessaryLSP);
+ }
+ updateUidsLSP(activeUids, nowElapsed);
+
+ for (int i = 0, size = targetProcesses.size(); i < size; i++) {
+ applyOomAdjLSP(targetProcesses.valueAt(i), false, now, nowElapsed, oomAdjReason);
+ }
+
+ activeProcesses.clear();
+ }
+ targetProcesses.clear();
+
+ if (startProfiling) {
+ mService.mOomAdjProfiler.oomAdjEnded();
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+ return;
+ }
+
+ /**
+ * Perform the oom adj update on the given {@code targetProcesses}.
+ *
+ * <p>Note: The expectation to the given {@code targetProcesses} is, the caller
+ * must have called {@link collectReachableProcessesLocked} on it.
+ */
+ private boolean performNewUpdateOomAdjLSP(@OomAdjReason int oomAdjReason,
+ ProcessRecord topApp, ArraySet<ProcessRecord> targetProcesses, ActiveUids uids,
+ boolean fullUpdate, long now, int cachedAdj) {
+
+ final ArrayList<ProcessRecord> clientProcesses = mTmpProcessList2;
+ clientProcesses.clear();
+
+ // We'll need to collect the upstream processes of the target apps here, because those
+ // processes would potentially impact the procstate/adj via bindings.
+ if (!fullUpdate) {
+ final boolean containsCycle = collectReversedReachableProcessesLocked(targetProcesses,
+ clientProcesses);
+
+ // If any of its upstream processes are in a cycle,
+ // move them into the candidate targets.
+ if (containsCycle) {
+ // Add all client apps to the target process list.
+ for (int i = 0, size = clientProcesses.size(); i < size; i++) {
+ final ProcessRecord client = clientProcesses.get(i);
+ final UidRecord uidRec = client.getUidRecord();
+ targetProcesses.add(client);
+ if (uidRec != null) {
+ uids.put(uidRec.getUid(), uidRec);
+ }
+ }
+ clientProcesses.clear();
+ }
+ for (int i = 0, size = targetProcesses.size(); i < size; i++) {
+ final ProcessRecord app = targetProcesses.valueAt(i);
+ app.mState.resetCachedInfo();
+ final UidRecord uidRec = app.getUidRecord();
+ if (uidRec != null) {
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "Starting update of " + uidRec);
+ }
+ uidRec.reset();
+ }
+ }
+ } else {
+ final ArrayList<ProcessRecord> lru = mProcessList.getLruProcessesLOSP();
+ for (int i = 0, size = lru.size(); i < size; i++) {
+ final ProcessRecord app = lru.get(i);
+ app.mState.resetCachedInfo();
+ final UidRecord uidRec = app.getUidRecord();
+ if (uidRec != null) {
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "Starting update of " + uidRec);
+ }
+ uidRec.reset();
+ }
+ }
+ }
+
+ updateNewOomAdjInnerLSP(oomAdjReason, topApp, targetProcesses, clientProcesses, uids,
+ cachedAdj, now, fullUpdate);
+
+ clientProcesses.clear();
+
+ return true;
+ }
+
+ /**
+ * Collect the reversed reachable processes from the given {@code apps}, the result will be
+ * returned in the given {@code processes}, which will <em>NOT</em> include the processes from
+ * the given {@code apps}.
+ */
+ @GuardedBy("mService")
+ private boolean collectReversedReachableProcessesLocked(ArraySet<ProcessRecord> apps,
+ ArrayList<ProcessRecord> clientProcesses) {
+ final ArrayDeque<ProcessRecord> queue = mTmpQueue;
+ queue.clear();
+ clientProcesses.clear();
+ for (int i = 0, size = apps.size(); i < size; i++) {
+ final ProcessRecord app = apps.valueAt(i);
+ app.mState.setReachable(true);
+ app.mState.setReversedReachable(true);
+ queue.offer(app);
+ }
+
+ // Track if any of them reachables could include a cycle
+ boolean containsCycle = false;
+
+ // Scan upstreams of the process record
+ for (ProcessRecord pr = queue.poll(); pr != null; pr = queue.poll()) {
+ if (!pr.mState.isReachable()) {
+ // If not in the given initial set of apps, add it.
+ clientProcesses.add(pr);
+ }
+ final ProcessServiceRecord psr = pr.mServices;
+ for (int i = psr.numberOfRunningServices() - 1; i >= 0; i--) {
+ final ServiceRecord s = psr.getRunningServiceAt(i);
+ final ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections =
+ s.getConnections();
+ for (int j = serviceConnections.size() - 1; j >= 0; j--) {
+ final ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(j);
+ for (int k = clist.size() - 1; k >= 0; k--) {
+ final ConnectionRecord cr = clist.get(k);
+ final ProcessRecord client = cr.binding.client;
+ containsCycle |= client.mState.isReversedReachable();
+ if (client.mState.isReversedReachable()) {
+ continue;
+ }
+ queue.offer(client);
+ client.mState.setReversedReachable(true);
+ }
+ }
+ }
+ final ProcessProviderRecord ppr = pr.mProviders;
+ for (int i = ppr.numberOfProviders() - 1; i >= 0; i--) {
+ final ContentProviderRecord cpr = ppr.getProviderAt(i);
+ for (int j = cpr.connections.size() - 1; j >= 0; j--) {
+ final ContentProviderConnection conn = cpr.connections.get(j);
+ final ProcessRecord client = conn.client;
+ containsCycle |= client.mState.isReversedReachable();
+ if (client.mState.isReversedReachable()) {
+ continue;
+ }
+ queue.offer(client);
+ client.mState.setReversedReachable(true);
+ }
+ }
+ // If this process is a sandbox itself, also add the app on whose behalf
+ // its running
+ if (pr.isSdkSandbox) {
+ for (int is = psr.numberOfRunningServices() - 1; is >= 0; is--) {
+ ServiceRecord s = psr.getRunningServiceAt(is);
+ ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections =
+ s.getConnections();
+ for (int conni = serviceConnections.size() - 1; conni >= 0; conni--) {
+ ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(conni);
+ for (int i = clist.size() - 1; i >= 0; i--) {
+ ConnectionRecord cr = clist.get(i);
+ ProcessRecord attributedApp = cr.binding.attributedClient;
+ if (attributedApp == null || attributedApp == pr) {
+ continue;
+ }
+ containsCycle |= attributedApp.mState.isReversedReachable();
+ if (attributedApp.mState.isReversedReachable()) {
+ continue;
+ }
+ queue.offer(attributedApp);
+ attributedApp.mState.setReversedReachable(true);
+ }
+ }
+ }
+ }
+ }
+
+ // Reset the temporary bits.
+ for (int i = clientProcesses.size() - 1; i >= 0; i--) {
+ clientProcesses.get(i).mState.setReversedReachable(false);
+ }
+ for (int i = 0, size = apps.size(); i < size; i++) {
+ final ProcessRecord app = apps.valueAt(i);
+ app.mState.setReachable(false);
+ app.mState.setReversedReachable(false);
+ }
+ return containsCycle;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ private void updateNewOomAdjInnerLSP(@OomAdjReason int oomAdjReason, final ProcessRecord topApp,
+ ArraySet<ProcessRecord> targetProcesses, ArrayList<ProcessRecord> clientProcesses,
+ ActiveUids uids, int cachedAdj, long now, boolean fullUpdate) {
+ mTmpOomAdjusterArgs.update(topApp, now, cachedAdj, oomAdjReason, uids, fullUpdate);
+
+ mProcessRecordProcStateNodes.resetLastNodes();
+ mProcessRecordAdjNodes.resetLastNodes();
+
+ final int procStateTarget = mProcessRecordProcStateNodes.size() - 1;
+ final int adjTarget = mProcessRecordAdjNodes.size() - 1;
+
+ final int appUid = !fullUpdate && targetProcesses.size() > 0
+ ? targetProcesses.valueAt(0).uid : -1;
+ final int logUid = mService.mCurOomAdjUid;
+
+ mAdjSeq++;
+ // All apps to be updated will be moved to the lowest slot.
+ if (fullUpdate) {
+ // Move all the process record node to the lowest slot, we'll do recomputation on all of
+ // them. Use the processes from the lru list, because the scanning order matters here.
+ final ArrayList<ProcessRecord> lruList = mProcessList.getLruProcessesLOSP();
+ for (int i = procStateTarget; i >= 0; i--) {
+ mProcessRecordProcStateNodes.reset(i);
+ // Force the last node to the head since we'll recompute all of them.
+ mProcessRecordProcStateNodes.setLastNodeToHead(i);
+ }
+ // enqueue the targets in the reverse order of the lru list.
+ for (int i = lruList.size() - 1; i >= 0; i--) {
+ mProcessRecordProcStateNodes.append(lruList.get(i), procStateTarget);
+ }
+ // Do the same to the adj nodes.
+ for (int i = adjTarget; i >= 0; i--) {
+ mProcessRecordAdjNodes.reset(i);
+ // Force the last node to the head since we'll recompute all of them.
+ mProcessRecordAdjNodes.setLastNodeToHead(i);
+ }
+ for (int i = lruList.size() - 1; i >= 0; i--) {
+ mProcessRecordAdjNodes.append(lruList.get(i), adjTarget);
+ }
+ } else {
+ // Move the target processes to the lowest slot.
+ for (int i = 0, size = targetProcesses.size(); i < size; i++) {
+ final ProcessRecord app = targetProcesses.valueAt(i);
+ final int procStateSlot = processStateToSlot(app.mState.getCurProcState());
+ final int adjSlot = adjToSlot(app.mState.getCurRawAdj());
+ mProcessRecordProcStateNodes.moveAppTo(app, procStateSlot, procStateTarget);
+ mProcessRecordAdjNodes.moveAppTo(app, adjSlot, adjTarget);
+ }
+ // Move the "lastNode" to head to make sure we scan all nodes in this slot.
+ mProcessRecordProcStateNodes.setLastNodeToHead(procStateTarget);
+ mProcessRecordAdjNodes.setLastNodeToHead(adjTarget);
+ }
+
+ // All apps to be updated have been moved to the lowest slot.
+ // Do an initial pass of the computation.
+ mProcessRecordProcStateNodes.forEachNewNode(mProcessRecordProcStateNodes.size() - 1,
+ this::computeInitialOomAdjLSP);
+
+ if (!fullUpdate) {
+ // We didn't update the client processes with the computeInitialOomAdjLSP
+ // because they don't need to do so. But they'll be playing vital roles in
+ // computing the bindings. So include them into the scan list below.
+ for (int i = 0, size = clientProcesses.size(); i < size; i++) {
+ mProcessRecordProcStateNodes.moveAppToTail(clientProcesses.get(i));
+ }
+ // We don't update the adj list since we're resetting it below.
+ }
+
+ // Now nodes are set into their slots, without facting in the bindings.
+ // The nodes between the `lastNode` pointer and the TAIL should be the new nodes.
+ //
+ // The whole rationale here is that, the bindings from client to host app, won't elevate
+ // the host app's procstate/adj higher than the client app's state (BIND_ABOVE_CLIENT
+ // is a special case here, but client app's raw adj is still no less than the host app's).
+ // Therefore, starting from the top to the bottom, for each slot, scan all of the new nodes,
+ // check its bindings, elevate its host app's slot if necessary.
+ //
+ // We'd have to do this in two passes: 1) scan procstate node list; 2) scan adj node list.
+ // Because the procstate and adj are not always in sync - there are cases where
+ // the processes with lower proc state could be getting a higher oom adj score.
+ // And because of this, the procstate and adj node lists are basically two priority heaps.
+ //
+ // As the 2nd pass with the adj node lists potentially includes a significant amount of
+ // duplicated scans as the 1st pass has done, we'll reset the last node pointers for
+ // the adj node list before the 1st pass; so during the 1st pass, if any app's adj slot
+ // gets bumped, we'll only scan those in 2nd pass.
+
+ mProcessRecordAdjNodes.resetLastNodes();
+
+ // 1st pass, scan each slot in the procstate node list.
+ for (int i = 0, end = mProcessRecordProcStateNodes.size() - 1; i < end; i++) {
+ mProcessRecordProcStateNodes.forEachNewNode(i, this::computeHostOomAdjLSP);
+ }
+
+ // 2nd pass, scan each slot in the adj node list.
+ for (int i = 0, end = mProcessRecordAdjNodes.size() - 1; i < end; i++) {
+ mProcessRecordAdjNodes.forEachNewNode(i, this::computeHostOomAdjLSP);
+ }
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ private void computeInitialOomAdjLSP(OomAdjusterArgs args) {
+ final ProcessRecord app = args.mApp;
+ final int cachedAdj = args.mCachedAdj;
+ final ProcessRecord topApp = args.mTopApp;
+ final long now = args.mNow;
+ final int oomAdjReason = args.mOomAdjReason;
+ final ActiveUids uids = args.mUids;
+ final boolean fullUpdate = args.mFullUpdate;
+
+ if (DEBUG_OOM_ADJ) {
+ Slog.i(TAG, "OOM ADJ initial args app=" + app
+ + " cachedAdj=" + cachedAdj
+ + " topApp=" + topApp
+ + " now=" + now
+ + " oomAdjReason=" + oomAdjReasonToString(oomAdjReason)
+ + " fullUpdate=" + fullUpdate);
+ }
+
+ if (uids != null) {
+ final UidRecord uidRec = app.getUidRecord();
+
+ if (uidRec != null) {
+ uids.put(uidRec.getUid(), uidRec);
+ }
+ }
+
+ computeOomAdjLSP(app, cachedAdj, topApp, fullUpdate, now, false, false, oomAdjReason,
+ false);
+ }
+
+ /**
+ * @return The proposed change to the schedGroup.
+ */
+ @GuardedBy({"mService", "mProcLock"})
+ @Override
+ protected int setIntermediateAdjLSP(ProcessRecord app, int adj, int prevRawAppAdj,
+ int schedGroup) {
+ schedGroup = super.setIntermediateAdjLSP(app, adj, prevRawAppAdj, schedGroup);
+
+ updateAdjSlotIfNecessary(app, prevRawAppAdj);
+
+ return schedGroup;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ @Override
+ protected void setIntermediateProcStateLSP(ProcessRecord app, int procState,
+ int prevProcState) {
+ super.setIntermediateProcStateLSP(app, procState, prevProcState);
+
+ updateProcStateSlotIfNecessary(app, prevProcState);
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ private void computeHostOomAdjLSP(OomAdjusterArgs args) {
+ final ProcessRecord app = args.mApp;
+ final int cachedAdj = args.mCachedAdj;
+ final ProcessRecord topApp = args.mTopApp;
+ final long now = args.mNow;
+ final @OomAdjReason int oomAdjReason = args.mOomAdjReason;
+ final boolean fullUpdate = args.mFullUpdate;
+ final ActiveUids uids = args.mUids;
+
+ final ProcessServiceRecord psr = app.mServices;
+ for (int i = psr.numberOfConnections() - 1; i >= 0; i--) {
+ ConnectionRecord cr = psr.getConnectionAt(i);
+ ProcessRecord service = cr.hasFlag(ServiceInfo.FLAG_ISOLATED_PROCESS)
+ ? cr.binding.service.isolationHostProc : cr.binding.service.app;
+ if (service == null || service == app
+ || (service.mState.getMaxAdj() >= SYSTEM_ADJ
+ && service.mState.getMaxAdj() < FOREGROUND_APP_ADJ)
+ || (service.mState.getCurAdj() <= FOREGROUND_APP_ADJ
+ && service.mState.getCurrentSchedulingGroup() > SCHED_GROUP_BACKGROUND
+ && service.mState.getCurProcState() <= PROCESS_STATE_TOP)) {
+ continue;
+ }
+
+
+ computeServiceHostOomAdjLSP(cr, service, app, now, topApp, fullUpdate, false, false,
+ oomAdjReason, cachedAdj, false);
+ }
+
+ for (int i = psr.numberOfSdkSandboxConnections() - 1; i >= 0; i--) {
+ final ConnectionRecord cr = psr.getSdkSandboxConnectionAt(i);
+ final ProcessRecord service = cr.binding.service.app;
+ if (service == null || service == app
+ || (service.mState.getMaxAdj() >= SYSTEM_ADJ
+ && service.mState.getMaxAdj() < FOREGROUND_APP_ADJ)
+ || (service.mState.getCurAdj() <= FOREGROUND_APP_ADJ
+ && service.mState.getCurrentSchedulingGroup() > SCHED_GROUP_BACKGROUND
+ && service.mState.getCurProcState() <= PROCESS_STATE_TOP)) {
+ continue;
+ }
+
+ computeServiceHostOomAdjLSP(cr, service, app, now, topApp, fullUpdate, false, false,
+ oomAdjReason, cachedAdj, false);
+ }
+
+ final ProcessProviderRecord ppr = app.mProviders;
+ for (int i = ppr.numberOfProviderConnections() - 1; i >= 0; i--) {
+ ContentProviderConnection cpc = ppr.getProviderConnectionAt(i);
+ ProcessRecord provider = cpc.provider.proc;
+ if (provider == null || provider == app
+ || (provider.mState.getMaxAdj() >= ProcessList.SYSTEM_ADJ
+ && provider.mState.getMaxAdj() < FOREGROUND_APP_ADJ)
+ || (provider.mState.getCurAdj() <= FOREGROUND_APP_ADJ
+ && provider.mState.getCurrentSchedulingGroup() > SCHED_GROUP_BACKGROUND
+ && provider.mState.getCurProcState() <= PROCESS_STATE_TOP)) {
+ continue;
+ }
+
+ computeProviderHostOomAdjLSP(cpc, provider, app, now, topApp, fullUpdate, false, false,
+ oomAdjReason, cachedAdj, false);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index f532122c10d9..7037fecb2713 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -22,6 +22,7 @@ import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityManagerService.MY_PID;
+import static com.android.server.am.OomAdjusterModernImpl.ProcessRecordNode.NUM_NODE_TYPE;
import static java.util.Objects.requireNonNull;
@@ -63,6 +64,7 @@ import com.android.internal.app.procstats.ProcessState;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.os.Zygote;
import com.android.server.FgThread;
+import com.android.server.am.OomAdjusterModernImpl.ProcessRecordNode;
import com.android.server.wm.WindowProcessController;
import com.android.server.wm.WindowProcessListener;
@@ -434,6 +436,8 @@ class ProcessRecord implements WindowProcessListener {
*/
volatile boolean mSkipProcessGroupCreation;
+ final ProcessRecordNode[] mLinkedNodes = new ProcessRecordNode[NUM_NODE_TYPE];
+
void setStartParams(int startUid, HostingRecord hostingRecord, String seInfo,
long startUptime, long startElapsedTime) {
this.mStartUid = startUid;
@@ -1114,6 +1118,7 @@ class ProcessRecord implements WindowProcessListener {
mState.onCleanupApplicationRecordLSP();
mServices.onCleanupApplicationRecordLocked();
mReceivers.onCleanupApplicationRecordLocked();
+ mService.mOomAdjuster.onProcessEndLocked(this);
return mProviders.onCleanupApplicationRecordLocked(allowRestart);
}
diff --git a/services/core/java/com/android/server/am/ProcessServiceRecord.java b/services/core/java/com/android/server/am/ProcessServiceRecord.java
index 7ff6d116baaf..a165e8897aa4 100644
--- a/services/core/java/com/android/server/am/ProcessServiceRecord.java
+++ b/services/core/java/com/android/server/am/ProcessServiceRecord.java
@@ -19,6 +19,7 @@ package com.android.server.am;
import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_BOUND_SERVICE;
import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_FOREGROUND_SERVICE;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ServiceInfo;
@@ -134,6 +135,11 @@ final class ProcessServiceRecord {
private final ArraySet<ConnectionRecord> mConnections = new ArraySet<>();
/**
+ * All ConnectionRecord this process holds indirectly to SDK sandbox processes.
+ */
+ private @Nullable ArraySet<ConnectionRecord> mSdkSandboxConnections;
+
+ /**
* A set of UIDs of all bound clients.
*/
private ArraySet<Integer> mBoundClientUids = new ArraySet<>();
@@ -490,13 +496,18 @@ final class ProcessServiceRecord {
void addConnection(ConnectionRecord connection) {
mConnections.add(connection);
+ addSdkSandboxConnectionIfNecessary(connection);
}
void removeConnection(ConnectionRecord connection) {
mConnections.remove(connection);
+ removeSdkSandboxConnectionIfNecessary(connection);
}
void removeAllConnections() {
+ for (int i = 0, size = mConnections.size(); i < size; i++) {
+ removeSdkSandboxConnectionIfNecessary(mConnections.valueAt(i));
+ }
mConnections.clear();
}
@@ -508,6 +519,39 @@ final class ProcessServiceRecord {
return mConnections.size();
}
+ private void addSdkSandboxConnectionIfNecessary(ConnectionRecord connection) {
+ final ProcessRecord attributedClient = connection.binding.attributedClient;
+ if (attributedClient != null && connection.binding.service.isSdkSandbox) {
+ if (attributedClient.mServices.mSdkSandboxConnections == null) {
+ attributedClient.mServices.mSdkSandboxConnections = new ArraySet<>();
+ }
+ attributedClient.mServices.mSdkSandboxConnections.add(connection);
+ }
+ }
+
+ private void removeSdkSandboxConnectionIfNecessary(ConnectionRecord connection) {
+ final ProcessRecord attributedClient = connection.binding.attributedClient;
+ if (attributedClient != null && connection.binding.service.isSdkSandbox) {
+ if (attributedClient.mServices.mSdkSandboxConnections == null) {
+ attributedClient.mServices.mSdkSandboxConnections.remove(connection);
+ }
+ }
+ }
+
+ void removeAllSdkSandboxConnections() {
+ if (mSdkSandboxConnections != null) {
+ mSdkSandboxConnections.clear();
+ }
+ }
+
+ ConnectionRecord getSdkSandboxConnectionAt(int index) {
+ return mSdkSandboxConnections != null ? mSdkSandboxConnections.valueAt(index) : null;
+ }
+
+ int numberOfSdkSandboxConnections() {
+ return mSdkSandboxConnections != null ? mSdkSandboxConnections.size() : 0;
+ }
+
void addBoundClientUid(int clientUid, String clientPackageName, long bindFlags) {
mBoundClientUids.add(clientUid);
mApp.getWindowProcessController()
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index db341d253818..a9c388c232ed 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -378,6 +378,12 @@ final class ProcessStateRecord {
private boolean mReachable;
/**
+ * Whether or not this process is reversed reachable from given process.
+ */
+ @GuardedBy("mService")
+ private boolean mReversedReachable;
+
+ /**
* The most recent time when the last visible activity within this process became invisible.
*
* <p> It'll be set to 0 if there is never a visible activity, or Long.MAX_VALUE if there is
@@ -454,6 +460,9 @@ final class ProcessStateRecord {
@GuardedBy("mService")
private int mCachedSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+ @GuardedBy("mService")
+ private boolean mScheduleLikeTopApp = false;
+
ProcessStateRecord(ProcessRecord app) {
mApp = app;
mService = app.mService;
@@ -614,9 +623,11 @@ final class ProcessStateRecord {
void forceProcessStateUpTo(int newState) {
if (mRepProcState > newState) {
synchronized (mProcLock) {
+ final int prevProcState = mRepProcState;
setReportedProcState(newState);
setCurProcState(newState);
setCurRawProcState(newState);
+ mService.mOomAdjuster.onProcessStateChanged(mApp, prevProcState);
}
}
}
@@ -985,6 +996,16 @@ final class ProcessStateRecord {
}
@GuardedBy("mService")
+ boolean isReversedReachable() {
+ return mReversedReachable;
+ }
+
+ @GuardedBy("mService")
+ void setReversedReachable(boolean reversedReachable) {
+ mReversedReachable = reversedReachable;
+ }
+
+ @GuardedBy("mService")
void resetCachedInfo() {
mCachedHasActivities = VALUE_INVALID;
mCachedIsHeavyWeight = VALUE_INVALID;
@@ -1134,6 +1155,16 @@ final class ProcessStateRecord {
return mCachedSchedGroup;
}
+ @GuardedBy("mService")
+ boolean shouldScheduleLikeTopApp() {
+ return mScheduleLikeTopApp;
+ }
+
+ @GuardedBy("mService")
+ void setScheduleLikeTopApp(boolean scheduleLikeTopApp) {
+ mScheduleLikeTopApp = scheduleLikeTopApp;
+ }
+
@GuardedBy(anyOf = {"mService", "mProcLock"})
public String makeAdjReason() {
if (mAdjSource != null || mAdjTarget != null) {
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 4f7a2ba58570..619186157cce 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -80,8 +80,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
@VisibleForTesting
static final String UNIQUE_ID_PREFIX = "virtual:";
- private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices =
- new ArrayMap<IBinder, VirtualDisplayDevice>();
+ private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices = new ArrayMap<>();
private final Handler mHandler;
private final SurfaceControlDisplayFactory mSurfaceControlDisplayFactory;
@@ -113,9 +112,16 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
public DisplayDevice createVirtualDisplayLocked(IVirtualDisplayCallback callback,
IMediaProjection projection, int ownerUid, String ownerPackageName, Surface surface,
int flags, VirtualDisplayConfig virtualDisplayConfig) {
+ IBinder appToken = callback.asBinder();
+ if (mVirtualDisplayDevices.containsKey(appToken)) {
+ Slog.wtfStack(TAG,
+ "Can't create virtual display, display with same appToken already exists");
+ return null;
+ }
+
String name = virtualDisplayConfig.getName();
boolean secure = (flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0;
- IBinder appToken = callback.asBinder();
+
IBinder displayToken = mSurfaceControlDisplayFactory.createDisplay(name, secure,
virtualDisplayConfig.getRequestedRefreshRate());
final String baseUniqueId =
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
index f0bf1ea80570..d0c346a63889 100644
--- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -334,7 +334,10 @@ public class ArtStatsLogUtils {
ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_UNKNOWN),
cancellationReason,
durationMs,
- 0); // deprecated, used to be durationIncludingSleepMs
+ 0, // deprecated, used to be durationIncludingSleepMs
+ 0, // optimizedPackagesCount
+ 0, // packagesDependingOnBootClasspathCount
+ 0); // totalPackagesCount
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 2c866abe614a..8673b90c966d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -18,6 +18,7 @@ package com.android.server.wm;
import static android.Manifest.permission.BIND_VOICE_INTERACTION;
import static android.Manifest.permission.CHANGE_CONFIGURATION;
+import static android.Manifest.permission.CONTROL_KEYGUARD;
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.Manifest.permission.DETECT_SCREEN_CAPTURE;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
@@ -3549,6 +3550,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public void keyguardGoingAway(int flags) {
+ mAmInternal.enforceCallingPermission(CONTROL_KEYGUARD, "unlock keyguard");
enforceNotIsolatedCaller("keyguardGoingAway");
final long token = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6d0fd2a51855..029f46f92dbe 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5682,6 +5682,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// TODO(b/233286785): Add sync support to wallpaper.
return true;
}
+ if (mActivityRecord != null && mViewVisibility != View.VISIBLE
+ && mWinAnimator.mAttrType != TYPE_BASE_APPLICATION
+ && mWinAnimator.mAttrType != TYPE_APPLICATION_STARTING) {
+ // Skip sync for invisible app windows which are not managed by activity lifecycle.
+ return false;
+ }
// In the WindowContainer implementation we immediately mark ready
// since a generic WindowContainer only needs to wait for its
// children to finish and is immediately ready from its own
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index c0cfa53a0a98..486ddb4cb354 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -420,14 +420,11 @@ public class MidiService extends IMidiManager.Stub {
setDeviceServer(server);
}
- @RequiresPermission(anyOf = {Manifest.permission.QUERY_USERS,
- Manifest.permission.CREATE_USERS,
- Manifest.permission.MANAGE_USERS})
public Device(BluetoothDevice bluetoothDevice) {
mBluetoothDevice = bluetoothDevice;
mServiceInfo = null;
mUid = mBluetoothServiceUid;
- mUserId = mUserManager.getMainUser().getIdentifier();
+ mUserId = UserHandle.getUserId(mUid);
}
private void setDeviceServer(IMidiDeviceServer server) {
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IndexedList.kt b/services/permission/java/com/android/server/permission/access/immutable/IndexedList.kt
index 6108ad298800..ce4aa4446698 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IndexedList.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IndexedList.kt
@@ -16,6 +16,9 @@
package com.android.server.permission.access.immutable
+/**
+ * Immutable list with index-based access.
+ */
sealed class IndexedList<T>(
internal val list: ArrayList<T>
) : Immutable<MutableIndexedList<T>> {
@@ -34,6 +37,9 @@ sealed class IndexedList<T>(
override fun toString(): String = list.toString()
}
+/**
+ * Mutable list with index-based access.
+ */
class MutableIndexedList<T>(
list: ArrayList<T> = ArrayList()
) : IndexedList<T>(list) {
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IndexedListSet.kt b/services/permission/java/com/android/server/permission/access/immutable/IndexedListSet.kt
index 1202c817d97b..77e71baf0ab7 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IndexedListSet.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IndexedListSet.kt
@@ -16,6 +16,9 @@
package com.android.server.permission.access.immutable
+/**
+ * Immutable set with index-based access, implemented using a list.
+ */
sealed class IndexedListSet<T>(
internal val list: ArrayList<T>
) : Immutable<MutableIndexedListSet<T>> {
@@ -36,6 +39,9 @@ sealed class IndexedListSet<T>(
override fun toString(): String = list.toString()
}
+/**
+ * Mutable set with index-based access, implemented using a list.
+ */
class MutableIndexedListSet<T>(
list: ArrayList<T> = ArrayList()
) : IndexedListSet<T>(list) {
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IndexedMap.kt b/services/permission/java/com/android/server/permission/access/immutable/IndexedMap.kt
index 5c75de83792f..299cc89d9a07 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IndexedMap.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IndexedMap.kt
@@ -18,6 +18,9 @@ package com.android.server.permission.access.immutable
import android.util.ArrayMap
+/**
+ * Immutable map with index-based access.
+ */
sealed class IndexedMap<K, V>(
internal val map: ArrayMap<K, V>
) : Immutable<MutableIndexedMap<K, V>> {
@@ -42,6 +45,9 @@ sealed class IndexedMap<K, V>(
override fun toString(): String = map.toString()
}
+/**
+ * Mutable map with index-based access.
+ */
class MutableIndexedMap<K, V>(
map: ArrayMap<K, V> = ArrayMap()
) : IndexedMap<K, V>(map) {
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IndexedReferenceMap.kt b/services/permission/java/com/android/server/permission/access/immutable/IndexedReferenceMap.kt
index 8c963aae1967..ff76a4745c8b 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IndexedReferenceMap.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IndexedReferenceMap.kt
@@ -18,6 +18,11 @@ package com.android.server.permission.access.immutable
import android.util.ArrayMap
+/**
+ * Immutable map with index-based access and mutable data structure values.
+ *
+ * @see MutableReference
+ */
sealed class IndexedReferenceMap<K, I : Immutable<M>, M : I>(
internal val map: ArrayMap<K, MutableReference<I, M>>
) : Immutable<MutableIndexedReferenceMap<K, I, M>> {
@@ -42,6 +47,11 @@ sealed class IndexedReferenceMap<K, I : Immutable<M>, M : I>(
override fun toString(): String = map.toString()
}
+/**
+ * Mutable map with index-based access and mutable data structure values.
+ *
+ * @see MutableReference
+ */
class MutableIndexedReferenceMap<K, I : Immutable<M>, M : I>(
map: ArrayMap<K, MutableReference<I, M>> = ArrayMap()
) : IndexedReferenceMap<K, I, M>(map) {
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IndexedSet.kt b/services/permission/java/com/android/server/permission/access/immutable/IndexedSet.kt
index 986861624e4b..547e56cef62a 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IndexedSet.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IndexedSet.kt
@@ -18,6 +18,9 @@ package com.android.server.permission.access.immutable
import android.util.ArraySet
+/**
+ * Immutable set with index-based access.
+ */
sealed class IndexedSet<T>(
internal val set: ArraySet<T>
) : Immutable<MutableIndexedSet<T>> {
@@ -37,6 +40,9 @@ sealed class IndexedSet<T>(
override fun toString(): String = set.toString()
}
+/**
+ * Mutable set with index-based access.
+ */
class MutableIndexedSet<T>(
set: ArraySet<T> = ArraySet()
) : IndexedSet<T>(set) {
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IntMap.kt b/services/permission/java/com/android/server/permission/access/immutable/IntMap.kt
index b7d8b4ccddd0..7ed29e8813ac 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IntMap.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IntMap.kt
@@ -18,6 +18,9 @@ package com.android.server.permission.access.immutable
import android.util.SparseArray
+/**
+ * Immutable map with index-based access and [Int] keys.
+ */
sealed class IntMap<T>(
internal val array: SparseArray<T>
) : Immutable<MutableIntMap<T>> {
@@ -41,6 +44,9 @@ sealed class IntMap<T>(
override fun toString(): String = array.toString()
}
+/**
+ * Mutable map with index-based access and [Int] keys.
+ */
class MutableIntMap<T>(
array: SparseArray<T> = SparseArray()
) : IntMap<T>(array) {
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IntReferenceMap.kt b/services/permission/java/com/android/server/permission/access/immutable/IntReferenceMap.kt
index 22fa8f2a7c7e..160b2279a0ba 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IntReferenceMap.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IntReferenceMap.kt
@@ -18,6 +18,11 @@ package com.android.server.permission.access.immutable
import android.util.SparseArray
+/**
+ * Immutable map with index-based access, [Int] keys and mutable data structure values.
+ *
+ * @see MutableReference
+ */
sealed class IntReferenceMap<I : Immutable<M>, M : I>(
internal val array: SparseArray<MutableReference<I, M>>
) : Immutable<MutableIntReferenceMap<I, M>> {
@@ -42,6 +47,11 @@ sealed class IntReferenceMap<I : Immutable<M>, M : I>(
override fun toString(): String = array.toString()
}
+/**
+ * Mutable map with index-based access, [Int] keys and mutable data structure values.
+ *
+ * @see MutableReference
+ */
class MutableIntReferenceMap<I : Immutable<M>, M : I>(
array: SparseArray<MutableReference<I, M>> = SparseArray()
) : IntReferenceMap<I, M>(array) {
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IntSet.kt b/services/permission/java/com/android/server/permission/access/immutable/IntSet.kt
index 9da36717db7e..21f2af20c3a9 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IntSet.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IntSet.kt
@@ -18,6 +18,9 @@ package com.android.server.permission.access.immutable
import android.util.SparseBooleanArray
+/**
+ * Immutable set with index-based access and [Int] elements.
+ */
sealed class IntSet(
internal val array: SparseBooleanArray
) : Immutable<MutableIntSet> {
@@ -37,6 +40,9 @@ sealed class IntSet(
override fun toString(): String = array.toString()
}
+/**
+ * Mutable set with index-based access and [Int] elements.
+ */
class MutableIntSet(
array: SparseBooleanArray = SparseBooleanArray()
) : IntSet(array) {
diff --git a/services/permission/java/com/android/server/permission/access/immutable/MutableReference.kt b/services/permission/java/com/android/server/permission/access/immutable/MutableReference.kt
index e39a3bbc8942..171cfeb4379d 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/MutableReference.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/MutableReference.kt
@@ -16,14 +16,39 @@
package com.android.server.permission.access.immutable
+/**
+ * Wrapper class for reference to a mutable data structure instance.
+ *
+ * This class encapsulates the logic to mutate/copy a mutable data structure instance and update the
+ * reference to the new mutated instance. It also remembers the mutated instance so that it can be
+ * reused during further mutations.
+ *
+ * Instances of this class should be kept private within a data structure, with the [get] method
+ * exposed on the immutable interface of the data structure as a `getFoo` method, and the [mutate]
+ * method exposed on the mutable interface of the data structure as a `mutateFoo` method. When the
+ * data structure is mutated/copied, a new instance of this class should be obtained with
+ * [toImmutable], which makes the wrapped reference immutable-only again and thus prevents
+ * further modifications to a data structure accessed with its immutable interface.
+ *
+ * @see MutableIndexedReferenceMap
+ * @see MutableIntReferenceMap
+ */
class MutableReference<I : Immutable<M>, M : I> private constructor(
private var immutable: I,
private var mutable: M?
) {
constructor(mutable: M) : this(mutable, mutable)
+ /**
+ * Return an immutable reference to the wrapped mutable data structure.
+ */
fun get(): I = immutable
+ /**
+ * Make the wrapped mutable data structure mutable, by either calling [Immutable.toMutable] and
+ * replacing the wrapped reference with its result, or reusing the existing reference if it's
+ * already mutable.
+ */
fun mutate(): M {
mutable?.let { return it }
return immutable.toMutable().also {
@@ -32,6 +57,10 @@ class MutableReference<I : Immutable<M>, M : I> private constructor(
}
}
+ /**
+ * Create a new [MutableReference] instance with the wrapped mutable data structure being
+ * immutable-only again.
+ */
fun toImmutable(): MutableReference<I, M> = MutableReference(immutable, null)
override fun equals(other: Any?): Boolean {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index d16c9c59bb1b..bf2311761891 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -248,6 +248,8 @@ public class DisplayManagerServiceTest {
@Mock VirtualDeviceManagerInternal mMockVirtualDeviceManagerInternal;
@Mock IVirtualDisplayCallback.Stub mMockAppToken;
@Mock IVirtualDisplayCallback.Stub mMockAppToken2;
+
+ @Mock IVirtualDisplayCallback.Stub mMockAppToken3;
@Mock WindowManagerInternal mMockWindowManagerInternal;
@Mock LightsManager mMockLightsManager;
@Mock VirtualDisplayAdapter mMockVirtualDisplayAdapter;
@@ -838,6 +840,7 @@ public class DisplayManagerServiceTest {
registerDefaultDisplays(displayManager);
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+ when(mMockAppToken2.asBinder()).thenReturn(mMockAppToken2);
IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
when(virtualDevice.getDeviceId()).thenReturn(1);
@@ -851,7 +854,7 @@ public class DisplayManagerServiceTest {
int displayId1 =
localService.createVirtualDisplay(
builder1.build(),
- mMockAppToken /* callback */,
+ mMockAppToken2 /* callback */,
virtualDevice /* virtualDeviceToken */,
mock(DisplayWindowPolicyController.class),
PACKAGE_NAME);
@@ -893,6 +896,7 @@ public class DisplayManagerServiceTest {
registerDefaultDisplays(displayManager);
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+ when(mMockAppToken2.asBinder()).thenReturn(mMockAppToken2);
IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
when(virtualDevice.getDeviceId()).thenReturn(1);
@@ -927,7 +931,7 @@ public class DisplayManagerServiceTest {
int displayId2 =
localService.createVirtualDisplay(
builder2.build(),
- mMockAppToken /* callback */,
+ mMockAppToken2 /* callback */,
virtualDevice /* virtualDeviceToken */,
mock(DisplayWindowPolicyController.class),
PACKAGE_NAME);
@@ -950,6 +954,8 @@ public class DisplayManagerServiceTest {
registerDefaultDisplays(displayManager);
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+ when(mMockAppToken2.asBinder()).thenReturn(mMockAppToken2);
+ when(mMockAppToken3.asBinder()).thenReturn(mMockAppToken3);
IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
when(virtualDevice.getDeviceId()).thenReturn(1);
@@ -999,7 +1005,7 @@ public class DisplayManagerServiceTest {
int ownDisplayGroupDisplayId =
localService.createVirtualDisplay(
ownDisplayGroupConfig,
- mMockAppToken /* callback */,
+ mMockAppToken2 /* callback */,
virtualDevice /* virtualDeviceToken */,
mock(DisplayWindowPolicyController.class),
PACKAGE_NAME);
@@ -1024,7 +1030,7 @@ public class DisplayManagerServiceTest {
int defaultDisplayGroupDisplayId =
localService.createVirtualDisplay(
defaultDisplayGroupConfig,
- mMockAppToken /* callback */,
+ mMockAppToken3 /* callback */,
null /* virtualDeviceToken */,
mock(DisplayWindowPolicyController.class),
PACKAGE_NAME);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index e58ec450dd61..01e49f2e207d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -59,6 +59,7 @@ import android.view.Display;
import android.view.DisplayInfo;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -303,6 +304,7 @@ public final class DisplayPowerControllerTest {
}
@Test
+ @FlakyTest(bugId = 294107062)
public void testDisplayBrightnessFollowers_BothDpcsSupportNits() {
DisplayPowerControllerHolder followerDpc =
createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
@@ -356,6 +358,7 @@ public final class DisplayPowerControllerTest {
}
@Test
+ @FlakyTest(bugId = 294107062)
public void testDisplayBrightnessFollowers_FollowerDoesNotSupportNits() {
DisplayPowerControllerHolder followerDpc =
createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
@@ -388,6 +391,7 @@ public final class DisplayPowerControllerTest {
}
@Test
+ @FlakyTest(bugId = 294107062)
public void testDisplayBrightnessFollowers_LeadDpcDoesNotSupportNits() {
DisplayPowerControllerHolder followerDpc =
createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
@@ -418,6 +422,7 @@ public final class DisplayPowerControllerTest {
}
@Test
+ @FlakyTest(bugId = 294107062)
public void testDisplayBrightnessFollowers_NeitherDpcSupportsNits() {
DisplayPowerControllerHolder followerDpc =
createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
@@ -450,6 +455,7 @@ public final class DisplayPowerControllerTest {
}
@Test
+ @FlakyTest(bugId = 294107062)
public void testDisplayBrightnessFollowers_AutomaticBrightness() {
Settings.System.putInt(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS_MODE,
@@ -521,6 +527,7 @@ public final class DisplayPowerControllerTest {
}
@Test
+ @FlakyTest(bugId = 294107062)
public void testDisplayBrightnessFollowersRemoval_RemoveSingleFollower() {
DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID,
FOLLOWER_UNIQUE_ID);
@@ -612,6 +619,7 @@ public final class DisplayPowerControllerTest {
}
@Test
+ @FlakyTest(bugId = 294107062)
public void testDisplayBrightnessFollowersRemoval_RemoveAllFollowers() {
DisplayPowerControllerHolder followerHolder =
createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
new file mode 100644
index 000000000000..8bbacc494efd
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+package com.android.server.display;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.display.IVirtualDisplayCallback;
+import android.hardware.display.VirtualDisplayConfig;
+import android.os.IBinder;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.testutils.TestHandler;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class VirtualDisplayAdapterTest {
+
+ @Mock
+ Context mContextMock;
+
+ @Mock
+ VirtualDisplayAdapter.SurfaceControlDisplayFactory mMockSufaceControlDisplayFactory;
+
+ @Mock
+ DisplayAdapter.Listener mMockListener;
+
+ @Mock
+ IVirtualDisplayCallback mMockCallback;
+
+ @Mock
+ IBinder mMockBinder;
+
+ private TestHandler mHandler;
+
+ private VirtualDisplayAdapter mVirtualDisplayAdapter;
+
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mHandler = new TestHandler(null);
+ mVirtualDisplayAdapter = new VirtualDisplayAdapter(new DisplayManagerService.SyncRoot(),
+ mContextMock, mHandler, mMockListener, mMockSufaceControlDisplayFactory);
+
+ when(mMockCallback.asBinder()).thenReturn(mMockBinder);
+ }
+
+ @Test
+ public void testCreatesVirtualDisplay() {
+ VirtualDisplayConfig config = new VirtualDisplayConfig.Builder("test", /* width= */ 1,
+ /* height= */ 1, /* densityDpi= */ 1).build();
+
+ DisplayDevice result = mVirtualDisplayAdapter.createVirtualDisplayLocked(mMockCallback,
+ /* projection= */ null, /* ownerUid= */ 10, /* packageName= */ "testpackage",
+ /* surface= */ null, /* flags= */ 0, config);
+
+ assertNotNull(result);
+ }
+
+ @Test
+ public void testDoesNotCreateVirtualDisplayForSameCallback() {
+ VirtualDisplayConfig config1 = new VirtualDisplayConfig.Builder("test", /* width= */ 1,
+ /* height= */ 1, /* densityDpi= */ 1).build();
+ VirtualDisplayConfig config2 = new VirtualDisplayConfig.Builder("test2", /* width= */ 1,
+ /* height= */ 1, /* densityDpi= */ 1).build();
+ mVirtualDisplayAdapter.createVirtualDisplayLocked(mMockCallback, /* projection= */ null,
+ /* ownerUid= */ 10, /* packageName= */ "testpackage", /* surface= */ null,
+ /* flags= */ 0, config1);
+
+ DisplayDevice result = mVirtualDisplayAdapter.createVirtualDisplayLocked(mMockCallback,
+ /* projection= */ null, /* ownerUid= */ 10, /* packageName= */ "testpackage",
+ /* surface= */ null, /* flags= */ 0, config2);
+
+ assertNull(result);
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 1f4563fb2682..976e74083849 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -105,6 +105,7 @@ import com.android.server.wm.ActivityServiceConnectionsHolder;
import com.android.server.wm.ActivityTaskManagerService;
import com.android.server.wm.WindowProcessController;
+import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -206,8 +207,10 @@ public class MockingOomAdjusterTests {
setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object());
doReturn(new ActivityManagerService.ProcessChangeItem()).when(pr)
.enqueueProcessChangeItemLocked(anyInt(), anyInt());
- sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList,
- new ActiveUids(sService, false));
+ sService.mOomAdjuster = sService.mConstants.ENABLE_NEW_OOMADJ
+ ? new OomAdjusterModernImpl(sService, sService.mProcessList,
+ new ActiveUids(sService, false))
+ : new OomAdjuster(sService, sService.mProcessList, new ActiveUids(sService, false));
sService.mOomAdjuster.mAdjSeq = 10000;
sService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE);
if (sService.mConstants.USE_TIERED_CACHED_ADJ) {
@@ -220,6 +223,11 @@ public class MockingOomAdjusterTests {
LocalServices.removeServiceForTest(PackageManagerInternal.class);
}
+ @After
+ public void tearDown() {
+ sService.mOomAdjuster.resetInternal();
+ }
+
private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
try {
Field field = clazz.getDeclaredField(fieldName);
@@ -249,6 +257,9 @@ public class MockingOomAdjusterTests {
ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
lru.clear();
Collections.addAll(lru, apps);
+ for (ProcessRecord app : apps) {
+ sService.mOomAdjuster.onProcessBeginLocked(app);
+ }
}
/**
@@ -259,6 +270,7 @@ public class MockingOomAdjusterTests {
@SuppressWarnings("GuardedBy")
private void updateOomAdj(ProcessRecord... apps) {
if (apps.length == 1) {
+ sService.mOomAdjuster.onProcessBeginLocked(apps[0]);
sService.mOomAdjuster.updateOomAdjLocked(apps[0], OOM_ADJ_REASON_NONE);
} else {
setProcessesToLru(apps);
@@ -600,10 +612,13 @@ public class MockingOomAdjusterTests {
s.lastTopAlmostPerceptibleBindRequestUptimeMs = nowUptime;
s.getConnections().clear();
app.mServices.updateHasTopStartedAlmostPerceptibleServices();
+ sService.mOomAdjuster.onProcessBeginLocked(system);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2, app.mState.getSetAdj());
+
+ sService.mOomAdjuster.resetInternal();
}
// Out of grace period but valid binding allows the adjustment.
@@ -620,10 +635,13 @@ public class MockingOomAdjusterTests {
s.lastTopAlmostPerceptibleBindRequestUptimeMs =
nowUptime - 2 * sService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs;
app.mServices.updateHasTopStartedAlmostPerceptibleServices();
+ sService.mOomAdjuster.onProcessBeginLocked(system);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2, app.mState.getSetAdj());
+
+ sService.mOomAdjuster.resetInternal();
}
// Out of grace period and no valid binding so no adjustment.
@@ -641,10 +659,13 @@ public class MockingOomAdjusterTests {
nowUptime - 2 * sService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs;
s.getConnections().clear();
app.mServices.updateHasTopStartedAlmostPerceptibleServices();
+ sService.mOomAdjuster.onProcessBeginLocked(system);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertNotEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2, app.mState.getSetAdj());
+
+ sService.mOomAdjuster.resetInternal();
}
}
@@ -657,11 +678,12 @@ public class MockingOomAdjusterTests {
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, true));
system.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
system.mState.setHasTopUi(true);
+ sService.mOomAdjuster.onProcessBeginLocked(system);
// Simulate the system starting and binding to a service in the app.
ServiceRecord s = bindService(app, system,
null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
- sService.mOomAdjuster.updateOomAdjLocked(app, OOM_ADJ_REASON_NONE);
+ updateOomAdj(system, app);
assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND,
PERCEPTIBLE_APP_ADJ + 1, SCHED_GROUP_DEFAULT);
@@ -850,6 +872,7 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+ client.mServices.setTreatLikeActivity(true);
bindService(app, client, null, Context.BIND_WAIVE_PRIORITY
| Context.BIND_TREAT_LIKE_ACTIVITY, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
@@ -1006,7 +1029,7 @@ public class MockingOomAdjusterTests {
bindService(app, client, null, Context.BIND_NOT_FOREGROUND, mock(IBinder.class));
client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
- updateOomAdj(app);
+ updateOomAdj(client, app);
assertEquals(PROCESS_STATE_TRANSIENT_BACKGROUND, app.mState.getSetProcState());
assertNoBfsl(app);
@@ -1132,7 +1155,7 @@ public class MockingOomAdjusterTests {
assertNoBfsl(app);
client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
- updateOomAdj(app);
+ updateOomAdj(client, app);
assertEquals(PERSISTENT_SERVICE_ADJ, app.mState.getSetAdj());
assertBfsl(app);
@@ -1148,7 +1171,7 @@ public class MockingOomAdjusterTests {
bindService(app, client, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class));
client.mState.setRunningRemoteAnimation(true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
- updateOomAdj(app);
+ updateOomAdj(client, app);
assertEquals(PERCEPTIBLE_LOW_APP_ADJ, app.mState.getSetAdj());
}
@@ -1199,6 +1222,8 @@ public class MockingOomAdjusterTests {
updateOomAdj(client, app);
assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ + 2, app.mState.getSetAdj());
+
+ sService.mOomAdjuster.resetInternal();
}
{
@@ -1217,6 +1242,8 @@ public class MockingOomAdjusterTests {
doReturn(false).when(wpc).isHeavyWeightProcess();
assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ + 2, app.mState.getSetAdj());
+
+ sService.mOomAdjuster.resetInternal();
}
{
@@ -1229,9 +1256,11 @@ public class MockingOomAdjusterTests {
mock(IBinder.class));
client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
- sService.mOomAdjuster.updateOomAdjLocked(app, OOM_ADJ_REASON_NONE);
+ updateOomAdj(client, app);
assertEquals(PERCEPTIBLE_APP_ADJ + 1, app.mState.getSetAdj());
+
+ sService.mOomAdjuster.resetInternal();
}
{
@@ -1246,10 +1275,12 @@ public class MockingOomAdjusterTests {
mock(IBinder.class));
client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
- sService.mOomAdjuster.updateOomAdjLocked(app, OOM_ADJ_REASON_NONE);
+ updateOomAdj(client, app);
doReturn(false).when(wpc).isHeavyWeightProcess();
assertEquals(PERCEPTIBLE_APP_ADJ + 1, app.mState.getSetAdj());
+
+ sService.mOomAdjuster.resetInternal();
}
}
@@ -1849,7 +1880,7 @@ public class MockingOomAdjusterTests {
bindService(app1, client1, null, Context.BIND_SCHEDULE_LIKE_TOP_APP, mock(IBinder.class));
bindService(app2, client2, null, Context.BIND_SCHEDULE_LIKE_TOP_APP, mock(IBinder.class));
- updateOomAdj(app1, app2);
+ updateOomAdj(client1, client2, app1, app2);
assertProcStates(app1, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, VISIBLE_APP_ADJ,
SCHED_GROUP_TOP_APP);
@@ -1899,6 +1930,8 @@ public class MockingOomAdjusterTests {
s1.getConnections().clear();
s2.getConnections().clear();
+ client1.mServices.removeAllConnections();
+ client2.mServices.removeAllConnections();
client1.mState.setMaxAdj(UNKNOWN_ADJ);
client2.mState.setMaxAdj(UNKNOWN_ADJ);
client1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
@@ -1909,7 +1942,7 @@ public class MockingOomAdjusterTests {
bindService(app2, client2, s2, Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE,
mock(IBinder.class));
- updateOomAdj(app1, app2);
+ updateOomAdj(client1, client2, app1, app2);
// VISIBLE_APP_ADJ is the max oom-adj for BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE.
assertProcStates(app1, PROCESS_STATE_FOREGROUND_SERVICE, VISIBLE_APP_ADJ,
@@ -1922,7 +1955,7 @@ public class MockingOomAdjusterTests {
doReturn(client2).when(sService).getTopApp();
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
- sService.mOomAdjuster.updateOomAdjLocked(app2, OOM_ADJ_REASON_NONE);
+ updateOomAdj(client2, app2);
assertProcStates(app2, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ,
SCHED_GROUP_DEFAULT);
}
@@ -1977,6 +2010,7 @@ public class MockingOomAdjusterTests {
app.setPendingFinishAttach(true);
app.mState.setHasForegroundActivities(false);
+ sService.mOomAdjuster.onProcessBeginLocked(app);
sService.mOomAdjuster.setAttachingProcessStatesLSP(app);
updateOomAdj(app);
@@ -1991,7 +2025,9 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
app.setPendingFinishAttach(true);
app.mState.setHasForegroundActivities(true);
+ doReturn(app).when(sService).getTopApp();
+ sService.mOomAdjuster.onProcessBeginLocked(app);
sService.mOomAdjuster.setAttachingProcessStatesLSP(app);
updateOomAdj(app);
@@ -2088,7 +2124,7 @@ public class MockingOomAdjusterTests {
anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
doNothing().when(sService.mServices)
.scheduleServiceTimeoutLocked(any(ProcessRecord.class));
- sService.mOomAdjuster.updateOomAdjLocked(client1, OOM_ADJ_REASON_NONE);
+ updateOomAdj(client1, client2, app1, app2, app3);
assertEquals(PROCESS_STATE_CACHED_EMPTY, client1.mState.getSetProcState());
assertEquals(PROCESS_STATE_SERVICE, app1.mState.getSetProcState());
@@ -2426,6 +2462,8 @@ public class MockingOomAdjusterTests {
lru.clear();
lru.add(app2);
lru.add(app);
+ sService.mOomAdjuster.onProcessBeginLocked(app2);
+ sService.mOomAdjuster.onProcessBeginLocked(app);
final ComponentName cn = ComponentName.unflattenFromString(
MOCKAPP_PACKAGENAME + "/.TestService");
@@ -2528,7 +2566,7 @@ public class MockingOomAdjusterTests {
doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
doReturn(app).when(sService).getTopApp();
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
- sService.mOomAdjuster.updateOomAdjLocked(app, OOM_ADJ_REASON_NONE);
+ updateOomAdj(app);
assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
diff --git a/services/tests/servicestests/src/com/android/server/companion/utils/OWNERS b/services/tests/servicestests/src/com/android/server/companion/utils/OWNERS
new file mode 100644
index 000000000000..008a53f6941f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/utils/OWNERS
@@ -0,0 +1 @@
+include /services/companion/java/com/android/server/companion/OWNERS \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/companion/utils/PackageUtilsTest.java b/services/tests/servicestests/src/com/android/server/companion/utils/PackageUtilsTest.java
new file mode 100644
index 000000000000..01159b1f901c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/utils/PackageUtilsTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+package com.android.server.companion.utils;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.content.res.Resources;
+import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.companion.PackageUtils;
+import com.android.server.pm.pkg.AndroidPackage;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@RunWith(AndroidTestingRunner.class)
+public class PackageUtilsTest {
+ private static final String[] ALLOWED_PACKAGE_NAMES = new String[]{
+ "allowed_app",
+ };
+ private static final Signature[] ALLOWED_PACKAGE_SIGNATURES = new Signature[]{
+ new Signature("001122"),
+ };
+ private static final String[] DISALLOWED_PACKAGE_NAMES = new String[]{
+ "disallowed_app",
+ };
+ private static final Signature[] DISALLOWED_PACKAGE_SIGNATURES = new Signature[]{
+ new Signature("778899"),
+ };
+
+ @Test
+ public void isAllowlisted_true() {
+ Context context = spy(
+ new ContextWrapper(
+ InstrumentationRegistry.getInstrumentation().getTargetContext()));
+ final Resources res = spy(context.getResources());
+ doReturn(ALLOWED_PACKAGE_NAMES).when(res).getStringArray(
+ com.android.internal.R.array.config_companionDevicePackages);
+ doReturn(android.util.PackageUtils.computeSignaturesSha256Digests(
+ ALLOWED_PACKAGE_SIGNATURES)).when(res).getStringArray(
+ com.android.internal.R.array.config_companionDeviceCerts);
+ doReturn(res).when(context).getResources();
+ PackageManagerInternal pm = mock(PackageManagerInternal.class);
+ AndroidPackage ap = mock(AndroidPackage.class);
+ SigningDetails sd = new SigningDetails(
+ ALLOWED_PACKAGE_SIGNATURES,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null);
+ doReturn(ap).when(pm).getPackage(ALLOWED_PACKAGE_NAMES[0]);
+ doReturn(sd).when(ap).getSigningDetails();
+ assertTrue(PackageUtils.isPackageAllowlisted(context, pm, ALLOWED_PACKAGE_NAMES[0]));
+ }
+
+ @Test
+ public void isAllowlisted_package_disallowed() {
+ Context context = spy(
+ new ContextWrapper(
+ InstrumentationRegistry.getInstrumentation().getTargetContext()));
+ final Resources res = spy(context.getResources());
+ doReturn(ALLOWED_PACKAGE_NAMES).when(res).getStringArray(
+ com.android.internal.R.array.config_companionDevicePackages);
+ doReturn(android.util.PackageUtils.computeSignaturesSha256Digests(
+ ALLOWED_PACKAGE_SIGNATURES)).when(res).getStringArray(
+ com.android.internal.R.array.config_companionDeviceCerts);
+ doReturn(res).when(context).getResources();
+ PackageManagerInternal pm = mock(PackageManagerInternal.class);
+ AndroidPackage ap = mock(AndroidPackage.class);
+ SigningDetails sd = new SigningDetails(
+ ALLOWED_PACKAGE_SIGNATURES, // Giving the package a wrong signature
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null);
+ doReturn(ap).when(pm).getPackage(DISALLOWED_PACKAGE_NAMES[0]);
+ doReturn(sd).when(ap).getSigningDetails();
+ assertFalse(PackageUtils.isPackageAllowlisted(context, pm, DISALLOWED_PACKAGE_NAMES[0]));
+ }
+
+ @Test
+ public void isAllowlisted_signature_mismatch() {
+ Context context = spy(
+ new ContextWrapper(
+ InstrumentationRegistry.getInstrumentation().getTargetContext()));
+ final Resources res = spy(context.getResources());
+ doReturn(ALLOWED_PACKAGE_NAMES).when(res).getStringArray(
+ com.android.internal.R.array.config_companionDevicePackages);
+ doReturn(android.util.PackageUtils.computeSignaturesSha256Digests(
+ ALLOWED_PACKAGE_SIGNATURES)).when(res).getStringArray(
+ com.android.internal.R.array.config_companionDeviceCerts);
+ doReturn(res).when(context).getResources();
+ PackageManagerInternal pm = mock(PackageManagerInternal.class);
+ AndroidPackage ap = mock(AndroidPackage.class);
+ SigningDetails sd = new SigningDetails(
+ DISALLOWED_PACKAGE_SIGNATURES, // Giving the package a wrong signature
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null);
+ doReturn(ap).when(pm).getPackage(ALLOWED_PACKAGE_NAMES[0]);
+ doReturn(sd).when(ap).getSigningDetails();
+ assertFalse(PackageUtils.isPackageAllowlisted(context, pm, ALLOWED_PACKAGE_NAMES[0]));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
index 9f855c5ca83f..f834cb24f245 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
@@ -99,23 +99,45 @@ class AndroidPackageParsingValidationTest {
@Test
fun parseApplicationTag() {
val tag = "application"
- validateTagAttr(tag, "backupAgent",
- R.styleable.AndroidManifestApplication_backupAgent, 1024)
- validateTagAttrComponentName(tag, "backupAgent",
- R.styleable.AndroidManifestApplication_backupAgent)
- validateTagAttr(tag, "manageSpaceActivity",
- R.styleable.AndroidManifestApplication_manageSpaceActivity, 1024)
+ validateTagAttr(
+ tag,
+ "backupAgent",
+ R.styleable.AndroidManifestApplication_backupAgent,
+ 1024
+ )
+ validateTagAttrComponentName(
+ tag,
+ "backupAgent",
+ R.styleable.AndroidManifestApplication_backupAgent
+ )
+ validateTagAttr(
+ tag,
+ "manageSpaceActivity",
+ R.styleable.AndroidManifestApplication_manageSpaceActivity,
+ 1024
+ )
validateTagAttr(tag, "name", R.styleable.AndroidManifestApplication_name, 1024)
- validateTagAttrComponentName(tag, "name",
- R.styleable.AndroidManifestApplication_name)
+ validateTagAttrComponentName(tag, "name", R.styleable.AndroidManifestApplication_name)
validateTagAttr(tag, "permission", R.styleable.AndroidManifestApplication_permission, 1024)
validateTagAttr(tag, "process", R.styleable.AndroidManifestApplication_process, 1024)
- validateTagAttr(tag, "requiredAccountType",
- R.styleable.AndroidManifestApplication_requiredAccountType, 1024)
- validateTagAttr(tag, "restrictedAccountType",
- R.styleable.AndroidManifestApplication_restrictedAccountType, 1024)
- validateTagAttr(tag, "taskAffinity",
- R.styleable.AndroidManifestApplication_taskAffinity, 1024)
+ validateTagAttr(
+ tag,
+ "requiredAccountType",
+ R.styleable.AndroidManifestApplication_requiredAccountType,
+ 1024
+ )
+ validateTagAttr(
+ tag,
+ "restrictedAccountType",
+ R.styleable.AndroidManifestApplication_restrictedAccountType,
+ 1024
+ )
+ validateTagAttr(
+ tag,
+ "taskAffinity",
+ R.styleable.AndroidManifestApplication_taskAffinity,
+ 1024
+ )
validateTagCount("profileable", 100, tag)
validateTagCount("uses-native-library", 100, tag)
validateTagCount("receiver", 1000, tag)
@@ -159,12 +181,23 @@ class AndroidPackageParsingValidationTest {
fun parseActivityAliasTag() {
val tag = "activity-alias"
validateTagAttr(tag, "name", R.styleable.AndroidManifestActivityAlias_name, 1024)
- validateTagAttr(tag, "permission",
- R.styleable.AndroidManifestActivityAlias_permission, 1024)
- validateTagAttr(tag, "targetActivity",
- R.styleable.AndroidManifestActivityAlias_targetActivity, 1024)
- validateTagAttrComponentName(tag, "targetActivity",
- R.styleable.AndroidManifestActivityAlias_targetActivity)
+ validateTagAttr(
+ tag,
+ "permission",
+ R.styleable.AndroidManifestActivityAlias_permission,
+ 1024
+ )
+ validateTagAttr(
+ tag,
+ "targetActivity",
+ R.styleable.AndroidManifestActivityAlias_targetActivity,
+ 1024
+ )
+ validateTagAttrComponentName(
+ tag,
+ "targetActivity",
+ R.styleable.AndroidManifestActivityAlias_targetActivity
+ )
validateTagCount("meta-data", 1000, tag)
validateTagCount("intent-filter", 20000, tag)
}
@@ -173,7 +206,6 @@ class AndroidPackageParsingValidationTest {
fun parseUsesLibraryTag() {
val tag = "uses-library"
validateTagAttr(tag, "name", R.styleable.AndroidManifestUsesLibrary_name, 1024)
- validateTagAttrComponentName(tag, "name", R.styleable.AndroidManifestUsesLibrary_name)
}
@Test
@@ -181,10 +213,17 @@ class AndroidPackageParsingValidationTest {
val tag = "activity"
validateTagAttr(tag, "name", R.styleable.AndroidManifestActivity_name, 1024)
validateTagAttrComponentName(tag, "name", R.styleable.AndroidManifestActivity_name)
- validateTagAttr(tag, "parentActivityName",
- R.styleable.AndroidManifestActivity_parentActivityName, 1024)
- validateTagAttrComponentName(tag, "parentActivityName",
- R.styleable.AndroidManifestActivity_parentActivityName)
+ validateTagAttr(
+ tag,
+ "parentActivityName",
+ R.styleable.AndroidManifestActivity_parentActivityName,
+ 1024
+ )
+ validateTagAttrComponentName(
+ tag,
+ "parentActivityName",
+ R.styleable.AndroidManifestActivity_parentActivityName
+ )
validateTagAttr(tag, "permission", R.styleable.AndroidManifestActivity_permission, 1024)
validateTagAttr(tag, "process", R.styleable.AndroidManifestActivity_process, 1024)
validateTagAttr(tag, "taskAffinity", R.styleable.AndroidManifestActivity_taskAffinity, 1024)
@@ -197,26 +236,49 @@ class AndroidPackageParsingValidationTest {
fun parseOverlayTag() {
val tag = "overlay"
validateTagAttr(tag, "category", R.styleable.AndroidManifestResourceOverlay_category, 1024)
- validateTagAttr(tag, "requiredSystemPropertyName",
- R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyName, 1024)
- validateTagAttr(tag, "requiredSystemPropertyValue",
- R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyValue, PROP_VALUE_MAX)
- validateTagAttr(tag, "targetPackage",
- R.styleable.AndroidManifestResourceOverlay_targetPackage, 256)
- validateTagAttr(tag, "targetName",
- R.styleable.AndroidManifestResourceOverlay_targetName, 1024)
+ validateTagAttr(
+ tag,
+ "requiredSystemPropertyName",
+ R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyName,
+ 1024
+ )
+ validateTagAttr(
+ tag,
+ "requiredSystemPropertyValue",
+ R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyValue,
+ PROP_VALUE_MAX
+ )
+ validateTagAttr(
+ tag,
+ "targetPackage",
+ R.styleable.AndroidManifestResourceOverlay_targetPackage,
+ 256
+ )
+ validateTagAttr(
+ tag,
+ "targetName",
+ R.styleable.AndroidManifestResourceOverlay_targetName,
+ 1024
+ )
}
@Test
fun parseInstrumentationTag() {
val tag = "instrumentation"
validateTagAttr(tag, "name", R.styleable.AndroidManifestInstrumentation_name, 1024)
- validateTagAttrComponentName(tag, "name",
- R.styleable.AndroidManifestInstrumentation_name)
- validateTagAttr(tag, "targetPackage",
- R.styleable.AndroidManifestInstrumentation_targetPackage, 256)
- validateTagAttr(tag, "targetProcesses",
- R.styleable.AndroidManifestInstrumentation_targetProcesses, 1024)
+ validateTagAttrComponentName(tag, "name", R.styleable.AndroidManifestInstrumentation_name)
+ validateTagAttr(
+ tag,
+ "targetPackage",
+ R.styleable.AndroidManifestInstrumentation_targetPackage,
+ 256
+ )
+ validateTagAttr(
+ tag,
+ "targetProcesses",
+ R.styleable.AndroidManifestInstrumentation_targetProcesses,
+ 1024
+ )
}
@Test
@@ -278,10 +340,18 @@ class AndroidPackageParsingValidationTest {
validateTagAttrComponentName(tag, "name", R.styleable.AndroidManifestProvider_name)
validateTagAttr(tag, "permission", R.styleable.AndroidManifestProvider_permission, 1024)
validateTagAttr(tag, "process", R.styleable.AndroidManifestProvider_process, 1024)
- validateTagAttr(tag, "readPermission",
- R.styleable.AndroidManifestProvider_readPermission, 1024)
- validateTagAttr(tag, "writePermission",
- R.styleable.AndroidManifestProvider_writePermission, 1024)
+ validateTagAttr(
+ tag,
+ "readPermission",
+ R.styleable.AndroidManifestProvider_readPermission,
+ 1024
+ )
+ validateTagAttr(
+ tag,
+ "writePermission",
+ R.styleable.AndroidManifestProvider_writePermission,
+ 1024
+ )
validateTagCount("grant-uri-permission", 100, tag)
validateTagCount("path-permission", 100, tag)
validateTagCount("meta-data", 1000, tag)
@@ -292,26 +362,54 @@ class AndroidPackageParsingValidationTest {
fun parseGrantUriPermissionTag() {
val tag = "grant-uri-permission"
validateTagAttr(tag, "path", R.styleable.AndroidManifestGrantUriPermission_path, 4000)
- validateTagAttr(tag, "pathPrefix",
- R.styleable.AndroidManifestGrantUriPermission_pathPrefix, 4000)
- validateTagAttr(tag, "pathPattern",
- R.styleable.AndroidManifestGrantUriPermission_pathPattern, 4000)
+ validateTagAttr(
+ tag,
+ "pathPrefix",
+ R.styleable.AndroidManifestGrantUriPermission_pathPrefix,
+ 4000
+ )
+ validateTagAttr(
+ tag,
+ "pathPattern",
+ R.styleable.AndroidManifestGrantUriPermission_pathPattern,
+ 4000
+ )
}
@Test
fun parsePathPermissionTag() {
val tag = "path-permission"
validateTagAttr(tag, "path", R.styleable.AndroidManifestPathPermission_path, 4000)
- validateTagAttr(tag, "pathPrefix",
- R.styleable.AndroidManifestPathPermission_pathPrefix, 4000)
- validateTagAttr(tag, "pathPattern",
- R.styleable.AndroidManifestPathPermission_pathPattern, 4000)
- validateTagAttr(tag, "permission",
- R.styleable.AndroidManifestPathPermission_permission, 1024)
- validateTagAttr(tag, "readPermission",
- R.styleable.AndroidManifestPathPermission_readPermission, 1024)
- validateTagAttr(tag, "writePermission",
- R.styleable.AndroidManifestPathPermission_writePermission, 1024)
+ validateTagAttr(
+ tag,
+ "pathPrefix",
+ R.styleable.AndroidManifestPathPermission_pathPrefix,
+ 4000
+ )
+ validateTagAttr(
+ tag,
+ "pathPattern",
+ R.styleable.AndroidManifestPathPermission_pathPattern,
+ 4000
+ )
+ validateTagAttr(
+ tag,
+ "permission",
+ R.styleable.AndroidManifestPathPermission_permission,
+ 1024
+ )
+ validateTagAttr(
+ tag,
+ "readPermission",
+ R.styleable.AndroidManifestPathPermission_readPermission,
+ 1024
+ )
+ validateTagAttr(
+ tag,
+ "writePermission",
+ R.styleable.AndroidManifestPathPermission_writePermission,
+ 1024
+ )
}
@Test
@@ -350,8 +448,12 @@ class AndroidPackageParsingValidationTest {
validateTagAttr(tag, "pathPattern", R.styleable.AndroidManifestData_pathPattern, 4000)
validateTagAttr(tag, "pathPrefix", R.styleable.AndroidManifestData_pathPrefix, 4000)
validateTagAttr(tag, "pathSuffix", R.styleable.AndroidManifestData_pathSuffix, 4000)
- validateTagAttr(tag, "pathAdvancedPattern",
- R.styleable.AndroidManifestData_pathAdvancedPattern, 4000)
+ validateTagAttr(
+ tag,
+ "pathAdvancedPattern",
+ R.styleable.AndroidManifestData_pathAdvancedPattern,
+ 4000
+ )
validateTagAttr(tag, "mimeType", R.styleable.AndroidManifestData_mimeType, 512)
}
@@ -365,8 +467,12 @@ class AndroidPackageParsingValidationTest {
fun parsePermissionTag() {
val tag = "permission"
validateTagAttr(tag, "name", R.styleable.AndroidManifestPermission_name, 1024)
- validateTagAttr(tag, "permissionGroup",
- R.styleable.AndroidManifestPermission_permissionGroup, 256)
+ validateTagAttr(
+ tag,
+ "permissionGroup",
+ R.styleable.AndroidManifestPermission_permissionGroup,
+ 256
+ )
}
@Test
@@ -386,14 +492,18 @@ class AndroidPackageParsingValidationTest {
try {
validator.validateStrAttr(pullParser, attr, name)
} catch (e: SecurityException) {
- fail("Failed to parse attribute $attr in <$tag> as valid Java class name:" +
- " ${e.message}")
+ fail(
+ "Failed to parse attribute $attr in <$tag> as valid Java class name:" +
+ " ${e.message}"
+ )
}
try {
validator.validateResStrAttr(pullParser, index, name)
} catch (e: SecurityException) {
- fail("Failed to parse attribute $attr in <$tag> as valid Java class name:" +
- " ${e.message}")
+ fail(
+ "Failed to parse attribute $attr in <$tag> as valid Java class name:" +
+ " ${e.message}"
+ )
}
}
@@ -404,13 +514,17 @@ class AndroidPackageParsingValidationTest {
val validator = Validator()
pullParser.nextTag()
validator.validate(pullParser)
- val e1 = assertThrows("$name is not valid Java class name",
- SecurityException::class.java) {
+ val e1 = assertThrows(
+ "$name is not valid Java class name",
+ SecurityException::class.java
+ ) {
validator.validateStrAttr(pullParser, attr, name)
}
assertEquals(expectedAttrComponentNameErrorMsg(name), e1.message)
- val e2 = assertThrows("$name is not valid Java class name",
- SecurityException::class.java) {
+ val e2 = assertThrows(
+ "$name is not valid Java class name",
+ SecurityException::class.java
+ ) {
validator.validateResStrAttr(pullParser, index, name)
}
assertEquals(expectedAttrComponentNameErrorMsg(name), e2.message)
@@ -437,15 +551,19 @@ class AndroidPackageParsingValidationTest {
try {
validator.validateStrAttr(pullParser, name, value)
} catch (e: SecurityException) {
- fail("Failed to parse valid <$tag> attribute $name with max length of $maxLen:" +
- " ${e.message}")
+ fail(
+ "Failed to parse valid <$tag> attribute $name with max length of $maxLen:" +
+ " ${e.message}"
+ )
}
if (index != null) {
try {
validator.validateResStrAttr(pullParser, index, value)
} catch (e: SecurityException) {
- fail("Failed to parse valid <$tag> resource string attribute $name with max" +
- " length of $maxLen: ${e.message}")
+ fail(
+ "Failed to parse valid <$tag> resource string attribute $name with max" +
+ " length of $maxLen: ${e.message}"
+ )
}
}
}
@@ -485,8 +603,10 @@ class AndroidPackageParsingValidationTest {
try {
parseXmlStr(xml)
} catch (e: SecurityException) {
- fail("Failed to parse <$tag> with max count limit of $maxNum under" +
- " <$parentTag>: ${e.message}")
+ fail(
+ "Failed to parse <$tag> with max count limit of $maxNum under" +
+ " <$parentTag>: ${e.message}"
+ )
}
}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
index 54995fb5bd84..edc5df20231a 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -1022,9 +1022,12 @@ public class VibrationThreadTest {
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100)
.compose();
CombinedVibration effect = CombinedVibration.createParallel(composed);
- long vibrationId = startThreadAndDispatcher(effect);
-
+ // We create the HalVibration here to obtain the vibration id and use it to mock the
+ // required response when calling triggerSyncedVibration.
+ HalVibration halVibration = createVibration(effect);
+ long vibrationId = halVibration.id;
when(mManagerHooks.triggerSyncedVibration(eq(vibrationId))).thenReturn(true);
+ startThreadAndDispatcher(halVibration);
assertTrue(waitUntil(
() -> !mVibratorProviders.get(1).getEffectSegments(vibrationId).isEmpty()
@@ -1056,7 +1059,6 @@ public class VibrationThreadTest {
mVibratorProviders.get(4).setSupportedPrimitives(
VibrationEffect.Composition.PRIMITIVE_CLICK);
when(mManagerHooks.prepareSyncedVibration(anyLong(), any())).thenReturn(true);
- when(mManagerHooks.triggerSyncedVibration(anyLong())).thenReturn(true);
VibrationEffect composed = VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
@@ -1067,7 +1069,12 @@ public class VibrationThreadTest {
.addVibrator(3, VibrationEffect.createWaveform(new long[]{10}, new int[]{100}, -1))
.addVibrator(4, composed)
.combine();
- long vibrationId = startThreadAndDispatcher(effect);
+ // We create the HalVibration here to obtain the vibration id and use it to mock the
+ // required response when calling triggerSyncedVibration.
+ HalVibration halVibration = createVibration(effect);
+ long vibrationId = halVibration.id;
+ when(mManagerHooks.triggerSyncedVibration(eq(vibrationId))).thenReturn(true);
+ startThreadAndDispatcher(halVibration);
waitForCompletion();
long expectedCap = IVibratorManager.CAP_SYNC
@@ -1117,13 +1124,17 @@ public class VibrationThreadTest {
mockVibrators(vibratorIds);
mVibratorProviders.get(2).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
when(mManagerHooks.prepareSyncedVibration(anyLong(), any())).thenReturn(true);
- when(mManagerHooks.triggerSyncedVibration(anyLong())).thenReturn(false);
CombinedVibration effect = CombinedVibration.startParallel()
.addVibrator(1, VibrationEffect.createOneShot(10, 100))
.addVibrator(2, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
.combine();
- long vibrationId = startThreadAndDispatcher(effect);
+ // We create the HalVibration here to obtain the vibration id and use it to mock the
+ // required response when calling triggerSyncedVibration.
+ HalVibration halVibration = createVibration(effect);
+ long vibrationId = halVibration.id;
+ when(mManagerHooks.triggerSyncedVibration(eq(vibrationId))).thenReturn(false);
+ startThreadAndDispatcher(halVibration);
waitForCompletion();
long expectedCap = IVibratorManager.CAP_SYNC
@@ -1492,7 +1503,6 @@ public class VibrationThreadTest {
assertTrue(fakeVibrator.getAmplitudes().isEmpty());
}
- @FlakyTest
@Test
public void vibrate_multipleVibrations_withCancel() throws Exception {
mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(
diff --git a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
index 1c6408bd6da7..56c3ec0ffe2d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
@@ -50,7 +50,6 @@ import android.platform.test.annotations.Presubmit;
import android.util.Log;
import android.view.IWindowManager;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import com.android.server.am.AssistDataRequester;
@@ -154,7 +153,6 @@ public class AssistDataRequesterTest {
.noteOpNoThrow(eq(OP_ASSIST_SCREENSHOT), anyInt(), anyString(), any(), any());
}
- @FlakyTest(bugId = 280107567)
@Test
public void testRequestData() throws Exception {
setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
@@ -262,7 +260,6 @@ public class AssistDataRequesterTest {
assertReceivedDataCount(0, 1, 0, 1);
}
- @FlakyTest(bugId = 280107567)
@Test
public void testNoFetchScreenshots_expectNoScreenshotCallbacks() throws Exception {
setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
@@ -274,7 +271,6 @@ public class AssistDataRequesterTest {
assertReceivedDataCount(5, 5, 0, 0);
}
- @FlakyTest(bugId = 280107567)
@Test
public void testDisallowAssistScreenshot_expectNullScreenshotCallback() throws Exception {
setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
@@ -321,7 +317,8 @@ public class AssistDataRequesterTest {
assertEquals("Expected " + numPendingScreenshots + " pending screenshots, got "
+ mDataRequester.getPendingScreenshotCount(),
numPendingScreenshots, mDataRequester.getPendingScreenshotCount());
- assertFalse("Expected request NOT completed", mCallbacks.mRequestCompleted);
+ assertEquals("Expected request NOT completed, unless no pending data",
+ numPendingData == 0 && numPendingScreenshots == 0, mCallbacks.mRequestCompleted);
mGate.countDown();
waitForIdle(mHandler);
assertEquals("Expected " + numReceivedData + " data, received "
@@ -380,14 +377,7 @@ public class AssistDataRequesterTest {
@Override
public void onAssistRequestCompleted() {
- mHandler.post(() -> {
- try {
- mGate.await(10, TimeUnit.SECONDS);
- mRequestCompleted = true;
- } catch (InterruptedException e) {
- Log.e(TAG, "Failed to wait", e);
- }
- });
+ mRequestCompleted = true;
}
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
index 8bc4cedf6fce..db08eabac699 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -34,6 +34,7 @@ import static org.junit.Assert.assertTrue;
import android.platform.test.annotations.Presubmit;
import android.view.InputChannel;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.Before;
@@ -72,6 +73,7 @@ public class TaskPositioningControllerTests extends WindowTestsBase {
doReturn(mock(InputMonitor.class)).when(mDisplayContent).getInputMonitor();
}
+ @FlakyTest(bugId = 291067614)
@Test
public void testStartAndFinishPositioning() {
assertFalse(mTarget.isPositioningLocked());