summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Sudheer Shanka <sudheersai@google.com> 2020-10-09 07:11:25 +0000
committer Sudheer Shanka <sudheersai@google.com> 2020-10-12 23:32:03 +0000
commite83d8766d3d165e8c09f5244e91ce3bb4c06f2be (patch)
tree3374691aea57f724c8cafa9a939a8150891b0fad
parent7c3ee071f7400d41ad590253b85c64a3a18e06fa (diff)
Move logic for handling uid observers outside AMS - part2
- Do not depend on AMS lock anymore for handling uid observers. - Update AMS to not directly access internal state of UidObserverController. Bug: 163963556 Test: atest tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java Test: atest services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java Test: atest services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java Test: atest tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/OomAdjPerfTest.java Change-Id: I7ba7552eace7e713f882af36bd507f108f398d7a
-rw-r--r--services/core/java/com/android/server/am/ActiveUids.java44
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java125
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java2
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java2
-rw-r--r--services/core/java/com/android/server/am/UidObserverController.java392
-rw-r--r--services/core/java/com/android/server/am/UidRecord.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java127
-rw-r--r--services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java233
8 files changed, 622 insertions, 314 deletions
diff --git a/services/core/java/com/android/server/am/ActiveUids.java b/services/core/java/com/android/server/am/ActiveUids.java
index 4e1435e14679..c86c29f8ac2a 100644
--- a/services/core/java/com/android/server/am/ActiveUids.java
+++ b/services/core/java/com/android/server/am/ActiveUids.java
@@ -16,7 +16,12 @@
package com.android.server.am;
+import android.app.ActivityManager;
+import android.os.UserHandle;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.PrintWriter;
/** Class for tracking active uids for running processes. */
final class ActiveUids {
@@ -71,4 +76,43 @@ final class ActiveUids {
int indexOfKey(int uid) {
return mActiveUids.indexOfKey(uid);
}
+
+ boolean dump(PrintWriter pw, String dumpPackage, int dumpAppId,
+ String header, boolean needSep) {
+ boolean printed = false;
+ for (int i = 0; i < mActiveUids.size(); i++) {
+ final UidRecord uidRec = mActiveUids.valueAt(i);
+ if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != dumpAppId) {
+ continue;
+ }
+ if (!printed) {
+ printed = true;
+ if (needSep) {
+ pw.println();
+ }
+ pw.print(" "); pw.println(header);
+ }
+ pw.print(" UID "); UserHandle.formatUid(pw, uidRec.uid);
+ pw.print(": "); pw.println(uidRec);
+ pw.print(" curProcState="); pw.print(uidRec.mCurProcState);
+ pw.print(" curCapability=");
+ ActivityManager.printCapabilitiesFull(pw, uidRec.curCapability);
+ pw.println();
+ for (int j = uidRec.procRecords.size() - 1; j >= 0; j--) {
+ pw.print(" proc=");
+ pw.println(uidRec.procRecords.valueAt(j));
+ }
+ }
+ return printed;
+ }
+
+ void dumpProto(ProtoOutputStream proto, String dumpPackage, int dumpAppId, long fieldId) {
+ for (int i = 0; i < mActiveUids.size(); i++) {
+ UidRecord uidRec = mActiveUids.valueAt(i);
+ if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != dumpAppId) {
+ continue;
+ }
+ uidRec.dumpDebug(proto, fieldId);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index dbc77d81c251..84584b50860c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1979,7 +1979,6 @@ public class ActivityManagerService extends IActivityManager.Stub
mConstants = hasHandlerThread
? new ActivityManagerConstants(mContext, this, mHandler) : null;
final ActiveUids activeUids = new ActiveUids(this, false /* postChangesToAtm */);
- mUidObserverController = new UidObserverController(this);
mPlatformCompat = null;
mProcessList = injector.getProcessList(this);
mProcessList.init(this, activeUids, mPlatformCompat);
@@ -1997,6 +1996,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mServices = hasHandlerThread ? new ActiveServices(this) : null;
mSystemThread = null;
mUiHandler = injector.getUiHandler(null /* service */);
+ mUidObserverController = new UidObserverController(mUiHandler);
mUserController = hasHandlerThread ? new UserController(this) : null;
mPendingIntentController = hasHandlerThread
? new PendingIntentController(handlerThread.getLooper(), mUserController,
@@ -2077,7 +2077,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mCpHelper = new ContentProviderHelper(this, true);
mPackageWatchdog = PackageWatchdog.getInstance(mUiContext);
mAppErrors = new AppErrors(mUiContext, this, mPackageWatchdog);
- mUidObserverController = new UidObserverController(this);
+ mUidObserverController = new UidObserverController(mUiHandler);
final File systemDir = SystemServiceManager.ensureSystemDir();
@@ -8816,37 +8816,6 @@ public class ActivityManagerService extends IActivityManager.Stub
return -1;
}
- boolean dumpUids(PrintWriter pw, String dumpPackage, int dumpAppId, ActiveUids uids,
- String header, boolean needSep) {
- boolean printed = false;
- for (int i=0; i<uids.size(); i++) {
- UidRecord uidRec = uids.valueAt(i);
- if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != dumpAppId) {
- continue;
- }
- if (!printed) {
- printed = true;
- if (needSep) {
- pw.println();
- }
- pw.print(" ");
- pw.println(header);
- needSep = true;
- }
- pw.print(" UID "); UserHandle.formatUid(pw, uidRec.uid);
- pw.print(": "); pw.println(uidRec);
- pw.print(" curProcState="); pw.print(uidRec.mCurProcState);
- pw.print(" curCapability=");
- ActivityManager.printCapabilitiesFull(pw, uidRec.curCapability);
- pw.println();
- for (int j = uidRec.procRecords.size() - 1; j >= 0; j--) {
- pw.print(" proc=");
- pw.println(uidRec.procRecords.valueAt(j));
- }
- }
- return printed;
- }
-
void dumpBinderProxyInterfaceCounts(PrintWriter pw, String header) {
final BinderProxy.InterfaceCount[] proxyCounts = BinderProxy.getSortedInterfaceCounts(50);
@@ -9096,19 +9065,13 @@ public class ActivityManagerService extends IActivityManager.Stub
needSep = dumpProcessesToGc(pw, needSep, dumpPackage);
if (mProcessList.mActiveUids.size() > 0) {
- if (dumpUids(pw, dumpPackage, dumpAppId, mProcessList.mActiveUids,
- "UID states:", needSep)) {
- needSep = true;
- }
+ needSep |= mProcessList.mActiveUids.dump(pw, dumpPackage, dumpAppId,
+ "UID states:", needSep);
}
if (dumpAll) {
- if (mUidObserverController.mValidateUids.size() > 0) {
- if (dumpUids(pw, dumpPackage, dumpAppId, mUidObserverController.mValidateUids,
- "UID validation:", needSep)) {
- needSep = true;
- }
- }
+ needSep |= mUidObserverController.dumpValidateUids(pw,
+ dumpPackage, dumpAppId, "UID validation:", needSep);
}
if (needSep) {
@@ -9377,22 +9340,12 @@ public class ActivityManagerService extends IActivityManager.Stub
ActivityManagerServiceDumpProcessesProto.ACTIVE_INSTRUMENTATIONS);
}
- int whichAppId = getAppId(dumpPackage);
- for (int i = 0; i < mProcessList.mActiveUids.size(); i++) {
- UidRecord uidRec = mProcessList.mActiveUids.valueAt(i);
- if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
- continue;
- }
- uidRec.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.ACTIVE_UIDS);
- }
+ final int dumpAppId = getAppId(dumpPackage);
+ mProcessList.mActiveUids.dumpProto(proto, dumpPackage, dumpAppId,
+ ActivityManagerServiceDumpProcessesProto.ACTIVE_UIDS);
- for (int i = 0; i < mUidObserverController.mValidateUids.size(); i++) {
- UidRecord uidRec = mUidObserverController.mValidateUids.valueAt(i);
- if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
- continue;
- }
- uidRec.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.VALIDATE_UIDS);
- }
+ mUidObserverController.dumpValidateUidsProto(proto, dumpPackage, dumpAppId,
+ ActivityManagerServiceDumpProcessesProto.VALIDATE_UIDS);
if (mProcessList.getLruSizeLocked() > 0) {
long lruToken = proto.start(ActivityManagerServiceDumpProcessesProto.LRU_PROCS);
@@ -14891,6 +14844,58 @@ public class ActivityManagerService extends IActivityManager.Stub
return false;
}
+ private boolean isEphemeralLocked(int uid) {
+ final String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
+ if (packages == null || packages.length != 1) { // Ephemeral apps cannot share uid
+ return false;
+ }
+ return getPackageManagerInternalLocked().isPackageEphemeral(
+ UserHandle.getUserId(uid), packages[0]);
+ }
+
+ void enqueueUidChangeLocked(UidRecord uidRec, int uid, int change) {
+ uid = uidRec != null ? uidRec.uid : uid;
+ if (uid < 0) {
+ throw new IllegalArgumentException("No UidRecord or uid");
+ }
+
+ final int procState = uidRec != null
+ ? uidRec.setProcState : PROCESS_STATE_NONEXISTENT;
+ final long procStateSeq = uidRec != null ? uidRec.curProcStateSeq : 0;
+ final int capability = uidRec != null ? uidRec.setCapability : 0;
+ final boolean ephemeral = uidRec != null ? uidRec.ephemeral : isEphemeralLocked(uid);
+
+ if (uidRec != null && !uidRec.idle && (change & UidRecord.CHANGE_GONE) != 0) {
+ // If this uid is going away, and we haven't yet reported it is gone,
+ // then do so now.
+ change |= UidRecord.CHANGE_IDLE;
+ }
+ final int enqueuedChange = mUidObserverController.enqueueUidChange(
+ uid, change, procState, procStateSeq, capability, ephemeral);
+ if (uidRec != null) {
+ uidRec.lastReportedChange = enqueuedChange;
+ uidRec.updateLastDispatchedProcStateSeq(enqueuedChange);
+ }
+
+ // Directly update the power manager, since we sit on top of it and it is critical
+ // it be kept in sync (so wake locks will be held as soon as appropriate).
+ if (mLocalPowerManager != null) {
+ // TODO: dispatch cached/uncached changes here, so we don't need to report
+ // all proc state changes.
+ if ((enqueuedChange & UidRecord.CHANGE_ACTIVE) != 0) {
+ mLocalPowerManager.uidActive(uid);
+ }
+ if ((enqueuedChange & UidRecord.CHANGE_IDLE) != 0) {
+ mLocalPowerManager.uidIdle(uid);
+ }
+ if ((enqueuedChange & UidRecord.CHANGE_GONE) != 0) {
+ mLocalPowerManager.uidGone(uid);
+ } else {
+ mLocalPowerManager.updateUidProcState(uid, procState);
+ }
+ }
+ }
+
final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
synchronized (mProcessStats.mLock) {
if (proc.thread != null && proc.baseProcessTracker != null) {
@@ -15156,7 +15161,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@GuardedBy("this")
final void doStopUidLocked(int uid, final UidRecord uidRec) {
mServices.stopInBackgroundLocked(uid);
- mUidObserverController.enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE);
+ enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE);
}
/**
@@ -16706,7 +16711,7 @@ public class ActivityManagerService extends IActivityManager.Stub
+ totalTime + ". Uid: " + callingUid + " procStateSeq: "
+ procStateSeq + " UidRec: " + record
+ " validateUidRec: "
- + mUidObserverController.mValidateUids.get(callingUid));
+ + mUidObserverController.getValidateUidRecord(callingUid));
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 58ac2dc869ce..9d49236191a9 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1147,7 +1147,7 @@ public final class OomAdjuster {
uidRec.setWhitelist = uidRec.curWhitelist;
uidRec.setIdle = uidRec.idle;
mService.mAtmInternal.onUidProcStateChanged(uidRec.uid, uidRec.setProcState);
- mService.mUidObserverController.enqueueUidChangeLocked(uidRec, -1, uidChange);
+ mService.enqueueUidChangeLocked(uidRec, -1, uidChange);
mService.noteUidProcessState(uidRec.uid, uidRec.getCurProcState(),
uidRec.curCapability);
if (uidRec.foregroundServices) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index cf0223bac289..6f6cad043a42 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2948,7 +2948,7 @@ public final class ProcessList {
// No more processes using this uid, tell clients it is gone.
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"No more processes in " + uidRecord);
- mService.mUidObserverController.enqueueUidChangeLocked(uidRecord, -1,
+ mService.enqueueUidChangeLocked(uidRecord, -1,
UidRecord.CHANGE_GONE);
EventLogTags.writeAmUidStopped(uid);
mActiveUids.remove(uid);
diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java
index 4d9260aec62e..b3488c3850b5 100644
--- a/services/core/java/com/android/server/am/UidObserverController.java
+++ b/services/core/java/com/android/server/am/UidObserverController.java
@@ -23,11 +23,13 @@ import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
import android.app.ActivityManager;
import android.app.ActivityManagerProto;
import android.app.IUidObserver;
+import android.os.Handler;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
import android.util.proto.ProtoUtils;
@@ -40,158 +42,150 @@ import java.io.PrintWriter;
import java.util.ArrayList;
public class UidObserverController {
- private final ActivityManagerService mService;
+ /** If a UID observer takes more than this long, send a WTF. */
+ private static final int SLOW_UID_OBSERVER_THRESHOLD_MS = 20;
+
+ private final Handler mHandler;
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
final RemoteCallbackList<IUidObserver> mUidObservers = new RemoteCallbackList<>();
- UidRecord.ChangeItem[] mActiveUidChanges = new UidRecord.ChangeItem[5];
- final ArrayList<UidRecord.ChangeItem> mPendingUidChanges = new ArrayList<>();
- final ArrayList<UidRecord.ChangeItem> mAvailUidChanges = new ArrayList<>();
+ @GuardedBy("mLock")
+ private final SparseArray<ChangeRecord> mPendingUidChanges = new SparseArray<>();
+ @GuardedBy("mLock")
+ private final ArrayList<ChangeRecord> mAvailUidChanges = new ArrayList<>();
+
+ private ChangeRecord[] mActiveUidChanges = new ChangeRecord[5];
/** Total # of UID change events dispatched, shown in dumpsys. */
- int mUidChangeDispatchCount;
+ @GuardedBy("mLock")
+ private int mUidChangeDispatchCount;
- /** If a UID observer takes more than this long, send a WTF. */
- private static final int SLOW_UID_OBSERVER_THRESHOLD_MS = 20;
+ private final Runnable mDispatchRunnable = this::dispatchUidsChanged;
/**
* This is for verifying the UID report flow.
*/
- static final boolean VALIDATE_UID_STATES = true;
- final ActiveUids mValidateUids;
+ private static final boolean VALIDATE_UID_STATES = true;
+ private final ActiveUids mValidateUids;
- UidObserverController(ActivityManagerService service) {
- mService = service;
- mValidateUids = new ActiveUids(mService, false /* postChangesToAtm */);
+ UidObserverController(Handler handler) {
+ mHandler = handler;
+ mValidateUids = new ActiveUids(null /* service */, false /* postChangesToAtm */);
}
- @GuardedBy("mService")
void register(IUidObserver observer, int which, int cutpoint, String callingPackage,
int callingUid) {
- mUidObservers.register(observer, new UidObserverRegistration(callingUid,
- callingPackage, which, cutpoint));
+ synchronized (mLock) {
+ mUidObservers.register(observer, new UidObserverRegistration(callingUid,
+ callingPackage, which, cutpoint));
+ }
}
- @GuardedBy("mService")
void unregister(IUidObserver observer) {
- mUidObservers.unregister(observer);
+ synchronized (mLock) {
+ mUidObservers.unregister(observer);
+ }
}
- @GuardedBy("mService")
- final void enqueueUidChangeLocked(UidRecord uidRec, int uid, int change) {
- final UidRecord.ChangeItem pendingChange;
- if (uidRec == null || uidRec.pendingChange == null) {
+ int enqueueUidChange(int uid, int change, int procState, long procStateSeq,
+ int capability, boolean ephemeral) {
+ synchronized (mLock) {
if (mPendingUidChanges.size() == 0) {
if (DEBUG_UID_OBSERVERS) {
Slog.i(TAG_UID_OBSERVERS, "*** Enqueueing dispatch uid changed!");
}
- mService.mUiHandler.post(this::dispatchUidsChanged);
+ mHandler.post(mDispatchRunnable);
}
- final int size = mAvailUidChanges.size();
- if (size > 0) {
- pendingChange = mAvailUidChanges.remove(size - 1);
- if (DEBUG_UID_OBSERVERS) {
- Slog.i(TAG_UID_OBSERVERS, "Retrieving available item: " + pendingChange);
- }
+
+ ChangeRecord changeRecord = mPendingUidChanges.get(uid);
+ if (changeRecord == null) {
+ changeRecord = getOrCreateChangeRecordLocked();
+ mPendingUidChanges.put(uid, changeRecord);
} else {
- pendingChange = new UidRecord.ChangeItem();
- if (DEBUG_UID_OBSERVERS) {
- Slog.i(TAG_UID_OBSERVERS, "Allocating new item: " + pendingChange);
- }
- }
- if (uidRec != null) {
- uidRec.pendingChange = pendingChange;
- if ((change & UidRecord.CHANGE_GONE) != 0 && !uidRec.idle) {
- // If this uid is going away, and we haven't yet reported it is gone,
- // then do so now.
- change |= UidRecord.CHANGE_IDLE;
- }
- } else if (uid < 0) {
- throw new IllegalArgumentException("No UidRecord or uid");
- }
- pendingChange.uidRecord = uidRec;
- pendingChange.uid = uidRec != null ? uidRec.uid : uid;
- mPendingUidChanges.add(pendingChange);
- } else {
- pendingChange = uidRec.pendingChange;
- // If there is no change in idle or active state, then keep whatever was pending.
- if ((change & (UidRecord.CHANGE_IDLE | UidRecord.CHANGE_ACTIVE)) == 0) {
- change |= (pendingChange.change & (UidRecord.CHANGE_IDLE
- | UidRecord.CHANGE_ACTIVE));
- }
- // If there is no change in cached or uncached state, then keep whatever was pending.
- if ((change & (UidRecord.CHANGE_CACHED | UidRecord.CHANGE_UNCACHED)) == 0) {
- change |= (pendingChange.change & (UidRecord.CHANGE_CACHED
- | UidRecord.CHANGE_UNCACHED));
- }
- // If this is a report of the UID being gone, then we shouldn't keep any previous
- // report of it being active or cached. (That is, a gone uid is never active,
- // and never cached.)
- if ((change & UidRecord.CHANGE_GONE) != 0) {
- change &= ~(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_CACHED);
- if (!uidRec.idle) {
- // If this uid is going away, and we haven't yet reported it is gone,
- // then do so now.
- change |= UidRecord.CHANGE_IDLE;
- }
+ change = mergeWithPendingChange(change, changeRecord.change);
}
+
+ changeRecord.uid = uid;
+ changeRecord.change = change;
+ changeRecord.procState = procState;
+ changeRecord.procStateSeq = procStateSeq;
+ changeRecord.capability = capability;
+ changeRecord.ephemeral = ephemeral;
+
+ return changeRecord.change;
}
- pendingChange.change = change;
- pendingChange.processState = uidRec != null
- ? uidRec.setProcState : PROCESS_STATE_NONEXISTENT;
- pendingChange.capability = uidRec != null ? uidRec.setCapability : 0;
- pendingChange.ephemeral = uidRec != null ? uidRec.ephemeral : isEphemeralLocked(uid);
- pendingChange.procStateSeq = uidRec != null ? uidRec.curProcStateSeq : 0;
- if (uidRec != null) {
- uidRec.lastReportedChange = change;
- uidRec.updateLastDispatchedProcStateSeq(change);
+ }
+
+ SparseArray<ChangeRecord> getPendingUidChangesForTest() {
+ return mPendingUidChanges;
+ }
+
+ ActiveUids getValidateUidsForTest() {
+ return mValidateUids;
+ }
+
+ @VisibleForTesting
+ static int mergeWithPendingChange(int currentChange, int pendingChange) {
+ // If there is no change in idle or active state, then keep whatever was pending.
+ if ((currentChange & (UidRecord.CHANGE_IDLE | UidRecord.CHANGE_ACTIVE)) == 0) {
+ currentChange |= (pendingChange & (UidRecord.CHANGE_IDLE
+ | UidRecord.CHANGE_ACTIVE));
+ }
+ // If there is no change in cached or uncached state, then keep whatever was pending.
+ if ((currentChange & (UidRecord.CHANGE_CACHED | UidRecord.CHANGE_UNCACHED)) == 0) {
+ currentChange |= (pendingChange & (UidRecord.CHANGE_CACHED
+ | UidRecord.CHANGE_UNCACHED));
}
+ // If this is a report of the UID being gone, then we shouldn't keep any previous
+ // report of it being active or cached. (That is, a gone uid is never active,
+ // and never cached.)
+ if ((currentChange & UidRecord.CHANGE_GONE) != 0) {
+ currentChange &= ~(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_CACHED);
+ }
+ return currentChange;
+ }
- // Directly update the power manager, since we sit on top of it and it is critical
- // it be kept in sync (so wake locks will be held as soon as appropriate).
- if (mService.mLocalPowerManager != null) {
- // TO DO: dispatch cached/uncached changes here, so we don't need to report
- // all proc state changes.
- if ((change & UidRecord.CHANGE_ACTIVE) != 0) {
- mService.mLocalPowerManager.uidActive(pendingChange.uid);
- }
- if ((change & UidRecord.CHANGE_IDLE) != 0) {
- mService.mLocalPowerManager.uidIdle(pendingChange.uid);
+ @GuardedBy("mLock")
+ private ChangeRecord getOrCreateChangeRecordLocked() {
+ final ChangeRecord changeRecord;
+ final int size = mAvailUidChanges.size();
+ if (size > 0) {
+ changeRecord = mAvailUidChanges.remove(size - 1);
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "Retrieving available item: " + changeRecord);
}
- if ((change & UidRecord.CHANGE_GONE) != 0) {
- mService.mLocalPowerManager.uidGone(pendingChange.uid);
- } else {
- mService.mLocalPowerManager.updateUidProcState(pendingChange.uid,
- pendingChange.processState);
+ } else {
+ changeRecord = new ChangeRecord();
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "Allocating new item: " + changeRecord);
}
}
+ return changeRecord;
}
@VisibleForTesting
void dispatchUidsChanged() {
- int numUidChanges;
- synchronized (mService) {
+ final int numUidChanges;
+ synchronized (mLock) {
numUidChanges = mPendingUidChanges.size();
if (mActiveUidChanges.length < numUidChanges) {
- mActiveUidChanges = new UidRecord.ChangeItem[numUidChanges];
+ mActiveUidChanges = new ChangeRecord[numUidChanges];
}
for (int i = 0; i < numUidChanges; i++) {
- final UidRecord.ChangeItem change = mPendingUidChanges.get(i);
- mActiveUidChanges[i] = change;
- if (change.uidRecord != null) {
- change.uidRecord.pendingChange = null;
- change.uidRecord = null;
- }
+ mActiveUidChanges[i] = mPendingUidChanges.valueAt(i);
}
mPendingUidChanges.clear();
if (DEBUG_UID_OBSERVERS) {
Slog.i(TAG_UID_OBSERVERS, "*** Delivering " + numUidChanges + " uid changes");
}
+ mUidChangeDispatchCount += numUidChanges;
}
- mUidChangeDispatchCount += numUidChanges;
int i = mUidObservers.beginBroadcast();
- while (i > 0) {
- i--;
+ while (i-- > 0) {
dispatchUidsChangedForObserver(mUidObservers.getBroadcastItem(i),
(UidObserverRegistration) mUidObservers.getBroadcastCookie(i), numUidChanges);
}
@@ -199,7 +193,7 @@ public class UidObserverController {
if (VALIDATE_UID_STATES && mUidObservers.getRegisteredCallbackCount() > 0) {
for (int j = 0; j < numUidChanges; ++j) {
- final UidRecord.ChangeItem item = mActiveUidChanges[j];
+ final ChangeRecord item = mActiveUidChanges[j];
if ((item.change & UidRecord.CHANGE_GONE) != 0) {
mValidateUids.remove(item.uid);
} else {
@@ -213,14 +207,14 @@ public class UidObserverController {
} else if ((item.change & UidRecord.CHANGE_ACTIVE) != 0) {
validateUid.idle = false;
}
- validateUid.setCurProcState(validateUid.setProcState = item.processState);
+ validateUid.setCurProcState(validateUid.setProcState = item.procState);
validateUid.curCapability = validateUid.setCapability = item.capability;
validateUid.lastDispatchedProcStateSeq = item.procStateSeq;
}
}
}
- synchronized (mService) {
+ synchronized (mLock) {
for (int j = 0; j < numUidChanges; j++) {
mAvailUidChanges.add(mActiveUidChanges[j]);
}
@@ -234,7 +228,7 @@ public class UidObserverController {
}
try {
for (int j = 0; j < changesSize; j++) {
- UidRecord.ChangeItem item = mActiveUidChanges[j];
+ final ChangeRecord item = mActiveUidChanges[j];
final int change = item.change;
if (change == UidRecord.CHANGE_PROCSTATE
&& (reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) == 0) {
@@ -285,7 +279,7 @@ public class UidObserverController {
if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
if (DEBUG_UID_OBSERVERS) {
Slog.i(TAG_UID_OBSERVERS, "UID CHANGED uid=" + item.uid
- + ": " + item.processState + ": " + item.capability);
+ + ": " + item.procState + ": " + item.capability);
}
boolean doReport = true;
if (reg.mCutpoint >= ActivityManager.MIN_PROCESS_STATE) {
@@ -293,17 +287,17 @@ public class UidObserverController {
ActivityManager.PROCESS_STATE_UNKNOWN);
if (lastState != ActivityManager.PROCESS_STATE_UNKNOWN) {
final boolean lastAboveCut = lastState <= reg.mCutpoint;
- final boolean newAboveCut = item.processState <= reg.mCutpoint;
+ final boolean newAboveCut = item.procState <= reg.mCutpoint;
doReport = lastAboveCut != newAboveCut;
} else {
- doReport = item.processState != PROCESS_STATE_NONEXISTENT;
+ doReport = item.procState != PROCESS_STATE_NONEXISTENT;
}
}
if (doReport) {
if (reg.mLastProcStates != null) {
- reg.mLastProcStates.put(item.uid, item.processState);
+ reg.mLastProcStates.put(item.uid, item.procState);
}
- observer.onUidStateChanged(item.uid, item.processState,
+ observer.onUidStateChanged(item.uid, item.procState,
item.procStateSeq, item.capability);
}
}
@@ -320,94 +314,82 @@ public class UidObserverController {
}
}
- private boolean isEphemeralLocked(int uid) {
- final String[] packages = mService.mContext.getPackageManager().getPackagesForUid(uid);
- if (packages == null || packages.length != 1) { // Ephemeral apps cannot share uid
- return false;
- }
- return mService.getPackageManagerInternalLocked().isPackageEphemeral(
- UserHandle.getUserId(uid), packages[0]);
+ UidRecord getValidateUidRecord(int uid) {
+ return mValidateUids.get(uid);
}
- @GuardedBy("mService")
void dump(PrintWriter pw, String dumpPackage) {
- final int count = mUidObservers.getRegisteredCallbackCount();
- boolean printed = false;
- for (int i = 0; i < count; i++) {
- final UidObserverRegistration reg = (UidObserverRegistration)
- mUidObservers.getRegisteredCallbackCookie(i);
- if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) {
- if (!printed) {
- pw.println(" mUidObservers:");
- printed = true;
- }
- pw.print(" "); UserHandle.formatUid(pw, reg.mUid);
- pw.print(" "); pw.print(reg.mPkg);
- final IUidObserver observer = mUidObservers.getRegisteredCallbackItem(i);
- pw.print(" "); pw.print(observer.getClass().getTypeName()); pw.print(":");
- if ((reg.mWhich & ActivityManager.UID_OBSERVER_IDLE) != 0) {
- pw.print(" IDLE");
- }
- if ((reg.mWhich & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
- pw.print(" ACT");
- }
- if ((reg.mWhich & ActivityManager.UID_OBSERVER_GONE) != 0) {
- pw.print(" GONE");
- }
- if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
- pw.print(" STATE");
- pw.print(" (cut="); pw.print(reg.mCutpoint);
- pw.print(")");
- }
- pw.println();
- if (reg.mLastProcStates != null) {
- final int size = reg.mLastProcStates.size();
- for (int j = 0; j < size; j++) {
- pw.print(" Last ");
- UserHandle.formatUid(pw, reg.mLastProcStates.keyAt(j));
- pw.print(": "); pw.println(reg.mLastProcStates.valueAt(j));
+ synchronized (mLock) {
+ final int count = mUidObservers.getRegisteredCallbackCount();
+ boolean printed = false;
+ for (int i = 0; i < count; i++) {
+ final UidObserverRegistration reg = (UidObserverRegistration)
+ mUidObservers.getRegisteredCallbackCookie(i);
+ if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) {
+ if (!printed) {
+ pw.println(" mUidObservers:");
+ printed = true;
}
+ reg.dump(pw, mUidObservers.getRegisteredCallbackItem(i));
}
}
- }
- pw.println();
- pw.print(" mUidChangeDispatchCount=");
- pw.print(mUidChangeDispatchCount);
- pw.println();
- pw.println(" Slow UID dispatches:");
- final int size = mUidObservers.beginBroadcast();
- for (int i = 0; i < size; i++) {
- UidObserverRegistration r =
- (UidObserverRegistration) mUidObservers.getBroadcastCookie(i);
- pw.print(" ");
- pw.print(mUidObservers.getBroadcastItem(i).getClass().getTypeName());
- pw.print(": ");
- pw.print(r.mSlowDispatchCount);
- pw.print(" / Max ");
- pw.print(r.mMaxDispatchTime);
- pw.println("ms");
+ pw.println();
+ pw.print(" mUidChangeDispatchCount=");
+ pw.print(mUidChangeDispatchCount);
+ pw.println();
+ pw.println(" Slow UID dispatches:");
+ for (int i = 0; i < count; i++) {
+ final UidObserverRegistration reg = (UidObserverRegistration)
+ mUidObservers.getRegisteredCallbackCookie(i);
+ pw.print(" ");
+ pw.print(mUidObservers.getRegisteredCallbackItem(i).getClass().getTypeName());
+ pw.print(": ");
+ pw.print(reg.mSlowDispatchCount);
+ pw.print(" / Max ");
+ pw.print(reg.mMaxDispatchTime);
+ pw.println("ms");
+ }
}
- mUidObservers.finishBroadcast();
}
- @GuardedBy("mService")
void dumpDebug(ProtoOutputStream proto, String dumpPackage) {
- final int count = mUidObservers.getRegisteredCallbackCount();
- for (int i = 0; i < count; i++) {
- final UidObserverRegistration reg = (UidObserverRegistration)
- mUidObservers.getRegisteredCallbackCookie(i);
- if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) {
- reg.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.UID_OBSERVERS);
+ synchronized (mLock) {
+ final int count = mUidObservers.getRegisteredCallbackCount();
+ for (int i = 0; i < count; i++) {
+ final UidObserverRegistration reg = (UidObserverRegistration)
+ mUidObservers.getRegisteredCallbackCookie(i);
+ if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) {
+ reg.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.UID_OBSERVERS);
+ }
}
}
}
+ boolean dumpValidateUids(PrintWriter pw, String dumpPackage, int dumpAppId,
+ String header, boolean needSep) {
+ return mValidateUids.dump(pw, dumpPackage, dumpAppId, header, needSep);
+ }
+
+ void dumpValidateUidsProto(ProtoOutputStream proto, String dumpPackage,
+ int dumpAppId, long fieldId) {
+ mValidateUids.dumpProto(proto, dumpPackage, dumpAppId, fieldId);
+ }
+
+ static final class ChangeRecord {
+ public int uid;
+ public int change;
+ public int procState;
+ public int capability;
+ public boolean ephemeral;
+ public long procStateSeq;
+ }
+
private static final class UidObserverRegistration {
- final int mUid;
- final String mPkg;
- final int mWhich;
- final int mCutpoint;
+ private final int mUid;
+ private final String mPkg;
+ private final int mWhich;
+ private final int mCutpoint;
/**
* Total # of callback calls that took more than {@link #SLOW_UID_OBSERVER_THRESHOLD_MS}.
@@ -439,10 +421,42 @@ public class UidObserverController {
this.mPkg = pkg;
this.mWhich = which;
this.mCutpoint = cutpoint;
- if (cutpoint >= ActivityManager.MIN_PROCESS_STATE) {
- mLastProcStates = new SparseIntArray();
- } else {
- mLastProcStates = null;
+ mLastProcStates = cutpoint >= ActivityManager.MIN_PROCESS_STATE
+ ? new SparseIntArray() : null;
+ }
+
+ void dump(PrintWriter pw, IUidObserver observer) {
+ pw.print(" ");
+ UserHandle.formatUid(pw, mUid);
+ pw.print(" ");
+ pw.print(mPkg);
+ pw.print(" ");
+ pw.print(observer.getClass().getTypeName());
+ pw.print(":");
+ if ((mWhich & ActivityManager.UID_OBSERVER_IDLE) != 0) {
+ pw.print(" IDLE");
+ }
+ if ((mWhich & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
+ pw.print(" ACT");
+ }
+ if ((mWhich & ActivityManager.UID_OBSERVER_GONE) != 0) {
+ pw.print(" GONE");
+ }
+ if ((mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
+ pw.print(" STATE");
+ pw.print(" (cut=");
+ pw.print(mCutpoint);
+ pw.print(")");
+ }
+ pw.println();
+ if (mLastProcStates != null) {
+ final int size = mLastProcStates.size();
+ for (int j = 0; j < size; j++) {
+ pw.print(" Last ");
+ UserHandle.formatUid(pw, mLastProcStates.keyAt(j));
+ pw.print(": ");
+ pw.println(mLastProcStates.valueAt(j));
+ }
}
}
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index c84ccb298bef..dfd6149cfcb7 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -107,17 +107,6 @@ public final class UidRecord {
UidRecordProto.CHANGE_UNCACHED,
};
- static final class ChangeItem {
- UidRecord uidRecord;
- int uid;
- int change;
- int processState;
- int capability;
- boolean ephemeral;
- long procStateSeq;
- }
-
- ChangeItem pendingChange;
int lastReportedChange;
public UidRecord(int _uid) {
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 03dce4c62fe8..faf7169a68c7 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -68,6 +68,7 @@ import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.util.SparseArray;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
@@ -76,6 +77,7 @@ import androidx.test.filters.SmallTest;
import com.android.server.LocalServices;
import com.android.server.am.ProcessList.IsolatedUidRange;
import com.android.server.am.ProcessList.IsolatedUidRangeAllocator;
+import com.android.server.am.UidObserverController.ChangeRecord;
import com.android.server.appop.AppOpsService;
import com.android.server.wm.ActivityTaskManagerService;
@@ -539,15 +541,15 @@ public class ActivityManagerServiceTest {
ActivityManager.PROCESS_STATE_CACHED_ACTIVITY,
ActivityManager.PROCESS_STATE_TOP
};
- final Map<Integer, UidRecord.ChangeItem> changeItems = new HashMap<>();
+ final Map<Integer, ChangeRecord> changeItems = new HashMap<>();
for (int i = 0; i < changesForPendingUidRecords.length; ++i) {
- final UidRecord.ChangeItem pendingChange = new UidRecord.ChangeItem();
+ final ChangeRecord pendingChange = new ChangeRecord();
pendingChange.change = changesForPendingUidRecords[i];
pendingChange.uid = i;
- pendingChange.processState = procStatesForPendingUidRecords[i];
+ pendingChange.procState = procStatesForPendingUidRecords[i];
pendingChange.procStateSeq = i;
changeItems.put(changesForPendingUidRecords[i], pendingChange);
- mAms.mUidObserverController.mPendingUidChanges.add(pendingChange);
+ addPendingUidChange(pendingChange);
}
mAms.mUidObserverController.dispatchUidsChanged();
@@ -602,7 +604,7 @@ public class ActivityManagerServiceTest {
verifyObserverReceivedChanges(observerToTest, changesToVerify, changeItems,
(observer, changeItem) -> {
verify(observer).onUidStateChanged(changeItem.uid,
- changeItem.processState, changeItem.procStateSeq,
+ changeItem.procState, changeItem.procStateSeq,
ActivityManager.PROCESS_CAPABILITY_NONE);
});
}
@@ -612,14 +614,14 @@ public class ActivityManagerServiceTest {
}
private interface ObserverChangesVerifier {
- void verify(IUidObserver observer, UidRecord.ChangeItem changeItem) throws RemoteException;
+ void verify(IUidObserver observer, ChangeRecord changeItem) throws RemoteException;
}
private void verifyObserverReceivedChanges(IUidObserver observer, int[] changesToVerify,
- Map<Integer, UidRecord.ChangeItem> changeItems, ObserverChangesVerifier verifier)
+ Map<Integer, ChangeRecord> changeItems, ObserverChangesVerifier verifier)
throws RemoteException {
for (int change : changesToVerify) {
- final UidRecord.ChangeItem changeItem = changeItems.get(change);
+ final ChangeRecord changeItem = changeItems.get(change);
verifier.verify(observer, changeItem);
}
}
@@ -641,59 +643,59 @@ public class ActivityManagerServiceTest {
// So, resetting the mock here.
Mockito.reset(observer);
- final UidRecord.ChangeItem changeItem = new UidRecord.ChangeItem();
+ final ChangeRecord changeItem = new ChangeRecord();
changeItem.uid = TEST_UID;
changeItem.change = UidRecord.CHANGE_PROCSTATE;
- changeItem.processState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+ changeItem.procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
changeItem.procStateSeq = 111;
- mAms.mUidObserverController.mPendingUidChanges.add(changeItem);
+ addPendingUidChange(changeItem);
mAms.mUidObserverController.dispatchUidsChanged();
// First process state message is always delivered regardless of whether the process state
// change is above or below the cutpoint (PROCESS_STATE_SERVICE).
verify(observer).onUidStateChanged(TEST_UID,
- changeItem.processState, changeItem.procStateSeq,
+ changeItem.procState, changeItem.procStateSeq,
ActivityManager.PROCESS_CAPABILITY_NONE);
verifyNoMoreInteractions(observer);
- changeItem.processState = ActivityManager.PROCESS_STATE_RECEIVER;
- mAms.mUidObserverController.mPendingUidChanges.add(changeItem);
+ changeItem.procState = ActivityManager.PROCESS_STATE_RECEIVER;
+ addPendingUidChange(changeItem);
mAms.mUidObserverController.dispatchUidsChanged();
// Previous process state change is below cutpoint (PROCESS_STATE_SERVICE) and
// the current process state change is also below cutpoint, so no callback will be invoked.
verifyNoMoreInteractions(observer);
- changeItem.processState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
- mAms.mUidObserverController.mPendingUidChanges.add(changeItem);
+ changeItem.procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+ addPendingUidChange(changeItem);
mAms.mUidObserverController.dispatchUidsChanged();
// Previous process state change is below cutpoint (PROCESS_STATE_SERVICE) and
// the current process state change is above cutpoint, so callback will be invoked with the
// current process state change.
verify(observer).onUidStateChanged(TEST_UID,
- changeItem.processState, changeItem.procStateSeq,
+ changeItem.procState, changeItem.procStateSeq,
ActivityManager.PROCESS_CAPABILITY_NONE);
verifyNoMoreInteractions(observer);
- changeItem.processState = ActivityManager.PROCESS_STATE_TOP;
- mAms.mUidObserverController.mPendingUidChanges.add(changeItem);
+ changeItem.procState = ActivityManager.PROCESS_STATE_TOP;
+ addPendingUidChange(changeItem);
mAms.mUidObserverController.dispatchUidsChanged();
// Previous process state change is above cutpoint (PROCESS_STATE_SERVICE) and
// the current process state change is also above cutpoint, so no callback will be invoked.
verifyNoMoreInteractions(observer);
- changeItem.processState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
- mAms.mUidObserverController.mPendingUidChanges.add(changeItem);
+ changeItem.procState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+ addPendingUidChange(changeItem);
mAms.mUidObserverController.dispatchUidsChanged();
// Previous process state change is above cutpoint (PROCESS_STATE_SERVICE) and
// the current process state change is below cutpoint, so callback will be invoked with the
// current process state change.
verify(observer).onUidStateChanged(TEST_UID,
- changeItem.processState, changeItem.procStateSeq,
+ changeItem.procState, changeItem.procStateSeq,
ActivityManager.PROCESS_CAPABILITY_NONE);
verifyNoMoreInteractions(observer);
}
/**
- * This test verifies that {@link ActivityManagerService#mValidateUids} which is a
+ * This test verifies that {@link UidObserverController#getValidateUidsForTest()} which is a
* part of dumpsys is correctly updated.
*/
@Test
@@ -707,45 +709,45 @@ public class ActivityManagerServiceTest {
ActivityManager.PROCESS_STATE_SERVICE,
ActivityManager.PROCESS_STATE_RECEIVER
};
- final ArrayList<UidRecord.ChangeItem> pendingItemsForUids =
+ final ArrayList<ChangeRecord> pendingItemsForUids =
new ArrayList<>(changesForPendingItems.length);
for (int i = 0; i < changesForPendingItems.length; ++i) {
- final UidRecord.ChangeItem item = new UidRecord.ChangeItem();
+ final ChangeRecord item = new ChangeRecord();
item.uid = i;
item.change = changesForPendingItems[i];
- item.processState = procStatesForPendingItems[i];
+ item.procState = procStatesForPendingItems[i];
pendingItemsForUids.add(i, item);
}
// Verify that when there no observers listening to uid state changes, then there will
// be no changes to validateUids.
- mAms.mUidObserverController.mPendingUidChanges.addAll(pendingItemsForUids);
+ addPendingUidChanges(pendingItemsForUids);
mAms.mUidObserverController.dispatchUidsChanged();
assertEquals("No observers registered, so validateUids should be empty",
- 0, mAms.mUidObserverController.mValidateUids.size());
+ 0, mAms.mUidObserverController.getValidateUidsForTest().size());
final IUidObserver observer = mock(IUidObserver.Stub.class);
when(observer.asBinder()).thenReturn((IBinder) observer);
mAms.registerUidObserver(observer, 0, 0, null);
// Verify that when observers are registered, then validateUids is correctly updated.
- mAms.mUidObserverController.mPendingUidChanges.addAll(pendingItemsForUids);
+ addPendingUidChanges(pendingItemsForUids);
mAms.mUidObserverController.dispatchUidsChanged();
for (int i = 0; i < pendingItemsForUids.size(); ++i) {
- final UidRecord.ChangeItem item = pendingItemsForUids.get(i);
+ final ChangeRecord item = pendingItemsForUids.get(i);
final UidRecord validateUidRecord =
- mAms.mUidObserverController.mValidateUids.get(item.uid);
+ mAms.mUidObserverController.getValidateUidsForTest().get(item.uid);
if ((item.change & UidRecord.CHANGE_GONE) != 0) {
assertNull("validateUidRecord should be null since the change is either "
+ "CHANGE_GONE or CHANGE_GONE_IDLE", validateUidRecord);
} else {
assertNotNull("validateUidRecord should not be null since the change is neither "
+ "CHANGE_GONE nor CHANGE_GONE_IDLE", validateUidRecord);
- assertEquals("processState: " + item.processState + " curProcState: "
+ assertEquals("processState: " + item.procState + " curProcState: "
+ validateUidRecord.getCurProcState() + " should have been equal",
- item.processState, validateUidRecord.getCurProcState());
- assertEquals("processState: " + item.processState + " setProcState: "
+ item.procState, validateUidRecord.getCurProcState());
+ assertEquals("processState: " + item.procState + " setProcState: "
+ validateUidRecord.getCurProcState() + " should have been equal",
- item.processState, validateUidRecord.setProcState);
+ item.procState, validateUidRecord.setProcState);
if (item.change == UidRecord.CHANGE_IDLE) {
assertTrue("UidRecord.idle should be updated to true for CHANGE_IDLE",
validateUidRecord.idle);
@@ -759,19 +761,19 @@ public class ActivityManagerServiceTest {
// Verify that when uid state changes to CHANGE_GONE or CHANGE_GONE_IDLE, then it
// will be removed from validateUids.
assertNotEquals("validateUids should not be empty", 0,
- mAms.mUidObserverController.mValidateUids.size());
+ mAms.mUidObserverController.getValidateUidsForTest().size());
for (int i = 0; i < pendingItemsForUids.size(); ++i) {
- final UidRecord.ChangeItem item = pendingItemsForUids.get(i);
+ final ChangeRecord item = pendingItemsForUids.get(i);
// Assign CHANGE_GONE_IDLE to some items and CHANGE_GONE to the others, using even/odd
// distribution for this assignment.
item.change = (i % 2) == 0 ? (UidRecord.CHANGE_GONE | UidRecord.CHANGE_IDLE)
: UidRecord.CHANGE_GONE;
}
- mAms.mUidObserverController.mPendingUidChanges.addAll(pendingItemsForUids);
+ addPendingUidChanges(pendingItemsForUids);
mAms.mUidObserverController.dispatchUidsChanged();
assertEquals("validateUids should be empty, size="
- + mAms.mUidObserverController.mValidateUids.size(),
- 0, mAms.mUidObserverController.mValidateUids.size());
+ + mAms.mUidObserverController.getValidateUidsForTest().size(),
+ 0, mAms.mUidObserverController.getValidateUidsForTest().size());
}
@Test
@@ -784,8 +786,6 @@ public class ActivityManagerServiceTest {
// Add a pending change for TEST_UID and verify enqueueUidChangeLocked still works as
// expected.
- final UidRecord.ChangeItem changeItem = new UidRecord.ChangeItem();
- uidRecord.pendingChange = changeItem;
uidRecord.curProcStateSeq = TEST_PROC_STATE_SEQ2;
verifyLastProcStateSeqUpdated(uidRecord, -1, TEST_PROC_STATE_SEQ2);
}
@@ -793,7 +793,7 @@ public class ActivityManagerServiceTest {
@Test
public void testEnqueueUidChangeLocked_nullUidRecord() {
// Use "null" uidRecord to make sure there is no crash.
- mAms.mUidObserverController.enqueueUidChangeLocked(null, TEST_UID, UidRecord.CHANGE_ACTIVE);
+ mAms.enqueueUidChangeLocked(null, TEST_UID, UidRecord.CHANGE_ACTIVE);
}
private void verifyLastProcStateSeqUpdated(UidRecord uidRecord, int uid, long curProcstateSeq) {
@@ -802,7 +802,7 @@ public class ActivityManagerServiceTest {
final int changeToDispatch = UID_RECORD_CHANGES[i];
// Reset lastProcStateSeqDispatchToObservers after every test.
uidRecord.lastDispatchedProcStateSeq = 0;
- mAms.mUidObserverController.enqueueUidChangeLocked(uidRecord, uid, changeToDispatch);
+ mAms.enqueueUidChangeLocked(uidRecord, uid, changeToDispatch);
// Verify there is no effect on curProcStateSeq.
assertEquals(curProcstateSeq, uidRecord.curProcStateSeq);
if ((changeToDispatch & UidRecord.CHANGE_GONE) != 0) {
@@ -833,16 +833,16 @@ public class ActivityManagerServiceTest {
// Reset the current state
mHandler.reset();
- uidRecord.pendingChange = null;
- mAms.mUidObserverController.mPendingUidChanges.clear();
+ clearPendingUidChanges();
- mAms.mUidObserverController.enqueueUidChangeLocked(uidRecord, -1, changeToDispatch);
+ mAms.enqueueUidChangeLocked(uidRecord, -1, changeToDispatch);
- // Verify that UidRecord.pendingChange is updated correctly.
- assertNotNull(uidRecord.pendingChange);
- assertEquals(TEST_UID, uidRecord.pendingChange.uid);
- assertEquals(expectedProcState, uidRecord.pendingChange.processState);
- assertEquals(TEST_PROC_STATE_SEQ1, uidRecord.pendingChange.procStateSeq);
+ // Verify that pendingChange is updated correctly.
+ final ChangeRecord pendingChange = getPendingChange(uidRecord.uid);
+ assertNotNull(pendingChange);
+ assertEquals(TEST_UID, pendingChange.uid);
+ assertEquals(expectedProcState, pendingChange.procState);
+ assertEquals(TEST_PROC_STATE_SEQ1, pendingChange.procStateSeq);
// TODO: Verify that DISPATCH_UIDS_CHANGED_UI_MSG is posted to handler.
}
@@ -923,6 +923,29 @@ public class ActivityManagerServiceTest {
mAms.mProcessList.mActiveUids.clear();
}
+ private ChangeRecord getPendingChange(int uid) {
+ final SparseArray<ChangeRecord> pendingChanges =
+ mAms.mUidObserverController.getPendingUidChangesForTest();
+ return pendingChanges.get(uid);
+ }
+
+ private void addPendingUidChange(ChangeRecord record) {
+ mAms.mUidObserverController.getPendingUidChangesForTest().put(record.uid, record);
+ }
+
+ private void addPendingUidChanges(ArrayList<ChangeRecord> changes) {
+ final SparseArray<ChangeRecord> pendingChanges =
+ mAms.mUidObserverController.getPendingUidChangesForTest();
+ for (int i = 0; i < changes.size(); ++i) {
+ final ChangeRecord record = changes.get(i);
+ pendingChanges.put(record.uid, record);
+ }
+ }
+
+ private void clearPendingUidChanges() {
+ mAms.mUidObserverController.getPendingUidChangesForTest().clear();
+ }
+
private static class TestHandler extends Handler {
private static final long WAIT_FOR_MSG_TIMEOUT_MS = 4000; // 4 sec
private static final long WAIT_FOR_MSG_INTERVAL_MS = 400; // 0.4 sec
diff --git a/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java
new file mode 100644
index 000000000000..57c581ea132f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2020 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_CAPABILITY_ALL;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
+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_IMPORTANT_BACKGROUND;
+import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+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 junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.IUidObserver;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.DebugUtils;
+import android.util.Pair;
+import android.util.SparseArray;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.am.UidObserverController.ChangeRecord;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+@SmallTest
+public class UidObserverControllerTest {
+ private static final int TEST_UID1 = 1111;
+ private static final int TEST_UID2 = 2222;
+ private static final int TEST_UID3 = 3333;
+
+ private static final String TEST_PKG1 = "com.example1";
+ private static final String TEST_PKG2 = "com.example2";
+ private static final String TEST_PKG3 = "com.example3";
+
+ private UidObserverController mUidObserverController;
+
+ @Before
+ public void setUp() {
+ mUidObserverController = new UidObserverController(Mockito.mock(Handler.class));
+ }
+
+ @Test
+ public void testEnqueueUidChange() {
+ int change = mUidObserverController.enqueueUidChange(TEST_UID1, UidRecord.CHANGE_ACTIVE,
+ PROCESS_STATE_FOREGROUND_SERVICE, PROCESS_CAPABILITY_ALL, 0, false);
+ assertEquals("expected=ACTIVE,actual=" + changeToStr(change),
+ UidRecord.CHANGE_ACTIVE, change);
+ assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE, PROCESS_STATE_FOREGROUND_SERVICE,
+ PROCESS_CAPABILITY_ALL, 0, false);
+ assertNull(getPendingChange(TEST_UID2));
+
+ change = mUidObserverController.enqueueUidChange(TEST_UID2, UidRecord.CHANGE_CACHED,
+ PROCESS_STATE_CACHED_RECENT, PROCESS_CAPABILITY_NONE, 99, true);
+ assertEquals("expected=ACTIVE,actual=" + changeToStr(change),
+ UidRecord.CHANGE_CACHED, change);
+ assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE, PROCESS_STATE_FOREGROUND_SERVICE,
+ PROCESS_CAPABILITY_ALL, 0, false);
+ assertPendingChange(TEST_UID2, UidRecord.CHANGE_CACHED, PROCESS_STATE_CACHED_RECENT,
+ PROCESS_CAPABILITY_NONE, 99, true);
+
+ change = mUidObserverController.enqueueUidChange(TEST_UID1, UidRecord.CHANGE_UNCACHED,
+ PROCESS_STATE_TOP, PROCESS_CAPABILITY_ALL, 0, false);
+ assertEquals("expected=ACTIVE|UNCACHED,actual=" + changeToStr(change),
+ UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_UNCACHED, change);
+ assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_UNCACHED,
+ PROCESS_STATE_TOP, PROCESS_CAPABILITY_ALL, 0, false);
+ assertPendingChange(TEST_UID2, UidRecord.CHANGE_CACHED, PROCESS_STATE_CACHED_RECENT,
+ PROCESS_CAPABILITY_NONE, 99, true);
+ }
+
+ @Test
+ public void testMergeWithPendingChange() {
+ final SparseArray<Pair<Integer, Integer>> changesToVerify = new SparseArray<>();
+
+ changesToVerify.put(UidRecord.CHANGE_ACTIVE,
+ Pair.create(UidRecord.CHANGE_ACTIVE, UidRecord.CHANGE_IDLE));
+ changesToVerify.put(UidRecord.CHANGE_IDLE,
+ Pair.create(UidRecord.CHANGE_IDLE, UidRecord.CHANGE_ACTIVE));
+ changesToVerify.put(UidRecord.CHANGE_CACHED,
+ Pair.create(UidRecord.CHANGE_CACHED, UidRecord.CHANGE_UNCACHED));
+ changesToVerify.put(UidRecord.CHANGE_UNCACHED,
+ Pair.create(UidRecord.CHANGE_UNCACHED, UidRecord.CHANGE_CACHED));
+ changesToVerify.put(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_UNCACHED,
+ Pair.create(UidRecord.CHANGE_ACTIVE, UidRecord.CHANGE_UNCACHED));
+ changesToVerify.put(UidRecord.CHANGE_IDLE | UidRecord.CHANGE_CACHED,
+ Pair.create(UidRecord.CHANGE_IDLE, UidRecord.CHANGE_CACHED));
+ changesToVerify.put(UidRecord.CHANGE_GONE,
+ Pair.create(UidRecord.CHANGE_GONE, UidRecord.CHANGE_ACTIVE));
+ changesToVerify.put(UidRecord.CHANGE_GONE,
+ Pair.create(UidRecord.CHANGE_GONE, UidRecord.CHANGE_CACHED));
+
+ for (int i = 0; i < changesToVerify.size(); ++i) {
+ final int expectedChange = changesToVerify.keyAt(i);
+ final int currentChange = changesToVerify.valueAt(i).first;
+ final int pendingChange = changesToVerify.valueAt(i).second;
+ assertEquals("current=" + changeToStr(currentChange) + ", pending="
+ + changeToStr(pendingChange) + "exp=" + changeToStr(expectedChange),
+ expectedChange, UidObserverController.mergeWithPendingChange(
+ currentChange, pendingChange));
+ }
+ }
+
+ @Test
+ public void testDispatchUidsChanged() throws RemoteException {
+ addPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_PROCSTATE,
+ PROCESS_STATE_TOP, 0, PROCESS_CAPABILITY_ALL, false);
+
+ final IUidObserver observer1 = Mockito.mock(IUidObserver.Stub.class);
+ registerObserver(observer1,
+ ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_ACTIVE,
+ PROCESS_STATE_IMPORTANT_FOREGROUND, TEST_PKG2, TEST_UID2);
+ final IUidObserver observer2 = Mockito.mock(IUidObserver.Stub.class);
+ registerObserver(observer2, ActivityManager.UID_OBSERVER_PROCSTATE,
+ PROCESS_STATE_SERVICE, TEST_PKG3, TEST_UID3);
+
+ mUidObserverController.dispatchUidsChanged();
+ verify(observer1).onUidStateChanged(TEST_UID1, PROCESS_STATE_TOP,
+ 0, PROCESS_CAPABILITY_ALL);
+ verify(observer1).onUidActive(TEST_UID1);
+ verifyNoMoreInteractions(observer1);
+ verify(observer2).onUidStateChanged(TEST_UID1, PROCESS_STATE_TOP,
+ 0, PROCESS_CAPABILITY_ALL);
+ verifyNoMoreInteractions(observer2);
+
+ addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_IMPORTANT_BACKGROUND,
+ 99, PROCESS_CAPABILITY_FOREGROUND_LOCATION, false);
+ mUidObserverController.dispatchUidsChanged();
+ verify(observer1).onUidStateChanged(TEST_UID1, PROCESS_STATE_IMPORTANT_BACKGROUND,
+ 99, PROCESS_CAPABILITY_FOREGROUND_LOCATION);
+ verifyNoMoreInteractions(observer1);
+ verifyNoMoreInteractions(observer2);
+
+ addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_RECEIVER,
+ 111, PROCESS_CAPABILITY_NONE, false);
+ mUidObserverController.dispatchUidsChanged();
+ verify(observer2).onUidStateChanged(TEST_UID1, PROCESS_STATE_RECEIVER,
+ 111, PROCESS_CAPABILITY_NONE);
+ verifyNoMoreInteractions(observer1);
+ verifyNoMoreInteractions(observer2);
+
+ unregisterObserver(observer1);
+
+ addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_TOP,
+ 112, PROCESS_CAPABILITY_ALL, false);
+ mUidObserverController.dispatchUidsChanged();
+ verify(observer2).onUidStateChanged(TEST_UID1, PROCESS_STATE_TOP,
+ 112, PROCESS_CAPABILITY_ALL);
+ verifyNoMoreInteractions(observer1);
+ verifyNoMoreInteractions(observer2);
+
+ unregisterObserver(observer2);
+
+ addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_CACHED_RECENT,
+ 112, PROCESS_CAPABILITY_NONE, false);
+ mUidObserverController.dispatchUidsChanged();
+ verifyNoMoreInteractions(observer1);
+ verifyNoMoreInteractions(observer2);
+ }
+
+ private void registerObserver(IUidObserver observer, int which, int cutpoint,
+ String callingPackage, int callingUid) {
+ when(observer.asBinder()).thenReturn((IBinder) observer);
+ mUidObserverController.register(observer, which, cutpoint, callingPackage, callingUid);
+ Mockito.reset(observer);
+ }
+
+ private void unregisterObserver(IUidObserver observer) {
+ when(observer.asBinder()).thenReturn((IBinder) observer);
+ mUidObserverController.unregister(observer);
+ Mockito.reset(observer);
+ }
+
+ private void addPendingChange(int uid, int change, int procState, long procStateSeq,
+ int capability, boolean ephemeral) {
+ final ChangeRecord record = new ChangeRecord();
+ record.uid = uid;
+ record.change = change;
+ record.procState = procState;
+ record.procStateSeq = procStateSeq;
+ record.capability = capability;
+ record.ephemeral = ephemeral;
+ mUidObserverController.getPendingUidChangesForTest().put(uid, record);
+ }
+
+ private void assertPendingChange(int uid, int change, int procState, long procStateSeq,
+ int capability, boolean ephemeral) {
+ final ChangeRecord record = getPendingChange(uid);
+ assertNotNull(record);
+ assertEquals(change, record.change);
+ assertEquals(procState, record.procState);
+ assertEquals(procStateSeq, record.procStateSeq);
+ assertEquals(capability, record.capability);
+ assertEquals(ephemeral, record.ephemeral);
+ }
+
+ private ChangeRecord getPendingChange(int uid) {
+ return mUidObserverController.getPendingUidChangesForTest().get(uid);
+ }
+
+ private static String changeToStr(int change) {
+ return DebugUtils.flagsToString(UidRecord.class, "CHANGE_", change);
+ }
+}