Move related classes into AttributedOp

Move InProgressStartOpEvent, InProgressStartOpEventPool, and
OpEventProxyInfoPool into the AttributedOp class.

These are currently in AppOpsService, but are only ever used by
AttributedOp. Moving them reduces the amount of noise in AppOpsService.

Test: presubmit
Change-Id: Ic4e03b82c481c73914470d143c733a0ea243a72d
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 3d5711f..b844fc6 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -68,7 +68,6 @@
 import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS;
 
 import android.Manifest;
-import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -129,8 +128,6 @@
 import android.util.IndentingPrintWriter;
 import android.util.KeyValueListParser;
 import android.util.Pair;
-import android.util.Pools;
-import android.util.Pools.SimplePool;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -194,7 +191,6 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.Scanner;
 import java.util.Set;
@@ -237,14 +233,22 @@
     private final @Nullable File mNoteOpCallerStacktracesFile;
     final Handler mHandler;
 
-    /** Pool for {@link OpEventProxyInfoPool} to avoid to constantly reallocate new objects */
+    /**
+     * Pool for {@link AttributedOp.OpEventProxyInfoPool} to avoid to constantly reallocate new
+     * objects
+     */
     @GuardedBy("this")
-    final OpEventProxyInfoPool mOpEventProxyInfoPool = new OpEventProxyInfoPool();
+    final AttributedOp.OpEventProxyInfoPool mOpEventProxyInfoPool =
+            new AttributedOp.OpEventProxyInfoPool(MAX_UNUSED_POOLED_OBJECTS);
 
-    /** Pool for {@link InProgressStartOpEventPool} to avoid to constantly reallocate new objects */
+    /**
+     * Pool for {@link AttributedOp.InProgressStartOpEventPool} to avoid to constantly reallocate
+     * new objects
+     */
     @GuardedBy("this")
-    final InProgressStartOpEventPool mInProgressStartOpEventPool =
-            new InProgressStartOpEventPool();
+    final AttributedOp.InProgressStartOpEventPool mInProgressStartOpEventPool =
+            new AttributedOp.InProgressStartOpEventPool(mOpEventProxyInfoPool,
+                    MAX_UNUSED_POOLED_OBJECTS);
 
     private final AppOpsManagerInternalImpl mAppOpsManagerInternal
             = new AppOpsManagerInternalImpl();
