Merge "Camera / UidObserver: Add the ability to subscribe to specific UIDs" into udc-dev am: 3fac471cfc am: ad0d693cf6

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/22829760

Change-Id: I8a320693e5eeacf230f3ff43e8bea954b64e0cd1
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Austin Borger
2023-05-08 19:51:20 +00:00
committed by Automerger Merge Worker
4 changed files with 232 additions and 7 deletions

View File

@@ -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)")

View File

@@ -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
@@ -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)) {
@@ -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

View File

@@ -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;
@@ -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. */
@@ -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) {
@@ -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) {
@@ -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) {
@@ -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}.
@@ -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);

View File

@@ -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);
}