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