• 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.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
19 
20 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
21 import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
22 
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.app.ActivityManager;
26 import android.app.ActivityManagerProto;
27 import android.app.IUidObserver;
28 import android.os.Handler;
29 import android.os.RemoteCallbackList;
30 import android.os.RemoteException;
31 import android.os.SystemClock;
32 import android.os.UserHandle;
33 import android.util.Slog;
34 import android.util.SparseIntArray;
35 import android.util.proto.ProtoOutputStream;
36 import android.util.proto.ProtoUtils;
37 
38 import com.android.internal.annotations.GuardedBy;
39 import com.android.internal.annotations.VisibleForTesting;
40 import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
41 
42 import java.io.PrintWriter;
43 import java.util.ArrayList;
44 
45 public class UidObserverController {
46     /** If a UID observer takes more than this long, send a WTF. */
47     private static final int SLOW_UID_OBSERVER_THRESHOLD_MS = 20;
48 
49     private final Handler mHandler;
50 
51     private final Object mLock = new Object();
52 
53     @GuardedBy("mLock")
54     final RemoteCallbackList<IUidObserver> mUidObservers = new RemoteCallbackList<>();
55 
56     @GuardedBy("mLock")
57     private final ArrayList<ChangeRecord> mPendingUidChanges = new ArrayList<>();
58     @GuardedBy("mLock")
59     private final ArrayList<ChangeRecord> mAvailUidChanges = new ArrayList<>();
60 
61     private ChangeRecord[] mActiveUidChanges = new ChangeRecord[5];
62 
63     /** Total # of UID change events dispatched, shown in dumpsys. */
64     @GuardedBy("mLock")
65     private int mUidChangeDispatchCount;
66 
67     private final Runnable mDispatchRunnable = this::dispatchUidsChanged;
68 
69     /**
70      * This is for verifying the UID report flow.
71      */
72     private static final boolean VALIDATE_UID_STATES = true;
73     private final ActiveUids mValidateUids;
74 
UidObserverController(@onNull Handler handler)75     UidObserverController(@NonNull Handler handler) {
76         mHandler = handler;
77         mValidateUids = new ActiveUids(null /* service */, false /* postChangesToAtm */);
78     }
79 
register(@onNull IUidObserver observer, int which, int cutpoint, @NonNull String callingPackage, int callingUid)80     void register(@NonNull IUidObserver observer, int which, int cutpoint,
81             @NonNull String callingPackage, int callingUid) {
82         synchronized (mLock) {
83             mUidObservers.register(observer, new UidObserverRegistration(callingUid,
84                     callingPackage, which, cutpoint));
85         }
86     }
87 
unregister(@onNull IUidObserver observer)88     void unregister(@NonNull IUidObserver observer) {
89         synchronized (mLock) {
90             mUidObservers.unregister(observer);
91         }
92     }
93 
enqueueUidChange(@ullable ChangeRecord currentRecord, int uid, int change, int procState, long procStateSeq, int capability, boolean ephemeral)94     int enqueueUidChange(@Nullable ChangeRecord currentRecord, int uid, int change, int procState,
95             long procStateSeq, int capability, boolean ephemeral) {
96         synchronized (mLock) {
97             if (mPendingUidChanges.size() == 0) {
98                 if (DEBUG_UID_OBSERVERS) {
99                     Slog.i(TAG_UID_OBSERVERS, "*** Enqueueing dispatch uid changed!");
100                 }
101                 mHandler.post(mDispatchRunnable);
102             }
103 
104             final ChangeRecord changeRecord = currentRecord != null
105                     ? currentRecord : getOrCreateChangeRecordLocked();
106             if (!changeRecord.isPending) {
107                 changeRecord.isPending = true;
108                 mPendingUidChanges.add(changeRecord);
109             } else {
110                 change = mergeWithPendingChange(change, changeRecord.change);
111             }
112 
113             changeRecord.uid = uid;
114             changeRecord.change = change;
115             changeRecord.procState = procState;
116             changeRecord.procStateSeq = procStateSeq;
117             changeRecord.capability = capability;
118             changeRecord.ephemeral = ephemeral;
119 
120             return changeRecord.change;
121         }
122     }
123 
getPendingUidChangesForTest()124     ArrayList<ChangeRecord> getPendingUidChangesForTest() {
125         return mPendingUidChanges;
126     }
127 
getValidateUidsForTest()128     ActiveUids getValidateUidsForTest() {
129         return mValidateUids;
130     }
131 
132     @VisibleForTesting
mergeWithPendingChange(int currentChange, int pendingChange)133     static int mergeWithPendingChange(int currentChange, int pendingChange) {
134         // If there is no change in idle or active state, then keep whatever was pending.
135         if ((currentChange & (UidRecord.CHANGE_IDLE | UidRecord.CHANGE_ACTIVE)) == 0) {
136             currentChange |= (pendingChange & (UidRecord.CHANGE_IDLE
137                     | UidRecord.CHANGE_ACTIVE));
138         }
139         // If there is no change in cached or uncached state, then keep whatever was pending.
140         if ((currentChange & (UidRecord.CHANGE_CACHED | UidRecord.CHANGE_UNCACHED)) == 0) {
141             currentChange |= (pendingChange & (UidRecord.CHANGE_CACHED
142                     | UidRecord.CHANGE_UNCACHED));
143         }
144         // If this is a report of the UID being gone, then we shouldn't keep any previous
145         // report of it being active or cached.  (That is, a gone uid is never active,
146         // and never cached.)
147         if ((currentChange & UidRecord.CHANGE_GONE) != 0) {
148             currentChange &= ~(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_CACHED);
149         }
150         if ((pendingChange & UidRecord.CHANGE_CAPABILITY) != 0) {
151             currentChange |= UidRecord.CHANGE_CAPABILITY;
152         }
153         return currentChange;
154     }
155 
156     @GuardedBy("mLock")
getOrCreateChangeRecordLocked()157     private ChangeRecord getOrCreateChangeRecordLocked() {
158         final ChangeRecord changeRecord;
159         final int size = mAvailUidChanges.size();
160         if (size > 0) {
161             changeRecord = mAvailUidChanges.remove(size - 1);
162             if (DEBUG_UID_OBSERVERS) {
163                 Slog.i(TAG_UID_OBSERVERS, "Retrieving available item: " + changeRecord);
164             }
165         } else {
166             changeRecord = new ChangeRecord();
167             if (DEBUG_UID_OBSERVERS) {
168                 Slog.i(TAG_UID_OBSERVERS, "Allocating new item: " + changeRecord);
169             }
170         }
171         return changeRecord;
172     }
173 
174     @VisibleForTesting
dispatchUidsChanged()175     void dispatchUidsChanged() {
176         final int numUidChanges;
177         synchronized (mLock) {
178             numUidChanges = mPendingUidChanges.size();
179             if (mActiveUidChanges.length < numUidChanges) {
180                 mActiveUidChanges = new ChangeRecord[numUidChanges];
181             }
182             for (int i = 0; i < numUidChanges; i++) {
183                 final ChangeRecord changeRecord = mPendingUidChanges.get(i);
184                 mActiveUidChanges[i] = getOrCreateChangeRecordLocked();
185                 changeRecord.copyTo(mActiveUidChanges[i]);
186                 changeRecord.isPending = false;
187             }
188             mPendingUidChanges.clear();
189             if (DEBUG_UID_OBSERVERS) {
190                 Slog.i(TAG_UID_OBSERVERS, "*** Delivering " + numUidChanges + " uid changes");
191             }
192             mUidChangeDispatchCount += numUidChanges;
193         }
194 
195         int i = mUidObservers.beginBroadcast();
196         while (i-- > 0) {
197             dispatchUidsChangedForObserver(mUidObservers.getBroadcastItem(i),
198                     (UidObserverRegistration) mUidObservers.getBroadcastCookie(i), numUidChanges);
199         }
200         mUidObservers.finishBroadcast();
201 
202         if (VALIDATE_UID_STATES && mUidObservers.getRegisteredCallbackCount() > 0) {
203             for (int j = 0; j < numUidChanges; ++j) {
204                 final ChangeRecord item = mActiveUidChanges[j];
205                 if ((item.change & UidRecord.CHANGE_GONE) != 0) {
206                     mValidateUids.remove(item.uid);
207                 } else {
208                     UidRecord validateUid = mValidateUids.get(item.uid);
209                     if (validateUid == null) {
210                         validateUid = new UidRecord(item.uid, null);
211                         mValidateUids.put(item.uid, validateUid);
212                     }
213                     if ((item.change & UidRecord.CHANGE_IDLE) != 0) {
214                         validateUid.setIdle(true);
215                     } else if ((item.change & UidRecord.CHANGE_ACTIVE) != 0) {
216                         validateUid.setIdle(false);
217                     }
218                     validateUid.setSetProcState(item.procState);
219                     validateUid.setCurProcState(item.procState);
220                     validateUid.setSetCapability(item.capability);
221                     validateUid.setCurCapability(item.capability);
222                     validateUid.lastDispatchedProcStateSeq = item.procStateSeq;
223                 }
224             }
225         }
226 
227         synchronized (mLock) {
228             for (int j = 0; j < numUidChanges; j++) {
229                 final ChangeRecord changeRecord = mActiveUidChanges[j];
230                 changeRecord.isPending = false;
231                 mAvailUidChanges.add(changeRecord);
232             }
233         }
234     }
235 
dispatchUidsChangedForObserver(@onNull IUidObserver observer, @NonNull UidObserverRegistration reg, int changesSize)236     private void dispatchUidsChangedForObserver(@NonNull IUidObserver observer,
237             @NonNull UidObserverRegistration reg, int changesSize) {
238         if (observer == null) {
239             return;
240         }
241         try {
242             for (int j = 0; j < changesSize; j++) {
243                 final ChangeRecord item = mActiveUidChanges[j];
244                 final int change = item.change;
245                 if (change == UidRecord.CHANGE_PROCSTATE
246                         && (reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) == 0) {
247                     // No-op common case: no significant change, the observer is not
248                     // interested in all proc state changes.
249                     continue;
250                 }
251                 final long start = SystemClock.uptimeMillis();
252                 if ((change & UidRecord.CHANGE_IDLE) != 0) {
253                     if ((reg.mWhich & ActivityManager.UID_OBSERVER_IDLE) != 0) {
254                         if (DEBUG_UID_OBSERVERS) {
255                             Slog.i(TAG_UID_OBSERVERS, "UID idle uid=" + item.uid);
256                         }
257                         observer.onUidIdle(item.uid, item.ephemeral);
258                     }
259                 } else if ((change & UidRecord.CHANGE_ACTIVE) != 0) {
260                     if ((reg.mWhich & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
261                         if (DEBUG_UID_OBSERVERS) {
262                             Slog.i(TAG_UID_OBSERVERS, "UID active uid=" + item.uid);
263                         }
264                         observer.onUidActive(item.uid);
265                     }
266                 }
267                 if ((reg.mWhich & ActivityManager.UID_OBSERVER_CACHED) != 0) {
268                     if ((change & UidRecord.CHANGE_CACHED) != 0) {
269                         if (DEBUG_UID_OBSERVERS) {
270                             Slog.i(TAG_UID_OBSERVERS, "UID cached uid=" + item.uid);
271                         }
272                         observer.onUidCachedChanged(item.uid, true);
273                     } else if ((change & UidRecord.CHANGE_UNCACHED) != 0) {
274                         if (DEBUG_UID_OBSERVERS) {
275                             Slog.i(TAG_UID_OBSERVERS, "UID active uid=" + item.uid);
276                         }
277                         observer.onUidCachedChanged(item.uid, false);
278                     }
279                 }
280                 if ((change & UidRecord.CHANGE_GONE) != 0) {
281                     if ((reg.mWhich & ActivityManager.UID_OBSERVER_GONE) != 0) {
282                         if (DEBUG_UID_OBSERVERS) {
283                             Slog.i(TAG_UID_OBSERVERS, "UID gone uid=" + item.uid);
284                         }
285                         observer.onUidGone(item.uid, item.ephemeral);
286                     }
287                     if (reg.mLastProcStates != null) {
288                         reg.mLastProcStates.delete(item.uid);
289                     }
290                 } else {
291                     boolean doReport = false;
292                     if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
293                         doReport = true;
294                         if (reg.mCutpoint >= ActivityManager.MIN_PROCESS_STATE) {
295                             final int lastState = reg.mLastProcStates.get(item.uid,
296                                     ActivityManager.PROCESS_STATE_UNKNOWN);
297                             if (lastState != ActivityManager.PROCESS_STATE_UNKNOWN) {
298                                 final boolean lastAboveCut = lastState <= reg.mCutpoint;
299                                 final boolean newAboveCut = item.procState <= reg.mCutpoint;
300                                 doReport = lastAboveCut != newAboveCut;
301                             } else {
302                                 doReport = item.procState != PROCESS_STATE_NONEXISTENT;
303                             }
304                         }
305                     }
306                     if ((reg.mWhich & ActivityManager.UID_OBSERVER_CAPABILITY) != 0) {
307                         doReport |= (change & UidRecord.CHANGE_CAPABILITY) != 0;
308                     }
309                     if (doReport) {
310                         if (DEBUG_UID_OBSERVERS) {
311                             Slog.i(TAG_UID_OBSERVERS, "UID CHANGED uid=" + item.uid
312                                     + ": " + item.procState + ": " + item.capability);
313                         }
314                         if (reg.mLastProcStates != null) {
315                             reg.mLastProcStates.put(item.uid, item.procState);
316                         }
317                         observer.onUidStateChanged(item.uid, item.procState,
318                                 item.procStateSeq, item.capability);
319                     }
320                 }
321                 final int duration = (int) (SystemClock.uptimeMillis() - start);
322                 if (reg.mMaxDispatchTime < duration) {
323                     reg.mMaxDispatchTime = duration;
324                 }
325                 if (duration >= SLOW_UID_OBSERVER_THRESHOLD_MS) {
326                     reg.mSlowDispatchCount++;
327                 }
328             }
329         } catch (RemoteException e) {
330         }
331     }
332 
getValidateUidRecord(int uid)333     UidRecord getValidateUidRecord(int uid) {
334         return mValidateUids.get(uid);
335     }
336 
dump(@onNull PrintWriter pw, @Nullable String dumpPackage)337     void dump(@NonNull PrintWriter pw, @Nullable String dumpPackage) {
338         synchronized (mLock) {
339             final int count = mUidObservers.getRegisteredCallbackCount();
340             boolean printed = false;
341             for (int i = 0; i < count; i++) {
342                 final UidObserverRegistration reg = (UidObserverRegistration)
343                         mUidObservers.getRegisteredCallbackCookie(i);
344                 if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) {
345                     if (!printed) {
346                         pw.println("  mUidObservers:");
347                         printed = true;
348                     }
349                     reg.dump(pw, mUidObservers.getRegisteredCallbackItem(i));
350                 }
351             }
352 
353             pw.println();
354             pw.print("  mUidChangeDispatchCount=");
355             pw.print(mUidChangeDispatchCount);
356             pw.println();
357             pw.println("  Slow UID dispatches:");
358             for (int i = 0; i < count; i++) {
359                 final UidObserverRegistration reg = (UidObserverRegistration)
360                         mUidObservers.getRegisteredCallbackCookie(i);
361                 pw.print("    ");
362                 pw.print(mUidObservers.getRegisteredCallbackItem(i).getClass().getTypeName());
363                 pw.print(": ");
364                 pw.print(reg.mSlowDispatchCount);
365                 pw.print(" / Max ");
366                 pw.print(reg.mMaxDispatchTime);
367                 pw.println("ms");
368             }
369         }
370     }
371 
dumpDebug(@onNull ProtoOutputStream proto, @Nullable String dumpPackage)372     void dumpDebug(@NonNull ProtoOutputStream proto, @Nullable String dumpPackage) {
373         synchronized (mLock) {
374             final int count = mUidObservers.getRegisteredCallbackCount();
375             for (int i = 0; i < count; i++) {
376                 final UidObserverRegistration reg = (UidObserverRegistration)
377                         mUidObservers.getRegisteredCallbackCookie(i);
378                 if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) {
379                     reg.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.UID_OBSERVERS);
380                 }
381             }
382         }
383     }
384 
dumpValidateUids(@onNull PrintWriter pw, @Nullable String dumpPackage, int dumpAppId, @NonNull String header, boolean needSep)385     boolean dumpValidateUids(@NonNull PrintWriter pw, @Nullable String dumpPackage, int dumpAppId,
386             @NonNull String header, boolean needSep) {
387         return mValidateUids.dump(pw, dumpPackage, dumpAppId, header, needSep);
388     }
389 
dumpValidateUidsProto(@onNull ProtoOutputStream proto, @Nullable String dumpPackage, int dumpAppId, long fieldId)390     void dumpValidateUidsProto(@NonNull ProtoOutputStream proto, @Nullable String dumpPackage,
391             int dumpAppId, long fieldId) {
392         mValidateUids.dumpProto(proto, dumpPackage, dumpAppId, fieldId);
393     }
394 
395     static final class ChangeRecord {
396         public boolean isPending;
397         public int uid;
398         public int change;
399         public int procState;
400         public int capability;
401         public boolean ephemeral;
402         public long procStateSeq;
403 
copyTo(@onNull ChangeRecord changeRecord)404         void copyTo(@NonNull ChangeRecord changeRecord) {
405             changeRecord.isPending = isPending;
406             changeRecord.uid = uid;
407             changeRecord.change = change;
408             changeRecord.procState = procState;
409             changeRecord.capability = capability;
410             changeRecord.ephemeral = ephemeral;
411             changeRecord.procStateSeq = procStateSeq;
412         }
413     }
414 
415     private static final class UidObserverRegistration {
416         private final int mUid;
417         private final String mPkg;
418         private final int mWhich;
419         private final int mCutpoint;
420 
421         /**
422          * Total # of callback calls that took more than {@link #SLOW_UID_OBSERVER_THRESHOLD_MS}.
423          * We show it in dumpsys.
424          */
425         int mSlowDispatchCount;
426 
427         /** Max time it took for each dispatch. */
428         int mMaxDispatchTime;
429 
430         final SparseIntArray mLastProcStates;
431 
432         // Please keep the enum lists in sync
433         private static final int[] ORIG_ENUMS = new int[]{
434                 ActivityManager.UID_OBSERVER_IDLE,
435                 ActivityManager.UID_OBSERVER_ACTIVE,
436                 ActivityManager.UID_OBSERVER_GONE,
437                 ActivityManager.UID_OBSERVER_PROCSTATE,
438                 ActivityManager.UID_OBSERVER_CAPABILITY,
439         };
440         private static final int[] PROTO_ENUMS = new int[]{
441                 ActivityManagerProto.UID_OBSERVER_FLAG_IDLE,
442                 ActivityManagerProto.UID_OBSERVER_FLAG_ACTIVE,
443                 ActivityManagerProto.UID_OBSERVER_FLAG_GONE,
444                 ActivityManagerProto.UID_OBSERVER_FLAG_PROCSTATE,
445                 ActivityManagerProto.UID_OBSERVER_FLAG_CAPABILITY,
446         };
447 
UidObserverRegistration(int uid, @NonNull String pkg, int which, int cutpoint)448         UidObserverRegistration(int uid, @NonNull String pkg, int which, int cutpoint) {
449             this.mUid = uid;
450             this.mPkg = pkg;
451             this.mWhich = which;
452             this.mCutpoint = cutpoint;
453             mLastProcStates = cutpoint >= ActivityManager.MIN_PROCESS_STATE
454                     ? new SparseIntArray() : null;
455         }
456 
dump(@onNull PrintWriter pw, @NonNull IUidObserver observer)457         void dump(@NonNull PrintWriter pw, @NonNull IUidObserver observer) {
458             pw.print("    ");
459             UserHandle.formatUid(pw, mUid);
460             pw.print(" ");
461             pw.print(mPkg);
462             pw.print(" ");
463             pw.print(observer.getClass().getTypeName());
464             pw.print(":");
465             if ((mWhich & ActivityManager.UID_OBSERVER_IDLE) != 0) {
466                 pw.print(" IDLE");
467             }
468             if ((mWhich & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
469                 pw.print(" ACT");
470             }
471             if ((mWhich & ActivityManager.UID_OBSERVER_GONE) != 0) {
472                 pw.print(" GONE");
473             }
474             if ((mWhich & ActivityManager.UID_OBSERVER_CAPABILITY) != 0) {
475                 pw.print(" CAP");
476             }
477             if ((mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
478                 pw.print(" STATE");
479                 pw.print(" (cut=");
480                 pw.print(mCutpoint);
481                 pw.print(")");
482             }
483             pw.println();
484             if (mLastProcStates != null) {
485                 final int size = mLastProcStates.size();
486                 for (int j = 0; j < size; j++) {
487                     pw.print("      Last ");
488                     UserHandle.formatUid(pw, mLastProcStates.keyAt(j));
489                     pw.print(": ");
490                     pw.println(mLastProcStates.valueAt(j));
491                 }
492             }
493         }
494 
dumpDebug(@onNull ProtoOutputStream proto, long fieldId)495         void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId) {
496             final long token = proto.start(fieldId);
497             proto.write(UidObserverRegistrationProto.UID, mUid);
498             proto.write(UidObserverRegistrationProto.PACKAGE, mPkg);
499             ProtoUtils.writeBitWiseFlagsToProtoEnum(proto, UidObserverRegistrationProto.FLAGS,
500                     mWhich, ORIG_ENUMS, PROTO_ENUMS);
501             proto.write(UidObserverRegistrationProto.CUT_POINT, mCutpoint);
502             if (mLastProcStates != null) {
503                 final int size = mLastProcStates.size();
504                 for (int i = 0; i < size; i++) {
505                     final long pToken = proto.start(UidObserverRegistrationProto.LAST_PROC_STATES);
506                     proto.write(UidObserverRegistrationProto.ProcState.UID,
507                             mLastProcStates.keyAt(i));
508                     proto.write(UidObserverRegistrationProto.ProcState.STATE,
509                             mLastProcStates.valueAt(i));
510                     proto.end(pToken);
511                 }
512             }
513             proto.end(token);
514         }
515     }
516 }
517