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