Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3fac471c authored by Austin Borger's avatar Austin Borger Committed by Android (Google) Code Review
Browse files

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

parents 38d07cba 24640107
Loading
Loading
Loading
Loading
+37 −0
Original line number Diff line number Diff line
@@ -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)")
+60 −2
Original line number Diff line number Diff line
@@ -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
+133 −4
Original line number Diff line number Diff line
@@ -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);
+2 −1
Original line number Diff line number Diff line
@@ -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);
    }