Merge "[Settings] Change network reset into reset mobile network"
diff --git a/Android.bp b/Android.bp
index b5dfe79..dc7270e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -80,6 +80,7 @@
         "guava",
         "jsr305",
         "net-utils-framework-common",
+        "app-usage-event-protos-lite",
         "settings-contextual-card-protos-lite",
         "settings-log-bridge-protos-lite",
         "settings-telephony-protos-lite",
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 27ecf53..9998d08 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -4647,6 +4647,9 @@
             </intent-filter>
         </receiver>
 
+        <service android:name=".sim.receivers.SimSlotChangeService"
+                 android:permission="android.permission.BIND_JOB_SERVICE" />
+
         <receiver
             android:name=".sim.receivers.SimCompleteBootReceiver"
             android:exported="true">
diff --git a/res/values/integers.xml b/res/values/integers.xml
index 530f987..9d28aaa 100644
--- a/res/values/integers.xml
+++ b/res/values/integers.xml
@@ -21,6 +21,7 @@
     <integer name="job_anomaly_detection">102</integer>
     <integer name="device_index_update">103</integer>
     <integer name="sim_notification_send">104</integer>
+    <integer name="sim_slot_changed">105</integer>
 
     <!-- Controls the maximum number of faces enrollable during SUW -->
     <integer name="suw_max_faces_enrollable">1</integer>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b939b3e..d17b501 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -10618,15 +10618,12 @@
     <string name="wifi_calling_summary">Use Wi\u2011Fi for calls to improve quality</string>
 
     <!-- Mobile network settings screen, title of Backup calling setting.  [CHAR LIMIT=NONE] -->
-    <!-- Deprecated ToDo: b/260059444 remove it next version-->
     <string name="backup_calling_settings_title">Backup calling</string>
 
     <!-- Backup calling summary.  [CHAR LIMIT=100] -->
-    <!-- Deprecated ToDo: b/260059444 remove it next version-->
     <string name="backup_calling_setting_summary">If <xliff:g id="backup_calling_operator_text" example="Google Fi">%1$s</xliff:g> is unavailable or roaming, use your mobile data SIM for <xliff:g id="backup_calling_carrier_text" example="Google Fi">%1$s</xliff:g> calls.</string>
 
     <!-- List of synonyms for the cross SIM calling titles, used to match in settings search [CHAR LIMIT=NONE] -->
-    <!-- Deprecated ToDo: b/260059444 remove it next version-->
     <string name="keywords_backup_calling">backup calling</string>
 
     <!-- Title for enable MMS notification when trying to receive MMS.  [CHAR LIMIT=40] -->
diff --git a/res/xml/memtag_page.xml b/res/xml/memtag_page.xml
index 6255b43..bad53c9 100644
--- a/res/xml/memtag_page.xml
+++ b/res/xml/memtag_page.xml
@@ -23,7 +23,7 @@
         android:title="@string/memtag_intro"
         settings:searchable="false"/>
 
-    <SwitchPreference
+    <com.android.settingslib.RestrictedSwitchPreference
         android:id="@+id/memtag_page_switch"
         android:key="memtag"
         android:title="@string/memtag_toggle"
diff --git a/res/xml/mobile_network_settings.xml b/res/xml/mobile_network_settings.xml
index 1ce7d27..987c2ce 100644
--- a/res/xml/mobile_network_settings.xml
+++ b/res/xml/mobile_network_settings.xml
@@ -183,6 +183,14 @@
                 android:persistent="true"
                 settings:controller="com.android.settings.network.telephony.VideoCallingPreferenceController"/>
 
+            <SwitchPreference
+                android:key="backup_calling_key"
+                android:title="@string/backup_calling_settings_title"
+                android:persistent="false"
+                android:summary="@string/backup_calling_setting_summary"
+                settings:keywords="@string/keywords_backup_calling"
+                settings:controller="com.android.settings.network.telephony.BackupCallingPreferenceController"/>
+
         </PreferenceCategory>
 
         <com.android.settings.network.telephony.cdma.CdmaListPreference
diff --git a/res/xml/network_provider_calls_sms.xml b/res/xml/network_provider_calls_sms.xml
index 8d15578..2281f48 100644
--- a/res/xml/network_provider_calls_sms.xml
+++ b/res/xml/network_provider_calls_sms.xml
@@ -43,4 +43,12 @@
         settings:controller="com.android.settings.network.telephony.NetworkProviderWifiCallingPreferenceController"
         settings:allowDividerAbove="true"
         />
+
+    <PreferenceCategory
+        android:key="provider_model_backup_calling_category"
+        android:title="@string/backup_calling_settings_title"
+        android:order="25"
+        settings:controller="com.android.settings.network.telephony.NetworkProviderBackupCallingPreferenceController"
+        settings:allowDividerAbove="true"
+        />
 </PreferenceScreen>
diff --git a/res/xml/security_advanced_settings.xml b/res/xml/security_advanced_settings.xml
index c489597..4db078e 100644
--- a/res/xml/security_advanced_settings.xml
+++ b/res/xml/security_advanced_settings.xml
@@ -107,7 +107,7 @@
         settings:isPreferenceVisible="@bool/config_show_sim_info"
         settings:controller="com.android.settings.security.ConfirmSimDeletionPreferenceController" />
 
-  <Preference
+  <com.android.settingslib.RestrictedPreference
         android:order="100"
         android:id="@+id/memtag_page"
         android:key="memtag_page"
diff --git a/src/com/android/settings/MainClearConfirm.java b/src/com/android/settings/MainClearConfirm.java
index 9208c4f..c8b982e 100644
--- a/src/com/android/settings/MainClearConfirm.java
+++ b/src/com/android/settings/MainClearConfirm.java
@@ -267,8 +267,10 @@
     @VisibleForTesting
     void setSubtitle() {
         if (mEraseEsims) {
-            ((TextView) mContentView.findViewById(R.id.sud_layout_description))
-                .setText(R.string.main_clear_final_desc_esim);
+            TextView confirmationMessage = mContentView.findViewById(R.id.sud_layout_description);
+            if (confirmationMessage != null) {
+                confirmationMessage.setText(R.string.main_clear_final_desc_esim);
+                }
         }
     }
 
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 076d0e1..c37b609 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -439,6 +439,7 @@
 
         trampolineIntent.putExtra(EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY,
                 highlightMenuKey);
+        trampolineIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
         return trampolineIntent;
     }
 
diff --git a/src/com/android/settings/SettingsApplication.java b/src/com/android/settings/SettingsApplication.java
index 9c37d55..f06d999 100644
--- a/src/com/android/settings/SettingsApplication.java
+++ b/src/com/android/settings/SettingsApplication.java
@@ -25,6 +25,7 @@
 import androidx.window.embedding.SplitController;
 
 import com.android.settings.activityembedding.ActivityEmbeddingRulesController;
+import com.android.settings.core.instrumentation.ElapsedTimeUtils;
 import com.android.settings.homepage.SettingsHomepageActivity;
 import com.android.settings.spa.SettingsSpaEnvironment;
 import com.android.settingslib.applications.AppIconCacheManager;
@@ -42,6 +43,7 @@
     @Override
     public void onCreate() {
         super.onCreate();
+        ElapsedTimeUtils.assignSuwFinishedTimeStamp(this.getApplicationContext());
 
         // Set Spa environment.
         setSpaEnvironment();
diff --git a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
index ea8a5f5..3c6077e 100644
--- a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
@@ -15,6 +15,8 @@
  */
 package com.android.settings.connecteddevice;
 
+import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
+
 import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.net.Uri;
@@ -36,7 +38,6 @@
 public class ConnectedDeviceDashboardFragment extends DashboardFragment {
 
     private static final String TAG = "ConnectedDeviceFrag";
-    private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
     private static final String SYSTEMUI_PACKAGE_NAME = "com.android.systemui";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     private static final String SLICE_ACTION = "com.android.settings.SEARCH_RESULT_TRAMPOLINE";
diff --git a/src/com/android/settings/core/instrumentation/ElapsedTimeUtils.java b/src/com/android/settings/core/instrumentation/ElapsedTimeUtils.java
new file mode 100644
index 0000000..5b09d5f
--- /dev/null
+++ b/src/com/android/settings/core/instrumentation/ElapsedTimeUtils.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 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.settings.core.instrumentation;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import java.util.Optional;
+
+/** Class for calculating the elapsed time of the metrics. */
+public class ElapsedTimeUtils {
+    private static final String TAG = "ElapsedTimeUtils";
+    private static final String ELAPSED_TIME_PREF_FILENAME = "elapsed_time_info";
+    private static final String SUW_FINISHED_TIME_MS = "suw_finished_time_ms";
+    private static Optional<Long> sSuwFinishedTimeStamp = Optional.empty();;
+    @VisibleForTesting
+    static final long DEFAULT_SETUP_TIME = -1L;
+
+    /**
+     * Keeps the timestamp after SetupWizard finished.
+     *
+     * @param timestamp The timestamp of the SetupWizard finished.
+     */
+    public static void storeSuwFinishedTimestamp(@NonNull Context context, long timestamp) {
+        final SharedPreferences sharedPrefs = getSharedPrefs(context);
+        if (!sharedPrefs.contains(SUW_FINISHED_TIME_MS)) {
+            sSuwFinishedTimeStamp = Optional.of(timestamp);
+            sharedPrefs.edit().putLong(SUW_FINISHED_TIME_MS, timestamp).apply();
+        }
+    }
+
+    /**
+     * Retrieves the preference value of SUW_FINISHED_TIME_MS and
+     * assigns to sSuwFinishedTimeStamp.
+     */
+    public static void assignSuwFinishedTimeStamp(@NonNull Context context) {
+        final SharedPreferences sharedPrefs = getSharedPrefs(context);
+        if (sharedPrefs.contains(SUW_FINISHED_TIME_MS) && !sSuwFinishedTimeStamp.isPresent()) {
+            sSuwFinishedTimeStamp = Optional.of(getSuwFinishedTimestamp(context));
+        }
+    }
+
+    /**
+     * Gets the elapsed time by (timestamp - time of SetupWizard finished).
+     * @param timestamp The timestamp of the current time.
+     * @return The elapsed time after device setup finished.
+     */
+    public static long getElapsedTime(long timestamp) {
+        if (!sSuwFinishedTimeStamp.isPresent()) {
+            Log.w(TAG, "getElapsedTime: sSuwFinishedTimeStamp is null");
+            return DEFAULT_SETUP_TIME;
+        }
+        if (sSuwFinishedTimeStamp.get() != DEFAULT_SETUP_TIME) {
+            final long elapsedTime = timestamp - sSuwFinishedTimeStamp.get();
+            return elapsedTime > 0L ? elapsedTime : DEFAULT_SETUP_TIME;
+        }
+        return DEFAULT_SETUP_TIME;
+    }
+
+    @VisibleForTesting
+    static long getSuwFinishedTimestamp(Context context) {
+        return getSharedPrefs(context).getLong(SUW_FINISHED_TIME_MS, DEFAULT_SETUP_TIME);
+    }
+
+    private static SharedPreferences getSharedPrefs(Context context) {
+        return context
+            .getApplicationContext()
+            .getSharedPreferences(ELAPSED_TIME_PREF_FILENAME, Context.MODE_PRIVATE);
+    }
+
+    private ElapsedTimeUtils() {}
+}
diff --git a/src/com/android/settings/core/instrumentation/StatsLogWriter.java b/src/com/android/settings/core/instrumentation/StatsLogWriter.java
index 7b5915a..2b17ba7 100644
--- a/src/com/android/settings/core/instrumentation/StatsLogWriter.java
+++ b/src/com/android/settings/core/instrumentation/StatsLogWriter.java
@@ -31,7 +31,8 @@
                 SettingsEnums.PAGE_VISIBLE /* action */,
                 pageId, /* target pageId */
                 "" /* changedPreferenceKey */,
-                latency /* changedPreferenceIntValue */);
+                latency /* changedPreferenceIntValue */,
+                ElapsedTimeUtils.getElapsedTime(System.currentTimeMillis()));
     }
 
     @Override
@@ -41,7 +42,8 @@
                 SettingsEnums.PAGE_HIDE /* action */,
                 pageId,
                 "" /* changedPreferenceKey */,
-                visibleTime /* changedPreferenceIntValue */);
+                visibleTime /* changedPreferenceIntValue */,
+                ElapsedTimeUtils.getElapsedTime(System.currentTimeMillis()));
     }
 
     @Override
@@ -51,7 +53,8 @@
                 SettingsEnums.ACTION_SETTINGS_TILE_CLICK /* action */,
                 SettingsEnums.PAGE_UNKNOWN /* pageId */,
                 key /* changedPreferenceKey */,
-                0 /* changedPreferenceIntValue */);
+                0 /* changedPreferenceIntValue */,
+                ElapsedTimeUtils.getElapsedTime(System.currentTimeMillis()));
     }
 
     @Override
@@ -61,7 +64,8 @@
                 SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE /* action */,
                 SettingsEnums.PAGE_UNKNOWN /* pageId */,
                 key /* changedPreferenceKey */,
-                value /* changedPreferenceIntValue */);
+                value /* changedPreferenceIntValue */,
+                ElapsedTimeUtils.getElapsedTime(System.currentTimeMillis()));
     }
 
     @Override
@@ -107,6 +111,7 @@
                 action,
                 pageId,
                 key,
-                value);
+                value,
+                ElapsedTimeUtils.getElapsedTime(System.currentTimeMillis()));
     }
 }
diff --git a/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragment.java b/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragment.java
index fb6d9f4..55ec6d7 100644
--- a/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragment.java
+++ b/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragment.java
@@ -37,7 +37,7 @@
 
     @Override
     public int getMetricsCategory() {
-        return SettingsEnums.DISPLAY_AUTO_ROTATE_SETTINGS;
+        return SettingsEnums.DISPLAY_DEVICE_STATE_AUTO_ROTATE_SETTINGS;
     }
 
     @Override
diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
index 3ab7e57..3083d31 100644
--- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
@@ -296,7 +296,6 @@
                 BatteryHistoricalLogUtil.getPackageNameWithUserId(
                         mBatteryOptimizeUtils.getPackageName(), UserHandle.myUserId()),
                 mLogStringBuilder.toString());
-        mBatteryOptimizeUtils.setAppUsageState(selectedPreference, Action.APPLY);
         Log.d(TAG, "Leave with mode: " + selectedPreference);
     }
 
@@ -424,6 +423,7 @@
         updatePreferenceState(mUnrestrictedPreference, selectedKey);
         updatePreferenceState(mOptimizePreference, selectedKey);
         updatePreferenceState(mRestrictedPreference, selectedKey);
+        mBatteryOptimizeUtils.setAppUsageState(getSelectedPreference(), Action.APPLY);
     }
 
     private void updatePreferenceState(SelectorWithWidgetPreference preference,
diff --git a/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java b/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java
index 1928662..ccc8892 100644
--- a/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java
+++ b/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java
@@ -24,7 +24,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
-import android.os.AsyncTask;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.ArraySet;
@@ -227,10 +226,8 @@
                 mode == MODE_RESTRICTED ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED;
         final boolean allowListed = mode == MODE_UNRESTRICTED;
 
-        AsyncTask.execute(() -> {
-            setAppOptimizationModeInternal(context, appOpsManagerMode, allowListed, uid,
-                    packageName, batteryUtils, powerAllowlistBackend, action);
-        });
+        setAppOptimizationModeInternal(context, appOpsManagerMode, allowListed, uid,
+                packageName, batteryUtils, powerAllowlistBackend, action);
     }
 
     private static void setAppOptimizationModeInternal(
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
index 71d56ae..8029194 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
@@ -141,12 +141,17 @@
     Intent getResumeChargeIntent(boolean isDockDefender);
 
     /**
-     * Returns {@link Set} for hidding applications background usage time.
+     * Returns {@link Set} for hiding applications background usage time.
      */
     Set<CharSequence> getHideBackgroundUsageTimeSet(Context context);
 
     /**
-     * Returns package names for hidding application in the usage screen.
+     * Returns package names for hiding application in the usage screen.
      */
     CharSequence[] getHideApplicationEntries(Context context);
+
+    /**
+     * Returns {@link Set} for ignoring task root class names for screen on time.
+     */
+    Set<CharSequence> getIgnoreScreenOnTimeTaskRootSet(Context context);
 }
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
index 932b35d..53d8701 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
@@ -165,4 +165,9 @@
     public CharSequence[] getHideApplicationEntries(Context context) {
         return new CharSequence[0];
     }
+
+    @Override
+    public Set<CharSequence> getIgnoreScreenOnTimeTaskRootSet(Context context) {
+        return new ArraySet<>();
+    }
 }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/AppUsageDataLoader.java b/src/com/android/settings/fuelgauge/batteryusage/AppUsageDataLoader.java
new file mode 100644
index 0000000..c336fcd
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/AppUsageDataLoader.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 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.settings.fuelgauge.batteryusage;
+
+import android.app.usage.UsageEvents;
+import android.content.Context;
+import android.os.AsyncTask;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
+
+/** Load app usage events data in the background. */
+public final class AppUsageDataLoader {
+    private static final String TAG = "AppUsageDataLoader";
+
+    // For testing only.
+    @VisibleForTesting
+    static Supplier<Map<Long, UsageEvents>> sFakeAppUsageEventsSupplier;
+    @VisibleForTesting
+    static Supplier<List<AppUsageEvent>> sFakeUsageEventsListSupplier;
+
+    private AppUsageDataLoader() {}
+
+    static void enqueueWork(final Context context) {
+        AsyncTask.execute(() -> {
+            Log.d(TAG, "loadAppUsageDataSafely() in the AsyncTask");
+            loadAppUsageDataSafely(context.getApplicationContext());
+        });
+    }
+
+    @VisibleForTesting
+    static void loadAppUsageData(final Context context) {
+        final long start = System.currentTimeMillis();
+        final Map<Long, UsageEvents> appUsageEvents =
+                sFakeAppUsageEventsSupplier != null
+                        ? sFakeAppUsageEventsSupplier.get()
+                        : DataProcessor.getAppUsageEvents(context);
+        if (appUsageEvents == null) {
+            Log.w(TAG, "loadAppUsageData() returns null");
+            return;
+        }
+        final List<AppUsageEvent> appUsageEventList =
+                sFakeUsageEventsListSupplier != null
+                        ? sFakeUsageEventsListSupplier.get()
+                        : DataProcessor.generateAppUsageEventListFromUsageEvents(
+                                context, appUsageEvents);
+        if (appUsageEventList == null || appUsageEventList.isEmpty()) {
+            Log.w(TAG, "loadAppUsageData() returns null or empty content");
+            return;
+        }
+        final long elapsedTime = System.currentTimeMillis() - start;
+        Log.d(TAG, String.format("loadAppUsageData() size=%d in %d/ms", appUsageEventList.size(),
+                elapsedTime));
+        // Uploads the AppUsageEvent data into database.
+        DatabaseUtils.sendAppUsageEventData(context, appUsageEventList);
+    }
+
+    private static void loadAppUsageDataSafely(final Context context) {
+        try {
+            loadAppUsageData(context);
+        } catch (RuntimeException e) {
+            Log.e(TAG, "loadAppUsageData:" + e);
+        }
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProvider.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProvider.java
index 4827f8f..4abcdc3 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProvider.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProvider.java
@@ -29,6 +29,8 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventDao;
+import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
 import com.android.settings.fuelgauge.batteryusage.db.BatteryState;
 import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDao;
 import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
@@ -43,11 +45,11 @@
     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
     public static final Duration QUERY_DURATION_HOURS = Duration.ofDays(6);
 
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    public static final String QUERY_KEY_TIMESTAMP = "timestamp";
-
     /** Codes */
     private static final int BATTERY_STATE_CODE = 1;
+    private static final int APP_USAGE_LATEST_TIMESTAMP_CODE = 2;
+    private static final int APP_USAGE_EVENT_CODE = 3;
+
     private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
 
     static {
@@ -55,10 +57,19 @@
                 DatabaseUtils.AUTHORITY,
                 /*path=*/ DatabaseUtils.BATTERY_STATE_TABLE,
                 /*code=*/ BATTERY_STATE_CODE);
+        sUriMatcher.addURI(
+                DatabaseUtils.AUTHORITY,
+                /*path=*/ DatabaseUtils.APP_USAGE_LATEST_TIMESTAMP_PATH,
+                /*code=*/ APP_USAGE_LATEST_TIMESTAMP_CODE);
+        sUriMatcher.addURI(
+                DatabaseUtils.AUTHORITY,
+                /*path=*/ DatabaseUtils.APP_USAGE_EVENT_TABLE,
+                /*code=*/ APP_USAGE_EVENT_CODE);
     }
 
     private Clock mClock;
     private BatteryStateDao mBatteryStateDao;
+    private AppUsageEventDao mAppUsageEventDao;
 
     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
     public void setClock(Clock clock) {
@@ -73,6 +84,7 @@
         }
         mClock = Clock.systemUTC();
         mBatteryStateDao = BatteryStateDatabase.getInstance(getContext()).batteryStateDao();
+        mAppUsageEventDao = BatteryStateDatabase.getInstance(getContext()).appUsageEventDao();
         Log.w(TAG, "create content provider from " + getCallingPackage());
         return true;
     }
