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