Loading core/java/android/app/IActivityManager.aidl +37 −0 Original line number Diff line number Diff line Loading @@ -102,6 +102,43 @@ interface IActivityManager { void registerUidObserver(in IUidObserver observer, int which, int cutpoint, String callingPackage); void unregisterUidObserver(in IUidObserver observer); /** * Registers a UidObserver with a uid filter. * * @param observer The UidObserver implementation to register. * @param which A bitmask of events to observe. See ActivityManager.UID_OBSERVER_*. * @param cutpoint The cutpoint for onUidStateChanged events. When the state crosses this * threshold in either direction, onUidStateChanged will be called. * @param callingPackage The name of the calling package. * @param uids A list of uids to watch. If all uids are to be watched, use * registerUidObserver instead. * @throws RemoteException * @return Returns A binder token identifying the UidObserver registration. */ IBinder registerUidObserverForUids(in IUidObserver observer, int which, int cutpoint, String callingPackage, in int[] uids); /** * Adds a uid to the list of uids that a UidObserver will receive updates about. * * @param observerToken The binder token identifying the UidObserver registration. * @param callingPackage The name of the calling package. * @param uid The uid to watch. * @throws RemoteException */ void addUidToObserver(in IBinder observerToken, String callingPackage, int uid); /** * Removes a uid from the list of uids that a UidObserver will receive updates about. * * @param observerToken The binder token identifying the UidObserver registration. * @param callingPackage The name of the calling package. * @param uid The uid to stop watching. * @throws RemoteException */ void removeUidFromObserver(in IBinder observerToken, String callingPackage, int uid); boolean isUidActive(int uid, String callingPackage); @JavaPassthrough(annotation= "@android.annotation.RequiresPermission(allOf = {android.Manifest.permission.PACKAGE_USAGE_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional = true)") Loading services/core/java/com/android/server/am/ActivityManagerService.java +60 −2 Original line number Diff line number Diff line Loading @@ -7511,7 +7511,31 @@ public class ActivityManagerService extends IActivityManager.Stub "registerUidObserver"); } mUidObserverController.register(observer, which, cutpoint, callingPackage, Binder.getCallingUid()); Binder.getCallingUid(), /*uids*/null); } /** * Registers a UidObserver with a uid filter. * * @param observer The UidObserver implementation to register. * @param which A bitmask of events to observe. See ActivityManager.UID_OBSERVER_*. * @param cutpoint The cutpoint for onUidStateChanged events. When the state crosses this * threshold in either direction, onUidStateChanged will be called. * @param callingPackage The name of the calling package. * @param uids A list of uids to watch. If all uids are to be watched, use * registerUidObserver instead. * @throws RemoteException * @return Returns A binder token identifying the UidObserver registration. */ @Override public IBinder registerUidObserverForUids(IUidObserver observer, int which, int cutpoint, String callingPackage, int[] uids) { if (!hasUsageStatsPermission(callingPackage)) { enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS, "registerUidObserver"); } return mUidObserverController.register(observer, which, cutpoint, callingPackage, Binder.getCallingUid(), uids); } @Override Loading @@ -7519,6 +7543,40 @@ public class ActivityManagerService extends IActivityManager.Stub mUidObserverController.unregister(observer); } /** * Adds a uid to the list of uids that a UidObserver will receive updates about. * * @param observerToken The binder token identifying the UidObserver registration. * @param callingPackage The name of the calling package. * @param uid The uid to watch. * @throws RemoteException */ @Override public void addUidToObserver(IBinder observerToken, String callingPackage, int uid) { if (!hasUsageStatsPermission(callingPackage)) { enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS, "registerUidObserver"); } mUidObserverController.addUidToObserver(observerToken, uid); } /** * Removes a uid from the list of uids that a UidObserver will receive updates about. * * @param observerToken The binder token identifying the UidObserver registration. * @param callingPackage The name of the calling package. * @param uid The uid to stop watching. * @throws RemoteException */ @Override public void removeUidFromObserver(IBinder observerToken, String callingPackage, int uid) { if (!hasUsageStatsPermission(callingPackage)) { enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS, "registerUidObserver"); } mUidObserverController.removeUidFromObserver(observerToken, uid); } @Override public boolean isUidActive(int uid, String callingPackage) { if (!hasUsageStatsPermission(callingPackage)) { Loading Loading @@ -18613,7 +18671,7 @@ public class ActivityManagerService extends IActivityManager.Stub int which, int cutpoint, @NonNull String callingPackage) { mNetworkPolicyUidObserver = observer; mUidObserverController.register(observer, which, cutpoint, callingPackage, Binder.getCallingUid()); Binder.getCallingUid(), /*uids*/null); } @Override services/core/java/com/android/server/am/UidObserverController.java +133 −4 Original line number Diff line number Diff line Loading @@ -27,7 +27,9 @@ import android.app.ActivityManager; import android.app.ActivityManagerProto; import android.app.IUidObserver; import android.content.pm.PackageManager; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SystemClock; Loading @@ -43,6 +45,8 @@ import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserve import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.UUID; public class UidObserverController { /** If a UID observer takes more than this long, send a WTF. */ Loading Loading @@ -79,14 +83,19 @@ public class UidObserverController { mValidateUids = new ActiveUids(null /* service */, false /* postChangesToAtm */); } void register(@NonNull IUidObserver observer, int which, int cutpoint, @NonNull String callingPackage, int callingUid) { IBinder register(@NonNull IUidObserver observer, int which, int cutpoint, @NonNull String callingPackage, int callingUid, @Nullable int[] uids) { IBinder token = new Binder("UidObserver-" + callingPackage + "-" + UUID.randomUUID().toString()); synchronized (mLock) { mUidObservers.register(observer, new UidObserverRegistration(callingUid, callingPackage, which, cutpoint, ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL, callingUid) == PackageManager.PERMISSION_GRANTED)); == PackageManager.PERMISSION_GRANTED, uids, token)); } return token; } void unregister(@NonNull IUidObserver observer) { Loading @@ -95,6 +104,42 @@ public class UidObserverController { } } void addUidToObserver(@NonNull IBinder observerToken, int uid) { synchronized (mLock) { int i = mUidObservers.beginBroadcast(); while (i-- > 0) { var reg = (UidObserverRegistration) mUidObservers.getBroadcastCookie(i); if (reg.getToken().equals(observerToken)) { reg.addUid(uid); break; } if (i == 0) { Slog.e(TAG_UID_OBSERVERS, "Unable to find UidObserver by token"); } } mUidObservers.finishBroadcast(); } } void removeUidFromObserver(@NonNull IBinder observerToken, int uid) { synchronized (mLock) { int i = mUidObservers.beginBroadcast(); while (i-- > 0) { var reg = (UidObserverRegistration) mUidObservers.getBroadcastCookie(i); if (reg.getToken().equals(observerToken)) { reg.removeUid(uid); break; } if (i == 0) { Slog.e(TAG_UID_OBSERVERS, "Unable to find UidObserver by token"); } } mUidObservers.finishBroadcast(); } } int enqueueUidChange(@Nullable ChangeRecord currentRecord, int uid, int change, int procState, int procAdj, long procStateSeq, int capability, boolean ephemeral) { synchronized (mLock) { Loading Loading @@ -257,6 +302,10 @@ public class UidObserverController { final ChangeRecord item = mActiveUidChanges[j]; final long start = SystemClock.uptimeMillis(); final int change = item.change; // Is the observer watching this uid? if (!reg.isWatchingUid(item.uid)) { continue; } // Does the user have permission? Don't send a non user UID change otherwise if (UserHandle.getUserId(item.uid) != UserHandle.getUserId(reg.mUid) && !reg.mCanInteractAcrossUsers) { Loading Loading @@ -450,6 +499,8 @@ public class UidObserverController { private final int mWhich; private final int mCutpoint; private final boolean mCanInteractAcrossUsers; private final IBinder mToken; private int[] mUids; /** * Total # of callback calls that took more than {@link #SLOW_UID_OBSERVER_THRESHOLD_MS}. Loading Loading @@ -481,16 +532,94 @@ public class UidObserverController { }; UidObserverRegistration(int uid, @NonNull String pkg, int which, int cutpoint, boolean canInteractAcrossUsers) { boolean canInteractAcrossUsers, @Nullable int[] uids, @NonNull IBinder token) { this.mUid = uid; this.mPkg = pkg; this.mWhich = which; this.mCutpoint = cutpoint; this.mCanInteractAcrossUsers = canInteractAcrossUsers; if (uids != null) { this.mUids = uids.clone(); Arrays.sort(this.mUids); } else { this.mUids = null; } this.mToken = token; mLastProcStates = cutpoint >= ActivityManager.MIN_PROCESS_STATE ? new SparseIntArray() : null; } boolean isWatchingUid(int uid) { if (mUids == null) { return true; } return Arrays.binarySearch(mUids, uid) != -1; } void addUid(int uid) { if (mUids == null) { return; } int[] temp = mUids; mUids = new int[temp.length + 1]; boolean inserted = false; for (int i = 0; i < temp.length; i++) { if (!inserted) { if (temp[i] < uid) { mUids[i] = temp[i]; } else if (temp[i] == uid) { // Duplicate uid, no-op and fallback to the previous array mUids = temp; return; } else { mUids[i] = uid; mUids[i + 1] = temp[i]; inserted = true; } } else { mUids[i + 1] = temp[i]; } } if (!inserted) { mUids[temp.length] = uid; } } void removeUid(int uid) { if (mUids == null || mUids.length == 0) { return; } int[] temp = mUids; mUids = new int[temp.length - 1]; boolean removed = false; for (int i = 0; i < temp.length; i++) { if (!removed) { if (temp[i] == uid) { removed = true; } else if (i == temp.length - 1) { // Uid not found, no-op and fallback to the previous array mUids = temp; return; } else { mUids[i] = temp[i]; } } else { mUids[i - 1] = temp[i]; } } } IBinder getToken() { return mToken; } void dump(@NonNull PrintWriter pw, @NonNull IUidObserver observer) { pw.print(" "); UserHandle.formatUid(pw, mUid); Loading services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java +2 −1 Original line number Diff line number Diff line Loading @@ -217,7 +217,8 @@ public class UidObserverControllerTest { 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); mUidObserverController.register(observer, which, cutpoint, callingPackage, callingUid, /*uids*/null); Mockito.reset(observer); } Loading Loading
core/java/android/app/IActivityManager.aidl +37 −0 Original line number Diff line number Diff line Loading @@ -102,6 +102,43 @@ interface IActivityManager { void registerUidObserver(in IUidObserver observer, int which, int cutpoint, String callingPackage); void unregisterUidObserver(in IUidObserver observer); /** * Registers a UidObserver with a uid filter. * * @param observer The UidObserver implementation to register. * @param which A bitmask of events to observe. See ActivityManager.UID_OBSERVER_*. * @param cutpoint The cutpoint for onUidStateChanged events. When the state crosses this * threshold in either direction, onUidStateChanged will be called. * @param callingPackage The name of the calling package. * @param uids A list of uids to watch. If all uids are to be watched, use * registerUidObserver instead. * @throws RemoteException * @return Returns A binder token identifying the UidObserver registration. */ IBinder registerUidObserverForUids(in IUidObserver observer, int which, int cutpoint, String callingPackage, in int[] uids); /** * Adds a uid to the list of uids that a UidObserver will receive updates about. * * @param observerToken The binder token identifying the UidObserver registration. * @param callingPackage The name of the calling package. * @param uid The uid to watch. * @throws RemoteException */ void addUidToObserver(in IBinder observerToken, String callingPackage, int uid); /** * Removes a uid from the list of uids that a UidObserver will receive updates about. * * @param observerToken The binder token identifying the UidObserver registration. * @param callingPackage The name of the calling package. * @param uid The uid to stop watching. * @throws RemoteException */ void removeUidFromObserver(in IBinder observerToken, String callingPackage, int uid); boolean isUidActive(int uid, String callingPackage); @JavaPassthrough(annotation= "@android.annotation.RequiresPermission(allOf = {android.Manifest.permission.PACKAGE_USAGE_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional = true)") Loading
services/core/java/com/android/server/am/ActivityManagerService.java +60 −2 Original line number Diff line number Diff line Loading @@ -7511,7 +7511,31 @@ public class ActivityManagerService extends IActivityManager.Stub "registerUidObserver"); } mUidObserverController.register(observer, which, cutpoint, callingPackage, Binder.getCallingUid()); Binder.getCallingUid(), /*uids*/null); } /** * Registers a UidObserver with a uid filter. * * @param observer The UidObserver implementation to register. * @param which A bitmask of events to observe. See ActivityManager.UID_OBSERVER_*. * @param cutpoint The cutpoint for onUidStateChanged events. When the state crosses this * threshold in either direction, onUidStateChanged will be called. * @param callingPackage The name of the calling package. * @param uids A list of uids to watch. If all uids are to be watched, use * registerUidObserver instead. * @throws RemoteException * @return Returns A binder token identifying the UidObserver registration. */ @Override public IBinder registerUidObserverForUids(IUidObserver observer, int which, int cutpoint, String callingPackage, int[] uids) { if (!hasUsageStatsPermission(callingPackage)) { enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS, "registerUidObserver"); } return mUidObserverController.register(observer, which, cutpoint, callingPackage, Binder.getCallingUid(), uids); } @Override Loading @@ -7519,6 +7543,40 @@ public class ActivityManagerService extends IActivityManager.Stub mUidObserverController.unregister(observer); } /** * Adds a uid to the list of uids that a UidObserver will receive updates about. * * @param observerToken The binder token identifying the UidObserver registration. * @param callingPackage The name of the calling package. * @param uid The uid to watch. * @throws RemoteException */ @Override public void addUidToObserver(IBinder observerToken, String callingPackage, int uid) { if (!hasUsageStatsPermission(callingPackage)) { enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS, "registerUidObserver"); } mUidObserverController.addUidToObserver(observerToken, uid); } /** * Removes a uid from the list of uids that a UidObserver will receive updates about. * * @param observerToken The binder token identifying the UidObserver registration. * @param callingPackage The name of the calling package. * @param uid The uid to stop watching. * @throws RemoteException */ @Override public void removeUidFromObserver(IBinder observerToken, String callingPackage, int uid) { if (!hasUsageStatsPermission(callingPackage)) { enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS, "registerUidObserver"); } mUidObserverController.removeUidFromObserver(observerToken, uid); } @Override public boolean isUidActive(int uid, String callingPackage) { if (!hasUsageStatsPermission(callingPackage)) { Loading Loading @@ -18613,7 +18671,7 @@ public class ActivityManagerService extends IActivityManager.Stub int which, int cutpoint, @NonNull String callingPackage) { mNetworkPolicyUidObserver = observer; mUidObserverController.register(observer, which, cutpoint, callingPackage, Binder.getCallingUid()); Binder.getCallingUid(), /*uids*/null); } @Override
services/core/java/com/android/server/am/UidObserverController.java +133 −4 Original line number Diff line number Diff line Loading @@ -27,7 +27,9 @@ import android.app.ActivityManager; import android.app.ActivityManagerProto; import android.app.IUidObserver; import android.content.pm.PackageManager; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SystemClock; Loading @@ -43,6 +45,8 @@ import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserve import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.UUID; public class UidObserverController { /** If a UID observer takes more than this long, send a WTF. */ Loading Loading @@ -79,14 +83,19 @@ public class UidObserverController { mValidateUids = new ActiveUids(null /* service */, false /* postChangesToAtm */); } void register(@NonNull IUidObserver observer, int which, int cutpoint, @NonNull String callingPackage, int callingUid) { IBinder register(@NonNull IUidObserver observer, int which, int cutpoint, @NonNull String callingPackage, int callingUid, @Nullable int[] uids) { IBinder token = new Binder("UidObserver-" + callingPackage + "-" + UUID.randomUUID().toString()); synchronized (mLock) { mUidObservers.register(observer, new UidObserverRegistration(callingUid, callingPackage, which, cutpoint, ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL, callingUid) == PackageManager.PERMISSION_GRANTED)); == PackageManager.PERMISSION_GRANTED, uids, token)); } return token; } void unregister(@NonNull IUidObserver observer) { Loading @@ -95,6 +104,42 @@ public class UidObserverController { } } void addUidToObserver(@NonNull IBinder observerToken, int uid) { synchronized (mLock) { int i = mUidObservers.beginBroadcast(); while (i-- > 0) { var reg = (UidObserverRegistration) mUidObservers.getBroadcastCookie(i); if (reg.getToken().equals(observerToken)) { reg.addUid(uid); break; } if (i == 0) { Slog.e(TAG_UID_OBSERVERS, "Unable to find UidObserver by token"); } } mUidObservers.finishBroadcast(); } } void removeUidFromObserver(@NonNull IBinder observerToken, int uid) { synchronized (mLock) { int i = mUidObservers.beginBroadcast(); while (i-- > 0) { var reg = (UidObserverRegistration) mUidObservers.getBroadcastCookie(i); if (reg.getToken().equals(observerToken)) { reg.removeUid(uid); break; } if (i == 0) { Slog.e(TAG_UID_OBSERVERS, "Unable to find UidObserver by token"); } } mUidObservers.finishBroadcast(); } } int enqueueUidChange(@Nullable ChangeRecord currentRecord, int uid, int change, int procState, int procAdj, long procStateSeq, int capability, boolean ephemeral) { synchronized (mLock) { Loading Loading @@ -257,6 +302,10 @@ public class UidObserverController { final ChangeRecord item = mActiveUidChanges[j]; final long start = SystemClock.uptimeMillis(); final int change = item.change; // Is the observer watching this uid? if (!reg.isWatchingUid(item.uid)) { continue; } // Does the user have permission? Don't send a non user UID change otherwise if (UserHandle.getUserId(item.uid) != UserHandle.getUserId(reg.mUid) && !reg.mCanInteractAcrossUsers) { Loading Loading @@ -450,6 +499,8 @@ public class UidObserverController { private final int mWhich; private final int mCutpoint; private final boolean mCanInteractAcrossUsers; private final IBinder mToken; private int[] mUids; /** * Total # of callback calls that took more than {@link #SLOW_UID_OBSERVER_THRESHOLD_MS}. Loading Loading @@ -481,16 +532,94 @@ public class UidObserverController { }; UidObserverRegistration(int uid, @NonNull String pkg, int which, int cutpoint, boolean canInteractAcrossUsers) { boolean canInteractAcrossUsers, @Nullable int[] uids, @NonNull IBinder token) { this.mUid = uid; this.mPkg = pkg; this.mWhich = which; this.mCutpoint = cutpoint; this.mCanInteractAcrossUsers = canInteractAcrossUsers; if (uids != null) { this.mUids = uids.clone(); Arrays.sort(this.mUids); } else { this.mUids = null; } this.mToken = token; mLastProcStates = cutpoint >= ActivityManager.MIN_PROCESS_STATE ? new SparseIntArray() : null; } boolean isWatchingUid(int uid) { if (mUids == null) { return true; } return Arrays.binarySearch(mUids, uid) != -1; } void addUid(int uid) { if (mUids == null) { return; } int[] temp = mUids; mUids = new int[temp.length + 1]; boolean inserted = false; for (int i = 0; i < temp.length; i++) { if (!inserted) { if (temp[i] < uid) { mUids[i] = temp[i]; } else if (temp[i] == uid) { // Duplicate uid, no-op and fallback to the previous array mUids = temp; return; } else { mUids[i] = uid; mUids[i + 1] = temp[i]; inserted = true; } } else { mUids[i + 1] = temp[i]; } } if (!inserted) { mUids[temp.length] = uid; } } void removeUid(int uid) { if (mUids == null || mUids.length == 0) { return; } int[] temp = mUids; mUids = new int[temp.length - 1]; boolean removed = false; for (int i = 0; i < temp.length; i++) { if (!removed) { if (temp[i] == uid) { removed = true; } else if (i == temp.length - 1) { // Uid not found, no-op and fallback to the previous array mUids = temp; return; } else { mUids[i] = temp[i]; } } else { mUids[i - 1] = temp[i]; } } } IBinder getToken() { return mToken; } void dump(@NonNull PrintWriter pw, @NonNull IUidObserver observer) { pw.print(" "); UserHandle.formatUid(pw, mUid); Loading
services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java +2 −1 Original line number Diff line number Diff line Loading @@ -217,7 +217,8 @@ public class UidObserverControllerTest { 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); mUidObserverController.register(observer, which, cutpoint, callingPackage, callingUid, /*uids*/null); Mockito.reset(observer); } Loading