@@ -88,6 +100,8 @@
         switch (sUriMatcher.match(uri)) {
             case BATTERY_STATE_CODE:
                 return getBatteryStates(uri);
+            case APP_USAGE_LATEST_TIMESTAMP_CODE:
+                return getAppUsageLatestTimestamp(uri);
             default:
                 throw new IllegalArgumentException("unknown URI: " + uri);
         }
@@ -111,6 +125,14 @@
                     Log.e(TAG, "insert() from:" + uri + " error:" + e);
                     return null;
                 }
+            case APP_USAGE_EVENT_CODE:
+                try {
+                    mAppUsageEventDao.insert(AppUsageEventEntity.create(contentValues));
+                    return uri;
+                } catch (RuntimeException e) {
+                    Log.e(TAG, "insert() from:" + uri + " error:" + e);
+                    return null;
+                }
             default:
                 throw new IllegalArgumentException("unknown URI: " + uri);
         }
@@ -145,24 +167,54 @@
             Log.e(TAG, "query() from:" + uri + " error:" + e);
         }
         AsyncTask.execute(() -> BootBroadcastReceiver.invokeJobRecheck(getContext()));
-        Log.w(TAG, "query battery states in " + (mClock.millis() - timestamp) + "/ms");
+        Log.d(TAG, "query battery states in " + (mClock.millis() - timestamp) + "/ms");
         return cursor;
     }
 
+    private Cursor getAppUsageLatestTimestamp(Uri uri) {
+        final long queryUserId = getQueryUserId(uri);
+        if (queryUserId == DatabaseUtils.INVALID_USER_ID) {
+            return null;
+        }
+        final long timestamp = mClock.millis();
+        Cursor cursor = null;
+        try {
+            cursor = mAppUsageEventDao.getLatestTimestampOfUser(queryUserId);
+        } catch (RuntimeException e) {
+            Log.e(TAG, "query() from:" + uri + " error:" + e);
+        }
+        Log.d(TAG, String.format("query app usage latest timestamp %d for user %d in %d/ms",
+                timestamp, queryUserId, (mClock.millis() - timestamp)));
+        return cursor;
+    }
+
+    // If URI contains query parameter QUERY_KEY_USERID, use the value directly.
+    // Otherwise, return INVALID_USER_ID.
+    private long getQueryUserId(Uri uri) {
+        Log.d(TAG, "getQueryUserId from uri: " + uri);
+        return getQueryValueFromUri(
+                uri, DatabaseUtils.QUERY_KEY_USERID, DatabaseUtils.INVALID_USER_ID);
+    }
+
     // If URI contains query parameter QUERY_KEY_TIMESTAMP, use the value directly.
     // Otherwise, load the data for QUERY_DURATION_HOURS by default.
     private long getQueryTimestamp(Uri uri, long defaultTimestamp) {
-        final String firstTimestampString = uri.getQueryParameter(QUERY_KEY_TIMESTAMP);
-        if (TextUtils.isEmpty(firstTimestampString)) {
-            Log.w(TAG, "empty query timestamp");
-            return defaultTimestamp;
+        Log.d(TAG, "getQueryTimestamp from uri: " + uri);
+        return getQueryValueFromUri(uri, DatabaseUtils.QUERY_KEY_TIMESTAMP, defaultTimestamp);
+    }
+
+    private long getQueryValueFromUri(Uri uri, String key, long defaultValue) {
+        final String value = uri.getQueryParameter(key);
+        if (TextUtils.isEmpty(value)) {
+            Log.w(TAG, "empty query value");
+            return defaultValue;
         }
 
         try {
-            return Long.parseLong(firstTimestampString);
+            return Long.parseLong(value);
         } catch (NumberFormatException e) {
-            Log.e(TAG, "invalid query timestamp: " + firstTimestampString, e);
-            return defaultTimestamp;
+            Log.e(TAG, "invalid query value: " + value, e);
+            return defaultValue;
         }
     }
 }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
index 3cb5465..d446bb2 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
@@ -58,7 +58,7 @@
         final long elapsedTime = System.currentTimeMillis() - start;
         Log.d(TAG, String.format("getBatteryUsageStats() in %d/ms", elapsedTime));
 
-        // Uploads the BatteryEntry data into SettingsIntelligence.
+        // Uploads the BatteryEntry data into database.
         DatabaseUtils.sendBatteryEntryData(
                 context, batteryEntryList, batteryUsageStats, isFullChargeStart);
         DataProcessor.closeBatteryUsageStats(batteryUsageStats);
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java b/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java
index 5b48c9f..c9d73cd 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java
@@ -23,6 +23,8 @@
 import android.os.Looper;
 import android.util.Log;
 
+import com.android.settings.core.instrumentation.ElapsedTimeUtils;
+
 import java.time.Duration;
 
 /** Receives broadcasts to start or stop the periodic fetching job. */
@@ -77,6 +79,8 @@
             recheckIntent.setClass(context, BootBroadcastReceiver.class);
             mHandler.postDelayed(() -> context.sendBroadcast(recheckIntent),
                     RESCHEDULE_FOR_BOOT_ACTION);
+        } else if (ACTION_SETUP_WIZARD_FINISHED.equals(action)) {
+            ElapsedTimeUtils.storeSuwFinishedTimestamp(context, System.currentTimeMillis());
         }
     }
 
diff --git a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
index c5c0522..38879d9 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
@@ -16,8 +16,11 @@
 package com.android.settings.fuelgauge.batteryusage;
 
 import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.app.usage.UsageEvents.Event;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.os.BatteryUsageStats;
 import android.os.Build;
@@ -25,10 +28,12 @@
 import android.os.UserHandle;
 import android.text.format.DateFormat;
 import android.util.Base64;
+import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
 
 import com.android.settings.fuelgauge.BatteryUtils;
