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.appop; 18 19 import static android.app.AppOpsManager.MODE_ALLOWED; 20 import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_RESUMED; 21 import static android.app.AppOpsManager.makeKey; 22 23 import android.annotation.IntRange; 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.app.AppOpsManager; 27 import android.os.IBinder; 28 import android.os.Process; 29 import android.os.RemoteException; 30 import android.os.SystemClock; 31 import android.util.ArrayMap; 32 import android.util.LongSparseArray; 33 import android.util.Pools; 34 import android.util.Slog; 35 36 import com.android.internal.util.function.pooled.PooledLambda; 37 38 import java.util.ArrayList; 39 import java.util.List; 40 import java.util.NoSuchElementException; 41 42 final class AttributedOp { 43 private final @NonNull AppOpsService mAppOpsService; 44 public final @Nullable String tag; 45 public final @NonNull AppOpsService.Op parent; 46 47 /** 48 * Last successful accesses (noteOp + finished startOp) for each uidState/opFlag combination 49 * 50 * <p>Key is {@link AppOpsManager#makeKey} 51 */ 52 // TODO(b/248108338) 53 // @GuardedBy("mAppOpsService") 54 private @Nullable LongSparseArray<AppOpsManager.NoteOpEvent> mAccessEvents; 55 56 /** 57 * Last rejected accesses for each uidState/opFlag combination 58 * 59 * <p>Key is {@link AppOpsManager#makeKey} 60 */ 61 // TODO(b/248108338) 62 // @GuardedBy("mAppOpsService") 63 private @Nullable LongSparseArray<AppOpsManager.NoteOpEvent> mRejectEvents; 64 65 /** 66 * Currently in progress startOp events 67 * 68 * <p>Key is clientId 69 */ 70 // TODO(b/248108338) 71 // @GuardedBy("mAppOpsService") 72 @Nullable ArrayMap<IBinder, InProgressStartOpEvent> mInProgressEvents; 73 74 /** 75 * Currently paused startOp events 76 * 77 * <p>Key is clientId 78 */ 79 // TODO(b/248108338) 80 // @GuardedBy("mAppOpsService") 81 @Nullable ArrayMap<IBinder, InProgressStartOpEvent> mPausedInProgressEvents; 82 AttributedOp(@onNull AppOpsService appOpsService, @Nullable String tag, @NonNull AppOpsService.Op parent)83 AttributedOp(@NonNull AppOpsService appOpsService, @Nullable String tag, 84 @NonNull AppOpsService.Op parent) { 85 mAppOpsService = appOpsService; 86 this.tag = tag; 87 this.parent = parent; 88 } 89 90 /** 91 * Update state when noteOp was rejected or startOp->finishOp event finished 92 * 93 * @param proxyUid The uid of the proxy 94 * @param proxyPackageName The package name of the proxy 95 * @param proxyAttributionTag the attributionTag in the proxies package 96 * @param uidState UID state of the app noteOp/startOp was called for 97 * @param flags OpFlags of the call 98 */ accessed(int proxyUid, @Nullable String proxyPackageName, @Nullable String proxyAttributionTag, @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags)99 public void accessed(int proxyUid, @Nullable String proxyPackageName, 100 @Nullable String proxyAttributionTag, @AppOpsManager.UidState int uidState, 101 @AppOpsManager.OpFlags int flags) { 102 long accessTime = System.currentTimeMillis(); 103 accessed(accessTime, -1, proxyUid, proxyPackageName, 104 proxyAttributionTag, uidState, flags); 105 106 mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, 107 parent.packageName, tag, uidState, flags, accessTime, 108 AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE); 109 } 110 111 /** 112 * Add an access that was previously collected. 113 * 114 * @param noteTime The time of the event 115 * @param duration The duration of the event 116 * @param proxyUid The uid of the proxy 117 * @param proxyPackageName The package name of the proxy 118 * @param proxyAttributionTag the attributionTag in the proxies package 119 * @param uidState UID state of the app noteOp/startOp was called for 120 * @param flags OpFlags of the call 121 */ 122 @SuppressWarnings("GuardedBy") // Lock is held on mAppOpsService accessed(long noteTime, long duration, int proxyUid, @Nullable String proxyPackageName, @Nullable String proxyAttributionTag, @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags)123 public void accessed(long noteTime, long duration, int proxyUid, 124 @Nullable String proxyPackageName, @Nullable String proxyAttributionTag, 125 @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags) { 126 long key = makeKey(uidState, flags); 127 128 if (mAccessEvents == null) { 129 mAccessEvents = new LongSparseArray<>(1); 130 } 131 132 AppOpsManager.OpEventProxyInfo proxyInfo = null; 133 if (proxyUid != Process.INVALID_UID) { 134 proxyInfo = mAppOpsService.mOpEventProxyInfoPool.acquire(proxyUid, proxyPackageName, 135 proxyAttributionTag); 136 } 137 138 AppOpsManager.NoteOpEvent existingEvent = mAccessEvents.get(key); 139 if (existingEvent != null) { 140 existingEvent.reinit(noteTime, duration, proxyInfo, 141 mAppOpsService.mOpEventProxyInfoPool); 142 } else { 143 mAccessEvents.put(key, new AppOpsManager.NoteOpEvent(noteTime, duration, proxyInfo)); 144 } 145 } 146 147 /** 148 * Update state when noteOp/startOp was rejected. 149 * 150 * @param uidState UID state of the app noteOp is called for 151 * @param flags OpFlags of the call 152 */ rejected(@ppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags)153 public void rejected(@AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags) { 154 rejected(System.currentTimeMillis(), uidState, flags); 155 156 mAppOpsService.mHistoricalRegistry.incrementOpRejected(parent.op, parent.uid, 157 parent.packageName, tag, uidState, flags); 158 } 159 160 /** 161 * Add an rejection that was previously collected 162 * 163 * @param noteTime The time of the event 164 * @param uidState UID state of the app noteOp/startOp was called for 165 * @param flags OpFlags of the call 166 */ 167 @SuppressWarnings("GuardedBy") // Lock is held on mAppOpsService rejected(long noteTime, @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags)168 public void rejected(long noteTime, @AppOpsManager.UidState int uidState, 169 @AppOpsManager.OpFlags int flags) { 170 long key = makeKey(uidState, flags); 171 172 if (mRejectEvents == null) { 173 mRejectEvents = new LongSparseArray<>(1); 174 } 175 176 // We do not collect proxy information for rejections yet 177 AppOpsManager.NoteOpEvent existingEvent = mRejectEvents.get(key); 178 if (existingEvent != null) { 179 existingEvent.reinit(noteTime, -1, null, mAppOpsService.mOpEventProxyInfoPool); 180 } else { 181 mRejectEvents.put(key, new AppOpsManager.NoteOpEvent(noteTime, -1, null)); 182 } 183 } 184 185 /** 186 * Update state when start was called 187 * 188 * @param clientId Id of the startOp caller 189 * @param proxyUid The UID of the proxy app 190 * @param proxyPackageName The package name of the proxy app 191 * @param proxyAttributionTag The attribution tag of the proxy app 192 * @param uidState UID state of the app startOp is called for 193 * @param flags The proxy flags 194 * @param attributionFlags The attribution flags associated with this operation. 195 * @param attributionChainId The if of the attribution chain this operations is a part of. 196 */ started(@onNull IBinder clientId, int proxyUid, @Nullable String proxyPackageName, @Nullable String proxyAttributionTag, @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags, @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId)197 public void started(@NonNull IBinder clientId, int proxyUid, 198 @Nullable String proxyPackageName, @Nullable String proxyAttributionTag, 199 @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags, 200 @AppOpsManager.AttributionFlags 201 int attributionFlags, int attributionChainId) throws RemoteException { 202 started(clientId, proxyUid, proxyPackageName, proxyAttributionTag, 203 uidState, flags, /*triggerCallbackIfNeeded*/ true, attributionFlags, 204 attributionChainId); 205 } 206 started(@onNull IBinder clientId, int proxyUid, @Nullable String proxyPackageName, @Nullable String proxyAttributionTag, @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags, boolean triggerCallbackIfNeeded, @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId)207 private void started(@NonNull IBinder clientId, int proxyUid, 208 @Nullable String proxyPackageName, @Nullable String proxyAttributionTag, 209 @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags, 210 boolean triggerCallbackIfNeeded, @AppOpsManager.AttributionFlags int attributionFlags, 211 int attributionChainId) throws RemoteException { 212 startedOrPaused(clientId, proxyUid, proxyPackageName, 213 proxyAttributionTag, uidState, flags, triggerCallbackIfNeeded, 214 /*triggerCallbackIfNeeded*/ true, attributionFlags, attributionChainId); 215 } 216 217 @SuppressWarnings("GuardedBy") // Lock is held on mAppOpsService startedOrPaused(@onNull IBinder clientId, int proxyUid, @Nullable String proxyPackageName, @Nullable String proxyAttributionTag, @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags, boolean triggerCallbackIfNeeded, boolean isStarted, @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId)218 private void startedOrPaused(@NonNull IBinder clientId, int proxyUid, 219 @Nullable String proxyPackageName, @Nullable String proxyAttributionTag, 220 @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags, 221 boolean triggerCallbackIfNeeded, boolean isStarted, @AppOpsManager.AttributionFlags 222 int attributionFlags, int attributionChainId) throws RemoteException { 223 if (triggerCallbackIfNeeded && !parent.isRunning() && isStarted) { 224 mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid, 225 parent.packageName, tag, true, attributionFlags, attributionChainId); 226 } 227 228 if (isStarted && mInProgressEvents == null) { 229 mInProgressEvents = new ArrayMap<>(1); 230 } else if (!isStarted && mPausedInProgressEvents == null) { 231 mPausedInProgressEvents = new ArrayMap<>(1); 232 } 233 ArrayMap<IBinder, InProgressStartOpEvent> events = isStarted 234 ? mInProgressEvents : mPausedInProgressEvents; 235 236 long startTime = System.currentTimeMillis(); 237 InProgressStartOpEvent event = events.get(clientId); 238 if (event == null) { 239 event = mAppOpsService.mInProgressStartOpEventPool.acquire(startTime, 240 SystemClock.elapsedRealtime(), clientId, tag, 241 PooledLambda.obtainRunnable(AppOpsService::onClientDeath, this, clientId), 242 proxyUid, proxyPackageName, proxyAttributionTag, uidState, flags, 243 attributionFlags, attributionChainId); 244 events.put(clientId, event); 245 } else { 246 if (uidState != event.getUidState()) { 247 onUidStateChanged(uidState); 248 } 249 } 250 251 event.mNumUnfinishedStarts++; 252 253 if (isStarted) { 254 mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, 255 parent.packageName, tag, uidState, flags, startTime, attributionFlags, 256 attributionChainId); 257 } 258 } 259 260 /** 261 * Update state when finishOp was called. Will finish started ops, and delete paused ops. 262 * 263 * @param clientId Id of the finishOp caller 264 */ finished(@onNull IBinder clientId)265 public void finished(@NonNull IBinder clientId) { 266 finished(clientId, true); 267 } 268 finished(@onNull IBinder clientId, boolean triggerCallbackIfNeeded)269 private void finished(@NonNull IBinder clientId, boolean triggerCallbackIfNeeded) { 270 finishOrPause(clientId, triggerCallbackIfNeeded, false); 271 } 272 273 /** 274 * Update state when paused or finished is called. If pausing, it records the op as 275 * stopping in the HistoricalRegistry, but does not delete it. 276 */ 277 @SuppressWarnings("GuardedBy") // Lock is held on mAppOpsService finishOrPause(@onNull IBinder clientId, boolean triggerCallbackIfNeeded, boolean isPausing)278 private void finishOrPause(@NonNull IBinder clientId, boolean triggerCallbackIfNeeded, 279 boolean isPausing) { 280 int indexOfToken = isRunning() ? mInProgressEvents.indexOfKey(clientId) : -1; 281 if (indexOfToken < 0) { 282 finishPossiblyPaused(clientId, isPausing); 283 return; 284 } 285 286 InProgressStartOpEvent event = mInProgressEvents.valueAt(indexOfToken); 287 if (!isPausing) { 288 event.mNumUnfinishedStarts--; 289 } 290 // If we are pausing, create a NoteOpEvent, but don't change the InProgress event 291 if (event.mNumUnfinishedStarts == 0 || isPausing) { 292 if (!isPausing) { 293 event.finish(); 294 mInProgressEvents.removeAt(indexOfToken); 295 } 296 297 if (mAccessEvents == null) { 298 mAccessEvents = new LongSparseArray<>(1); 299 } 300 301 AppOpsManager.OpEventProxyInfo proxyCopy = event.getProxy() != null 302 ? new AppOpsManager.OpEventProxyInfo(event.getProxy()) : null; 303 304 long accessDurationMillis = 305 SystemClock.elapsedRealtime() - event.getStartElapsedTime(); 306 AppOpsManager.NoteOpEvent finishedEvent = new AppOpsManager.NoteOpEvent( 307 event.getStartTime(), 308 accessDurationMillis, proxyCopy); 309 mAccessEvents.put(makeKey(event.getUidState(), event.getFlags()), 310 finishedEvent); 311 312 mAppOpsService.mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid, 313 parent.packageName, tag, event.getUidState(), 314 event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration(), 315 event.getAttributionFlags(), event.getAttributionChainId()); 316 317 if (!isPausing) { 318 mAppOpsService.mInProgressStartOpEventPool.release(event); 319 if (mInProgressEvents.isEmpty()) { 320 mInProgressEvents = null; 321 322 // TODO ntmyren: Also callback for single attribution tag activity changes 323 if (triggerCallbackIfNeeded && !parent.isRunning()) { 324 mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op, 325 parent.uid, parent.packageName, tag, false, 326 event.getAttributionFlags(), event.getAttributionChainId()); 327 } 328 } 329 } 330 } 331 } 332 333 // Finish or pause (no-op) an already paused op 334 @SuppressWarnings("GuardedBy") // Lock is held on mAppOpsService finishPossiblyPaused(@onNull IBinder clientId, boolean isPausing)335 private void finishPossiblyPaused(@NonNull IBinder clientId, boolean isPausing) { 336 if (!isPaused()) { 337 Slog.wtf(AppOpsService.TAG, "No ops running or paused"); 338 return; 339 } 340 341 int indexOfToken = mPausedInProgressEvents.indexOfKey(clientId); 342 if (indexOfToken < 0) { 343 Slog.wtf(AppOpsService.TAG, "No op running or paused for the client"); 344 return; 345 } else if (isPausing) { 346 // already paused 347 return; 348 } 349 350 // no need to record a paused event finishing. 351 InProgressStartOpEvent event = mPausedInProgressEvents.valueAt(indexOfToken); 352 event.mNumUnfinishedStarts--; 353 if (event.mNumUnfinishedStarts == 0) { 354 mPausedInProgressEvents.removeAt(indexOfToken); 355 mAppOpsService.mInProgressStartOpEventPool.release(event); 356 if (mPausedInProgressEvents.isEmpty()) { 357 mPausedInProgressEvents = null; 358 } 359 } 360 } 361 362 /** 363 * Create an event that will be started, if the op is unpaused. 364 */ createPaused(@onNull IBinder clientId, int proxyUid, @Nullable String proxyPackageName, @Nullable String proxyAttributionTag, @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags, @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId)365 public void createPaused(@NonNull IBinder clientId, int proxyUid, 366 @Nullable String proxyPackageName, @Nullable String proxyAttributionTag, 367 @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags, 368 @AppOpsManager.AttributionFlags 369 int attributionFlags, int attributionChainId) throws RemoteException { 370 startedOrPaused(clientId, proxyUid, proxyPackageName, proxyAttributionTag, 371 uidState, flags, true, false, attributionFlags, attributionChainId); 372 } 373 374 /** 375 * Pause all currently started ops. This will create a HistoricalRegistry 376 */ pause()377 public void pause() { 378 if (!isRunning()) { 379 return; 380 } 381 382 if (mPausedInProgressEvents == null) { 383 mPausedInProgressEvents = new ArrayMap<>(1); 384 } 385 386 for (int i = 0; i < mInProgressEvents.size(); i++) { 387 InProgressStartOpEvent event = mInProgressEvents.valueAt(i); 388 mPausedInProgressEvents.put(event.getClientId(), event); 389 finishOrPause(event.getClientId(), true, true); 390 391 mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid, 392 parent.packageName, tag, false, 393 event.getAttributionFlags(), event.getAttributionChainId()); 394 } 395 mInProgressEvents = null; 396 } 397 398 /** 399 * Unpause all currently paused ops. This will reinitialize their start and duration 400 * times, but keep all other values the same 401 */ resume()402 public void resume() { 403 if (!isPaused()) { 404 return; 405 } 406 407 if (mInProgressEvents == null) { 408 mInProgressEvents = new ArrayMap<>(mPausedInProgressEvents.size()); 409 } 410 boolean shouldSendActive = !mPausedInProgressEvents.isEmpty() 411 && mInProgressEvents.isEmpty(); 412 413 long startTime = System.currentTimeMillis(); 414 for (int i = 0; i < mPausedInProgressEvents.size(); i++) { 415 InProgressStartOpEvent event = mPausedInProgressEvents.valueAt(i); 416 mInProgressEvents.put(event.getClientId(), event); 417 event.setStartElapsedTime(SystemClock.elapsedRealtime()); 418 event.setStartTime(startTime); 419 mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, 420 parent.packageName, tag, event.getUidState(), event.getFlags(), startTime, 421 event.getAttributionFlags(), event.getAttributionChainId()); 422 if (shouldSendActive) { 423 mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid, 424 parent.packageName, tag, true, event.getAttributionFlags(), 425 event.getAttributionChainId()); 426 } 427 // Note: this always sends MODE_ALLOWED, even if the mode is FOREGROUND 428 // TODO ntmyren: figure out how to get the real mode. 429 mAppOpsService.scheduleOpStartedIfNeededLocked(parent.op, parent.uid, 430 parent.packageName, tag, event.getFlags(), MODE_ALLOWED, START_TYPE_RESUMED, 431 event.getAttributionFlags(), event.getAttributionChainId()); 432 } 433 mPausedInProgressEvents = null; 434 } 435 436 /** 437 * Called in the case the client dies without calling finish first 438 * 439 * @param clientId The client that died 440 */ onClientDeath(@onNull IBinder clientId)441 void onClientDeath(@NonNull IBinder clientId) { 442 synchronized (mAppOpsService) { 443 if (!isPaused() && !isRunning()) { 444 return; 445 } 446 447 ArrayMap<IBinder, InProgressStartOpEvent> events = isPaused() 448 ? mPausedInProgressEvents : mInProgressEvents; 449 InProgressStartOpEvent deadEvent = events.get(clientId); 450 if (deadEvent != null) { 451 deadEvent.mNumUnfinishedStarts = 1; 452 } 453 454 finished(clientId); 455 } 456 } 457 458 /** 459 * Notify that the state of the uid changed 460 * 461 * @param newState The new state 462 */ onUidStateChanged(@ppOpsManager.UidState int newState)463 public void onUidStateChanged(@AppOpsManager.UidState int newState) { 464 if (!isPaused() && !isRunning()) { 465 return; 466 } 467 468 boolean isRunning = isRunning(); 469 ArrayMap<IBinder, InProgressStartOpEvent> events = 470 isRunning ? mInProgressEvents : mPausedInProgressEvents; 471 472 int numInProgressEvents = events.size(); 473 List<IBinder> binders = new ArrayList<>(events.keySet()); 474 for (int i = 0; i < numInProgressEvents; i++) { 475 InProgressStartOpEvent event = events.get(binders.get(i)); 476 477 if (event != null && event.getUidState() != newState) { 478 try { 479 // Remove all but one unfinished start count and then call finished() to 480 // remove start event object 481 int numPreviousUnfinishedStarts = event.mNumUnfinishedStarts; 482 event.mNumUnfinishedStarts = 1; 483 AppOpsManager.OpEventProxyInfo proxy = event.getProxy(); 484 485 finished(event.getClientId(), false); 486 487 // Call started() to add a new start event object and then add the 488 // previously removed unfinished start counts back 489 if (proxy != null) { 490 startedOrPaused(event.getClientId(), proxy.getUid(), 491 proxy.getPackageName(), proxy.getAttributionTag(), newState, 492 event.getFlags(), false, isRunning, 493 event.getAttributionFlags(), event.getAttributionChainId()); 494 } else { 495 startedOrPaused(event.getClientId(), Process.INVALID_UID, null, null, 496 newState, event.getFlags(), false, isRunning, 497 event.getAttributionFlags(), event.getAttributionChainId()); 498 } 499 500 events = isRunning ? mInProgressEvents : mPausedInProgressEvents; 501 InProgressStartOpEvent newEvent = events.get(binders.get(i)); 502 if (newEvent != null) { 503 newEvent.mNumUnfinishedStarts += numPreviousUnfinishedStarts - 1; 504 } 505 } catch (RemoteException e) { 506 if (AppOpsService.DEBUG) { 507 Slog.e(AppOpsService.TAG, 508 "Cannot switch to new uidState " + newState); 509 } 510 } 511 } 512 } 513 } 514 515 /** 516 * Combine {@code a} and {@code b} and return the result. The result might be {@code a} 517 * or {@code b}. If there is an event for the same key in both the later event is retained. 518 */ add( @ullable LongSparseArray<AppOpsManager.NoteOpEvent> a, @Nullable LongSparseArray<AppOpsManager.NoteOpEvent> b)519 private @Nullable LongSparseArray<AppOpsManager.NoteOpEvent> add( 520 @Nullable LongSparseArray<AppOpsManager.NoteOpEvent> a, 521 @Nullable LongSparseArray<AppOpsManager.NoteOpEvent> b) { 522 if (a == null) { 523 return b; 524 } 525 526 if (b == null) { 527 return a; 528 } 529 530 int numEventsToAdd = b.size(); 531 for (int i = 0; i < numEventsToAdd; i++) { 532 long keyOfEventToAdd = b.keyAt(i); 533 AppOpsManager.NoteOpEvent bEvent = b.valueAt(i); 534 AppOpsManager.NoteOpEvent aEvent = a.get(keyOfEventToAdd); 535 536 if (aEvent == null || bEvent.getNoteTime() > aEvent.getNoteTime()) { 537 a.put(keyOfEventToAdd, bEvent); 538 } 539 } 540 541 return a; 542 } 543 544 /** 545 * Add all data from the {@code opToAdd} to this op. 546 * 547 * <p>If there is an event for the same key in both the later event is retained. 548 * <p>{@code opToAdd} should not be used after this method is called. 549 * 550 * @param opToAdd The op to add 551 */ 552 @SuppressWarnings("GuardedBy") // Lock is held on mAppOpsService add(@onNull AttributedOp opToAdd)553 public void add(@NonNull AttributedOp opToAdd) { 554 if (opToAdd.isRunning() || opToAdd.isPaused()) { 555 ArrayMap<IBinder, InProgressStartOpEvent> ignoredEvents = 556 opToAdd.isRunning() 557 ? opToAdd.mInProgressEvents : opToAdd.mPausedInProgressEvents; 558 Slog.w(AppOpsService.TAG, "Ignoring " + ignoredEvents.size() + " app-ops, running: " 559 + opToAdd.isRunning()); 560 561 int numInProgressEvents = ignoredEvents.size(); 562 for (int i = 0; i < numInProgressEvents; i++) { 563 InProgressStartOpEvent event = ignoredEvents.valueAt(i); 564 565 event.finish(); 566 mAppOpsService.mInProgressStartOpEventPool.release(event); 567 } 568 } 569 570 mAccessEvents = add(mAccessEvents, opToAdd.mAccessEvents); 571 mRejectEvents = add(mRejectEvents, opToAdd.mRejectEvents); 572 } 573 isRunning()574 public boolean isRunning() { 575 return mInProgressEvents != null && !mInProgressEvents.isEmpty(); 576 } 577 isPaused()578 public boolean isPaused() { 579 return mPausedInProgressEvents != null && !mPausedInProgressEvents.isEmpty(); 580 } 581 hasAnyTime()582 boolean hasAnyTime() { 583 return (mAccessEvents != null && mAccessEvents.size() > 0) 584 || (mRejectEvents != null && mRejectEvents.size() > 0); 585 } 586 587 /** 588 * Clone a {@link LongSparseArray} and clone all values. 589 */ deepClone( @ullable LongSparseArray<AppOpsManager.NoteOpEvent> original)590 private @Nullable LongSparseArray<AppOpsManager.NoteOpEvent> deepClone( 591 @Nullable LongSparseArray<AppOpsManager.NoteOpEvent> original) { 592 if (original == null) { 593 return original; 594 } 595 596 int size = original.size(); 597 LongSparseArray<AppOpsManager.NoteOpEvent> clone = new LongSparseArray<>(size); 598 for (int i = 0; i < size; i++) { 599 clone.put(original.keyAt(i), new AppOpsManager.NoteOpEvent(original.valueAt(i))); 600 } 601 602 return clone; 603 } 604 createAttributedOpEntryLocked()605 @NonNull AppOpsManager.AttributedOpEntry createAttributedOpEntryLocked() { 606 LongSparseArray<AppOpsManager.NoteOpEvent> accessEvents = deepClone(mAccessEvents); 607 608 // Add in progress events as access events 609 if (isRunning()) { 610 long now = SystemClock.elapsedRealtime(); 611 int numInProgressEvents = mInProgressEvents.size(); 612 613 if (accessEvents == null) { 614 accessEvents = new LongSparseArray<>(numInProgressEvents); 615 } 616 617 for (int i = 0; i < numInProgressEvents; i++) { 618 InProgressStartOpEvent event = mInProgressEvents.valueAt(i); 619 620 accessEvents.append(makeKey(event.getUidState(), event.getFlags()), 621 new AppOpsManager.NoteOpEvent(event.getStartTime(), 622 now - event.getStartElapsedTime(), 623 event.getProxy())); 624 } 625 } 626 627 LongSparseArray<AppOpsManager.NoteOpEvent> rejectEvents = deepClone(mRejectEvents); 628 629 return new AppOpsManager.AttributedOpEntry(parent.op, isRunning(), accessEvents, 630 rejectEvents); 631 } 632 633 /** A in progress startOp->finishOp event */ 634 static final class InProgressStartOpEvent implements IBinder.DeathRecipient { 635 /** Wall clock time of startOp event (not monotonic) */ 636 private long mStartTime; 637 638 /** Elapsed time since boot of startOp event */ 639 private long mStartElapsedTime; 640 641 /** Id of the client that started the event */ 642 private @NonNull IBinder mClientId; 643 644 /** The attribution tag for this operation */ 645 private @Nullable String mAttributionTag; 646 647 /** To call when client dies */ 648 private @NonNull Runnable mOnDeath; 649 650 /** uidstate used when calling startOp */ 651 private @AppOpsManager.UidState int mUidState; 652 653 /** Proxy information of the startOp event */ 654 private @Nullable AppOpsManager.OpEventProxyInfo mProxy; 655 656 /** Proxy flag information */ 657 private @AppOpsManager.OpFlags int mFlags; 658 659 /** How many times the op was started but not finished yet */ 660 int mNumUnfinishedStarts; 661 662 /** The attribution flags related to this event */ 663 private @AppOpsManager.AttributionFlags int mAttributionFlags; 664 665 /** The id of the attribution chain this even is a part of */ 666 private int mAttributionChainId; 667 668 /** 669 * Create a new {@link InProgressStartOpEvent}. 670 * 671 * @param startTime The time {@link #startOperation} was called 672 * @param startElapsedTime The elapsed time when {@link #startOperation} was called 673 * @param clientId The client id of the caller of {@link #startOperation} 674 * @param attributionTag The attribution tag for the operation. 675 * @param onDeath The code to execute on client death 676 * @param uidState The uidstate of the app {@link #startOperation} was called for 677 * @param attributionFlags the attribution flags for this operation. 678 * @param attributionChainId the unique id of the attribution chain this op is a part of. 679 * @param proxy The proxy information, if {@link #startProxyOperation} was 680 * called 681 * @param flags The trusted/nontrusted/self flags. 682 * @throws RemoteException If the client is dying 683 */ InProgressStartOpEvent(long startTime, long startElapsedTime, @NonNull IBinder clientId, @Nullable String attributionTag, @NonNull Runnable onDeath, @AppOpsManager.UidState int uidState, @Nullable AppOpsManager.OpEventProxyInfo proxy, @AppOpsManager.OpFlags int flags, @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId)684 InProgressStartOpEvent(long startTime, long startElapsedTime, 685 @NonNull IBinder clientId, @Nullable String attributionTag, 686 @NonNull Runnable onDeath, @AppOpsManager.UidState int uidState, 687 @Nullable AppOpsManager.OpEventProxyInfo proxy, @AppOpsManager.OpFlags int flags, 688 @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) 689 throws RemoteException { 690 mStartTime = startTime; 691 mStartElapsedTime = startElapsedTime; 692 mClientId = clientId; 693 mAttributionTag = attributionTag; 694 mOnDeath = onDeath; 695 mUidState = uidState; 696 mProxy = proxy; 697 mFlags = flags; 698 mAttributionFlags = attributionFlags; 699 mAttributionChainId = attributionChainId; 700 701 clientId.linkToDeath(this, 0); 702 } 703 704 /** Clean up event */ finish()705 public void finish() { 706 try { 707 mClientId.unlinkToDeath(this, 0); 708 } catch (NoSuchElementException e) { 709 // Either not linked, or already unlinked. Either way, nothing to do. 710 } 711 } 712 713 @Override binderDied()714 public void binderDied() { 715 mOnDeath.run(); 716 } 717 718 /** 719 * Reinit existing object with new state. 720 * 721 * @param startTime The time {@link #startOperation} was called 722 * @param startElapsedTime The elapsed time when {@link #startOperation} was called 723 * @param clientId The client id of the caller of {@link #startOperation} 724 * @param attributionTag The attribution tag for this operation. 725 * @param onDeath The code to execute on client death 726 * @param uidState The uidstate of the app {@link #startOperation} was called for 727 * @param flags The flags relating to the proxy 728 * @param proxy The proxy information, if {@link #startProxyOperation} 729 * was called 730 * @param attributionFlags the attribution flags for this operation. 731 * @param attributionChainId the unique id of the attribution chain this op is a part of. 732 * @param proxyPool The pool to release 733 * previous {@link AppOpsManager.OpEventProxyInfo} to 734 * @throws RemoteException If the client is dying 735 */ reinit(long startTime, long startElapsedTime, @NonNull IBinder clientId, @Nullable String attributionTag, @NonNull Runnable onDeath, @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags, @Nullable AppOpsManager.OpEventProxyInfo proxy, @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId, @NonNull Pools.Pool<AppOpsManager.OpEventProxyInfo> proxyPool )736 public void reinit(long startTime, long startElapsedTime, @NonNull IBinder clientId, 737 @Nullable String attributionTag, @NonNull Runnable onDeath, 738 @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags, 739 @Nullable AppOpsManager.OpEventProxyInfo proxy, 740 @AppOpsManager.AttributionFlags int attributionFlags, 741 int attributionChainId, 742 @NonNull Pools.Pool<AppOpsManager.OpEventProxyInfo> proxyPool 743 ) throws RemoteException { 744 mStartTime = startTime; 745 mStartElapsedTime = startElapsedTime; 746 mClientId = clientId; 747 mAttributionTag = attributionTag; 748 mOnDeath = onDeath; 749 mUidState = uidState; 750 mFlags = flags; 751 752 if (mProxy != null) { 753 proxyPool.release(mProxy); 754 } 755 mProxy = proxy; 756 mAttributionFlags = attributionFlags; 757 mAttributionChainId = attributionChainId; 758 759 clientId.linkToDeath(this, 0); 760 } 761 762 /** @return Wall clock time of startOp event */ getStartTime()763 public long getStartTime() { 764 return mStartTime; 765 } 766 767 /** @return Elapsed time since boot of startOp event */ getStartElapsedTime()768 public long getStartElapsedTime() { 769 return mStartElapsedTime; 770 } 771 772 /** @return Id of the client that started the event */ getClientId()773 public @NonNull IBinder getClientId() { 774 return mClientId; 775 } 776 777 /** @return uidstate used when calling startOp */ getUidState()778 public @AppOpsManager.UidState int getUidState() { 779 return mUidState; 780 } 781 782 /** @return proxy tag for the access */ getProxy()783 public @Nullable AppOpsManager.OpEventProxyInfo getProxy() { 784 return mProxy; 785 } 786 787 /** @return flags used for the access */ getFlags()788 public @AppOpsManager.OpFlags int getFlags() { 789 return mFlags; 790 } 791 792 /** @return attributoin flags used for the access */ getAttributionFlags()793 public @AppOpsManager.AttributionFlags int getAttributionFlags() { 794 return mAttributionFlags; 795 } 796 797 /** @return attribution chain id for the access */ getAttributionChainId()798 public int getAttributionChainId() { 799 return mAttributionChainId; 800 } 801 setStartTime(long startTime)802 public void setStartTime(long startTime) { 803 mStartTime = startTime; 804 } 805 setStartElapsedTime(long startElapsedTime)806 public void setStartElapsedTime(long startElapsedTime) { 807 mStartElapsedTime = startElapsedTime; 808 } 809 } 810 811 /** 812 * An unsynchronized pool of {@link InProgressStartOpEvent} objects. 813 */ 814 static class InProgressStartOpEventPool extends Pools.SimplePool<InProgressStartOpEvent> { 815 private OpEventProxyInfoPool mOpEventProxyInfoPool; 816 InProgressStartOpEventPool(OpEventProxyInfoPool opEventProxyInfoPool, int maxUnusedPooledObjects)817 InProgressStartOpEventPool(OpEventProxyInfoPool opEventProxyInfoPool, 818 int maxUnusedPooledObjects) { 819 super(maxUnusedPooledObjects); 820 this.mOpEventProxyInfoPool = opEventProxyInfoPool; 821 } 822 acquire(long startTime, long elapsedTime, @NonNull IBinder clientId, @Nullable String attributionTag, @NonNull Runnable onDeath, int proxyUid, @Nullable String proxyPackageName, @Nullable String proxyAttributionTag, @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags, @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId)823 InProgressStartOpEvent acquire(long startTime, long elapsedTime, @NonNull IBinder clientId, 824 @Nullable String attributionTag, @NonNull Runnable onDeath, int proxyUid, 825 @Nullable String proxyPackageName, @Nullable String proxyAttributionTag, 826 @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags, 827 @AppOpsManager.AttributionFlags 828 int attributionFlags, int attributionChainId) throws RemoteException { 829 830 InProgressStartOpEvent recycled = acquire(); 831 832 AppOpsManager.OpEventProxyInfo proxyInfo = null; 833 if (proxyUid != Process.INVALID_UID) { 834 proxyInfo = mOpEventProxyInfoPool.acquire(proxyUid, proxyPackageName, 835 proxyAttributionTag); 836 } 837 838 if (recycled != null) { 839 recycled.reinit(startTime, elapsedTime, clientId, attributionTag, onDeath, 840 uidState, flags, proxyInfo, attributionFlags, attributionChainId, 841 mOpEventProxyInfoPool); 842 return recycled; 843 } 844 845 return new InProgressStartOpEvent(startTime, elapsedTime, clientId, attributionTag, 846 onDeath, uidState, proxyInfo, flags, attributionFlags, attributionChainId); 847 } 848 } 849 850 /** 851 * An unsynchronized pool of {@link AppOpsManager.OpEventProxyInfo} objects. 852 */ 853 static class OpEventProxyInfoPool extends Pools.SimplePool<AppOpsManager.OpEventProxyInfo> { OpEventProxyInfoPool(int maxUnusedPooledObjects)854 OpEventProxyInfoPool(int maxUnusedPooledObjects) { 855 super(maxUnusedPooledObjects); 856 } 857 acquire(@ntRangefrom = 0) int uid, @Nullable String packageName, @Nullable String attributionTag)858 AppOpsManager.OpEventProxyInfo acquire(@IntRange(from = 0) int uid, 859 @Nullable String packageName, 860 @Nullable String attributionTag) { 861 AppOpsManager.OpEventProxyInfo recycled = acquire(); 862 if (recycled != null) { 863 recycled.reinit(uid, packageName, attributionTag); 864 return recycled; 865 } 866 867 return new AppOpsManager.OpEventProxyInfo(uid, packageName, attributionTag); 868 } 869 } 870 } 871