@@ -379,60 +383,6 @@
     }
 
     /**
-     * An unsynchronized pool of {@link OpEventProxyInfo} objects.
-     */
-    class OpEventProxyInfoPool extends SimplePool<OpEventProxyInfo> {
-        OpEventProxyInfoPool() {
-            super(MAX_UNUSED_POOLED_OBJECTS);
-        }
-
-        OpEventProxyInfo acquire(@IntRange(from = 0) int uid, @Nullable String packageName,
-                @Nullable String attributionTag) {
-            OpEventProxyInfo recycled = acquire();
-            if (recycled != null) {
-                recycled.reinit(uid, packageName, attributionTag);
-                return recycled;
-            }
-
-            return new OpEventProxyInfo(uid, packageName, attributionTag);
-        }
-    }
-
-    /**
-     * An unsynchronized pool of {@link InProgressStartOpEvent} objects.
-     */
-    class InProgressStartOpEventPool extends SimplePool<InProgressStartOpEvent> {
-        InProgressStartOpEventPool() {
-            super(MAX_UNUSED_POOLED_OBJECTS);
-        }
-
-        InProgressStartOpEvent acquire(long startTime, long elapsedTime, @NonNull IBinder clientId,
-                @Nullable String attributionTag, @NonNull Runnable onDeath, int proxyUid,
-                @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
-                @AppOpsManager.UidState int uidState, @OpFlags int flags, @AttributionFlags
-                int attributionFlags, int attributionChainId) throws RemoteException {
-
-            InProgressStartOpEvent recycled = acquire();
-
-            OpEventProxyInfo proxyInfo = null;
-            if (proxyUid != Process.INVALID_UID) {
-                proxyInfo = mOpEventProxyInfoPool.acquire(proxyUid, proxyPackageName,
-                        proxyAttributionTag);
-            }
-
-            if (recycled != null) {
-                recycled.reinit(startTime, elapsedTime, clientId, attributionTag, onDeath,
-                        uidState, flags, proxyInfo,  attributionFlags, attributionChainId,
-                        mOpEventProxyInfoPool);
-                return recycled;
-            }
-
-            return new InProgressStartOpEvent(startTime, elapsedTime, clientId, attributionTag,
-                    onDeath, uidState, proxyInfo, flags, attributionFlags, attributionChainId);
-        }
-    }
-
-    /**
      * All times are in milliseconds. These constants are kept synchronized with the system
      * global Settings. Any access to this class or its fields should be done while
      * holding the AppOpsService lock.
@@ -648,181 +598,6 @@
         }
     }
 
-    /** A in progress startOp->finishOp event */
-    static final class InProgressStartOpEvent implements IBinder.DeathRecipient {
-        /** Wall clock time of startOp event (not monotonic) */
-        private long mStartTime;
-
-        /** Elapsed time since boot of startOp event */
-        private long mStartElapsedTime;
-
-        /** Id of the client that started the event */
-        private @NonNull IBinder mClientId;
-
-        /** The attribution tag for this operation */
-        private @Nullable String mAttributionTag;
-
-        /** To call when client dies */
-        private @NonNull Runnable mOnDeath;
-
-        /** uidstate used when calling startOp */
-        private @AppOpsManager.UidState int mUidState;
-
-        /** Proxy information of the startOp event */
-        private @Nullable OpEventProxyInfo mProxy;
-
-        /** Proxy flag information */
-        private @OpFlags int mFlags;
-
-        /** How many times the op was started but not finished yet */
-        int numUnfinishedStarts;
-
-        /** The attribution flags related to this event */
-        private @AttributionFlags int mAttributionFlags;
-
-        /** The id of the attribution chain this even is a part of */
-        private int mAttributionChainId;
-
-        /**
-         * Create a new {@link InProgressStartOpEvent}.
-         *
-         * @param startTime The time {@link #startOperation} was called
-         * @param startElapsedTime The elapsed time when {@link #startOperation} was called
-         * @param clientId The client id of the caller of {@link #startOperation}
-         * @param attributionTag The attribution tag for the operation.
-         * @param onDeath The code to execute on client death
-         * @param uidState The uidstate of the app {@link #startOperation} was called for
-         * @param attributionFlags the attribution flags for this operation.
-         * @param attributionChainId the unique id of the attribution chain this op is a part of.
-         * @param proxy The proxy information, if {@link #startProxyOperation} was called
-         * @param flags The trusted/nontrusted/self flags.
-         *
-         * @throws RemoteException If the client is dying
-         */
-        private InProgressStartOpEvent(long startTime, long startElapsedTime,
-                @NonNull IBinder clientId, @Nullable String attributionTag,
-                @NonNull Runnable onDeath, @AppOpsManager.UidState int uidState,
-                @Nullable OpEventProxyInfo proxy, @OpFlags int flags,
-                @AttributionFlags int attributionFlags, int attributionChainId)
-                throws RemoteException {
-            mStartTime = startTime;
-            mStartElapsedTime = startElapsedTime;
-            mClientId = clientId;
-            mAttributionTag = attributionTag;
-            mOnDeath = onDeath;
-            mUidState = uidState;
-            mProxy = proxy;
-            mFlags = flags;
-            mAttributionFlags = attributionFlags;
-            mAttributionChainId = attributionChainId;
-
-            clientId.linkToDeath(this, 0);
-        }
-
-        /** Clean up event */
-        public void finish() {
-            try {
-                mClientId.unlinkToDeath(this, 0);
-            } catch (NoSuchElementException e) {
-                // Either not linked, or already unlinked. Either way, nothing to do.
-            }
-        }
-
-        @Override
-        public void binderDied() {
-            mOnDeath.run();
-        }
-
-        /**
-         * Reinit existing object with new state.
-         *
-         * @param startTime The time {@link #startOperation} was called
-         * @param startElapsedTime The elapsed time when {@link #startOperation} was called
-         * @param clientId The client id of the caller of {@link #startOperation}
-         * @param attributionTag The attribution tag for this operation.
-         * @param onDeath The code to execute on client death
-         * @param uidState The uidstate of the app {@link #startOperation} was called for
-         * @param flags The flags relating to the proxy
-         * @param proxy The proxy information, if {@link #startProxyOperation} was called
-         * @param attributionFlags the attribution flags for this operation.
-         * @param attributionChainId the unique id of the attribution chain this op is a part of.
-         * @param proxyPool The pool to release previous {@link OpEventProxyInfo} to
-         *
-         * @throws RemoteException If the client is dying
-         */
-        public void reinit(long startTime, long startElapsedTime, @NonNull IBinder clientId,
-                @Nullable String attributionTag, @NonNull Runnable onDeath,
-                @AppOpsManager.UidState int uidState, @OpFlags int flags,
-                @Nullable OpEventProxyInfo proxy, @AttributionFlags int attributionFlags,
-                int attributionChainId, @NonNull Pools.Pool<OpEventProxyInfo> proxyPool
-        ) throws RemoteException {
-            mStartTime = startTime;
-            mStartElapsedTime = startElapsedTime;
-            mClientId = clientId;
-            mAttributionTag = attributionTag;
-            mOnDeath = onDeath;
-            mUidState = uidState;
-            mFlags = flags;
-
-            if (mProxy != null) {
-                proxyPool.release(mProxy);
-            }
-            mProxy = proxy;
-            mAttributionFlags = attributionFlags;
-            mAttributionChainId = attributionChainId;
-
-            clientId.linkToDeath(this, 0);
-        }
-
-        /** @return Wall clock time of startOp event */
-        public long getStartTime() {
-            return mStartTime;
-        }
-
-        /** @return Elapsed time since boot of startOp event */
-        public long getStartElapsedTime() {
-            return mStartElapsedTime;
-        }
-
-        /** @return Id of the client that started the event */
-        public @NonNull IBinder getClientId() {
-            return mClientId;
-        }
-
-        /** @return uidstate used when calling startOp */
-        public @AppOpsManager.UidState int getUidState() {
-            return mUidState;
-        }
-
-        /** @return proxy tag for the access */
-        public @Nullable OpEventProxyInfo getProxy() {
-            return mProxy;
-        }
-
-        /** @return flags used for the access */
-        public @OpFlags int getFlags() {
-            return mFlags;
-        }
-
-        /** @return attributoin flags used for the access */
-        public @AttributionFlags int getAttributionFlags() {
-            return mAttributionFlags;
-        }
-
-        /** @return attribution chain id for the access */
-        public int getAttributionChainId() {
-            return mAttributionChainId;
-        }
-
-        public void setStartTime(long startTime) {
-            mStartTime = startTime;
-        }
-
-        public void setStartElapsedTime(long startElapsedTime) {
-            mStartElapsedTime = startElapsedTime;
-        }
-    }
-
     final class Op {
         int op;
         int uid;
@@ -5137,10 +4912,11 @@
             long maxNumStarts = 0;
             int numInProgressEvents = attributedOp.mInProgressEvents.size();
             for (int i = 0; i < numInProgressEvents; i++) {
-                InProgressStartOpEvent event = attributedOp.mInProgressEvents.valueAt(i);
+                AttributedOp.InProgressStartOpEvent event =
+                        attributedOp.mInProgressEvents.valueAt(i);
 
                 earliestElapsedTime = Math.min(earliestElapsedTime, event.getStartElapsedTime());
-                maxNumStarts = Math.max(maxNumStarts, event.numUnfinishedStarts);
+                maxNumStarts = Math.max(maxNumStarts, event.mNumUnfinishedStarts);
             }
 
             pw.print(prefix + "Running start at: ");
diff --git a/services/core/java/com/android/server/appop/AttributedOp.java b/services/core/java/com/android/server/appop/AttributedOp.java
index c37876b..dcc36bc 100644
--- a/services/core/java/com/android/server/appop/AttributedOp.java
+++ b/services/core/java/com/android/server/appop/AttributedOp.java
@@ -20,6 +20,7 @@
 import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_RESUMED;
 import static android.app.AppOpsManager.makeKey;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
@@ -29,13 +30,14 @@
 import android.os.SystemClock;
 import android.util.ArrayMap;
 import android.util.LongSparseArray;
+import android.util.Pools;
 import android.util.Slog;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.NoSuchElementException;
 
 final class AttributedOp {
     private final @NonNull AppOpsService mAppOpsService;
@@ -47,7 +49,8 @@
      *
      * <p>Key is {@link AppOpsManager#makeKey}
      */
-    @GuardedBy("mAppOpsService")
+    // TODO(b/248108338)
+    // @GuardedBy("mAppOpsService")
     private @Nullable LongSparseArray<AppOpsManager.NoteOpEvent> mAccessEvents;
 
     /**
@@ -55,7 +58,8 @@
      *
      * <p>Key is {@link AppOpsManager#makeKey}
      */
-    @GuardedBy("mAppOpsService")
+    // TODO(b/248108338)
+    // @GuardedBy("mAppOpsService")
     private @Nullable LongSparseArray<AppOpsManager.NoteOpEvent> mRejectEvents;
 
     /**
@@ -63,16 +67,18 @@
      *
      * <p>Key is clientId
      */
-    @GuardedBy("mAppOpsService")
-    @Nullable ArrayMap<IBinder, AppOpsService.InProgressStartOpEvent> mInProgressEvents;
+    // TODO(b/248108338)
+    // @GuardedBy("mAppOpsService")
+    @Nullable ArrayMap<IBinder, InProgressStartOpEvent> mInProgressEvents;
 
     /**
      * Currently paused startOp events
      *
      * <p>Key is clientId
      */
-    @GuardedBy("mAppOpsService")
-    @Nullable ArrayMap<IBinder, AppOpsService.InProgressStartOpEvent> mPausedInProgressEvents;
+    // TODO(b/248108338)
+    // @GuardedBy("mAppOpsService")
+    @Nullable ArrayMap<IBinder, InProgressStartOpEvent> mPausedInProgressEvents;
 
     AttributedOp(@NonNull AppOpsService appOpsService, @Nullable String tag,
             @NonNull AppOpsService.Op parent) {
@@ -224,11 +230,11 @@
         } else if (!isStarted && mPausedInProgressEvents == null) {
             mPausedInProgressEvents = new ArrayMap<>(1);
         }
-        ArrayMap<IBinder, AppOpsService.InProgressStartOpEvent> events = isStarted
+        ArrayMap<IBinder, InProgressStartOpEvent> events = isStarted
                 ? mInProgressEvents : mPausedInProgressEvents;
 
         long startTime = System.currentTimeMillis();
-        AppOpsService.InProgressStartOpEvent event = events.get(clientId);
+        InProgressStartOpEvent event = events.get(clientId);
         if (event == null) {
             event = mAppOpsService.mInProgressStartOpEventPool.acquire(startTime,
                     SystemClock.elapsedRealtime(), clientId, tag,
@@ -242,7 +248,7 @@
             }
         }
 
-        event.numUnfinishedStarts++;
+        event.mNumUnfinishedStarts++;
 
         if (isStarted) {
             mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
@@ -277,12 +283,12 @@
             return;
         }
 
-        AppOpsService.InProgressStartOpEvent event = mInProgressEvents.valueAt(indexOfToken);
+        InProgressStartOpEvent event = mInProgressEvents.valueAt(indexOfToken);
         if (!isPausing) {
-            event.numUnfinishedStarts--;
+            event.mNumUnfinishedStarts--;
         }
         // If we are pausing, create a NoteOpEvent, but don't change the InProgress event
-        if (event.numUnfinishedStarts == 0 || isPausing) {
+        if (event.mNumUnfinishedStarts == 0 || isPausing) {
             if (!isPausing) {
                 event.finish();
                 mInProgressEvents.removeAt(indexOfToken);
@@ -342,9 +348,9 @@
         }
 
         // no need to record a paused event finishing.
-        AppOpsService.InProgressStartOpEvent event = mPausedInProgressEvents.valueAt(indexOfToken);
-        event.numUnfinishedStarts--;
-        if (event.numUnfinishedStarts == 0) {
+        InProgressStartOpEvent event = mPausedInProgressEvents.valueAt(indexOfToken);
+        event.mNumUnfinishedStarts--;
+        if (event.mNumUnfinishedStarts == 0) {
             mPausedInProgressEvents.removeAt(indexOfToken);
             mAppOpsService.mInProgressStartOpEventPool.release(event);
             if (mPausedInProgressEvents.isEmpty()) {
@@ -378,7 +384,7 @@
         }
 
         for (int i = 0; i < mInProgressEvents.size(); i++) {
-            AppOpsService.InProgressStartOpEvent event = mInProgressEvents.valueAt(i);
+            InProgressStartOpEvent event = mInProgressEvents.valueAt(i);
             mPausedInProgressEvents.put(event.getClientId(), event);
             finishOrPause(event.getClientId(), true, true);
 
@@ -406,7 +412,7 @@
 
         long startTime = System.currentTimeMillis();
         for (int i = 0; i < mPausedInProgressEvents.size(); i++) {
-            AppOpsService.InProgressStartOpEvent event = mPausedInProgressEvents.valueAt(i);
+            InProgressStartOpEvent event = mPausedInProgressEvents.valueAt(i);
             mInProgressEvents.put(event.getClientId(), event);
             event.setStartElapsedTime(SystemClock.elapsedRealtime());
             event.setStartTime(startTime);
@@ -438,11 +444,11 @@
                 return;
             }
 
-            ArrayMap<IBinder, AppOpsService.InProgressStartOpEvent> events = isPaused()
+            ArrayMap<IBinder, InProgressStartOpEvent> events = isPaused()
                     ? mPausedInProgressEvents : mInProgressEvents;
-            AppOpsService.InProgressStartOpEvent deadEvent = events.get(clientId);
+            InProgressStartOpEvent deadEvent = events.get(clientId);
             if (deadEvent != null) {
-                deadEvent.numUnfinishedStarts = 1;
+                deadEvent.mNumUnfinishedStarts = 1;
             }
 
             finished(clientId);
@@ -460,20 +466,20 @@
         }
 
         boolean isRunning = isRunning();
-        ArrayMap<IBinder, AppOpsService.InProgressStartOpEvent> events =
+        ArrayMap<IBinder, InProgressStartOpEvent> events =
                 isRunning ? mInProgressEvents : mPausedInProgressEvents;
 
         int numInProgressEvents = events.size();
         List<IBinder> binders = new ArrayList<>(events.keySet());
         for (int i = 0; i < numInProgressEvents; i++) {
-            AppOpsService.InProgressStartOpEvent event = events.get(binders.get(i));
+            InProgressStartOpEvent event = events.get(binders.get(i));
 
             if (event != null && event.getUidState() != newState) {
                 try {
                     // Remove all but one unfinished start count and then call finished() to
                     // remove start event object
-                    int numPreviousUnfinishedStarts = event.numUnfinishedStarts;
-                    event.numUnfinishedStarts = 1;
+                    int numPreviousUnfinishedStarts = event.mNumUnfinishedStarts;
+                    event.mNumUnfinishedStarts = 1;
                     AppOpsManager.OpEventProxyInfo proxy = event.getProxy();
 
                     finished(event.getClientId(), false);
@@ -492,9 +498,9 @@
                     }
 
                     events = isRunning ? mInProgressEvents : mPausedInProgressEvents;
-                    AppOpsService.InProgressStartOpEvent newEvent = events.get(binders.get(i));
+                    InProgressStartOpEvent newEvent = events.get(binders.get(i));
                     if (newEvent != null) {
-                        newEvent.numUnfinishedStarts += numPreviousUnfinishedStarts - 1;
+                        newEvent.mNumUnfinishedStarts += numPreviousUnfinishedStarts - 1;
                     }
                 } catch (RemoteException e) {
                     if (AppOpsService.DEBUG) {
@@ -546,7 +552,7 @@
     @SuppressWarnings("GuardedBy") // Lock is held on mAppOpsService
     public void add(@NonNull AttributedOp opToAdd) {
         if (opToAdd.isRunning() || opToAdd.isPaused()) {
-            ArrayMap<IBinder, AppOpsService.InProgressStartOpEvent> ignoredEvents =
+            ArrayMap<IBinder, InProgressStartOpEvent> ignoredEvents =
                     opToAdd.isRunning()
                             ? opToAdd.mInProgressEvents : opToAdd.mPausedInProgressEvents;
             Slog.w(AppOpsService.TAG, "Ignoring " + ignoredEvents.size() + " app-ops, running: "
@@ -554,7 +560,7 @@
 
             int numInProgressEvents = ignoredEvents.size();
             for (int i = 0; i < numInProgressEvents; i++) {
-                AppOpsService.InProgressStartOpEvent event = ignoredEvents.valueAt(i);
+                InProgressStartOpEvent event = ignoredEvents.valueAt(i);
 
                 event.finish();
                 mAppOpsService.mInProgressStartOpEventPool.release(event);
@@ -609,7 +615,7 @@
             }
 
             for (int i = 0; i < numInProgressEvents; i++) {
-                AppOpsService.InProgressStartOpEvent event = mInProgressEvents.valueAt(i);
+                InProgressStartOpEvent event = mInProgressEvents.valueAt(i);
 
                 accessEvents.append(makeKey(event.getUidState(), event.getFlags()),
                         new AppOpsManager.NoteOpEvent(event.getStartTime(),
@@ -623,4 +629,242 @@
         return new AppOpsManager.AttributedOpEntry(parent.op, isRunning(), accessEvents,
                 rejectEvents);
     }
+
+    /** A in progress startOp->finishOp event */
+    static final class InProgressStartOpEvent implements IBinder.DeathRecipient {
+        /** Wall clock time of startOp event (not monotonic) */
+        private long mStartTime;
+
+        /** Elapsed time since boot of startOp event */
+        private long mStartElapsedTime;
+
+        /** Id of the client that started the event */
+        private @NonNull IBinder mClientId;
+
+        /** The attribution tag for this operation */
+        private @Nullable String mAttributionTag;
+
+        /** To call when client dies */
+        private @NonNull Runnable mOnDeath;
+
+        /** uidstate used when calling startOp */
+        private @AppOpsManager.UidState int mUidState;
+
+        /** Proxy information of the startOp event */
+        private @Nullable AppOpsManager.OpEventProxyInfo mProxy;
+
+        /** Proxy flag information */
+        private @AppOpsManager.OpFlags int mFlags;
+
+        /** How many times the op was started but not finished yet */
+        int mNumUnfinishedStarts;
+
+        /** The attribution flags related to this event */
+        private @AppOpsManager.AttributionFlags int mAttributionFlags;
+
+        /** The id of the attribution chain this even is a part of */
+        private int mAttributionChainId;
+
+        /**
+         * Create a new {@link InProgressStartOpEvent}.
+         *
+         * @param startTime          The time {@link #startOperation} was called
+         * @param startElapsedTime   The elapsed time when {@link #startOperation} was called
+         * @param clientId           The client id of the caller of {@link #startOperation}
+         * @param attributionTag     The attribution tag for the operation.
+         * @param onDeath            The code to execute on client death
+         * @param uidState           The uidstate of the app {@link #startOperation} was called for
+         * @param attributionFlags   the attribution flags for this operation.
+         * @param attributionChainId the unique id of the attribution chain this op is a part of.
+         * @param proxy              The proxy information, if {@link #startProxyOperation} was
+         *                           called
+         * @param flags              The trusted/nontrusted/self flags.
+         * @throws RemoteException If the client is dying
+         */
+        InProgressStartOpEvent(long startTime, long startElapsedTime,
+                @NonNull IBinder clientId, @Nullable String attributionTag,
+                @NonNull Runnable onDeath, @AppOpsManager.UidState int uidState,
+                @Nullable AppOpsManager.OpEventProxyInfo proxy, @AppOpsManager.OpFlags int flags,
+                @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId)
+                throws RemoteException {
+            mStartTime = startTime;
+            mStartElapsedTime = startElapsedTime;
+            mClientId = clientId;
+            mAttributionTag = attributionTag;
+            mOnDeath = onDeath;
+            mUidState = uidState;
+            mProxy = proxy;
+            mFlags = flags;
+            mAttributionFlags = attributionFlags;
+            mAttributionChainId = attributionChainId;
+
+            clientId.linkToDeath(this, 0);
+        }
+
+        /** Clean up event */
+        public void finish() {
+            try {
+                mClientId.unlinkToDeath(this, 0);
+            } catch (NoSuchElementException e) {
+                // Either not linked, or already unlinked. Either way, nothing to do.
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            mOnDeath.run();
+        }
+
+        /**
+         * Reinit existing object with new state.
+         *
+         * @param startTime          The time {@link #startOperation} was called
+         * @param startElapsedTime   The elapsed time when {@link #startOperation} was called
+         * @param clientId           The client id of the caller of {@link #startOperation}
+         * @param attributionTag     The attribution tag for this operation.
+         * @param onDeath            The code to execute on client death
+         * @param uidState           The uidstate of the app {@link #startOperation} was called for
+         * @param flags              The flags relating to the proxy
+         * @param proxy              The proxy information, if {@link #startProxyOperation}
+         *                           was called
+         * @param attributionFlags   the attribution flags for this operation.
+         * @param attributionChainId the unique id of the attribution chain this op is a part of.
+         * @param proxyPool          The pool to release
+         *                           previous {@link AppOpsManager.OpEventProxyInfo} to
+         * @throws RemoteException If the client is dying
+         */
+        public void reinit(long startTime, long startElapsedTime, @NonNull IBinder clientId,
+                @Nullable String attributionTag, @NonNull Runnable onDeath,
+                @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags,
+                @Nullable AppOpsManager.OpEventProxyInfo proxy,
+                @AppOpsManager.AttributionFlags int attributionFlags,
+                int attributionChainId,
+                @NonNull Pools.Pool<AppOpsManager.OpEventProxyInfo> proxyPool
+        ) throws RemoteException {
+            mStartTime = startTime;
+            mStartElapsedTime = startElapsedTime;
+            mClientId = clientId;
+            mAttributionTag = attributionTag;
+            mOnDeath = onDeath;
+            mUidState = uidState;
+            mFlags = flags;
+
+            if (mProxy != null) {
+                proxyPool.release(mProxy);
+            }
+            mProxy = proxy;
+            mAttributionFlags = attributionFlags;
+            mAttributionChainId = attributionChainId;
+
+            clientId.linkToDeath(this, 0);
+        }
+
+        /** @return Wall clock time of startOp event */
+        public long getStartTime() {
+            return mStartTime;
+        }
+
+        /** @return Elapsed time since boot of startOp event */
+        public long getStartElapsedTime() {
+            return mStartElapsedTime;
+        }
+
+        /** @return Id of the client that started the event */
+        public @NonNull IBinder getClientId() {
+            return mClientId;
+        }
+
+        /** @return uidstate used when calling startOp */
+        public @AppOpsManager.UidState int getUidState() {
+            return mUidState;
+        }
+
+        /** @return proxy tag for the access */
+        public @Nullable AppOpsManager.OpEventProxyInfo getProxy() {
+            return mProxy;
+        }
+
+        /** @return flags used for the access */
+        public @AppOpsManager.OpFlags int getFlags() {
+            return mFlags;
+        }
+
+        /** @return attributoin flags used for the access */
+        public @AppOpsManager.AttributionFlags int getAttributionFlags() {
+            return mAttributionFlags;
+        }
+
+        /** @return attribution chain id for the access */
+        public int getAttributionChainId() {
+            return mAttributionChainId;
+        }
+
+        public void setStartTime(long startTime) {
+            mStartTime = startTime;
+        }
+
+        public void setStartElapsedTime(long startElapsedTime) {
+            mStartElapsedTime = startElapsedTime;
+        }
+    }
+
+    /**
+     * An unsynchronized pool of {@link InProgressStartOpEvent} objects.
+     */
+    static class InProgressStartOpEventPool extends Pools.SimplePool<InProgressStartOpEvent> {
+        private OpEventProxyInfoPool mOpEventProxyInfoPool;
+
+        InProgressStartOpEventPool(OpEventProxyInfoPool opEventProxyInfoPool,
+                int maxUnusedPooledObjects) {
+            super(maxUnusedPooledObjects);
+            this.mOpEventProxyInfoPool = opEventProxyInfoPool;
+        }
+
+        InProgressStartOpEvent acquire(long startTime, long elapsedTime, @NonNull IBinder clientId,
+                @Nullable String attributionTag, @NonNull Runnable onDeath, int proxyUid,
+                @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
+                @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags,
+                @AppOpsManager.AttributionFlags
+                        int attributionFlags, int attributionChainId) throws RemoteException {
+
+            InProgressStartOpEvent recycled = acquire();
+
+            AppOpsManager.OpEventProxyInfo proxyInfo = null;
+            if (proxyUid != Process.INVALID_UID) {
+                proxyInfo = mOpEventProxyInfoPool.acquire(proxyUid, proxyPackageName,
+                        proxyAttributionTag);
+            }
+
+            if (recycled != null) {
+                recycled.reinit(startTime, elapsedTime, clientId, attributionTag, onDeath,
+                        uidState, flags, proxyInfo, attributionFlags, attributionChainId,
+                        mOpEventProxyInfoPool);
+                return recycled;
+            }
+
+            return new InProgressStartOpEvent(startTime, elapsedTime, clientId, attributionTag,
+                    onDeath, uidState, proxyInfo, flags, attributionFlags, attributionChainId);
+        }
+    }
+
+    /**
+     * An unsynchronized pool of {@link AppOpsManager.OpEventProxyInfo} objects.
+     */
+    static class OpEventProxyInfoPool extends Pools.SimplePool<AppOpsManager.OpEventProxyInfo> {
+        OpEventProxyInfoPool(int maxUnusedPooledObjects) {
+            super(maxUnusedPooledObjects);
+        }
+
+        AppOpsManager.OpEventProxyInfo acquire(@IntRange(from = 0) int uid,
+                @Nullable String packageName,
+                @Nullable String attributionTag) {
+            AppOpsManager.OpEventProxyInfo recycled = acquire();
+            if (recycled != null) {
+                recycled.reinit(uid, packageName, attributionTag);
+                return recycled;
+            }
+
+            return new AppOpsManager.OpEventProxyInfo(uid, packageName, attributionTag);
+        }
+    }
 }