+import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -49,7 +54,7 @@
             CONSUMER_TYPE_SYSTEM_BATTERY,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public static @interface ConsumerType {
+    public @interface ConsumerType {
     }
 
     public static final int CONSUMER_TYPE_UNKNOWN = 0;
@@ -60,8 +65,8 @@
     private ConvertUtils() {
     }
 
-    /** Converts to content values */
-    public static ContentValues convertToContentValues(
+    /** Converts {@link BatteryEntry} to content values */
+    public static ContentValues convertBatteryEntryToContentValues(
             final BatteryEntry entry,
             final BatteryUsageStats batteryUsageStats,
             final int batteryLevel,
@@ -103,6 +108,19 @@
         return values;
     }
 
+    /** Converts {@link AppUsageEvent} to content values */
+    public static ContentValues convertAppUsageEventToContentValues(final AppUsageEvent event) {
+        final ContentValues values = new ContentValues();
+        values.put(AppUsageEventEntity.KEY_UID, event.getUid());
+        values.put(AppUsageEventEntity.KEY_USER_ID, event.getUserId());
+        values.put(AppUsageEventEntity.KEY_TIMESTAMP, event.getTimestamp());
+        values.put(AppUsageEventEntity.KEY_APP_USAGE_EVENT_TYPE, event.getType().getNumber());
+        values.put(AppUsageEventEntity.KEY_PACKAGE_NAME, event.getPackageName());
+        values.put(AppUsageEventEntity.KEY_INSTANCE_ID, event.getInstanceId());
+        values.put(AppUsageEventEntity.KEY_TASK_ROOT_PACKAGE_NAME, event.getTaskRootPackageName());
+        return values;
+    }
+
     /** Gets the encoded string from {@link BatteryInformation} instance. */
     public static String convertBatteryInformationToString(
             final BatteryInformation batteryInformation) {
@@ -135,7 +153,7 @@
             BatteryEntry entry,
             BatteryUsageStats batteryUsageStats) {
         return new BatteryHistEntry(
-                convertToContentValues(
+                convertBatteryEntryToContentValues(
                         entry,
                         batteryUsageStats,
                         /*batteryLevel=*/ 0,
@@ -146,6 +164,51 @@
                         /*isFullChargeStart=*/ false));
     }
 
+    /** Converts to {@link AppUsageEvent} from {@link Event} */
+    @Nullable
+    public static AppUsageEvent convertToAppUsageEvent(
+            Context context, final Event event, final long userId) {
+        if (event.getPackageName() == null) {
+            // See b/190609174: Event package names should never be null, but sometimes they are.
+            // Note that system events like device shutting down should still come with the android
+            // package name.
+            Log.w(TAG, String.format(
+                    "Ignoring a usage event with null package name (timestamp=%d, type=%d)",
+                    event.getTimeStamp(), event.getEventType()));
+            return null;
+        }
+
+        final AppUsageEvent.Builder appUsageEventBuilder = AppUsageEvent.newBuilder();
+        appUsageEventBuilder
+                .setTimestamp(event.getTimeStamp())
+                .setType(getAppUsageEventType(event.getEventType()))
+                .setPackageName(event.getPackageName())
+                .setUserId(userId);
+
+        try {
+            final long uid = context
+                    .getPackageManager()
+                    .getPackageUidAsUser(event.getPackageName(), (int) userId);
+            appUsageEventBuilder.setUid(uid);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.w(TAG, String.format(
+                    "Fail to get uid for package %s of user %d)", event.getPackageName(), userId));
+            return null;
+        }
+
+        try {
+            appUsageEventBuilder.setInstanceId(event.getInstanceId());
+        } catch (NoClassDefFoundError | NoSuchMethodError e) {
+            Log.w(TAG, "UsageEvent instance ID API error");
+        }
+        String taskRootPackageName = getTaskRootPackageName(event);
+        if (taskRootPackageName != null) {
+            appUsageEventBuilder.setTaskRootPackageName(taskRootPackageName);
+        }
+
+        return appUsageEventBuilder.build();
+    }
+
     /** Converts UTC timestamp to human readable local time string. */
     public static String utcToLocalTime(Context context, long timestamp) {
         final Locale locale = getLocale(context);
@@ -185,6 +248,50 @@
                 : Locale.getDefault();
     }
 
+    /**
+     * Returns the package name of the task root when this event was reported when {@code event} is
+     * one of:
+     *
+     * <ul>
+     *   <li>{@link Event#ACTIVITY_RESUMED}
+     *   <li>{@link Event#ACTIVITY_STOPPED}
+     * </ul>
+     */
+    @Nullable
+    private static String getTaskRootPackageName(Event event) {
+        int eventType = event.getEventType();
+        if (eventType != Event.ACTIVITY_RESUMED && eventType != Event.ACTIVITY_STOPPED) {
+            // Task root is only relevant for ACTIVITY_* events.
+            return null;
+        }
+
+        try {
+            String taskRootPackageName = event.getTaskRootPackageName();
+            if (taskRootPackageName == null) {
+                Log.w(TAG, String.format(
+                        "Null task root in event with timestamp %d, type=%d, package %s",
+                        event.getTimeStamp(), event.getEventType(), event.getPackageName()));
+            }
+            return taskRootPackageName;
+        } catch (NoSuchMethodError e) {
+            Log.w(TAG, "Failed to call Event#getTaskRootPackageName()");
+            return null;
+        }
+    }
+
+    private static AppUsageEventType getAppUsageEventType(final int eventType) {
+        switch (eventType) {
+            case Event.ACTIVITY_RESUMED:
+                return AppUsageEventType.ACTIVITY_RESUMED;
+            case Event.ACTIVITY_STOPPED:
+                return AppUsageEventType.ACTIVITY_STOPPED;
+            case Event.DEVICE_SHUTDOWN:
+                return AppUsageEventType.DEVICE_SHUTDOWN;
+            default:
+                return AppUsageEventType.UNKNOWN;
+        }
+    }
+
     private static BatteryInformation constructBatteryInformation(
             final BatteryEntry entry,
             final BatteryUsageStats batteryUsageStats,
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
index 9d2774e..e3e1912 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
@@ -18,10 +18,14 @@
 
 import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.utcToLocalTime;
 
+import android.app.usage.IUsageStatsManager;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageEvents.Event;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
 import android.os.AsyncTask;
 import android.os.BatteryConsumer;
 import android.os.BatteryStatsManager;
@@ -30,6 +34,8 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UidBatteryConsumer;
 import android.os.UserBatteryConsumer;
 import android.os.UserHandle;
@@ -90,6 +96,11 @@
     @VisibleForTesting
     static long sFakeCurrentTimeMillis = 0;
 
+    @VisibleForTesting
+    static IUsageStatsManager sUsageStatsManager =
+            IUsageStatsManager.Stub.asInterface(
+                    ServiceManager.getService(Context.USAGE_STATS_SERVICE));
+
     /** A callback listener when battery usage loading async task is executed. */
     public interface UsageMapAsyncResponse {
         /** The callback function when batteryUsageMap is loaded. */
@@ -184,6 +195,55 @@
     }
 
     /**
+     * Gets the {@link UsageEvents} from system service for all unlocked users.
+     */
+    @Nullable
+    public static Map<Long, UsageEvents> getAppUsageEvents(Context context) {
+        final long start = System.currentTimeMillis();
+        final boolean isWorkProfileUser = DatabaseUtils.isWorkProfile(context);
+        Log.d(TAG, "getAppUsageEvents() isWorkProfileUser:" + isWorkProfileUser);
+        if (isWorkProfileUser) {
+            try {
+                context = context.createPackageContextAsUser(
+                        /*packageName=*/ context.getPackageName(),
+                        /*flags=*/ 0,
+                        /*user=*/ UserHandle.OWNER);
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.e(TAG, "context.createPackageContextAsUser() fail:" + e);
+                return null;
+            }
+        }
+        final Map<Long, UsageEvents> resultMap = new HashMap();
+        final UserManager userManager = context.getSystemService(UserManager.class);
+        if (userManager == null) {
+            return null;
+        }
+        final long sixDaysAgoTimestamp =
+                DatabaseUtils.getTimestampSixDaysAgo(Calendar.getInstance());
+        final String callingPackage = context.getPackageName();
+        final long now = System.currentTimeMillis();
+        for (final UserInfo user : userManager.getAliveUsers()) {
+            // When the user is not unlocked, UsageStatsManager will return null, so bypass the
+            // following data loading logics directly.
+            if (!userManager.isUserUnlocked(user.id)) {
+                Log.w(TAG, "fail to load app usage event for user :" + user.id + " because locked");
+                continue;
+            }
+            final long startTime = DatabaseUtils.getAppUsageStartTimestampOfUser(
+                    context, user.id, sixDaysAgoTimestamp);
+            final UsageEvents events = getAppUsageEventsForUser(
+                    sUsageStatsManager, startTime, now, user.id, callingPackage);
+            if (events != null) {
+                resultMap.put(Long.valueOf(user.id), events);
+            }
+        }
+        final long elapsedTime = System.currentTimeMillis() - start;
+        Log.d(TAG, String.format("getAppUsageEvents() for all unlocked users in %d/ms",
+                elapsedTime));
+        return resultMap.isEmpty() ? null : resultMap;
+    }
+
+    /**
      * Closes the {@link BatteryUsageStats} after using it.
      */
     public static void closeBatteryUsageStats(BatteryUsageStats batteryUsageStats) {
@@ -197,6 +257,59 @@
     }
 
     /**
+     * Generates the list of {@link AppUsageEvent} from the supplied {@link UsageEvents}.
+     */
+    public static List<AppUsageEvent> generateAppUsageEventListFromUsageEvents(
+            Context context, Map<Long, UsageEvents> usageEventsMap) {
+        final List<AppUsageEvent> appUsageEventList = new ArrayList<>();
+        long numEventsFetched = 0;
+        long numAllEventsFetched = 0;
+        final Set<CharSequence> ignoreScreenOnTimeTaskRootSet =
+                FeatureFactory.getFactory(context)
+                        .getPowerUsageFeatureProvider(context)
+                        .getIgnoreScreenOnTimeTaskRootSet(context);
+        for (final long userId : usageEventsMap.keySet()) {
+            final UsageEvents usageEvents = usageEventsMap.get(userId);
+            while (usageEvents.hasNextEvent()) {
+                final Event event = new Event();
+                usageEvents.getNextEvent(event);
+                numAllEventsFetched++;
+                switch (event.getEventType()) {
+                    case Event.ACTIVITY_RESUMED:
+                    case Event.ACTIVITY_STOPPED:
+                    case Event.DEVICE_SHUTDOWN:
+                        final String taskRootClassName = event.getTaskRootClassName();
+                        if (!TextUtils.isEmpty(taskRootClassName)
+                                && !ignoreScreenOnTimeTaskRootSet.isEmpty()
+                                && contains(
+                                        taskRootClassName, ignoreScreenOnTimeTaskRootSet)) {
+                            Log.w(TAG, String.format(
+                                    "Ignoring a usage event with task root class name %s, "
+                                            + "(timestamp=%d, type=%d)",
+                                    taskRootClassName,
+                                    event.getTimeStamp(),
+                                    event.getEventType()));
+                            break;
+                        }
+                        final AppUsageEvent appUsageEvent =
+                                ConvertUtils.convertToAppUsageEvent(context, event, userId);
+                        if (appUsageEvent != null) {
+                            numEventsFetched++;
+                            appUsageEventList.add(appUsageEvent);
+                        }
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+        Log.w(TAG, String.format(
+                "Read %d relevant events (%d total) from UsageStatsManager", numEventsFetched,
+                numAllEventsFetched));
+        return appUsageEventList;
+    }
+
+    /**
      * Generates the list of {@link BatteryEntry} from the supplied {@link BatteryUsageStats}.
      */
     @Nullable
@@ -508,13 +621,30 @@
                 asyncResponseDelegate).execute();
     }
 
+    @Nullable
+    private static UsageEvents getAppUsageEventsForUser(
+            final IUsageStatsManager usageStatsManager, final long startTime, final long endTime,
+            final int userId, final String callingPackage) {
+        final long start = System.currentTimeMillis();
+        UsageEvents events = null;
+        try {
+            events = usageStatsManager.queryEventsForUser(
+                    startTime, endTime, userId, callingPackage);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error fetching usage events: ", e);
+        }
+        final long elapsedTime = System.currentTimeMillis() - start;
+        Log.d(TAG, String.format("getAppUsageEventsForUser(): %d from %d to %d in %d/ms", userId,
+                startTime, endTime, elapsedTime));
+        return events;
+    }
+
     /**
      * @return Returns the overall battery usage data from battery stats service directly.
      *
      * The returned value should be always a 2d map and composed by only 1 part:
      * - [SELECTED_INDEX_ALL][SELECTED_INDEX_ALL]
      */
-    @Nullable
     private static Map<Integer, Map<Integer, BatteryDiffData>> getBatteryUsageMapFromStatsService(
             final Context context) {
         final Map<Integer, Map<Integer, BatteryDiffData>> resultMap = new HashMap<>();
@@ -1335,6 +1465,7 @@
         return calendar.getTimeInMillis();
     }
 
+    /** Whether the Set contains the target. */
     private static boolean contains(String target, Set<CharSequence> packageNames) {
         if (target != null && packageNames != null) {
             for (CharSequence packageName : packageNames) {
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
index 8ff802d..d7c98a7 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
@@ -45,12 +45,11 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Supplier;
 
 /** A utility class to operate battery usage database. */
 public final class DatabaseUtils {
     private static final String TAG = "DatabaseUtils";
-    /** Key for query parameter timestamp used in BATTERY_CONTENT_URI **/
-    private static final String QUERY_KEY_TIMESTAMP = "timestamp";
     /** Clear memory threshold for device booting phase. **/
     private static final long CLEAR_MEMORY_THRESHOLD_MS = Duration.ofMinutes(5).toMillis();
     private static final long CLEAR_MEMORY_DELAYED_MS = Duration.ofSeconds(2).toMillis();
@@ -62,8 +61,18 @@
     public static final String AUTHORITY = "com.android.settings.battery.usage.provider";
     /** A table name for battery usage history. */
     public static final String BATTERY_STATE_TABLE = "BatteryState";
+    /** A table name for app usage events. */
+    public static final String APP_USAGE_EVENT_TABLE = "AppUsageEvent";
+    /** A path name for app usage latest timestamp query. */
+    public static final String APP_USAGE_LATEST_TIMESTAMP_PATH = "appUsageLatestTimestamp";
     /** A class name for battery usage data provider. */
     public static final String SETTINGS_PACKAGE_PATH = "com.android.settings";
+    /** Key for query parameter timestamp used in BATTERY_CONTENT_URI **/
+    public static final String QUERY_KEY_TIMESTAMP = "timestamp";
+    /** Key for query parameter userid used in APP_USAGE_EVENT_URI **/
+    public static final String QUERY_KEY_USERID = "userid";
+
+    public static final long INVALID_USER_ID = Integer.MIN_VALUE;
 
     /** A content URI to access battery usage states data. */
     public static final Uri BATTERY_CONTENT_URI =
@@ -72,6 +81,19 @@
                     .authority(AUTHORITY)
                     .appendPath(BATTERY_STATE_TABLE)
                     .build();
+    /** A content URI to access app usage events data. */
+    public static final Uri APP_USAGE_EVENT_URI =
+            new Uri.Builder()
+                    .scheme(ContentResolver.SCHEME_CONTENT)
+                    .authority(AUTHORITY)
+                    .appendPath(APP_USAGE_EVENT_TABLE)
+                    .build();
+
+    // For testing only.
+    @VisibleForTesting
+    static Supplier<Cursor> sFakeBatteryStateSupplier;
+    @VisibleForTesting
+    static Supplier<Cursor> sFakeAppUsageLatestTimestampSupplier;
 
     private DatabaseUtils() {
     }
@@ -82,6 +104,27 @@
         return userManager.isManagedProfile() && !userManager.isSystemUser();
     }
 
+    /** Returns the latest timestamp current user data in app usage event table. */
+    public static long getAppUsageStartTimestampOfUser(
+            Context context, final long userId, final long earliestTimestamp) {
+        final long startTime = System.currentTimeMillis();
+        // Builds the content uri everytime to avoid cache.
+        final Uri appUsageLatestTimestampUri =
+                new Uri.Builder()
+                        .scheme(ContentResolver.SCHEME_CONTENT)
+                        .authority(AUTHORITY)
+                        .appendPath(APP_USAGE_LATEST_TIMESTAMP_PATH)
+                        .appendQueryParameter(
+                                QUERY_KEY_USERID, Long.toString(userId))
+                        .build();
+        final long latestTimestamp =
+                loadAppUsageLatestTimestampFromContentProvider(context, appUsageLatestTimestampUri);
+        Log.d(TAG, String.format(
+                "getAppUsageStartTimestampOfUser() userId=%d latestTimestamp=%d in %d/ms",
+                userId, latestTimestamp, (System.currentTimeMillis() - startTime)));
+        return Math.max(latestTimestamp, earliestTimestamp);
+    }
+
     /** Long: for timestamp and String: for BatteryHistEntry.getKey() */
     public static Map<Long, Map<String, BatteryHistEntry>> getHistoryMapSinceLastFullCharge(
             Context context, Calendar calendar) {
@@ -113,10 +156,10 @@
     public static void clearAll(Context context) {
         AsyncTask.execute(() -> {
             try {
-                BatteryStateDatabase
-                        .getInstance(context.getApplicationContext())
-                        .batteryStateDao()
-                        .clearAll();
+                final BatteryStateDatabase database = BatteryStateDatabase
+                        .getInstance(context.getApplicationContext());
+                database.batteryStateDao().clearAll();
+                database.appUsageEventDao().clearAll();
             } catch (RuntimeException e) {
                 Log.e(TAG, "clearAll() failed", e);
             }
@@ -127,17 +170,59 @@
     public static void clearExpiredDataIfNeeded(Context context) {
         AsyncTask.execute(() -> {
             try {
-                BatteryStateDatabase
-                        .getInstance(context.getApplicationContext())
-                        .batteryStateDao()
-                        .clearAllBefore(Clock.systemUTC().millis()
-                                - Duration.ofDays(DATA_RETENTION_INTERVAL_DAY).toMillis());
+                final BatteryStateDatabase database = BatteryStateDatabase
+                        .getInstance(context.getApplicationContext());
+                final long earliestTimestamp = Clock.systemUTC().millis()
+                        - Duration.ofDays(DATA_RETENTION_INTERVAL_DAY).toMillis();
+                database.batteryStateDao().clearAllBefore(earliestTimestamp);
+                database.appUsageEventDao().clearAllBefore(earliestTimestamp);
             } catch (RuntimeException e) {
                 Log.e(TAG, "clearAllBefore() failed", e);
             }
         });
     }
 
+    /** Returns the timestamp for 00:00 6 days before the calendar date. */
+    public static long getTimestampSixDaysAgo(Calendar calendar) {
+        Calendar startCalendar =
+                calendar == null ? Calendar.getInstance() : (Calendar) calendar.clone();
+        startCalendar.add(Calendar.DAY_OF_YEAR, -6);
+        startCalendar.set(Calendar.HOUR_OF_DAY, 0);
+        startCalendar.set(Calendar.MINUTE, 0);
+        startCalendar.set(Calendar.SECOND, 0);
+        startCalendar.set(Calendar.MILLISECOND, 0);
+        return startCalendar.getTimeInMillis();
+    }
+
+    static List<ContentValues> sendAppUsageEventData(
+            final Context context, final List<AppUsageEvent> appUsageEventList) {
+        final long startTime = System.currentTimeMillis();
+        // Creates the ContentValues list to insert them into provider.
+        final List<ContentValues> valuesList = new ArrayList<>();
+        appUsageEventList.stream()
+                .filter(appUsageEvent -> appUsageEvent.hasUid())
+                .forEach(appUsageEvent -> valuesList.add(
+                        ConvertUtils.convertAppUsageEventToContentValues(appUsageEvent)));
+        int size = 0;
+        final ContentResolver resolver = context.getContentResolver();
+        // Inserts all ContentValues into battery provider.
+        if (!valuesList.isEmpty()) {
+            final ContentValues[] valuesArray = new ContentValues[valuesList.size()];
+            valuesList.toArray(valuesArray);
+            try {
+                size = resolver.bulkInsert(APP_USAGE_EVENT_URI, valuesArray);
+                resolver.notifyChange(APP_USAGE_EVENT_URI, /*observer=*/ null);
+                Log.d(TAG, "insert() app usage events data into database");
+            } catch (Exception e) {
+                Log.e(TAG, "bulkInsert() app usage data into database error:\n" + e);
+            }
+        }
+        Log.d(TAG, String.format("sendAppUsageEventData() size=%d in %d/ms",
+                size, (System.currentTimeMillis() - startTime)));
+        clearMemory();
+        return valuesList;
+    }
+
     static List<ContentValues> sendBatteryEntryData(
             final Context context,
             final List<BatteryEntry> batteryEntryList,
@@ -178,7 +263,7 @@
                                 || backgroundMs != 0;
                     })
                     .forEach(entry -> valuesList.add(
-                            ConvertUtils.convertToContentValues(
+                            ConvertUtils.convertBatteryEntryToContentValues(
                                     entry,
                                     batteryUsageStats,
                                     batteryLevel,
@@ -197,15 +282,15 @@
             valuesList.toArray(valuesArray);
             try {
                 size = resolver.bulkInsert(BATTERY_CONTENT_URI, valuesArray);
-                Log.d(TAG, "insert() data into database with isFullChargeStart:"
+                Log.d(TAG, "insert() battery states data into database with isFullChargeStart:"
                         + isFullChargeStart);
             } catch (Exception e) {
-                Log.e(TAG, "bulkInsert() data into database error:\n" + e);
+                Log.e(TAG, "bulkInsert() battery states data into database error:\n" + e);
             }
         } else {
             // Inserts one fake data into battery provider.
             final ContentValues contentValues =
-                    ConvertUtils.convertToContentValues(
+                    ConvertUtils.convertBatteryEntryToContentValues(
                             /*entry=*/ null,
                             /*batteryUsageStats=*/ null,
                             batteryLevel,
@@ -231,6 +316,30 @@
         return valuesList;
     }
 
+    private static long loadAppUsageLatestTimestampFromContentProvider(
+            Context context, final Uri appUsageLatestTimestampUri) {
+        // We have already make sure the context here is with OWNER user identity. Don't need to
+        // check whether current user is work profile.
+        try (Cursor cursor = sFakeAppUsageLatestTimestampSupplier != null
+                ? sFakeAppUsageLatestTimestampSupplier.get()
+                : context.getContentResolver().query(
+                        appUsageLatestTimestampUri, null, null, null)) {
+            if (cursor == null || cursor.getCount() == 0) {
+                return INVALID_USER_ID;
+            }
+            cursor.moveToFirst();
+            // There is only one column returned so use the index 0 directly.
+            final long latestTimestamp = cursor.getLong(/*columnIndex=*/ 0);
+            try {
+                cursor.close();
+            } catch (Exception e) {
+                Log.e(TAG, "cursor.close() failed", e);
+            }
+            // If there is no data for this user, 0 will be returned from the database.
+            return latestTimestamp == 0 ? INVALID_USER_ID : latestTimestamp;
+        }
+    }
+
     private static Map<Long, Map<String, BatteryHistEntry>> loadHistoryMapFromContentProvider(
             Context context, Uri batteryStateUri) {
         final boolean isWorkProfileUser = isWorkProfile(context);
@@ -247,7 +356,7 @@
             }
         }
         final Map<Long, Map<String, BatteryHistEntry>> resultMap = new HashMap();
-        try (Cursor cursor =
+        try (Cursor cursor = sFakeBatteryStateSupplier != null ? sFakeBatteryStateSupplier.get() :
                      context.getContentResolver().query(batteryStateUri, null, null, null)) {
             if (cursor == null || cursor.getCount() == 0) {
                 return resultMap;
@@ -286,17 +395,4 @@
             Log.w(TAG, "invoke clearMemory()");
         }, CLEAR_MEMORY_DELAYED_MS);
     }
-
-    /** Returns the timestamp for 00:00 6 days before the calendar date. */
-    private static long getTimestampSixDaysAgo(Calendar calendar) {
-        Calendar startCalendar =
-                calendar == null ? Calendar.getInstance() : (Calendar) calendar.clone();
-        startCalendar.add(Calendar.DAY_OF_YEAR, -6);
-        startCalendar.set(Calendar.HOUR_OF_DAY, 0);
-        startCalendar.set(Calendar.MINUTE, 0);
-        startCalendar.set(Calendar.SECOND, 0);
-        startCalendar.set(Calendar.MILLISECOND, 0);
-        return startCalendar.getTimeInMillis();
-    }
-
 }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiver.java b/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiver.java
index fddf01b..d6a2f62 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiver.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiver.java
@@ -40,6 +40,7 @@
             return;
         }
         BatteryUsageDataLoader.enqueueWork(context, /*isFullChargeStart=*/ false);
+        AppUsageDataLoader.enqueueWork(context);
         Log.d(TAG, "refresh periodic job from action=" + action);
         PeriodicJobManager.getInstance(context).refreshJob();
         DatabaseUtils.clearExpiredDataIfNeeded(context);
diff --git a/src/com/android/settings/fuelgauge/batteryusage/TabPreference.java b/src/com/android/settings/fuelgauge/batteryusage/TabPreference.java
index 517e01c..0c5a699 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/TabPreference.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/TabPreference.java
@@ -87,7 +87,7 @@
     @Override
     public void onDetached() {
         super.onDetached();
-        if (mOnPageChangeCallback != null) {
+        if (mViewPager != null && mOnPageChangeCallback != null) {
             mViewPager.unregisterOnPageChangeCallback(mOnPageChangeCallback);
         }
     }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventDao.java b/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventDao.java
new file mode 100644
index 0000000..578a1ff
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventDao.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 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.settings.fuelgauge.batteryusage.db;
+
+import android.database.Cursor;
+
+import androidx.room.Dao;
+import androidx.room.Insert;
+import androidx.room.OnConflictStrategy;
+import androidx.room.Query;
+
+import java.util.List;
+
+/** Data access object for accessing {@link AppUsageEventEntity} in the database. */
+@Dao
+public interface AppUsageEventDao {
+
+    /** Inserts a {@link AppUsageEventEntity} data into the database. */
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    void insert(AppUsageEventEntity event);
+
+    /** Inserts {@link AppUsageEventEntity} data into the database. */
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    void insertAll(List<AppUsageEventEntity> events);
+
+    /** Lists all recorded data after a specific timestamp. */
+    @Query("SELECT * FROM AppUsageEventEntity WHERE timestamp > :timestamp ORDER BY timestamp DESC")
+    List<AppUsageEventEntity> getAllAfter(long timestamp);
+
+    /** Gets the {@link Cursor} of the latest timestamp of the specific user. */
+    @Query("SELECT MAX(timestamp) as timestamp FROM AppUsageEventEntity WHERE userId = :userId")
+    Cursor getLatestTimestampOfUser(long userId);
+
+    /** Deletes all recorded data before a specific timestamp. */
+    @Query("DELETE FROM AppUsageEventEntity WHERE timestamp <= :timestamp")
+    void clearAllBefore(long timestamp);
+
+    /** Clears all recorded data in the database. */
+    @Query("DELETE FROM AppUsageEventEntity")
+    void clearAll();
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventEntity.java b/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventEntity.java
new file mode 100644
index 0000000..9d62d07
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventEntity.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2022 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.settings.fuelgauge.batteryusage.db;
+
+import android.content.ContentValues;
+
+import androidx.room.Entity;
+import androidx.room.PrimaryKey;
+
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/** A {@link Entity} class to save app usage events into database. */
+@Entity
+public class AppUsageEventEntity {
+    private static String sCacheZoneId;
+    private static SimpleDateFormat sCacheSimpleDateFormat;
+
+    /** Keys for accessing {@link ContentValues}. */
+    public static final String KEY_UID = "uid";
+    public static final String KEY_USER_ID = "userId";
+    public static final String KEY_TIMESTAMP = "timestamp";
+    public static final String KEY_APP_USAGE_EVENT_TYPE = "appUsageEventType";
+    public static final String KEY_PACKAGE_NAME = "packageName";
+    public static final String KEY_INSTANCE_ID = "instanceId";
+    public static final String KEY_TASK_ROOT_PACKAGE_NAME = "taskRootPackageName";
+
+    @PrimaryKey(autoGenerate = true)
+    private long mId;
+
+    // Records the app relative information.
+    public final long uid;
+    public final long userId;
+    public final long timestamp;
+    public final int appUsageEventType;
+    public final String packageName;
+    public final int instanceId;
+    public final String taskRootPackageName;
+
+    public AppUsageEventEntity(
+            final long uid,
+            final long userId,
+            final long timestamp,
+            final int appUsageEventType,
+            final String packageName,
+            final int instanceId,
+            final String taskRootPackageName) {
+        this.uid = uid;
+        this.userId = userId;
+        this.timestamp = timestamp;
+        this.appUsageEventType = appUsageEventType;
+        this.packageName = packageName;
+        this.instanceId = instanceId;
+        this.taskRootPackageName = taskRootPackageName;
+    }
+
+    /** Sets the auto-generated content ID. */
+    public void setId(long id) {
+        this.mId = id;
+    }
+
+    /** Gets the auto-generated content ID. */
+    public long getId() {
+        return mId;
+    }
+
+    @Override
+    @SuppressWarnings("JavaUtilDate")
+    public String toString() {
+        final String currentZoneId = TimeZone.getDefault().getID();
+        if (!currentZoneId.equals(sCacheZoneId) || sCacheSimpleDateFormat == null) {
+            sCacheZoneId = currentZoneId;
+            sCacheSimpleDateFormat = new SimpleDateFormat("MMM dd,yyyy HH:mm:ss", Locale.US);
+        }
+        final String recordAtDateTime = sCacheSimpleDateFormat.format(new Date(timestamp));
+        final StringBuilder builder = new StringBuilder()
+                .append("\nAppUsageEvent{")
+                .append(String.format(Locale.US,
+                        "\n\tpackage=%s|uid=%d|userId=%d", packageName, uid, userId))
+                .append(String.format(Locale.US, "\n\ttimestamp=%s|eventType=%d|instanceId=%d",
+                        recordAtDateTime, appUsageEventType, instanceId))
+                .append(String.format(Locale.US, "\n\ttaskRootPackageName=%s",
+                        taskRootPackageName));
+        return builder.toString();
+    }
+
+    /** Creates new {@link AppUsageEventEntity} from {@link ContentValues}. */
+    public static AppUsageEventEntity create(ContentValues contentValues) {
+        Builder builder = AppUsageEventEntity.newBuilder();
+        if (contentValues.containsKey(KEY_UID)) {
+            builder.setUid(contentValues.getAsLong(KEY_UID));
+        }
+        if (contentValues.containsKey(KEY_USER_ID)) {
+            builder.setUserId(contentValues.getAsLong(KEY_USER_ID));
+        }
+        if (contentValues.containsKey(KEY_TIMESTAMP)) {
+            builder.setTimestamp(contentValues.getAsLong(KEY_TIMESTAMP));
+        }
+        if (contentValues.containsKey(KEY_APP_USAGE_EVENT_TYPE)) {
+            builder.setAppUsageEventType(contentValues.getAsInteger(KEY_APP_USAGE_EVENT_TYPE));
+        }
+        if (contentValues.containsKey(KEY_PACKAGE_NAME)) {
+            builder.setPackageName(contentValues.getAsString(KEY_PACKAGE_NAME));
+        }
+        if (contentValues.containsKey(KEY_INSTANCE_ID)) {
+            builder.setInstanceId(
+                    contentValues.getAsInteger(KEY_INSTANCE_ID));
+        }
+        if (contentValues.containsKey(KEY_TASK_ROOT_PACKAGE_NAME)) {
+            builder.setTaskRootPackageName(contentValues.getAsString(KEY_TASK_ROOT_PACKAGE_NAME));
+        }
+        return builder.build();
+    }
+
+    /** Creates a new {@link Builder} instance. */
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /** A convenience builder class to improve readability. */
+    public static class Builder {
+        private long mUid;
+        private long mUserId;
+        private long mTimestamp;
+        private int mAppUsageEventType;
+        private String mPackageName;
+        private int mInstanceId;
+        private String mTaskRootPackageName;
+
+        /** Sets the uid. */
+        @CanIgnoreReturnValue
+        public Builder setUid(final long uid) {
+            this.mUid = uid;
+            return this;
+        }
+
+        /** Sets the user ID. */
+        @CanIgnoreReturnValue
+        public Builder setUserId(final long userId) {
+            this.mUserId = userId;
+            return this;
+        }
+
+        /** Sets the timestamp. */
+        @CanIgnoreReturnValue
+        public Builder setTimestamp(final long timestamp) {
+            this.mTimestamp = timestamp;
+            return this;
+        }
+
+        /** Sets the app usage event type. */
+        @CanIgnoreReturnValue
+        public Builder setAppUsageEventType(final int appUsageEventType) {
+            this.mAppUsageEventType = appUsageEventType;
+            return this;
+        }
+
+        /** Sets the package name. */
+        @CanIgnoreReturnValue
+        public Builder setPackageName(final String packageName) {
+            this.mPackageName = packageName;
+            return this;
+        }
+
+        /** Sets the instance ID. */
+        @CanIgnoreReturnValue
+        public Builder setInstanceId(final int instanceId) {
+            this.mInstanceId = instanceId;
+            return this;
+        }
+
+        /** Sets the task root package name. */
+        @CanIgnoreReturnValue
+        public Builder setTaskRootPackageName(final String taskRootPackageName) {
+            this.mTaskRootPackageName = taskRootPackageName;
+            return this;
+        }
+
+        /** Builds the AppUsageEvent. */
+        public AppUsageEventEntity build() {
+            return new AppUsageEventEntity(
+                    mUid,
+                    mUserId,
+                    mTimestamp,
+                    mAppUsageEventType,
+                    mPackageName,
+                    mInstanceId,
+                    mTaskRootPackageName);
+        }
+
+        private Builder() {}
+    }
+
+
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryState.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryState.java
index a50578b..9139c10 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryState.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryState.java
@@ -204,14 +204,14 @@
             return this;
         }
 
-        /** Sets the consumer type. */
+        /** Sets the battery information. */
         @CanIgnoreReturnValue
         public Builder setBatteryInformation(String batteryInformation) {
             this.mBatteryInformation = batteryInformation;
             return this;
         }
 
-        /** Sets the consumer type. */
+        /** Sets the battery information debug string. */
         @CanIgnoreReturnValue
         public Builder setBatteryInformationDebug(String batteryInformationDebug) {
             this.mBatteryInformationDebug = batteryInformationDebug;
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java
index 64c629c..4c8df7e 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java
@@ -25,7 +25,7 @@
 
 /** A {@link RoomDatabase} for battery usage states history. */
 @Database(
-        entities = {BatteryState.class},
+        entities = {BatteryState.class, AppUsageEventEntity.class},
         version = 1)
 public abstract class BatteryStateDatabase extends RoomDatabase {
     private static final String TAG = "BatteryStateDatabase";
@@ -34,13 +34,15 @@
 
     /** Provides DAO for battery state table. */
     public abstract BatteryStateDao batteryStateDao();
+    /** Provides DAO for app usage event table. */
+    public abstract AppUsageEventDao appUsageEventDao();
 
     /** Gets or creates an instance of {@link RoomDatabase}. */
     public static BatteryStateDatabase getInstance(Context context) {
         if (sBatteryStateDatabase == null) {
             sBatteryStateDatabase =
                     Room.databaseBuilder(
-                                    context, BatteryStateDatabase.class, "battery-usage-db-v6")
+                                    context, BatteryStateDatabase.class, "battery-usage-db-v7")
                             // Allows accessing data in the main thread for dumping bugreport.
                             .allowMainThreadQueries()
                             .fallbackToDestructiveMigration()
diff --git a/src/com/android/settings/fuelgauge/protos/Android.bp b/src/com/android/settings/fuelgauge/protos/Android.bp
index ab5e36b..2c63af4 100644
--- a/src/com/android/settings/fuelgauge/protos/Android.bp
+++ b/src/com/android/settings/fuelgauge/protos/Android.bp
@@ -13,4 +13,12 @@
         type: "lite",
     },
     srcs: ["fuelgauge_usage_state.proto"],
+}
+
+java_library {
+    name: "app-usage-event-protos-lite",
+    proto: {
+        type: "lite",
+    },
+    srcs: ["app_usage_event.proto"],
 }
\ No newline at end of file
diff --git a/src/com/android/settings/fuelgauge/protos/OWNERS b/src/com/android/settings/fuelgauge/protos/OWNERS
new file mode 100644
index 0000000..b861481c
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/protos/OWNERS
@@ -0,0 +1,3 @@
+clairewang@google.com
+xuezaiyue@google.com
+ykhung@google.com
\ No newline at end of file
diff --git a/src/com/android/settings/fuelgauge/protos/app_usage_event.proto b/src/com/android/settings/fuelgauge/protos/app_usage_event.proto
new file mode 100644
index 0000000..921fb0a
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/protos/app_usage_event.proto
@@ -0,0 +1,34 @@
+syntax = "proto2";
+
+option java_multiple_files = true;
+option java_package = "com.android.settings.fuelgauge.batteryusage";
+option java_outer_classname = "AppUsageEventProto";
+
+enum AppUsageEventType {
+  UNKNOWN = 0;
+  ACTIVITY_RESUMED = 1;
+  ACTIVITY_STOPPED = 2;
+  DEVICE_SHUTDOWN = 3;
+}
+
+message AppUsageEvent {
+  // Timestamp of the usage event.
+  optional int64 timestamp = 1;
+  // Type of the usage event.
+  optional AppUsageEventType type = 2;
+  // Package name of the app.
+  optional string package_name = 3;
+  // Instance ID for the activity. This is important for matching events of
+  // different event types for the same instance because an activity can be
+  // instantiated multiple times. Only available on Q builds after Dec 13 2018.
+  optional int32 instance_id = 4;
+  // Package name of the task root. For example, if a Twitter activity starts a
+  // Chrome activity within the same task, then while package_name is Chrome,
+  // task_root_package_name will be Twitter.
+  // Note: Activities that are task roots themselves (most activities) will have
+  // this field is populated as package_name.
+  // Note: The task root might be missing due to b/123404490.
+  optional string task_root_package_name = 5;
+  optional int64 user_id = 6;
+  optional int64 uid = 7;
+}
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index fc0c68e..7ca4de8 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -32,6 +32,8 @@
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.os.Bundle;
+import android.os.Process;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.text.TextUtils;
@@ -178,6 +180,7 @@
             if (userInfo.isManagedProfile()) {
                 final Intent intent = new Intent(getIntent())
                         .setClass(this, DeepLinkHomepageActivityInternal.class)
+                        .addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
                         .putExtra(EXTRA_USER_HANDLE, getUser());
                 intent.removeFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                 startActivityAsUser(intent, um.getPrimaryUser().getUserHandle());
@@ -465,20 +468,28 @@
             return;
         }
 
-        if (!TextUtils.equals(PasswordUtils.getCallingAppPackageName(getActivityToken()),
-                getPackageName())) {
-            ActivityInfo targetActivityInfo = null;
-            try {
-                targetActivityInfo = getPackageManager().getActivityInfo(targetComponentName,
-                        /* flags= */ 0);
-            } catch (PackageManager.NameNotFoundException e) {
-                Log.e(TAG, "Failed to get target ActivityInfo: " + e);
-                finish();
-                return;
-            }
+        ActivityInfo targetActivityInfo = null;
+        try {
+            targetActivityInfo = getPackageManager().getActivityInfo(targetComponentName,
+                    /* flags= */ 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Failed to get target ActivityInfo: " + e);
+            finish();
+            return;
+        }
 
+        int callingUid = -1;
+        try {
+            callingUid = ActivityManager.getService().getLaunchedFromUid(getActivityToken());
+        } catch (RemoteException re) {
+            Log.e(TAG, "Not able to get callingUid: " + re);
+            finish();
+            return;
+        }
+
+        if (!hasPrivilegedAccess(callingUid, targetActivityInfo)) {
             if (!targetActivityInfo.exported) {
-                Log.e(TAG, "Must not launch an unexported Actvity for deep link");
+                Log.e(TAG, "Target Activity is not exported");
                 finish();
                 return;
             }
@@ -490,12 +501,26 @@
             }
         }
 
+        // Only allow FLAG_GRANT_READ/WRITE_URI_PERMISSION if calling app has the permission to
+        // access specified Uri.
+        int uriPermissionFlags = targetIntent.getFlags()
+                & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        if (targetIntent.getData() != null
+                && uriPermissionFlags != 0
+                && checkUriPermission(targetIntent.getData(), /* pid= */ -1, callingUid,
+                        uriPermissionFlags) == PackageManager.PERMISSION_DENIED) {
+            Log.e(TAG, "Calling app must have the permission to access Uri and grant permission");
+            finish();
+            return;
+        }
+
         targetIntent.setComponent(targetComponentName);
 
         // To prevent launchDeepLinkIntentToRight again for configuration change.
         intent.setAction(null);
 
         targetIntent.removeFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
+        targetIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
 
         // Sender of intent may want to send intent extra data to the destination of targetIntent.
         targetIntent.replaceExtras(intent);
@@ -527,6 +552,38 @@
         }
     }
 
+    // Check if calling app has privileged access to launch Activity of activityInfo.
+    private boolean hasPrivilegedAccess(int callingUid, ActivityInfo activityInfo) {
+        if (TextUtils.equals(PasswordUtils.getCallingAppPackageName(getActivityToken()),
+                    getPackageName())) {
+            return true;
+        }
+
+        int targetUid = -1;
+        try {
+            targetUid = getPackageManager().getApplicationInfo(activityInfo.packageName,
+                    /* flags= */ 0).uid;
+        } catch (PackageManager.NameNotFoundException nnfe) {
+            Log.e(TAG, "Not able to get targetUid: " + nnfe);
+            return false;
+        }
+
+        // When activityInfo.exported is false, Activity still can be launched if applications have
+        // the same user ID.
+        if (UserHandle.isSameApp(callingUid, targetUid)) {
+            return true;
+        }
+
+        // When activityInfo.exported is false, Activity still can be launched if calling app has
+        // root or system privilege.
+        int callingAppId = UserHandle.getAppId(callingUid);
+        if (callingAppId == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID) {
+            return true;
+        }
+
+        return false;
+    }
+
     @VisibleForTesting
     boolean isCallingAppPermitted(String permission) {
         return TextUtils.isEmpty(permission) || PasswordUtils.isCallingAppPermitted(
diff --git a/src/com/android/settings/network/MobileNetworkIntentConverter.java b/src/com/android/settings/network/MobileNetworkIntentConverter.java
index 651215a..f500c52 100644
--- a/src/com/android/settings/network/MobileNetworkIntentConverter.java
+++ b/src/com/android/settings/network/MobileNetworkIntentConverter.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.network;
 
+import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
+
 import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
@@ -51,7 +53,7 @@
     private static final String TAG = "MobileNetworkIntentConverter";
 
     private static final ComponentName sTargetComponent = ComponentName
-            .createRelative("com.android.settings",
+            .createRelative(SETTINGS_PACKAGE_NAME,
                     MobileNetworkActivity.class.getTypeName());
     private static final String INTENT_TRAMPOLINE = "android.settings.SEARCH_RESULT_TRAMPOLINE";
     /**
diff --git a/src/com/android/settings/network/NetworkProviderCallsSmsFragment.java b/src/com/android/settings/network/NetworkProviderCallsSmsFragment.java
index ab80f6a..8d5048c 100644
--- a/src/com/android/settings/network/NetworkProviderCallsSmsFragment.java
+++ b/src/com/android/settings/network/NetworkProviderCallsSmsFragment.java
@@ -26,6 +26,7 @@
 import com.android.settings.Utils;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.network.telephony.CallsDefaultSubscriptionController;
+import com.android.settings.network.telephony.NetworkProviderBackupCallingPreferenceController;
 import com.android.settings.network.telephony.NetworkProviderWifiCallingPreferenceController;
 import com.android.settings.network.telephony.SmsDefaultSubscriptionController;
 import com.android.settings.search.BaseSearchIndexProvider;
@@ -41,6 +42,9 @@
     static final String LOG_TAG = "NetworkProviderCallsSmsFragment";
     @VisibleForTesting
     static final String KEY_PREFERENCE_CATEGORY_CALLING = "provider_model_calling_category";
+    @VisibleForTesting
+    static final String KEY_PREFERENCE_CATEGORY_BACKUP_CALLING =
+            "provider_model_backup_calling_category";
 
     @VisibleForTesting
     static final String KEY_PREFERENCE_CALLS= "provider_model_calls_preference";
@@ -63,6 +67,12 @@
         mNetworkProviderWifiCallingPreferenceController.init(getSettingsLifecycle());
         controllers.add(mNetworkProviderWifiCallingPreferenceController);
 
+        NetworkProviderBackupCallingPreferenceController backupCallingPrefCtrl =
+                new NetworkProviderBackupCallingPreferenceController(context,
+                        KEY_PREFERENCE_CATEGORY_BACKUP_CALLING);
+        backupCallingPrefCtrl.init(getSettingsLifecycle());
+        controllers.add(backupCallingPrefCtrl);
+
         return controllers;
     }
 
diff --git a/src/com/android/settings/network/telephony/BackupCallingPreferenceController.java b/src/com/android/settings/network/telephony/BackupCallingPreferenceController.java
index 3db6dfe..4f64399 100644
--- a/src/com/android/settings/network/telephony/BackupCallingPreferenceController.java
+++ b/src/com/android/settings/network/telephony/BackupCallingPreferenceController.java
@@ -38,7 +38,6 @@
 
 /**
  * Preference controller for "Backup Calling"
- * Deprecated ToDo: b/260059444 remove it next version
  **/
 public class BackupCallingPreferenceController extends TelephonyTogglePreferenceController {
 
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
index 8bc1224..cbd61b1 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
@@ -281,6 +281,11 @@
 
         final VideoCallingPreferenceController videoCallingPreferenceController =
                 use(VideoCallingPreferenceController.class).init(mSubId);
+        final BackupCallingPreferenceController crossSimCallingPreferenceController =
+                use(BackupCallingPreferenceController.class).init(mSubId);
+        use(CallingPreferenceCategoryController.class).setChildren(
+                Arrays.asList(wifiCallingPreferenceController, videoCallingPreferenceController,
+                        crossSimCallingPreferenceController));
         use(Enhanced4gLtePreferenceController.class).init(mSubId)
                 .addListener(videoCallingPreferenceController);
         use(Enhanced4gCallingPreferenceController.class).init(mSubId)
diff --git a/src/com/android/settings/network/telephony/NetworkProviderBackupCallingGroup.java b/src/com/android/settings/network/telephony/NetworkProviderBackupCallingGroup.java
index 3659f65..58dd18f 100644
--- a/src/com/android/settings/network/telephony/NetworkProviderBackupCallingGroup.java
+++ b/src/com/android/settings/network/telephony/NetworkProviderBackupCallingGroup.java
@@ -49,7 +49,6 @@
 
 /**
  * Preference controller for "Backup Calling"
- * Deprecated ToDo: b/260059444 remove it next version
  **/
 public class NetworkProviderBackupCallingGroup extends
         TelephonyTogglePreferenceController implements LifecycleObserver,
diff --git a/src/com/android/settings/network/telephony/NetworkProviderBackupCallingPreferenceController.java b/src/com/android/settings/network/telephony/NetworkProviderBackupCallingPreferenceController.java
index c10bfb8..5c336ef 100644
--- a/src/com/android/settings/network/telephony/NetworkProviderBackupCallingPreferenceController.java
+++ b/src/com/android/settings/network/telephony/NetworkProviderBackupCallingPreferenceController.java
@@ -32,7 +32,6 @@
 
 /**
  * Preference controller for "Backup Calling" summary list
- * Deprecated ToDo: b/260059444 remove it next version
  */
 public class NetworkProviderBackupCallingPreferenceController extends
         BasePreferenceController implements LifecycleObserver {
diff --git a/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.java b/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.java
index a19702f..72d9e91 100644
--- a/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.java
+++ b/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.java
@@ -19,6 +19,8 @@
 import static androidx.lifecycle.Lifecycle.Event.ON_START;
 import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
 
+import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
+
 import android.app.ProgressDialog;
 import android.content.Context;
 import android.content.Intent;
@@ -170,8 +172,8 @@
         } else {
             if (mSwitchPreference != null) {
                 Intent intent = new Intent();
-                intent.setClassName("com.android.settings",
-                        "com.android.settings.Settings$NetworkSelectActivity");
+                intent.setClassName(SETTINGS_PACKAGE_NAME,
+                        SETTINGS_PACKAGE_NAME + ".Settings$NetworkSelectActivity");
                 intent.putExtra(Settings.EXTRA_SUB_ID, mSubId);
                 mSwitchPreference.setIntent(intent);
             }
diff --git a/src/com/android/settings/network/telephony/gsm/OpenNetworkSelectPagePreferenceController.java b/src/com/android/settings/network/telephony/gsm/OpenNetworkSelectPagePreferenceController.java
index 8462228..7bc0dc1 100644
--- a/src/com/android/settings/network/telephony/gsm/OpenNetworkSelectPagePreferenceController.java
+++ b/src/com/android/settings/network/telephony/gsm/OpenNetworkSelectPagePreferenceController.java
@@ -19,6 +19,8 @@
 import static androidx.lifecycle.Lifecycle.Event.ON_START;
 import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
 
+import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
+
 import android.content.Context;
 import android.content.Intent;
 import android.provider.Settings;
@@ -101,8 +103,8 @@
                 != TelephonyManager.NETWORK_SELECTION_MODE_AUTO);
 
         Intent intent = new Intent();
-        intent.setClassName("com.android.settings",
-                "com.android.settings.Settings$NetworkSelectActivity");
+        intent.setClassName(SETTINGS_PACKAGE_NAME,
+                SETTINGS_PACKAGE_NAME + ".Settings$NetworkSelectActivity");
         intent.putExtra(Settings.EXTRA_SUB_ID, mSubId);
         preference.setIntent(intent);
     }
diff --git a/src/com/android/settings/notification/zen/ZenRulePreference.java b/src/com/android/settings/notification/zen/ZenRulePreference.java
index 0b36700..db06003 100644
--- a/src/com/android/settings/notification/zen/ZenRulePreference.java
+++ b/src/com/android/settings/notification/zen/ZenRulePreference.java
@@ -22,8 +22,10 @@
 import android.content.Intent;
 import android.content.pm.ComponentInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeConfig.ScheduleInfo;
+import android.util.Log;
 
 import androidx.fragment.app.Fragment;
 import androidx.preference.Preference;
@@ -34,9 +36,11 @@
 import com.android.settingslib.PrimarySwitchPreference;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
+import java.util.List;
 import java.util.Map;
 
 public class ZenRulePreference extends PrimarySwitchPreference {
+    private static final String TAG = "ZenRulePreference";
     private static final ManagedServiceSettings.Config CONFIG =
             ZenModeAutomationSettings.getConditionProviderConfig();
     final String mId;
@@ -121,8 +125,14 @@
                 getSettingsActivity(mPm, rule, si);
         mIntent = AbstractZenModeAutomaticRulePreferenceController.getRuleIntent(action,
                 settingsActivity, mId);
-        if (mIntent.resolveActivity(mPm) == null) {
+        // If the intent's activity for this rule doesn't exist or resolve to anything, disable the
+        // preference and rule.
+        List<ResolveInfo> results = mPm.queryIntentActivities(
+                mIntent, PackageManager.ResolveInfoFlags.of(0));
+        if (mIntent.resolveActivity(mPm) == null || results.size() == 0) {
+            Log.w(TAG, "intent for zen rule invalid: " + mIntent);
             mIntent = null;
+            setEnabled(false);
         }
         setKey(mId);
     }
diff --git a/src/com/android/settings/notification/zen/ZenRuleSelectionDialog.java b/src/com/android/settings/notification/zen/ZenRuleSelectionDialog.java
index b159a01..71df014 100644
--- a/src/com/android/settings/notification/zen/ZenRuleSelectionDialog.java
+++ b/src/com/android/settings/notification/zen/ZenRuleSelectionDialog.java
@@ -21,9 +21,11 @@
 import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ComponentInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.os.Bundle;
@@ -45,6 +47,7 @@
 import java.lang.ref.WeakReference;
 import java.text.Collator;
 import java.util.Comparator;
+import java.util.List;
 import java.util.Set;
 import java.util.TreeSet;
 
@@ -113,6 +116,14 @@
         }
     }
 
+    // Returns whether the rule's configuration activity exists and is valid.
+    private boolean isRuleActivityValid(final ZenRuleInfo ri) {
+        Intent intent = new Intent().setComponent(ri.configurationActivity);
+        List<ResolveInfo> results = mPm.queryIntentActivities(
+                intent, PackageManager.ResolveInfoFlags.of(0));
+        return intent.resolveActivity(mPm) != null && results.size() > 0;
+    }
+
     private void bindType(final ZenRuleInfo ri) {
         try {
             ApplicationInfo info = mPm.getApplicationInfo(ri.packageName, 0);
@@ -122,6 +133,11 @@
             ImageView iconView = v.findViewById(R.id.icon);
             ((TextView) v.findViewById(R.id.title)).setText(ri.title);
             if (!ri.isSystem) {
+                // Omit rule if the externally provided rule activity is not valid.
+                if (!isRuleActivityValid(ri)) {
+                    Log.w(TAG, "rule configuration activity invalid: " + ri.configurationActivity);
+                    return;
+                }
                 LoadIconTask task = new LoadIconTask(iconView);
                 task.execute(info);
 
diff --git a/src/com/android/settings/panel/PanelFragment.java b/src/com/android/settings/panel/PanelFragment.java
index 1ce4492..5bc0cc8 100644
--- a/src/com/android/settings/panel/PanelFragment.java
+++ b/src/com/android/settings/panel/PanelFragment.java
@@ -214,6 +214,8 @@
         // Make the panel layout gone here, to avoid janky animation when updating from old panel.
         // We will make it visible once the panel is ready to load.
         mPanelSlices.setVisibility(View.GONE);
+        // Remove the animator to avoid a RecyclerView crash.
+        mPanelSlices.setItemAnimator(null);
 
         final Bundle arguments = getArguments();
         final String callingPackageName =
diff --git a/src/com/android/settings/search/SearchResultTrampoline.java b/src/com/android/settings/search/SearchResultTrampoline.java
index 6e7d911..bcd8ee0 100644
--- a/src/com/android/settings/search/SearchResultTrampoline.java
+++ b/src/com/android/settings/search/SearchResultTrampoline.java
@@ -100,6 +100,8 @@
             }
         }
 
+        intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+
         if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this)) {
             startActivity(intent);
         } else if (isSettingsIntelligence(callingActivity)) {
diff --git a/src/com/android/settings/security/MemtagPagePreferenceController.java b/src/com/android/settings/security/MemtagPagePreferenceController.java
index 90e765b..f759e0d 100644
--- a/src/com/android/settings/security/MemtagPagePreferenceController.java
+++ b/src/com/android/settings/security/MemtagPagePreferenceController.java
@@ -16,12 +16,16 @@
 
 package com.android.settings.security;
 
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
 import android.content.Context;
 
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.RestrictedLockUtilsInternal;
+import com.android.settingslib.RestrictedPreference;
 
 public class MemtagPagePreferenceController extends BasePreferenceController {
     static final String KEY_MEMTAG = "memtag_page";
@@ -39,6 +43,10 @@
     public void displayPreference(PreferenceScreen screen) {
         super.displayPreference(screen);
         Preference preference = screen.findPreference(getPreferenceKey());
+        EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfMteIsDisabled(mContext);
+        if (admin != null) {
+            ((RestrictedPreference) preference).setDisabledByAdmin(admin);
+        }
         refreshSummary(preference);
     }
 
diff --git a/src/com/android/settings/security/MemtagPreferenceController.java b/src/com/android/settings/security/MemtagPreferenceController.java
index 290e40c..b5a4be7 100644
--- a/src/com/android/settings/security/MemtagPreferenceController.java
+++ b/src/com/android/settings/security/MemtagPreferenceController.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.security;
 
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
 import android.content.Context;
 
 import androidx.fragment.app.Fragment;
@@ -24,6 +26,8 @@
 
 import com.android.settings.R;
 import com.android.settings.core.TogglePreferenceController;
+import com.android.settingslib.RestrictedLockUtilsInternal;
+import com.android.settingslib.RestrictedSwitchPreference;
 
 public class MemtagPreferenceController extends TogglePreferenceController {
     private Preference mPreference;
@@ -74,6 +78,12 @@
     @Override
     public void updateState(Preference preference) {
         super.updateState(preference);
+        EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfMteIsDisabled(mContext);
+        if (admin != null) {
+            // Make sure this is disabled even if the user directly goes to this
+            // page via the android.settings.ADVANCED_MEMORY_PROTECTION_SETTINGS intent.
+            ((RestrictedSwitchPreference) preference).setDisabledByAdmin(admin);
+        }
         refreshSummary(preference);
     }
 
diff --git a/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java b/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java
index f144c6b..af8b38c 100644
--- a/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java
+++ b/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java
@@ -37,9 +37,6 @@
 public class SimSlotChangeReceiver extends BroadcastReceiver {
     private static final String TAG = "SlotChangeReceiver";
 
-    private final SimSlotChangeHandler mSlotChangeHandler = SimSlotChangeHandler.get();
-    private final Object mLock = new Object();
-
     @Override
     public void onReceive(Context context, Intent intent) {
 
@@ -49,20 +46,17 @@
             return;
         }
 
-        final PendingResult pendingResult = goAsync();
-        ThreadUtils.postOnBackgroundThread(
-                () -> {
-                    synchronized (mLock) {
-                        if (shouldHandleSlotChange(context)) {
-                            mSlotChangeHandler.onSlotsStatusChange(context.getApplicationContext());
-                        }
-                    }
-                    ThreadUtils.postOnMainThread(pendingResult::finish);
-                });
+        SimSlotChangeService.scheduleSimSlotChange(context);
+    }
+
+    public static void runOnBackgroundThread(Context context) {
+        if (shouldHandleSlotChange(context)) {
+            SimSlotChangeHandler.get().onSlotsStatusChange(context.getApplicationContext());
+        }
     }
 
     // Checks whether the slot event should be handled.
-    private boolean shouldHandleSlotChange(Context context) {
+    private static boolean shouldHandleSlotChange(Context context) {
         if (!context.getResources().getBoolean(R.bool.config_handle_sim_slot_change)) {
             Log.i(TAG, "The flag is off. Ignore slot changes.");
             return false;
@@ -88,7 +82,7 @@
     }
 
     // Checks whether the SIM slot state is valid for slot change event.
-    private boolean isSimSlotStateValid(Context context) {
+    private static boolean isSimSlotStateValid(Context context) {
         final TelephonyManager telMgr = context.getSystemService(TelephonyManager.class);
         UiccSlotInfo[] slotInfos = telMgr.getUiccSlotsInfo();
         if (slotInfos == null) {
@@ -136,7 +130,8 @@
     }
 
     @Nullable
-    private UiccCardInfo findUiccCardInfoBySlot(TelephonyManager telMgr, int physicalSlotIndex) {
+    private static UiccCardInfo findUiccCardInfoBySlot(TelephonyManager telMgr,
+            int physicalSlotIndex) {
         List<UiccCardInfo> cardInfos = telMgr.getUiccCardsInfo();
         if (cardInfos == null) {
             return null;
diff --git a/src/com/android/settings/sim/receivers/SimSlotChangeService.java b/src/com/android/settings/sim/receivers/SimSlotChangeService.java
new file mode 100644
index 0000000..deaecaf
--- /dev/null
+++ b/src/com/android/settings/sim/receivers/SimSlotChangeService.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 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.settings.sim.receivers;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Log;
+
+import com.android.settings.R;
+import com.android.settingslib.utils.ThreadUtils;
+
+import java.util.concurrent.Phaser;
+
+/** A JobService work on SIM slot change. */
+public class SimSlotChangeService extends JobService {
+
+    private static final String TAG = "SimSlotChangeService";
+
+    /**
+     * Schedules a service to work on SIM slot change.
+     *
+     * @param context is the caller context.
+     */
+    public static void scheduleSimSlotChange(Context context) {
+        Context appContext = context.getApplicationContext();
+        JobScheduler jobScheduler = appContext.getSystemService(JobScheduler.class);
+        ComponentName component = new ComponentName(appContext, SimSlotChangeService.class);
+
+        jobScheduler.schedule(
+                new JobInfo.Builder(R.integer.sim_slot_changed, component).build());
+    }
+
+    @Override
+    public boolean onStartJob(JobParameters params) {
+
+        HandlerThread thread = new HandlerThread(TAG);
+        thread.start();
+        final Phaser blocker = new Phaser(1);
+        Handler handler = new Handler(thread.getLooper());
+        handler.post(() -> {
+            try {
+                SimSlotChangeReceiver.runOnBackgroundThread(this);
+            } catch (Throwable exception) {
+                Log.e(TAG, "Exception running job", exception);
+            }
+            blocker.arrive();
+        });
+        blocker.awaitAdvance(0);
+        thread.quit();
+        return false;
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters params) {
+        return false;
+    }
+}
diff --git a/src/com/android/settings/users/TimeoutToDockUserSettings.java b/src/com/android/settings/users/TimeoutToDockUserSettings.java
index bb61b67..60bbdbb 100644
--- a/src/com/android/settings/users/TimeoutToDockUserSettings.java
+++ b/src/com/android/settings/users/TimeoutToDockUserSettings.java
@@ -37,7 +37,8 @@
  */
 public class TimeoutToDockUserSettings extends RadioButtonPickerFragment {
     // Index of the default key of the timeout setting if it hasn't been changed by the user.
-    public static final int DEFAULT_TIMEOUT_SETTING_VALUE_INDEX = 0;
+    // Default to the smallest non-zero option (which is currently 1 minute).
+    public static final int DEFAULT_TIMEOUT_SETTING_VALUE_INDEX = 1;
 
     // Labels of the options, for example, "never", "after 5 minutes".
     private String[] mEntries;
diff --git a/tests/robotests/src/com/android/settings/core/instrumentation/ElapsedTimeUtilsTest.java b/tests/robotests/src/com/android/settings/core/instrumentation/ElapsedTimeUtilsTest.java
new file mode 100644
index 0000000..b334349
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/core/instrumentation/ElapsedTimeUtilsTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2022 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.settings.core.instrumentation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class ElapsedTimeUtilsTest {
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+    }
+
+    @Test
+    public void storeSuwFinishedTimestamp_firstTimeAccess_shouldStoreTimestamp() {
+        final long timestamp = 1000000L;
+
+        ElapsedTimeUtils.storeSuwFinishedTimestamp(mContext, timestamp);
+
+        final long result = ElapsedTimeUtils.getSuwFinishedTimestamp(mContext);
+        assertThat(result).isEqualTo(timestamp);
+    }
+
+    @Test
+    public void storeSuwFinishedTimestamp_NotFirstTimeAccess_shouldNotStoreTimestamp() {
+        final long timestamp = 1000000L;
+
+        ElapsedTimeUtils.storeSuwFinishedTimestamp(mContext, timestamp);
+        ElapsedTimeUtils.storeSuwFinishedTimestamp(mContext, 2000000L);
+
+        final long result = ElapsedTimeUtils.getSuwFinishedTimestamp(mContext);
+        assertThat(result).isEqualTo(timestamp);
+    }
+
+    @Test
+    public void getElapsedTime_valueInPrefs_positiveElapsedTime_returnElapsedTime() {
+        final long suwTime = 1000L;
+        final long actionTime = 2000L;
+        ElapsedTimeUtils.storeSuwFinishedTimestamp(mContext, suwTime);
+
+        final long result = ElapsedTimeUtils.getElapsedTime(actionTime);
+
+        assertThat(result).isEqualTo(actionTime - suwTime);
+    }
+
+    @Test
+    public void getElapsedTime_valueInPrefs_negativeElapsedTime_returnDefault() {
+        final long suwTime = 2000L;
+        final long actionTime = 1000L;
+        ElapsedTimeUtils.storeSuwFinishedTimestamp(mContext, suwTime);
+
+        final long result = ElapsedTimeUtils.getElapsedTime(actionTime);
+
+        assertThat(result).isEqualTo(ElapsedTimeUtils.DEFAULT_SETUP_TIME);
+    }
+
+    @Test
+    public void getElapsedTime_noValueInPrefs_returnDefault() {
+        final long actionTime = 1000L;
+
+        final long result = ElapsedTimeUtils.getElapsedTime(actionTime);
+
+        assertThat(result).isEqualTo(ElapsedTimeUtils.DEFAULT_SETUP_TIME);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragmentTest.java b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragmentTest.java
index b773657..c1268b6 100644
--- a/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragmentTest.java
@@ -56,7 +56,7 @@
     @Test
     public void getMetricsCategory_returnsAutoRotateSettings() {
         assertThat(mFragment.getMetricsCategory()).isEqualTo(
-                SettingsEnums.DISPLAY_AUTO_ROTATE_SETTINGS);
+                SettingsEnums.DISPLAY_DEVICE_STATE_AUTO_ROTATE_SETTINGS);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/AppUsageDataLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/AppUsageDataLoaderTest.java
new file mode 100644
index 0000000..4b250a3
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/AppUsageDataLoaderTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 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.settings.fuelgauge.batteryusage;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public final class AppUsageDataLoaderTest {
+    private Context mContext;
+    @Mock
+    private ContentResolver mMockContentResolver;
+    @Mock
+    private UserManager mUserManager;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        doReturn(mContext).when(mContext).getApplicationContext();
+        doReturn(mMockContentResolver).when(mContext).getContentResolver();
+        doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
+        doReturn(new Intent()).when(mContext).registerReceiver(any(), any());
+    }
+
+    @Test
+    public void loadAppUsageData_withData_insertFakeDataIntoProvider() {
+        final List<AppUsageEvent> AppUsageEventList = new ArrayList<>();
+        final AppUsageEvent appUsageEvent = AppUsageEvent.newBuilder().setUid(0).build();
+        AppUsageEventList.add(appUsageEvent);
+        AppUsageDataLoader.sFakeAppUsageEventsSupplier = () -> new HashMap<>();
+        AppUsageDataLoader.sFakeUsageEventsListSupplier = () -> AppUsageEventList;
+
+        AppUsageDataLoader.loadAppUsageData(mContext);
+
+        verify(mMockContentResolver).bulkInsert(any(), any());
+        verify(mMockContentResolver).notifyChange(any(), any());
+    }
+
+    @Test
+    public void loadAppUsageData_nullAppUsageEvents_notInsertDataIntoProvider() {
+        AppUsageDataLoader.sFakeAppUsageEventsSupplier = () -> null;
+
+        AppUsageDataLoader.loadAppUsageData(mContext);
+
+        verifyNoMoreInteractions(mMockContentResolver);
+    }
+
+    @Test
+    public void loadAppUsageData_nullUsageEventsList_notInsertDataIntoProvider() {
+        AppUsageDataLoader.sFakeAppUsageEventsSupplier = () -> new HashMap<>();
+        AppUsageDataLoader.sFakeUsageEventsListSupplier = () -> null;
+
+        AppUsageDataLoader.loadAppUsageData(mContext);
+
+        verifyNoMoreInteractions(mMockContentResolver);
+    }
+
+    @Test
+    public void loadAppUsageData_emptyUsageEventsList_notInsertDataIntoProvider() {
+        AppUsageDataLoader.sFakeAppUsageEventsSupplier = () -> new HashMap<>();
+        AppUsageDataLoader.sFakeUsageEventsListSupplier = () -> new ArrayList<>();
+
+        AppUsageDataLoader.loadAppUsageData(mContext);
+
+        verifyNoMoreInteractions(mMockContentResolver);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistEntryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistEntryTest.java
index 5c143b1..9667760 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistEntryTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistEntryTest.java
@@ -68,7 +68,7 @@
         when(mMockBatteryEntry.getConsumerType())
                 .thenReturn(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
         final ContentValues values =
-                ConvertUtils.convertToContentValues(
+                ConvertUtils.convertBatteryEntryToContentValues(
                         mMockBatteryEntry,
                         mBatteryUsageStats,
                         /*batteryLevel=*/ 12,
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProviderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProviderTest.java
index d578b89..b43727d 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProviderTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProviderTest.java
@@ -30,6 +30,7 @@
 
 import androidx.test.core.app.ApplicationProvider;
 
+import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
 import com.android.settings.fuelgauge.batteryusage.db.BatteryState;
 import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
 import com.android.settings.testutils.BatteryTestUtils;
@@ -52,6 +53,8 @@
     private static final String PACKAGE_NAME1 = "com.android.settings1";
     private static final String PACKAGE_NAME2 = "com.android.settings2";
     private static final String PACKAGE_NAME3 = "com.android.settings3";
+    private static final long USER_ID1 = 1;
+    private static final long USER_ID2 = 2;
 
     private Context mContext;
     private BatteryUsageContentProvider mProvider;
@@ -178,6 +181,37 @@
     }
 
     @Test
+    public void query_appUsageTimestamp_returnsExpectedResult() throws Exception {
+        mProvider.onCreate();
+        final long timestamp1 = System.currentTimeMillis();
+        final long timestamp2 = timestamp1 + 2;
+        final long timestamp3 = timestamp1 + 4;
+        // Inserts some valid testing data.
+        BatteryTestUtils.insertDataToAppUsageEventTable(
+                mContext, USER_ID1, timestamp1, PACKAGE_NAME1);
+        BatteryTestUtils.insertDataToAppUsageEventTable(
+                mContext, USER_ID2, timestamp2, PACKAGE_NAME2);
+        BatteryTestUtils.insertDataToAppUsageEventTable(
+                mContext, USER_ID1, timestamp3, PACKAGE_NAME3);
+
+        final Cursor cursor1 = getCursorOfLatestTimestamp(USER_ID1);
+        assertThat(cursor1.getCount()).isEqualTo(1);
+        cursor1.moveToFirst();
+        assertThat(cursor1.getLong(0)).isEqualTo(timestamp3);
+
+        final Cursor cursor2 = getCursorOfLatestTimestamp(USER_ID2);
+        assertThat(cursor2.getCount()).isEqualTo(1);
+        cursor2.moveToFirst();
+        assertThat(cursor2.getLong(0)).isEqualTo(timestamp2);
+
+        final long notExistingUserId = 3;
+        final Cursor cursor3 = getCursorOfLatestTimestamp(notExistingUserId);
+        assertThat(cursor3.getCount()).isEqualTo(1);
+        cursor3.moveToFirst();
+        assertThat(cursor3.getLong(0)).isEqualTo(0);
+    }
+
+    @Test
     public void insert_batteryState_returnsExpectedResult() {
         mProvider.onCreate();
         final DeviceBatteryState deviceBatteryState =
@@ -267,6 +301,34 @@
     }
 
     @Test
+    public void insert_appUsageEvent_returnsExpectedResult() {
+        mProvider.onCreate();
+        ContentValues values = new ContentValues();
+        values.put(AppUsageEventEntity.KEY_UID, 101L);
+        values.put(AppUsageEventEntity.KEY_USER_ID, 1001L);
+        values.put(AppUsageEventEntity.KEY_TIMESTAMP, 10001L);
+        values.put(AppUsageEventEntity.KEY_APP_USAGE_EVENT_TYPE, 1);
+        values.put(AppUsageEventEntity.KEY_PACKAGE_NAME, "com.android.settings1");
+        values.put(AppUsageEventEntity.KEY_INSTANCE_ID, 100001L);
+        values.put(AppUsageEventEntity.KEY_TASK_ROOT_PACKAGE_NAME, "com.android.settings2");
+
+        final Uri uri = mProvider.insert(DatabaseUtils.APP_USAGE_EVENT_URI, values);
+
+        assertThat(uri).isEqualTo(DatabaseUtils.APP_USAGE_EVENT_URI);
+        // Verifies the AppUsageEventEntity content.
+        final List<AppUsageEventEntity> entities =
+                BatteryStateDatabase.getInstance(mContext).appUsageEventDao().getAllAfter(0);
+        assertThat(entities).hasSize(1);
+        assertThat(entities.get(0).uid).isEqualTo(101L);
+        assertThat(entities.get(0).userId).isEqualTo(1001L);
+        assertThat(entities.get(0).timestamp).isEqualTo(10001L);
+        assertThat(entities.get(0).appUsageEventType).isEqualTo(1);
+        assertThat(entities.get(0).packageName).isEqualTo("com.android.settings1");
+        assertThat(entities.get(0).instanceId).isEqualTo(100001L);
+        assertThat(entities.get(0).taskRootPackageName).isEqualTo("com.android.settings2");
+    }
+
+    @Test
     public void delete_throwsUnsupportedOperationException() {
         assertThrows(
                 UnsupportedOperationException.class,
@@ -293,12 +355,12 @@
         mProvider.setClock(fakeClock);
         final long currentTimestamp = currentTime.toMillis();
         // Inserts some valid testing data.
-        BatteryTestUtils.insertDataToBatteryStateDatabase(
+        BatteryTestUtils.insertDataToBatteryStateTable(
                 mContext, currentTimestamp - 2, PACKAGE_NAME1,
                 /*isFullChargeStart=*/ true);
-        BatteryTestUtils.insertDataToBatteryStateDatabase(
+        BatteryTestUtils.insertDataToBatteryStateTable(
                 mContext, currentTimestamp - 1, PACKAGE_NAME2);
-        BatteryTestUtils.insertDataToBatteryStateDatabase(
+        BatteryTestUtils.insertDataToBatteryStateTable(
                 mContext, currentTimestamp, PACKAGE_NAME3);
 
         final Uri batteryStateQueryContentUri =
@@ -307,7 +369,7 @@
                         .authority(DatabaseUtils.AUTHORITY)
                         .appendPath(DatabaseUtils.BATTERY_STATE_TABLE)
                         .appendQueryParameter(
-                                BatteryUsageContentProvider.QUERY_KEY_TIMESTAMP, queryTimestamp)
+                                DatabaseUtils.QUERY_KEY_TIMESTAMP, queryTimestamp)
                         .build();
 
         final Cursor cursor =
@@ -320,4 +382,22 @@
 
         return cursor;
     }
+
+    private Cursor getCursorOfLatestTimestamp(final long userId) {
+        final Uri appUsageLatestTimestampQueryContentUri =
+                new Uri.Builder()
+                        .scheme(ContentResolver.SCHEME_CONTENT)
+                        .authority(DatabaseUtils.AUTHORITY)
+                        .appendPath(DatabaseUtils.APP_USAGE_LATEST_TIMESTAMP_PATH)
+                        .appendQueryParameter(
+                                DatabaseUtils.QUERY_KEY_USERID, Long.toString(userId))
+                        .build();
+
+        return mProvider.query(
+                        appUsageLatestTimestampQueryContentUri,
+                        /*strings=*/ null,
+                        /*s=*/ null,
+                        /*strings1=*/ null,
+                        /*s1=*/ null);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java
index b67066d..514ac63 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java
@@ -62,7 +62,7 @@
 
         // Inserts fake data into database for testing.
         final BatteryStateDatabase database = BatteryTestUtils.setUpBatteryStateDatabase(mContext);
-        BatteryTestUtils.insertDataToBatteryStateDatabase(
+        BatteryTestUtils.insertDataToBatteryStateTable(
                 mContext, Clock.systemUTC().millis(), "com.android.systemui");
         mDao = database.batteryStateDao();
     }
@@ -170,9 +170,9 @@
     private void insertExpiredData(int shiftDay) {
         final long expiredTimeInMs =
                 Clock.systemUTC().millis() - Duration.ofDays(shiftDay).toMillis();
-        BatteryTestUtils.insertDataToBatteryStateDatabase(
+        BatteryTestUtils.insertDataToBatteryStateTable(
                 mContext, expiredTimeInMs - 1, "com.android.systemui");
-        BatteryTestUtils.insertDataToBatteryStateDatabase(
+        BatteryTestUtils.insertDataToBatteryStateTable(
                 mContext, expiredTimeInMs, "com.android.systemui");
         // Ensures the testing environment is correct.
         assertThat(mDao.getAllAfter(0)).hasSize(3);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
index 978930a..42cd7ef 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
@@ -17,16 +17,23 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageEvents.Event;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.BatteryManager;
 import android.os.BatteryUsageStats;
 import android.os.LocaleList;
 import android.os.UserHandle;
 
+import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -44,6 +51,8 @@
     private Context mContext;
 
     @Mock
+    private PackageManager mMockPackageManager;
+    @Mock
     private BatteryUsageStats mBatteryUsageStats;
     @Mock
     private BatteryEntry mMockBatteryEntry;
@@ -52,10 +61,11 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = spy(RuntimeEnvironment.application);
+        when(mContext.getPackageManager()).thenReturn(mMockPackageManager);
     }
 
     @Test
-    public void convertToContentValues_returnsExpectedContentValues() {
+    public void convertBatteryEntryToContentValues_returnsExpectedContentValues() {
         final int expectedType = 3;
         when(mMockBatteryEntry.getUid()).thenReturn(1001);
         when(mMockBatteryEntry.getLabel()).thenReturn("Settings");
@@ -76,7 +86,7 @@
                 .thenReturn(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
 
         final ContentValues values =
-                ConvertUtils.convertToContentValues(
+                ConvertUtils.convertBatteryEntryToContentValues(
                         mMockBatteryEntry,
                         mBatteryUsageStats,
                         /*batteryLevel=*/ 12,
@@ -121,9 +131,9 @@
     }
 
     @Test
-    public void convertToContentValues_nullBatteryEntry_returnsExpectedContentValues() {
+    public void convertBatteryEntryToContentValues_nullBatteryEntry_returnsExpectedContentValues() {
         final ContentValues values =
-                ConvertUtils.convertToContentValues(
+                ConvertUtils.convertBatteryEntryToContentValues(
                         /*entry=*/ null,
                         /*batteryUsageStats=*/ null,
                         /*batteryLevel=*/ 12,
@@ -152,6 +162,31 @@
     }
 
     @Test
+    public void convertAppUsageEventToContentValues_returnsExpectedContentValues() {
+        final AppUsageEvent appUsageEvent =
+                AppUsageEvent.newBuilder()
+                        .setUid(101L)
+                        .setUserId(1001L)
+                        .setTimestamp(10001L)
+                        .setType(AppUsageEventType.ACTIVITY_RESUMED)
+                        .setPackageName("com.android.settings1")
+                        .setInstanceId(100001)
+                        .setTaskRootPackageName("com.android.settings2")
+                        .build();
+        final ContentValues values =
+                ConvertUtils.convertAppUsageEventToContentValues(appUsageEvent);
+        assertThat(values.getAsLong(AppUsageEventEntity.KEY_UID)).isEqualTo(101L);
+        assertThat(values.getAsLong(AppUsageEventEntity.KEY_USER_ID)).isEqualTo(1001L);
+        assertThat(values.getAsLong(AppUsageEventEntity.KEY_TIMESTAMP)).isEqualTo(10001L);
+        assertThat(values.getAsInteger(AppUsageEventEntity.KEY_APP_USAGE_EVENT_TYPE)).isEqualTo(1);
+        assertThat(values.getAsString(AppUsageEventEntity.KEY_PACKAGE_NAME))
+                .isEqualTo("com.android.settings1");
+        assertThat(values.getAsInteger(AppUsageEventEntity.KEY_INSTANCE_ID)).isEqualTo(100001);
+        assertThat(values.getAsString(AppUsageEventEntity.KEY_TASK_ROOT_PACKAGE_NAME))
+                .isEqualTo("com.android.settings2");
+    }
+
+    @Test
     public void convertToBatteryHistEntry_returnsExpectedResult() {
         final int expectedType = 3;
         when(mMockBatteryEntry.getUid()).thenReturn(1001);
@@ -230,6 +265,77 @@
     }
 
     @Test
+    public void convertToAppUsageEvent_returnsExpectedResult()
+            throws PackageManager.NameNotFoundException {
+        final Event event = new Event();
+        event.mEventType = UsageEvents.Event.ACTIVITY_RESUMED;
+        event.mPackage = "com.android.settings1";
+        event.mTimeStamp = 101L;
+        event.mInstanceId = 100001;
+        event.mTaskRootPackage = "com.android.settings2";
+        when(mMockPackageManager.getPackageUidAsUser(any(), anyInt())).thenReturn(1001);
+
+        final long userId = 2;
+        final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent(
+                mContext, event, userId);
+        assertThat(appUsageEvent.getTimestamp()).isEqualTo(101L);
+        assertThat(appUsageEvent.getType()).isEqualTo(AppUsageEventType.ACTIVITY_RESUMED);
+        assertThat(appUsageEvent.getPackageName()).isEqualTo("com.android.settings1");
+        assertThat(appUsageEvent.getInstanceId()).isEqualTo(100001);
+        assertThat(appUsageEvent.getTaskRootPackageName()).isEqualTo("com.android.settings2");
+        assertThat(appUsageEvent.getUid()).isEqualTo(1001L);
+        assertThat(appUsageEvent.getUserId()).isEqualTo(userId);
+    }
+
+    @Test
+    public void convertToAppUsageEvent_emptyInstanceIdAndRootName_returnsExpectedResult()
+            throws PackageManager.NameNotFoundException {
+        final Event event = new Event();
+        event.mEventType = UsageEvents.Event.DEVICE_SHUTDOWN;
+        event.mPackage = "com.android.settings1";
+        event.mTimeStamp = 101L;
+        when(mMockPackageManager.getPackageUidAsUser(any(), anyInt())).thenReturn(1001);
+
+        final long userId = 1;
+        final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent(
+                mContext, event, userId);
+        assertThat(appUsageEvent.getTimestamp()).isEqualTo(101L);
+        assertThat(appUsageEvent.getType()).isEqualTo(AppUsageEventType.DEVICE_SHUTDOWN);
+        assertThat(appUsageEvent.getPackageName()).isEqualTo("com.android.settings1");
+        assertThat(appUsageEvent.getInstanceId()).isEqualTo(0);
+        assertThat(appUsageEvent.getTaskRootPackageName()).isEqualTo("");
+        assertThat(appUsageEvent.getUid()).isEqualTo(1001L);
+        assertThat(appUsageEvent.getUserId()).isEqualTo(userId);
+    }
+
+    @Test
+    public void convertToAppUsageEvent_emptyPackageName_returnsNull() {
+        final Event event = new Event();
+        event.mPackage = null;
+
+        final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent(
+                mContext, event, /*userId=*/ 0);
+
+        assertThat(appUsageEvent).isNull();
+    }
+
+    @Test
+    public void convertToAppUsageEvent_failToGetUid_returnsNull()
+            throws PackageManager.NameNotFoundException  {
+        final Event event = new Event();
+        event.mEventType = UsageEvents.Event.DEVICE_SHUTDOWN;
+        event.mPackage = "com.android.settings1";
+        when(mMockPackageManager.getPackageUidAsUser(any(), anyInt()))
+                .thenThrow(new PackageManager.NameNotFoundException());
+
+        final long userId = 1;
+        final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent(
+                mContext, event, userId);
+
+        assertThat(appUsageEvent).isNull();
+    }
+
+    @Test
     public void getLocale_nullContext_returnDefaultLocale() {
         assertThat(ConvertUtils.getLocale(/*context=*/ null))
                 .isEqualTo(Locale.getDefault());
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
index 210a21b..ce958c9 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doReturn;
@@ -25,12 +26,19 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
+import android.app.usage.IUsageStatsManager;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageEvents.Event;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.UserInfo;
 import android.os.BatteryConsumer;
 import android.os.BatteryManager;
 import android.os.BatteryUsageStats;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.UserManager;
 import android.text.format.DateUtils;
 
 import com.android.settings.fuelgauge.BatteryUtils;
@@ -65,10 +73,13 @@
 
     @Mock private Intent mIntent;
     @Mock private BatteryUsageStats mBatteryUsageStats;
+    @Mock private UserManager mUserManager;
+    @Mock private IUsageStatsManager mUsageStatsManager;
     @Mock private BatteryEntry mMockBatteryEntry1;
     @Mock private BatteryEntry mMockBatteryEntry2;
     @Mock private BatteryEntry mMockBatteryEntry3;
     @Mock private BatteryEntry mMockBatteryEntry4;
+    @Mock private UsageEvents mUsageEvents1;
 
 
     @Before
@@ -80,9 +91,11 @@
         mFeatureFactory = FakeFeatureFactory.setupForTest();
         mPowerUsageFeatureProvider = mFeatureFactory.powerUsageFeatureProvider;
 
+        DataProcessor.sUsageStatsManager = mUsageStatsManager;
         doReturn(mIntent).when(mContext).registerReceiver(any(), any());
         doReturn(100).when(mIntent).getIntExtra(eq(BatteryManager.EXTRA_SCALE), anyInt());
         doReturn(66).when(mIntent).getIntExtra(eq(BatteryManager.EXTRA_LEVEL), anyInt());
+        doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
     }
 
     @Test
@@ -146,6 +159,86 @@
     }
 
     @Test
+    public void getAppUsageEvents_returnExpectedResult() throws RemoteException {
+        UserInfo userInfo = new UserInfo(/*id=*/ 0, "user_0", /*flags=*/ 0);
+        final List<UserInfo> userInfoList = new ArrayList<>();
+        userInfoList.add(userInfo);
+        doReturn(userInfoList).when(mUserManager).getAliveUsers();
+        doReturn(true).when(mUserManager).isUserUnlocked(userInfo.id);
+        doReturn(mUsageEvents1)
+                .when(mUsageStatsManager)
+                .queryEventsForUser(anyLong(), anyLong(), anyInt(), any());
+
+        final Map<Long, UsageEvents> resultMap = DataProcessor.getAppUsageEvents(mContext);
+
+        assertThat(resultMap.size()).isEqualTo(1);
+        assertThat(resultMap.get(Long.valueOf(userInfo.id))).isEqualTo(mUsageEvents1);
+    }
+
+    @Test
+    public void getAppUsageEvents_lockedUser_returnNull() throws RemoteException {
+        UserInfo userInfo = new UserInfo(/*id=*/ 0, "user_0", /*flags=*/ 0);
+        final List<UserInfo> userInfoList = new ArrayList<>();
+        userInfoList.add(userInfo);
+        doReturn(userInfoList).when(mUserManager).getAliveUsers();
+        // Test locked user.
+        doReturn(false).when(mUserManager).isUserUnlocked(userInfo.id);
+
+        final Map<Long, UsageEvents> resultMap = DataProcessor.getAppUsageEvents(mContext);
+
+        assertThat(resultMap).isNull();
+    }
+
+    @Test
+    public void getAppUsageEvents_nullUsageEvents_returnNull() throws RemoteException {
+        UserInfo userInfo = new UserInfo(/*id=*/ 0, "user_0", /*flags=*/ 0);
+        final List<UserInfo> userInfoList = new ArrayList<>();
+        userInfoList.add(userInfo);
+        doReturn(userInfoList).when(mUserManager).getAliveUsers();
+        doReturn(true).when(mUserManager).isUserUnlocked(userInfo.id);
+        doReturn(null)
+                .when(mUsageStatsManager).queryEventsForUser(anyLong(), anyLong(), anyInt(), any());
+
+        final Map<Long, UsageEvents> resultMap = DataProcessor.getAppUsageEvents(mContext);
+
+        assertThat(resultMap).isNull();
+    }
+
+    @Test public void generateAppUsageEventListFromUsageEvents_returnExpectedResult() {
+        Event event1 = getUsageEvent(Event.NOTIFICATION_INTERRUPTION, /*timestamp=*/ 1);
+        Event event2 = getUsageEvent(Event.ACTIVITY_RESUMED, /*timestamp=*/ 2);
+        Event event3 = getUsageEvent(Event.ACTIVITY_STOPPED, /*timestamp=*/ 3);
+        Event event4 = getUsageEvent(Event.DEVICE_SHUTDOWN, /*timestamp=*/ 4);
+        Event event5 = getUsageEvent(Event.ACTIVITY_RESUMED, /*timestamp=*/ 5);
+        event5.mPackage = null;
+        List<Event> events1 = new ArrayList<>();
+        events1.add(event1);
+        events1.add(event2);
+        List<Event> events2 = new ArrayList<>();
+        events2.add(event3);
+        events2.add(event4);
+        events2.add(event5);
+        final long userId1 = 101L;
+        final long userId2 = 102L;
+        final long userId3 = 103L;
+        final Map<Long, UsageEvents> appUsageEvents = new HashMap();
+        appUsageEvents.put(userId1, getUsageEvents(events1));
+        appUsageEvents.put(userId2, getUsageEvents(events2));
+        appUsageEvents.put(userId3, getUsageEvents(new ArrayList<>()));
+
+        final List<AppUsageEvent> appUsageEventList =
+                DataProcessor.generateAppUsageEventListFromUsageEvents(mContext, appUsageEvents);
+
+        assertThat(appUsageEventList.size()).isEqualTo(3);
+        assetAppUsageEvent(
+                appUsageEventList.get(0), AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 2);
+        assetAppUsageEvent(
+                appUsageEventList.get(1), AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 3);
+        assetAppUsageEvent(
+                appUsageEventList.get(2), AppUsageEventType.DEVICE_SHUTDOWN, /*timestamp=*/ 4);
+    }
+
+    @Test
     public void getHistoryMapWithExpectedTimestamps_emptyHistoryMap_returnEmptyMap() {
         assertThat(DataProcessor
                 .getHistoryMapWithExpectedTimestamps(mContext, new HashMap<>()))
@@ -1213,6 +1306,30 @@
         return new BatteryHistEntry(values);
     }
 
+    private UsageEvents getUsageEvents(final List<Event> events) {
+        UsageEvents usageEvents = new UsageEvents(events, new String[] {"package"});
+        Parcel parcel = Parcel.obtain();
+        parcel.setDataPosition(0);
+        usageEvents.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        return UsageEvents.CREATOR.createFromParcel(parcel);
+    }
+
+    private Event getUsageEvent(
+            final int eventType, final long timestamp) {
+        final Event event = new Event();
+        event.mEventType = eventType;
+        event.mPackage = "package";
+        event.mTimeStamp = timestamp;
+        return event;
+    }
+
+    private void assetAppUsageEvent(
+            final AppUsageEvent event, final AppUsageEventType eventType, final long timestamp) {
+        assertThat(event.getType()).isEqualTo(eventType);
+        assertThat(event.getTimestamp()).isEqualTo(timestamp);
+    }
+
     private static void verifyExpectedBatteryLevelData(
             final BatteryLevelData resultData,
             final List<Long> expectedDailyTimestamps,
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtilsTest.java
index 0c66267..a04baa0 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtilsTest.java
@@ -22,6 +22,7 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.content.ContentResolver;
 import android.content.ContentValues;
@@ -34,6 +35,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 
+import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
 import com.android.settings.testutils.BatteryTestUtils;
 
 import org.junit.Before;
@@ -94,6 +96,53 @@
     }
 
     @Test
+    public void sendAppUsageEventData_returnsExpectedList() {
+        // Configures the testing AppUsageEvent data.
+        final List<AppUsageEvent> appUsageEventList = new ArrayList<>();
+        final AppUsageEvent appUsageEvent1 =
+                AppUsageEvent.newBuilder()
+                        .setUid(101L)
+                        .setType(AppUsageEventType.ACTIVITY_RESUMED)
+                        .build();
+        final AppUsageEvent appUsageEvent2 =
+                AppUsageEvent.newBuilder()
+                        .setUid(1001L)
+                        .setType(AppUsageEventType.ACTIVITY_STOPPED)
+                        .build();
+        final AppUsageEvent appUsageEvent3 =
+                AppUsageEvent.newBuilder()
+                        .setType(AppUsageEventType.DEVICE_SHUTDOWN)
+                        .build();
+        appUsageEventList.add(appUsageEvent1);
+        appUsageEventList.add(appUsageEvent2);
+        appUsageEventList.add(appUsageEvent3);
+
+        final List<ContentValues> valuesList =
+                DatabaseUtils.sendAppUsageEventData(mContext, appUsageEventList);
+
+        assertThat(valuesList).hasSize(2);
+        assertThat(valuesList.get(0).getAsInteger(AppUsageEventEntity.KEY_APP_USAGE_EVENT_TYPE))
+                .isEqualTo(1);
+        assertThat(valuesList.get(1).getAsInteger(AppUsageEventEntity.KEY_APP_USAGE_EVENT_TYPE))
+                .isEqualTo(2);
+        // Verifies the inserted ContentValues into content provider.
+        final ContentValues[] valuesArray =
+                new ContentValues[] {valuesList.get(0), valuesList.get(1)};
+        verify(mMockContentResolver).bulkInsert(
+                DatabaseUtils.APP_USAGE_EVENT_URI, valuesArray);
+        verify(mMockContentResolver).notifyChange(
+                DatabaseUtils.APP_USAGE_EVENT_URI, /*observer=*/ null);
+    }
+
+    @Test
+    public void sendAppUsageEventData_emptyAppUsageEventList_notSend() {
+        final List<ContentValues> valuesList =
+                DatabaseUtils.sendAppUsageEventData(mContext, new ArrayList<>());
+        assertThat(valuesList).hasSize(0);
+        verifyNoMoreInteractions(mMockContentResolver);
+    }
+
+    @Test
     public void sendBatteryEntryData_nullBatteryIntent_returnsNullValue() {
         doReturn(null).when(mContext).registerReceiver(any(), any());
         assertThat(
@@ -123,8 +172,8 @@
 
         assertThat(valuesList).hasSize(2);
         // Verifies the ContentValues content.
-        verifyContentValues(0.5, valuesList.get(0));
-        verifyContentValues(0.0, valuesList.get(1));
+        verifyBatteryEntryContentValues(0.5, valuesList.get(0));
+        verifyBatteryEntryContentValues(0.0, valuesList.get(1));
         // Verifies the inserted ContentValues into content provider.
         final ContentValues[] valuesArray =
                 new ContentValues[] {valuesList.get(0), valuesList.get(1)};
@@ -146,7 +195,7 @@
                         /*isFullChargeStart=*/ false);
 
         assertThat(valuesList).hasSize(1);
-        verifyFakeContentValues(valuesList.get(0));
+        verifyFakeBatteryEntryContentValues(valuesList.get(0));
         // Verifies the inserted ContentValues into content provider.
         verify(mMockContentResolver).insert(any(), any());
         verify(mMockContentResolver).notifyChange(
@@ -165,7 +214,7 @@
                         /*isFullChargeStart=*/ false);
 
         assertThat(valuesList).hasSize(1);
-        verifyFakeContentValues(valuesList.get(0));
+        verifyFakeBatteryEntryContentValues(valuesList.get(0));
         // Verifies the inserted ContentValues into content provider.
         verify(mMockContentResolver).insert(any(), any());
         verify(mMockContentResolver).notifyChange(
@@ -184,7 +233,7 @@
                         /*isFullChargeStart=*/ false);
 
         assertThat(valuesList).hasSize(1);
-        verifyFakeContentValues(valuesList.get(0));
+        verifyFakeBatteryEntryContentValues(valuesList.get(0));
         // Verifies the inserted ContentValues into content provider.
         verify(mMockContentResolver).insert(any(), any());
         verify(mMockContentResolver).notifyChange(
@@ -192,13 +241,49 @@
     }
 
     @Test
+    public void getAppUsageStartTimestampOfUser_emptyCursorContent_returnEarliestTimestamp() {
+        final MatrixCursor cursor =
+                new MatrixCursor(new String[] {AppUsageEventEntity.KEY_TIMESTAMP});
+        DatabaseUtils.sFakeAppUsageLatestTimestampSupplier = () -> cursor;
+
+        final long earliestTimestamp = 10001L;
+        assertThat(DatabaseUtils.getAppUsageStartTimestampOfUser(
+                mContext, /*userId=*/ 0, earliestTimestamp)).isEqualTo(earliestTimestamp);
+    }
+
+    @Test
+    public void getAppUsageStartTimestampOfUser_nullCursor_returnEarliestTimestamp() {
+        DatabaseUtils.sFakeAppUsageLatestTimestampSupplier = () -> null;
+        final long earliestTimestamp = 10001L;
+        assertThat(DatabaseUtils.getAppUsageStartTimestampOfUser(
+                mContext, /*userId=*/ 0, earliestTimestamp)).isEqualTo(earliestTimestamp);
+    }
+
+    @Test
+    public void getAppUsageStartTimestampOfUser_returnExpectedResult() {
+        final long returnedTimestamp = 10001L;
+        final MatrixCursor cursor =
+                new MatrixCursor(new String[] {AppUsageEventEntity.KEY_TIMESTAMP});
+        // Adds fake data into the cursor.
+        cursor.addRow(new Object[] {returnedTimestamp});
+        DatabaseUtils.sFakeAppUsageLatestTimestampSupplier = () -> cursor;
+
+        final long earliestTimestamp1 = 1001L;
+        assertThat(DatabaseUtils.getAppUsageStartTimestampOfUser(
+                mContext, /*userId=*/ 0, earliestTimestamp1)).isEqualTo(returnedTimestamp);
+        final long earliestTimestamp2 = 100001L;
+        assertThat(DatabaseUtils.getAppUsageStartTimestampOfUser(
+                mContext, /*userId=*/ 0, earliestTimestamp2)).isEqualTo(earliestTimestamp2);
+    }
+
+    @Test
     public void getHistoryMapSinceLastFullCharge_emptyCursorContent_returnEmptyMap() {
         final MatrixCursor cursor = new MatrixCursor(
                 new String[] {
                         BatteryHistEntry.KEY_UID,
                         BatteryHistEntry.KEY_USER_ID,
                         BatteryHistEntry.KEY_TIMESTAMP});
-        doReturn(cursor).when(mMockContentResolver).query(any(), any(), any(), any());
+        DatabaseUtils.sFakeBatteryStateSupplier = () -> cursor;
 
         assertThat(DatabaseUtils.getHistoryMapSinceLastFullCharge(
                 mContext, /*calendar=*/ null)).isEmpty();
@@ -206,7 +291,7 @@
 
     @Test
     public void getHistoryMapSinceLastFullCharge_nullCursor_returnEmptyMap() {
-        doReturn(null).when(mMockContentResolver).query(any(), any(), any(), any());
+        DatabaseUtils.sFakeBatteryStateSupplier = () -> null;
         assertThat(DatabaseUtils.getHistoryMapSinceLastFullCharge(
                 mContext, /*calendar=*/ null)).isEmpty();
     }
@@ -216,7 +301,6 @@
         final Long timestamp1 = Long.valueOf(1001L);
         final Long timestamp2 = Long.valueOf(1002L);
         final MatrixCursor cursor = getMatrixCursor();
-        doReturn(cursor).when(mMockContentResolver).query(any(), any(), any(), any());
         // Adds fake data into the cursor.
         cursor.addRow(new Object[] {
                 "app name1", timestamp1, 1, ConvertUtils.CONSUMER_TYPE_UID_BATTERY});
@@ -226,6 +310,7 @@
                 "app name3", timestamp2, 3, ConvertUtils.CONSUMER_TYPE_UID_BATTERY});
         cursor.addRow(new Object[] {
                 "app name4", timestamp2, 4, ConvertUtils.CONSUMER_TYPE_UID_BATTERY});
+        DatabaseUtils.sFakeBatteryStateSupplier = () -> cursor;
 
         final Map<Long, Map<String, BatteryHistEntry>> batteryHistMap =
                 DatabaseUtils.getHistoryMapSinceLastFullCharge(
@@ -251,9 +336,7 @@
         doReturn(mMockContext).when(mContext).createPackageContextAsUser(
                 "com.fake.package", /*flags=*/ 0, UserHandle.OWNER);
         BatteryTestUtils.setWorkProfile(mContext);
-        doReturn(getMatrixCursor()).when(mMockContentResolver2)
-                .query(any(), any(), any(), any());
-        doReturn(null).when(mMockContentResolver).query(any(), any(), any(), any());
+        DatabaseUtils.sFakeBatteryStateSupplier = () -> getMatrixCursor();
 
         final Map<Long, Map<String, BatteryHistEntry>> batteryHistMap =
                 DatabaseUtils.getHistoryMapSinceLastFullCharge(
@@ -262,7 +345,8 @@
         assertThat(batteryHistMap).isEmpty();
     }
 
-    private static void verifyContentValues(double consumedPower, ContentValues values) {
+    private static void verifyBatteryEntryContentValues(
+            double consumedPower, ContentValues values) {
         final BatteryInformation batteryInformation =
                 ConvertUtils.getBatteryInformation(
                         values, BatteryHistEntry.KEY_BATTERY_INFORMATION);
@@ -275,7 +359,7 @@
                 .isEqualTo(BatteryManager.BATTERY_HEALTH_COLD);
     }
 
-    private static void verifyFakeContentValues(ContentValues values) {
+    private static void verifyFakeBatteryEntryContentValues(ContentValues values) {
         final BatteryInformation batteryInformation =
                 ConvertUtils.getBatteryInformation(
                         values, BatteryHistEntry.KEY_BATTERY_INFORMATION);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiverTest.java
index 3693209..c9a3e64 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiverTest.java
@@ -62,7 +62,7 @@
 
         // Inserts fake data into database for testing.
         final BatteryStateDatabase database = BatteryTestUtils.setUpBatteryStateDatabase(mContext);
-        BatteryTestUtils.insertDataToBatteryStateDatabase(
+        BatteryTestUtils.insertDataToBatteryStateTable(
                 mContext, Clock.systemUTC().millis(), "com.android.systemui");
         mDao = database.batteryStateDao();
     }
@@ -122,9 +122,9 @@
     private void insertExpiredData(int shiftDay) {
         final long expiredTimeInMs =
                 Clock.systemUTC().millis() - Duration.ofDays(shiftDay).toMillis();
-        BatteryTestUtils.insertDataToBatteryStateDatabase(
+        BatteryTestUtils.insertDataToBatteryStateTable(
                 mContext, expiredTimeInMs - 1, "com.android.systemui");
-        BatteryTestUtils.insertDataToBatteryStateDatabase(
+        BatteryTestUtils.insertDataToBatteryStateTable(
                 mContext, expiredTimeInMs, "com.android.systemui");
         // Ensures the testing environment is correct.
         assertThat(mDao.getAllAfter(0)).hasSize(3);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProviderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProviderTest.java
index 87e253f..bbbf45f 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProviderTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProviderTest.java
@@ -53,9 +53,9 @@
         mBugReportContentProvider.attachInfo(mContext, /*info=*/ null);
         // Inserts fake data into database for testing.
         BatteryTestUtils.setUpBatteryStateDatabase(mContext);
-        BatteryTestUtils.insertDataToBatteryStateDatabase(
+        BatteryTestUtils.insertDataToBatteryStateTable(
                 mContext, System.currentTimeMillis(), PACKAGE_NAME1);
-        BatteryTestUtils.insertDataToBatteryStateDatabase(
+        BatteryTestUtils.insertDataToBatteryStateTable(
                 mContext, System.currentTimeMillis(), PACKAGE_NAME2);
     }
 
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventDaoTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventDaoTest.java
new file mode 100644
index 0000000..ade585f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventDaoTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2022 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.settings.fuelgauge.batteryusage.db;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.database.Cursor;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.testutils.BatteryTestUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.List;
+
+/** Tests for {@link AppUsageEventDao}. */
+@RunWith(RobolectricTestRunner.class)
+public final class AppUsageEventDaoTest {
+    private static final long TIMESTAMP1 = System.currentTimeMillis();
+    private static final long TIMESTAMP2 = System.currentTimeMillis() + 2;
+    private static final long TIMESTAMP3 = System.currentTimeMillis() + 4;
+    private static final long USER_ID1 = 1;
+    private static final long USER_ID2 = 2;
+    private static final String PACKAGE_NAME1 = "com.android.apps.settings";
+    private static final String PACKAGE_NAME2 = "com.android.apps.calendar";
+    private static final String PACKAGE_NAME3 = "com.android.apps.gmail";
+
+    private Context mContext;
+    private BatteryStateDatabase mDatabase;
+    private AppUsageEventDao mAppUsageEventDao;
+
+    @Before
+    public void setUp() {
+        mContext = ApplicationProvider.getApplicationContext();
+        mDatabase = BatteryTestUtils.setUpBatteryStateDatabase(mContext);
+        mAppUsageEventDao = mDatabase.appUsageEventDao();
+        BatteryTestUtils.insertDataToAppUsageEventTable(
+                mContext, USER_ID1, TIMESTAMP3, PACKAGE_NAME3);
+        BatteryTestUtils.insertDataToAppUsageEventTable(
+                mContext, USER_ID2, TIMESTAMP2, PACKAGE_NAME2);
+        BatteryTestUtils.insertDataToAppUsageEventTable(
+                mContext, USER_ID1, TIMESTAMP1, PACKAGE_NAME1, /*multiple=*/ true);
+    }
+
+    @After
+    public void closeDb() {
+        mDatabase.close();
+        BatteryStateDatabase.setBatteryStateDatabase(/*database=*/ null);
+    }
+
+    @Test
+    public void appUsageEventDao_insertAll() throws Exception {
+        final List<AppUsageEventEntity> entities = mAppUsageEventDao.getAllAfter(TIMESTAMP1);
+        assertThat(entities).hasSize(2);
+        // Verifies the queried battery states.
+        assertAppUsageEvent(entities.get(0), TIMESTAMP3, PACKAGE_NAME3);
+        assertAppUsageEvent(entities.get(1), TIMESTAMP2, PACKAGE_NAME2);
+    }
+
+    @Test
+    public void appUsageEventDao_getLatestTimestampOfUser() throws Exception {
+        final Cursor cursor1 = mAppUsageEventDao.getLatestTimestampOfUser(USER_ID1);
+        assertThat(cursor1.getCount()).isEqualTo(1);
+        cursor1.moveToFirst();
+        assertThat(cursor1.getLong(0)).isEqualTo(TIMESTAMP3);
+
+        final Cursor cursor2 = mAppUsageEventDao.getLatestTimestampOfUser(USER_ID2);
+        assertThat(cursor2.getCount()).isEqualTo(1);
+        cursor2.moveToFirst();
+        assertThat(cursor2.getLong(0)).isEqualTo(TIMESTAMP2);
+
+        final long notExistingUserId = 3;
+        final Cursor cursor3 = mAppUsageEventDao.getLatestTimestampOfUser(notExistingUserId);
+        assertThat(cursor3.getCount()).isEqualTo(1);
+        cursor3.moveToFirst();
+        assertThat(cursor3.getLong(0)).isEqualTo(0);
+    }
+
+    @Test
+    public void appUsageEventDao_clearAllBefore() throws Exception {
+        mAppUsageEventDao.clearAllBefore(TIMESTAMP2);
+
+        final List<AppUsageEventEntity> entities = mAppUsageEventDao.getAllAfter(0);
+        assertThat(entities).hasSize(1);
+        // Verifies the queried battery state.
+        assertAppUsageEvent(entities.get(0), TIMESTAMP3, PACKAGE_NAME3);
+    }
+
+    @Test
+    public void appUsageEventDao_clearAll() throws Exception {
+        assertThat(mAppUsageEventDao.getAllAfter(0)).hasSize(3);
+        mAppUsageEventDao.clearAll();
+        assertThat(mAppUsageEventDao.getAllAfter(0)).isEmpty();
+    }
+
+    @Test
+    public void getInstance_createNewInstance() throws Exception {
+        BatteryStateDatabase.setBatteryStateDatabase(/*database=*/ null);
+        assertThat(BatteryStateDatabase.getInstance(mContext)).isNotNull();
+    }
+
+    private static void assertAppUsageEvent(
+            AppUsageEventEntity entity, long timestamp, String packageName) {
+        assertThat(entity.timestamp).isEqualTo(timestamp);
+        assertThat(entity.packageName).isEqualTo(packageName);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventEntityTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventEntityTest.java
new file mode 100644
index 0000000..3cbf845
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventEntityTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 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.settings.fuelgauge.batteryusage.db;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link AppUsageEventEntity}. */
+@RunWith(RobolectricTestRunner.class)
+public final class AppUsageEventEntityTest {
+    @Test
+    public void testBuilder_returnsExpectedResult() {
+        final long uid = 101L;
+        final long userId = 1001L;
+        final long timestamp = 10001L;
+        final int appUsageEventType = 1;
+        final String packageName = "com.android.settings1";
+        final int instanceId = 100001;
+        final String taskRootPackageName = "com.android.settings2";
+
+        AppUsageEventEntity entity = AppUsageEventEntity
+                .newBuilder()
+                .setUid(uid)
+                .setUserId(userId)
+                .setTimestamp(timestamp)
+                .setAppUsageEventType(appUsageEventType)
+                .setPackageName(packageName)
+                .setInstanceId(instanceId)
+                .setTaskRootPackageName(taskRootPackageName)
+                .build();
+
+        // Verifies the app relative information.
+        assertThat(entity.uid).isEqualTo(uid);
+        assertThat(entity.userId).isEqualTo(userId);
+        assertThat(entity.timestamp).isEqualTo(timestamp);
+        assertThat(entity.appUsageEventType).isEqualTo(appUsageEventType);
+        assertThat(entity.packageName).isEqualTo(packageName);
+        assertThat(entity.instanceId).isEqualTo(instanceId);
+        assertThat(entity.taskRootPackageName).isEqualTo(taskRootPackageName);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDaoTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDaoTest.java
index 3b887ad..57cf648 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDaoTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDaoTest.java
@@ -53,9 +53,9 @@
         mContext = ApplicationProvider.getApplicationContext();
         mDatabase = BatteryTestUtils.setUpBatteryStateDatabase(mContext);
         mBatteryStateDao = mDatabase.batteryStateDao();
-        BatteryTestUtils.insertDataToBatteryStateDatabase(mContext, TIMESTAMP3, PACKAGE_NAME3);
-        BatteryTestUtils.insertDataToBatteryStateDatabase(mContext, TIMESTAMP2, PACKAGE_NAME2);
-        BatteryTestUtils.insertDataToBatteryStateDatabase(
+        BatteryTestUtils.insertDataToBatteryStateTable(mContext, TIMESTAMP3, PACKAGE_NAME3);
+        BatteryTestUtils.insertDataToBatteryStateTable(mContext, TIMESTAMP2, PACKAGE_NAME2);
+        BatteryTestUtils.insertDataToBatteryStateTable(
                 mContext, TIMESTAMP1, PACKAGE_NAME1, /*multiple=*/ true,
                 /*isFullChargeStart=*/ true);
     }
@@ -102,9 +102,9 @@
     public void batteryStateDao_getCursorSinceLastFullCharge_noFullChargeData_returnSevenDaysData()
             throws Exception {
         mBatteryStateDao.clearAll();
-        BatteryTestUtils.insertDataToBatteryStateDatabase(mContext, TIMESTAMP3, PACKAGE_NAME3);
-        BatteryTestUtils.insertDataToBatteryStateDatabase(mContext, TIMESTAMP2, PACKAGE_NAME2);
-        BatteryTestUtils.insertDataToBatteryStateDatabase(mContext, TIMESTAMP1, PACKAGE_NAME1);
+        BatteryTestUtils.insertDataToBatteryStateTable(mContext, TIMESTAMP3, PACKAGE_NAME3);
+        BatteryTestUtils.insertDataToBatteryStateTable(mContext, TIMESTAMP2, PACKAGE_NAME2);
+        BatteryTestUtils.insertDataToBatteryStateTable(mContext, TIMESTAMP1, PACKAGE_NAME1);
         final Cursor cursor = mBatteryStateDao.getCursorSinceLastFullCharge(TIMESTAMP2);
         assertThat(cursor.getCount()).isEqualTo(2);
         assertThat(cursor.getColumnCount()).isEqualTo(CURSOR_COLUMN_SIZE);
diff --git a/tests/robotests/src/com/android/settings/security/MemtagPagePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/security/MemtagPagePreferenceControllerTest.java
new file mode 100644
index 0000000..6236361
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/security/MemtagPagePreferenceControllerTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 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.settings.security;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal;
+import com.android.settingslib.RestrictedPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowSystemProperties;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowRestrictedLockUtilsInternal.class})
+public class MemtagPagePreferenceControllerTest {
+    private final String mMemtagSupportedProperty = "ro.arm64.memtag.bootctl_supported";
+
+    private MemtagPagePreferenceController mController;
+    private Context mContext;
+
+    private static final String FRAGMENT_TAG = "memtag_page";
+
+    @Before
+    public void setUp() {
+        ShadowSystemProperties.override(mMemtagSupportedProperty, "true");
+
+        mContext = RuntimeEnvironment.application;
+        mController = new MemtagPagePreferenceController(mContext, FRAGMENT_TAG);
+    }
+
+    @Test
+    public void displayPreference_disabledByAdmin_disablesPreference() {
+        ShadowRestrictedLockUtilsInternal.setMteIsDisabled(true);
+        RestrictedPreference preference = new RestrictedPreference(mContext);
+        preference.setKey(mController.getPreferenceKey());
+        PreferenceScreen screen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
+        screen.addPreference(preference);
+
+        mController.displayPreference(screen);
+        assertThat(preference.isDisabledByAdmin()).isTrue();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/security/MemtagPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/security/MemtagPreferenceControllerTest.java
index 2f8f658..e3fc3cc 100644
--- a/tests/robotests/src/com/android/settings/security/MemtagPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/security/MemtagPreferenceControllerTest.java
@@ -32,6 +32,8 @@
 
 import com.android.settings.R;
 import com.android.settings.testutils.shadow.ShadowDeviceConfig;
+import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal;
+import com.android.settingslib.RestrictedSwitchPreference;
 import com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor;
 
 import org.junit.Before;
@@ -48,7 +50,8 @@
         shadows = {
             ZygoteShadow.class,
             ShadowDeviceConfig.class,
-            ShadowInteractionJankMonitor.class
+            ShadowInteractionJankMonitor.class,
+            ShadowRestrictedLockUtilsInternal.class
         })
 public class MemtagPreferenceControllerTest {
     private final String mMemtagSupportedProperty = "ro.arm64.memtag.bootctl_supported";
@@ -145,6 +148,14 @@
         onView(withText(R.string.memtag_reboot_title)).inRoot(isDialog()).check(doesNotExist());
     }
 
+    @Test
+    public void updateState_disabledByAdmin_disablesPreference() {
+        ShadowRestrictedLockUtilsInternal.setMteIsDisabled(true);
+        RestrictedSwitchPreference preference = new RestrictedSwitchPreference(mContext);
+        mController.updateState(preference);
+        assertThat(preference.isDisabledByAdmin()).isTrue();
+    }
+
     private static final class TestActivity extends FragmentActivity {
 
         private static final int CONTAINER_VIEW_ID = 1234;
diff --git a/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java b/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java
index f119d6e6..c7680b5 100644
--- a/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java
+++ b/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java
@@ -26,6 +26,8 @@
 import com.android.settings.fuelgauge.batteryusage.BatteryInformation;
 import com.android.settings.fuelgauge.batteryusage.ConvertUtils;
 import com.android.settings.fuelgauge.batteryusage.DeviceBatteryState;
+import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventDao;
+import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
 import com.android.settings.fuelgauge.batteryusage.db.BatteryState;
 import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDao;
 import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
@@ -70,21 +72,21 @@
     }
 
     /** Inserts a fake data into the database for testing. */
-    public static void insertDataToBatteryStateDatabase(
+    public static void insertDataToBatteryStateTable(
             Context context, long timestamp, String packageName) {
-        insertDataToBatteryStateDatabase(
+        insertDataToBatteryStateTable(
                 context, timestamp, packageName, /*multiple=*/ false, /*isFullChargeStart=*/ false);
     }
 
     /** Inserts a fake data into the database for testing. */
-    public static void insertDataToBatteryStateDatabase(
+    public static void insertDataToBatteryStateTable(
             Context context, long timestamp, String packageName, boolean isFullChargeStart) {
-        insertDataToBatteryStateDatabase(
+        insertDataToBatteryStateTable(
                 context, timestamp, packageName, /*multiple=*/ false, isFullChargeStart);
     }
 
     /** Inserts a fake data into the database for testing. */
-    public static void insertDataToBatteryStateDatabase(
+    public static void insertDataToBatteryStateTable(
             Context context, long timestamp, String packageName, boolean multiple,
             boolean isFullChargeStart) {
         DeviceBatteryState deviceBatteryState =
@@ -133,6 +135,34 @@
         }
     }
 
+    /** Inserts a fake data into the database for testing. */
+    public static void insertDataToAppUsageEventTable(
+            Context context, long userId, long timestamp, String packageName) {
+        insertDataToAppUsageEventTable(
+                context, userId, timestamp, packageName, /*multiple=*/ false);
+    }
+
+    /** Inserts a fake data into the database for testing. */
+    public static void insertDataToAppUsageEventTable(
+            Context context, long userId, long timestamp, String packageName, boolean multiple) {
+        final AppUsageEventEntity entity =
+                new AppUsageEventEntity(
+                        /*uid=*/ 101L,
+                        userId,
+                        timestamp,
+                        /*appUsageEventType=*/ 2,
+                        packageName,
+                        /*instanceId=*/ 10001,
+                        /*taskRootPackageName=*/ "com.android.settings");
+        AppUsageEventDao dao =
+                BatteryStateDatabase.getInstance(context).appUsageEventDao();
+        if (multiple) {
+            dao.insertAll(ImmutableList.of(entity));
+        } else {
+            dao.insert(entity);
+        }
+    }
+
     public static Intent getCustomBatteryIntent(int plugged, int level, int scale, int status) {
         Intent intent = new Intent();
         intent.putExtra(BatteryManager.EXTRA_PLUGGED, plugged);
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtilsInternal.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtilsInternal.java
index e39056c..d66aa61 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtilsInternal.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtilsInternal.java
@@ -33,6 +33,7 @@
     private static boolean sIsRestricted;
     private static boolean sHasSystemFeature;
     private static boolean sMaximumTimeToLockIsSet;
+    private static boolean sMteOverridden;
     private static String[] sRestrictedPkgs;
     private static DevicePolicyManager sDevicePolicyManager;
     private static String[] sDisabledTypes;
@@ -45,6 +46,7 @@
         sKeyguardDisabledFeatures = 0;
         sDisabledTypes = new String[0];
         sMaximumTimeToLockIsSet = false;
+        sMteOverridden = false;
     }
 
     @Implementation
@@ -101,6 +103,11 @@
         return sMaximumTimeToLockIsSet ? new EnforcedAdmin() : null;
     }
 
+    @Implementation
+    public static EnforcedAdmin checkIfMteIsDisabled(Context context) {
+        return sMteOverridden ? new EnforcedAdmin() : null;
+    }
+
     public static void setRestricted(boolean restricted) {
         sIsRestricted = restricted;
     }
@@ -132,4 +139,8 @@
     public static void setMaximumTimeToLockIsSet(boolean isSet) {
         sMaximumTimeToLockIsSet = isSet;
     }
+
+    public static void setMteIsDisabled(boolean isSet) {
+        sMteOverridden = isSet;
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/users/TimeoutToDockUserPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/users/TimeoutToDockUserPreferenceControllerTest.java
index 2c93e94..774117f 100644
--- a/tests/robotests/src/com/android/settings/users/TimeoutToDockUserPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/users/TimeoutToDockUserPreferenceControllerTest.java
@@ -124,7 +124,8 @@
         Settings.Secure.putStringForUser(mContext.getContentResolver(), TIMEOUT_TO_DOCK_USER,
                 null, UserHandle.myUserId());
 
-        assertThat(mController.getSummary().toString()).isEqualTo(mEntries[0]);
+        assertThat(mController.getSummary().toString()).isEqualTo(
+                mEntries[TimeoutToDockUserSettings.DEFAULT_TIMEOUT_SETTING_VALUE_INDEX]);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/users/TimeoutToDockUserSettingsTest.java b/tests/robotests/src/com/android/settings/users/TimeoutToDockUserSettingsTest.java
index 55b630d..6c95fce 100644
--- a/tests/robotests/src/com/android/settings/users/TimeoutToDockUserSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/users/TimeoutToDockUserSettingsTest.java
@@ -78,8 +78,9 @@
     }
 
     @Test
-    public void defaultKey_settingNotSet_shouldReturnFirstValueAsDefault() {
-        assertThat(mSettings.getDefaultKey()).isEqualTo(mValues[0]);
+    public void defaultKey_settingNotSet_shouldReturnSecondValueAsDefault() {
+        assertThat(mSettings.getDefaultKey()).isEqualTo(
+                mValues[TimeoutToDockUserSettings.DEFAULT_TIMEOUT_SETTING_VALUE_INDEX]);
     }
 
     @Test
@@ -95,4 +96,11 @@
         mSettings.setDefaultKey(expectedKey);
         assertThat(mSettings.getDefaultKey()).isEqualTo(expectedKey);
     }
+
+    @Test
+    public void defaultKey_setToThirdValue_shouldSaveToSettings() {
+        final String expectedKey = mValues[2];
+        mSettings.setDefaultKey(expectedKey);
+        assertThat(mSettings.getDefaultKey()).isEqualTo(expectedKey);
+    }
 }
diff --git a/tests/unit/src/com/android/settings/network/telephony/BackupCallingPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/BackupCallingPreferenceControllerTest.java
index 34d7c92..4e110f0 100644
--- a/tests/unit/src/com/android/settings/network/telephony/BackupCallingPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/network/telephony/BackupCallingPreferenceControllerTest.java
@@ -28,13 +28,9 @@
 import com.android.settings.core.BasePreferenceController;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-/**
- * Deprecated ToDo: b/260059444 remove it next version
- */
 @RunWith(AndroidJUnit4.class)
 public class BackupCallingPreferenceControllerTest {
     private static final int SUB_ID = 2;
@@ -51,7 +47,6 @@
     }
 
     @Test
-    @Ignore
     public void controller_isUnavailable() {
         assertThat(mController.getAvailabilityStatus())
                 .isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
diff --git a/tests/unit/src/com/android/settings/network/telephony/NetworkProviderBackupCallingGroupTest.java b/tests/unit/src/com/android/settings/network/telephony/NetworkProviderBackupCallingGroupTest.java
index 09f0200..a5717ef 100644
--- a/tests/unit/src/com/android/settings/network/telephony/NetworkProviderBackupCallingGroupTest.java
+++ b/tests/unit/src/com/android/settings/network/telephony/NetworkProviderBackupCallingGroupTest.java
@@ -40,7 +40,6 @@
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -48,9 +47,6 @@
 
 import java.util.List;
 
-/**
- * Deprecated ToDo: b/260059444 remove it next version
- */
 @RunWith(AndroidJUnit4.class)
 public class NetworkProviderBackupCallingGroupTest {
 
@@ -137,14 +133,12 @@
     }
 
     @Test
-    @Ignore
     public void shouldShowBackupCallingForSub_invalidSubId_returnFalse() {
         assertThat(mNetworkProviderBackupCallingGroup.hasBackupCallingFeature(
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID)).isEqualTo(false);
     }
 
     @Test
-    @Ignore
     public void shouldShowBackupCallingForSub_carrierConfigIsUnavailable_returnFalse() {
         mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL,
                 false);
@@ -154,7 +148,6 @@
     }
 
     @Test
-    @Ignore
     public void
     shouldShowBackupCallingForSub_crossSimDisabled_returnFalse() {
         doReturn(false).when(mNetworkProviderBackupCallingGroup).isCrossSimEnabledByPlatform(
@@ -165,7 +158,6 @@
     }
 
     @Test
-    @Ignore
     public void shouldBackupCallingForSub_crossSimEnabled_returnTrue() {
         doReturn(true).when(mNetworkProviderBackupCallingGroup).isCrossSimEnabledByPlatform(
                 mContext, SUB_ID_1);
diff --git a/tests/unit/src/com/android/settings/sim/receivers/SimSlotChangeServiceTest.java b/tests/unit/src/com/android/settings/sim/receivers/SimSlotChangeServiceTest.java
new file mode 100644
index 0000000..029e0d7
--- /dev/null
+++ b/tests/unit/src/com/android/settings/sim/receivers/SimSlotChangeServiceTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 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.settings.sim.receivers;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.app.job.JobScheduler;
+import android.content.Context;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class SimSlotChangeServiceTest {
+
+    @Mock
+    private JobScheduler mJobScheduler;
+
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        doReturn(mContext).when(mContext).getApplicationContext();
+        mockService(Context.JOB_SCHEDULER_SERVICE, JobScheduler.class, mJobScheduler);
+    }
+
+    @Test
+    public void scheduleSimSlotChange_addSchedule_whenInvoked() {
+        SimSlotChangeService.scheduleSimSlotChange(mContext);
+        verify(mJobScheduler).schedule(any());
+    }
+
+    private <T> void mockService(String serviceName, Class<T> serviceClass, T service) {
+        doReturn(serviceName).when(mContext).getSystemServiceName(serviceClass);
+        doReturn(service).when(mContext).getSystemService(serviceName);
+    }
+}