1 /* 2 * Copyright (C) 2022 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 17 package com.android.server.usage; 18 19 import android.annotation.ElapsedRealtimeLong; 20 import android.annotation.IntDef; 21 import android.annotation.IntRange; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.UserIdInt; 25 import android.app.ActivityManager.ProcessState; 26 import android.app.role.OnRoleHoldersChangedListener; 27 import android.app.role.RoleManager; 28 import android.app.usage.BroadcastResponseStats; 29 import android.content.Context; 30 import android.content.pm.PackageManager; 31 import android.os.Process; 32 import android.os.SystemClock; 33 import android.os.UserHandle; 34 import android.util.ArrayMap; 35 import android.util.ArraySet; 36 import android.util.LongArrayQueue; 37 import android.util.Slog; 38 import android.util.SparseArray; 39 40 import com.android.internal.annotations.GuardedBy; 41 import com.android.internal.os.BackgroundThread; 42 import com.android.internal.util.CollectionUtils; 43 import com.android.internal.util.IndentingPrintWriter; 44 45 import java.lang.annotation.Retention; 46 import java.lang.annotation.RetentionPolicy; 47 import java.util.ArrayList; 48 import java.util.List; 49 50 class BroadcastResponseStatsTracker { 51 static final String TAG = "ResponseStatsTracker"; 52 53 @Retention(RetentionPolicy.SOURCE) 54 @IntDef(prefix = {"NOTIFICATION_EVENT_TYPE_"}, value = { 55 NOTIFICATION_EVENT_TYPE_POSTED, 56 NOTIFICATION_EVENT_TYPE_UPDATED, 57 NOTIFICATION_EVENT_TYPE_CANCELLED 58 }) 59 public @interface NotificationEventType {} 60 61 static final int NOTIFICATION_EVENT_TYPE_POSTED = 0; 62 static final int NOTIFICATION_EVENT_TYPE_UPDATED = 1; 63 static final int NOTIFICATION_EVENT_TYPE_CANCELLED = 2; 64 65 private final Object mLock = new Object(); 66 67 /** 68 * Contains the mapping of user -> UserBroadcastEvents data. 69 */ 70 @GuardedBy("mLock") 71 private SparseArray<UserBroadcastEvents> mUserBroadcastEvents = new SparseArray<>(); 72 73 /** 74 * Contains the mapping of sourceUid -> {targetUser -> UserBroadcastResponseStats} data. 75 * Here sourceUid refers to the uid that sent a broadcast and targetUser is the user that the 76 * broadcast was directed to. 77 */ 78 @GuardedBy("mLock") 79 private SparseArray<SparseArray<UserBroadcastResponseStats>> mUserResponseStats = 80 new SparseArray<>(); 81 82 /** 83 * Cache of package names holding exempted roles. 84 * 85 * Contains the mapping of userId -> {roleName -> <packages>} data. 86 */ 87 // TODO: Use SparseArrayMap to simplify the logic. 88 @GuardedBy("mLock") 89 private SparseArray<ArrayMap<String, List<String>>> mExemptedRoleHoldersCache = 90 new SparseArray<>(); 91 private final OnRoleHoldersChangedListener mRoleHoldersChangedListener = 92 this::onRoleHoldersChanged; 93 94 private AppStandbyInternal mAppStandby; 95 private BroadcastResponseStatsLogger mLogger; 96 private RoleManager mRoleManager; 97 private final Context mContext; 98 BroadcastResponseStatsTracker(@onNull AppStandbyInternal appStandby, @NonNull Context context)99 BroadcastResponseStatsTracker(@NonNull AppStandbyInternal appStandby, 100 @NonNull Context context) { 101 mAppStandby = appStandby; 102 mContext = context; 103 mLogger = new BroadcastResponseStatsLogger(); 104 } 105 onSystemServicesReady(Context context)106 void onSystemServicesReady(Context context) { 107 mRoleManager = context.getSystemService(RoleManager.class); 108 mRoleManager.addOnRoleHoldersChangedListenerAsUser(BackgroundThread.getExecutor(), 109 mRoleHoldersChangedListener, UserHandle.ALL); 110 } 111 112 // TODO (206518114): Move all callbacks handling to a handler thread. reportBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage, UserHandle targetUser, long idForResponseEvent, @ElapsedRealtimeLong long timestampMs, @ProcessState int targetUidProcState)113 void reportBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage, 114 UserHandle targetUser, long idForResponseEvent, 115 @ElapsedRealtimeLong long timestampMs, @ProcessState int targetUidProcState) { 116 mLogger.logBroadcastDispatchEvent(sourceUid, targetPackage, targetUser, 117 idForResponseEvent, timestampMs, targetUidProcState); 118 if (targetUidProcState <= mAppStandby.getBroadcastResponseFgThresholdState()) { 119 // No need to track the broadcast response stats while the target app is 120 // in the foreground. 121 return; 122 } 123 if (doesPackageHoldExemptedRole(targetPackage, targetUser)) { 124 // Package holds an exempted role, so no need to track the broadcast response stats. 125 return; 126 } 127 if (doesPackageHoldExemptedPermission(targetPackage, targetUser)) { 128 // Package holds an exempted permission, so no need to track the broadcast response 129 // stats 130 return; 131 } 132 synchronized (mLock) { 133 final ArraySet<BroadcastEvent> broadcastEvents = 134 getOrCreateBroadcastEventsLocked(targetPackage, targetUser); 135 final BroadcastEvent broadcastEvent = getOrCreateBroadcastEvent(broadcastEvents, 136 sourceUid, targetPackage, targetUser.getIdentifier(), idForResponseEvent); 137 broadcastEvent.addTimestampMs(timestampMs); 138 139 // Delete any old broadcast event related data so that we don't keep accumulating them. 140 recordAndPruneOldBroadcastDispatchTimestamps(broadcastEvent); 141 } 142 } 143 reportNotificationPosted(@onNull String packageName, UserHandle user, @ElapsedRealtimeLong long timestampMs)144 void reportNotificationPosted(@NonNull String packageName, UserHandle user, 145 @ElapsedRealtimeLong long timestampMs) { 146 reportNotificationEvent(NOTIFICATION_EVENT_TYPE_POSTED, packageName, user, timestampMs); 147 } 148 reportNotificationUpdated(@onNull String packageName, UserHandle user, @ElapsedRealtimeLong long timestampMs)149 void reportNotificationUpdated(@NonNull String packageName, UserHandle user, 150 @ElapsedRealtimeLong long timestampMs) { 151 reportNotificationEvent(NOTIFICATION_EVENT_TYPE_UPDATED, packageName, user, timestampMs); 152 153 } 154 reportNotificationCancelled(@onNull String packageName, UserHandle user, @ElapsedRealtimeLong long timestampMs)155 void reportNotificationCancelled(@NonNull String packageName, UserHandle user, 156 @ElapsedRealtimeLong long timestampMs) { 157 reportNotificationEvent(NOTIFICATION_EVENT_TYPE_CANCELLED, packageName, user, timestampMs); 158 } 159 reportNotificationEvent(@otificationEventType int event, @NonNull String packageName, UserHandle user, @ElapsedRealtimeLong long timestampMs)160 private void reportNotificationEvent(@NotificationEventType int event, 161 @NonNull String packageName, UserHandle user, @ElapsedRealtimeLong long timestampMs) { 162 mLogger.logNotificationEvent(event, packageName, user, timestampMs); 163 synchronized (mLock) { 164 final ArraySet<BroadcastEvent> broadcastEvents = 165 getBroadcastEventsLocked(packageName, user); 166 if (broadcastEvents == null) { 167 return; 168 } 169 final long broadcastResponseWindowDurationMs = 170 mAppStandby.getBroadcastResponseWindowDurationMs(); 171 final long broadcastsSessionWithResponseDurationMs = 172 mAppStandby.getBroadcastSessionsWithResponseDurationMs(); 173 final boolean recordAllBroadcastsSessionsWithinResponseWindow = 174 mAppStandby.shouldNoteResponseEventForAllBroadcastSessions(); 175 for (int i = broadcastEvents.size() - 1; i >= 0; --i) { 176 final BroadcastEvent broadcastEvent = broadcastEvents.valueAt(i); 177 recordAndPruneOldBroadcastDispatchTimestamps(broadcastEvent); 178 179 final LongArrayQueue dispatchTimestampsMs = broadcastEvent.getTimestampsMs(); 180 long broadcastsSessionEndTimestampMs = 0; 181 // We only need to look at the broadcast events that occurred before 182 // this notification related event. 183 while (dispatchTimestampsMs.size() > 0 184 && dispatchTimestampsMs.peekFirst() <= timestampMs) { 185 final long dispatchTimestampMs = dispatchTimestampsMs.peekFirst(); 186 final long elapsedDurationMs = timestampMs - dispatchTimestampMs; 187 // Only increment the counts if the broadcast was sent not too long ago, as 188 // decided by 'broadcastResponseWindowDurationMs' and is part of a new session. 189 // That is, it occurred 'broadcastsSessionWithResponseDurationMs' after the 190 // previously handled broadcast event which is represented by 191 // 'broadcastsSessionEndTimestampMs'. 192 if (elapsedDurationMs <= broadcastResponseWindowDurationMs 193 && dispatchTimestampMs >= broadcastsSessionEndTimestampMs) { 194 if (broadcastsSessionEndTimestampMs != 0 195 && !recordAllBroadcastsSessionsWithinResponseWindow) { 196 break; 197 } 198 final BroadcastResponseStats responseStats = 199 getOrCreateBroadcastResponseStats(broadcastEvent); 200 responseStats.incrementBroadcastsDispatchedCount(1); 201 broadcastsSessionEndTimestampMs = dispatchTimestampMs 202 + broadcastsSessionWithResponseDurationMs; 203 switch (event) { 204 case NOTIFICATION_EVENT_TYPE_POSTED: 205 responseStats.incrementNotificationsPostedCount(1); 206 break; 207 case NOTIFICATION_EVENT_TYPE_UPDATED: 208 responseStats.incrementNotificationsUpdatedCount(1); 209 break; 210 case NOTIFICATION_EVENT_TYPE_CANCELLED: 211 responseStats.incrementNotificationsCancelledCount(1); 212 break; 213 default: 214 Slog.wtf(TAG, "Unknown event: " + event); 215 } 216 } 217 dispatchTimestampsMs.removeFirst(); 218 } 219 if (dispatchTimestampsMs.size() == 0) { 220 broadcastEvents.removeAt(i); 221 } 222 } 223 } 224 } 225 226 @GuardedBy("mLock") recordAndPruneOldBroadcastDispatchTimestamps(BroadcastEvent broadcastEvent)227 private void recordAndPruneOldBroadcastDispatchTimestamps(BroadcastEvent broadcastEvent) { 228 final LongArrayQueue timestampsMs = broadcastEvent.getTimestampsMs(); 229 final long broadcastResponseWindowDurationMs = 230 mAppStandby.getBroadcastResponseWindowDurationMs(); 231 final long broadcastsSessionDurationMs = 232 mAppStandby.getBroadcastSessionsDurationMs(); 233 final long nowElapsedMs = SystemClock.elapsedRealtime(); 234 long broadcastsSessionEndTimestampMs = 0; 235 while (timestampsMs.size() > 0 236 && timestampsMs.peekFirst() < (nowElapsedMs - broadcastResponseWindowDurationMs)) { 237 final long eventTimestampMs = timestampsMs.peekFirst(); 238 if (eventTimestampMs >= broadcastsSessionEndTimestampMs) { 239 final BroadcastResponseStats responseStats = 240 getOrCreateBroadcastResponseStats(broadcastEvent); 241 responseStats.incrementBroadcastsDispatchedCount(1); 242 broadcastsSessionEndTimestampMs = eventTimestampMs + broadcastsSessionDurationMs; 243 } 244 timestampsMs.removeFirst(); 245 } 246 } 247 queryBroadcastResponseStats(int callingUid, @Nullable String packageName, @IntRange(from = 0) long id, @UserIdInt int userId)248 @NonNull List<BroadcastResponseStats> queryBroadcastResponseStats(int callingUid, 249 @Nullable String packageName, @IntRange(from = 0) long id, @UserIdInt int userId) { 250 final List<BroadcastResponseStats> broadcastResponseStatsList = new ArrayList<>(); 251 synchronized (mLock) { 252 final SparseArray<UserBroadcastResponseStats> responseStatsForCaller = 253 mUserResponseStats.get(callingUid); 254 if (responseStatsForCaller == null) { 255 return broadcastResponseStatsList; 256 } 257 final UserBroadcastResponseStats responseStatsForUser = 258 responseStatsForCaller.get(userId); 259 if (responseStatsForUser == null) { 260 return broadcastResponseStatsList; 261 } 262 responseStatsForUser.populateAllBroadcastResponseStats( 263 broadcastResponseStatsList, packageName, id); 264 } 265 return broadcastResponseStatsList; 266 } 267 clearBroadcastResponseStats(int callingUid, @Nullable String packageName, long id, @UserIdInt int userId)268 void clearBroadcastResponseStats(int callingUid, @Nullable String packageName, long id, 269 @UserIdInt int userId) { 270 synchronized (mLock) { 271 final SparseArray<UserBroadcastResponseStats> responseStatsForCaller = 272 mUserResponseStats.get(callingUid); 273 if (responseStatsForCaller == null) { 274 return; 275 } 276 final UserBroadcastResponseStats responseStatsForUser = 277 responseStatsForCaller.get(userId); 278 if (responseStatsForUser == null) { 279 return; 280 } 281 responseStatsForUser.clearBroadcastResponseStats(packageName, id); 282 } 283 } 284 clearBroadcastEvents(int callingUid, @UserIdInt int userId)285 void clearBroadcastEvents(int callingUid, @UserIdInt int userId) { 286 synchronized (mLock) { 287 final UserBroadcastEvents userBroadcastEvents = mUserBroadcastEvents.get(userId); 288 if (userBroadcastEvents == null) { 289 return; 290 } 291 userBroadcastEvents.clear(callingUid); 292 } 293 } 294 isPackageExemptedFromBroadcastResponseStats(@onNull String packageName, @NonNull UserHandle user)295 boolean isPackageExemptedFromBroadcastResponseStats(@NonNull String packageName, 296 @NonNull UserHandle user) { 297 synchronized (mLock) { 298 if (doesPackageHoldExemptedPermission(packageName, user)) { 299 return true; 300 } 301 if (doesPackageHoldExemptedRole(packageName, user)) { 302 return true; 303 } 304 return false; 305 } 306 } 307 doesPackageHoldExemptedRole(@onNull String packageName, @NonNull UserHandle user)308 boolean doesPackageHoldExemptedRole(@NonNull String packageName, @NonNull UserHandle user) { 309 final List<String> exemptedRoles = mAppStandby.getBroadcastResponseExemptedRoles(); 310 synchronized (mLock) { 311 for (int i = exemptedRoles.size() - 1; i >= 0; --i) { 312 final String roleName = exemptedRoles.get(i); 313 final List<String> roleHolders = getRoleHoldersLocked(roleName, user); 314 if (CollectionUtils.contains(roleHolders, packageName)) { 315 return true; 316 } 317 } 318 } 319 return false; 320 } 321 doesPackageHoldExemptedPermission(@onNull String packageName, @NonNull UserHandle user)322 boolean doesPackageHoldExemptedPermission(@NonNull String packageName, 323 @NonNull UserHandle user) { 324 int uid; 325 try { 326 uid = mContext.getPackageManager().getPackageUidAsUser( 327 packageName, user.getIdentifier()); 328 } catch (PackageManager.NameNotFoundException e) { 329 return false; 330 } 331 final List<String> exemptedPermissions = 332 mAppStandby.getBroadcastResponseExemptedPermissions(); 333 for (int i = exemptedPermissions.size() - 1; i >= 0; --i) { 334 final String permissionName = exemptedPermissions.get(i); 335 if (mContext.checkPermission(permissionName, Process.INVALID_PID, uid) 336 == PackageManager.PERMISSION_GRANTED) { 337 return true; 338 } 339 } 340 return false; 341 } 342 343 @GuardedBy("mLock") 344 @Nullable getRoleHoldersLocked(@onNull String roleName, @NonNull UserHandle user)345 private List<String> getRoleHoldersLocked(@NonNull String roleName, @NonNull UserHandle user) { 346 ArrayMap<String, List<String>> roleHoldersForUser = mExemptedRoleHoldersCache.get( 347 user.getIdentifier()); 348 if (roleHoldersForUser == null) { 349 roleHoldersForUser = new ArrayMap<>(); 350 mExemptedRoleHoldersCache.put(user.getIdentifier(), roleHoldersForUser); 351 } 352 List<String> roleHolders = roleHoldersForUser.get(roleName); 353 if (roleHolders == null && mRoleManager != null) { 354 roleHolders = mRoleManager.getRoleHoldersAsUser(roleName, user); 355 roleHoldersForUser.put(roleName, roleHolders); 356 } 357 return roleHolders; 358 } 359 onRoleHoldersChanged(@onNull String roleName, @NonNull UserHandle user)360 private void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) { 361 synchronized (mLock) { 362 final ArrayMap<String, List<String>> roleHoldersForUser = 363 mExemptedRoleHoldersCache.get(user.getIdentifier()); 364 if (roleHoldersForUser == null) { 365 return; 366 } 367 roleHoldersForUser.remove(roleName); 368 } 369 } 370 onUserRemoved(@serIdInt int userId)371 void onUserRemoved(@UserIdInt int userId) { 372 synchronized (mLock) { 373 mUserBroadcastEvents.remove(userId); 374 375 for (int i = mUserResponseStats.size() - 1; i >= 0; --i) { 376 mUserResponseStats.valueAt(i).remove(userId); 377 } 378 mExemptedRoleHoldersCache.remove(userId); 379 } 380 } 381 onPackageRemoved(@onNull String packageName, @UserIdInt int userId)382 void onPackageRemoved(@NonNull String packageName, @UserIdInt int userId) { 383 synchronized (mLock) { 384 final UserBroadcastEvents userBroadcastEvents = mUserBroadcastEvents.get(userId); 385 if (userBroadcastEvents != null) { 386 userBroadcastEvents.onPackageRemoved(packageName); 387 } 388 389 for (int i = mUserResponseStats.size() - 1; i >= 0; --i) { 390 final UserBroadcastResponseStats userResponseStats = 391 mUserResponseStats.valueAt(i).get(userId); 392 if (userResponseStats != null) { 393 userResponseStats.onPackageRemoved(packageName); 394 } 395 } 396 } 397 } 398 onUidRemoved(int uid)399 void onUidRemoved(int uid) { 400 synchronized (mLock) { 401 for (int i = mUserBroadcastEvents.size() - 1; i >= 0; --i) { 402 mUserBroadcastEvents.valueAt(i).onUidRemoved(uid); 403 } 404 405 mUserResponseStats.remove(uid); 406 } 407 } 408 409 @GuardedBy("mLock") 410 @Nullable getBroadcastEventsLocked( @onNull String packageName, UserHandle user)411 private ArraySet<BroadcastEvent> getBroadcastEventsLocked( 412 @NonNull String packageName, UserHandle user) { 413 final UserBroadcastEvents userBroadcastEvents = mUserBroadcastEvents.get( 414 user.getIdentifier()); 415 if (userBroadcastEvents == null) { 416 return null; 417 } 418 return userBroadcastEvents.getBroadcastEvents(packageName); 419 } 420 421 @GuardedBy("mLock") 422 @NonNull getOrCreateBroadcastEventsLocked( @onNull String packageName, UserHandle user)423 private ArraySet<BroadcastEvent> getOrCreateBroadcastEventsLocked( 424 @NonNull String packageName, UserHandle user) { 425 UserBroadcastEvents userBroadcastEvents = mUserBroadcastEvents.get(user.getIdentifier()); 426 if (userBroadcastEvents == null) { 427 userBroadcastEvents = new UserBroadcastEvents(); 428 mUserBroadcastEvents.put(user.getIdentifier(), userBroadcastEvents); 429 } 430 return userBroadcastEvents.getOrCreateBroadcastEvents(packageName); 431 } 432 433 @GuardedBy("mLock") 434 @Nullable getBroadcastResponseStats( @ullable SparseArray<UserBroadcastResponseStats> responseStatsForUid, @NonNull BroadcastEvent broadcastEvent)435 private BroadcastResponseStats getBroadcastResponseStats( 436 @Nullable SparseArray<UserBroadcastResponseStats> responseStatsForUid, 437 @NonNull BroadcastEvent broadcastEvent) { 438 if (responseStatsForUid == null) { 439 return null; 440 } 441 final UserBroadcastResponseStats userResponseStats = responseStatsForUid.get( 442 broadcastEvent.getTargetUserId()); 443 if (userResponseStats == null) { 444 return null; 445 } 446 return userResponseStats.getBroadcastResponseStats(broadcastEvent); 447 } 448 449 @GuardedBy("mLock") 450 @NonNull getOrCreateBroadcastResponseStats( @onNull BroadcastEvent broadcastEvent)451 private BroadcastResponseStats getOrCreateBroadcastResponseStats( 452 @NonNull BroadcastEvent broadcastEvent) { 453 final int sourceUid = broadcastEvent.getSourceUid(); 454 SparseArray<UserBroadcastResponseStats> userResponseStatsForUid = 455 mUserResponseStats.get(sourceUid); 456 if (userResponseStatsForUid == null) { 457 userResponseStatsForUid = new SparseArray<>(); 458 mUserResponseStats.put(sourceUid, userResponseStatsForUid); 459 } 460 UserBroadcastResponseStats userResponseStats = userResponseStatsForUid.get( 461 broadcastEvent.getTargetUserId()); 462 if (userResponseStats == null) { 463 userResponseStats = new UserBroadcastResponseStats(); 464 userResponseStatsForUid.put(broadcastEvent.getTargetUserId(), userResponseStats); 465 } 466 return userResponseStats.getOrCreateBroadcastResponseStats(broadcastEvent); 467 } 468 getOrCreateBroadcastEvent( ArraySet<BroadcastEvent> broadcastEvents, int sourceUid, String targetPackage, int targetUserId, long idForResponseEvent)469 private static BroadcastEvent getOrCreateBroadcastEvent( 470 ArraySet<BroadcastEvent> broadcastEvents, 471 int sourceUid, String targetPackage, int targetUserId, long idForResponseEvent) { 472 final BroadcastEvent broadcastEvent = new BroadcastEvent( 473 sourceUid, targetPackage, targetUserId, idForResponseEvent); 474 final int index = broadcastEvents.indexOf(broadcastEvent); 475 if (index >= 0) { 476 return broadcastEvents.valueAt(index); 477 } else { 478 broadcastEvents.add(broadcastEvent); 479 return broadcastEvent; 480 } 481 } 482 dump(@onNull IndentingPrintWriter ipw)483 void dump(@NonNull IndentingPrintWriter ipw) { 484 ipw.println("Broadcast response stats:"); 485 ipw.increaseIndent(); 486 487 synchronized (mLock) { 488 dumpBroadcastEventsLocked(ipw); 489 ipw.println(); 490 dumpResponseStatsLocked(ipw); 491 ipw.println(); 492 dumpRoleHoldersLocked(ipw); 493 ipw.println(); 494 mLogger.dumpLogs(ipw); 495 } 496 497 ipw.decreaseIndent(); 498 } 499 500 @GuardedBy("mLock") dumpBroadcastEventsLocked(@onNull IndentingPrintWriter ipw)501 private void dumpBroadcastEventsLocked(@NonNull IndentingPrintWriter ipw) { 502 ipw.println("Broadcast events:"); 503 ipw.increaseIndent(); 504 for (int i = 0; i < mUserBroadcastEvents.size(); ++i) { 505 final int userId = mUserBroadcastEvents.keyAt(i); 506 final UserBroadcastEvents userBroadcastEvents = mUserBroadcastEvents.valueAt(i); 507 ipw.println("User " + userId + ":"); 508 ipw.increaseIndent(); 509 userBroadcastEvents.dump(ipw); 510 ipw.decreaseIndent(); 511 } 512 ipw.decreaseIndent(); 513 } 514 515 @GuardedBy("mLock") dumpResponseStatsLocked(@onNull IndentingPrintWriter ipw)516 private void dumpResponseStatsLocked(@NonNull IndentingPrintWriter ipw) { 517 ipw.println("Response stats:"); 518 ipw.increaseIndent(); 519 for (int i = 0; i < mUserResponseStats.size(); ++i) { 520 final int sourceUid = mUserResponseStats.keyAt(i); 521 final SparseArray<UserBroadcastResponseStats> userBroadcastResponseStats = 522 mUserResponseStats.valueAt(i); 523 ipw.println("Uid " + sourceUid + ":"); 524 ipw.increaseIndent(); 525 for (int j = 0; j < userBroadcastResponseStats.size(); ++j) { 526 final int userId = userBroadcastResponseStats.keyAt(j); 527 final UserBroadcastResponseStats broadcastResponseStats = 528 userBroadcastResponseStats.valueAt(j); 529 ipw.println("User " + userId + ":"); 530 ipw.increaseIndent(); 531 broadcastResponseStats.dump(ipw); 532 ipw.decreaseIndent(); 533 } 534 ipw.decreaseIndent(); 535 } 536 ipw.decreaseIndent(); 537 } 538 539 @GuardedBy("mLock") dumpRoleHoldersLocked(@onNull IndentingPrintWriter ipw)540 private void dumpRoleHoldersLocked(@NonNull IndentingPrintWriter ipw) { 541 ipw.println("Role holders:"); 542 ipw.increaseIndent(); 543 for (int userIdx = 0; userIdx < mExemptedRoleHoldersCache.size(); ++userIdx) { 544 final int userId = mExemptedRoleHoldersCache.keyAt(userIdx); 545 final ArrayMap<String, List<String>> roleHoldersForUser = 546 mExemptedRoleHoldersCache.valueAt(userIdx); 547 ipw.println("User " + userId + ":"); 548 ipw.increaseIndent(); 549 for (int roleIdx = 0; roleIdx < roleHoldersForUser.size(); ++roleIdx) { 550 final String roleName = roleHoldersForUser.keyAt(roleIdx); 551 final List<String> holders = roleHoldersForUser.valueAt(roleIdx); 552 ipw.print(roleName + ": "); 553 for (int holderIdx = 0; holderIdx < holders.size(); ++holderIdx) { 554 if (holderIdx > 0) { 555 ipw.print(", "); 556 } 557 ipw.print(holders.get(holderIdx)); 558 } 559 ipw.println(); 560 } 561 ipw.decreaseIndent(); 562 } 563 ipw.decreaseIndent(); 564 } 565 } 566 567