• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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