Move logic for handling uid observers outside AMS.

Bug: 163963556

Test: atest src/android/app/cts/ActivityManagerProcessStateTest.java
Test: atest services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
Test: atest src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
Test: check activity dump contains uid observers info

Change-Id: I1292decd1f24b6cbae094a909662aa795dd054a4
This commit is contained in:
Sudheer Shanka
2020-08-12 11:38:01 +00:00
parent 5fed70ab92
commit 70f4ae340f
5 changed files with 514 additions and 442 deletions

View File

@@ -98,7 +98,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_WHITELISTS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
@@ -146,7 +145,6 @@ import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerProto;
import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -176,7 +174,6 @@ import android.app.PendingIntent;
import android.app.ProcessMemoryState;
import android.app.ProfilerInfo;
import android.app.WaitResult;
import android.app.backup.BackupManager;
import android.app.backup.BackupManager.OperationType;
import android.app.backup.IBackupManager;
import android.app.usage.UsageEvents;
@@ -349,7 +346,6 @@ import com.android.server.SystemServiceManager;
import com.android.server.ThreadPriorityBooster;
import com.android.server.UserspaceRebootLogger;
import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
import com.android.server.appop.AppOpsService;
import com.android.server.compat.PlatformCompat;
import com.android.server.contentcapture.ContentCaptureManagerInternal;
@@ -490,9 +486,6 @@ public class ActivityManagerService extends IActivityManager.Stub
// as one line, but close enough for now.
static final int RESERVED_BYTES_PER_LOGCAT_LINE = 100;
/** If a UID observer takes more than this long, send a WTF. */
private static final int SLOW_UID_OBSERVER_THRESHOLD_MS = 20;
// Necessary ApplicationInfo flags to mark an app as persistent
static final int PERSISTENT_MASK =
ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT;
@@ -644,9 +637,6 @@ public class ActivityManagerService extends IActivityManager.Stub
@VisibleForTesting
long mWaitForNetworkTimeoutMs;
/** Total # of UID change events dispatched, shown in dumpsys. */
int mUidChangeDispatchCount;
/**
* Uids of apps with current active camera sessions. Access synchronized on
* the IntArray instance itself, and no other locks must be acquired while that
@@ -1013,12 +1003,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
};
/**
* This is for verifying the UID report flow.
*/
static final boolean VALIDATE_UID_STATES = true;
final ActiveUids mValidateUids = new ActiveUids(this, false /* postChangesToAtm */);
/**
* Fingerprints (hashCode()) of stack traces that we've
* already logged DropBox entries for. Guarded by itself. If
@@ -1399,69 +1383,6 @@ public class ActivityManagerService extends IActivityManager.Stub
int foregroundServiceTypes;
}
static final class UidObserverRegistration {
final int uid;
final String pkg;
final int which;
final int cutpoint;
/**
* Total # of callback calls that took more than {@link #SLOW_UID_OBSERVER_THRESHOLD_MS}.
* We show it in dumpsys.
*/
int mSlowDispatchCount;
/** Max time it took for each dispatch. */
int mMaxDispatchTime;
final SparseIntArray lastProcStates;
// Please keep the enum lists in sync
private static int[] ORIG_ENUMS = new int[]{
ActivityManager.UID_OBSERVER_IDLE,
ActivityManager.UID_OBSERVER_ACTIVE,
ActivityManager.UID_OBSERVER_GONE,
ActivityManager.UID_OBSERVER_PROCSTATE,
};
private static int[] PROTO_ENUMS = new int[]{
ActivityManagerProto.UID_OBSERVER_FLAG_IDLE,
ActivityManagerProto.UID_OBSERVER_FLAG_ACTIVE,
ActivityManagerProto.UID_OBSERVER_FLAG_GONE,
ActivityManagerProto.UID_OBSERVER_FLAG_PROCSTATE,
};
UidObserverRegistration(int _uid, String _pkg, int _which, int _cutpoint) {
uid = _uid;
pkg = _pkg;
which = _which;
cutpoint = _cutpoint;
if (cutpoint >= ActivityManager.MIN_PROCESS_STATE) {
lastProcStates = new SparseIntArray();
} else {
lastProcStates = null;
}
}
void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(UidObserverRegistrationProto.UID, uid);
proto.write(UidObserverRegistrationProto.PACKAGE, pkg);
ProtoUtils.writeBitWiseFlagsToProtoEnum(proto, UidObserverRegistrationProto.FLAGS,
which, ORIG_ENUMS, PROTO_ENUMS);
proto.write(UidObserverRegistrationProto.CUT_POINT, cutpoint);
if (lastProcStates != null) {
final int NI = lastProcStates.size();
for (int i=0; i<NI; i++) {
final long pToken = proto.start(UidObserverRegistrationProto.LAST_PROC_STATES);
proto.write(UidObserverRegistrationProto.ProcState.UID, lastProcStates.keyAt(i));
proto.write(UidObserverRegistrationProto.ProcState.STATE, lastProcStates.valueAt(i));
proto.end(pToken);
}
}
proto.end(token);
}
}
// TODO: Move below 4 members and code to ProcessList
final RemoteCallbackList<IProcessObserver> mProcessObservers = new RemoteCallbackList<>();
ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5];
@@ -1469,12 +1390,6 @@ public class ActivityManagerService extends IActivityManager.Stub
final ArrayList<ProcessChangeItem> mPendingProcessChanges = new ArrayList<>();
final ArrayList<ProcessChangeItem> mAvailProcessChanges = new ArrayList<>();
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<>();
OomAdjObserver mCurOomAdjObserver;
int mCurOomAdjUid;
@@ -1525,6 +1440,8 @@ public class ActivityManagerService extends IActivityManager.Stub
public final ActivityManagerInternal mInternal;
final ActivityThread mSystemThread;
final UidObserverController mUidObserverController;
private final class AppDeathRecipient implements IBinder.DeathRecipient {
final ProcessRecord mApp;
final int mPid;
@@ -1570,7 +1487,6 @@ public class ActivityManagerService extends IActivityManager.Stub
static final int NOTIFY_CLEARTEXT_NETWORK_MSG = 49;
static final int POST_DUMP_HEAP_NOTIFICATION_MSG = 50;
static final int ABORT_DUMPHEAP_MSG = 51;
static final int DISPATCH_UIDS_CHANGED_UI_MSG = 53;
static final int SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG = 56;
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG = 57;
static final int IDLE_UIDS_MSG = 58;
@@ -1698,17 +1614,14 @@ public class ActivityManagerService extends IActivityManager.Stub
break;
}
case DISPATCH_PROCESS_DIED_UI_MSG: {
if (false) { // DO NOT SUBMIT WITH TRUE
maybeTriggerWatchdog();
}
final int pid = msg.arg1;
final int uid = msg.arg2;
dispatchProcessDied(pid, uid);
break;
}
case DISPATCH_UIDS_CHANGED_UI_MSG: {
if (false) { // DO NOT SUBMIT WITH TRUE
maybeTriggerWatchdog();
}
dispatchUidsChanged();
} break;
case DISPATCH_OOM_ADJ_OBSERVER_MSG: {
dispatchOomAdjObserver((String) msg.obj);
} break;
@@ -2505,6 +2418,7 @@ 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);
@@ -2600,6 +2514,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);
final File systemDir = SystemServiceManager.ensureSystemDir();
@@ -3416,153 +3331,6 @@ public class ActivityManagerService extends IActivityManager.Stub
mProcessObservers.finishBroadcast();
}
@VisibleForTesting
void dispatchUidsChanged() {
int N;
synchronized (this) {
N = mPendingUidChanges.size();
if (mActiveUidChanges.length < N) {
mActiveUidChanges = new UidRecord.ChangeItem[N];
}
for (int i=0; i<N; i++) {
final UidRecord.ChangeItem change = mPendingUidChanges.get(i);
mActiveUidChanges[i] = change;
if (change.uidRecord != null) {
change.uidRecord.pendingChange = null;
change.uidRecord = null;
}
}
mPendingUidChanges.clear();
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"*** Delivering " + N + " uid changes");
}
mUidChangeDispatchCount += N;
int i = mUidObservers.beginBroadcast();
while (i > 0) {
i--;
dispatchUidsChangedForObserver(mUidObservers.getBroadcastItem(i),
(UidObserverRegistration) mUidObservers.getBroadcastCookie(i), N);
}
mUidObservers.finishBroadcast();
if (VALIDATE_UID_STATES && mUidObservers.getRegisteredCallbackCount() > 0) {
for (int j = 0; j < N; ++j) {
final UidRecord.ChangeItem item = mActiveUidChanges[j];
if ((item.change & UidRecord.CHANGE_GONE) != 0) {
mValidateUids.remove(item.uid);
} else {
UidRecord validateUid = mValidateUids.get(item.uid);
if (validateUid == null) {
validateUid = new UidRecord(item.uid);
mValidateUids.put(item.uid, validateUid);
}
if ((item.change & UidRecord.CHANGE_IDLE) != 0) {
validateUid.idle = true;
} else if ((item.change & UidRecord.CHANGE_ACTIVE) != 0) {
validateUid.idle = false;
}
validateUid.setCurProcState(validateUid.setProcState = item.processState);
validateUid.curCapability = validateUid.setCapability = item.capability;
validateUid.lastDispatchedProcStateSeq = item.procStateSeq;
}
}
}
synchronized (this) {
for (int j = 0; j < N; j++) {
mAvailUidChanges.add(mActiveUidChanges[j]);
}
}
}
private void dispatchUidsChangedForObserver(IUidObserver observer,
UidObserverRegistration reg, int changesSize) {
if (observer == null) {
return;
}
try {
for (int j = 0; j < changesSize; j++) {
UidRecord.ChangeItem item = mActiveUidChanges[j];
final int change = item.change;
if (change == UidRecord.CHANGE_PROCSTATE &&
(reg.which & ActivityManager.UID_OBSERVER_PROCSTATE) == 0) {
// No-op common case: no significant change, the observer is not
// interested in all proc state changes.
continue;
}
final long start = SystemClock.uptimeMillis();
if ((change & UidRecord.CHANGE_IDLE) != 0) {
if ((reg.which & ActivityManager.UID_OBSERVER_IDLE) != 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"UID idle uid=" + item.uid);
observer.onUidIdle(item.uid, item.ephemeral);
}
} else if ((change & UidRecord.CHANGE_ACTIVE) != 0) {
if ((reg.which & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"UID active uid=" + item.uid);
observer.onUidActive(item.uid);
}
}
if ((reg.which & ActivityManager.UID_OBSERVER_CACHED) != 0) {
if ((change & UidRecord.CHANGE_CACHED) != 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"UID cached uid=" + item.uid);
observer.onUidCachedChanged(item.uid, true);
} else if ((change & UidRecord.CHANGE_UNCACHED) != 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"UID active uid=" + item.uid);
observer.onUidCachedChanged(item.uid, false);
}
}
if ((change & UidRecord.CHANGE_GONE) != 0) {
if ((reg.which & ActivityManager.UID_OBSERVER_GONE) != 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"UID gone uid=" + item.uid);
observer.onUidGone(item.uid, item.ephemeral);
}
if (reg.lastProcStates != null) {
reg.lastProcStates.delete(item.uid);
}
} else {
if ((reg.which & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"UID CHANGED uid=" + item.uid
+ ": " + item.processState + ": " + item.capability);
boolean doReport = true;
if (reg.cutpoint >= ActivityManager.MIN_PROCESS_STATE) {
final int lastState = reg.lastProcStates.get(item.uid,
ActivityManager.PROCESS_STATE_UNKNOWN);
if (lastState != ActivityManager.PROCESS_STATE_UNKNOWN) {
final boolean lastAboveCut = lastState <= reg.cutpoint;
final boolean newAboveCut = item.processState <= reg.cutpoint;
doReport = lastAboveCut != newAboveCut;
} else {
doReport = item.processState != PROCESS_STATE_NONEXISTENT;
}
}
if (doReport) {
if (reg.lastProcStates != null) {
reg.lastProcStates.put(item.uid, item.processState);
}
observer.onUidStateChanged(item.uid, item.processState,
item.procStateSeq, item.capability);
}
}
}
final int duration = (int) (SystemClock.uptimeMillis() - start);
if (reg.mMaxDispatchTime < duration) {
reg.mMaxDispatchTime = duration;
}
if (duration >= SLOW_UID_OBSERVER_THRESHOLD_MS) {
reg.mSlowDispatchCount++;
}
}
} catch (RemoteException e) {
}
}
void dispatchOomAdjObserver(String msg) {
OomAdjObserver observer;
synchronized (this) {
@@ -7494,15 +7262,15 @@ public class ActivityManagerService extends IActivityManager.Stub
"registerUidObserver");
}
synchronized (this) {
mUidObservers.register(observer, new UidObserverRegistration(Binder.getCallingUid(),
callingPackage, which, cutpoint));
mUidObserverController.register(observer, which, cutpoint, callingPackage,
Binder.getCallingUid());
}
}
@Override
public void unregisterUidObserver(IUidObserver observer) {
synchronized (this) {
mUidObservers.unregister(observer);
mUidObserverController.unregister(observer);
}
}
@@ -10121,9 +9889,9 @@ public class ActivityManagerService extends IActivityManager.Stub
}
if (dumpAll) {
if (mValidateUids.size() > 0) {
if (dumpUids(pw, dumpPackage, dumpAppId, mValidateUids, "UID validation:",
needSep)) {
if (mUidObserverController.mValidateUids.size() > 0) {
if (dumpUids(pw, dumpPackage, dumpAppId, mUidObserverController.mValidateUids,
"UID validation:", needSep)) {
needSep = true;
}
}
@@ -10255,45 +10023,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
if (dumpAll) {
final int NI = mUidObservers.getRegisteredCallbackCount();
boolean printed = false;
for (int i=0; i<NI; i++) {
final UidObserverRegistration reg = (UidObserverRegistration)
mUidObservers.getRegisteredCallbackCookie(i);
if (dumpPackage == null || dumpPackage.equals(reg.pkg)) {
if (!printed) {
pw.println(" mUidObservers:");
printed = true;
}
pw.print(" "); UserHandle.formatUid(pw, reg.uid);
pw.print(" "); pw.print(reg.pkg);
final IUidObserver observer = mUidObservers.getRegisteredCallbackItem(i);
pw.print(" "); pw.print(observer.getClass().getTypeName()); pw.print(":");
if ((reg.which&ActivityManager.UID_OBSERVER_IDLE) != 0) {
pw.print(" IDLE");
}
if ((reg.which&ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
pw.print(" ACT" );
}
if ((reg.which&ActivityManager.UID_OBSERVER_GONE) != 0) {
pw.print(" GONE");
}
if ((reg.which&ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
pw.print(" STATE");
pw.print(" (cut="); pw.print(reg.cutpoint);
pw.print(")");
}
pw.println();
if (reg.lastProcStates != null) {
final int NJ = reg.lastProcStates.size();
for (int j=0; j<NJ; j++) {
pw.print(" Last ");
UserHandle.formatUid(pw, reg.lastProcStates.keyAt(j));
pw.print(": "); pw.println(reg.lastProcStates.valueAt(j));
}
}
}
}
mUidObserverController.dump(pw, dumpPackage);
pw.println(" mDeviceIdleWhitelist=" + Arrays.toString(mDeviceIdleWhitelist));
pw.println(" mDeviceIdleExceptIdleWhitelist="
+ Arrays.toString(mDeviceIdleExceptIdleWhitelist));
@@ -10421,25 +10152,6 @@ public class ActivityManagerService extends IActivityManager.Stub
pw.print(" mLowRamSinceLastIdle=");
TimeUtils.formatDuration(getLowRamTimeSinceIdle(now), pw);
pw.println();
pw.println();
pw.print(" mUidChangeDispatchCount=");
pw.print(mUidChangeDispatchCount);
pw.println();
pw.println(" Slow UID dispatches:");
final int N = mUidObservers.beginBroadcast();
for (int i = 0; i < N; 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");
}
mUidObservers.finishBroadcast();
pw.println();
pw.println(" ServiceManager statistics:");
@@ -10507,8 +10219,8 @@ public class ActivityManagerService extends IActivityManager.Stub
uidRec.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.ACTIVE_UIDS);
}
for (int i = 0; i < mValidateUids.size(); i++) {
UidRecord uidRec = mValidateUids.valueAt(i);
for (int i = 0; i < mUidObserverController.mValidateUids.size(); i++) {
UidRecord uidRec = mUidObserverController.mValidateUids.valueAt(i);
if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
continue;
}
@@ -10593,14 +10305,7 @@ public class ActivityManagerService extends IActivityManager.Stub
ActivityManagerServiceDumpProcessesProto.USER_CONTROLLER);
}
final int NI = mUidObservers.getRegisteredCallbackCount();
for (int i=0; i<NI; i++) {
final UidObserverRegistration reg = (UidObserverRegistration)
mUidObservers.getRegisteredCallbackCookie(i);
if (dumpPackage == null || dumpPackage.equals(reg.pkg)) {
reg.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.UID_OBSERVERS);
}
}
mUidObserverController.dumpDebug(proto, dumpPackage);
for (int v : mDeviceIdleWhitelist) {
proto.write(ActivityManagerServiceDumpProcessesProto.DEVICE_IDLE_WHITELIST, v);
@@ -16299,101 +16004,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
private boolean isEphemeralLocked(int uid) {
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]);
}
@VisibleForTesting
final void enqueueUidChangeLocked(UidRecord uidRec, int uid, int change) {
final UidRecord.ChangeItem pendingChange;
if (uidRec == null || uidRec.pendingChange == null) {
if (mPendingUidChanges.size() == 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"*** Enqueueing dispatch uid changed!");
mUiHandler.obtainMessage(DISPATCH_UIDS_CHANGED_UI_MSG).sendToTarget();
}
final int NA = mAvailUidChanges.size();
if (NA > 0) {
pendingChange = mAvailUidChanges.remove(NA-1);
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"Retrieving available item: " + pendingChange);
} 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;
}
}
}
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);
}
// 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) {
// TO DO: dispatch cached/uncached changes here, so we don't need to report
// all proc state changes.
if ((change & UidRecord.CHANGE_ACTIVE) != 0) {
mLocalPowerManager.uidActive(pendingChange.uid);
}
if ((change & UidRecord.CHANGE_IDLE) != 0) {
mLocalPowerManager.uidIdle(pendingChange.uid);
}
if ((change & UidRecord.CHANGE_GONE) != 0) {
mLocalPowerManager.uidGone(pendingChange.uid);
} else {
mLocalPowerManager.updateUidProcState(pendingChange.uid,
pendingChange.processState);
}
}
}
final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
if (proc.thread != null && proc.baseProcessTracker != null) {
final int procState = proc.getReportedProcState();
@@ -16812,7 +16422,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@GuardedBy("this")
final void doStopUidLocked(int uid, final UidRecord uidRec) {
mServices.stopInBackgroundLocked(uid);
enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE);
mUidObserverController.enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE);
}
/**
@@ -18473,7 +18083,8 @@ public class ActivityManagerService extends IActivityManager.Stub
Slog.w(TAG_NETWORK, "Total time waited for network rules to get updated: "
+ totalTime + ". Uid: " + callingUid + " procStateSeq: "
+ procStateSeq + " UidRec: " + record
+ " validateUidRec: " + mValidateUids.get(callingUid));
+ " validateUidRec: "
+ mUidObserverController.mValidateUids.get(callingUid));
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();

View File

@@ -987,7 +987,7 @@ public final class OomAdjuster {
uidRec.setWhitelist = uidRec.curWhitelist;
uidRec.setIdle = uidRec.idle;
mService.mAtmInternal.onUidProcStateChanged(uidRec.uid, uidRec.setProcState);
mService.enqueueUidChangeLocked(uidRec, -1, uidChange);
mService.mUidObserverController.enqueueUidChangeLocked(uidRec, -1, uidChange);
mService.noteUidProcessState(uidRec.uid, uidRec.getCurProcState(),
uidRec.curCapability);
if (uidRec.foregroundServices) {

View File

@@ -2960,7 +2960,8 @@ 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.enqueueUidChangeLocked(uidRecord, -1, UidRecord.CHANGE_GONE);
mService.mUidObserverController.enqueueUidChangeLocked(uidRecord, -1,
UidRecord.CHANGE_GONE);
EventLogTags.writeAmUidStopped(uid);
mActiveUids.remove(uid);
mService.noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT,

View File

@@ -0,0 +1,460 @@
/*
* 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_STATE_NONEXISTENT;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
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.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Slog;
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
import android.util.proto.ProtoUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
import java.io.PrintWriter;
import java.util.ArrayList;
public class UidObserverController {
private final ActivityManagerService mService;
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<>();
/** Total # of UID change events dispatched, shown in dumpsys. */
int mUidChangeDispatchCount;
/** If a UID observer takes more than this long, send a WTF. */
private static final int SLOW_UID_OBSERVER_THRESHOLD_MS = 20;
/**
* This is for verifying the UID report flow.
*/
static final boolean VALIDATE_UID_STATES = true;
final ActiveUids mValidateUids;
UidObserverController(ActivityManagerService service) {
mService = service;
mValidateUids = new ActiveUids(mService, 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));
}
@GuardedBy("mService")
void unregister(IUidObserver observer) {
mUidObservers.unregister(observer);
}
@GuardedBy("mService")
final void enqueueUidChangeLocked(UidRecord uidRec, int uid, int change) {
final UidRecord.ChangeItem pendingChange;
if (uidRec == null || uidRec.pendingChange == null) {
if (mPendingUidChanges.size() == 0) {
if (DEBUG_UID_OBSERVERS) {
Slog.i(TAG_UID_OBSERVERS, "*** Enqueueing dispatch uid changed!");
}
mService.mUiHandler.post(this::dispatchUidsChanged);
}
final int NA = mAvailUidChanges.size();
if (NA > 0) {
pendingChange = mAvailUidChanges.remove(NA-1);
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"Retrieving available item: " + pendingChange);
} 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;
}
}
}
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);
}
// 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);
}
if ((change & UidRecord.CHANGE_GONE) != 0) {
mService.mLocalPowerManager.uidGone(pendingChange.uid);
} else {
mService.mLocalPowerManager.updateUidProcState(pendingChange.uid,
pendingChange.processState);
}
}
}
@VisibleForTesting
void dispatchUidsChanged() {
int N;
synchronized (mService) {
N = mPendingUidChanges.size();
if (mActiveUidChanges.length < N) {
mActiveUidChanges = new UidRecord.ChangeItem[N];
}
for (int i=0; i<N; i++) {
final UidRecord.ChangeItem change = mPendingUidChanges.get(i);
mActiveUidChanges[i] = change;
if (change.uidRecord != null) {
change.uidRecord.pendingChange = null;
change.uidRecord = null;
}
}
mPendingUidChanges.clear();
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"*** Delivering " + N + " uid changes");
}
mUidChangeDispatchCount += N;
int i = mUidObservers.beginBroadcast();
while (i > 0) {
i--;
dispatchUidsChangedForObserver(mUidObservers.getBroadcastItem(i),
(UidObserverRegistration) mUidObservers.getBroadcastCookie(i), N);
}
mUidObservers.finishBroadcast();
if (VALIDATE_UID_STATES && mUidObservers.getRegisteredCallbackCount() > 0) {
for (int j = 0; j < N; ++j) {
final UidRecord.ChangeItem item = mActiveUidChanges[j];
if ((item.change & UidRecord.CHANGE_GONE) != 0) {
mValidateUids.remove(item.uid);
} else {
UidRecord validateUid = mValidateUids.get(item.uid);
if (validateUid == null) {
validateUid = new UidRecord(item.uid);
mValidateUids.put(item.uid, validateUid);
}
if ((item.change & UidRecord.CHANGE_IDLE) != 0) {
validateUid.idle = true;
} else if ((item.change & UidRecord.CHANGE_ACTIVE) != 0) {
validateUid.idle = false;
}
validateUid.setCurProcState(validateUid.setProcState = item.processState);
validateUid.curCapability = validateUid.setCapability = item.capability;
validateUid.lastDispatchedProcStateSeq = item.procStateSeq;
}
}
}
synchronized (mService) {
for (int j = 0; j < N; j++) {
mAvailUidChanges.add(mActiveUidChanges[j]);
}
}
}
private void dispatchUidsChangedForObserver(IUidObserver observer,
UidObserverRegistration reg, int changesSize) {
if (observer == null) {
return;
}
try {
for (int j = 0; j < changesSize; j++) {
UidRecord.ChangeItem item = mActiveUidChanges[j];
final int change = item.change;
if (change == UidRecord.CHANGE_PROCSTATE &&
(reg.which & ActivityManager.UID_OBSERVER_PROCSTATE) == 0) {
// No-op common case: no significant change, the observer is not
// interested in all proc state changes.
continue;
}
final long start = SystemClock.uptimeMillis();
if ((change & UidRecord.CHANGE_IDLE) != 0) {
if ((reg.which & ActivityManager.UID_OBSERVER_IDLE) != 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"UID idle uid=" + item.uid);
observer.onUidIdle(item.uid, item.ephemeral);
}
} else if ((change & UidRecord.CHANGE_ACTIVE) != 0) {
if ((reg.which & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"UID active uid=" + item.uid);
observer.onUidActive(item.uid);
}
}
if ((reg.which & ActivityManager.UID_OBSERVER_CACHED) != 0) {
if ((change & UidRecord.CHANGE_CACHED) != 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"UID cached uid=" + item.uid);
observer.onUidCachedChanged(item.uid, true);
} else if ((change & UidRecord.CHANGE_UNCACHED) != 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"UID active uid=" + item.uid);
observer.onUidCachedChanged(item.uid, false);
}
}
if ((change & UidRecord.CHANGE_GONE) != 0) {
if ((reg.which & ActivityManager.UID_OBSERVER_GONE) != 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"UID gone uid=" + item.uid);
observer.onUidGone(item.uid, item.ephemeral);
}
if (reg.lastProcStates != null) {
reg.lastProcStates.delete(item.uid);
}
} else {
if ((reg.which & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"UID CHANGED uid=" + item.uid
+ ": " + item.processState + ": " + item.capability);
boolean doReport = true;
if (reg.cutpoint >= ActivityManager.MIN_PROCESS_STATE) {
final int lastState = reg.lastProcStates.get(item.uid,
ActivityManager.PROCESS_STATE_UNKNOWN);
if (lastState != ActivityManager.PROCESS_STATE_UNKNOWN) {
final boolean lastAboveCut = lastState <= reg.cutpoint;
final boolean newAboveCut = item.processState <= reg.cutpoint;
doReport = lastAboveCut != newAboveCut;
} else {
doReport = item.processState != PROCESS_STATE_NONEXISTENT;
}
}
if (doReport) {
if (reg.lastProcStates != null) {
reg.lastProcStates.put(item.uid, item.processState);
}
observer.onUidStateChanged(item.uid, item.processState,
item.procStateSeq, item.capability);
}
}
}
final int duration = (int) (SystemClock.uptimeMillis() - start);
if (reg.mMaxDispatchTime < duration) {
reg.mMaxDispatchTime = duration;
}
if (duration >= SLOW_UID_OBSERVER_THRESHOLD_MS) {
reg.mSlowDispatchCount++;
}
}
} catch (RemoteException e) {
}
}
private boolean isEphemeralLocked(int uid) {
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]);
}
@GuardedBy("mService")
void dump(PrintWriter pw, String dumpPackage) {
final int NI = mUidObservers.getRegisteredCallbackCount();
boolean printed = false;
for (int i=0; i<NI; i++) {
final UidObserverRegistration reg = (UidObserverRegistration)
mUidObservers.getRegisteredCallbackCookie(i);
if (dumpPackage == null || dumpPackage.equals(reg.pkg)) {
if (!printed) {
pw.println(" mUidObservers:");
printed = true;
}
pw.print(" "); UserHandle.formatUid(pw, reg.uid);
pw.print(" "); pw.print(reg.pkg);
final IUidObserver observer = mUidObservers.getRegisteredCallbackItem(i);
pw.print(" "); pw.print(observer.getClass().getTypeName()); pw.print(":");
if ((reg.which&ActivityManager.UID_OBSERVER_IDLE) != 0) {
pw.print(" IDLE");
}
if ((reg.which&ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
pw.print(" ACT" );
}
if ((reg.which&ActivityManager.UID_OBSERVER_GONE) != 0) {
pw.print(" GONE");
}
if ((reg.which&ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
pw.print(" STATE");
pw.print(" (cut="); pw.print(reg.cutpoint);
pw.print(")");
}
pw.println();
if (reg.lastProcStates != null) {
final int NJ = reg.lastProcStates.size();
for (int j=0; j<NJ; j++) {
pw.print(" Last ");
UserHandle.formatUid(pw, reg.lastProcStates.keyAt(j));
pw.print(": "); pw.println(reg.lastProcStates.valueAt(j));
}
}
}
}
pw.println();
pw.print(" mUidChangeDispatchCount=");
pw.print(mUidChangeDispatchCount);
pw.println();
pw.println(" Slow UID dispatches:");
final int N = mUidObservers.beginBroadcast();
for (int i = 0; i < N; 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");
}
mUidObservers.finishBroadcast();
}
@GuardedBy("mService")
void dumpDebug(ProtoOutputStream proto, String dumpPackage) {
final int NI = mUidObservers.getRegisteredCallbackCount();
for (int i=0; i<NI; i++) {
final UidObserverRegistration reg = (UidObserverRegistration)
mUidObservers.getRegisteredCallbackCookie(i);
if (dumpPackage == null || dumpPackage.equals(reg.pkg)) {
reg.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.UID_OBSERVERS);
}
}
}
static final class UidObserverRegistration {
final int uid;
final String pkg;
final int which;
final int cutpoint;
/**
* Total # of callback calls that took more than {@link #SLOW_UID_OBSERVER_THRESHOLD_MS}.
* We show it in dumpsys.
*/
int mSlowDispatchCount;
/** Max time it took for each dispatch. */
int mMaxDispatchTime;
final SparseIntArray lastProcStates;
// Please keep the enum lists in sync
private static int[] ORIG_ENUMS = new int[]{
ActivityManager.UID_OBSERVER_IDLE,
ActivityManager.UID_OBSERVER_ACTIVE,
ActivityManager.UID_OBSERVER_GONE,
ActivityManager.UID_OBSERVER_PROCSTATE,
};
private static int[] PROTO_ENUMS = new int[]{
ActivityManagerProto.UID_OBSERVER_FLAG_IDLE,
ActivityManagerProto.UID_OBSERVER_FLAG_ACTIVE,
ActivityManagerProto.UID_OBSERVER_FLAG_GONE,
ActivityManagerProto.UID_OBSERVER_FLAG_PROCSTATE,
};
UidObserverRegistration(int _uid, String _pkg, int _which, int _cutpoint) {
uid = _uid;
pkg = _pkg;
which = _which;
cutpoint = _cutpoint;
if (cutpoint >= ActivityManager.MIN_PROCESS_STATE) {
lastProcStates = new SparseIntArray();
} else {
lastProcStates = null;
}
}
void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(UidObserverRegistrationProto.UID, uid);
proto.write(UidObserverRegistrationProto.PACKAGE, pkg);
ProtoUtils.writeBitWiseFlagsToProtoEnum(proto, UidObserverRegistrationProto.FLAGS,
which, ORIG_ENUMS, PROTO_ENUMS);
proto.write(UidObserverRegistrationProto.CUT_POINT, cutpoint);
if (lastProcStates != null) {
final int NI = lastProcStates.size();
for (int i=0; i<NI; i++) {
final long pToken = proto.start(UidObserverRegistrationProto.LAST_PROC_STATES);
proto.write(UidObserverRegistrationProto.ProcState.UID,
lastProcStates.keyAt(i));
proto.write(UidObserverRegistrationProto.ProcState.STATE,
lastProcStates.valueAt(i));
proto.end(pToken);
}
}
proto.end(token);
}
}
}

