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