Merge "Lookup notification package with PM#getApplicationInfoAsUser"
diff --git a/Android.bp b/Android.bp
index 2d7e2d4..6b2fb0c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -213,6 +213,9 @@
         ":PacProcessor-aidl-sources",
         ":ProxyHandler-aidl-sources",
 
+        // AIDL from frameworks/base/native/
+        ":platform-compat-native-aidl",
+
         // AIDL sources from external directories
         ":dumpstate_aidl",
         ":framework_native_aidl",
diff --git a/core/java/android/app/usage/EventList.java b/core/java/android/app/usage/EventList.java
index 8c03405..afdcbe6 100644
--- a/core/java/android/app/usage/EventList.java
+++ b/core/java/android/app/usage/EventList.java
@@ -79,6 +79,21 @@
     }
 
     /**
+     * Removes the event at the given index.
+     *
+     * @param index the index of the event to remove
+     * @return the event removed, or {@code null} if the index was out of bounds
+     */
+    public UsageEvents.Event remove(int index) {
+        try {
+            return mEvents.remove(index);
+        } catch (IndexOutOfBoundsException e) {
+            // catch and handle the exception here instead of throwing it to the client
+            return null;
+        }
+    }
+
+    /**
      * Finds the index of the first event whose timestamp is greater than or equal to the given
      * timestamp.
      *
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 5dbca12..4bf9c04 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -312,6 +312,11 @@
         public static final int VALID_FLAG_BITS = FLAG_IS_PACKAGE_INSTANT_APP;
 
         /**
+         * @hide
+         */
+        private static final int UNASSIGNED_TOKEN = -1;
+
+        /**
          * {@hide}
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -320,12 +325,22 @@
         /**
          * {@hide}
          */
+        public int mPackageToken = UNASSIGNED_TOKEN;
+
+        /**
+         * {@hide}
+         */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         public String mClass;
 
         /**
          * {@hide}
          */
+        public int mClassToken = UNASSIGNED_TOKEN;
+
+        /**
+         * {@hide}
+         */
         public int mInstanceId;
 
         /**
@@ -336,11 +351,21 @@
         /**
          * {@hide}
          */
+        public int mTaskRootPackageToken = UNASSIGNED_TOKEN;
+
+        /**
+         * {@hide}
+         */
         public String mTaskRootClass;
 
         /**
          * {@hide}
          */
+        public int mTaskRootClassToken = UNASSIGNED_TOKEN;
+
+        /**
+         * {@hide}
+         */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         public long mTimeStamp;
 
@@ -365,6 +390,11 @@
         public String mShortcutId;
 
         /**
+         * {@hide}
+         */
+        public int mShortcutIdToken = UNASSIGNED_TOKEN;
+
+        /**
          * Action type passed to ChooserActivity
          * Only present for {@link #CHOOSER_ACTION} event types.
          * {@hide}
@@ -401,6 +431,11 @@
          */
         public String mNotificationChannelId;
 
+        /**
+         * {@hide}
+         */
+        public int mNotificationChannelIdToken = UNASSIGNED_TOKEN;
+
         /** @hide */
         @EventFlags
         public int mFlags;
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index 2c021cc..9d43dd3 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -35,6 +35,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArrayMap;
+import android.util.SparseArray;
 import android.util.SparseIntArray;
 
 /**
@@ -52,6 +53,11 @@
     /**
      * {@hide}
      */
+    public int mPackageToken = -1;
+
+    /**
+     * {@hide}
+     */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public long mBeginTimeStamp;
 
@@ -143,6 +149,11 @@
     /**
      * {@hide}
      */
+    public SparseArray<SparseIntArray> mChooserCountsObfuscated = new SparseArray<>();
+
+    /**
+     * {@hide}
+     */
     public UsageStats() {
     }
 
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index cb61863..9a9fc89 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -75,6 +75,7 @@
 import android.view.textclassifier.TextClassificationManager;
 
 import com.android.internal.compat.IPlatformCompat;
+import com.android.internal.compat.IPlatformCompatNative;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -3255,6 +3256,7 @@
             //@hide ROLE_CONTROLLER_SERVICE,
             CAMERA_SERVICE,
             //@hide: PLATFORM_COMPAT_SERVICE,
+            //@hide: PLATFORM_COMPAT_NATIVE_SERVICE,
             PRINT_SERVICE,
             CONSUMER_IR_SERVICE,
             //@hide: TRUST_SERVICE,
@@ -4632,6 +4634,14 @@
     public static final String PLATFORM_COMPAT_SERVICE = "platform_compat";
 
     /**
+     * Use with {@link android.os.ServiceManager.getService()} to retrieve a
+     * {@link IPlatformCompatNative} IBinder for native code communicating with the platform compat
+     * service.
+     * @hide
+     */
+    public static final String PLATFORM_COMPAT_NATIVE_SERVICE = "platform_compat_native";
+
+    /**
      * Service to capture a bugreport.
      * @see #getSystemService(String)
      * @see android.os.BugreportManager
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index ccfa184..1e88ce7 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -312,7 +312,7 @@
      */
     public boolean supportsSwitchToByUser() {
         // Hide the system user when it does not represent a human user.
-        boolean hideSystemUser = UserManager.isSplitSystemUser();
+        boolean hideSystemUser = UserManager.isHeadlessSystemUserMode();
         return (!hideSystemUser || id != UserHandle.USER_SYSTEM) && supportsSwitchTo();
     }
 
diff --git a/core/java/android/hardware/usb/UsbDevice.java b/core/java/android/hardware/usb/UsbDevice.java
index 11f4ffb..ee2e262 100644
--- a/core/java/android/hardware/usb/UsbDevice.java
+++ b/core/java/android/hardware/usb/UsbDevice.java
@@ -63,6 +63,8 @@
     private final boolean mHasAudioPlayback;
     private final boolean mHasAudioCapture;
     private final boolean mHasMidi;
+    private final boolean mHasVideoPlayback;
+    private final boolean mHasVideoCapture;
 
     /** All interfaces on the device. Initialized on first call to getInterfaceList */
     @UnsupportedAppUsage
@@ -77,7 +79,8 @@
             int protocol, @Nullable String manufacturerName, @Nullable String productName,
             @NonNull String version, @NonNull UsbConfiguration[] configurations,
             @NonNull IUsbSerialReader serialNumberReader,
-            boolean hasAudioPlayback, boolean hasAudioCapture, boolean hasMidi) {
+            boolean hasAudioPlayback, boolean hasAudioCapture, boolean hasMidi,
+            boolean hasVideoPlayback, boolean hasVideoCapture) {
         mName = Preconditions.checkNotNull(name);
         mVendorId = vendorId;
         mProductId = productId;
@@ -92,6 +95,8 @@
         mHasAudioPlayback = hasAudioPlayback;
         mHasAudioCapture = hasAudioCapture;
         mHasMidi = hasMidi;
+        mHasVideoPlayback = hasVideoPlayback;
+        mHasVideoCapture = hasVideoCapture;
 
         // Make sure the binder belongs to the system
         if (ActivityThread.isSystem()) {
@@ -236,6 +241,16 @@
         return mHasMidi;
     }
 
+    /** @hide */
+    public boolean getHasVideoPlayback() {
+        return mHasVideoPlayback;
+    }
+
+    /** @hide */
+    public boolean getHasVideoCapture() {
+        return mHasVideoCapture;
+    }
+
     /**
      * Returns the {@link UsbConfiguration} at the given index.
      *
@@ -316,6 +331,8 @@
                 + ", mHasAudioPlayback=" + mHasAudioPlayback
                 + ", mHasAudioCapture=" + mHasAudioCapture
                 + ", mHasMidi=" + mHasMidi
+                + ", mHasVideoCapture=" + mHasVideoCapture
+                + ", mHasVideoPlayback=" + mHasVideoPlayback
                 + ", mConfigurations=[");
         for (int i = 0; i < mConfigurations.length; i++) {
             builder.append("\n");
@@ -345,10 +362,12 @@
             boolean hasAudioPlayback = in.readInt() == 1;
             boolean hasAudioCapture = in.readInt() == 1;
             boolean hasMidi = in.readInt() == 1;
-
+            boolean hasVideoPlayback = in.readInt() == 1;
+            boolean hasVideoCapture = in.readInt() == 1;
             UsbDevice device = new UsbDevice(name, vendorId, productId, clasz, subClass, protocol,
                     manufacturerName, productName, version, configurations, serialNumberReader,
-                    hasAudioPlayback, hasAudioCapture, hasMidi);
+                    hasAudioPlayback, hasAudioCapture, hasMidi,
+                    hasVideoPlayback, hasVideoCapture);
 
             return device;
         }
@@ -377,6 +396,8 @@
         parcel.writeInt(mHasAudioPlayback ? 1 : 0);
         parcel.writeInt(mHasAudioCapture ? 1 : 0);
         parcel.writeInt(mHasMidi ? 1 : 0);
+        parcel.writeInt(mHasVideoPlayback ? 1 : 0);
+        parcel.writeInt(mHasVideoCapture ? 1 : 0);
     }
 
     public static int getDeviceId(String name) {
@@ -407,6 +428,8 @@
         private final boolean mHasAudioPlayback;
         private final boolean mHasAudioCapture;
         private final boolean mHasMidi;
+        private final boolean mHasVideoPlayback;
+        private final boolean mHasVideoCapture;
 
         // Temporary storage for serial number. Serial number reader need to be wrapped in a
         // IUsbSerialReader as they might be used as PII.
@@ -416,7 +439,8 @@
                 int protocol, @Nullable String manufacturerName, @Nullable String productName,
                 @NonNull String version, @NonNull UsbConfiguration[] configurations,
                 @Nullable String serialNumber,
-                boolean hasAudioPlayback, boolean hasAudioCapture, boolean hasMidi) {
+                boolean hasAudioPlayback, boolean hasAudioCapture, boolean hasMidi,
+                boolean hasVideoPlayback, boolean hasVideoCapture) {
             mName = Preconditions.checkNotNull(name);
             mVendorId = vendorId;
             mProductId = productId;
@@ -431,6 +455,8 @@
             mHasAudioPlayback = hasAudioPlayback;
             mHasAudioCapture = hasAudioCapture;
             mHasMidi = hasMidi;
+            mHasVideoPlayback = hasVideoPlayback;
+            mHasVideoCapture = hasVideoCapture;
         }
 
         /**
@@ -443,7 +469,8 @@
         public UsbDevice build(@NonNull IUsbSerialReader serialReader) {
             return new UsbDevice(mName, mVendorId, mProductId, mClass, mSubclass, mProtocol,
                     mManufacturerName, mProductName, mVersion, mConfigurations, serialReader,
-                    mHasAudioPlayback, mHasAudioCapture, mHasMidi);
+                    mHasAudioPlayback, mHasAudioCapture, mHasMidi,
+                    mHasVideoPlayback, mHasVideoCapture);
         }
     }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 800c15c..bdc7834 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7319,6 +7319,19 @@
         public static final String SILENCE_CALL_TOUCH_COUNT = "silence_call_touch_count";
 
         /**
+         * Number of successful "Motion Sense" tap gestures to pause media.
+         * @hide
+         */
+        public static final String AWARE_TAP_PAUSE_GESTURE_COUNT = "aware_tap_pause_gesture_count";
+
+        /**
+         * Number of touch interactions to pause media when a "Motion Sense" gesture could
+         * have been used.
+         * @hide
+         */
+        public static final String AWARE_TAP_PAUSE_TOUCH_COUNT = "aware_tap_pause_touch_count";
+
+        /**
          * The current night mode that has been selected by the user.  Owned
          * and controlled by UiModeManagerService.  Constants are as per
          * UiModeManager.
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index d5528de..4ea574d 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -211,6 +211,11 @@
         optional SettingProto silence_timer_touch_count = 11 [ (android.privacy).dest =
             DEST_AUTOMATIC ];
         optional SettingProto skip_touch_count = 12 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto aware_tap_pause_gesture_count = 13 [
+            (android.privacy).dest =
+            DEST_AUTOMATIC ];
+        optional SettingProto aware_tap_pause_touch_count = 14 [ (android.privacy).dest =
+            DEST_AUTOMATIC ];
     }
     optional Gesture gesture = 74;
 
diff --git a/core/proto/android/server/usagestatsservice.proto b/core/proto/android/server/usagestatsservice.proto
index 75f265e..f26eefa 100644
--- a/core/proto/android/server/usagestatsservice.proto
+++ b/core/proto/android/server/usagestatsservice.proto
@@ -114,6 +114,4 @@
   repeated UsageStats packages = 20;
   repeated Configuration configurations = 21;
   repeated Event event_log = 22;
-
-  repeated Event pending_events = 23; // TODO: move to usagestatsservice_v2.proto
 }
diff --git a/core/proto/android/server/usagestatsservice_v2.proto b/core/proto/android/server/usagestatsservice_v2.proto
new file mode 100644
index 0000000..a28fcf3
--- /dev/null
+++ b/core/proto/android/server/usagestatsservice_v2.proto
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package com.android.server.usage;
+import "frameworks/base/core/proto/android/content/configuration.proto";
+import "frameworks/base/core/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+
+/**
+ * Obfuscated version of android.service.IntervalStatsProto (usagestatsservice.proto).
+ */
+message IntervalStatsObfuscatedProto {
+
+  message CountAndTime {
+    optional int32 count = 1;
+    optional int64 time_ms = 2;
+  }
+
+  // Stores the relevant information an IntervalStats will have about a Configuration
+  message Configuration {
+    optional .android.content.ConfigurationProto config = 1;
+    optional int64 last_time_active_ms = 2;
+    optional int64 total_time_active_ms = 3;
+    optional int32 count = 4;
+    optional bool active = 5;
+  }
+
+  // The following fields contain supplemental data used to build IntervalStats.
+  optional int64 end_time_ms = 1;
+  optional int32 major_version = 2;
+  optional int32 minor_version = 3;
+
+  // The following fields contain aggregated usage stats data
+  optional CountAndTime interactive = 10;
+  optional CountAndTime non_interactive = 11;
+  optional CountAndTime keyguard_shown = 12;
+  optional CountAndTime keyguard_hidden = 13;
+
+  // The following fields contain listed usage stats data
+  repeated UsageStatsObfuscatedProto packages = 20;
+  repeated Configuration configurations = 21;
+  repeated EventObfuscatedProto event_log = 22;
+  // The following field is only used to persist the reported events before a user unlock
+  repeated PendingEventProto pending_events = 23;
+}
+
+/**
+ * Stores the relevant information from an obfuscated UsageStats.
+ */
+message UsageStatsObfuscatedProto {
+  message ChooserAction {
+    message CategoryCount {
+      optional int32 category_token = 1;
+      optional int32 count = 2;
+    }
+    optional int32 action_token = 1;
+    repeated CategoryCount counts = 2;
+  }
+  optional int32 package_token = 1;
+  optional int64 last_time_active_ms = 3;
+  optional int64 total_time_active_ms = 4;
+  optional int32 last_event = 5;
+  optional int32 app_launch_count = 6;
+  repeated ChooserAction chooser_actions = 7;
+  optional int64 last_time_service_used_ms = 8;
+  optional int64 total_time_service_used_ms = 9;
+  optional int64 last_time_visible_ms = 10;
+  optional int64 total_time_visible_ms = 11;
+}
+
+/**
+ * Stores the relevant information from an obfuscated Event.
+ */
+message EventObfuscatedProto {
+  optional int32 package_token = 1;
+  optional int32 class_token = 2;
+  optional int64 time_ms = 3;
+  optional int32 flags = 4;
+  optional int32 type = 5;
+  optional .android.content.ConfigurationProto config = 6;
+  optional int32 shortcut_id_token = 7;
+  optional int32 standby_bucket = 8;
+  optional int32 notification_channel_id_token = 9;
+  optional int32 instance_id = 10;
+  optional int32 task_root_package_token = 11;
+  optional int32 task_root_class_token = 12;
+}
+
+/**
+ * This message stores all of the fields in an Event object as strings instead of tokens.
+ */
+message PendingEventProto {
+  optional string package_name = 1;
+  optional string class_name = 2;
+  optional int64 time_ms = 3;
+  optional int32 flags = 4;
+  optional int32 type = 5;
+  optional .android.content.ConfigurationProto config = 6;
+  optional string shortcut_id = 7;
+  optional int32 standby_bucket = 8;
+  optional string notification_channel_id = 9;
+  optional int32 instance_id = 10;
+  optional string task_root_package = 11;
+  optional string task_root_class = 12;
+}
+
+/**
+ * A proto message representing the obfuscated tokens mappings for Usage Stats.
+ */
+message ObfuscatedPackagesProto {
+  message PackagesMap {
+    optional int32 package_token = 1;
+    // The list of strings for each package where their indices are the token
+    repeated string strings = 2;
+  }
+
+  optional int32 counter = 1;
+  // Stores the mappings for every package
+  repeated PackagesMap packages_map = 2;
+}
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 7c1af4a..91297b0 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -110,3 +110,36 @@
     symbol_file: "libandroid_net.map.txt",
     unversioned: true,
 }
+
+
+// Aidl library for platform compat.
+cc_library_shared {
+    name: "lib-platform-compat-native-api",
+    cflags: [
+            "-Wall",
+            "-Werror",
+            "-Wno-missing-field-initializers",
+            "-Wno-unused-variable",
+            "-Wunused-parameter",
+        ],
+    shared_libs: [
+        "libbinder",
+         "libutils",
+    ],
+    aidl: {
+        local_include_dirs: ["aidl"],
+        export_aidl_headers: true,
+    },
+    srcs: [
+         ":platform-compat-native-aidl",
+    ],
+    export_include_dirs: ["aidl"],
+}
+
+filegroup {
+    name: "platform-compat-native-aidl",
+    srcs: [
+        "aidl/com/android/internal/compat/IPlatformCompatNative.aidl",
+    ],
+    path: "aidl",
+}
\ No newline at end of file
diff --git a/native/android/aidl/com/android/internal/compat/IPlatformCompatNative.aidl b/native/android/aidl/com/android/internal/compat/IPlatformCompatNative.aidl
new file mode 100644
index 0000000..c022388
--- /dev/null
+++ b/native/android/aidl/com/android/internal/compat/IPlatformCompatNative.aidl
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.compat;
+
+/**
+ * Platform native private API for talking with the PlatformCompat service.
+ *
+ * <p> Should be used for gating and logging from non-app processes running cpp code.
+ * For app processes please use android.compat.Compatibility API.
+ *
+ * {@hide}
+ */
+interface IPlatformCompatNative
+{
+    /**
+     * Reports that a compatibility change is affecting an app process now.
+     *
+     * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, String)},
+     * you do not need to call this API directly. The change will be reported for you.
+     *
+     * @param changeId    The ID of the compatibility change taking effect.
+     * @param packageName The package name of the app in question.
+     */
+     void reportChangeByPackageName(long changeId, @utf8InCpp String packageName);
+
+    /**
+     * Reports that a compatibility change is affecting an app process now.
+     *
+     * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, int)},
+     * you do not need to call this API directly. The change will be reported for you.
+     *
+     * @param changeId The ID of the compatibility change taking effect.
+     * @param uid      The UID of the app in question.
+     */
+    void reportChangeByUid(long changeId, int uid);
+
+    /**
+     * Query if a given compatibility change is enabled for an app process. This method should
+     * be called when implementing functionality on behalf of the affected app.
+     *
+     * <p>Returns {@code true} if there is no installed package by the provided package name.
+     *
+     * <p>If this method returns {@code true}, the calling code should implement the compatibility
+     * change, resulting in differing behaviour compared to earlier releases. If this method
+     * returns
+     * {@code false}, the calling code should behave as it did in earlier releases.
+     *
+     * <p>It will also report the change as {@link #reportChange(long, String)} would, so there is
+     * no need to call that method directly.
+     *
+     * @param changeId    The ID of the compatibility change in question.
+     * @param packageName The package name of the app in question.
+     * @return {@code true} if the change is enabled for the current app.
+     */
+    boolean isChangeEnabledByPackageName(long changeId, @utf8InCpp String packageName);
+
+    /**
+     * Query if a given compatibility change is enabled for an app process. This method should
+     * be called when implementing functionality on behalf of the affected app.
+     *
+     * <p> Returns {@code true} if there are no installed packages for the required UID, or if the
+     * change is enabled for ALL of the installed packages associated with the provided UID. Please
+     * use a more specific API if you want a different behaviour for multi-package UIDs.
+     *
+     * <p>If this method returns {@code true}, the calling code should implement the compatibility
+     * change, resulting in differing behaviour compared to earlier releases. If this method
+     * returns {@code false}, the calling code should behave as it did in earlier releases.
+     *
+     * <p>It will also report the change as {@link #reportChange(long, int)} would, so there is
+     * no need to call that method directly.
+     *
+     * @param changeId The ID of the compatibility change in question.
+     * @param uid      The UID of the app in question.
+     * @return {@code true} if the change is enabled for the current app.
+     */
+    boolean isChangeEnabledByUid(long changeId, int uid);
+}
\ No newline at end of file
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index beea1ac..3014452 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -55,7 +55,6 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 /**
  * Displays a GridLayout with icons for the users in the system to allow switching between users.
@@ -109,21 +108,16 @@
      * @return the adapter
      */
     public void buildAdapter() {
-        List<UserRecord> userRecords = createUserRecords(getAllUsers());
+        List<UserRecord> userRecords = createUserRecords(getUsersForUserGrid());
         mAdapter = new UserAdapter(mContext, userRecords);
         super.setAdapter(mAdapter);
     }
 