View File

@@ -32,7 +32,6 @@ import static android.util.DebugUtils.valueToString;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.android.server.am.ActivityManagerInternalTest.CustomThread;
import static com.android.server.am.ActivityManagerService.DISPATCH_UIDS_CHANGED_UI_MSG;
import static com.android.server.am.ActivityManagerService.Injector;
import static com.android.server.am.ProcessList.NETWORK_STATE_BLOCK;
import static com.android.server.am.ProcessList.NETWORK_STATE_NO_CHANGE;
@@ -548,10 +547,10 @@ public class ActivityManagerServiceTest {
pendingChange.processState = procStatesForPendingUidRecords[i];
pendingChange.procStateSeq = i;
changeItems.put(changesForPendingUidRecords[i], pendingChange);
mAms.mPendingUidChanges.add(pendingChange);
mAms.mUidObserverController.mPendingUidChanges.add(pendingChange);
}
mAms.dispatchUidsChanged();
mAms.mUidObserverController.dispatchUidsChanged();
// Verify the required changes have been dispatched to observers.
for (int i = 0; i < observers.length; ++i) {
final int changeToObserve = changesToObserve[i];
@@ -647,8 +646,8 @@ public class ActivityManagerServiceTest {
changeItem.change = UidRecord.CHANGE_PROCSTATE;
changeItem.processState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
changeItem.procStateSeq = 111;
mAms.mPendingUidChanges.add(changeItem);
mAms.dispatchUidsChanged();
mAms.mUidObserverController.mPendingUidChanges.add(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,
@@ -657,15 +656,15 @@ public class ActivityManagerServiceTest {
verifyNoMoreInteractions(observer);
changeItem.processState = ActivityManager.PROCESS_STATE_RECEIVER;
mAms.mPendingUidChanges.add(changeItem);
mAms.dispatchUidsChanged();
mAms.mUidObserverController.mPendingUidChanges.add(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.mPendingUidChanges.add(changeItem);
mAms.dispatchUidsChanged();
mAms.mUidObserverController.mPendingUidChanges.add(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.
@@ -675,15 +674,15 @@ public class ActivityManagerServiceTest {
verifyNoMoreInteractions(observer);
changeItem.processState = ActivityManager.PROCESS_STATE_TOP;
mAms.mPendingUidChanges.add(changeItem);
mAms.dispatchUidsChanged();
mAms.mUidObserverController.mPendingUidChanges.add(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.mPendingUidChanges.add(changeItem);
mAms.dispatchUidsChanged();
mAms.mUidObserverController.mPendingUidChanges.add(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.
@@ -720,20 +719,21 @@ public class ActivityManagerServiceTest {
// Verify that when there no observers listening to uid state changes, then there will
// be no changes to validateUids.
mAms.mPendingUidChanges.addAll(pendingItemsForUids);
mAms.dispatchUidsChanged();
mAms.mUidObserverController.mPendingUidChanges.addAll(pendingItemsForUids);
mAms.mUidObserverController.dispatchUidsChanged();
assertEquals("No observers registered, so validateUids should be empty",
0, mAms.mValidateUids.size());
0, mAms.mUidObserverController.mValidateUids.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.mPendingUidChanges.addAll(pendingItemsForUids);
mAms.dispatchUidsChanged();
mAms.mUidObserverController.mPendingUidChanges.addAll(pendingItemsForUids);
mAms.mUidObserverController.dispatchUidsChanged();
for (int i = 0; i < pendingItemsForUids.size(); ++i) {
final UidRecord.ChangeItem item = pendingItemsForUids.get(i);
final UidRecord validateUidRecord = mAms.mValidateUids.get(item.uid);
final UidRecord validateUidRecord =
mAms.mUidObserverController.mValidateUids.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);
@@ -759,7 +759,7 @@ 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.mValidateUids.size());
mAms.mUidObserverController.mValidateUids.size());
for (int i = 0; i < pendingItemsForUids.size(); ++i) {
final UidRecord.ChangeItem item = pendingItemsForUids.get(i);
// Assign CHANGE_GONE_IDLE to some items and CHANGE_GONE to the others, using even/odd
@@ -767,10 +767,11 @@ public class ActivityManagerServiceTest {
item.change = (i % 2) == 0 ? (UidRecord.CHANGE_GONE | UidRecord.CHANGE_IDLE)
: UidRecord.CHANGE_GONE;
}
mAms.mPendingUidChanges.addAll(pendingItemsForUids);
mAms.dispatchUidsChanged();
assertEquals("validateUids should be empty, size=" + mAms.mValidateUids.size(),
0, mAms.mValidateUids.size());
mAms.mUidObserverController.mPendingUidChanges.addAll(pendingItemsForUids);
mAms.mUidObserverController.dispatchUidsChanged();
assertEquals("validateUids should be empty, size="
+ mAms.mUidObserverController.mValidateUids.size(),
0, mAms.mUidObserverController.mValidateUids.size());
}
@Test
@@ -792,7 +793,7 @@ public class ActivityManagerServiceTest {
@Test
public void testEnqueueUidChangeLocked_nullUidRecord() {
// Use "null" uidRecord to make sure there is no crash.
mAms.enqueueUidChangeLocked(null, TEST_UID, UidRecord.CHANGE_ACTIVE);
mAms.mUidObserverController.enqueueUidChangeLocked(null, TEST_UID, UidRecord.CHANGE_ACTIVE);
}
private void verifyLastProcStateSeqUpdated(UidRecord uidRecord, int uid, long curProcstateSeq) {
@@ -801,7 +802,7 @@ public class ActivityManagerServiceTest {
final int changeToDispatch = UID_RECORD_CHANGES[i];
// Reset lastProcStateSeqDispatchToObservers after every test.
uidRecord.lastDispatchedProcStateSeq = 0;
mAms.enqueueUidChangeLocked(uidRecord, uid, changeToDispatch);
mAms.mUidObserverController.enqueueUidChangeLocked(uidRecord, uid, changeToDispatch);
// Verify there is no effect on curProcStateSeq.
assertEquals(curProcstateSeq, uidRecord.curProcStateSeq);
if ((changeToDispatch & UidRecord.CHANGE_GONE) != 0) {
@@ -833,9 +834,9 @@ public class ActivityManagerServiceTest {
// Reset the current state
mHandler.reset();
uidRecord.pendingChange = null;
mAms.mPendingUidChanges.clear();
mAms.mUidObserverController.mPendingUidChanges.clear();
mAms.enqueueUidChangeLocked(uidRecord, -1, changeToDispatch);
mAms.mUidObserverController.enqueueUidChangeLocked(uidRecord, -1, changeToDispatch);
// Verify that UidRecord.pendingChange is updated correctly.
assertNotNull(uidRecord.pendingChange);
@@ -843,8 +844,7 @@ public class ActivityManagerServiceTest {
assertEquals(expectedProcState, uidRecord.pendingChange.processState);
assertEquals(TEST_PROC_STATE_SEQ1, uidRecord.pendingChange.procStateSeq);
// Verify that DISPATCH_UIDS_CHANGED_UI_MSG is posted to handler.
mHandler.waitForMessage(DISPATCH_UIDS_CHANGED_UI_MSG);
// TODO: Verify that DISPATCH_UIDS_CHANGED_UI_MSG is posted to handler.
}
}