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