diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 36c66e5c34147..db42803ac9f9f 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -794,6 +794,9 @@ public class ActivityManager { /** @hide Flag for registerUidObserver: report uid cached state has changed. */ public static final int UID_OBSERVER_CACHED = 1<<4; + /** @hide Flag for registerUidObserver: report uid capability has changed. */ + public static final int UID_OBSERVER_CAPABILITY = 1<<5; + /** @hide Mode for {@link IActivityManager#isAppStartModeDisabled}: normal free-to-run operation. */ public static final int APP_START_MODE_NORMAL = 0; diff --git a/core/proto/android/app/activitymanager.proto b/core/proto/android/app/activitymanager.proto index ead7e1633c56b..a21d1d4f0a853 100644 --- a/core/proto/android/app/activitymanager.proto +++ b/core/proto/android/app/activitymanager.proto @@ -33,4 +33,6 @@ enum UidObserverFlag { UID_OBSERVER_FLAG_ACTIVE = 4; // report uid cached state has changed, original value is 1 << 4 UID_OBSERVER_FLAG_CACHED = 5; + // report uid capability has changed, original value is 1 << 5 + UID_OBSERVER_FLAG_CAPABILITY = 6; } diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto index 74a37cadc54c5..17dc4589d402b 100644 --- a/core/proto/android/server/activitymanagerservice.proto +++ b/core/proto/android/server/activitymanagerservice.proto @@ -778,6 +778,7 @@ message UidRecordProto { CHANGE_ACTIVE = 2; CHANGE_CACHED = 3; CHANGE_UNCACHED = 4; + CHANGE_CAPABILITY = 5; } repeated Change last_reported_changes = 8; optional int32 num_procs = 9; diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index c8721cc209ce0..960cc42e8ec95 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1328,6 +1328,9 @@ public final class OomAdjuster { || uidRec.getSetProcState() == PROCESS_STATE_NONEXISTENT) { uidChange |= isCached ? UidRecord.CHANGE_CACHED : UidRecord.CHANGE_UNCACHED; } + if (uidRec.getSetCapability() != uidRec.getCurCapability()) { + uidChange |= UidRecord.CHANGE_CAPABILITY; + } uidRec.setSetProcState(uidRec.getCurProcState()); uidRec.setSetCapability(uidRec.getCurCapability()); uidRec.setSetAllowListed(uidRec.isCurAllowListed()); diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java index df65ee6e3a280..c1bfe25618b20 100644 --- a/services/core/java/com/android/server/am/UidObserverController.java +++ b/services/core/java/com/android/server/am/UidObserverController.java @@ -147,6 +147,9 @@ public class UidObserverController { if ((currentChange & UidRecord.CHANGE_GONE) != 0) { currentChange &= ~(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_CACHED); } + if ((pendingChange & UidRecord.CHANGE_CAPABILITY) != 0) { + currentChange |= UidRecord.CHANGE_CAPABILITY; + } return currentChange; } @@ -285,12 +288,9 @@ public class UidObserverController { reg.mLastProcStates.delete(item.uid); } } else { + boolean doReport = false; if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) { - if (DEBUG_UID_OBSERVERS) { - Slog.i(TAG_UID_OBSERVERS, "UID CHANGED uid=" + item.uid - + ": " + item.procState + ": " + item.capability); - } - boolean doReport = true; + doReport = true; if (reg.mCutpoint >= ActivityManager.MIN_PROCESS_STATE) { final int lastState = reg.mLastProcStates.get(item.uid, ActivityManager.PROCESS_STATE_UNKNOWN); @@ -302,13 +302,20 @@ public class UidObserverController { doReport = item.procState != PROCESS_STATE_NONEXISTENT; } } - if (doReport) { - if (reg.mLastProcStates != null) { - reg.mLastProcStates.put(item.uid, item.procState); - } - observer.onUidStateChanged(item.uid, item.procState, - item.procStateSeq, item.capability); + } + if ((reg.mWhich & ActivityManager.UID_OBSERVER_CAPABILITY) != 0) { + doReport |= (change & UidRecord.CHANGE_CAPABILITY) != 0; + } + if (doReport) { + if (DEBUG_UID_OBSERVERS) { + Slog.i(TAG_UID_OBSERVERS, "UID CHANGED uid=" + item.uid + + ": " + item.procState + ": " + item.capability); } + if (reg.mLastProcStates != null) { + reg.mLastProcStates.put(item.uid, item.procState); + } + observer.onUidStateChanged(item.uid, item.procState, + item.procStateSeq, item.capability); } } final int duration = (int) (SystemClock.uptimeMillis() - start); @@ -428,12 +435,14 @@ public class UidObserverController { ActivityManager.UID_OBSERVER_ACTIVE, ActivityManager.UID_OBSERVER_GONE, ActivityManager.UID_OBSERVER_PROCSTATE, + ActivityManager.UID_OBSERVER_CAPABILITY, }; private static final 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, + ActivityManagerProto.UID_OBSERVER_FLAG_CAPABILITY, }; UidObserverRegistration(int uid, @NonNull String pkg, int which, int cutpoint) { @@ -462,6 +471,9 @@ public class UidObserverController { if ((mWhich & ActivityManager.UID_OBSERVER_GONE) != 0) { pw.print(" GONE"); } + if ((mWhich & ActivityManager.UID_OBSERVER_CAPABILITY) != 0) { + pw.print(" CAP"); + } if ((mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) { pw.print(" STATE"); pw.print(" (cut="); diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java index 2fb662f5ee69b..4ba59faea9ec2 100644 --- a/services/core/java/com/android/server/am/UidRecord.java +++ b/services/core/java/com/android/server/am/UidRecord.java @@ -122,6 +122,7 @@ public final class UidRecord { static final int CHANGE_ACTIVE = 1<<2; static final int CHANGE_CACHED = 1<<3; static final int CHANGE_UNCACHED = 1<<4; + static final int CHANGE_CAPABILITY = 1<<5; // Keep the enum lists in sync private static int[] ORIG_ENUMS = new int[] { @@ -130,6 +131,7 @@ public final class UidRecord { CHANGE_ACTIVE, CHANGE_CACHED, CHANGE_UNCACHED, + CHANGE_CAPABILITY, }; private static int[] PROTO_ENUMS = new int[] { UidRecordProto.CHANGE_GONE, @@ -137,6 +139,7 @@ public final class UidRecord { UidRecordProto.CHANGE_ACTIVE, UidRecordProto.CHANGE_CACHED, UidRecordProto.CHANGE_UNCACHED, + UidRecordProto.CHANGE_CAPABILITY, }; // UidObserverController is the only thing that should modify this. diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index bcd90073af075..05922b3bb0702 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -926,10 +926,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); try { - // TODO: There shouldn't be a need to receive callback for all changes. - mActivityManager.registerUidObserver(mUidObserver, - ActivityManager.UID_OBSERVER_PROCSTATE|ActivityManager.UID_OBSERVER_GONE, - ActivityManager.PROCESS_STATE_UNKNOWN, "android"); + final int changes = ActivityManager.UID_OBSERVER_PROCSTATE + | ActivityManager.UID_OBSERVER_GONE + | ActivityManager.UID_OBSERVER_CAPABILITY; + mActivityManager.registerUidObserver(mUidObserver, changes, + NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE, "android"); mNetworkManager.registerObserver(mAlertObserver); } catch (RemoteException e) { // ignored; both services live in system_server 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 fc4804b4543db..41237c8ae51fd 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java @@ -72,6 +72,7 @@ import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; +import android.util.IntArray; import androidx.test.filters.MediumTest; import androidx.test.filters.SmallTest; @@ -121,7 +122,8 @@ public class ActivityManagerServiceTest { UidRecord.CHANGE_GONE, UidRecord.CHANGE_GONE | UidRecord.CHANGE_IDLE, UidRecord.CHANGE_IDLE, - UidRecord.CHANGE_ACTIVE + UidRecord.CHANGE_ACTIVE, + UidRecord.CHANGE_CAPABILITY, }; private static PackageManagerInternal sPackageManagerInternal; @@ -528,8 +530,10 @@ public class ActivityManagerServiceTest { ActivityManager.UID_OBSERVER_GONE, ActivityManager.UID_OBSERVER_IDLE, ActivityManager.UID_OBSERVER_ACTIVE, + ActivityManager.UID_OBSERVER_CAPABILITY, ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_ACTIVE | ActivityManager.UID_OBSERVER_IDLE + | ActivityManager.UID_OBSERVER_CAPABILITY }; final IUidObserver[] observers = new IUidObserver.Stub[changesToObserve.length]; for (int i = 0; i < observers.length; ++i) { @@ -553,7 +557,16 @@ public class ActivityManagerServiceTest { ActivityManager.PROCESS_STATE_NONEXISTENT, ActivityManager.PROCESS_STATE_CACHED_EMPTY, ActivityManager.PROCESS_STATE_CACHED_ACTIVITY, - ActivityManager.PROCESS_STATE_TOP + ActivityManager.PROCESS_STATE_TOP, + ActivityManager.PROCESS_STATE_TOP, + }; + final int[] capabilitiesForPendingUidRecords = { + ActivityManager.PROCESS_CAPABILITY_ALL, + ActivityManager.PROCESS_CAPABILITY_NONE, + ActivityManager.PROCESS_CAPABILITY_NONE, + ActivityManager.PROCESS_CAPABILITY_NONE, + ActivityManager.PROCESS_CAPABILITY_NONE, + ActivityManager.PROCESS_CAPABILITY_NETWORK, }; final Map changeItems = new HashMap<>(); for (int i = 0; i < changesForPendingUidRecords.length; ++i) { @@ -562,6 +575,7 @@ public class ActivityManagerServiceTest { pendingChange.uid = i; pendingChange.procState = procStatesForPendingUidRecords[i]; pendingChange.procStateSeq = i; + pendingChange.capability = capabilitiesForPendingUidRecords[i]; changeItems.put(changesForPendingUidRecords[i], pendingChange); addPendingUidChange(pendingChange); } @@ -606,20 +620,26 @@ public class ActivityManagerServiceTest { verify(observer).onUidGone(changeItem.uid, changeItem.ephemeral); }); } - if ((changeToObserve & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) { + if ((changeToObserve & ActivityManager.UID_OBSERVER_PROCSTATE) != 0 + || (changeToObserve & ActivityManager.UID_OBSERVER_CAPABILITY) != 0) { // Observer listens to uid procState changes, so change items corresponding to // UidRecord.CHANGE_PROCSTATE or UidRecord.CHANGE_IDLE or UidRecord.CHANGE_ACTIVE // needs to be delivered to this observer. - final int[] changesToVerify = { - UidRecord.CHANGE_PROCSTATE, - UidRecord.CHANGE_ACTIVE, - UidRecord.CHANGE_IDLE - }; - verifyObserverReceivedChanges(observerToTest, changesToVerify, changeItems, + final IntArray changesToVerify = new IntArray(); + if ((changeToObserve & ActivityManager.UID_OBSERVER_PROCSTATE) == 0) { + changesToVerify.add(UidRecord.CHANGE_CAPABILITY); + } else { + changesToVerify.add(UidRecord.CHANGE_PROCSTATE); + changesToVerify.add(UidRecord.CHANGE_ACTIVE); + changesToVerify.add(UidRecord.CHANGE_IDLE); + changesToVerify.add(UidRecord.CHANGE_CAPABILITY); + } + verifyObserverReceivedChanges(observerToTest, changesToVerify.toArray(), + changeItems, (observer, changeItem) -> { verify(observer).onUidStateChanged(changeItem.uid, changeItem.procState, changeItem.procStateSeq, - ActivityManager.PROCESS_CAPABILITY_NONE); + changeItem.capability); }); } // Verify there are no other callbacks for this observer. @@ -725,11 +745,11 @@ public class ActivityManagerServiceTest { ActivityManager.PROCESS_STATE_CACHED_EMPTY, ActivityManager.PROCESS_STATE_CACHED_ACTIVITY, ActivityManager.PROCESS_STATE_SERVICE, - ActivityManager.PROCESS_STATE_RECEIVER + ActivityManager.PROCESS_STATE_RECEIVER, }; final ArrayList pendingItemsForUids = - new ArrayList<>(changesForPendingItems.length); - for (int i = 0; i < changesForPendingItems.length; ++i) { + new ArrayList<>(procStatesForPendingItems.length); + for (int i = 0; i < procStatesForPendingItems.length; ++i) { final ChangeRecord item = new ChangeRecord(); item.uid = i; item.change = changesForPendingItems[i]; diff --git a/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java index 1de5f6f005d17..0db118d3db47d 100644 --- a/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java @@ -18,6 +18,7 @@ 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_NETWORK; 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; @@ -109,6 +110,7 @@ public class UidObserverControllerTest { @Test public void testMergeWithPendingChange() { + // Map of expectedChange -> {(currentChange, pendingChange)} final SparseArray> changesToVerify = new SparseArray<>(); changesToVerify.put(UidRecord.CHANGE_ACTIVE, @@ -127,6 +129,8 @@ public class UidObserverControllerTest { Pair.create(UidRecord.CHANGE_GONE, UidRecord.CHANGE_ACTIVE)); changesToVerify.put(UidRecord.CHANGE_GONE, Pair.create(UidRecord.CHANGE_GONE, UidRecord.CHANGE_CACHED)); + changesToVerify.put(UidRecord.CHANGE_PROCSTATE | UidRecord.CHANGE_CAPABILITY, + Pair.create(UidRecord.CHANGE_PROCSTATE, UidRecord.CHANGE_CAPABILITY)); for (int i = 0; i < changesToVerify.size(); ++i) { final int expectedChange = changesToVerify.keyAt(i); @@ -149,7 +153,8 @@ public class UidObserverControllerTest { ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_ACTIVE, PROCESS_STATE_IMPORTANT_FOREGROUND, TEST_PKG2, TEST_UID2); final IUidObserver observer2 = mock(IUidObserver.Stub.class); - registerObserver(observer2, ActivityManager.UID_OBSERVER_PROCSTATE, + registerObserver(observer2, + ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_CAPABILITY, PROCESS_STATE_SERVICE, TEST_PKG3, TEST_UID3); mUidObserverController.dispatchUidsChanged(); @@ -177,6 +182,14 @@ public class UidObserverControllerTest { verifyNoMoreInteractions(observer1); verifyNoMoreInteractions(observer2); + addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE | UidRecord.CHANGE_CAPABILITY, + PROCESS_STATE_RECEIVER, 111, PROCESS_CAPABILITY_NETWORK, false); + mUidObserverController.dispatchUidsChanged(); + verify(observer2).onUidStateChanged(TEST_UID1, PROCESS_STATE_RECEIVER, + 111, PROCESS_CAPABILITY_NETWORK); + verifyNoMoreInteractions(observer1); + verifyNoMoreInteractions(observer2); + unregisterObserver(observer1); addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_TOP,