-    private List<UserInfo> getAllUsers() {
-        Stream<UserInfo> userListStream =
-                mUserManager.getUsers(/* excludeDying= */ true).stream();
-
-        if (UserManager.isHeadlessSystemUserMode()) {
-            userListStream =
-                    userListStream.filter(userInfo -> userInfo.id != UserHandle.USER_SYSTEM);
-        }
-        userListStream = userListStream.filter(userInfo -> userInfo.supportsSwitchToByUser());
-        return userListStream.collect(Collectors.toList());
+    private List<UserInfo> getUsersForUserGrid() {
+        return mUserManager.getUsers(/* excludeDying= */ true)
+                .stream()
+                .filter(UserInfo::supportsSwitchToByUser)
+                .collect(Collectors.toList());
     }
 
     private List<UserRecord> createUserRecords(List<UserInfo> userInfoList) {
@@ -189,7 +183,7 @@
 
     private void onUsersUpdate() {
         mAdapter.clearUsers();
-        mAdapter.updateUsers(createUserRecords(getAllUsers()));
+        mAdapter.updateUsers(createUserRecords(getUsersForUserGrid()));
         mAdapter.notifyDataSetChanged();
     }
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index d0a63f0..22c7c7a 100644
--- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -143,6 +143,7 @@
     private boolean mHovering;
     private int mCurrentlyDisplayingGroupId;
     private boolean mShowing;
+    private boolean mDismissing;
     private boolean mExpanded;
     private View mExpandIcon;
     private final ServiceConnection mServiceConnection = new ServiceConnection() {
@@ -244,6 +245,7 @@
 
         mHovering = false;
         mShowing = false;
+        mDismissing = false;
         mExpanded = false;
         mWindow = mDialog.getWindow();
         mWindow.requestFeature(Window.FEATURE_NO_TITLE);
@@ -335,14 +337,11 @@
 
         mHandler.removeMessages(H.DISMISS);
         mHandler.removeMessages(H.SHOW);
-        if (!mShowing) {
+        if (!mShowing || mDismissing) {
             return;
         }
 
-        mListView.animate().cancel();
-
-        mListView.setTranslationY(0);
-        mListView.setAlpha(1);
+        mDismissing = true;
         mListView.animate()
                 .alpha(0)
                 .translationY(-mListView.getHeight())
@@ -354,7 +353,7 @@
                     }
                     mDialog.dismiss();
                     mShowing = false;
-                    mShowing = false;
+                    mDismissing = false;
                     // if mExpandIcon is null that means user never clicked on the expanded arrow
                     // which implies that the dialog is still not expanded. In that case we do
                     // not want to reset the state
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 8c97057..146f30d 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -152,6 +152,8 @@
         Settings.Secure.SILENCE_TIMER_TOUCH_COUNT,
         Settings.Secure.DARK_MODE_DIALOG_SEEN,
         Settings.Secure.GLOBAL_ACTIONS_PANEL_ENABLED,
-        Settings.Secure.AWARE_LOCK_ENABLED
+        Settings.Secure.AWARE_LOCK_ENABLED,
+        Settings.Secure.AWARE_TAP_PAUSE_GESTURE_COUNT,
+        Settings.Secure.AWARE_TAP_PAUSE_TOUCH_COUNT
     };
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index ef67bbd..f7fc0c5 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -220,6 +220,8 @@
         VALIDATORS.put(Secure.SILENCE_ALARMS_TOUCH_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.SILENCE_TIMER_TOUCH_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.SILENCE_CALL_TOUCH_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
+        VALIDATORS.put(Secure.AWARE_TAP_PAUSE_GESTURE_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
+        VALIDATORS.put(Secure.AWARE_TAP_PAUSE_TOUCH_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.ODI_CAPTIONS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.DARK_MODE_DIALOG_SEEN, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.UI_NIGHT_MODE, new InclusiveIntegerRangeValidator(0, 2));
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 2ce4e97..6bd26bf 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2047,6 +2047,12 @@
         dumpSetting(s, p,
                 Settings.Secure.SKIP_TOUCH_COUNT,
                 SecureSettingsProto.Gesture.SKIP_TOUCH_COUNT);
+        dumpSetting(s, p,
+                Settings.Secure.AWARE_TAP_PAUSE_GESTURE_COUNT,
+                SecureSettingsProto.Gesture.AWARE_TAP_PAUSE_GESTURE_COUNT);
+        dumpSetting(s, p,
+                Settings.Secure.AWARE_TAP_PAUSE_TOUCH_COUNT,
+                SecureSettingsProto.Gesture.AWARE_TAP_PAUSE_TOUCH_COUNT);
         p.end(gestureToken);
 
         dumpSetting(s, p,
diff --git a/services/art-profile b/services/art-profile
index cbc4627..8b911a2 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -20824,3 +20824,395 @@
 Lcom/google/android/startop/iorap/IorapForwardingService$AppLaunchObserver;
 Lcom/google/android/startop/iorap/IorapForwardingService$BinderConnectionHandler;
 Lcom/google/android/startop/iorap/IorapForwardingService;
+HPLandroid/hardware/health/V1_0/HealthInfo;-><init>()V
+HPLandroid/hardware/health/V2_0/DiskStats;-><init>()V
+HPLcom/android/server/-$$Lambda$GnssManagerService$a17GVVAgEci0VYD4EMvKwuPLhdQ;->onUidImportance(II)V
+HPLcom/android/server/-$$Lambda$GnssManagerService$mZAgy7PA5q3tB1aq7tHsX4xM14E;->run()V
+HPLcom/android/server/-$$Lambda$LocationManagerService$GVLGDgL1Vk3AKo-zMjRmo3-OLpQ;->run()V
+HPLcom/android/server/-$$Lambda$LocationManagerService$tHPgS5c0niUhGntiX8gOnWrZpg8;->onUidImportance(II)V
+HPLcom/android/server/accessibility/AccessibilityManagerService$Client;-><init>(Lcom/android/server/accessibility/AccessibilityManagerService;Landroid/view/accessibility/IAccessibilityManagerClient;ILcom/android/server/accessibility/AccessibilityUserState;)V
+HPLcom/android/server/accessibility/AccessibilitySecurityPolicy;->resolveCallingUserIdEnforcingPermissionsLocked(I)I
+HPLcom/android/server/am/-$$Lambda$OomAdjuster$OVkqAAacT5-taN3pgDzyZj3Ymvk;->handleMessage(Landroid/os/Message;)Z
+HPLcom/android/server/am/-$$Lambda$ProcessList$vtq7LF5jIHO4t5NE03c8g7BT7Jc;->run()V
+HPLcom/android/server/am/ActivityManagerService$2;->onActivityLaunched([BI)V
+HPLcom/android/server/am/ActivityManagerService$4;->isPackageForFilter(Ljava/lang/String;Landroid/content/IntentFilter;)Z
+HPLcom/android/server/am/ActivityManagerService$4;->newResult(Landroid/content/IntentFilter;II)Ljava/lang/Object;
+HPLcom/android/server/am/ActivityManagerService$PidMap;->put(Lcom/android/server/am/ProcessRecord;)V
+HPLcom/android/server/am/ActivityManagerService;->trimApplications(Ljava/lang/String;)V
+HPLcom/android/server/am/ActivityManagerService;->updateOomAdjLocked(Lcom/android/server/am/ProcessRecord;Ljava/lang/String;)V
+HPLcom/android/server/am/ActivityManagerService;->updateOomAdjLocked(Lcom/android/server/am/ProcessRecord;ZLjava/lang/String;)Z
+HPLcom/android/server/am/HostingRecord;->getName()Ljava/lang/String;
+HPLcom/android/server/am/HostingRecord;->getType()Ljava/lang/String;
+HPLcom/android/server/am/OomAdjuster;->setAttachingSchedGroupLocked(Lcom/android/server/am/ProcessRecord;)V
+HPLcom/android/server/am/ProcessList;->startProcessLocked(Lcom/android/server/am/HostingRecord;Ljava/lang/String;Lcom/android/server/am/ProcessRecord;I[IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;J)Z
+HPLcom/android/server/am/ProcessRecord;->computeOomAdjFromActivitiesIfNecessary(Lcom/android/server/am/OomAdjuster$ComputeOomAdjWindowCallback;IZIIIII)V
+HPLcom/android/server/appop/AppOpsService$FeatureOp;->started(JII)V
+HPLcom/android/server/appop/AppOpsService$FeatureOp;->updateProxyState(JILjava/lang/String;Ljava/lang/String;)V
+HPLcom/android/server/appop/AppOpsService;->noteOperation(IILjava/lang/String;Ljava/lang/String;)I
+HPLcom/android/server/appop/AudioRestrictionManager;->checkAudioOperation(IIILjava/lang/String;)I
+HPLcom/android/server/appprediction/-$$Lambda$AppPredictionManagerService$PredictionManagerServiceStub$4yDhFef-19aMlJ-Y7O6RdjSAvnk;-><init>(Landroid/app/prediction/AppPredictionSessionId;Landroid/app/prediction/AppTargetEvent;)V
+HPLcom/android/server/appprediction/-$$Lambda$RemoteAppPredictionService$qroIh2ewx0BLP-J9XIAX2CaX8J4;-><init>(Landroid/app/prediction/AppPredictionSessionId;Landroid/app/prediction/AppTargetEvent;)V
+HPLcom/android/server/appprediction/AppPredictionManagerService$PredictionManagerServiceStub;->runForUserLocked(Ljava/lang/String;Ljava/util/function/Consumer;)V
+HPLcom/android/server/appprediction/RemoteAppPredictionService;->lambda$notifyAppTargetEvent$1(Landroid/app/prediction/AppPredictionSessionId;Landroid/app/prediction/AppTargetEvent;Landroid/service/appprediction/IPredictionService;)V
+HPLcom/android/server/audio/SoundEffectsHelper$SfxHandler;->handleMessage(Landroid/os/Message;)V
+HPLcom/android/server/autofill/AutofillManagerService$AugmentedAutofillState;->injectAugmentedAutofillInfo(Landroid/content/AutofillOptions;ILjava/lang/String;)V
+HPLcom/android/server/autofill/AutofillManagerService$LocalService;->injectDisableAppInfo(Landroid/content/AutofillOptions;ILjava/lang/String;)V
+HPLcom/android/server/compat/CompatConfig;->getDisabledChanges(Landroid/content/pm/ApplicationInfo;)[J
+HPLcom/android/server/compat/CompatConfig;->get()Lcom/android/server/compat/CompatConfig;
+HPLcom/android/server/compat/PlatformCompat;->resetReporting(Landroid/content/pm/ApplicationInfo;)V
+HPLcom/android/server/contentcapture/ContentCaptureManagerService$GlobalContentCaptureOptions;->getOptions(ILjava/lang/String;)Landroid/content/ContentCaptureOptions;
+HPLcom/android/server/input/InputManagerService;->onPointerDownOutsideFocus(Landroid/os/IBinder;)V
+HPLcom/android/server/input/InputManagerService;->registerInputChannel(Landroid/view/InputChannel;)V
+HPLcom/android/server/inputmethod/InputMethodManagerService;->getActivityViewToScreenMatrixLocked(II)Landroid/graphics/Matrix;
+HPLcom/android/server/media/projection/MediaProjectionManagerService$1;->onForegroundActivitiesChanged(IIZ)V
+HPLcom/android/server/net/NetworkPolicyManagerService;->updateRulesForPowerRestrictionsULInner(II)I
+HPLcom/android/server/pm/permission/PermissionManagerService;->checkSingleUidPermissionInternal(ILjava/lang/String;)Z
+HPLcom/android/server/policy/PermissionPolicyService$Internal;->checkStartActivity(Landroid/content/Intent;ILjava/lang/String;)Z
+HPLcom/android/server/soundtrigger/SoundTriggerHelper;->computeRecognitionRequestedLocked()Z
+HPLcom/android/server/soundtrigger/SoundTriggerHelper;->isRecognitionAllowed()Z
+HPLcom/android/server/statusbar/-$$Lambda$StatusBarManagerService$dQguzfF4tEgBOj3Pr8MpGRN8HT0;->run()V
+HPLcom/android/server/statusbar/-$$Lambda$StatusBarManagerService$u5u_W7qW5cMnzk9Qhp_oReST4Dc;->run()V
+HPLcom/android/server/statusbar/StatusBarManagerService;->setImeWindowStatus(ILandroid/os/IBinder;IIZZ)V
+HPLcom/android/server/usage/UsageStatsService;->reportEventOrAddToQueue(ILandroid/app/usage/UsageEvents$Event;)V
+HPLcom/android/server/usage/UserUsageStatsService;->checkAndGetTimeLocked()J
+HPLcom/android/server/wm/-$$Lambda$1Hjf_Nn5x4aIy9rIBTwVrtrzWFA;->apply(Ljava/lang/Object;)Ljava/lang/Object;
+HPLcom/android/server/wm/-$$Lambda$9vBfnQOmNnsc9WU80IIatZHQGKc;->get()Ljava/lang/Object;
+HPLcom/android/server/wm/-$$Lambda$ActivityRecord$BGON-BKR54yaxY8PHFXNV2xpxCM;->accept(Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$ActivityRecord$tt99EJHW_Nk5qgU9galJBIm5wXg;->run()V
+HPLcom/android/server/wm/-$$Lambda$ActivityRecord$YSVwd546vKWMiMYy7MFzg1qRiio;-><init>(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/-$$Lambda$ActivityRecord$YY5kCNb4uWg5W_2lbH3ZOqirP1g;->apply(Ljava/lang/Object;)Z
+HPLcom/android/server/wm/-$$Lambda$DisplayContent$a4EkCBfpZNIl1xfYgm2ktgndF8w;->apply(Ljava/lang/Object;)Z
+HPLcom/android/server/wm/-$$Lambda$DisplayContent$D0QJUvhaQkGgoMtOmjw5foY9F8M;->accept(Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$DisplayContent$eJsj3GR1HdCnOJrZ8_oaLP52jg0;->accept(Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$DisplayContent$SeHNTr4WUVpGmQniHULUi1ST7k8;->accept(Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$DisplayContent$sYPOy6TL-QiWuU_jcEHYn4HeFnQ;->accept(Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$DisplayPolicy$3MnyIKSHFLqhfUifWEQPNp_-J6A;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$DisplayPolicy$IOyP8YVRG92tn9u1muYWZgBbgc0;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$DisplayPolicy$J8sIwXJvltUaPM3jEGO948Bx9ig;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$DisplayPolicy$LkHee4mchNXMwNt7HLgsMzHofeE;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$DisplayPolicy$QDPgWUhyEOraWnf6a-u4mTBttdw;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$DisplayPolicy$qQY9m_Itua9TDy-Nk3zzDxvjEwE;->run()V
+HPLcom/android/server/wm/-$$Lambda$LaunchObserverRegistryImpl$QcawcFcJtEX4EhYptq_Vb4j368Y;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$LaunchObserverRegistryImpl$veRn_GhgLZLlOHOJ0ZYT6KcfYqo;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$qMFJUmfG50ZSjk7Tac67xBia0d4;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$RemoteAnimationController$74uuXaM2TqjkzYi0b8LqJdbycxA;-><init>(Lcom/android/server/wm/RemoteAnimationController;[Landroid/view/RemoteAnimationTarget;[Landroid/view/RemoteAnimationTarget;)V
+HPLcom/android/server/wm/-$$Lambda$RemoteAnimationController$74uuXaM2TqjkzYi0b8LqJdbycxA;->run()V
+HPLcom/android/server/wm/-$$Lambda$RootWindowContainer$7XcqfZjQLAbjpIyed3iDnVtZro4;->accept(Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$SurfaceAnimator$M9kRDTUpVS03LTqe-QLQz3DnMhk;-><init>(Lcom/android/server/wm/SurfaceAnimator;Lcom/android/server/wm/AnimationAdapter;Ljava/lang/Runnable;)V
+HPLcom/android/server/wm/-$$Lambda$SurfaceAnimator$M9kRDTUpVS03LTqe-QLQz3DnMhk;->run()V
+HPLcom/android/server/wm/-$$Lambda$TaskChangeNotificationController$Kz-Od_gLhLbMtGka4r78W0Gmzgo;->accept(Landroid/app/ITaskStackListener;Landroid/os/Message;)V
+HPLcom/android/server/wm/-$$Lambda$TaskPersister$xdLXwftXa6l84QTg1zpxMnmtQ0g;-><init>(Lcom/android/server/wm/TaskRecord;)V
+HPLcom/android/server/wm/-$$Lambda$uwO6wQlqU3CG7OTdH7NBCKnHs64;->accept(Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$UZl9uqUNteVgplGGEK6TMzf-7zk;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$VY87MmFWaCLMkNa2qHGaPrThyrI;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/ActivityDisplay;->alwaysCreateStack(II)Z
+HPLcom/android/server/wm/ActivityDisplay;->pauseBackStacks(ZLcom/android/server/wm/ActivityRecord;)Z
+HPLcom/android/server/wm/ActivityDisplay;->updateDisplayOverrideConfigurationLocked(Landroid/content/res/Configuration;Lcom/android/server/wm/ActivityRecord;ZLcom/android/server/wm/ActivityTaskManagerService$UpdateConfigurationResult;)Z
+HPLcom/android/server/wm/ActivityMetricsLogger;->reset(ZLcom/android/server/wm/ActivityMetricsLogger$WindowingModeTransitionInfo;Ljava/lang/String;J)V
+HPLcom/android/server/wm/ActivityRecord$AddStartingWindow;-><init>(Lcom/android/server/wm/ActivityRecord;Lcom/android/server/wm/ActivityRecord$1;)V
+HPLcom/android/server/wm/ActivityRecord$AddStartingWindow;->run()V
+HPLcom/android/server/wm/ActivityRecord$Token;-><init>(Landroid/content/Intent;)V
+HPLcom/android/server/wm/ActivityRecord;->addWindow(Lcom/android/server/wm/WindowState;)V
+HPLcom/android/server/wm/ActivityRecord;->adjustPinnedStackAndInitChangeTransitionIfNeeded(II)V
+HPLcom/android/server/wm/ActivityRecord;->asActivityRecord()Lcom/android/server/wm/ActivityRecord;
+HPLcom/android/server/wm/ActivityRecord;->checkAppWindowsReadyToShow()V
+HPLcom/android/server/wm/ActivityRecord;->checkCompleteDeferredRemoval()Z
+HPLcom/android/server/wm/ActivityRecord;->clearAnimatingFlags()V
+HPLcom/android/server/wm/ActivityRecord;->commitVisibility(Landroid/view/WindowManager$LayoutParams;ZIZZ)Z
+HPLcom/android/server/wm/ActivityRecord;->computeBounds(Landroid/graphics/Rect;Landroid/graphics/Rect;)V
+HPLcom/android/server/wm/ActivityRecord;->containsDismissKeyguardWindow()Z
+HPLcom/android/server/wm/ActivityRecord;->containsShowWhenLockedWindow()Z
+HPLcom/android/server/wm/ActivityRecord;->destroySurfaces(Z)V
+HPLcom/android/server/wm/ActivityRecord;->detachChildren()V
+HPLcom/android/server/wm/ActivityRecord;->fillsParent()Z
+HPLcom/android/server/wm/ActivityRecord;->findMainWindow()Lcom/android/server/wm/WindowState;
+HPLcom/android/server/wm/ActivityRecord;->findMainWindow(Z)Lcom/android/server/wm/WindowState;
+HPLcom/android/server/wm/ActivityRecord;->forAllWindows(Lcom/android/internal/util/ToBooleanFunction;Z)Z
+HPLcom/android/server/wm/ActivityRecord;->getAnimationLeashParent()Landroid/view/SurfaceControl;
+HPLcom/android/server/wm/ActivityRecord;->getDisplayedBounds()Landroid/graphics/Rect;
+HPLcom/android/server/wm/ActivityRecord;->getOrientation(I)I
+HPLcom/android/server/wm/ActivityRecord;->getStartingWindowType(ZZZZZZLandroid/app/ActivityManager$TaskSnapshot;)I
+HPLcom/android/server/wm/ActivityRecord;->getTask()Lcom/android/server/wm/Task;
+HPLcom/android/server/wm/ActivityRecord;->handleAlreadyVisible()V
+HPLcom/android/server/wm/ActivityRecord;->isAppAnimating()Z
+HPLcom/android/server/wm/ActivityRecord;->isClientHidden()Z
+HPLcom/android/server/wm/ActivityRecord;->isFirstChildWindowGreaterThanSecond(Lcom/android/server/wm/WindowState;Lcom/android/server/wm/WindowState;)Z
+HPLcom/android/server/wm/ActivityRecord;->isResolverOrDelegateActivity()Z
+HPLcom/android/server/wm/ActivityRecord;->isSelfAnimating()Z
+HPLcom/android/server/wm/ActivityRecord;->isVisible()Z
+HPLcom/android/server/wm/ActivityRecord;->isWaitingForTransitionStart()Z
+HPLcom/android/server/wm/ActivityRecord;->layoutLetterbox(Lcom/android/server/wm/WindowState;)V
+HPLcom/android/server/wm/ActivityRecord;->makeInvisible()V
+HPLcom/android/server/wm/ActivityRecord;->needsZBoost()Z
+HPLcom/android/server/wm/ActivityRecord;->onAnimationLeashCreated(Landroid/view/SurfaceControl$Transaction;Landroid/view/SurfaceControl;)V
+HPLcom/android/server/wm/ActivityRecord;->onAnimationLeashLost(Landroid/view/SurfaceControl$Transaction;)V
+HPLcom/android/server/wm/ActivityRecord;->onAppTransitionDone()V
+HPLcom/android/server/wm/ActivityRecord;->onDisplayChanged(Lcom/android/server/wm/DisplayContent;)V
+HPLcom/android/server/wm/ActivityRecord;->onParentChanged()V
+HPLcom/android/server/wm/ActivityRecord;->postWindowRemoveStartingWindowCleanup(Lcom/android/server/wm/WindowState;)V
+HPLcom/android/server/wm/ActivityRecord;->prepareSurfaces()V
+HPLcom/android/server/wm/ActivityRecord;->removeChild(Lcom/android/server/wm/WindowContainer;)V
+HPLcom/android/server/wm/ActivityRecord;->removeStartingWindow()V
+HPLcom/android/server/wm/ActivityRecord;->scheduleAddStartingWindow()V
+HPLcom/android/server/wm/ActivityRecord;->scheduleTopResumedActivityChanged(Z)Z
+HPLcom/android/server/wm/ActivityRecord;->setHidden(Z)V
+HPLcom/android/server/wm/ActivityRecord;->setLayer(Landroid/view/SurfaceControl$Transaction;I)V
+HPLcom/android/server/wm/ActivityRecord;->setVisibility(ZZ)V
+HPLcom/android/server/wm/ActivityRecord;->shouldDeferAnimationFinish(Ljava/lang/Runnable;)Z
+HPLcom/android/server/wm/ActivityRecord;->stopFreezingScreen(ZZ)V
+HPLcom/android/server/wm/ActivityRecord;->stopIfPossible()V
+HPLcom/android/server/wm/ActivityRecord;->transferStartingWindow(Landroid/os/IBinder;)Z
+HPLcom/android/server/wm/ActivityRecord;->updateAllDrawn()V
+HPLcom/android/server/wm/ActivityRecord;->updateDrawnWindowStates(Lcom/android/server/wm/WindowState;)Z
+HPLcom/android/server/wm/ActivityRecord;->updateLetterboxSurface(Lcom/android/server/wm/WindowState;)V
+HPLcom/android/server/wm/ActivityRecord;->updateReportedVisibilityLocked()V
+HPLcom/android/server/wm/ActivityRecord;->windowsAreFocusable()Z
+HPLcom/android/server/wm/ActivityRecord;->writeToProto(Landroid/util/proto/ProtoOutputStream;JI)V
+HPLcom/android/server/wm/ActivityStack;->removeLaunchTickMessages()V
+HPLcom/android/server/wm/ActivityStack;->removeStopTimeoutForActivity(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/ActivityStack;->scheduleLaunchTickForActivity(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/ActivityStack;->topTask()Lcom/android/server/wm/TaskRecord;
+HPLcom/android/server/wm/ActivityStarter;->startActivity(Lcom/android/server/wm/ActivityRecord;Lcom/android/server/wm/ActivityRecord;Landroid/service/voice/IVoiceInteractionSession;Lcom/android/internal/app/IVoiceInteractor;IZLandroid/app/ActivityOptions;Lcom/android/server/wm/TaskRecord;[Lcom/android/server/wm/ActivityRecord;Z)I
+HPLcom/android/server/wm/ActivityTaskManagerService;->addWindowLayoutReasons(I)V
+HPLcom/android/server/wm/ActivityTaskManagerService;->continueWindowLayout()V
+HPLcom/android/server/wm/ActivityTaskManagerService;->deferWindowLayout()V
+HPLcom/android/server/wm/ActivityTaskManagerService;->getPermissionPolicyInternal()Lcom/android/server/policy/PermissionPolicyInternal;
+HPLcom/android/server/wm/ActivityTaskManagerService;->isCrossUserAllowed(II)Z
+HPLcom/android/server/wm/ActivityTaskManagerService;->startProcessAsync(Lcom/android/server/wm/ActivityRecord;ZZLjava/lang/String;)V
+HPLcom/android/server/wm/AppTransitionController;->findAnimLayoutParamsToken(ILandroid/util/ArraySet;)Lcom/android/server/wm/ActivityRecord;
+HPLcom/android/server/wm/AppTransitionController;->getRemoteAnimationOverride(Lcom/android/server/wm/ActivityRecord;ILandroid/util/ArraySet;)Landroid/view/RemoteAnimationAdapter;
+HPLcom/android/server/wm/AppTransitionController;->getTopApp(Landroid/util/ArraySet;Z)Lcom/android/server/wm/ActivityRecord;
+HPLcom/android/server/wm/AppTransition;->getAnimationStyleResId(Landroid/view/WindowManager$LayoutParams;)I
+HPLcom/android/server/wm/AppTransition;->goodToGo(ILcom/android/server/wm/ActivityRecord;Landroid/util/ArraySet;)I
+HPLcom/android/server/wm/AppTransition;->setLastAppTransition(ILcom/android/server/wm/ActivityRecord;Lcom/android/server/wm/ActivityRecord;Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/ConfigurationContainer;->hasChild()Z
+HPLcom/android/server/wm/ConfigurationContainer;->setWindowingMode(I)V
+HPLcom/android/server/wm/DisplayContent;->computeImeTargetIfNeeded(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/DisplayContent;->getInsetsPolicy()Lcom/android/server/wm/InsetsPolicy;
+HPLcom/android/server/wm/DisplayContent;->getRotationAnimation()Lcom/android/server/wm/ScreenRotationAnimation;
+HPLcom/android/server/wm/DisplayContent;->performLayoutNoTrace(ZZ)V
+HPLcom/android/server/wm/DisplayContent;->setFocusedApp(Lcom/android/server/wm/ActivityRecord;)Z
+HPLcom/android/server/wm/DisplayContent;->updateDisplayAndOrientation(ILandroid/content/res/Configuration;)Landroid/view/DisplayInfo;
+HPLcom/android/server/wm/DisplayContent;->updateFocusedWindowLocked(IZI)Z
+HPLcom/android/server/wm/DisplayContent;->updateOrientation(Landroid/content/res/Configuration;Landroid/os/IBinder;Z)Landroid/content/res/Configuration;
+HPLcom/android/server/wm/DisplayContent;->updateOrientation()Z
+HPLcom/android/server/wm/DisplayPolicy;->addWindowLw(Lcom/android/server/wm/WindowState;Landroid/view/WindowManager$LayoutParams;)V
+HPLcom/android/server/wm/DisplayPolicy;->adjustWindowParamsLw(Lcom/android/server/wm/WindowState;Landroid/view/WindowManager$LayoutParams;II)V
+HPLcom/android/server/wm/DisplayPolicy;->configureNavBarOpacity(IZZZZZ)I
+HPLcom/android/server/wm/DisplayPolicy;->drawsBarBackground(ILcom/android/server/wm/WindowState;Lcom/android/server/wm/BarController;I)Z
+HPLcom/android/server/wm/DisplayPolicy;->drawsNavigationBarBackground(ILcom/android/server/wm/WindowState;)Z
+HPLcom/android/server/wm/DisplayPolicy;->getRefreshRatePolicy()Lcom/android/server/wm/RefreshRatePolicy;
+HPLcom/android/server/wm/DisplayPolicy;->isWindowExcludedFromContent(Lcom/android/server/wm/WindowState;)Z
+HPLcom/android/server/wm/DisplayPolicy;->selectAnimation(Lcom/android/server/wm/WindowState;I)I
+HPLcom/android/server/wm/DisplayPolicy;->updateConfigurationAndScreenSizeDependentBehaviors()V
+HPLcom/android/server/wm/DisplayRotation;->getRotation()I
+HPLcom/android/server/wm/DisplayRotation;->markForSeamlessRotation(Lcom/android/server/wm/WindowState;Z)V
+HPLcom/android/server/wm/ImeInsetsSourceProvider;->onPostInsetsDispatched()V
+HPLcom/android/server/wm/ImeInsetsSourceProvider;->onPostLayout()V
+HPLcom/android/server/wm/InputManagerCallback;->notifyFocusChanged(Landroid/os/IBinder;Landroid/os/IBinder;)Z
+HPLcom/android/server/wm/InputMonitor$UpdateInputWindows;->run()V
+HPLcom/android/server/wm/InsetsPolicy;->areSystemBarsForciblyVisible()Z
+HPLcom/android/server/wm/InsetsPolicy;->isNavBarForciblyVisible()Z
+HPLcom/android/server/wm/InsetsPolicy;->isStatusBarForciblyVisible()Z
+HPLcom/android/server/wm/InsetsStateController;->getImeSourceProvider()Lcom/android/server/wm/ImeInsetsSourceProvider;
+HPLcom/android/server/wm/InsetsStateController;->onBarControlTargetChanged(Lcom/android/server/wm/InsetsControlTarget;Lcom/android/server/wm/InsetsControlTarget;)V
+HPLcom/android/server/wm/LaunchObserverRegistryImpl;->onIntentStarted(Landroid/content/Intent;J)V
+HPLcom/android/server/wm/PinnedStackController;->resetReentrySnapFraction(Landroid/content/ComponentName;)V
+HPLcom/android/server/wm/RecentTasks;->isInVisibleRange(Lcom/android/server/wm/TaskRecord;IIZ)Z
+HPLcom/android/server/wm/RefreshRatePolicy;->getPreferredModeId(Lcom/android/server/wm/WindowState;)I
+HPLcom/android/server/wm/RemoteAnimationController$FinishedCallback;-><init>(Lcom/android/server/wm/RemoteAnimationController;)V
+HPLcom/android/server/wm/RemoteAnimationController$RemoteAnimationRecord;->getMode()I
+HPLcom/android/server/wm/RemoteAnimationController;->createAppAnimations()[Landroid/view/RemoteAnimationTarget;
+HPLcom/android/server/wm/RemoteAnimationController;->createWallpaperAnimations()[Landroid/view/RemoteAnimationTarget;
+HPLcom/android/server/wm/RemoteAnimationController;-><init>(Lcom/android/server/wm/WindowManagerService;Landroid/view/RemoteAnimationAdapter;Landroid/os/Handler;)V
+HPLcom/android/server/wm/RemoteAnimationController;->releaseFinishedCallback()V
+HPLcom/android/server/wm/RemoteAnimationController;->setRunningRemoteAnimation(Z)V
+HPLcom/android/server/wm/RemoteAnimationController;->unlinkToDeathOfRunner()V
+HPLcom/android/server/wm/RootActivityContainer;->getLaunchStack(Lcom/android/server/wm/ActivityRecord;Landroid/app/ActivityOptions;Lcom/android/server/wm/TaskRecord;ZLcom/android/server/wm/LaunchParamsController$LaunchParams;II)Lcom/android/server/wm/ActivityStack;
+HPLcom/android/server/wm/RootActivityContainer;->getRunningTasks(ILjava/util/List;IIIZZLandroid/util/ArraySet;)V
+HPLcom/android/server/wm/RootWindowContainer;->getActivityRecord(Landroid/os/IBinder;)Lcom/android/server/wm/ActivityRecord;
+HPLcom/android/server/wm/Session;->finishDrawing(Landroid/view/IWindow;Landroid/view/SurfaceControl$Transaction;)V
+HPLcom/android/server/wm/Session;->reportSystemGestureExclusionChanged(Landroid/view/IWindow;Ljava/util/List;)V
+HPLcom/android/server/wm/StartingData;-><init>(Lcom/android/server/wm/WindowManagerService;)V
+HPLcom/android/server/wm/SurfaceAnimationRunner;->startPendingAnimationsLocked()V
+HPLcom/android/server/wm/SurfaceAnimator$Animatable;->shouldDeferAnimationFinish(Ljava/lang/Runnable;)Z
+HPLcom/android/server/wm/SurfaceAnimator;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
+HPLcom/android/server/wm/Task;->addChild(Lcom/android/server/wm/ActivityRecord;I)V
+HPLcom/android/server/wm/TaskChangeNotificationController;->notifyTaskListUpdated()V
+HPLcom/android/server/wm/Task;->getTopVisibleActivity()Lcom/android/server/wm/ActivityRecord;
+HPLcom/android/server/wm/TaskLaunchParamsModifier;->canInheritWindowingModeFromSource(Lcom/android/server/wm/ActivityDisplay;Lcom/android/server/wm/ActivityRecord;)Z
+HPLcom/android/server/wm/Task;->onConfigurationChanged(Landroid/content/res/Configuration;Z)V
+HPLcom/android/server/wm/TaskPersister$TaskWriteQueueItem;-><init>(Lcom/android/server/wm/TaskRecord;Lcom/android/server/wm/ActivityTaskManagerService;)V
+HPLcom/android/server/wm/Task;->positionChildAt(Lcom/android/server/wm/ActivityRecord;I)V
+HPLcom/android/server/wm/TaskRecord;->computeFullscreenBounds(Landroid/graphics/Rect;Lcom/android/server/wm/ActivityRecord;Landroid/graphics/Rect;I)V
+HPLcom/android/server/wm/TaskRecord;->findRootIndex(Z)I
+HPLcom/android/server/wm/TaskRecord;-><init>(Lcom/android/server/wm/ActivityTaskManagerService;ILandroid/content/pm/ActivityInfo;Landroid/content/Intent;Landroid/service/voice/IVoiceInteractionSession;Lcom/android/internal/app/IVoiceInteractor;Landroid/app/ActivityManager$TaskDescription;)V
+HPLcom/android/server/wm/UnknownAppVisibilityController;->notifyAppResumedFinished(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/UnknownAppVisibilityController;->notifyRelayouted(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/WallpaperAnimationAdapter;->getLeashFinishedCallback()Lcom/android/server/wm/SurfaceAnimator$OnAnimationFinishedCallback;
+HPLcom/android/server/wm/WallpaperAnimationAdapter;->lambda$startWallpaperAnimations$0(JJLjava/util/function/Consumer;Ljava/util/ArrayList;Ljava/util/ArrayList;Lcom/android/server/wm/WallpaperWindowToken;)V
+HPLcom/android/server/wm/WallpaperAnimationAdapter;->startAnimation(Landroid/view/SurfaceControl;Landroid/view/SurfaceControl$Transaction;Lcom/android/server/wm/SurfaceAnimator$OnAnimationFinishedCallback;)V
+HPLcom/android/server/wm/WallpaperAnimationAdapter;->startWallpaperAnimations(Lcom/android/server/wm/WindowManagerService;JJLjava/util/function/Consumer;Ljava/util/ArrayList;)[Landroid/view/RemoteAnimationTarget;
+HPLcom/android/server/wm/WindowAnimator;->cancelAnimation()V
+HPLcom/android/server/wm/WindowContainer;->onAnimationLeashLost(Landroid/view/SurfaceControl$Transaction;)V
+HPLcom/android/server/wm/WindowContainer;->onParentChanged(Lcom/android/server/wm/WindowContainer$PreAssignChildLayersCallback;)V
+HPLcom/android/server/wm/WindowManagerService$LocalService;->isStackVisibleLw(I)Z
+HPLcom/android/server/wm/WindowManagerService;->prepareNoneTransitionForRelaunching(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/WindowManagerService;->prepareWindowReplacementTransition(Lcom/android/server/wm/ActivityRecord;)Z
+HPLcom/android/server/wm/WindowProcessController;->createProfilerInfoIfNeeded()Landroid/app/ProfilerInfo;
+HPLcom/android/server/wm/WindowProcessControllerMap;->put(ILcom/android/server/wm/WindowProcessController;)V
+HPLcom/android/server/wm/WindowProcessController;->onStartActivity(ILandroid/content/pm/ActivityInfo;)V
+HPLcom/android/server/wm/WindowProcessController;->setLastActivityLaunchTime(J)V
+HPLcom/android/server/wm/WindowState$UpdateReportedVisibilityResults;-><init>()V
+HPLcom/android/server/wm/WindowState;->getLastReportedConfiguration()Landroid/content/res/Configuration;
+HPLcom/android/server/wm/WindowState;->getSurfaceTouchableRegion(Landroid/view/InputWindowHandle;I)I
+HPLcom/android/server/wm/WindowState;->logExclusionRestrictions(I)V
+HPLcom/android/server/wm/WindowState;->onAnimationLeashLost(Landroid/view/SurfaceControl$Transaction;)V
+HPLcom/android/server/wm/WindowState;->relayoutVisibleWindow(II)I
+HPLcom/android/server/wm/WindowState;->updateLocationInParentDisplayIfNeeded()V
+HPLcom/android/server/wm/WindowSurfaceController;-><init>(Ljava/lang/String;IIIILcom/android/server/wm/WindowStateAnimator;II)V
+HPLcom/android/server/wm/WindowSurfacePlacer$Traverser;->run()V
+HPLcom/android/server/wm/WindowToken;-><init>(Lcom/android/server/wm/WindowManagerService;Landroid/os/IBinder;IZLcom/android/server/wm/DisplayContent;Z)V
+HPLcom/google/android/startop/iorap/IorapForwardingService$AppLaunchObserver;->onActivityLaunched([BI)V
+HPLcom/android/server/accessibility/AccessibilityManagerService;->computeRelevantEventTypesLocked(Lcom/android/server/accessibility/AccessibilityUserState;Lcom/android/server/accessibility/AccessibilityManagerService$Client;)I
+HPLcom/android/server/accessibility/AccessibilitySecurityPolicy;->resolveProfileParentLocked(I)I
+HPLcom/android/server/am/-$$Lambda$ProcessList$vtq7LF5jIHO4t5NE03c8g7BT7Jc;-><init>(Lcom/android/server/am/ProcessList;Lcom/android/server/am/ProcessRecord;Ljava/lang/String;[IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;J)V
+HPLcom/android/server/am/ActivityManagerService$2;->onActivityLaunchFinished([BJ)V
+HPLcom/android/server/am/ActivityManagerService$2;->onIntentStarted(Landroid/content/Intent;J)V
+HPLcom/android/server/am/ActivityManagerService$4;->isPackageForFilter(Ljava/lang/String;Lcom/android/server/am/BroadcastFilter;)Z
+HPLcom/android/server/am/ActivityManagerService$4;->newResult(Lcom/android/server/am/BroadcastFilter;II)Lcom/android/server/am/BroadcastFilter;
+HPLcom/android/server/am/ActivityManagerService$LocalService;->startProcess(Ljava/lang/String;Landroid/content/pm/ApplicationInfo;ZZLjava/lang/String;Landroid/content/ComponentName;)V
+HPLcom/android/server/am/ActivityManagerService;->trimApplicationsLocked(Ljava/lang/String;)V
+HPLcom/android/server/am/ProcessList;->lambda$startProcessLocked$0$ProcessList(Lcom/android/server/am/ProcessRecord;Ljava/lang/String;[IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;J)V
+HPLcom/android/server/am/ProcessRecord;->onStartActivity(IZLjava/lang/String;J)V
+HPLcom/android/server/am/ProcessRecord;->setRunningRemoteAnimation(Z)V
+HPLcom/android/server/am/ProcessRecord;->setStartParams(ILcom/android/server/am/HostingRecord;Ljava/lang/String;J)V
+HPLcom/android/server/appprediction/RemoteAppPredictionService;->lambda$notifyAppTargetEvent$1(Landroid/app/prediction/AppPredictionSessionId;Landroid/app/prediction/AppTargetEvent;Landroid/service/appprediction/IPredictionService;)V
+HPLcom/android/server/audio/SoundEffectsHelper$SfxHandler$1;-><init>(Lcom/android/server/audio/SoundEffectsHelper$SfxHandler;Landroid/os/Message;)V
+HPLcom/android/server/audio/SoundEffectsHelper;->onLoadSoundEffects(Lcom/android/server/audio/SoundEffectsHelper$OnEffectsLoadCompleteHandler;)V
+HPLcom/android/server/autofill/AutofillManagerServiceImpl;->getAppDisabledActivitiesLocked(Ljava/lang/String;)Landroid/util/ArrayMap;
+HPLcom/android/server/compat/CompatChange;->isEnabled(Landroid/content/pm/ApplicationInfo;)Z
+HPLcom/android/server/GnssManagerService;->onForegroundChanged(IZ)V
+HPLcom/android/server/LocationManagerService;->lambda$initializeLocked$4$LocationManagerService(II)V
+HPLcom/android/server/LocationManagerService;->lambda$initializeLocked$5$LocationManagerService(II)V
+HPLcom/android/server/policy/PermissionPolicyService$Internal;->isActionRemovedForCallingPackage(Landroid/content/Intent;ILjava/lang/String;)Z
+HPLcom/android/server/statusbar/-$$Lambda$StatusBarManagerService$dQguzfF4tEgBOj3Pr8MpGRN8HT0;-><init>(Lcom/android/server/statusbar/StatusBarManagerService;ILandroid/os/IBinder;IIZZ)V
+HPLcom/android/server/statusbar/StatusBarManagerService;->lambda$setImeWindowStatus$2$StatusBarManagerService(ILandroid/os/IBinder;IIZZ)V
+HPLcom/android/server/statusbar/StatusBarManagerService;->lambda$updateUiVisibilityLocked$3$StatusBarManagerService(IIIIILandroid/graphics/Rect;Landroid/graphics/Rect;Z)V
+HPLcom/android/server/wm/-$$Lambda$ActivityRecord$tt99EJHW_Nk5qgU9galJBIm5wXg;-><init>(Lcom/android/server/policy/WindowManagerPolicy$StartingSurface;)V
+HPLcom/android/server/wm/-$$Lambda$RemoteAnimationController$dP8qDptNigoqhzVtIudsX5naGu4;-><init>(Lcom/android/server/wm/RemoteAnimationController;)V
+HPLcom/android/server/wm/-$$Lambda$RemoteAnimationController$uQS8vaPKQ-E3x_9G8NCxPQmw1fw;-><init>(Lcom/android/server/wm/RemoteAnimationController;)V
+HPLcom/android/server/wm/ActivityRecord;->addToStopping(ZZLjava/lang/String;)V
+HPLcom/android/server/wm/ActivityRecord;->allDrawnStatesConsidered()Z
+HPLcom/android/server/wm/ActivityRecord;->applyAnimationLocked(Landroid/view/WindowManager$LayoutParams;IZZ)Z
+HPLcom/android/server/wm/ActivityRecord;->checkKeyguardFlagsChanged()V
+HPLcom/android/server/wm/ActivityRecord;->lambda$removeStartingWindow$3(Lcom/android/server/policy/WindowManagerPolicy$StartingSurface;)V
+HPLcom/android/server/wm/ActivityRecord;->shouldStartChangeTransition(II)Z
+HPLcom/android/server/wm/ActivityRecord;->transferStartingWindowFromHiddenAboveTokenIfNeeded()V
+HPLcom/android/server/wm/ActivityStack;->scheduleStopTimeoutForActivity(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/ActivityStack;->startPausingLocked(ZZLcom/android/server/wm/ActivityRecord;)Z
+HPLcom/android/server/wm/ActivityStackSupervisor;->getRunningTasks()Lcom/android/server/wm/RunningTasks;
+HPLcom/android/server/wm/ActivityStackSupervisor;->getSystemChooserActivity()Landroid/content/ComponentName;
+HPLcom/android/server/wm/ActivityStarter;->handleStartResult(Lcom/android/server/wm/ActivityRecord;I)Lcom/android/server/wm/ActivityStack;
+HPLcom/android/server/wm/ActivityStarter;->startActivityInner(Lcom/android/server/wm/ActivityRecord;Lcom/android/server/wm/ActivityRecord;Landroid/service/voice/IVoiceInteractionSession;Lcom/android/internal/app/IVoiceInteractor;IZLandroid/app/ActivityOptions;Lcom/android/server/wm/TaskRecord;[Lcom/android/server/wm/ActivityRecord;Z)I
+HPLcom/android/server/wm/ActivityTaskManagerService;->ensureConfigAndVisibilityAfterUpdate(Lcom/android/server/wm/ActivityRecord;I)Z
+HPLcom/android/server/wm/AnimatingActivityRegistry;->endDeferringFinished()V
+HPLcom/android/server/wm/AnimationAdapter;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
+HPLcom/android/server/wm/AppTransitionController;->lookForHighestTokenWithFilter(Landroid/util/ArraySet;Landroid/util/ArraySet;Landroid/util/ArraySet;Ljava/util/function/Predicate;)Lcom/android/server/wm/ActivityRecord;
+HPLcom/android/server/wm/DisplayContent;->findFocusedWindowIfNeeded(I)Lcom/android/server/wm/WindowState;
+HPLcom/android/server/wm/DisplayContent;->getActivityRecord(Landroid/os/IBinder;)Lcom/android/server/wm/ActivityRecord;
+HPLcom/android/server/wm/DisplayContent;->getParentWindow()Lcom/android/server/wm/WindowState;
+HPLcom/android/server/wm/DisplayContent;->lambda$new$1$DisplayContent(Lcom/android/server/wm/WindowState;)V
+HPLcom/android/server/wm/DisplayContent;->lambda$new$2$DisplayContent(Lcom/android/server/wm/WindowState;)Z
+HPLcom/android/server/wm/DisplayContent;->lambda$new$3$DisplayContent(Lcom/android/server/wm/WindowState;)V
+HPLcom/android/server/wm/DisplayContent;->lambda$updateSystemUiVisibility$20(IILcom/android/server/wm/WindowState;)V
+HPLcom/android/server/wm/DisplayContent;->logsGestureExclusionRestrictions(Lcom/android/server/wm/WindowState;)Z
+HPLcom/android/server/wm/DisplayPolicy;->getCurrentUserResources()Landroid/content/res/Resources;
+HPLcom/android/server/wm/DisplayPolicy;->getStatusBar()Lcom/android/server/wm/WindowState;
+HPLcom/android/server/wm/DisplayPolicy;->lambda$addWindowLw$3$DisplayPolicy(Lcom/android/server/wm/DisplayFrames;Lcom/android/server/wm/WindowState;Landroid/graphics/Rect;)V
+HPLcom/android/server/wm/DisplayPolicy;->lambda$addWindowLw$4$DisplayPolicy(Lcom/android/server/wm/DisplayFrames;Lcom/android/server/wm/WindowState;Landroid/graphics/Rect;)V
+HPLcom/android/server/wm/DisplayPolicy;->lambda$addWindowLw$5$DisplayPolicy(Lcom/android/server/wm/DisplayFrames;Lcom/android/server/wm/WindowState;Landroid/graphics/Rect;)V
+HPLcom/android/server/wm/DisplayPolicy;->lambda$addWindowLw$6$DisplayPolicy(Lcom/android/server/wm/DisplayFrames;Lcom/android/server/wm/WindowState;Landroid/graphics/Rect;)V
+HPLcom/android/server/wm/DisplayPolicy;->lambda$addWindowLw$7$DisplayPolicy(Lcom/android/server/wm/DisplayFrames;Lcom/android/server/wm/WindowState;Landroid/graphics/Rect;)V
+HPLcom/android/server/wm/DisplayPolicy;->lambda$updateSystemUiVisibilityLw$10$DisplayPolicy(IIILandroid/graphics/Rect;Landroid/graphics/Rect;ZLcom/android/server/wm/WindowState;Z)V
+HPLcom/android/server/wm/DisplayRotation;->updateOrientation(IZ)Z
+HPLcom/android/server/wm/DisplayRotation;->updateUserDependentConfiguration(Landroid/content/res/Resources;)V
+HPLcom/android/server/wm/HighRefreshRateBlacklist;->isBlacklisted(Ljava/lang/String;)Z
+HPLcom/android/server/wm/InputManagerCallback;->dispatchPointerCaptureChanged(Landroid/view/IWindow;Z)Z
+HPLcom/android/server/wm/InputManagerCallback;->onPointerDownOutsideFocus(Landroid/os/IBinder;)V
+HPLcom/android/server/wm/InputMonitor;->layoutInputConsumers(II)V
+HPLcom/android/server/wm/InputMonitor;->setFocusedAppLw(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/InsetsStateController;->onControlChanged(ILcom/android/server/wm/InsetsControlTarget;)V
+HPLcom/android/server/wm/LaunchObserverRegistryImpl;->onActivityLaunchFinished([BJ)V
+HPLcom/android/server/wm/RemoteAnimationController$FinishedCallback;->release()V
+HPLcom/android/server/wm/RemoteAnimationController;->lambda$goodToGo$1$RemoteAnimationController([Landroid/view/RemoteAnimationTarget;[Landroid/view/RemoteAnimationTarget;)V
+HPLcom/android/server/wm/RunningTasks;->getTasks(ILjava/util/List;IILjava/util/ArrayList;IZZLandroid/util/ArraySet;)V
+HPLcom/android/server/wm/SplashScreenStartingData;->createStartingSurface(Lcom/android/server/wm/ActivityRecord;)Lcom/android/server/policy/WindowManagerPolicy$StartingSurface;
+HPLcom/android/server/wm/SurfaceAnimator$Animatable;->shouldDeferAnimationFinish(Ljava/lang/Runnable;)Z
+HPLcom/android/server/wm/SurfaceAnimator;->lambda$getFinishedCallback$0$SurfaceAnimator(Lcom/android/server/wm/AnimationAdapter;Ljava/lang/Runnable;)V
+HPLcom/android/server/wm/Task;->positionChildAt(ILcom/android/server/wm/ActivityRecord;Z)V
+HPLcom/android/server/wm/TaskRecord;->handlesOrientationChangeFromDescendant()Z
+HPLcom/android/server/wm/TaskRecord;-><init>(Lcom/android/server/wm/ActivityTaskManagerService;ILandroid/content/Intent;Landroid/content/Intent;Ljava/lang/String;Ljava/lang/String;Landroid/content/ComponentName;Landroid/content/ComponentName;ZZZIILjava/lang/String;Ljava/util/ArrayList;JZLandroid/app/ActivityManager$TaskDescription;IIIIILjava/lang/String;IZZZIILandroid/content/pm/ActivityInfo;Landroid/service/voice/IVoiceInteractionSession;Lcom/android/internal/app/IVoiceInteractor;)V
+HPLcom/android/server/wm/TaskStack;->getAnimatingActivityRegistry()Lcom/android/server/wm/AnimatingActivityRegistry;
+HPLcom/android/server/wm/WallpaperAnimationAdapter;->startWallpaperAnimations(Lcom/android/server/wm/WindowManagerService;JJLjava/util/function/Consumer;Ljava/util/ArrayList;)[Landroid/view/RemoteAnimationTarget;
+HPLcom/android/server/wm/WindowContainer$ForAllWindowsConsumerWrapper;-><init>(Lcom/android/server/wm/WindowContainer;Lcom/android/server/wm/WindowContainer$1;)V
+HPLcom/android/server/wm/WindowManagerService;->finishDrawingWindow(Lcom/android/server/wm/Session;Landroid/view/IWindow;Landroid/view/SurfaceControl$Transaction;)V
+HPLcom/android/server/wm/WindowManagerService;->reportSystemGestureExclusionChanged(Lcom/android/server/wm/Session;Landroid/view/IWindow;Ljava/util/List;)V
+HPLcom/android/server/wm/WindowProcessController;->setRunningRemoteAnimation(Z)V
+HPLcom/android/server/wm/WindowProcessController;->shouldSetProfileProc()Z
+HPLcom/android/server/wm/WindowState;->onMergedOverrideConfigurationChanged()V
+HPLcom/android/server/wm/WindowSurfacePlacer;->continueLayout(Z)V
+HPLcom/android/server/wm/WindowSurfacePlacer;->isLayoutDeferred()Z
+HPLcom/google/android/startop/iorap/IorapForwardingService$AppLaunchObserver;->onActivityLaunchFinished([BJ)V
+HPLcom/google/android/startop/iorap/IorapForwardingService$AppLaunchObserver;->onIntentStarted(Landroid/content/Intent;J)V
+HPLcom/google/android/startop/iorap/IorapForwardingService;->invokeRemote(Lcom/google/android/startop/iorap/IIorap;Lcom/google/android/startop/iorap/IorapForwardingService$RemoteRunnable;)Z
+HPLcom/android/server/am/HostingRecord;-><init>(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;IZ)V
+HPLcom/android/server/am/ProcessList;->startProcessLocked(Ljava/lang/String;Landroid/content/pm/ApplicationInfo;ZILcom/android/server/am/HostingRecord;ZZIZLjava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/Runnable;)Lcom/android/server/am/ProcessRecord;
+HPLcom/android/server/am/ProcessRecord;->updateProcessInfo(ZZZ)V
+HPLcom/android/server/appprediction/RemoteAppPredictionService;->lambda$notifyAppTargetEvent$1(Landroid/app/prediction/AppPredictionSessionId;Landroid/app/prediction/AppTargetEvent;Landroid/service/appprediction/IPredictionService;)V
+HPLcom/android/server/audio/SoundEffectsHelper$SfxHandler$1;->run(Z)V
+HPLcom/android/server/input/InputManagerService$InputDevicesChangedListenerRecord;-><init>(Lcom/android/server/input/InputManagerService;ILandroid/hardware/input/IInputDevicesChangedListener;)V
+HPLcom/android/server/pm/PackageManagerServiceUtils;->enforceShellRestriction(Landroid/os/UserManagerInternal;Ljava/lang/String;II)V
+HPLcom/android/server/statusbar/StatusBarManagerService$1;->setSystemUiVisibility(IIIIILandroid/graphics/Rect;Landroid/graphics/Rect;ZLjava/lang/String;)V
+HPLcom/android/server/wm/-$$Lambda$AppTransitionController$o_nkoN7a-ZHaSAgJCQZcboKz9Ig;->test(Ljava/lang/Object;)Z
+HPLcom/android/server/wm/-$$Lambda$AppTransitionController$z5kCoexPNTWFncmRBfeXr6HA2JA;->test(Ljava/lang/Object;)Z
+HPLcom/android/server/wm/-$$Lambda$TaskPersister$8TcnoL7JFvpj8NzBRg91ns5JOBw;-><init>(Lcom/android/server/wm/TaskRecord;)V
+HPLcom/android/server/wm/ActivityRecord;->getAnimationBounds(I)Landroid/graphics/Rect;
+HPLcom/android/server/wm/ActivityRecord;->logStartActivity(ILcom/android/server/wm/TaskRecord;)V
+HPLcom/android/server/wm/ActivityRecord;->shouldAnimate(I)Z
+HPLcom/android/server/wm/ActivityStack;->getRunningTasks(Ljava/util/List;IIIZZLandroid/util/ArraySet;)V
+HPLcom/android/server/wm/ActivityStarter;->computeTargetTask(Lcom/android/server/wm/ActivityRecord;)Lcom/android/server/wm/TaskRecord;
+HPLcom/android/server/wm/ActivityStarter;->deliverToCurrentTopIfNeeded(Lcom/android/server/wm/ActivityStack;)I
+HPLcom/android/server/wm/ActivityStarter;->isAllowedToStart(Lcom/android/server/wm/ActivityRecord;ZLcom/android/server/wm/TaskRecord;)I
+HPLcom/android/server/wm/ActivityStarter;->setInitialState(Lcom/android/server/wm/ActivityRecord;Landroid/app/ActivityOptions;Lcom/android/server/wm/TaskRecord;ZILcom/android/server/wm/ActivityRecord;Landroid/service/voice/IVoiceInteractionSession;Lcom/android/internal/app/IVoiceInteractor;Z)V
+HPLcom/android/server/wm/ActivityStarter;->setNewTask(Lcom/android/server/wm/TaskRecord;)V
+HPLcom/android/server/wm/AnimationAdapter;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
+HPLcom/android/server/wm/DisplayRotation;->updateRotationUnchecked(Z)Z
+HPLcom/android/server/wm/RemoteAnimationController;->createRemoteAnimationRecord(Lcom/android/server/wm/ActivityRecord;Landroid/graphics/Point;Landroid/graphics/Rect;Landroid/graphics/Rect;)Lcom/android/server/wm/RemoteAnimationController$RemoteAnimationRecord;
+HPLcom/android/server/wm/RemoteAnimationController;->linkToDeathOfRunner()V
+HPLcom/android/server/wm/RemoteAnimationController;->writeStartDebugStatement()V
+HPLcom/android/server/wm/SurfaceAnimator$Animatable;->shouldDeferAnimationFinish(Ljava/lang/Runnable;)Z
+HPLcom/android/server/wm/TaskRecord;->setMinDimensions(Landroid/content/pm/ActivityInfo;)V
+HPLcom/android/server/wm/WallpaperAnimationAdapter;->startWallpaperAnimations(Lcom/android/server/wm/WindowManagerService;JJLjava/util/function/Consumer;Ljava/util/ArrayList;)[Landroid/view/RemoteAnimationTarget;
+HPLcom/android/server/wm/WindowStateAnimator;->finishDrawingLocked(Landroid/view/SurfaceControl$Transaction;)Z
+HPLcom/google/android/startop/iorap/-$$Lambda$IorapForwardingService$AppLaunchObserver$6ikxM-3KospNGDidAY7yA-rECHw;->run(Lcom/google/android/startop/iorap/IIorap;)V
+HPLcom/google/android/startop/iorap/-$$Lambda$IorapForwardingService$AppLaunchObserver$B9wq4q5y7qahY6TuLMO_s8nPIwY;->run(Lcom/google/android/startop/iorap/IIorap;)V
+HPLcom/google/android/startop/iorap/-$$Lambda$IorapForwardingService$AppLaunchObserver$J1AHa-Qs75WQr3stjbN97THbudE;->run(Lcom/google/android/startop/iorap/IIorap;)V
+HPLcom/android/server/am/ProcessList;->newProcessRecordLocked(Landroid/content/pm/ApplicationInfo;Ljava/lang/String;ZILcom/android/server/am/HostingRecord;)Lcom/android/server/am/ProcessRecord;
+HPLcom/android/server/appprediction/RemoteAppPredictionService;->lambda$notifyAppTargetEvent$1(Landroid/app/prediction/AppPredictionSessionId;Landroid/app/prediction/AppTargetEvent;Landroid/service/appprediction/IPredictionService;)V
+HPLcom/android/server/audio/SoundEffectsHelper;->onPlaySoundEffect(II)V
+HPLcom/android/server/LocationManagerService;->isThrottlingExemptLocked(Lcom/android/server/location/CallerIdentity;)Z
+HPLcom/android/server/statusbar/StatusBarManagerService;->setSystemUiVisibility(IIIIILandroid/graphics/Rect;Landroid/graphics/Rect;ZLjava/lang/String;)V
+HPLcom/android/server/wm/AnimationAdapter;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
+HPLcom/android/server/wm/RemoteAnimationController$RemoteAnimationAdapterWrapper;->dump(Ljava/io/PrintWriter;Ljava/lang/String;)V
+HPLcom/android/server/wm/RemoteAnimationController$RemoteAnimationRecord;-><init>(Lcom/android/server/wm/RemoteAnimationController;Lcom/android/server/wm/ActivityRecord;Landroid/graphics/Point;Landroid/graphics/Rect;Landroid/graphics/Rect;)V
+HPLcom/android/server/wm/SurfaceAnimator$Animatable;->shouldDeferAnimationFinish(Ljava/lang/Runnable;)Z
+HPLcom/android/server/wm/WindowProcessController;->setBoundClientUids(Landroid/util/ArraySet;)V
+HPLcom/google/android/startop/iorap/IorapForwardingService$AppLaunchObserver;->lambda$onActivityLaunched$2$IorapForwardingService$AppLaunchObserver([BILcom/google/android/startop/iorap/IIorap;)V
+HPLcom/google/android/startop/iorap/IorapForwardingService$AppLaunchObserver;->lambda$onActivityLaunchFinished$4$IorapForwardingService$AppLaunchObserver([BJLcom/google/android/startop/iorap/IIorap;)V
+HPLcom/google/android/startop/iorap/IorapForwardingService$AppLaunchObserver;->lambda$onIntentStarted$0$IorapForwardingService$AppLaunchObserver(Landroid/content/Intent;JLcom/google/android/startop/iorap/IIorap;)V
+HPLcom/android/server/statusbar/StatusBarManagerService;->updateUiVisibilityLocked(IIIIILandroid/graphics/Rect;Landroid/graphics/Rect;Z)V
+HPLcom/android/server/wm/AnimationAdapter;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
+HPLcom/android/server/wm/SurfaceAnimator$Animatable;->shouldDeferAnimationFinish(Ljava/lang/Runnable;)Z
+HPLcom/google/android/startop/iorap/IIorap$Stub$Proxy;->onAppLaunchEvent(Lcom/google/android/startop/iorap/RequestId;Lcom/google/android/startop/iorap/AppLaunchEvent;)V
+HPLcom/google/android/startop/iorap/RequestId;->nextValueForSequence()Lcom/google/android/startop/iorap/RequestId;
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 8a7dcc1..854f16a 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.os.Process;
 import android.util.Slog;
 import android.util.StatsLog;
 
@@ -128,7 +129,8 @@
 
     private ApplicationInfo getApplicationInfo(String packageName) {
         try {
-            return mContext.getPackageManager().getApplicationInfo(packageName, 0);
+            return mContext.getPackageManager().getApplicationInfoAsUser(packageName, 0,
+                    Process.myUid());
         } catch (PackageManager.NameNotFoundException e) {
             Slog.e(TAG, "No installed package " + packageName);
         }
diff --git a/services/core/java/com/android/server/compat/PlatformCompatNative.java b/services/core/java/com/android/server/compat/PlatformCompatNative.java
new file mode 100644
index 0000000..8399671
--- /dev/null
+++ b/services/core/java/com/android/server/compat/PlatformCompatNative.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.compat;
+
+import com.android.internal.compat.IPlatformCompatNative;
+
+/**
+ * @hide
+ */
+public class PlatformCompatNative extends IPlatformCompatNative.Stub {
+    private final PlatformCompat mPlatformCompat;
+
+    public PlatformCompatNative(PlatformCompat platformCompat) {
+        mPlatformCompat = platformCompat;
+    }
+
+    @Override
+    public void reportChangeByPackageName(long changeId, String packageName) {
+        mPlatformCompat.reportChangeByPackageName(changeId, packageName);
+    }
+
+    @Override
+    public void reportChangeByUid(long changeId, int uid) {
+        mPlatformCompat.reportChangeByUid(changeId, uid);
+    }
+
+    @Override
+    public boolean isChangeEnabledByPackageName(long changeId, String packageName) {
+        return mPlatformCompat.isChangeEnabledByPackageName(changeId, packageName);
+    }
+
+    @Override
+    public boolean isChangeEnabledByUid(long changeId, int uid) {
+        return mPlatformCompat.isChangeEnabledByUid(changeId, uid);
+    }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 88859a7..5883048 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -90,6 +90,7 @@
 import com.android.server.camera.CameraServiceProxy;
 import com.android.server.clipboard.ClipboardService;
 import com.android.server.compat.PlatformCompat;
+import com.android.server.compat.PlatformCompatNative;
 import com.android.server.connectivity.IpConnectivityMetrics;
 import com.android.server.contentcapture.ContentCaptureManagerInternal;
 import com.android.server.coverage.CoverageService;
@@ -641,8 +642,10 @@
         // Platform compat service is used by ActivityManagerService, PackageManagerService, and
         // possibly others in the future. b/135010838.
         t.traceBegin("PlatformCompat");
-        ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE,
-                new PlatformCompat(mSystemContext));
+        PlatformCompat platformCompat = new PlatformCompat(mSystemContext);
+        ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE, platformCompat);
+        ServiceManager.addService(Context.PLATFORM_COMPAT_NATIVE_SERVICE,
+                new PlatformCompatNative(platformCompat));
         t.traceEnd();
 
         // Wait for installd to finish starting up so that it has a chance to
diff --git a/services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java b/services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java
new file mode 100644
index 0000000..f1b2ef8
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usage;
+
+import static android.app.usage.UsageEvents.Event.MAX_EVENT_TYPE;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import android.app.usage.UsageEvents;
+import android.content.res.Configuration;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IntervalStatsTests {
+    private static final int NUMBER_OF_PACKAGES = 7;
+    private static final int NUMBER_OF_EVENTS_PER_PACKAGE = 200;
+    private static final int NUMBER_OF_EVENTS = NUMBER_OF_PACKAGES * NUMBER_OF_EVENTS_PER_PACKAGE;
+
+    private long mEndTime = 0;
+
+    private void populateIntervalStats(IntervalStats intervalStats) {
+        final int timeProgression = 23;
+        long time = System.currentTimeMillis() - (NUMBER_OF_EVENTS * timeProgression);
+
+        intervalStats.majorVersion = 7;
+        intervalStats.minorVersion = 8;
+        intervalStats.beginTime = time;
+        intervalStats.interactiveTracker.count = 2;
+        intervalStats.interactiveTracker.duration = 111111;
+        intervalStats.nonInteractiveTracker.count = 3;
+        intervalStats.nonInteractiveTracker.duration = 222222;
+        intervalStats.keyguardShownTracker.count = 4;
+        intervalStats.keyguardShownTracker.duration = 333333;
+        intervalStats.keyguardHiddenTracker.count = 5;
+        intervalStats.keyguardHiddenTracker.duration = 4444444;
+
+        for (int i = 0; i < NUMBER_OF_EVENTS; i++) {
+            UsageEvents.Event event = new UsageEvents.Event();
+            final int packageInt = ((i / 3) % NUMBER_OF_PACKAGES); // clusters of 3 events
+            event.mPackage = "fake.package.name" + packageInt;
+            if (packageInt == 3) {
+                // Third app is an instant app
+                event.mFlags |= UsageEvents.Event.FLAG_IS_PACKAGE_INSTANT_APP;
+            }
+
+            final int instanceId = i % 11;
+            event.mClass = ".fake.class.name" + instanceId;
+            event.mTimeStamp = time;
+            event.mEventType = i % (MAX_EVENT_TYPE + 1); //"random" event type
+            event.mInstanceId = instanceId;
+
+
+            final int rootPackageInt = (i % 5); // 5 "apps" start each task
+            event.mTaskRootPackage = "fake.package.name" + rootPackageInt;
+
+            final int rootClassInt = i % 6;
+            event.mTaskRootClass = ".fake.class.name" + rootClassInt;
+
+            switch (event.mEventType) {
+                case UsageEvents.Event.CONFIGURATION_CHANGE:
+                    event.mConfiguration = new Configuration(); //empty config
+                    break;
+                case UsageEvents.Event.SHORTCUT_INVOCATION:
+                    event.mShortcutId = "shortcut" + (i % 8); //"random" shortcut
+                    break;
+                case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+                    //"random" bucket and reason
+                    event.mBucketAndReason = (((i % 5 + 1) * 10) << 16) & (i % 5 + 1) << 8;
+                    break;
+                case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+                    event.mNotificationChannelId = "channel" + (i % 5); //"random" channel
+                    break;
+            }
+
+            intervalStats.addEvent(event);
+            intervalStats.update(event.mPackage, event.mClass, event.mTimeStamp, event.mEventType,
+                    event.mInstanceId);
+
+            time += timeProgression; // Arbitrary progression of time
+        }
+        mEndTime = time;
+
+        final Configuration config1 = new Configuration();
+        config1.fontScale = 3.3f;
+        config1.mcc = 4;
+        intervalStats.getOrCreateConfigurationStats(config1);
+
+        final Configuration config2 = new Configuration();
+        config2.mnc = 5;
+        config2.setLocale(new Locale("en", "US"));
+        intervalStats.getOrCreateConfigurationStats(config2);
+
+        intervalStats.activeConfiguration = config2;
+    }
+
+    @Test
+    public void testObfuscation() {
+        final IntervalStats intervalStats = new IntervalStats();
+        populateIntervalStats(intervalStats);
+
+        final PackagesTokenData packagesTokenData = new PackagesTokenData();
+        intervalStats.obfuscateData(packagesTokenData);
+
+        // data is populated with 7 different "apps"
+        assertEquals(packagesTokenData.tokensToPackagesMap.size(), NUMBER_OF_PACKAGES);
+        assertEquals(packagesTokenData.packagesToTokensMap.size(), NUMBER_OF_PACKAGES);
+        assertEquals(packagesTokenData.counter, NUMBER_OF_PACKAGES + 1);
+
+        assertEquals(intervalStats.events.size(), NUMBER_OF_EVENTS);
+        assertEquals(intervalStats.packageStats.size(), NUMBER_OF_PACKAGES);
+    }
+
+    @Test
+    public void testDeobfuscation() {
+        final IntervalStats intervalStats = new IntervalStats();
+        populateIntervalStats(intervalStats);
+
+        final PackagesTokenData packagesTokenData = new PackagesTokenData();
+        intervalStats.obfuscateData(packagesTokenData);
+        intervalStats.deobfuscateData(packagesTokenData);
+
+        // ensure deobfuscation doesn't update any of the mappings data
+        assertEquals(packagesTokenData.tokensToPackagesMap.size(), NUMBER_OF_PACKAGES);
+        assertEquals(packagesTokenData.packagesToTokensMap.size(), NUMBER_OF_PACKAGES);
+        assertEquals(packagesTokenData.counter, NUMBER_OF_PACKAGES + 1);
+
+        // ensure deobfuscation didn't remove any events or usage stats
+        assertEquals(intervalStats.events.size(), NUMBER_OF_EVENTS);
+        assertEquals(intervalStats.packageStats.size(), NUMBER_OF_PACKAGES);
+    }
+
+    @Test
+    public void testBadDataOnDeobfuscation() {
+        final IntervalStats intervalStats = new IntervalStats();
+        populateIntervalStats(intervalStats);
+
+        final PackagesTokenData packagesTokenData = new PackagesTokenData();
+        intervalStats.obfuscateData(packagesTokenData);
+        intervalStats.packageStats.clear();
+
+        // remove the mapping for token 2
+        packagesTokenData.tokensToPackagesMap.remove(2);
+
+        intervalStats.deobfuscateData(packagesTokenData);
+        // deobfuscation should have removed all events mapped to package token 2
+        assertEquals(intervalStats.events.size(),
+                NUMBER_OF_EVENTS - NUMBER_OF_EVENTS_PER_PACKAGE - 1);
+        assertEquals(intervalStats.packageStats.size(), NUMBER_OF_PACKAGES - 1);
+    }
+
+    @Test
+    public void testBadPackageDataOnDeobfuscation() {
+        final IntervalStats intervalStats = new IntervalStats();
+        populateIntervalStats(intervalStats);
+
+        final PackagesTokenData packagesTokenData = new PackagesTokenData();
+        intervalStats.obfuscateData(packagesTokenData);
+        intervalStats.packageStats.clear();
+
+        // remove mapping number 2 within package 3 (random)
+        packagesTokenData.tokensToPackagesMap.valueAt(3).remove(2);
+
+        intervalStats.deobfuscateData(packagesTokenData);
+        // deobfuscation should not have removed all events for a package - however, it's possible
+        // that some events were removed because of how shortcut and notification events are handled
+        assertTrue(intervalStats.events.size() > NUMBER_OF_EVENTS - NUMBER_OF_EVENTS_PER_PACKAGE);
+        assertEquals(intervalStats.packageStats.size(), NUMBER_OF_PACKAGES);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index c55f459..df0c37a 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -48,7 +48,7 @@
 @SmallTest
 public class UsageStatsDatabaseTest {
 
-    private static final int MAX_TESTED_VERSION = 4;
+    private static final int MAX_TESTED_VERSION = 5;
     protected Context mContext;
     private UsageStatsDatabase mUsageStatsDatabase;
     private File mTestDir;
@@ -74,6 +74,7 @@
         mContext = InstrumentationRegistry.getTargetContext();
         mTestDir = new File(mContext.getFilesDir(), "UsageStatsDatabaseTest");
         mUsageStatsDatabase = new UsageStatsDatabase(mTestDir);
+        mUsageStatsDatabase.readMappingsLocked();
         mUsageStatsDatabase.init(1);
         populateIntervalStats();
         clearUsageStatsFiles();
@@ -259,6 +260,24 @@
 
     void compareUsageEvent(Event e1, Event e2, int debugId, int minVersion) {
         switch (minVersion) {
+            case 5: // test fields added in version 5
+                assertEquals(e1.mPackageToken, e2.mPackageToken, "Usage event " + debugId);
+                assertEquals(e1.mClassToken, e2.mClassToken, "Usage event " + debugId);
+                assertEquals(e1.mTaskRootPackageToken, e2.mTaskRootPackageToken,
+                        "Usage event " + debugId);
+                assertEquals(e1.mTaskRootClassToken, e2.mTaskRootClassToken,
+                        "Usage event " + debugId);
+                switch (e1.mEventType) {
+                    case Event.SHORTCUT_INVOCATION:
+                        assertEquals(e1.mShortcutIdToken, e2.mShortcutIdToken,
+                                "Usage event " + debugId);
+                        break;
+                    case Event.NOTIFICATION_INTERRUPTION:
+                        assertEquals(e1.mNotificationChannelIdToken, e2.mNotificationChannelIdToken,
+                                "Usage event " + debugId);
+                        break;
+                }
+                // fallthrough
             case 4: // test fields added in version 4
                 assertEquals(e1.mInstanceId, e2.mInstanceId, "Usage event " + debugId);
                 assertEquals(e1.mTaskRootPackage, e2.mTaskRootPackage, "Usage event " + debugId);
@@ -370,11 +389,16 @@
     void runVersionChangeTest(int oldVersion, int newVersion, int interval) throws IOException {
         // Write IntervalStats to disk in old version format
         UsageStatsDatabase prevDB = new UsageStatsDatabase(mTestDir, oldVersion);
+        prevDB.readMappingsLocked();
         prevDB.init(1);
         prevDB.putUsageStats(interval, mIntervalStats);
+        if (oldVersion >= 5) {
+            prevDB.writeMappingsLocked();
+        }
 
         // Simulate an upgrade to a new version and read from the disk
         UsageStatsDatabase newDB = new UsageStatsDatabase(mTestDir, newVersion);
+        newDB.readMappingsLocked();
         newDB.init(mEndTime);
         List<IntervalStats> stats = newDB.queryUsageStats(interval, 0, mEndTime,
                 mIntervalStatsVerifier);
@@ -394,6 +418,7 @@
      */
     void runBackupRestoreTest(int version) throws IOException {
         UsageStatsDatabase prevDB = new UsageStatsDatabase(mTestDir);
+        prevDB.readMappingsLocked();
         prevDB.init(1);
         prevDB.putUsageStats(UsageStatsManager.INTERVAL_DAILY, mIntervalStats);
         // Create a backup with a specific version
@@ -402,6 +427,7 @@
         clearUsageStatsFiles();
 
         UsageStatsDatabase newDB = new UsageStatsDatabase(mTestDir);
+        newDB.readMappingsLocked();
         newDB.init(1);
         // Attempt to restore the usage stats from the backup
         newDB.applyRestoredPayload(KEY_USAGE_STATS, blob);
@@ -438,6 +464,28 @@
         runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_YEARLY);
     }
 
+    /**
+     * Test the version upgrade from 4 to 5
+     */
+    @Test
+    public void testVersionUpgradeFrom4to5() throws IOException {
+        runVersionChangeTest(4, 5, UsageStatsManager.INTERVAL_DAILY);
+        runVersionChangeTest(4, 5, UsageStatsManager.INTERVAL_WEEKLY);
+        runVersionChangeTest(4, 5, UsageStatsManager.INTERVAL_MONTHLY);
+        runVersionChangeTest(4, 5, UsageStatsManager.INTERVAL_YEARLY);
+    }
+
+    /**
+     * Test the version upgrade from 3 to 5
+     */
+    @Test
+    public void testVersionUpgradeFrom3to5() throws IOException {
+        runVersionChangeTest(3, 5, UsageStatsManager.INTERVAL_DAILY);
+        runVersionChangeTest(3, 5, UsageStatsManager.INTERVAL_WEEKLY);
+        runVersionChangeTest(3, 5, UsageStatsManager.INTERVAL_MONTHLY);
+        runVersionChangeTest(3, 5, UsageStatsManager.INTERVAL_YEARLY);
+    }
+
 
     /**
      * Test the version upgrade from 3 to 4
@@ -492,4 +540,68 @@
             assertEquals(extra, files.keyAt(0));
         }
     }
+
+    private void compareObfuscatedData(int interval) throws IOException {
+        // Write IntervalStats to disk
+        UsageStatsDatabase prevDB = new UsageStatsDatabase(mTestDir, 5);
+        prevDB.readMappingsLocked();
+        prevDB.init(1);
+        prevDB.putUsageStats(interval, mIntervalStats);
+        prevDB.writeMappingsLocked();
+
+        // Read IntervalStats from disk into a new db
+        UsageStatsDatabase newDB = new UsageStatsDatabase(mTestDir, 5);
+        newDB.readMappingsLocked();
+        newDB.init(mEndTime);
+        List<IntervalStats> stats = newDB.queryUsageStats(interval, 0, mEndTime,
+                mIntervalStatsVerifier);
+
+        assertEquals(1, stats.size());
+        // The written and read IntervalStats should match
+        compareIntervalStats(mIntervalStats, stats.get(0), 5);
+    }
+
+    @Test
+    public void testObfuscation() throws IOException {
+        compareObfuscatedData(UsageStatsManager.INTERVAL_DAILY);
+        compareObfuscatedData(UsageStatsManager.INTERVAL_WEEKLY);
+        compareObfuscatedData(UsageStatsManager.INTERVAL_MONTHLY);
+        compareObfuscatedData(UsageStatsManager.INTERVAL_YEARLY);
+    }
+
+    private void verifyPackageNotRetained(int interval) throws IOException {
+        UsageStatsDatabase db = new UsageStatsDatabase(mTestDir, 5);
+        db.readMappingsLocked();
+        db.init(1);
+        db.putUsageStats(interval, mIntervalStats);
+
+        final String removedPackage = "fake.package.name0";
+        // invoke handler call directly from test to remove package
+        db.onPackageRemoved(removedPackage, System.currentTimeMillis());
+
+        List<IntervalStats> stats = db.queryUsageStats(interval, 0, mEndTime,
+                mIntervalStatsVerifier);
+        for (int i = 0; i < stats.size(); i++) {
+            final IntervalStats stat = stats.get(i);
+            if (stat.packageStats.containsKey(removedPackage)) {
+                fail("Found removed package " + removedPackage + " in package stats.");
+                return;
+            }
+            for (int j = 0; j < stat.events.size(); j++) {
+                final Event event = stat.events.get(j);
+                if (removedPackage.equals(event.mPackage)) {
+                    fail("Found an event from removed package " + removedPackage);
+                    return;
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testPackageRetention() throws IOException {
+        verifyPackageNotRetained(UsageStatsManager.INTERVAL_DAILY);
+        verifyPackageNotRetained(UsageStatsManager.INTERVAL_WEEKLY);
+        verifyPackageNotRetained(UsageStatsManager.INTERVAL_MONTHLY);
+        verifyPackageNotRetained(UsageStatsManager.INTERVAL_YEARLY);
+    }
 }
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index a783a40..46b261b 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -42,8 +42,12 @@
 import android.app.usage.UsageEvents.Event;
 import android.app.usage.UsageStats;
 import android.content.res.Configuration;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
 import android.util.proto.ProtoInputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -52,6 +56,8 @@
 import java.util.List;
 
 public class IntervalStats {
+    private static final String TAG = "IntervalStats";
+
     public static final int CURRENT_MAJOR_VERSION = 1;
     public static final int CURRENT_MINOR_VERSION = 1;
     public int majorVersion = CURRENT_MAJOR_VERSION;
@@ -64,6 +70,8 @@
     public final EventTracker keyguardShownTracker = new EventTracker();
     public final EventTracker keyguardHiddenTracker = new EventTracker();
     public final ArrayMap<String, UsageStats> packageStats = new ArrayMap<>();
+    /** @hide */
+    public final SparseArray<UsageStats> packageStatsObfuscated = new SparseArray<>();
     public final ArrayMap<Configuration, ConfigurationStats> configurations = new ArrayMap<>();
     public Configuration activeConfiguration;
     public final EventList events = new EventList();
@@ -436,4 +444,234 @@
         */
         majorVersion = CURRENT_MAJOR_VERSION;
     }
+
+    /**
+     * Parses all of the tokens to strings in the obfuscated usage stats data. This includes
+     * deobfuscating each of the package tokens and chooser actions and categories.
+     */
+    private void deobfuscateUsageStats(PackagesTokenData packagesTokenData) {
+        final int usageStatsSize = packageStatsObfuscated.size();
+        for (int statsIndex = 0; statsIndex < usageStatsSize; statsIndex++) {
+            final int packageToken = packageStatsObfuscated.keyAt(statsIndex);
+            final UsageStats usageStats = packageStatsObfuscated.valueAt(statsIndex);
+            usageStats.mPackageName = packagesTokenData.getPackageString(packageToken);
+            if (usageStats.mPackageName == null) {
+                Slog.e(TAG, "Unable to parse usage stats package " + packageToken);
+                continue;
+            }
+
+            // Update chooser counts
+            final int chooserActionsSize = usageStats.mChooserCountsObfuscated.size();
+            for (int actionIndex = 0; actionIndex < chooserActionsSize; actionIndex++) {
+                final ArrayMap<String, Integer> categoryCountsMap = new ArrayMap<>();
+                final int actionToken = usageStats.mChooserCountsObfuscated.keyAt(actionIndex);
+                final String action = packagesTokenData.getString(packageToken, actionToken);
+                if (action == null) {
+                    Slog.i(TAG, "Unable to parse chooser action " + actionToken
+                            + " for package " + packageToken);
+                    continue;
+                }
+                final SparseIntArray categoryCounts =
+                        usageStats.mChooserCountsObfuscated.valueAt(actionIndex);
+                final int categoriesSize = categoryCounts.size();
+                for (int categoryIndex = 0; categoryIndex < categoriesSize; categoryIndex++) {
+                    final int categoryToken = categoryCounts.keyAt(categoryIndex);
+                    final String category = packagesTokenData.getString(packageToken,
+                            categoryToken);
+                    if (category == null) {
+                        Slog.i(TAG, "Unable to parse chooser category " + categoryToken
+                                + " for package " + packageToken);
+                        continue;
+                    }
+                    categoryCountsMap.put(category, categoryCounts.valueAt(categoryIndex));
+                }
+                usageStats.mChooserCounts.put(action, categoryCountsMap);
+            }
+            packageStats.put(usageStats.mPackageName, usageStats);
+        }
+    }
+
+    /**
+     * Parses all of the tokens to strings in the obfuscated events data. This includes
+     * deobfuscating the package token, along with any class, task root package/class tokens, and
+     * shortcut or notification channel tokens.
+     */
+    private void deobfuscateEvents(PackagesTokenData packagesTokenData) {
+        for (int i = this.events.size() - 1; i >= 0; i--) {
+            final Event event = this.events.get(i);
+            final int packageToken = event.mPackageToken;
+            event.mPackage = packagesTokenData.getPackageString(packageToken);
+            if (event.mPackage == null) {
+                Slog.e(TAG, "Unable to parse event package " + packageToken);
+                this.events.remove(i);
+                continue;
+            }
+
+            if (event.mClassToken != PackagesTokenData.UNASSIGNED_TOKEN) {
+                event.mClass = packagesTokenData.getString(packageToken, event.mClassToken);
+                if (event.mClass == null) {
+                    Slog.i(TAG, "Unable to parse class " + event.mClassToken
+                            + " for package " + packageToken);
+                }
+            }
+            if (event.mTaskRootPackageToken != PackagesTokenData.UNASSIGNED_TOKEN) {
+                event.mTaskRootPackage = packagesTokenData.getString(packageToken,
+                        event.mTaskRootPackageToken);
+                if (event.mTaskRootPackage == null) {
+                    Slog.i(TAG, "Unable to parse task root package " + event.mTaskRootPackageToken
+                            + " for package " + packageToken);
+                }
+            }
+            if (event.mTaskRootClassToken != PackagesTokenData.UNASSIGNED_TOKEN) {
+                event.mTaskRootClass = packagesTokenData.getString(packageToken,
+                        event.mTaskRootClassToken);
+                if (event.mTaskRootClass == null) {
+                    Slog.i(TAG, "Unable to parse task root class " + event.mTaskRootClassToken
+                            + " for package " + packageToken);
+                }
+            }
+            switch (event.mEventType) {
+                case CONFIGURATION_CHANGE:
+                    if (event.mConfiguration == null) {
+                        event.mConfiguration = new Configuration();
+                    }
+                    break;
+                case SHORTCUT_INVOCATION:
+                    event.mShortcutId = packagesTokenData.getString(packageToken,
+                            event.mShortcutIdToken);
+                    if (event.mShortcutId == null) {
+                        Slog.e(TAG, "Unable to parse shortcut " + event.mShortcutIdToken
+                                + " for package " + packageToken);
+                        this.events.remove(i);
+                        continue;
+                    }
+                    break;
+                case NOTIFICATION_INTERRUPTION:
+                    event.mNotificationChannelId = packagesTokenData.getString(packageToken,
+                            event.mNotificationChannelIdToken);
+                    if (event.mNotificationChannelId == null) {
+                        Slog.e(TAG, "Unable to parse notification channel "
+                                + event.mNotificationChannelIdToken + " for package "
+                                + packageToken);
+                        this.events.remove(i);
+                        continue;
+                    }
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Parses the obfuscated tokenized data held in this interval stats object.
+     *
+     * @hide
+     */
+    public void deobfuscateData(PackagesTokenData packagesTokenData) {
+        deobfuscateUsageStats(packagesTokenData);
+        deobfuscateEvents(packagesTokenData);
+    }
+
+    /**
+     * Obfuscates certain strings within each package stats such as the package name, and the
+     * chooser actions and categories.
+     */
+    private void obfuscateUsageStatsData(PackagesTokenData packagesTokenData) {
+        final int usageStatsSize = packageStats.size();
+        for (int statsIndex = 0; statsIndex < usageStatsSize; statsIndex++) {
+            final String packageName = packageStats.keyAt(statsIndex);
+            final UsageStats usageStats = packageStats.valueAt(statsIndex);
+            if (usageStats == null) {
+                continue;
+            }
+
+            final int packageToken = packagesTokenData.getPackageTokenOrAdd(
+                    packageName, usageStats.mEndTimeStamp);
+            // don't obfuscate stats whose packages have been removed
+            if (packageToken == PackagesTokenData.UNASSIGNED_TOKEN) {
+                continue;
+            }
+            usageStats.mPackageToken = packageToken;
+            // Update chooser counts.
+            final int chooserActionsSize = usageStats.mChooserCounts.size();
+            for (int actionIndex = 0; actionIndex < chooserActionsSize; actionIndex++) {
+                final String action = usageStats.mChooserCounts.keyAt(actionIndex);
+                final ArrayMap<String, Integer> categoriesMap =
+                        usageStats.mChooserCounts.valueAt(actionIndex);
+                if (categoriesMap == null) {
+                    continue;
+                }
+
+                final SparseIntArray categoryCounts = new SparseIntArray();
+                final int categoriesSize = categoriesMap.size();
+                for (int categoryIndex = 0; categoryIndex < categoriesSize; categoryIndex++) {
+                    String category = categoriesMap.keyAt(categoryIndex);
+                    int categoryToken = packagesTokenData.getTokenOrAdd(packageToken, packageName,
+                            category);
+                    categoryCounts.put(categoryToken, categoriesMap.valueAt(categoryIndex));
+                }
+                int actionToken = packagesTokenData.getTokenOrAdd(packageToken, packageName,
+                        action);
+                usageStats.mChooserCountsObfuscated.put(actionToken, categoryCounts);
+            }
+            packageStatsObfuscated.put(packageToken, usageStats);
+        }
+    }
+
+    /**
+     * Obfuscates certain strings within an event such as the package name, the class name,
+     * task root package and class names, and shortcut and notification channel ids.
+     */
+    private void obfuscateEventsData(PackagesTokenData packagesTokenData) {
+        for (int i = events.size() - 1; i >= 0; i--) {
+            final Event event = events.get(i);
+            if (event == null) {
+                continue;
+            }
+
+            final int packageToken = packagesTokenData.getPackageTokenOrAdd(
+                    event.mPackage, event.mTimeStamp);
+            // don't obfuscate events from packages that have been removed
+            if (packageToken == PackagesTokenData.UNASSIGNED_TOKEN) {
+                events.remove(i);
+                continue;
+            }
+            event.mPackageToken = packageToken;
+            if (!TextUtils.isEmpty(event.mClass)) {
+                event.mClassToken = packagesTokenData.getTokenOrAdd(packageToken,
+                        event.mPackage, event.mClass);
+            }
+            if (!TextUtils.isEmpty(event.mTaskRootPackage)) {
+                event.mTaskRootPackageToken = packagesTokenData.getTokenOrAdd(packageToken,
+                        event.mPackage, event.mTaskRootPackage);
+            }
+            if (!TextUtils.isEmpty(event.mTaskRootClass)) {
+                event.mTaskRootClassToken = packagesTokenData.getTokenOrAdd(packageToken,
+                        event.mPackage, event.mTaskRootClass);
+            }
+            switch (event.mEventType) {
+                case SHORTCUT_INVOCATION:
+                    if (!TextUtils.isEmpty(event.mShortcutId)) {
+                        event.mShortcutIdToken = packagesTokenData.getTokenOrAdd(packageToken,
+                                event.mPackage, event.mShortcutId);
+                    }
+                    break;
+                case NOTIFICATION_INTERRUPTION:
+                    if (!TextUtils.isEmpty(event.mNotificationChannelId)) {
+                        event.mNotificationChannelIdToken = packagesTokenData.getTokenOrAdd(
+                                packageToken, event.mPackage, event.mNotificationChannelId);
+                    }
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Obfuscates the data in this instance of interval stats.
+     *
+     * @hide
+     */
+    public void obfuscateData(PackagesTokenData packagesTokenData) {
+        obfuscateUsageStatsData(packagesTokenData);
+        obfuscateEventsData(packagesTokenData);
+    }
 }
diff --git a/services/usage/java/com/android/server/usage/PackagesTokenData.java b/services/usage/java/com/android/server/usage/PackagesTokenData.java
new file mode 100644
index 0000000..4bf08a4
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/PackagesTokenData.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usage;
+
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+
+/**
+ * An object holding data defining the obfuscated packages and their token mappings.
+ * Used by {@link UsageStatsDatabase}.
+ *
+ * @hide
+ */
+public final class PackagesTokenData {
+    /**
+     * The package name is always stored at index 0 in {@code tokensToPackagesMap}.
+     */
+    private static final int PACKAGE_NAME_INDEX = 0;
+
+    /**
+     * The default token for any string that hasn't been tokenized yet.
+     */
+    public static final int UNASSIGNED_TOKEN = -1;
+
+    /**
+     * The main token counter for each package.
+     */
+    public int counter = 1;
+    /**
+     * Stores a hierarchy of token to string mappings for each package, indexed by the main
+     * package token. The 0th index within the array list will always hold the package name.
+     */
+    public final SparseArray<ArrayList<String>> tokensToPackagesMap = new SparseArray<>();
+    /**
+     * Stores a hierarchy of strings to token mappings for each package. This is simply an inverse
+     * map of the {@code tokenToPackagesMap} in this class, mainly for an O(1) access to the tokens.
+     */
+    public final ArrayMap<String, ArrayMap<String, Integer>> packagesToTokensMap = new ArrayMap<>();
+    /**
+     * Stores a map of packages that were removed and when they were removed.
+     */
+    public final ArrayMap<String, Long> removedPackagesMap = new ArrayMap<>();
+
+    public PackagesTokenData() {
+    }
+
+    /**
+     * Fetches the token mapped to the given package name. If there is no mapping, a new token is
+     * created and the relevant mappings are updated.
+     *
+     * @param packageName the package name whose token is being fetched
+     * @param timeStamp the time stamp of the event or end time of the usage stats; used to verify
+     *                  the package hasn't been removed
+     * @return the mapped token
+     */
+    public int getPackageTokenOrAdd(String packageName, long timeStamp) {
+        final Long timeRemoved = removedPackagesMap.get(packageName);
+        if (timeRemoved != null && timeRemoved > timeStamp) {
+            return UNASSIGNED_TOKEN; // package was removed
+            /*
+             Note: instead of querying Package Manager each time for a list of packages to verify
+             if this package is still installed, it's more efficient to check the internal list of
+             removed packages and verify with the incoming time stamp. Although rare, it is possible
+             that some asynchronous function is triggered after a package is removed and the
+             time stamp passed into this function is not accurate. We'll have to keep the respective
+             event/usage stat until the next time the device reboots and the mappings are cleaned.
+             Additionally, this is a data class with some helper methods - it doesn't make sense to
+             overload it with references to other services.
+             */
+        }
+
+        ArrayMap<String, Integer> packageTokensMap = packagesToTokensMap.get(packageName);
+        if (packageTokensMap == null) {
+            packageTokensMap = new ArrayMap<>();
+            packagesToTokensMap.put(packageName, packageTokensMap);
+        }
+        int token = packageTokensMap.getOrDefault(packageName, UNASSIGNED_TOKEN);
+        if (token == UNASSIGNED_TOKEN) {
+            token = counter++;
+            // package name should always be at index 0 in the sub-mapping
+            ArrayList<String> tokenPackages = new ArrayList<>();
+            tokenPackages.add(packageName);
+            packageTokensMap.put(packageName, token);
+            tokensToPackagesMap.put(token, tokenPackages);
+        }
+        return token;
+    }
+
+    /**
+     * Fetches the token mapped to the given key within the package's context. If there is no
+     * mapping, a new token is created and the relevant mappings are updated.
+     *
+     * @param packageToken the package token for which the given key belongs to
+     * @param packageName the package name for which the given key belongs to
+     * @param key the key whose token is being fetched
+     * @return the mapped token
+     */
+    public int getTokenOrAdd(int packageToken, String packageName, String key) {
+        if (packageName.equals(key)) {
+            return PACKAGE_NAME_INDEX;
+        }
+        int token = packagesToTokensMap.get(packageName).getOrDefault(key, UNASSIGNED_TOKEN);
+        if (token == UNASSIGNED_TOKEN) {
+            token = tokensToPackagesMap.get(packageToken).size();
+            packagesToTokensMap.get(packageName).put(key, token);
+            tokensToPackagesMap.get(packageToken).add(key);
+        }
+        return token;
+    }
+
+    /**
+     * Fetches the package name for the given token.
+     *
+     * @param packageToken the package token representing the package name
+     * @return the string representing the given token or {@code null} if not found
+     */
+    public String getPackageString(int packageToken) {
+        final ArrayList<String> packageStrings = tokensToPackagesMap.get(packageToken);
+        if (packageStrings == null) {
+            return null;
+        }
+        return packageStrings.get(PACKAGE_NAME_INDEX);
+    }
+
+    /**
+     * Fetches the string represented by the given token.
+     *
+     * @param packageToken the package token for which this token belongs to
+     * @param token the token whose string needs to be fetched
+     * @return the string representing the given token or {@code null} if not found
+     */
+    public String getString(int packageToken, int token) {
+        try {
+            return tokensToPackagesMap.get(packageToken).get(token);
+        } catch (NullPointerException npe) {
+            Slog.e("PackagesTokenData",
+                    "Unable to find tokenized strings for package " + packageToken, npe);
+            return null;
+        } catch (IndexOutOfBoundsException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Removes the package from all known mappings.
+     *
+     * @param packageName the package to be removed
+     * @param timeRemoved the time stamp of when the package was removed
+     */
+    public void removePackage(String packageName, long timeRemoved) {
+        removedPackagesMap.put(packageName, timeRemoved);
+
+        if (!packagesToTokensMap.containsKey(packageName)) {
+            return;
+        }
+        final int packageToken = packagesToTokensMap.get(packageName).get(packageName);
+        packagesToTokensMap.remove(packageName);
+        tokensToPackagesMap.delete(packageToken);
+    }
+}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index 5197b3b..db7ed1f 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -17,12 +17,15 @@
 package com.android.server.usage;
 
 import android.app.usage.TimeSparseArray;
+import android.app.usage.UsageEvents;
 import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManager;
 import android.os.Build;
 import android.os.SystemProperties;
+import android.util.ArrayMap;
 import android.util.AtomicFile;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.TimeUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -75,7 +78,7 @@
  * directory should be deserialized.
  */
 public class UsageStatsDatabase {
-    private static final int DEFAULT_CURRENT_VERSION = 4;
+    private static final int DEFAULT_CURRENT_VERSION = 5;
     /**
      * Current version of the backup schema
      *
@@ -93,7 +96,8 @@
 
     // Persist versioned backup files.
     // Should be false, except when testing new versions
-    static final boolean KEEP_BACKUP_DIR = false;
+    // STOPSHIP: b/139937606 this should be false on launch
+    static final boolean KEEP_BACKUP_DIR = true;
 
     private static final String TAG = "UsageStatsDatabase";
     private static final boolean DEBUG = UsageStatsService.DEBUG;
@@ -119,6 +123,11 @@
     private boolean mFirstUpdate;
     private boolean mNewUpdate;
 
+    // The obfuscated packages to tokens mappings file
+    private final File mPackageMappingsFile;
+    // Holds all of the data related to the obfuscated packages and their token mappings.
+    final PackagesTokenData mPackagesTokenData = new PackagesTokenData();
+
     /**
      * UsageStatsDatabase constructor that allows setting the version number.
      * This should only be used for testing.
@@ -138,6 +147,7 @@
         mBackupsDir = new File(dir, "backups");
         mUpdateBreadcrumb = new File(dir, "breadcrumb");
         mSortedStatFiles = new TimeSparseArray[mIntervalDirs.length];
+        mPackageMappingsFile = new File(dir, "mappings");
         mCal = new UnixCalendar(0);
     }
 
@@ -479,6 +489,11 @@
     private void continueUpgradeLocked(int version, long token) {
         final File backupDir = new File(mBackupsDir, Long.toString(token));
 
+        // Upgrade step logic for the entire usage stats directory, not individual interval dirs.
+        if (version >= 5) {
+            readMappingsLocked();
+        }
+
         // Read each file in the backup according to the version and write to the interval
         // directories in the current versions format
         for (int i = 0; i < mIntervalDirs.length; i++) {
@@ -494,9 +509,16 @@
                     }
                     try {
                         IntervalStats stats = new IntervalStats();
-                        readLocked(new AtomicFile(files[j]), stats, version);
+                        readLocked(new AtomicFile(files[j]), stats, version, mPackagesTokenData);
+                        // Upgrade to version 5+.
+                        // Future version upgrades should add additional logic here to upgrade.
+                        if (mCurrentVersion >= 5) {
+                            // Create the initial obfuscated packages map.
+                            stats.obfuscateData(mPackagesTokenData);
+                        }
                         writeLocked(new AtomicFile(new File(mIntervalDirs[i],
-                                Long.toString(stats.beginTime))), stats, mCurrentVersion);
+                                Long.toString(stats.beginTime))), stats, mCurrentVersion,
+                                mPackagesTokenData);
                     } catch (Exception e) {
                         // This method is called on boot, log the exception and move on
                         Slog.e(TAG, "Failed to upgrade backup file : " + files[j].toString());
@@ -504,6 +526,21 @@
                 }
             }
         }
+
+        // Upgrade step logic for the entire usage stats directory, not individual interval dirs.
+        if (mCurrentVersion >= 5) {
+            try {
+                writeMappingsLocked();
+            } catch (IOException e) {
+                Slog.e(TAG, "Failed to write the tokens mappings file.");
+            }
+        }
+    }
+
+    void onPackageRemoved(String packageName, long timeRemoved) {
+        synchronized (mLock) {
+            mPackagesTokenData.removePackage(packageName, timeRemoved);
+        }
     }
 
     public void onTimeChanged(long timeDiffMillis) {
@@ -580,6 +617,37 @@
     }
 
     /**
+     * Filter out those stats from the given stats that belong to removed packages. Filtering out
+     * all of the stats at once has an amortized cost for future calls.
+     */
+    void filterStats(IntervalStats stats) {
+        if (mPackagesTokenData.removedPackagesMap.isEmpty()) {
+            return;
+        }
+        final ArrayMap<String, Long> removedPackagesMap = mPackagesTokenData.removedPackagesMap;
+
+        // filter out package usage stats
+        final int removedPackagesSize = removedPackagesMap.size();
+        for (int i = 0; i < removedPackagesSize; i++) {
+            final String removedPackage = removedPackagesMap.keyAt(i);
+            final UsageStats usageStats = stats.packageStats.get(removedPackage);
+            if (usageStats != null && usageStats.mEndTimeStamp < removedPackagesMap.valueAt(i)) {
+                stats.packageStats.remove(removedPackage);
+            }
+        }
+
+        // filter out events
+        final int eventsSize = stats.events.size();
+        for (int i = stats.events.size() - 1; i >= 0; i--) {
+            final UsageEvents.Event event = stats.events.get(i);
+            final Long timeRemoved = removedPackagesMap.get(event.mPackage);
+            if (timeRemoved != null && timeRemoved > event.mTimeStamp) {
+                stats.events.remove(i);
+            }
+        }
+    }
+
+    /**
      * Figures out what to extract from the given IntervalStats object.
      */
     public interface StatCombiner<T> {
@@ -808,14 +876,14 @@
     }
 
     private void writeLocked(AtomicFile file, IntervalStats stats) throws IOException {
-        writeLocked(file, stats, mCurrentVersion);
+        writeLocked(file, stats, mCurrentVersion, mPackagesTokenData);
     }
 
-    private static void writeLocked(AtomicFile file, IntervalStats stats, int version)
-            throws IOException {
+    private static void writeLocked(AtomicFile file, IntervalStats stats, int version,
+            PackagesTokenData packagesTokenData) throws IOException {
         FileOutputStream fos = file.startWrite();
         try {
-            writeLocked(fos, stats, version);
+            writeLocked(fos, stats, version, packagesTokenData);
             file.finishWrite(fos);
             fos = null;
         } finally {
@@ -825,11 +893,11 @@
     }
 
     private void writeLocked(OutputStream out, IntervalStats stats) throws IOException {
-        writeLocked(out, stats, mCurrentVersion);
+        writeLocked(out, stats, mCurrentVersion, mPackagesTokenData);
     }
 
-    private static void writeLocked(OutputStream out, IntervalStats stats, int version)
-            throws IOException {
+    private static void writeLocked(OutputStream out, IntervalStats stats, int version,
+            PackagesTokenData packagesTokenData) throws IOException {
         switch (version) {
             case 1:
             case 2:
@@ -837,7 +905,19 @@
                 UsageStatsXml.write(out, stats);
                 break;
             case 4:
-                UsageStatsProto.write(out, stats);
+                try {
+                    UsageStatsProto.write(out, stats);
+                } catch (IOException | IllegalArgumentException e) {
+                    Slog.e(TAG, "Unable to write interval stats to proto.", e);
+                }
+                break;
+            case 5:
+                stats.obfuscateData(packagesTokenData);
+                try {
+                    UsageStatsProtoV2.write(out, stats);
+                } catch (IOException | IllegalArgumentException e) {
+                    Slog.e(TAG, "Unable to write interval stats to proto.", e);
+                }
                 break;
             default:
                 throw new RuntimeException(
@@ -847,16 +927,16 @@
     }
 
     private void readLocked(AtomicFile file, IntervalStats statsOut) throws IOException {
-        readLocked(file, statsOut, mCurrentVersion);
+        readLocked(file, statsOut, mCurrentVersion, mPackagesTokenData);
     }
 
-    private static void readLocked(AtomicFile file, IntervalStats statsOut, int version)
-            throws IOException {
+    private static void readLocked(AtomicFile file, IntervalStats statsOut, int version,
+            PackagesTokenData packagesTokenData) throws IOException {
         try {
             FileInputStream in = file.openRead();
             try {
                 statsOut.beginTime = parseBeginTime(file);
-                readLocked(in, statsOut, version);
+                readLocked(in, statsOut, version, packagesTokenData);
                 statsOut.lastTimeSaved = file.getLastModifiedTime();
             } finally {
                 try {
@@ -872,11 +952,11 @@
     }
 
     private void readLocked(InputStream in, IntervalStats statsOut) throws IOException {
-        readLocked(in, statsOut, mCurrentVersion);
+        readLocked(in, statsOut, mCurrentVersion, mPackagesTokenData);
     }
 
-    private static void readLocked(InputStream in, IntervalStats statsOut, int version)
-            throws IOException {
+    private static void readLocked(InputStream in, IntervalStats statsOut, int version,
+            PackagesTokenData packagesTokenData) throws IOException {
         switch (version) {
             case 1:
             case 2:
@@ -884,7 +964,19 @@
                 UsageStatsXml.read(in, statsOut);
                 break;
             case 4:
-                UsageStatsProto.read(in, statsOut);
+                try {
+                    UsageStatsProto.read(in, statsOut);
+                } catch (IOException e) {
+                    Slog.e(TAG, "Unable to read interval stats from proto.", e);
+                }
+                break;
+            case 5:
+                try {
+                    UsageStatsProtoV2.read(in, statsOut);
+                } catch (IOException e) {
+                    Slog.e(TAG, "Unable to read interval stats from proto.", e);
+                }
+                statsOut.deobfuscateData(packagesTokenData);
                 break;
             default:
                 throw new RuntimeException(
@@ -895,6 +987,63 @@
     }
 
     /**
+     * Reads the obfuscated data file from disk containing the tokens to packages mappings and
+     * rebuilds the packages to tokens mappings based on that data.
+     */
+    public void readMappingsLocked() {
+        if (!mPackageMappingsFile.exists()) {
+            return; // package mappings file is missing - recreate mappings on next write.
+        }
+
+        try (FileInputStream in = new AtomicFile(mPackageMappingsFile).openRead()) {
+            UsageStatsProtoV2.readObfuscatedData(in, mPackagesTokenData);
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to read the obfuscated packages mapping file.", e);
+            return;
+        }
+
+        final SparseArray<ArrayList<String>> tokensToPackagesMap =
+                mPackagesTokenData.tokensToPackagesMap;
+        final int tokensToPackagesMapSize = tokensToPackagesMap.size();
+        for (int i = 0; i < tokensToPackagesMapSize; i++) {
+            final int packageToken = tokensToPackagesMap.keyAt(i);
+            final ArrayList<String> tokensMap = tokensToPackagesMap.valueAt(i);
+            final ArrayMap<String, Integer> packageStringsMap = new ArrayMap<>();
+            final int tokensMapSize = tokensMap.size();
+            // package name will always be at index 0 but its token should not be 0
+            packageStringsMap.put(tokensMap.get(0), packageToken);
+            for (int j = 1; j < tokensMapSize; j++) {
+                packageStringsMap.put(tokensMap.get(j), j);
+            }
+            mPackagesTokenData.packagesToTokensMap.put(tokensMap.get(0), packageStringsMap);
+        }
+    }
+
+    void writeMappingsLocked() throws IOException {
+        final AtomicFile file = new AtomicFile(mPackageMappingsFile);
+        FileOutputStream fos = file.startWrite();
+        try {
+            UsageStatsProtoV2.writeObfuscatedData(fos, mPackagesTokenData);
+            file.finishWrite(fos);
+            fos = null;
+        } catch (IOException | IllegalArgumentException e) {
+            Slog.e(TAG, "Unable to write obfuscated data to proto.", e);
+        } finally {
+            file.failWrite(fos);
+        }
+    }
+
+    void obfuscateCurrentStats(IntervalStats[] currentStats) {
+        if (mCurrentVersion < 5) {
+            return;
+        }
+        for (int i = 0; i < currentStats.length; i++) {
+            final IntervalStats stats = currentStats[i];
+            stats.obfuscateData(mPackagesTokenData);
+        }
+    }
+
+    /**
      * Update the stats in the database. They may not be written to disk immediately.
      */
     public void putUsageStats(int intervalType, IntervalStats stats) throws IOException {
@@ -1098,7 +1247,7 @@
         DataOutputStream out = new DataOutputStream(baos);
         try {
             out.writeLong(stats.beginTime);
-            writeLocked(out, stats, version);
+            writeLocked(out, stats, version, mPackagesTokenData);
         } catch (Exception ioe) {
             Slog.d(TAG, "Serializing IntervalStats Failed", ioe);
             baos.reset();
@@ -1112,7 +1261,7 @@
         IntervalStats stats = new IntervalStats();
         try {
             stats.beginTime = in.readLong();
-            readLocked(in, stats, version);
+            readLocked(in, stats, version, mPackagesTokenData);
         } catch (IOException ioe) {
             Slog.d(TAG, "DeSerializing IntervalStats Failed", ioe);
             stats = null;
@@ -1142,13 +1291,18 @@
     }
 
     /**
-     * print total number and list of stats files for each interval type.
-     * @param pw
+     * Prints the obfuscated package mappings and a summary of the database files.
+     * @param pw the print writer to print to
      */
     public void dump(IndentingPrintWriter pw, boolean compact) {
         synchronized (mLock) {
+            pw.println();
             pw.println("UsageStatsDatabase:");
             pw.increaseIndent();
+            dumpMappings(pw);
+            pw.decreaseIndent();
+            pw.println("Database Summary:");
+            pw.increaseIndent();
             for (int i = 0; i < mSortedStatFiles.length; i++) {
                 final TimeSparseArray<AtomicFile> files = mSortedStatFiles[i];
                 final int size = files.size();
@@ -1173,6 +1327,23 @@
         }
     }
 
+    void dumpMappings(IndentingPrintWriter pw) {
+        synchronized (mLock) {
+            pw.println("Obfuscated Packages Mappings:");
+            pw.increaseIndent();
+            pw.println("Counter: " + mPackagesTokenData.counter);
+            pw.println("Tokens Map Size: " + mPackagesTokenData.tokensToPackagesMap.size());
+            for (int i = 0; i < mPackagesTokenData.tokensToPackagesMap.size(); i++) {
+                final int packageToken = mPackagesTokenData.tokensToPackagesMap.keyAt(i);
+                final String packageStrings = String.join(", ",
+                        mPackagesTokenData.tokensToPackagesMap.valueAt(i));
+                pw.println("Token " + packageToken + ": [" + packageStrings + "]");
+            }
+            pw.println();
+            pw.decreaseIndent();
+        }
+    }
+
     IntervalStats readIntervalStatsForFile(int interval, long fileName) {
         synchronized (mLock) {
             final IntervalStats stats = new IntervalStats();
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProto.java b/services/usage/java/com/android/server/usage/UsageStatsProto.java
index 6d3f416..5d1f730 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsProto.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsProto.java
@@ -120,10 +120,14 @@
                             IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT);
                     break;
                 case (int) IntervalStatsProto.UsageStats.CHOOSER_ACTIONS:
-                    final long chooserToken = proto.start(
-                            IntervalStatsProto.UsageStats.CHOOSER_ACTIONS);
-                    loadChooserCounts(proto, stats);
-                    proto.end(chooserToken);
+                    try {
+                        final long chooserToken = proto.start(
+                                IntervalStatsProto.UsageStats.CHOOSER_ACTIONS);
+                        loadChooserCounts(proto, stats);
+                        proto.end(chooserToken);
+                    } catch (IOException e) {
+                        Slog.e(TAG, "Unable to read chooser counts for " + stats.mPackageName, e);
+                    }
                     break;
                 case (int) IntervalStatsProto.UsageStats.LAST_TIME_SERVICE_USED_MS:
                     // Time attributes stored is an offset of the beginTime.
@@ -153,20 +157,24 @@
     }
 
     private static void loadCountAndTime(ProtoInputStream proto, long fieldId,
-            IntervalStats.EventTracker tracker) throws IOException {
-        final long token = proto.start(fieldId);
-        while (true) {
-            switch (proto.nextField()) {
-                case (int) IntervalStatsProto.CountAndTime.COUNT:
-                    tracker.count = proto.readInt(IntervalStatsProto.CountAndTime.COUNT);
-                    break;
-                case (int) IntervalStatsProto.CountAndTime.TIME_MS:
-                    tracker.duration = proto.readLong(IntervalStatsProto.CountAndTime.TIME_MS);
-                    break;
-                case ProtoInputStream.NO_MORE_FIELDS:
-                    proto.end(token);
-                    return;
+            IntervalStats.EventTracker tracker) {
+        try {
+            final long token = proto.start(fieldId);
+            while (true) {
+                switch (proto.nextField()) {
+                    case (int) IntervalStatsProto.CountAndTime.COUNT:
+                        tracker.count = proto.readInt(IntervalStatsProto.CountAndTime.COUNT);
+                        break;
+                    case (int) IntervalStatsProto.CountAndTime.TIME_MS:
+                        tracker.duration = proto.readLong(IntervalStatsProto.CountAndTime.TIME_MS);
+                        break;
+                    case ProtoInputStream.NO_MORE_FIELDS:
+                        proto.end(token);
+                        return;
+                }
             }
+        } catch (IOException e) {
+            Slog.e(TAG, "Unable to read event tracker " + fieldId, e);
         }
     }
 
@@ -306,7 +314,7 @@
     }
 
     private static void writeStringPool(ProtoOutputStream proto, final IntervalStats stats)
-            throws IOException {
+            throws IllegalArgumentException {
         final long token = proto.start(IntervalStatsProto.STRINGPOOL);
         final int size = stats.mStringCache.size();
         proto.write(IntervalStatsProto.StringPool.SIZE, size);
@@ -317,7 +325,8 @@
     }
 
     private static void writeUsageStats(ProtoOutputStream proto, long fieldId,
-            final IntervalStats stats, final UsageStats usageStats) throws IOException {
+            final IntervalStats stats, final UsageStats usageStats)
+            throws IllegalArgumentException {
         final long token = proto.start(fieldId);
         // Write the package name first, so loadUsageStats can avoid creating an extra object
         final int packageIndex = stats.mStringCache.indexOf(usageStats.mPackageName);
@@ -347,12 +356,16 @@
         proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS,
                 usageStats.mTotalTimeVisible);
         proto.write(IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT, usageStats.mAppLaunchCount);
-        writeChooserCounts(proto, usageStats);
+        try {
+            writeChooserCounts(proto, usageStats);
+        } catch (IllegalArgumentException e) {
+            Slog.e(TAG, "Unable to write chooser counts for " + usageStats.mPackageName, e);
+        }
         proto.end(token);
     }
 
     private static void writeCountAndTime(ProtoOutputStream proto, long fieldId, int count,
-            long time) throws IOException {
+            long time) throws IllegalArgumentException {
         final long token = proto.start(fieldId);
         proto.write(IntervalStatsProto.CountAndTime.COUNT, count);
         proto.write(IntervalStatsProto.CountAndTime.TIME_MS, time);
@@ -361,7 +374,7 @@
 
 
     private static void writeChooserCounts(ProtoOutputStream proto, final UsageStats usageStats)
-            throws IOException {
+            throws IllegalArgumentException {
         if (usageStats == null || usageStats.mChooserCounts == null
                 || usageStats.mChooserCounts.keySet().isEmpty()) {
             return;
@@ -381,7 +394,7 @@
     }
 
     private static void writeCountsForAction(ProtoOutputStream proto,
-            ArrayMap<String, Integer> counts) throws IOException {
+            ArrayMap<String, Integer> counts) throws IllegalArgumentException {
         final int countsSize = counts.size();
         for (int i = 0; i < countsSize; i++) {
             String key = counts.keyAt(i);
@@ -397,7 +410,7 @@
 
     private static void writeConfigStats(ProtoOutputStream proto, long fieldId,
             final IntervalStats stats, final ConfigurationStats configStats, boolean isActive)
-            throws IOException {
+            throws IllegalArgumentException {
         final long token = proto.start(fieldId);
         configStats.mConfiguration.writeToProto(proto, IntervalStatsProto.Configuration.CONFIG);
         proto.write(IntervalStatsProto.Configuration.LAST_TIME_ACTIVE_MS,
@@ -407,11 +420,10 @@
         proto.write(IntervalStatsProto.Configuration.COUNT, configStats.mActivationCount);
         proto.write(IntervalStatsProto.Configuration.ACTIVE, isActive);
         proto.end(token);
-
     }
 
     private static void writeEvent(ProtoOutputStream proto, long fieldId, final IntervalStats stats,
-            final UsageEvents.Event event) throws IOException {
+            final UsageEvents.Event event) throws IllegalArgumentException {
         final long token = proto.start(fieldId);
         final int packageIndex = stats.mStringCache.indexOf(event.mPackage);
         if (packageIndex >= 0) {
@@ -542,17 +554,33 @@
                             statsOut.keyguardHiddenTracker);
                     break;
                 case (int) IntervalStatsProto.STRINGPOOL:
-                    stringPool = readStringPool(proto);
-                    statsOut.mStringCache.addAll(stringPool);
+                    try {
+                        stringPool = readStringPool(proto);
+                        statsOut.mStringCache.addAll(stringPool);
+                    } catch (IOException e) {
+                        Slog.e(TAG, "Unable to read string pool from proto.", e);
+                    }
                     break;
                 case (int) IntervalStatsProto.PACKAGES:
-                    loadUsageStats(proto, IntervalStatsProto.PACKAGES, statsOut, stringPool);
+                    try {
+                        loadUsageStats(proto, IntervalStatsProto.PACKAGES, statsOut, stringPool);
+                    } catch (IOException e) {
+                        Slog.e(TAG, "Unable to read some usage stats from proto.", e);
+                    }
                     break;
                 case (int) IntervalStatsProto.CONFIGURATIONS:
-                    loadConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, statsOut);
+                    try {
+                        loadConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, statsOut);
+                    } catch (IOException e) {
+                        Slog.e(TAG, "Unable to read some configuration stats from proto.", e);
+                    }
                     break;
                 case (int) IntervalStatsProto.EVENT_LOG:
-                    loadEvent(proto, IntervalStatsProto.EVENT_LOG, statsOut, stringPool);
+                    try {
+                        loadEvent(proto, IntervalStatsProto.EVENT_LOG, statsOut, stringPool);
+                    } catch (IOException e) {
+                        Slog.e(TAG, "Unable to read some events from proto.", e);
+                    }
                     break;
                 case ProtoInputStream.NO_MORE_FIELDS:
                     if (statsOut.endTime == 0) {
@@ -571,72 +599,60 @@
      * @param proto The serializer to which to write the packageStats data.
      * @param stats The stats object to write to the XML file.
      */
-    public static void write(OutputStream out, IntervalStats stats) throws IOException {
+    public static void write(OutputStream out, IntervalStats stats)
+            throws IOException, IllegalArgumentException {
         final ProtoOutputStream proto = new ProtoOutputStream(out);
         proto.write(IntervalStatsProto.END_TIME_MS, stats.endTime - stats.beginTime);
         proto.write(IntervalStatsProto.MAJOR_VERSION, stats.majorVersion);
         proto.write(IntervalStatsProto.MINOR_VERSION, stats.minorVersion);
         // String pool should be written before the rest of the usage stats
-        writeStringPool(proto, stats);
+        try {
+            writeStringPool(proto, stats);
+        } catch (IllegalArgumentException e) {
+            Slog.e(TAG, "Unable to write string pool to proto.", e);
+        }
 
-        writeCountAndTime(proto, IntervalStatsProto.INTERACTIVE, stats.interactiveTracker.count,
-                stats.interactiveTracker.duration);
-        writeCountAndTime(proto, IntervalStatsProto.NON_INTERACTIVE,
-                stats.nonInteractiveTracker.count, stats.nonInteractiveTracker.duration);
-        writeCountAndTime(proto, IntervalStatsProto.KEYGUARD_SHOWN,
-                stats.keyguardShownTracker.count, stats.keyguardShownTracker.duration);
-        writeCountAndTime(proto, IntervalStatsProto.KEYGUARD_HIDDEN,
-                stats.keyguardHiddenTracker.count, stats.keyguardHiddenTracker.duration);
+        try {
+            writeCountAndTime(proto, IntervalStatsProto.INTERACTIVE, stats.interactiveTracker.count,
+                    stats.interactiveTracker.duration);
+            writeCountAndTime(proto, IntervalStatsProto.NON_INTERACTIVE,
+                    stats.nonInteractiveTracker.count, stats.nonInteractiveTracker.duration);
+            writeCountAndTime(proto, IntervalStatsProto.KEYGUARD_SHOWN,
+                    stats.keyguardShownTracker.count, stats.keyguardShownTracker.duration);
+            writeCountAndTime(proto, IntervalStatsProto.KEYGUARD_HIDDEN,
+                    stats.keyguardHiddenTracker.count, stats.keyguardHiddenTracker.duration);
+        }  catch (IllegalArgumentException e) {
+            Slog.e(TAG, "Unable to write some interval stats trackers to proto.", e);
+        }
 
         final int statsCount = stats.packageStats.size();
         for (int i = 0; i < statsCount; i++) {
-            writeUsageStats(proto, IntervalStatsProto.PACKAGES, stats,
-                    stats.packageStats.valueAt(i));
+            try {
+                writeUsageStats(proto, IntervalStatsProto.PACKAGES, stats,
+                        stats.packageStats.valueAt(i));
+            } catch (IllegalArgumentException e) {
+                Slog.e(TAG, "Unable to write some usage stats to proto.", e);
+            }
         }
         final int configCount = stats.configurations.size();
         for (int i = 0; i < configCount; i++) {
             boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i));
-            writeConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, stats,
-                    stats.configurations.valueAt(i), active);
+            try {
+                writeConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, stats,
+                        stats.configurations.valueAt(i), active);
+            } catch (IllegalArgumentException e) {
+                Slog.e(TAG, "Unable to write some configuration stats to proto.", e);
+            }
         }
         final int eventCount = stats.events.size();
         for (int i = 0; i < eventCount; i++) {
-            writeEvent(proto, IntervalStatsProto.EVENT_LOG, stats, stats.events.get(i));
-        }
-
-        proto.flush();
-    }
-
-    // TODO: move to UsageStatsProtoV2
-    static void readPendingEvents(InputStream in, List<UsageEvents.Event> events)
-            throws IOException {
-        final ProtoInputStream proto = new ProtoInputStream(in);
-        final List<String> stringPool = new ArrayList<>();
-        final IntervalStats tmpStatsObj = new IntervalStats();
-        while (true) {
-            switch (proto.nextField()) {
-                case (int) IntervalStatsProto.PENDING_EVENTS:
-                    loadEvent(proto, IntervalStatsProto.PENDING_EVENTS, tmpStatsObj, stringPool);
-                    break;
-                case ProtoInputStream.NO_MORE_FIELDS:
-                    final int eventCount = tmpStatsObj.events.size();
-                    for (int i = 0; i < eventCount; i++) {
-                        events.add(tmpStatsObj.events.get(i));
-                    }
-                    return;
+            try {
+                writeEvent(proto, IntervalStatsProto.EVENT_LOG, stats, stats.events.get(i));
+            } catch (IllegalArgumentException e) {
+                Slog.e(TAG, "Unable to write some events to proto.", e);
             }
         }
-    }
 
-    // TODO: move to UsageStatsProtoV2
-    static void writePendingEvents(OutputStream out, List<UsageEvents.Event> events)
-            throws IOException {
-        final ProtoOutputStream proto = new ProtoOutputStream(out);
-        final IntervalStats tmpStatsObj = new IntervalStats();
-        final int eventCount = events.size();
-        for (int i = 0; i < eventCount; i++) {
-            writeEvent(proto, IntervalStatsProto.PENDING_EVENTS, tmpStatsObj, events.get(i));
-        }
         proto.flush();
     }
 }
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
new file mode 100644
index 0000000..7d8e430
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
@@ -0,0 +1,788 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usage;
+
+import android.app.usage.ConfigurationStats;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStats;
+import android.content.res.Configuration;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+/**
+ * UsageStats reader/writer V2 for Protocol Buffer format.
+ */
+final class UsageStatsProtoV2 {
+    private static final String TAG = "UsageStatsProtoV2";
+
+    // Static-only utility class.
+    private UsageStatsProtoV2() {}
+
+    private static UsageStats parseUsageStats(ProtoInputStream proto, final long beginTime)
+            throws IOException {
+        UsageStats stats = new UsageStats();
+        // Time attributes stored is an offset of the beginTime.
+        while (true) {
+            switch (proto.nextField()) {
+                case (int) UsageStatsObfuscatedProto.PACKAGE_TOKEN:
+                    stats.mPackageToken = proto.readInt(
+                            UsageStatsObfuscatedProto.PACKAGE_TOKEN) - 1;
+                    break;
+                case (int) UsageStatsObfuscatedProto.LAST_TIME_ACTIVE_MS:
+                    stats.mLastTimeUsed = beginTime + proto.readLong(
+                            UsageStatsObfuscatedProto.LAST_TIME_ACTIVE_MS);
+                    break;
+                case (int) UsageStatsObfuscatedProto.TOTAL_TIME_ACTIVE_MS:
+                    stats.mTotalTimeInForeground = proto.readLong(
+                            UsageStatsObfuscatedProto.TOTAL_TIME_ACTIVE_MS);
+                    break;
+                case (int) UsageStatsObfuscatedProto.APP_LAUNCH_COUNT:
+                    stats.mAppLaunchCount = proto.readInt(
+                            UsageStatsObfuscatedProto.APP_LAUNCH_COUNT);
+                    break;
+                case (int) UsageStatsObfuscatedProto.CHOOSER_ACTIONS:
+                    try {
+                        final long token = proto.start(UsageStatsObfuscatedProto.CHOOSER_ACTIONS);
+                        loadChooserCounts(proto, stats);
+                        proto.end(token);
+                    } catch (IOException e) {
+                        Slog.e(TAG, "Unable to read chooser counts for " + stats.mPackageToken);
+                    }
+                    break;
+                case (int) UsageStatsObfuscatedProto.LAST_TIME_SERVICE_USED_MS:
+                    stats.mLastTimeForegroundServiceUsed = beginTime + proto.readLong(
+                            UsageStatsObfuscatedProto.LAST_TIME_SERVICE_USED_MS);
+                    break;
+                case (int) UsageStatsObfuscatedProto.TOTAL_TIME_SERVICE_USED_MS:
+                    stats.mTotalTimeForegroundServiceUsed = proto.readLong(
+                            UsageStatsObfuscatedProto.TOTAL_TIME_SERVICE_USED_MS);
+                    break;
+                case (int) UsageStatsObfuscatedProto.LAST_TIME_VISIBLE_MS:
+                    stats.mLastTimeVisible = beginTime + proto.readLong(
+                            UsageStatsObfuscatedProto.LAST_TIME_VISIBLE_MS);
+                    break;
+                case (int) UsageStatsObfuscatedProto.TOTAL_TIME_VISIBLE_MS:
+                    stats.mTotalTimeVisible = proto.readLong(
+                            UsageStatsObfuscatedProto.TOTAL_TIME_VISIBLE_MS);
+                    break;
+                case ProtoInputStream.NO_MORE_FIELDS:
+                    // mLastTimeUsed was not read, assume default value of 0 plus beginTime
+                    if (stats.mLastTimeUsed == 0) {
+                        stats.mLastTimeUsed = beginTime;
+                    }
+                    return stats;
+            }
+        }
+    }
+
+    private static void loadCountAndTime(ProtoInputStream proto, long fieldId,
+            IntervalStats.EventTracker tracker) {
+        try {
+            final long token = proto.start(fieldId);
+            while (true) {
+                switch (proto.nextField()) {
+                    case (int) IntervalStatsObfuscatedProto.CountAndTime.COUNT:
+                        tracker.count = proto.readInt(
+                                IntervalStatsObfuscatedProto.CountAndTime.COUNT);
+                        break;
+                    case (int) IntervalStatsObfuscatedProto.CountAndTime.TIME_MS:
+                        tracker.duration = proto.readLong(
+                                IntervalStatsObfuscatedProto.CountAndTime.TIME_MS);
+                        break;
+                    case ProtoInputStream.NO_MORE_FIELDS:
+                        proto.end(token);
+                        return;
+                }
+            }
+        } catch (IOException e) {
+            Slog.e(TAG, "Unable to read event tracker " + fieldId, e);
+        }
+    }
+
+    private static void loadChooserCounts(ProtoInputStream proto, UsageStats usageStats)
+            throws IOException {
+        int actionToken;
+        SparseIntArray counts;
+        if (proto.nextField(UsageStatsObfuscatedProto.ChooserAction.ACTION_TOKEN)) {
+            // Fast path; this should work for most cases since the action token is written first
+            actionToken = proto.readInt(UsageStatsObfuscatedProto.ChooserAction.ACTION_TOKEN) - 1;
+            counts = usageStats.mChooserCountsObfuscated.get(actionToken);
+            if (counts == null) {
+                counts = new SparseIntArray();
+                usageStats.mChooserCountsObfuscated.put(actionToken, counts);
+            }
+        } else {
+            counts = new SparseIntArray();
+        }
+
+        while (true) {
+            switch (proto.nextField()) {
+                case (int) UsageStatsObfuscatedProto.ChooserAction.ACTION_TOKEN:
+                    // Fast path failed for some reason, add the SparseIntArray object to usageStats
+                    actionToken = proto.readInt(
+                            UsageStatsObfuscatedProto.ChooserAction.ACTION_TOKEN) - 1;
+                    usageStats.mChooserCountsObfuscated.put(actionToken, counts);
+                    break;
+                case (int) UsageStatsObfuscatedProto.ChooserAction.COUNTS:
+                    final long token = proto.start(UsageStatsObfuscatedProto.ChooserAction.COUNTS);
+                    loadCountsForAction(proto, counts);
+                    proto.end(token);
+                    break;
+                case ProtoInputStream.NO_MORE_FIELDS:
+                    return; // if the action was never read, the loaded counts will be ignored.
+            }
+        }
+    }
+
+    private static void loadCountsForAction(ProtoInputStream proto, SparseIntArray counts)
+            throws IOException {
+        int categoryToken = PackagesTokenData.UNASSIGNED_TOKEN;
+        int count = 0;
+        while (true) {
+            switch (proto.nextField()) {
+                case (int) UsageStatsObfuscatedProto.ChooserAction.CategoryCount.CATEGORY_TOKEN:
+                    categoryToken = proto.readInt(
+                            UsageStatsObfuscatedProto.ChooserAction.CategoryCount.CATEGORY_TOKEN)
+                            - 1;
+                    break;
+                case (int) UsageStatsObfuscatedProto.ChooserAction.CategoryCount.COUNT:
+                    count = proto.readInt(
+                            UsageStatsObfuscatedProto.ChooserAction.CategoryCount.COUNT);
+                    break;
+                case ProtoInputStream.NO_MORE_FIELDS:
+                    if (categoryToken != PackagesTokenData.UNASSIGNED_TOKEN) {
+                        counts.put(categoryToken, count);
+                    }
+                    return;
+            }
+        }
+    }
+
+    private static void loadConfigStats(ProtoInputStream proto, IntervalStats stats)
+            throws IOException {
+        boolean configActive = false;
+        final Configuration config = new Configuration();
+        ConfigurationStats configStats = new ConfigurationStats();
+        if (proto.nextField(IntervalStatsObfuscatedProto.Configuration.CONFIG)) {
+            // Fast path; this should work since the configuration is written first
+            config.readFromProto(proto, IntervalStatsObfuscatedProto.Configuration.CONFIG);
+            configStats = stats.getOrCreateConfigurationStats(config);
+        }
+
+        while (true) {
+            switch (proto.nextField()) {
+                case (int) IntervalStatsObfuscatedProto.Configuration.CONFIG:
+                    // Fast path failed from some reason, add ConfigStats object to statsOut now
+                    config.readFromProto(proto, IntervalStatsObfuscatedProto.Configuration.CONFIG);
+                    final ConfigurationStats temp = stats.getOrCreateConfigurationStats(config);
+                    temp.mLastTimeActive = configStats.mLastTimeActive;
+                    temp.mTotalTimeActive = configStats.mTotalTimeActive;
+                    temp.mActivationCount = configStats.mActivationCount;
+                    configStats = temp;
+                    break;
+                case (int) IntervalStatsObfuscatedProto.Configuration.LAST_TIME_ACTIVE_MS:
+                    configStats.mLastTimeActive = stats.beginTime + proto.readLong(
+                            IntervalStatsObfuscatedProto.Configuration.LAST_TIME_ACTIVE_MS);
+                    break;
+                case (int) IntervalStatsObfuscatedProto.Configuration.TOTAL_TIME_ACTIVE_MS:
+                    configStats.mTotalTimeActive = proto.readLong(
+                            IntervalStatsObfuscatedProto.Configuration.TOTAL_TIME_ACTIVE_MS);
+                    break;
+                case (int) IntervalStatsObfuscatedProto.Configuration.COUNT:
+                    configStats.mActivationCount = proto.readInt(
+                            IntervalStatsObfuscatedProto.Configuration.COUNT);
+                    break;
+                case (int) IntervalStatsObfuscatedProto.Configuration.ACTIVE:
+                    configActive = proto.readBoolean(
+                            IntervalStatsObfuscatedProto.Configuration.ACTIVE);
+                    break;
+                case ProtoInputStream.NO_MORE_FIELDS:
+                    // mLastTimeActive was not assigned, assume default value of 0 plus beginTime
+                    if (configStats.mLastTimeActive == 0) {
+                        configStats.mLastTimeActive = stats.beginTime;
+                    }
+                    if (configActive) {
+                        stats.activeConfiguration = configStats.mConfiguration;
+                    }
+                    return;
+            }
+        }
+    }
+
+    private static UsageEvents.Event parseEvent(ProtoInputStream proto, long beginTime)
+            throws IOException {
+        final UsageEvents.Event event = new UsageEvents.Event();
+        while (true) {
+            switch (proto.nextField()) {
+                case (int) EventObfuscatedProto.PACKAGE_TOKEN:
+                    event.mPackageToken = proto.readInt(EventObfuscatedProto.PACKAGE_TOKEN) - 1;
+                    break;
+                case (int) EventObfuscatedProto.CLASS_TOKEN:
+                    event.mClassToken = proto.readInt(EventObfuscatedProto.CLASS_TOKEN) - 1;
+                    break;
+                case (int) EventObfuscatedProto.TIME_MS:
+                    event.mTimeStamp = beginTime + proto.readLong(EventObfuscatedProto.TIME_MS);
+                    break;
+                case (int) EventObfuscatedProto.FLAGS:
+                    event.mFlags = proto.readInt(EventObfuscatedProto.FLAGS);
+                    break;
+                case (int) EventObfuscatedProto.TYPE:
+                    event.mEventType = proto.readInt(EventObfuscatedProto.TYPE);
+                    break;
+                case (int) EventObfuscatedProto.CONFIG:
+                    event.mConfiguration = new Configuration();
+                    event.mConfiguration.readFromProto(proto, EventObfuscatedProto.CONFIG);
+                    break;
+                case (int) EventObfuscatedProto.SHORTCUT_ID_TOKEN:
+                    event.mShortcutIdToken = proto.readInt(
+                            EventObfuscatedProto.SHORTCUT_ID_TOKEN) - 1;
+                    break;
+                case (int) EventObfuscatedProto.STANDBY_BUCKET:
+                    event.mBucketAndReason = proto.readInt(EventObfuscatedProto.STANDBY_BUCKET);
+                    break;
+                case (int) EventObfuscatedProto.NOTIFICATION_CHANNEL_ID_TOKEN:
+                    event.mNotificationChannelIdToken = proto.readInt(
+                            EventObfuscatedProto.NOTIFICATION_CHANNEL_ID_TOKEN) - 1;
+                    break;
+                case (int) EventObfuscatedProto.INSTANCE_ID:
+                    event.mInstanceId = proto.readInt(EventObfuscatedProto.INSTANCE_ID);
+                    break;
+                case (int) EventObfuscatedProto.TASK_ROOT_PACKAGE_TOKEN:
+                    event.mTaskRootPackageToken = proto.readInt(
+                            EventObfuscatedProto.TASK_ROOT_PACKAGE_TOKEN) - 1;
+                    break;
+                case (int) EventObfuscatedProto.TASK_ROOT_CLASS_TOKEN:
+                    event.mTaskRootClassToken = proto.readInt(
+                            EventObfuscatedProto.TASK_ROOT_CLASS_TOKEN) - 1;
+                    break;
+                case ProtoInputStream.NO_MORE_FIELDS:
+                    // timeStamp was not read, assume default value 0 plus beginTime
+                    if (event.mTimeStamp == 0) {
+                        event.mTimeStamp = beginTime;
+                    }
+                    return event.mPackageToken == PackagesTokenData.UNASSIGNED_TOKEN ? null : event;
+            }
+        }
+    }
+
+    private static void writeUsageStats(ProtoOutputStream proto, final long beginTime,
+            final UsageStats stats) throws IllegalArgumentException {
+        // Time attributes stored as an offset of the beginTime.
+        proto.write(UsageStatsObfuscatedProto.PACKAGE_TOKEN, stats.mPackageToken + 1);
+        proto.write(UsageStatsObfuscatedProto.LAST_TIME_ACTIVE_MS, stats.mLastTimeUsed - beginTime);
+        proto.write(UsageStatsObfuscatedProto.TOTAL_TIME_ACTIVE_MS, stats.mTotalTimeInForeground);
+        proto.write(UsageStatsObfuscatedProto.LAST_TIME_SERVICE_USED_MS,
+                stats.mLastTimeForegroundServiceUsed - beginTime);
+        proto.write(UsageStatsObfuscatedProto.TOTAL_TIME_SERVICE_USED_MS,
+                stats.mTotalTimeForegroundServiceUsed);
+        proto.write(UsageStatsObfuscatedProto.LAST_TIME_VISIBLE_MS,
+                stats.mLastTimeVisible - beginTime);
+        proto.write(UsageStatsObfuscatedProto.TOTAL_TIME_VISIBLE_MS, stats.mTotalTimeVisible);
+        proto.write(UsageStatsObfuscatedProto.APP_LAUNCH_COUNT, stats.mAppLaunchCount);
+        try {
+            writeChooserCounts(proto, stats);
+        } catch (IllegalArgumentException e) {
+            Slog.e(TAG, "Unable to write chooser counts for " + stats.mPackageName, e);
+        }
+    }
+
+    private static void writeCountAndTime(ProtoOutputStream proto, long fieldId, int count,
+            long time) throws IllegalArgumentException {
+        final long token = proto.start(fieldId);
+        proto.write(IntervalStatsObfuscatedProto.CountAndTime.COUNT, count);
+        proto.write(IntervalStatsObfuscatedProto.CountAndTime.TIME_MS, time);
+        proto.end(token);
+    }
+
+    private static void writeChooserCounts(ProtoOutputStream proto, final UsageStats stats)
+            throws IllegalArgumentException {
+        if (stats == null || stats.mChooserCountsObfuscated.size() == 0) {
+            return;
+        }
+        final int chooserCountSize = stats.mChooserCountsObfuscated.size();
+        for (int i = 0; i < chooserCountSize; i++) {
+            final int action = stats.mChooserCountsObfuscated.keyAt(i);
+            final SparseIntArray counts = stats.mChooserCountsObfuscated.valueAt(i);
+            if (counts == null || counts.size() == 0) {
+                continue;
+            }
+            final long token = proto.start(UsageStatsObfuscatedProto.CHOOSER_ACTIONS);
+            proto.write(UsageStatsObfuscatedProto.ChooserAction.ACTION_TOKEN, action + 1);
+            writeCountsForAction(proto, counts);
+            proto.end(token);
+        }
+    }
+
+    private static void writeCountsForAction(ProtoOutputStream proto, SparseIntArray counts)
+            throws IllegalArgumentException {
+        final int countsSize = counts.size();
+        for (int i = 0; i < countsSize; i++) {
+            final int category = counts.keyAt(i);
+            final int count = counts.valueAt(i);
+            if (count <= 0) {
+                continue;
+            }
+            final long token = proto.start(UsageStatsObfuscatedProto.ChooserAction.COUNTS);
+            proto.write(UsageStatsObfuscatedProto.ChooserAction.CategoryCount.CATEGORY_TOKEN,
+                    category + 1);
+            proto.write(UsageStatsObfuscatedProto.ChooserAction.CategoryCount.COUNT, count);
+            proto.end(token);
+        }
+    }
+
+    private static void writeConfigStats(ProtoOutputStream proto, final long statsBeginTime,
+            final ConfigurationStats configStats, boolean isActive)
+            throws IllegalArgumentException {
+        configStats.mConfiguration.writeToProto(proto,
+                IntervalStatsObfuscatedProto.Configuration.CONFIG);
+        proto.write(IntervalStatsObfuscatedProto.Configuration.LAST_TIME_ACTIVE_MS,
+                configStats.mLastTimeActive - statsBeginTime);
+        proto.write(IntervalStatsObfuscatedProto.Configuration.TOTAL_TIME_ACTIVE_MS,
+                configStats.mTotalTimeActive);
+        proto.write(IntervalStatsObfuscatedProto.Configuration.COUNT, configStats.mActivationCount);
+        proto.write(IntervalStatsObfuscatedProto.Configuration.ACTIVE, isActive);
+    }
+
+    private static void writeEvent(ProtoOutputStream proto, final long statsBeginTime,
+            final UsageEvents.Event event) throws IllegalArgumentException {
+        proto.write(EventObfuscatedProto.PACKAGE_TOKEN, event.mPackageToken + 1);
+        if (event.mClassToken != PackagesTokenData.UNASSIGNED_TOKEN) {
+            proto.write(EventObfuscatedProto.CLASS_TOKEN, event.mClassToken + 1);
+        }
+        proto.write(EventObfuscatedProto.TIME_MS, event.mTimeStamp - statsBeginTime);
+        proto.write(EventObfuscatedProto.FLAGS, event.mFlags);
+        proto.write(EventObfuscatedProto.TYPE, event.mEventType);
+        proto.write(EventObfuscatedProto.INSTANCE_ID, event.mInstanceId);
+        if (event.mTaskRootPackageToken != PackagesTokenData.UNASSIGNED_TOKEN) {
+            proto.write(EventObfuscatedProto.TASK_ROOT_PACKAGE_TOKEN,
+                    event.mTaskRootPackageToken + 1);
+        }
+        if (event.mTaskRootClassToken != PackagesTokenData.UNASSIGNED_TOKEN) {
+            proto.write(EventObfuscatedProto.TASK_ROOT_CLASS_TOKEN, event.mTaskRootClassToken + 1);
+        }
+        switch (event.mEventType) {
+            case UsageEvents.Event.CONFIGURATION_CHANGE:
+                if (event.mConfiguration != null) {
+                    event.mConfiguration.writeToProto(proto, EventObfuscatedProto.CONFIG);
+                }
+                break;
+            case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+                if (event.mBucketAndReason != 0) {
+                    proto.write(EventObfuscatedProto.STANDBY_BUCKET, event.mBucketAndReason);
+                }
+                break;
+            case UsageEvents.Event.SHORTCUT_INVOCATION:
+                if (event.mShortcutIdToken != PackagesTokenData.UNASSIGNED_TOKEN) {
+                    proto.write(EventObfuscatedProto.SHORTCUT_ID_TOKEN, event.mShortcutIdToken + 1);
+                }
+                break;
+            case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+                if (event.mNotificationChannelIdToken != PackagesTokenData.UNASSIGNED_TOKEN) {
+                    proto.write(EventObfuscatedProto.NOTIFICATION_CHANNEL_ID_TOKEN,
+                            event.mNotificationChannelIdToken + 1);
+                }
+                break;
+        }
+    }
+
+    /**
+     * Populates a tokenized version of interval stats from the input stream given.
+     *
+     * @param in the input stream from which to read events.
+     * @param stats the interval stats object which will be populated.
+     */
+    public static void read(InputStream in, IntervalStats stats) throws IOException {
+        final ProtoInputStream proto = new ProtoInputStream(in);
+        while (true) {
+            switch (proto.nextField()) {
+                case (int) IntervalStatsObfuscatedProto.END_TIME_MS:
+                    stats.endTime = stats.beginTime + proto.readLong(
+                            IntervalStatsObfuscatedProto.END_TIME_MS);
+                    break;
+                case (int) IntervalStatsObfuscatedProto.MAJOR_VERSION:
+                    stats.majorVersion = proto.readInt(IntervalStatsObfuscatedProto.MAJOR_VERSION);
+                    break;
+                case (int) IntervalStatsObfuscatedProto.MINOR_VERSION:
+                    stats.minorVersion = proto.readInt(IntervalStatsObfuscatedProto.MINOR_VERSION);
+                    break;
+                case (int) IntervalStatsObfuscatedProto.INTERACTIVE:
+                    loadCountAndTime(proto, IntervalStatsObfuscatedProto.INTERACTIVE,
+                            stats.interactiveTracker);
+                    break;
+                case (int) IntervalStatsObfuscatedProto.NON_INTERACTIVE:
+                    loadCountAndTime(proto, IntervalStatsObfuscatedProto.NON_INTERACTIVE,
+                            stats.nonInteractiveTracker);
+                    break;
+                case (int) IntervalStatsObfuscatedProto.KEYGUARD_SHOWN:
+                    loadCountAndTime(proto, IntervalStatsObfuscatedProto.KEYGUARD_SHOWN,
+                            stats.keyguardShownTracker);
+                    break;
+                case (int) IntervalStatsObfuscatedProto.KEYGUARD_HIDDEN:
+                    loadCountAndTime(proto, IntervalStatsObfuscatedProto.KEYGUARD_HIDDEN,
+                            stats.keyguardHiddenTracker);
+                    break;
+                case (int) IntervalStatsObfuscatedProto.PACKAGES:
+                    try {
+                        final long packagesToken = proto.start(
+                                IntervalStatsObfuscatedProto.PACKAGES);
+                        UsageStats usageStats = parseUsageStats(proto, stats.beginTime);
+                        if (usageStats.mPackageToken != PackagesTokenData.UNASSIGNED_TOKEN) {
+                            stats.packageStatsObfuscated.put(usageStats.mPackageToken, usageStats);
+                        }
+                        proto.end(packagesToken);
+                    } catch (IOException e) {
+                        Slog.e(TAG, "Unable to read some usage stats from proto.", e);
+                    }
+                    break;
+                case (int) IntervalStatsObfuscatedProto.CONFIGURATIONS:
+                    try {
+                        final long configsToken = proto.start(
+                                IntervalStatsObfuscatedProto.CONFIGURATIONS);
+                        loadConfigStats(proto, stats);
+                        proto.end(configsToken);
+                    } catch (IOException e) {
+                        Slog.e(TAG, "Unable to read some configuration stats from proto.", e);
+                    }
+                    break;
+                case (int) IntervalStatsObfuscatedProto.EVENT_LOG:
+                    try {
+                        final long eventsToken = proto.start(
+                                IntervalStatsObfuscatedProto.EVENT_LOG);
+                        UsageEvents.Event event = parseEvent(proto, stats.beginTime);
+                        proto.end(eventsToken);
+                        if (event != null) {
+                            stats.events.insert(event);
+                        }
+                    } catch (IOException e) {
+                        Slog.e(TAG, "Unable to read some events from proto.", e);
+                    }
+                    break;
+                case ProtoInputStream.NO_MORE_FIELDS:
+                    // endTime not assigned, assume default value of 0 plus beginTime
+                    if (stats.endTime == 0) {
+                        stats.endTime = stats.beginTime;
+                    }
+                    return;
+            }
+        }
+    }
+
+    /**
+     * Writes the tokenized interval stats object to a ProtoBuf file.
+     *
+     * @param out the output stream to which to write the interval stats data.
+     * @param stats the interval stats object to write to the proto file.
+     */
+    public static void write(OutputStream out, IntervalStats stats)
+            throws IOException, IllegalArgumentException {
+        final ProtoOutputStream proto = new ProtoOutputStream(out);
+        proto.write(IntervalStatsObfuscatedProto.END_TIME_MS, stats.endTime - stats.beginTime);
+        proto.write(IntervalStatsObfuscatedProto.MAJOR_VERSION, stats.majorVersion);
+        proto.write(IntervalStatsObfuscatedProto.MINOR_VERSION, stats.minorVersion);
+
+        try {
+            writeCountAndTime(proto, IntervalStatsObfuscatedProto.INTERACTIVE,
+                    stats.interactiveTracker.count, stats.interactiveTracker.duration);
+            writeCountAndTime(proto, IntervalStatsObfuscatedProto.NON_INTERACTIVE,
+                    stats.nonInteractiveTracker.count, stats.nonInteractiveTracker.duration);
+            writeCountAndTime(proto, IntervalStatsObfuscatedProto.KEYGUARD_SHOWN,
+                    stats.keyguardShownTracker.count, stats.keyguardShownTracker.duration);
+            writeCountAndTime(proto, IntervalStatsObfuscatedProto.KEYGUARD_HIDDEN,
+                    stats.keyguardHiddenTracker.count, stats.keyguardHiddenTracker.duration);
+        } catch (IllegalArgumentException e) {
+            Slog.e(TAG, "Unable to write some interval stats trackers to proto.", e);
+        }
+
+        final int statsCount = stats.packageStatsObfuscated.size();
+        for (int i = 0; i < statsCount; i++) {
+            try {
+                final long token = proto.start(IntervalStatsObfuscatedProto.PACKAGES);
+                writeUsageStats(proto, stats.beginTime, stats.packageStatsObfuscated.valueAt(i));
+                proto.end(token);
+            } catch (IllegalArgumentException e) {
+                Slog.e(TAG, "Unable to write some usage stats to proto.", e);
+            }
+        }
+        final int configCount = stats.configurations.size();
+        for (int i = 0; i < configCount; i++) {
+            boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i));
+            try {
+                final long token = proto.start(IntervalStatsObfuscatedProto.CONFIGURATIONS);
+                writeConfigStats(proto, stats.beginTime, stats.configurations.valueAt(i), active);
+                proto.end(token);
+            } catch (IllegalArgumentException e) {
+                Slog.e(TAG, "Unable to write some configuration stats to proto.", e);
+            }
+        }
+        final int eventCount = stats.events.size();
+        for (int i = 0; i < eventCount; i++) {
+            try {
+                final long token = proto.start(IntervalStatsObfuscatedProto.EVENT_LOG);
+                writeEvent(proto, stats.beginTime, stats.events.get(i));
+                proto.end(token);
+            } catch (IllegalArgumentException e) {
+                Slog.e(TAG, "Unable to write some events to proto.", e);
+            }
+        }
+
+        proto.flush();
+    }
+
+    /***** Read/Write obfuscated packages data logic. *****/
+
+    private static void loadPackagesMap(ProtoInputStream proto,
+            SparseArray<ArrayList<String>> tokensToPackagesMap) throws IOException {
+        int key = PackagesTokenData.UNASSIGNED_TOKEN;
+        final ArrayList<String> strings = new ArrayList<>();
+        while (true) {
+            switch (proto.nextField()) {
+                case (int) ObfuscatedPackagesProto.PackagesMap.PACKAGE_TOKEN:
+                    key = proto.readInt(ObfuscatedPackagesProto.PackagesMap.PACKAGE_TOKEN) - 1;
+                    break;
+                case (int) ObfuscatedPackagesProto.PackagesMap.STRINGS:
+                    strings.add(proto.readString(ObfuscatedPackagesProto.PackagesMap.STRINGS));
+                    break;
+                case ProtoInputStream.NO_MORE_FIELDS:
+                    if (key != PackagesTokenData.UNASSIGNED_TOKEN) {
+                        tokensToPackagesMap.put(key, strings);
+                    }
+                    return;
+            }
+        }
+    }
+
+    /**
+     * Populates the package mappings from the input stream given.
+     *
+     * @param in the input stream from which to read the mappings.
+     * @param packagesTokenData the packages data object to which the data will be read to.
+     */
+    static void readObfuscatedData(InputStream in, PackagesTokenData packagesTokenData)
+            throws IOException {
+        final ProtoInputStream proto = new ProtoInputStream(in);
+        while (true) {
+            switch (proto.nextField()) {
+                case (int) ObfuscatedPackagesProto.COUNTER:
+                    packagesTokenData.counter = proto.readInt(ObfuscatedPackagesProto.COUNTER);
+                    break;
+                case (int) ObfuscatedPackagesProto.PACKAGES_MAP:
+                    final long token = proto.start(ObfuscatedPackagesProto.PACKAGES_MAP);
+                    loadPackagesMap(proto, packagesTokenData.tokensToPackagesMap);
+                    proto.end(token);
+                    break;
+                case ProtoInputStream.NO_MORE_FIELDS:
+                    return;
+            }
+        }
+    }
+
+    /**
+     * Writes the packages mapping data to a ProtoBuf file.
+     *
+     * @param out the output stream to which to write the mappings.
+     * @param packagesTokenData the packages data object holding the data to write.
+     */
+    static void writeObfuscatedData(OutputStream out, PackagesTokenData packagesTokenData)
+            throws IOException, IllegalArgumentException {
+        final ProtoOutputStream proto = new ProtoOutputStream(out);
+        proto.write(ObfuscatedPackagesProto.COUNTER, packagesTokenData.counter);
+
+        final int mapSize = packagesTokenData.tokensToPackagesMap.size();
+        for (int i = 0; i < mapSize; i++) {
+            final long token = proto.start(ObfuscatedPackagesProto.PACKAGES_MAP);
+            int packageToken = packagesTokenData.tokensToPackagesMap.keyAt(i);
+            proto.write(ObfuscatedPackagesProto.PackagesMap.PACKAGE_TOKEN, packageToken + 1);
+
+            final ArrayList<String> strings = packagesTokenData.tokensToPackagesMap.valueAt(i);
+            final int listSize = strings.size();
+            for (int j = 0; j < listSize; j++) {
+                proto.write(ObfuscatedPackagesProto.PackagesMap.STRINGS, strings.get(j));
+            }
+            proto.end(token);
+        }
+
+        proto.flush();
+    }
+
+    /***** Read/Write pending events logic. *****/
+
+    private static UsageEvents.Event parsePendingEvent(ProtoInputStream proto) throws IOException {
+        final UsageEvents.Event event = new UsageEvents.Event();
+        while (true) {
+            switch (proto.nextField()) {
+                case (int) PendingEventProto.PACKAGE_NAME:
+                    event.mPackage = proto.readString(PendingEventProto.PACKAGE_NAME);
+                    break;
+                case (int) PendingEventProto.CLASS_NAME:
+                    event.mClass = proto.readString(PendingEventProto.CLASS_NAME);
+                    break;
+                case (int) PendingEventProto.TIME_MS:
+                    event.mTimeStamp = proto.readLong(PendingEventProto.TIME_MS);
+                    break;
+                case (int) PendingEventProto.FLAGS:
+                    event.mFlags = proto.readInt(PendingEventProto.FLAGS);
+                    break;
+                case (int) PendingEventProto.TYPE:
+                    event.mEventType = proto.readInt(PendingEventProto.TYPE);
+                    break;
+                case (int) PendingEventProto.CONFIG:
+                    event.mConfiguration = new Configuration();
+                    event.mConfiguration.readFromProto(proto, PendingEventProto.CONFIG);
+                    break;
+                case (int) PendingEventProto.SHORTCUT_ID:
+                    event.mShortcutId = proto.readString(PendingEventProto.SHORTCUT_ID);
+                    break;
+                case (int) PendingEventProto.STANDBY_BUCKET:
+                    event.mBucketAndReason = proto.readInt(PendingEventProto.STANDBY_BUCKET);
+                    break;
+                case (int) PendingEventProto.NOTIFICATION_CHANNEL_ID:
+                    event.mNotificationChannelId = proto.readString(
+                            PendingEventProto.NOTIFICATION_CHANNEL_ID);
+                    break;
+                case (int) PendingEventProto.INSTANCE_ID:
+                    event.mInstanceId = proto.readInt(PendingEventProto.INSTANCE_ID);
+                    break;
+                case (int) PendingEventProto.TASK_ROOT_PACKAGE:
+                    event.mTaskRootPackage = proto.readString(PendingEventProto.TASK_ROOT_PACKAGE);
+                    break;
+                case (int) PendingEventProto.TASK_ROOT_CLASS:
+                    event.mTaskRootClass = proto.readString(PendingEventProto.TASK_ROOT_CLASS);
+                    break;
+                case ProtoInputStream.NO_MORE_FIELDS:
+                    // Handle default values for certain events types
+                    switch (event.mEventType) {
+                        case UsageEvents.Event.CONFIGURATION_CHANGE:
+                            if (event.mConfiguration == null) {
+                                event.mConfiguration = new Configuration();
+                            }
+                            break;
+                        case UsageEvents.Event.SHORTCUT_INVOCATION:
+                            if (event.mShortcutId == null) {
+                                event.mShortcutId = "";
+                            }
+                            break;
+                        case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+                            if (event.mNotificationChannelId == null) {
+                                event.mNotificationChannelId = "";
+                            }
+                            break;
+                    }
+                    return event.mPackage == null ? null : event;
+            }
+        }
+    }
+
+    /**
+     * Populates the list of pending events from the input stream given.
+     *
+     * @param in the input stream from which to read the pending events.
+     * @param events the list of pending events to populate.
+     */
+    static void readPendingEvents(InputStream in, LinkedList<UsageEvents.Event> events)
+            throws IOException {
+        final ProtoInputStream proto = new ProtoInputStream(in);
+        while (true) {
+            switch (proto.nextField()) {
+                case (int) IntervalStatsObfuscatedProto.PENDING_EVENTS:
+                    try {
+                        final long token = proto.start(IntervalStatsObfuscatedProto.PENDING_EVENTS);
+                        UsageEvents.Event event = parsePendingEvent(proto);
+                        proto.end(token);
+                        if (event != null) {
+                            events.add(event);
+                        }
+                    } catch (IOException e) {
+                        Slog.e(TAG, "Unable to parse some pending events from proto.", e);
+                    }
+                    break;
+                case ProtoInputStream.NO_MORE_FIELDS:
+                    return;
+            }
+        }
+    }
+
+    private static void writePendingEvent(ProtoOutputStream proto, UsageEvents.Event event)
+            throws IllegalArgumentException {
+        proto.write(PendingEventProto.PACKAGE_NAME, event.mPackage);
+        if (event.mClass != null) {
+            proto.write(PendingEventProto.CLASS_NAME, event.mClass);
+        }
+        proto.write(PendingEventProto.TIME_MS, event.mTimeStamp);
+        proto.write(PendingEventProto.FLAGS, event.mFlags);
+        proto.write(PendingEventProto.TYPE, event.mEventType);
+        proto.write(PendingEventProto.INSTANCE_ID, event.mInstanceId);
+        if (event.mTaskRootPackage != null) {
+            proto.write(PendingEventProto.TASK_ROOT_PACKAGE, event.mTaskRootPackage);
+        }
+        if (event.mTaskRootClass != null) {
+            proto.write(PendingEventProto.TASK_ROOT_CLASS, event.mTaskRootClass);
+        }
+        switch (event.mEventType) {
+            case UsageEvents.Event.CONFIGURATION_CHANGE:
+                if (event.mConfiguration != null) {
+                    event.mConfiguration.writeToProto(proto, PendingEventProto.CONFIG);
+                }
+                break;
+            case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+                if (event.mBucketAndReason != 0) {
+                    proto.write(PendingEventProto.STANDBY_BUCKET, event.mBucketAndReason);
+                }
+                break;
+            case UsageEvents.Event.SHORTCUT_INVOCATION:
+                if (event.mShortcutId != null) {
+                    proto.write(PendingEventProto.SHORTCUT_ID, event.mShortcutId);
+                }
+                break;
+            case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+                if (event.mNotificationChannelId != null) {
+                    proto.write(PendingEventProto.NOTIFICATION_CHANNEL_ID,
+                            event.mNotificationChannelId);
+                }
+                break;
+        }
+    }
+
+    /**
+     * Writes the pending events to a ProtoBuf file.
+     *
+     * @param out the output stream to which to write the pending events.
+     * @param events the list of pending events.
+     */
+    static void writePendingEvents(OutputStream out, LinkedList<UsageEvents.Event> events)
+            throws IOException, IllegalArgumentException {
+        final ProtoOutputStream proto = new ProtoOutputStream(out);
+        final int eventCount = events.size();
+        for (int i = 0; i < eventCount; i++) {
+            try {
+                final long token = proto.start(IntervalStatsObfuscatedProto.PENDING_EVENTS);
+                writePendingEvent(proto, events.get(i));
+                proto.end(token);
+            } catch (IllegalArgumentException e) {
+                Slog.e(TAG, "Unable to write some pending events to proto.", e);
+            }
+        }
+        proto.flush();
+    }
+}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 2cd207f..5d03e151 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -141,6 +141,7 @@
     static final int MSG_UID_STATE_CHANGED = 3;
     static final int MSG_REPORT_EVENT_TO_ALL_USERID = 4;
     static final int MSG_UNLOCKED_USER = 5;
+    static final int MSG_PACKAGE_REMOVED = 6;
 
     private final Object mLock = new Object();
     Handler mHandler;
@@ -148,7 +149,6 @@
     UserManager mUserManager;
     PackageManager mPackageManager;
     PackageManagerInternal mPackageManagerInternal;
-    PackageMonitor mPackageMonitor;
     IDeviceIdleController mDeviceIdleController;
     // Do not use directly. Call getDpmInternal() instead
     DevicePolicyManagerInternal mDpmInternal;
@@ -164,6 +164,8 @@
     /** Manages app time limit observers */
     AppTimeLimitController mAppTimeLimit;
 
+    private final PackageMonitor mPackageMonitor = new MyPackageMonitor();
+
     // A map maintaining a queue of events to be reported per user.
     private final SparseArray<LinkedList<Event>> mReportedEvents = new SparseArray<>();
     final SparseArray<ArraySet<String>> mUsageReporters = new SparseArray();
@@ -243,6 +245,8 @@
 
         mAppStandby.addListener(mStandbyChangeListener);
 
+        mPackageMonitor.register(getContext(), null, UserHandle.ALL, true);
+
         IntentFilter filter = new IntentFilter(Intent.ACTION_USER_REMOVED);
         filter.addAction(Intent.ACTION_USER_STARTED);
         getContext().registerReceiverAsUser(new UserActionsReceiver(), UserHandle.ALL, filter,
@@ -617,7 +621,7 @@
             final AtomicFile af = new AtomicFile(pendingEventsFiles[i]);
             try {
                 try (FileInputStream in = af.openRead()) {
-                    UsageStatsProto.readPendingEvents(in, pendingEvents);
+                    UsageStatsProtoV2.readPendingEvents(in, pendingEvents);
                 }
             } catch (IOException e) {
                 // Even if one file read fails, exit here to keep all events in order on disk -
@@ -647,11 +651,11 @@
         FileOutputStream fos = null;
         try {
             fos = af.startWrite();
-            UsageStatsProto.writePendingEvents(fos, pendingEvents);
+            UsageStatsProtoV2.writePendingEvents(fos, pendingEvents);
             af.finishWrite(fos);
             fos = null;
             pendingEvents.clear();
-        } catch (IOException e) {
+        } catch (IOException | IllegalArgumentException e) {
             Slog.e(TAG, "Failed to write " + pendingEventsFile.getAbsolutePath()
                     + " for user " + userId);
         } finally {
@@ -843,6 +847,26 @@
     }
 
     /**
+     * Called by the Handler for message MSG_PACKAGE_REMOVED.
+     */
+    private void onPackageRemoved(int userId, String packageName) {
+        synchronized (mLock) {
+            final long timeRemoved = System.currentTimeMillis();
+            if (!mUserUnlockedStates.get(userId, false)) {
+                // If user is not unlocked and a package is removed for them, we will handle it
+                // when the user service is initialized and package manager is queried.
+                return;
+            }
+            final UserUsageStatsService userService = mUserState.get(userId);
+            if (userService == null) {
+                return;
+            }
+
+            userService.onPackageRemoved(packageName, timeRemoved);
+        }
+    }
+
+    /**
      * Called by the Binder stub.
      */
     List<UsageStats> queryUsageStats(int userId, int bucketType, long beginTime, long endTime,
@@ -1030,21 +1054,13 @@
                                 ipw.decreaseIndent();
                             }
                         } else {
-                            final int user;
-                            try {
-                                user = Integer.valueOf(args[i + 1]);
-                            } catch (NumberFormatException nfe) {
-                                ipw.println("invalid user specified.");
-                                return;
+                            final int user = parseUserIdFromArgs(args, i, ipw);
+                            if (user != UserHandle.USER_NULL) {
+                                final String[] remainingArgs = Arrays.copyOfRange(
+                                        args, i + 2, args.length);
+                                // dump everything for the specified user
+                                mUserState.get(user).dumpFile(ipw, remainingArgs);
                             }
-                            if (mUserState.indexOfKey(user) < 0) {
-                                ipw.println("the specified user does not exist.");
-                                return;
-                            }
-                            final String[] remainingArgs = Arrays.copyOfRange(
-                                    args, i + 2, args.length);
-                            // dump everything for the specified user
-                            mUserState.get(user).dumpFile(ipw, remainingArgs);
                         }
                         return;
                     } else if ("database-info".equals(arg)) {
@@ -1059,19 +1075,11 @@
                                 ipw.decreaseIndent();
                             }
                         } else {
-                            final int user;
-                            try {
-                                user = Integer.valueOf(args[i + 1]);
-                            } catch (NumberFormatException nfe) {
-                                ipw.println("invalid user specified.");
-                                return;
+                            final int user = parseUserIdFromArgs(args, i, ipw);
+                            if (user != UserHandle.USER_NULL) {
+                                // dump info only for the specified user
+                                mUserState.get(user).dumpDatabaseInfo(ipw);
                             }
-                            if (mUserState.indexOfKey(user) < 0) {
-                                ipw.println("the specified user does not exist.");
-                                return;
-                            }
-                            // dump info only for the specified user
-                            mUserState.get(user).dumpDatabaseInfo(ipw);
                         }
                         return;
                     } else if ("appstandby".equals(arg)) {
@@ -1079,15 +1087,18 @@
                         return;
                     } else if ("stats-directory".equals(arg)) {
                         final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
-                        final int userId;
-                        try {
-                            userId = Integer.valueOf(args[i + 1]);
-                        } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
-                            ipw.println("invalid user specified.");
-                            return;
+                        final int userId = parseUserIdFromArgs(args, i, ipw);
+                        if (userId != UserHandle.USER_NULL) {
+                            ipw.println(new File(Environment.getDataSystemCeDirectory(userId),
+                                    "usagestats").getAbsolutePath());
                         }
-                        ipw.println(new File(Environment.getDataSystemCeDirectory(userId),
-                                "usagestats").getAbsolutePath());
+                        return;
+                    } else if ("mappings".equals(arg)) {
+                        final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
+                        final int userId = parseUserIdFromArgs(args, i, ipw);
+                        if (userId != UserHandle.USER_NULL) {
+                            mUserState.get(userId).dumpMappings(ipw);
+                        }
                         return;
                     } else if (arg != null && !arg.startsWith("-")) {
                         // Anything else that doesn't start with '-' is a pkg to filter
@@ -1126,6 +1137,21 @@
         }
     }
 
+    private int parseUserIdFromArgs(String[] args, int index, IndentingPrintWriter ipw) {
+        final int userId;
+        try {
+            userId = Integer.valueOf(args[index + 1]);
+        } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
+            ipw.println("invalid user specified.");
+            return UserHandle.USER_NULL;
+        }
+        if (mUserState.indexOfKey(userId) < 0) {
+            ipw.println("the specified user does not exist.");
+            return UserHandle.USER_NULL;
+        }
+        return userId;
+    }
+
     class H extends Handler {
         public H(Looper looper) {
             super(looper);
@@ -1157,7 +1183,9 @@
                 case MSG_REMOVE_USER:
                     onUserRemoved(msg.arg1);
                     break;
-
+                case MSG_PACKAGE_REMOVED:
+                    onPackageRemoved(msg.arg1, (String) msg.obj);
+                    break;
                 case MSG_UID_STATE_CHANGED: {
                     final int uid = msg.arg1;
                     final int procState = msg.arg2;
@@ -2105,4 +2133,13 @@
             return mAppTimeLimit.getAppUsageLimit(packageName, user);
         }
     }
+
+    private class MyPackageMonitor extends PackageMonitor {
+        @Override
+        public void onPackageRemoved(String packageName, int uid) {
+            mHandler.obtainMessage(MSG_PACKAGE_REMOVED, getChangingUserId(), 0, packageName)
+                    .sendToTarget();
+            super.onPackageRemoved(packageName, uid);
+        }
+    }
 }
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 1560b9e..23df1c5 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -34,7 +34,10 @@
 import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManager;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
 import android.content.res.Configuration;
+import android.os.Process;
 import android.os.SystemClock;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
@@ -44,6 +47,7 @@
 import android.util.SparseIntArray;
 
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
 import com.android.server.usage.UsageStatsDatabase.StatCombiner;
 
 import java.io.File;
@@ -51,6 +55,7 @@
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 
 /**
@@ -108,6 +113,7 @@
     }
 
     void init(final long currentTimeMillis) {
+        readPackageMappingsLocked();
         mDatabase.init(currentTimeMillis);
 
         int nullCount = 0;
@@ -169,6 +175,54 @@
         persistActiveStats();
     }
 
+    void onPackageRemoved(String packageName, long timeRemoved) {
+        mDatabase.onPackageRemoved(packageName, timeRemoved);
+    }
+
+    private void readPackageMappingsLocked() {
+        mDatabase.readMappingsLocked();
+        cleanUpPackageMappingsLocked();
+    }
+
+    /**
+     * Queries Package Manager for a list of installed packages and removes those packages from
+     * mPackagesTokenData which are not installed any more.
+     * This will only happen once per device boot, when the user is unlocked for the first time.
+     */
+    private void cleanUpPackageMappingsLocked() {
+        final long timeNow = System.currentTimeMillis();
+        /*
+         Note (b/142501248): PackageManagerInternal#getInstalledApplications is not lightweight.
+         Once its implementation is updated, or it's replaced with a better alternative, update
+         the call here to use it. For now, using the heavy #getInstalledApplications is okay since
+         this clean-up is only performed once every boot.
+         */
+        final PackageManagerInternal packageManagerInternal =
+                LocalServices.getService(PackageManagerInternal.class);
+        if (packageManagerInternal == null) {
+            return;
+        }
+        final List<ApplicationInfo> installedPackages =
+                packageManagerInternal.getInstalledApplications(0, mUserId, Process.SYSTEM_UID);
+        // convert the package list to a set for easy look-ups
+        final HashSet<String> packagesSet = new HashSet<>(installedPackages.size());
+        for (int i = installedPackages.size() - 1; i >= 0; i--) {
+            packagesSet.add(installedPackages.get(i).packageName);
+        }
+        final List<String> removedPackages = new ArrayList<>();
+        // populate list of packages that are found in the mappings but not in the installed list
+        for (int i = mDatabase.mPackagesTokenData.packagesToTokensMap.size() - 1; i >= 0; i--) {
+            if (!packagesSet.contains(mDatabase.mPackagesTokenData.packagesToTokensMap.keyAt(i))) {
+                removedPackages.add(mDatabase.mPackagesTokenData.packagesToTokensMap.keyAt(i));
+            }
+        }
+
+        // remove packages in the mappings that are no longer installed
+        for (int i = removedPackages.size() - 1; i >= 0; i--) {
+            mDatabase.mPackagesTokenData.removePackage(removedPackages.get(i), timeNow);
+        }
+    }
+
     private void onTimeChanged(long oldTime, long newTime) {
         persistActiveStats();
         mDatabase.onTimeChanged(newTime - oldTime);
@@ -400,6 +454,7 @@
             if (results == null) {
                 results = new ArrayList<>();
             }
+            mDatabase.filterStats(currentStats);
             combiner.combine(currentStats, true, results);
         }
 
@@ -524,6 +579,8 @@
         if (mStatsChanged) {
             Slog.i(TAG, mLogPrefix + "Flushing usage stats to disk");
             try {
+                mDatabase.obfuscateCurrentStats(mCurrentStats);
+                mDatabase.writeMappingsLocked();
                 for (int i = 0; i < mCurrentStats.length; i++) {
                     mDatabase.putUsageStats(i, mCurrentStats[i]);
                 }
@@ -700,6 +757,10 @@
         mDatabase.dump(ipw, false);
     }
 
+    void dumpMappings(IndentingPrintWriter ipw) {
+        mDatabase.dumpMappings(ipw);
+    }
+
     void dumpFile(IndentingPrintWriter ipw, String[] args) {
         if (args == null || args.length == 0) {
             // dump all files for every interval for specified user
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
index 0535d71..ff7f393 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
@@ -52,6 +52,7 @@
                                                 int length, byte type) {
         UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
         int subClass = interfaceDesc.getUsbSubclass();
+        // TODO shouldn't this switch on subtype?
         switch (subClass) {
             case AUDIO_AUDIOCONTROL:
                 if (UsbDescriptorParser.DEBUG) {
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index 8e7babb..b230e4b 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -26,7 +26,7 @@
  */
 public final class UsbDescriptorParser {
     private static final String TAG = "UsbDescriptorParser";
-    public static final boolean DEBUG = true;
+    public static final boolean DEBUG = false;
 
     private final String mDeviceAddr;
 
@@ -43,6 +43,11 @@
     // Obtained from the first AudioClass Header descriptor.
     private int mACInterfacesSpec = UsbDeviceDescriptor.USBSPEC_1_0;
 
+    // The VideoClass spec implemented by the VideoClass Interfaces
+    // This may well be different than the overall USB Spec.
+    // Obtained from the first VidieoClass Header descriptor.
+    private int mVCInterfacesSpec = UsbDeviceDescriptor.USBSPEC_1_0;
+
     /**
      * Connect this parser to an existing set of already parsed descriptors.
      * This is useful for reporting.
@@ -90,6 +95,14 @@
         return mACInterfacesSpec;
     }
 
+    public void setVCInterfaceSpec(int spec) {
+        mVCInterfacesSpec = spec;
+    }
+
+    public int getVCInterfaceSpec() {
+        return mVCInterfacesSpec;
+    }
+
     private class UsbDescriptorsStreamFormatException extends Exception {
         String mMessage;
         UsbDescriptorsStreamFormatException(String message) {
@@ -186,19 +199,20 @@
                             break;
 
                         case UsbDescriptor.CLASSID_VIDEO:
-                            Log.d(TAG, "  UsbDescriptor.CLASSID_VIDEO subType:0x"
-                                    + Integer.toHexString(stream.getByte()));
+                            if (DEBUG) {
+                                Log.d(TAG, "  UsbDescriptor.CLASSID_VIDEO");
+                            }
                             descriptor = UsbVCInterface.allocDescriptor(this, stream, length, type);
                             break;
 
                         case UsbDescriptor.CLASSID_AUDIOVIDEO:
-                            Log.d(TAG, "  UsbDescriptor.CLASSID_AUDIOVIDEO subType:0x"
-                                    + Integer.toHexString(stream.getByte()));
+                            if (DEBUG) {
+                                Log.d(TAG, "  UsbDescriptor.CLASSID_AUDIOVIDEO");
+                            }
                             break;
 
                         default:
-                            Log.d(TAG, "  Unparsed Class-specific Interface:0x"
-                                    + Integer.toHexString(mCurInterfaceDescriptor.getUsbClass()));
+                            Log.w(TAG, "  Unparsed Class-specific");
                             break;
                     }
                 }
@@ -206,23 +220,32 @@
 
             case UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_ENDPOINT:
                 if (mCurInterfaceDescriptor != null) {
-                    switch (mCurInterfaceDescriptor.getUsbClass()) {
+                    int subClass = mCurInterfaceDescriptor.getUsbClass();
+                    switch (subClass) {
                         case UsbDescriptor.CLASSID_AUDIO:
                             descriptor = UsbACEndpoint.allocDescriptor(this, length, type);
                             break;
-                        case UsbDescriptor.CLASSID_VIDEO:
-                            Log.d(TAG, "UsbDescriptor.CLASSID_VIDEO subType:0x"
-                                    + Integer.toHexString(stream.getByte()));
-                            descriptor = UsbVCEndpoint.allocDescriptor(this, length, type);
+
+                        case UsbDescriptor.CLASSID_VIDEO: {
+                            Byte subtype = stream.getByte();
+                            if (DEBUG) {
+                                Log.d(TAG, "UsbDescriptor.CLASSID_VIDEO type:0x"
+                                        + Integer.toHexString(type));
+                            }
+                            descriptor = UsbVCEndpoint.allocDescriptor(this, length, type, subtype);
+                        }
                             break;
 
                         case UsbDescriptor.CLASSID_AUDIOVIDEO:
-                            Log.d(TAG, "UsbDescriptor.CLASSID_AUDIOVIDEO subType:0x"
-                                    + Integer.toHexString(stream.getByte()));
+                            if (DEBUG) {
+                                Log.d(TAG, "UsbDescriptor.CLASSID_AUDIOVIDEO type:0x"
+                                        + Integer.toHexString(type));
+                            }
                             break;
+
                         default:
-                            Log.d(TAG, "  Unparsed Class-specific Endpoint:0x"
-                                    + Integer.toHexString(mCurInterfaceDescriptor.getUsbClass()));
+                            Log.w(TAG, "  Unparsed Class-specific Endpoint:0x"
+                                    + Integer.toHexString(subClass));
                             break;
                     }
                 }
@@ -555,6 +578,30 @@
     /**
      * @hide
      */
+    public boolean hasVideoCapture() {
+        for (UsbDescriptor descriptor : mDescriptors) {
+            if (descriptor instanceof UsbVCInputTerminal) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @hide
+     */
+    public boolean hasVideoPlayback() {
+        for (UsbDescriptor descriptor : mDescriptors) {
+            if (descriptor instanceof UsbVCOutputTerminal) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @hide
+     */
     public boolean hasHIDInterface() {
         ArrayList<UsbDescriptor> descriptors =
                 getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_HID);
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
index 9739243..b1cbbaf 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
@@ -160,7 +160,8 @@
         return new UsbDevice.Builder(parser.getDeviceAddr(), mVendorID,
                 mProductID, mDevClass, mDevSubClass, mProtocol, mfgName, prodName, versionString,
                 configs, serialStr, parser.hasAudioPlayback(), parser.hasAudioCapture(),
-                parser.hasMIDIInterface());
+                parser.hasMIDIInterface(),
+                parser.hasVideoPlayback(), parser.hasVideoCapture());
     }
 
     @Override
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCEndpoint.java
index 39fbc0d..f9acece 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbVCEndpoint.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCEndpoint.java
@@ -20,41 +20,54 @@
 /**
  * @hide
  * A video class-specific Endpoint
- * see
+ * see USB_Video_Class_1.1.pdf - 3.10 VideoStreaming Endpoint Descriptors
  */
 abstract class UsbVCEndpoint extends UsbDescriptor {
     private static final String TAG = "UsbVCEndpoint";
 
-    UsbVCEndpoint(int length, byte type, int subclass) {
+
+    public static final byte VCEP_UNDEFINED = 0x00;
+    public static final byte VCEP_GENERAL   = 0x01;
+    public static final byte VCEP_ENDPOINT  = 0x02;
+    public static final byte VCEP_INTERRUPT = 0x03;
+
+    UsbVCEndpoint(int length, byte type) {
         super(length, type);
-        // mSubclass = subclass;
     }
 
     public static UsbDescriptor allocDescriptor(UsbDescriptorParser parser,
-                                                int length, byte type) {
+                                                int length, byte type, byte subtype) {
         UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
-        int subClass = interfaceDesc.getUsbSubclass();
-        switch (subClass) {
-//            case AUDIO_AUDIOCONTROL:
-//                if (UsbDescriptorParser.DEBUG) {
-//                    Log.i(TAG, "---> AUDIO_AUDIOCONTROL");
-//                }
-//                return new UsbACAudioControlEndpoint(length, type, subClass);
-//
-//            case AUDIO_AUDIOSTREAMING:
-//                if (UsbDescriptorParser.DEBUG) {
-//                    Log.i(TAG, "---> AUDIO_AUDIOSTREAMING");
-//                }
-//                return new UsbACAudioStreamEndpoint(length, type, subClass);
-//
-//            case AUDIO_MIDISTREAMING:
-//                if (UsbDescriptorParser.DEBUG) {
-//                    Log.i(TAG, "---> AUDIO_MIDISTREAMING");
-//                }
-//                return new UsbACMidiEndpoint(length, type, subClass);
+
+        // TODO - create classes for each specific subtype
+        //  (don't need it to answer if this device supports video
+        switch (subtype) {
+            case VCEP_UNDEFINED:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "---> VCEP_UNDEFINED");
+                }
+                return null;
+
+            case VCEP_GENERAL:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "---> VCEP_GENERAL");
+                }
+                return null;
+
+            case VCEP_ENDPOINT:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "---> VCEP_ENDPOINT");
+                }
+                return null;
+
+            case VCEP_INTERRUPT:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "---> VCEP_INTERRUPT");
+                }
+                return null;
 
             default:
-                Log.w(TAG, "Unknown Video Class Endpoint id:0x" + Integer.toHexString(subClass));
+                Log.w(TAG, "Unknown Video Class Endpoint id:0x" + Integer.toHexString(subtype));
                 return null;
         }
     }
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCHeader.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCHeader.java
new file mode 100644
index 0000000..3fc4224
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCHeader.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.descriptors;
+
+import android.util.Log;
+
+import com.android.server.usb.descriptors.report.ReportCanvas;
+
+/**
+ * @hide
+ * A video class-specific Interface Header.
+ * see USB_Video_Class_1.1.pdf section 3.9.2 - Class-Specific VS Interface Descriptors
+ */
+public final class UsbVCHeader extends UsbVCHeaderInterface {
+    private static final String TAG = "UsbVCHeader";
+
+    // TODO Add data members for this descriptor's data
+
+    public UsbVCHeader(int length, byte type, byte subtype, int spec) {
+        super(length, type, subtype, spec);
+    }
+
+    @Override
+    public int parseRawDescriptors(ByteStream stream) {
+        if (UsbDescriptorParser.DEBUG) {
+            Log.d(TAG, " ---> parseRawDescriptors()");
+        }
+        // TODO parse data members for this descriptor's data
+        return super.parseRawDescriptors(stream);
+    }
+
+    @Override
+    public void report(ReportCanvas canvas) {
+        super.report(canvas);
+        // TODO add reporting specific to this descriptor
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCHeaderInterface.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCHeaderInterface.java
new file mode 100644
index 0000000..3725091
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCHeaderInterface.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.descriptors;
+
+import com.android.server.usb.descriptors.report.ReportCanvas;
+
+/**
+ * @hide
+ * A video class-specific Interface Header super class.
+ * see USB_Video_Class_1.1.pdf section 3.9.2 - Class-Specific VS Interface Descriptors
+ */
+public abstract class UsbVCHeaderInterface extends UsbVCInterface {
+    private static final String TAG = "UsbVCHeaderInterface";
+
+    protected int mVDCRelease;  // Video Device Class Specification Release (BCD).
+    protected int mTotalLength; // Total number of bytes returned for the class-specific
+    // VideoControl interface descriptor. Includes the combined length
+    // of this descriptor header and all Unit and Terminal descriptors.
+
+    public UsbVCHeaderInterface(
+            int length, byte type, byte subtype, int vdcRelease) {
+        super(length, type, subtype);
+        mVDCRelease = vdcRelease;
+    }
+
+    public int getVDCRelease() {
+        return mVDCRelease;
+    }
+
+    public int getTotalLength() {
+        return mTotalLength;
+    }
+
+    @Override
+    public void report(ReportCanvas canvas) {
+        super.report(canvas);
+
+        canvas.openList();
+        canvas.writeListItem("Release: " + ReportCanvas.getBCDString(getVDCRelease()));
+        canvas.writeListItem("Total Length: " + getTotalLength());
+        canvas.closeList();
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCInputTerminal.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCInputTerminal.java
new file mode 100644
index 0000000..df63795
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCInputTerminal.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.descriptors;
+
+import android.util.Log;
+
+import com.android.server.usb.descriptors.report.ReportCanvas;
+
+/**
+ * @hide
+ * A video class-specific Input terminal interface.
+ * see USB_Video_Class_1.1.pdf section 3.7.2.1 Input Terminal Descriptor
+ */
+public final class UsbVCInputTerminal extends UsbVCInterface {
+    private static final String TAG = "UsbVCInputTerminal";
+
+    // TODO Define members to hold the data from this descriptor
+    public UsbVCInputTerminal(int length, byte type, byte subtype) {
+        super(length, type, subtype);
+    }
+
+    @Override
+    public int parseRawDescriptors(ByteStream stream) {
+        // TODO Parse the data from this descriptor
+        if (UsbDescriptorParser.DEBUG) {
+            Log.d(TAG, " ---> parseRawDescriptors()");
+        }
+        return super.parseRawDescriptors(stream);
+    }
+
+    @Override
+    public void report(ReportCanvas canvas) {
+        // TODO Add reporting specific to this descriptor
+        super.report(canvas);
+    }
+};
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCInterface.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCInterface.java
index c9eb1ec..46263dd 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbVCInterface.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCInterface.java
@@ -29,20 +29,17 @@
     public static final byte VCI_UNDEFINED          = 0x00;
     public static final byte VCI_VEADER             = 0x01;
     public static final byte VCI_INPUT_TERMINAL     = 0x02;
-    public static final byte VCI_VOUTPUT_TERMINAL   = 0x03;
+    public static final byte VCI_OUTPUT_TERMINAL    = 0x03;
     public static final byte VCI_SELECTOR_UNIT      = 0x04;
-    public static final byte VCI_VROCESSING_UNIT    = 0x05;
-    public static final byte VCI_VEXTENSION_UNIT    = 0x06;
+    public static final byte VCI_PROCESSING_UNIT    = 0x05;
+    public static final byte VCI_EXTENSION_UNIT     = 0x06;
 
-   // See “Universal Serial Bus Device Class Definition for Video
+    // See “Universal Serial Bus Device Class Definition for Video
     protected final byte mSubtype;  // 2:1 HEADER descriptor subtype
-    protected final int mSubclass;  // from the mSubclass member of the
-    // "enclosing" Interface Descriptor
 
-    public UsbVCInterface(int length, byte type, byte subtype, int subclass) {
+    public UsbVCInterface(int length, byte type, byte subtype) {
         super(length, type);
         mSubtype = subtype;
-        mSubclass = subclass;
     }
 
     /**
@@ -52,12 +49,10 @@
                                                 int length, byte type) {
         byte subtype = stream.getByte();
         UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
-        int subClass = interfaceDesc.getUsbSubclass();
         if (UsbDescriptorParser.DEBUG) {
-            Log.d(TAG, "  Video Class-specific Interface subClass:0x"
-                    + Integer.toHexString(subClass));
+            Log.d(TAG, "  Video Class-specific Interface subtype: " + subtype);
         }
-        switch (subClass) {
+        switch (subtype) {
             // TODO - Create descriptor classes and parse these...
             case VCI_UNDEFINED:
                 if (UsbDescriptorParser.DEBUG) {
@@ -66,44 +61,51 @@
                 break;
 
             case VCI_VEADER:
+            {
                 if (UsbDescriptorParser.DEBUG) {
                     Log.d(TAG, " ---> VCI_VEADER");
                 }
-                break;
+                int vcInterfaceSpec = stream.unpackUsbShort();
+                parser.setVCInterfaceSpec(vcInterfaceSpec);
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "  vcInterfaceSpec:0x" + Integer.toHexString(vcInterfaceSpec));
+                }
+                return new UsbVCHeader(length, type, subtype, vcInterfaceSpec);
+            }
 
             case VCI_INPUT_TERMINAL:
                 if (UsbDescriptorParser.DEBUG) {
                     Log.d(TAG, " ---> VCI_INPUT_TERMINAL");
                 }
-                break;
+                return new UsbVCInputTerminal(length, type, subtype);
 
-            case VCI_VOUTPUT_TERMINAL:
+            case VCI_OUTPUT_TERMINAL:
                 if (UsbDescriptorParser.DEBUG) {
-                    Log.d(TAG, " ---> VCI_VOUTPUT_TERMINAL");
+                    Log.d(TAG, " ---> VCI_OUTPUT_TERMINAL");
                 }
-                break;
+                return new UsbVCOutputTerminal(length, type, subtype);
 
             case VCI_SELECTOR_UNIT:
                 if (UsbDescriptorParser.DEBUG) {
                     Log.d(TAG, " ---> VCI_SELECTOR_UNIT");
                 }
-                break;
+                return new UsbVCSelectorUnit(length, type, subtype);
 
-            case VCI_VROCESSING_UNIT:
+            case VCI_PROCESSING_UNIT:
                 if (UsbDescriptorParser.DEBUG) {
-                    Log.d(TAG, " ---> VCI_VROCESSING_UNIT");
+                    Log.d(TAG, " ---> VCI_PROCESSING_UNIT");
                 }
-                break;
+                return new UsbVCProcessingUnit(length, type, subtype);
 
-            case VCI_VEXTENSION_UNIT:
+            case VCI_EXTENSION_UNIT:
                 if (UsbDescriptorParser.DEBUG) {
-                    Log.d(TAG, " ---> VCI_VEXTENSION_UNIT");
+                    Log.d(TAG, " ---> VCI_EXTENSION_UNIT");
                 }
                 break;
 
             default:
-                Log.w(TAG, "Unknown Video Class Interface Subclass: 0x"
-                        + Integer.toHexString(subClass));
+                Log.w(TAG, "Unknown Video Class Interface subtype: 0x"
+                        + Integer.toHexString(subtype));
                 return null;
         }
 
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCOutputTerminal.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCOutputTerminal.java
new file mode 100644
index 0000000..4aa8ca2
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCOutputTerminal.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.descriptors;
+
+import android.util.Log;
+
+import com.android.server.usb.descriptors.report.ReportCanvas;
+
+/**
+ * @hide
+ * A video class-specific Output Terminal Descriptor.
+ * see USB_Video_Class_1.1.pdf section 3.7.2.2 Output Terminal Descriptor
+ */
+public final class UsbVCOutputTerminal extends UsbVCInterface {
+    private static final String TAG = "UsbVCOutputTerminal";
+
+    // TODO Add members for the data in this descriptor
+    public UsbVCOutputTerminal(int length, byte type, byte subtype) {
+        super(length, type, subtype);
+    }
+
+    @Override
+    public int parseRawDescriptors(ByteStream stream) {
+        // TODO Parse the data in this descriptor
+        if (UsbDescriptorParser.DEBUG) {
+            Log.d(TAG, " ---> parseRawDescriptors()");
+        }
+        return super.parseRawDescriptors(stream);
+    }
+
+    @Override
+    public void report(ReportCanvas canvas) {
+        super.report(canvas);
+        // TODO Add reporting specific to this descriptor
+    }
+};
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCProcessingUnit.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCProcessingUnit.java
new file mode 100644
index 0000000..5ce842e
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCProcessingUnit.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.descriptors;
+
+import android.util.Log;
+
+import com.android.server.usb.descriptors.report.ReportCanvas;
+
+/**
+ * @hide
+ * An video class-specific Processing Unit Interface.
+ * see USB_Video_Class_1.1.pdf section Table 3-8 Processing Unit Descriptor
+ */
+public final class UsbVCProcessingUnit extends UsbVCInterface {
+    private static final String TAG = "UsbVCProcessingUnit";
+
+    // TODO Add data members for this descriptor
+
+    public UsbVCProcessingUnit(int length, byte type, byte subtype) {
+        super(length, type, subtype);
+    }
+
+    @Override
+    public int parseRawDescriptors(ByteStream stream) {
+        // TODO Parse this descriptor
+        if (UsbDescriptorParser.DEBUG) {
+            Log.d(TAG, " ---> parseRawDescriptors()");
+        }
+        return super.parseRawDescriptors(stream);
+    }
+
+    @Override
+    public void report(ReportCanvas canvas) {
+        super.report(canvas);
+        // TODO Add reporting specific to this descriptor
+    }
+};
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCSelectorUnit.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCSelectorUnit.java
new file mode 100644
index 0000000..8e9b0d8
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCSelectorUnit.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.descriptors;
+
+import android.util.Log;
+
+import com.android.server.usb.descriptors.report.ReportCanvas;
+
+/**
+ * @hide
+ * An video class-specific Selector Unit Descriptor
+ * see USB_Video_Class_1.1.pdf section 3.7.2.4
+ */
+public final class UsbVCSelectorUnit extends UsbVCInterface {
+    private static final String TAG = "UsbVCSelectorUnit";
+
+    // TODO Add data members for this descriptor
+
+    public UsbVCSelectorUnit(int length, byte type, byte subtype) {
+        super(length, type, subtype);
+    }
+
+    @Override
+    public int parseRawDescriptors(ByteStream stream) {
+        // TODO Parse this descriptor
+        if (UsbDescriptorParser.DEBUG) {
+            Log.d(TAG, " ---> parseRawDescriptors()");
+        }
+        return super.parseRawDescriptors(stream);
+    }
+
+    @Override
+    public void report(ReportCanvas canvas) {
+        super.report(canvas);
+        // TODO Add reporting specific to this descriptor
+    }
+};
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index 3e02871..56b7236 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -24,8 +24,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.AccessNetworkConstants.TransportType;
-
 import android.telephony.Annotation.NetworkType;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -90,7 +90,7 @@
      * Dual Connectivity(EN-DC).
      * @hide
      */
-    public static final int NR_STATE_NONE = -1;
+    public static final int NR_STATE_NONE = 0;
 
     /**
      * The device is camped on an LTE cell that supports E-UTRA-NR Dual Connectivity(EN-DC) but
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 1d00b4f..f503348 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -102,7 +102,7 @@
      * Indicates frequency range is unknown.
      * @hide
      */
-    public static final int FREQUENCY_RANGE_UNKNOWN = -1;
+    public static final int FREQUENCY_RANGE_UNKNOWN = 0;
 
     /**
      * Indicates the frequency range is below 1GHz.
diff --git a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
index 7d9d0d5..7e8a134 100644
--- a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
+++ b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
@@ -30,6 +30,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.usage.IntervalStats;
+import com.android.server.usage.PackagesTokenData;
 import com.android.server.usage.UsageStatsDatabase;
 import com.android.server.usage.UsageStatsDatabase.StatCombiner;
 
@@ -79,6 +80,7 @@
         sContext = InstrumentationRegistry.getTargetContext();
         mTestDir = new File(sContext.getFilesDir(), "UsageStatsDatabasePerfTest");
         sUsageStatsDatabase = new UsageStatsDatabase(mTestDir);
+        sUsageStatsDatabase.readMappingsLocked();
         sUsageStatsDatabase.init(1);
     }
 
@@ -140,6 +142,37 @@
         }
     }
 
+    private void runObfuscateStatsTest(int packageCount, int eventsPerPackage) {
+        final ManualBenchmarkState benchmarkState = mPerfManualStatusReporter.getBenchmarkState();
+        IntervalStats intervalStats = new IntervalStats();
+        populateIntervalStats(intervalStats, packageCount, eventsPerPackage);
+        long elapsedTimeNs = 0;
+        while (benchmarkState.keepRunning(elapsedTimeNs)) {
+            final long startTime = SystemClock.elapsedRealtimeNanos();
+            PackagesTokenData packagesTokenData = new PackagesTokenData();
+            intervalStats.obfuscateData(packagesTokenData);
+            final long endTime = SystemClock.elapsedRealtimeNanos();
+            elapsedTimeNs = endTime - startTime;
+            clearUsageStatsFiles();
+        }
+    }
+
+    private void runDeobfuscateStatsTest(int packageCount, int eventsPerPackage) {
+        final ManualBenchmarkState benchmarkState = mPerfManualStatusReporter.getBenchmarkState();
+        IntervalStats intervalStats = new IntervalStats();
+        populateIntervalStats(intervalStats, packageCount, eventsPerPackage);
+        long elapsedTimeNs = 0;
+        while (benchmarkState.keepRunning(elapsedTimeNs)) {
+            PackagesTokenData packagesTokenData = new PackagesTokenData();
+            intervalStats.obfuscateData(packagesTokenData);
+            final long startTime = SystemClock.elapsedRealtimeNanos();
+            intervalStats.deobfuscateData(packagesTokenData);
+            final long endTime = SystemClock.elapsedRealtimeNanos();
+            elapsedTimeNs = endTime - startTime;
+            clearUsageStatsFiles();
+        }
+    }
+
     @Test
     public void testQueryUsageStats_FewPkgsLightUse() throws IOException {
         runQueryUsageStatsTest(FEW_PKGS, LIGHT_USE);
@@ -151,6 +184,16 @@
     }
 
     @Test
+    public void testObfuscateStats_FewPkgsLightUse() {
+        runObfuscateStatsTest(FEW_PKGS, LIGHT_USE);
+    }
+
+    @Test
+    public void testDeobfuscateStats_FewPkgsLightUse() {
+        runDeobfuscateStatsTest(FEW_PKGS, LIGHT_USE);
+    }
+
+    @Test
     public void testQueryUsageStats_FewPkgsHeavyUse() throws IOException {
         runQueryUsageStatsTest(FEW_PKGS, HEAVY_USE);
     }
@@ -161,6 +204,16 @@
     }
 
     @Test
+    public void testObfuscateStats_FewPkgsHeavyUse() {
+        runObfuscateStatsTest(FEW_PKGS, HEAVY_USE);
+    }
+
+    @Test
+    public void testDeobfuscateStats_FewPkgsHeavyUse() {
+        runDeobfuscateStatsTest(FEW_PKGS, HEAVY_USE);
+    }
+
+    @Test
     public void testQueryUsageStats_ManyPkgsLightUse() throws IOException {
         runQueryUsageStatsTest(MANY_PKGS, LIGHT_USE);
     }
@@ -171,6 +224,16 @@
     }
 
     @Test
+    public void testObfuscateStats_ManyPkgsLightUse() {
+        runObfuscateStatsTest(MANY_PKGS, LIGHT_USE);
+    }
+
+    @Test
+    public void testDeobfuscateStats_ManyPkgsLightUse() {
+        runDeobfuscateStatsTest(MANY_PKGS, LIGHT_USE);
+    }
+
+    @Test
     public void testQueryUsageStats_ManyPkgsHeavyUse() throws IOException {
         runQueryUsageStatsTest(MANY_PKGS, HEAVY_USE);
     }
@@ -179,4 +242,14 @@
     public void testPutUsageStats_ManyPkgsHeavyUse() throws IOException {
         runPutUsageStatsTest(MANY_PKGS, HEAVY_USE);
     }
+
+    @Test
+    public void testObfuscateStats_ManyPkgsHeavyUse() {
+        runObfuscateStatsTest(MANY_PKGS, HEAVY_USE);
+    }
+
+    @Test
+    public void testDeobfuscateStats_ManyPkgsHeavyUse() {
+        runDeobfuscateStatsTest(MANY_PKGS, HEAVY_USE);
+    }
 }