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