diff options
| author | 2020-10-09 07:11:25 +0000 | |
|---|---|---|
| committer | 2020-10-12 23:32:03 +0000 | |
| commit | e83d8766d3d165e8c09f5244e91ce3bb4c06f2be (patch) | |
| tree | 3374691aea57f724c8cafa9a939a8150891b0fad | |
| parent | 7c3ee071f7400d41ad590253b85c64a3a18e06fa (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
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); + } +} |