1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.server.am;
17 
18 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
19 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
20 
21 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
22 import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
23 
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.app.ActivityManager;
27 import android.app.ActivityManagerProto;
28 import android.app.IUidObserver;
29 import android.content.pm.PackageManager;
30 import android.os.Binder;
31 import android.os.Handler;
32 import android.os.IBinder;
33 import android.os.Message;
34 import android.os.RemoteCallbackList;
35 import android.os.RemoteException;
36 import android.os.SystemClock;
37 import android.os.UserHandle;
38 import android.util.Slog;
39 import android.util.SparseIntArray;
40 import android.util.proto.ProtoOutputStream;
41 import android.util.proto.ProtoUtils;
42 
43 import com.android.internal.annotations.GuardedBy;
44 import com.android.internal.annotations.VisibleForTesting;
45 import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
46 
47 import java.io.PrintWriter;
48 import java.util.ArrayList;
49 import java.util.Arrays;
50 import java.util.UUID;
51 
52 public class UidObserverController {
53     /** If a UID observer takes more than this long, send a WTF. */
54     private static final int SLOW_UID_OBSERVER_THRESHOLD_MS = 20;
55 
56     private final Handler mHandler;
57 
58     private final Object mLock = new Object();
59 
60     @GuardedBy("mLock")
61     final RemoteCallbackList<IUidObserver> mUidObservers = new RemoteCallbackList<>();
62 
63     @GuardedBy("mLock")
64     private final ArrayList<ChangeRecord> mPendingUidChanges = new ArrayList<>();
65     @GuardedBy("mLock")
66     private final ArrayList<ChangeRecord> mAvailUidChanges = new ArrayList<>();
67 
68     private ChangeRecord[] mActiveUidChanges = new ChangeRecord[5];
69 
70     /** Total # of UID change events dispatched, shown in dumpsys. */
71     @GuardedBy("mLock")
72     private int mUidChangeDispatchCount;
73 
74     private final Runnable mDispatchRunnable = this::dispatchUidsChanged;
75 
76     /**
77      * This is for verifying the UID report flow.
78      */
79     private static final boolean VALIDATE_UID_STATES = true;
80     private final ActiveUids mValidateUids;
81 
UidObserverController(@onNull Handler handler)82     UidObserverController(@NonNull Handler handler) {
83         mHandler = handler;
84         mValidateUids = new ActiveUids(null /* service */, false /* postChangesToAtm */);
85     }
86 
register(@onNull IUidObserver observer, int which, int cutpoint, @NonNull String callingPackage, int callingUid, @Nullable int[] uids)87     IBinder register(@NonNull IUidObserver observer, int which, int cutpoint,
88             @NonNull String callingPackage, int callingUid, @Nullable int[] uids) {
89         IBinder token = new Binder("UidObserver-" + callingPackage + "-"
90                 + UUID.randomUUID().toString());
91 
92         synchronized (mLock) {
93             mUidObservers.register(observer, new UidObserverRegistration(callingUid,
94                     callingPackage, which, cutpoint,
95                     ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL, callingUid)
96                     == PackageManager.PERMISSION_GRANTED, uids, token));
97         }
98 
99         return token;
100     }
101 
unregister(@onNull IUidObserver observer)102     void unregister(@NonNull IUidObserver observer) {
103         synchronized (mLock) {
104             mUidObservers.unregister(observer);
105         }
106     }
107 
addUidToObserver(@onNull IBinder observerToken, int uid)108     final void addUidToObserver(@NonNull IBinder observerToken, int uid) {
109         Message msg = Message.obtain(mHandler, ActivityManagerService.ADD_UID_TO_OBSERVER_MSG,
110                 uid, /*arg2*/ 0, observerToken);
111         mHandler.sendMessage(msg);
112     }
113 
114     /**
115      * Add a uid to the list of uids an observer is interested in. Must be run on the same thread
116      * as mDispatchRunnable.
117      *
118      * @param observerToken The token identifier for a UidObserver
119      * @param uid The uid to add to the list of watched uids
120      */
addUidToObserverImpl(@onNull IBinder observerToken, int uid)121     public final void addUidToObserverImpl(@NonNull IBinder observerToken, int uid) {
122         int i = mUidObservers.beginBroadcast();
123         while (i-- > 0) {
124             var reg = (UidObserverRegistration) mUidObservers.getBroadcastCookie(i);
125             if (reg.getToken().equals(observerToken)) {
126                 reg.addUid(uid);
127                 break;
128             }
129 
130             if (i == 0) {
131                 Slog.e(TAG_UID_OBSERVERS, "Unable to find UidObserver by token");
132             }
133         }
134         mUidObservers.finishBroadcast();
135     }
136 
removeUidFromObserver(@onNull IBinder observerToken, int uid)137     final void removeUidFromObserver(@NonNull IBinder observerToken, int uid) {
138         Message msg = Message.obtain(mHandler, ActivityManagerService.REMOVE_UID_FROM_OBSERVER_MSG,
139                 uid, /*arg2*/ 0, observerToken);
140         mHandler.sendMessage(msg);
141     }
142 
143     /**
144      * Remove a uid from the list of uids an observer is interested in. Must be run on the same
145      * thread as mDispatchRunnable.
146      *
147      * @param observerToken The token identifier for a UidObserver
148      * @param uid The uid to remove from the list of watched uids
149      */
removeUidFromObserverImpl(@onNull IBinder observerToken, int uid)150     public final void removeUidFromObserverImpl(@NonNull IBinder observerToken, int uid) {
151         int i = mUidObservers.beginBroadcast();
152         while (i-- > 0) {
153             var reg = (UidObserverRegistration) mUidObservers.getBroadcastCookie(i);
154             if (reg.getToken().equals(observerToken)) {
155                 reg.removeUid(uid);
156                 break;
157             }
158 
159             if (i == 0) {
160                 Slog.e(TAG_UID_OBSERVERS, "Unable to find UidObserver by token");
161             }
162         }
163         mUidObservers.finishBroadcast();
164     }
165 
enqueueUidChange(@ullable ChangeRecord currentRecord, int uid, int change, int procState, int procAdj, long procStateSeq, int capability, boolean ephemeral)166     int enqueueUidChange(@Nullable ChangeRecord currentRecord, int uid, int change, int procState,
167             int procAdj, long procStateSeq, int capability, boolean ephemeral) {
168         synchronized (mLock) {
169             if (mPendingUidChanges.size() == 0) {
170                 if (DEBUG_UID_OBSERVERS) {
171                     Slog.i(TAG_UID_OBSERVERS, "*** Enqueueing dispatch uid changed!");
172                 }
173                 mHandler.post(mDispatchRunnable);
174             }
175 
176             final ChangeRecord changeRecord = currentRecord != null
177                     ? currentRecord : getOrCreateChangeRecordLocked();
178             if (!changeRecord.isPending) {
179                 changeRecord.isPending = true;
180                 mPendingUidChanges.add(changeRecord);
181             } else {
182                 change = mergeWithPendingChange(change, changeRecord.change);
183             }
184 
185             changeRecord.uid = uid;
186             changeRecord.change = change;
187             changeRecord.procState = procState;
188             changeRecord.procAdj = procAdj;
189             changeRecord.procStateSeq = procStateSeq;
190             changeRecord.capability = capability;
191             changeRecord.ephemeral = ephemeral;
192 
193             return changeRecord.change;
194         }
195     }
196 
getPendingUidChangesForTest()197     ArrayList<ChangeRecord> getPendingUidChangesForTest() {
198         return mPendingUidChanges;
199     }
200 
getValidateUidsForTest()201     ActiveUids getValidateUidsForTest() {
202         return mValidateUids;
203     }
204 
getDispatchRunnableForTest()205     Runnable getDispatchRunnableForTest() {
206         return mDispatchRunnable;
207     }
208 
209     @VisibleForTesting
mergeWithPendingChange(int currentChange, int pendingChange)210     static int mergeWithPendingChange(int currentChange, int pendingChange) {
211         // If there is no change in idle or active state, then keep whatever was pending.
212         if ((currentChange & (UidRecord.CHANGE_IDLE | UidRecord.CHANGE_ACTIVE)) == 0) {
213             currentChange |= (pendingChange & (UidRecord.CHANGE_IDLE
214                     | UidRecord.CHANGE_ACTIVE));
215         }
216         // If there is no change in cached or uncached state, then keep whatever was pending.
217         if ((currentChange & (UidRecord.CHANGE_CACHED | UidRecord.CHANGE_UNCACHED)) == 0) {
218             currentChange |= (pendingChange & (UidRecord.CHANGE_CACHED
219                     | UidRecord.CHANGE_UNCACHED));
220         }
221         // If this is a report of the UID being gone, then we shouldn't keep any previous
222         // report of it being active or cached.  (That is, a gone uid is never active,
223         // and never cached.)
224         if ((currentChange & UidRecord.CHANGE_GONE) != 0) {
225             currentChange &= ~(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_CACHED);
226         }
227         if ((pendingChange & UidRecord.CHANGE_CAPABILITY) != 0) {
228             currentChange |= UidRecord.CHANGE_CAPABILITY;
229         }
230         if ((pendingChange & UidRecord.CHANGE_PROCSTATE) != 0) {
231             currentChange |= UidRecord.CHANGE_PROCSTATE;
232         }
233         if ((pendingChange & UidRecord.CHANGE_PROCADJ) != 0) {
234             currentChange |= UidRecord.CHANGE_PROCADJ;
235         }
236         return currentChange;
237     }
238 
239     @GuardedBy("mLock")
getOrCreateChangeRecordLocked()240     private ChangeRecord getOrCreateChangeRecordLocked() {
241         final ChangeRecord changeRecord;
242         final int size = mAvailUidChanges.size();
243         if (size > 0) {
244             changeRecord = mAvailUidChanges.remove(size - 1);
245             if (DEBUG_UID_OBSERVERS) {
246                 Slog.i(TAG_UID_OBSERVERS, "Retrieving available item: " + changeRecord);
247             }
248         } else {
249             changeRecord = new ChangeRecord();
250             if (DEBUG_UID_OBSERVERS) {
251                 Slog.i(TAG_UID_OBSERVERS, "Allocating new item: " + changeRecord);
252             }
253         }
254         return changeRecord;
255     }
256 
257     @VisibleForTesting
dispatchUidsChanged()258     void dispatchUidsChanged() {
259         final int numUidChanges;
260         synchronized (mLock) {
261             numUidChanges = mPendingUidChanges.size();
262             if (mActiveUidChanges.length < numUidChanges) {
263                 mActiveUidChanges = new ChangeRecord[numUidChanges];
264             }
265             for (int i = 0; i < numUidChanges; i++) {
266                 final ChangeRecord changeRecord = mPendingUidChanges.get(i);
267                 mActiveUidChanges[i] = getOrCreateChangeRecordLocked();
268                 changeRecord.copyTo(mActiveUidChanges[i]);
269                 changeRecord.isPending = false;
270             }
271             mPendingUidChanges.clear();
272             if (DEBUG_UID_OBSERVERS) {
273                 Slog.i(TAG_UID_OBSERVERS, "*** Delivering " + numUidChanges + " uid changes");
274             }
275             mUidChangeDispatchCount += numUidChanges;
276         }
277 
278         int i = mUidObservers.beginBroadcast();
279         while (i-- > 0) {
280             dispatchUidsChangedForObserver(mUidObservers.getBroadcastItem(i),
281                     (UidObserverRegistration) mUidObservers.getBroadcastCookie(i), numUidChanges);
282         }
283         mUidObservers.finishBroadcast();
284 
285         if (VALIDATE_UID_STATES && mUidObservers.getRegisteredCallbackCount() > 0) {
286             for (int j = 0; j < numUidChanges; ++j) {
287                 final ChangeRecord item = mActiveUidChanges[j];
288                 if ((item.change & UidRecord.CHANGE_GONE) != 0) {
289                     mValidateUids.remove(item.uid);
290                 } else {
291                     UidRecord validateUid = mValidateUids.get(item.uid);
292                     if (validateUid == null) {
293                         validateUid = new UidRecord(item.uid, null);
294                         mValidateUids.put(item.uid, validateUid);
295                     }
296                     if ((item.change & UidRecord.CHANGE_IDLE) != 0) {
297                         validateUid.setIdle(true);
298                     } else if ((item.change & UidRecord.CHANGE_ACTIVE) != 0) {
299                         validateUid.setIdle(false);
300                     }
301                     validateUid.setSetProcState(item.procState);
302                     validateUid.setCurProcState(item.procState);
303                     validateUid.setSetCapability(item.capability);
304                     validateUid.setCurCapability(item.capability);
305                 }
306             }
307         }
308 
309         synchronized (mLock) {
310             for (int j = 0; j < numUidChanges; j++) {
311                 final ChangeRecord changeRecord = mActiveUidChanges[j];
312                 changeRecord.isPending = false;
313                 mAvailUidChanges.add(changeRecord);
314             }
315         }
316     }
317 
dispatchUidsChangedForObserver(@onNull IUidObserver observer, @NonNull UidObserverRegistration reg, int changesSize)318     private void dispatchUidsChangedForObserver(@NonNull IUidObserver observer,
319             @NonNull UidObserverRegistration reg, int changesSize) {
320         if (observer == null) {
321             return;
322         }
323         try {
324             for (int j = 0; j < changesSize; j++) {
325                 final ChangeRecord item = mActiveUidChanges[j];
326                 final long start = SystemClock.uptimeMillis();
327                 final int change = item.change;
328                 // Is the observer watching this uid?
329                 if (!reg.isWatchingUid(item.uid)) {
330                     continue;
331                 }
332                 // Does the user have permission? Don't send a non user UID change otherwise
333                 if (UserHandle.getUserId(item.uid) != UserHandle.getUserId(reg.mUid)
334                         && !reg.mCanInteractAcrossUsers) {
335                     continue;
336                 }
337                 if (change == UidRecord.CHANGE_PROCSTATE
338                         && (reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) == 0) {
339                     // No-op common case: no significant change, the observer is not
340                     // interested in all proc state changes.
341                     continue;
342                 }
343                 if (change == UidRecord.CHANGE_PROCADJ
344                         && (reg.mWhich & ActivityManager.UID_OBSERVER_PROC_OOM_ADJ) == 0) {
345                     // No-op common case: no significant change, the observer is not
346                     // interested in proc adj changes.
347                     continue;
348                 }
349                 if ((change & UidRecord.CHANGE_IDLE) != 0) {
350                     if ((reg.mWhich & ActivityManager.UID_OBSERVER_IDLE) != 0) {
351                         if (DEBUG_UID_OBSERVERS) {
352                             Slog.i(TAG_UID_OBSERVERS, "UID idle uid=" + item.uid);
353                         }
354                         observer.onUidIdle(item.uid, item.ephemeral);
355                     }
356                 } else if ((change & UidRecord.CHANGE_ACTIVE) != 0) {
357                     if ((reg.mWhich & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
358                         if (DEBUG_UID_OBSERVERS) {
359                             Slog.i(TAG_UID_OBSERVERS, "UID active uid=" + item.uid);
360                         }
361                         observer.onUidActive(item.uid);
362                     }
363                 }
364                 if ((reg.mWhich & ActivityManager.UID_OBSERVER_CACHED) != 0) {
365                     if ((change & UidRecord.CHANGE_CACHED) != 0) {
366                         if (DEBUG_UID_OBSERVERS) {
367                             Slog.i(TAG_UID_OBSERVERS, "UID cached uid=" + item.uid);
368                         }
369                         observer.onUidCachedChanged(item.uid, true);
370                     } else if ((change & UidRecord.CHANGE_UNCACHED) != 0) {
371                         if (DEBUG_UID_OBSERVERS) {
372                             Slog.i(TAG_UID_OBSERVERS, "UID active uid=" + item.uid);
373                         }
374                         observer.onUidCachedChanged(item.uid, false);
375                     }
376                 }
377                 if ((change & UidRecord.CHANGE_GONE) != 0) {
378                     if ((reg.mWhich & ActivityManager.UID_OBSERVER_GONE) != 0) {
379                         if (DEBUG_UID_OBSERVERS) {
380                             Slog.i(TAG_UID_OBSERVERS, "UID gone uid=" + item.uid);
381                         }
382                         observer.onUidGone(item.uid, item.ephemeral);
383                     }
384                     if (reg.mLastProcStates != null) {
385                         reg.mLastProcStates.delete(item.uid);
386                     }
387                 } else {
388                     boolean doReport = false;
389                     if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
390                         doReport = true;
391                         if (reg.mCutpoint >= ActivityManager.MIN_PROCESS_STATE) {
392                             final int lastState = reg.mLastProcStates.get(item.uid,
393                                     ActivityManager.PROCESS_STATE_UNKNOWN);
394                             if (lastState != ActivityManager.PROCESS_STATE_UNKNOWN) {
395                                 final boolean lastAboveCut = lastState <= reg.mCutpoint;
396                                 final boolean newAboveCut = item.procState <= reg.mCutpoint;
397                                 doReport = lastAboveCut != newAboveCut;
398                             } else {
399                                 doReport = item.procState != PROCESS_STATE_NONEXISTENT;
400                             }
401                         }
402                     }
403                     if ((reg.mWhich & ActivityManager.UID_OBSERVER_CAPABILITY) != 0) {
404                         doReport |= (change & UidRecord.CHANGE_CAPABILITY) != 0;
405                     }
406                     if (doReport) {
407                         if (DEBUG_UID_OBSERVERS) {
408                             Slog.i(TAG_UID_OBSERVERS, "UID CHANGED uid=" + item.uid
409                                     + ": " + item.procState + ": " + item.capability);
410                         }
411                         if (reg.mLastProcStates != null) {
412                             reg.mLastProcStates.put(item.uid, item.procState);
413                         }
414                         observer.onUidStateChanged(item.uid, item.procState,
415                                 item.procStateSeq,
416                                 item.capability);
417                     }
418                     if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROC_OOM_ADJ) != 0
419                             && (change & UidRecord.CHANGE_PROCADJ) != 0) {
420                         observer.onUidProcAdjChanged(item.uid, item.procAdj);
421                     }
422                 }
423                 final int duration = (int) (SystemClock.uptimeMillis() - start);
424                 if (reg.mMaxDispatchTime < duration) {
425                     reg.mMaxDispatchTime = duration;
426                 }
427                 if (duration >= SLOW_UID_OBSERVER_THRESHOLD_MS) {
428                     reg.mSlowDispatchCount++;
429                 }
430             }
431         } catch (RemoteException e) {
432         }
433     }
434 
getValidateUidRecord(int uid)435     UidRecord getValidateUidRecord(int uid) {
436         return mValidateUids.get(uid);
437     }
438 
dump(@onNull PrintWriter pw, @Nullable String dumpPackage)439     void dump(@NonNull PrintWriter pw, @Nullable String dumpPackage) {
440         synchronized (mLock) {
441             final int count = mUidObservers.getRegisteredCallbackCount();
442             boolean printed = false;
443             for (int i = 0; i < count; i++) {
444                 final UidObserverRegistration reg = (UidObserverRegistration)
445                         mUidObservers.getRegisteredCallbackCookie(i);
446                 if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) {
447                     if (!printed) {
448                         pw.println("  mUidObservers:");
449                         printed = true;
450                     }
451                     reg.dump(pw, mUidObservers.getRegisteredCallbackItem(i));
452                 }
453             }
454 
455             if (dumpPackage == null) {
456                 pw.println();
457                 pw.print("  mUidChangeDispatchCount=");
458                 pw.print(mUidChangeDispatchCount);
459                 pw.println();
460                 pw.println("  Slow UID dispatches:");
461                 for (int i = 0; i < count; i++) {
462                     final UidObserverRegistration reg = (UidObserverRegistration)
463                             mUidObservers.getRegisteredCallbackCookie(i);
464                     pw.print("    ");
465                     pw.print(mUidObservers.getRegisteredCallbackItem(i).getClass().getTypeName());
466                     pw.print(": ");
467                     pw.print(reg.mSlowDispatchCount);
468                     pw.print(" / Max ");
469                     pw.print(reg.mMaxDispatchTime);
470                     pw.println("ms");
471                 }
472             }
473         }
474     }
475 
dumpDebug(@onNull ProtoOutputStream proto, @Nullable String dumpPackage)476     void dumpDebug(@NonNull ProtoOutputStream proto, @Nullable String dumpPackage) {
477         synchronized (mLock) {
478             final int count = mUidObservers.getRegisteredCallbackCount();
479             for (int i = 0; i < count; i++) {
480                 final UidObserverRegistration reg = (UidObserverRegistration)
481                         mUidObservers.getRegisteredCallbackCookie(i);
482                 if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) {
483                     reg.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.UID_OBSERVERS);
484                 }
485             }
486         }
487     }
488 
dumpValidateUids(@onNull PrintWriter pw, @Nullable String dumpPackage, int dumpAppId, @NonNull String header, boolean needSep)489     boolean dumpValidateUids(@NonNull PrintWriter pw, @Nullable String dumpPackage, int dumpAppId,
490             @NonNull String header, boolean needSep) {
491         return mValidateUids.dump(pw, dumpPackage, dumpAppId, header, needSep);
492     }
493 
dumpValidateUidsProto(@onNull ProtoOutputStream proto, @Nullable String dumpPackage, int dumpAppId, long fieldId)494     void dumpValidateUidsProto(@NonNull ProtoOutputStream proto, @Nullable String dumpPackage,
495             int dumpAppId, long fieldId) {
496         mValidateUids.dumpProto(proto, dumpPackage, dumpAppId, fieldId);
497     }
498 
499     static final class ChangeRecord {
500         public boolean isPending;
501         public int uid;
502         public int change;
503         public int procState;
504         public int procAdj;
505         public int capability;
506         public boolean ephemeral;
507         public long procStateSeq;
508 
copyTo(@onNull ChangeRecord changeRecord)509         void copyTo(@NonNull ChangeRecord changeRecord) {
510             changeRecord.isPending = isPending;
511             changeRecord.uid = uid;
512             changeRecord.change = change;
513             changeRecord.procState = procState;
514             changeRecord.procAdj = procAdj;
515             changeRecord.capability = capability;
516             changeRecord.ephemeral = ephemeral;
517             changeRecord.procStateSeq = procStateSeq;
518         }
519     }
520 
521     private static final class UidObserverRegistration {
522         private final int mUid;
523         private final String mPkg;
524         private final int mWhich;
525         private final int mCutpoint;
526         private final boolean mCanInteractAcrossUsers;
527         private final IBinder mToken;
528         private int[] mUids;
529 
530         /**
531          * Total # of callback calls that took more than {@link #SLOW_UID_OBSERVER_THRESHOLD_MS}.
532          * We show it in dumpsys.
533          */
534         int mSlowDispatchCount;
535 
536         /** Max time it took for each dispatch. */
537         int mMaxDispatchTime;
538 
539         final SparseIntArray mLastProcStates;
540 
541         // Please keep the enum lists in sync
542         private static final int[] ORIG_ENUMS = new int[]{
543                 ActivityManager.UID_OBSERVER_IDLE,
544                 ActivityManager.UID_OBSERVER_ACTIVE,
545                 ActivityManager.UID_OBSERVER_GONE,
546                 ActivityManager.UID_OBSERVER_PROCSTATE,
547                 ActivityManager.UID_OBSERVER_CAPABILITY,
548                 ActivityManager.UID_OBSERVER_PROC_OOM_ADJ,
549         };
550         private static final int[] PROTO_ENUMS = new int[]{
551                 ActivityManagerProto.UID_OBSERVER_FLAG_IDLE,
552                 ActivityManagerProto.UID_OBSERVER_FLAG_ACTIVE,
553                 ActivityManagerProto.UID_OBSERVER_FLAG_GONE,
554                 ActivityManagerProto.UID_OBSERVER_FLAG_PROCSTATE,
555                 ActivityManagerProto.UID_OBSERVER_FLAG_CAPABILITY,
556                 ActivityManagerProto.UID_OBSERVER_FLAG_PROC_OOM_ADJ,
557         };
558 
UidObserverRegistration(int uid, @NonNull String pkg, int which, int cutpoint, boolean canInteractAcrossUsers, @Nullable int[] uids, @NonNull IBinder token)559         UidObserverRegistration(int uid, @NonNull String pkg, int which, int cutpoint,
560                 boolean canInteractAcrossUsers, @Nullable int[] uids, @NonNull IBinder token) {
561             this.mUid = uid;
562             this.mPkg = pkg;
563             this.mWhich = which;
564             this.mCutpoint = cutpoint;
565             this.mCanInteractAcrossUsers = canInteractAcrossUsers;
566 
567             if (uids != null) {
568                 this.mUids = uids.clone();
569                 Arrays.sort(this.mUids);
570             } else {
571                 this.mUids = null;
572             }
573 
574             this.mToken = token;
575 
576             mLastProcStates = cutpoint >= ActivityManager.MIN_PROCESS_STATE
577                     ? new SparseIntArray() : null;
578         }
579 
isWatchingUid(int uid)580         boolean isWatchingUid(int uid) {
581             if (mUids == null) {
582                 return true;
583             }
584 
585             return Arrays.binarySearch(mUids, uid) >= 0;
586         }
587 
addUid(int uid)588         void addUid(int uid) {
589             if (mUids == null) {
590                 return;
591             }
592 
593             int[] temp = mUids;
594             mUids = new int[temp.length + 1];
595             boolean inserted = false;
596             for (int i = 0; i < temp.length; i++) {
597                 if (!inserted) {
598                     if (temp[i] < uid) {
599                         mUids[i] = temp[i];
600                     } else if (temp[i] == uid) {
601                         // Duplicate uid, no-op and fallback to the previous array
602                         mUids = temp;
603                         return;
604                     } else {
605                         mUids[i] = uid;
606                         mUids[i + 1] = temp[i];
607                         inserted = true;
608                     }
609                 } else {
610                     mUids[i + 1] = temp[i];
611                 }
612             }
613 
614             if (!inserted) {
615                 mUids[temp.length] = uid;
616             }
617         }
618 
removeUid(int uid)619         void removeUid(int uid) {
620             if (mUids == null || mUids.length == 0) {
621                 return;
622             }
623 
624             int[] temp = mUids;
625             mUids = new int[temp.length - 1];
626             boolean removed = false;
627             for (int i = 0; i < temp.length; i++) {
628                 if (!removed) {
629                     if (temp[i] == uid) {
630                         removed = true;
631                     } else if (i == temp.length - 1) {
632                         // Uid not found, no-op and fallback to the previous array
633                         mUids = temp;
634                         return;
635                     } else {
636                         mUids[i] = temp[i];
637                     }
638                 } else {
639                     mUids[i - 1] = temp[i];
640                 }
641             }
642         }
643 
getToken()644         IBinder getToken() {
645             return mToken;
646         }
647 
dump(@onNull PrintWriter pw, @NonNull IUidObserver observer)648         void dump(@NonNull PrintWriter pw, @NonNull IUidObserver observer) {
649             pw.print("    ");
650             UserHandle.formatUid(pw, mUid);
651             pw.print(" ");
652             pw.print(mPkg);
653             pw.print(" ");
654             pw.print(observer.getClass().getTypeName());
655             pw.print(":");
656             if ((mWhich & ActivityManager.UID_OBSERVER_IDLE) != 0) {
657                 pw.print(" IDLE");
658             }
659             if ((mWhich & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
660                 pw.print(" ACT");
661             }
662             if ((mWhich & ActivityManager.UID_OBSERVER_GONE) != 0) {
663                 pw.print(" GONE");
664             }
665             if ((mWhich & ActivityManager.UID_OBSERVER_CAPABILITY) != 0) {
666                 pw.print(" CAP");
667             }
668             if ((mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
669                 pw.print(" STATE");
670                 pw.print(" (cut=");
671                 pw.print(mCutpoint);
672                 pw.print(")");
673             }
674             pw.println();
675             if (mLastProcStates != null) {
676                 final int size = mLastProcStates.size();
677                 for (int j = 0; j < size; j++) {
678                     pw.print("      Last ");
679                     UserHandle.formatUid(pw, mLastProcStates.keyAt(j));
680                     pw.print(": ");
681                     pw.println(mLastProcStates.valueAt(j));
682                 }
683             }
684         }
685 
dumpDebug(@onNull ProtoOutputStream proto, long fieldId)686         void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId) {
687             final long token = proto.start(fieldId);
688             proto.write(UidObserverRegistrationProto.UID, mUid);
689             proto.write(UidObserverRegistrationProto.PACKAGE, mPkg);
690             ProtoUtils.writeBitWiseFlagsToProtoEnum(proto, UidObserverRegistrationProto.FLAGS,
691                     mWhich, ORIG_ENUMS, PROTO_ENUMS);
692             proto.write(UidObserverRegistrationProto.CUT_POINT, mCutpoint);
693             if (mLastProcStates != null) {
694                 final int size = mLastProcStates.size();
695                 for (int i = 0; i < size; i++) {
696                     final long pToken = proto.start(UidObserverRegistrationProto.LAST_PROC_STATES);
697                     proto.write(UidObserverRegistrationProto.ProcState.UID,
698                             mLastProcStates.keyAt(i));
699                     proto.write(UidObserverRegistrationProto.ProcState.STATE,
700                             mLastProcStates.valueAt(i));
701                     proto.end(pToken);
702                 }
703             }
704             proto.end(token);
705         }
706     }
707 }
708