[automerger skipped] [automerged blank] Import translations. DO NOT MERGE ANYWHERE 2p: fcbebd4b59 am: edebb4bb6d -s ours am: 0c38f7ebcb -s ours

am skip reason: subject contains skip directive

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/modules/Permission/+/15809190

Change-Id: I19a8a66f7151b67630f42ca67b357384de553019
diff --git a/PermissionController/res/values/strings.xml b/PermissionController/res/values/strings.xml
index ba03cbf..7a08681 100644
--- a/PermissionController/res/values/strings.xml
+++ b/PermissionController/res/values/strings.xml
@@ -136,6 +136,9 @@
     <!-- Label when there are no unused apps [CHAR LIMIT=30] -->
     <string name="no_unused_apps">No unused apps</string>
 
+    <!-- Label when there are zero unused apps [CHAR LIMIT=30] -->
+    <string name="zero_unused_apps">0 unused apps</string>
+
     <!-- [CHAR LIMIT=30] Manage applications, label for option to disable app -->
     <string name="app_disable_dlg_positive">Disable app</string>
 
diff --git a/PermissionController/res/values/themes.xml b/PermissionController/res/values/themes.xml
index 04265a3..16ff3c1 100644
--- a/PermissionController/res/values/themes.xml
+++ b/PermissionController/res/values/themes.xml
@@ -57,6 +57,10 @@
         <item name="android:windowIsTranslucent">true</item>
     </style>
 
+    <style name="GrantPermissions.Car">
+        <item name="carUiActivity">true</item>
+    </style>
+
     <!-- Unused since R but exposed as overlayable. -->
     <style name="Header.Settings"
             parent="@android:style/Theme.DeviceDefault.Settings">
@@ -118,6 +122,10 @@
         <item name="android:filterTouchesWhenObscured">true</item>
     </style>
 
+    <style name="GrantPermissions.Car.FilterTouches">
+        <item name="android:filterTouchesWhenObscured">true</item>
+    </style>
+
     <style name="RequestRole.FilterTouches">
         <item name="android:filterTouchesWhenObscured">true</item>
     </style>
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionUsages.java b/PermissionController/src/com/android/permissioncontroller/permission/model/PermissionUsages.java
similarity index 92%
rename from PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionUsages.java
rename to PermissionController/src/com/android/permissioncontroller/permission/model/PermissionUsages.java
index f83309f..b1dd404 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionUsages.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/PermissionUsages.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.permission.ui.handheld.dashboard;
+package com.android.permissioncontroller.permission.model;
 
 import static android.Manifest.permission.CAMERA;
 import static android.Manifest.permission.RECORD_AUDIO;
@@ -45,10 +45,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 
-import com.android.permissioncontroller.permission.model.AppPermissionGroup;
-import com.android.permissioncontroller.permission.model.AppPermissionUsage;
 import com.android.permissioncontroller.permission.model.AppPermissionUsage.Builder;
-import com.android.permissioncontroller.permission.model.Permission;
 import com.android.permissioncontroller.permission.model.legacy.PermissionApps.PermissionApp;
 import com.android.permissioncontroller.permission.model.legacy.PermissionGroup;
 import com.android.permissioncontroller.permission.model.legacy.PermissionGroups;
@@ -92,14 +89,27 @@
 
     private @Nullable PermissionsUsagesChangeCallback mCallback;
 
+    /**
+     * Callback for when the permission usages has loaded or changed.
+     */
     public interface PermissionsUsagesChangeCallback {
+        /**
+         * Called when the permission usages have loaded or changed.
+         */
         void onPermissionUsagesChanged();
     }
 
+    /**
+     * Creates a new instance of {@link PermissionUsages}.
+     */
     public PermissionUsages(@NonNull Context context) {
         mContext = context;
     }
 
+    /**
+     * Start the {@link Loader} to load the permission usages in the background. Loads without a uid
+     * filter.
+     */
     public void load(@Nullable String filterPackageName,
             @Nullable String[] filterPermissionGroups, long filterBeginTimeMillis,
             long filterEndTimeMillis, int usageFlags, @NonNull LoaderManager loaderManager,
@@ -110,6 +120,10 @@
                 getNonPlatformPermissions, callback, sync);
     }
 
+    /**
+     * Start the {@link Loader} to load the permission usages in the background. Loads only
+     * permissions for the specified {@code filterUid}.
+     */
     public void load(int filterUid, @Nullable String filterPackageName,
             @Nullable String[] filterPermissionGroups, long filterBeginTimeMillis,
             long filterEndTimeMillis, int usageFlags, @NonNull LoaderManager loaderManager,
@@ -155,36 +169,20 @@
         mCallback.onPermissionUsagesChanged();
     }
 
+    /**
+     * Return the usages that have already been loaded.
+     */
     public @NonNull List<AppPermissionUsage> getUsages() {
         return mUsages;
     }
 
+    /**
+     * Stop the {@link Loader} from loading the usages.
+     */
     public void stopLoader(@NonNull LoaderManager loaderManager) {
         loaderManager.destroyLoader(1);
     }
 
-    public static @Nullable AppPermissionUsage.GroupUsage loadLastGroupUsage(
-            @NonNull Context context, @NonNull AppPermissionGroup group) {
-        final ArraySet<String> opNames = new ArraySet<>();
-        final List<Permission> permissions = group.getPermissions();
-        final int permCount = permissions.size();
-        for (int i = 0; i < permCount; i++) {
-            final Permission permission = permissions.get(i);
-            final String opName = permission.getAppOp();
-            if (opName != null) {
-                opNames.add(opName);
-            }
-        }
-        final String[] opNamesArray = opNames.toArray(new String[opNames.size()]);
-        final List<PackageOps> usageOps = context.getSystemService(AppOpsManager.class)
-                .getOpsForPackage(group.getApp().applicationInfo.uid,
-                        group.getApp().packageName, opNamesArray);
-        if (usageOps == null || usageOps.isEmpty()) {
-            return null;
-        }
-        return new AppPermissionUsage.GroupUsage(group, usageOps.get(0), null);
-    }
-
     private static final class UsageLoader extends AsyncTaskLoader<List<AppPermissionUsage>> {
         private final int mFilterUid;
         private @Nullable String mFilterPackageName;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
index b96efef..812e4fe 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
@@ -127,6 +127,9 @@
 
     @Override
     public void onCreate(Bundle icicle) {
+        if (DeviceUtils.isAuto(this)) {
+            setTheme(R.style.GrantPermissions_Car_FilterTouches);
+        }
         super.onCreate(icicle);
 
         if (icicle == null) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java
index bf48e28..f33a1dd 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java
@@ -256,10 +256,10 @@
                 if (DeviceUtils.isAuto(this)) {
                     if (allPermissions) {
                         androidXFragment = AutoAllAppPermissionsFragment.newInstance(packageName,
-                                userHandle);
+                                userHandle, sessionId);
                     } else {
                         androidXFragment = AutoAppPermissionsFragment.newInstance(packageName,
-                                userHandle);
+                                userHandle, sessionId);
                     }
                 } else if (DeviceUtils.isWear(this)) {
                     androidXFragment = AppPermissionsFragmentWear.newInstance(packageName);
@@ -296,7 +296,8 @@
                     return;
                 }
                 if (DeviceUtils.isAuto(this)) {
-                    androidXFragment = AutoPermissionAppsFragment.newInstance(permissionName);
+                    androidXFragment =
+                            AutoPermissionAppsFragment.newInstance(permissionName, sessionId);
                 } else if (DeviceUtils.isTelevision(this)) {
                     androidXFragment = com.android.permissioncontroller.permission.ui.television
                             .PermissionAppsFragment.newInstance(permissionName);
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt
index c1d26dd..e1af9d6 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt
@@ -56,14 +56,13 @@
     where PF : PreferenceFragmentCompat, PF : UnusedAppsFragment.Parent<UnusedAppPref>,
           UnusedAppPref : Preference, UnusedAppPref : RemovablePref {
 
-    private val INFO_MSG_CATEGORY = "info_msg_category"
-
     private lateinit var viewModel: UnusedAppsViewModel
     private lateinit var collator: Collator
     private var sessionId: Long = 0L
     private var isFirstLoad = false
 
     companion object {
+        public const val INFO_MSG_CATEGORY = "info_msg_category"
         private const val SHOW_LOAD_DELAY_MS = 200L
         private const val INFO_MSG_KEY = "info_msg"
         private const val ELEVATION_HIGH = 8f
@@ -253,9 +252,7 @@
             }
         }
 
-        val infoMsgCategory =
-            preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)!!
-        infoMsgCategory.isVisible = !allCategoriesEmpty
+        preferenceFragment.setEmptyState(allCategoriesEmpty)
 
         if (isFirstLoad) {
             if (categorizedPackages[Months.SIX]!!.isNotEmpty() ||
@@ -377,5 +374,12 @@
             user: UserHandle,
             context: Context
         ): UnusedAppPref
+
+        /**
+         * Updates the state based on whether the content is empty.
+         *
+         * @param empty whether the content is empty
+         */
+        fun setEmptyState(empty: Boolean)
     }
 }
\ No newline at end of file
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAllAppPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAllAppPermissionsFragment.java
index f065f1c..e509d8a 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAllAppPermissionsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAllAppPermissionsFragment.java
@@ -16,6 +16,8 @@
 
 package com.android.permissioncontroller.permission.ui.auto;
 
+import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
+
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -62,18 +64,19 @@
 
     /** Creates an {@link AutoAllAppPermissionsFragment} with no filter. */
     public static AutoAllAppPermissionsFragment newInstance(@NonNull String packageName,
-            @NonNull UserHandle userHandle) {
-        return newInstance(packageName, /* filterGroup= */ null, userHandle);
+            @NonNull UserHandle userHandle, long sessionId) {
+        return newInstance(packageName, /* filterGroup= */ null, userHandle, sessionId);
     }
 
     /** Creates an {@link AutoAllAppPermissionsFragment} with a specific filter group. */
     public static AutoAllAppPermissionsFragment newInstance(@NonNull String packageName,
-            @NonNull String filterGroup, @NonNull UserHandle userHandle) {
+            @NonNull String filterGroup, @NonNull UserHandle userHandle, long sessionId) {
         AutoAllAppPermissionsFragment instance = new AutoAllAppPermissionsFragment();
         Bundle arguments = new Bundle();
         arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
         arguments.putString(Intent.EXTRA_PERMISSION_GROUP_NAME, filterGroup);
         arguments.putParcelable(Intent.EXTRA_USER, userHandle);
+        arguments.putLong(EXTRA_SESSION_ID, sessionId);
         instance.setArguments(arguments);
         return instance;
     }
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java
index 3181db4..2a74fd3 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java
@@ -16,6 +16,10 @@
 
 package com.android.permissioncontroller.permission.ui.auto;
 
+import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
+import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_VIEWED;
 import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_INTERACTED;
 import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_RESULT;
 
@@ -48,6 +52,7 @@
 import androidx.preference.TwoStatePreference;
 
 import com.android.car.ui.AlertDialogBuilder;
+import com.android.permissioncontroller.PermissionControllerStatsLog;
 import com.android.permissioncontroller.R;
 import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
 import com.android.permissioncontroller.permission.model.AppPermissionGroup;
@@ -60,7 +65,9 @@
 import com.android.settingslib.RestrictedLockUtils;
 
 import java.lang.annotation.Retention;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Random;
 
 /** Settings related to a particular permission for the given app. */
 public class AutoAppPermissionFragment extends AutoSettingsFrameFragment {
@@ -114,7 +121,8 @@
      */
     @NonNull
     public static AutoAppPermissionFragment newInstance(@NonNull String packageName,
-            @NonNull String permName, @Nullable String groupName, @NonNull UserHandle userHandle) {
+            @NonNull String permName, @Nullable String groupName, @NonNull UserHandle userHandle,
+            @NonNull long sessionId) {
         AutoAppPermissionFragment fragment = new AutoAppPermissionFragment();
         Bundle arguments = new Bundle();
         arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
@@ -124,6 +132,7 @@
             arguments.putString(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName);
         }
         arguments.putParcelable(Intent.EXTRA_USER, userHandle);
+        arguments.putLong(EXTRA_SESSION_ID, sessionId);
         fragment.setArguments(arguments);
         return fragment;
     }
@@ -143,6 +152,7 @@
 
         setHeaderLabel(
                 requireContext().getString(R.string.app_permission_title, mGroup.getFullLabel()));
+        logAppPermissionFragmentViewed();
     }
 
     private void setResult(@GrantPermissionsViewHandler.Result int result) {
@@ -265,6 +275,15 @@
         updateUi();
     }
 
+    void logAppPermissionFragmentViewed() {
+        long sessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID);
+        PermissionControllerStatsLog.write(APP_PERMISSION_FRAGMENT_VIEWED, sessionId,
+                mGroup.getApp().applicationInfo.uid, mGroup.getApp().packageName, mGroup.getName());
+        Log.v(LOG_TAG, "AutoAppPermission fragment viewed with sessionId=" + sessionId + " uid="
+                + mGroup.getApp().applicationInfo.uid + " packageName="
+                + mGroup.getApp().packageName + " permissionGroupName=" + mGroup.getName());
+    }
+
     @Override
     public void onStop() {
         super.onStop();
@@ -588,7 +607,8 @@
      */
     private void showAllPermissions(@NonNull String filterGroup) {
         Fragment frag = AutoAllAppPermissionsFragment.newInstance(mGroup.getApp().packageName,
-                filterGroup, UserHandle.getUserHandleForUid(mGroup.getApp().applicationInfo.uid));
+                filterGroup, UserHandle.getUserHandleForUid(mGroup.getApp().applicationInfo.uid),
+                        getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID));
         requireFragmentManager().beginTransaction()
                 .replace(android.R.id.content, frag)
                 .addToBackStack("AllPerms")
@@ -624,6 +644,8 @@
         }
 
         if (requestGrant) {
+            ArrayList<PermissionState> stateBefore = createPermissionSnapshot();
+
             if ((changeTarget & CHANGE_FOREGROUND) != 0) {
                 if (!mGroup.areRuntimePermissionsGranted()) {
                     SafetyNetLogger.logPermissionToggled(mGroup);
@@ -640,6 +662,7 @@
                     mGroup.getBackgroundPermissions().grantRuntimePermissions(true, false);
                 }
             }
+            logPermissionChanges(stateBefore);
         } else {
             boolean showDefaultDenyDialog = false;
 
@@ -664,6 +687,7 @@
                 updateUi();
                 return false;
             } else {
+                ArrayList<PermissionState> stateBefore = createPermissionSnapshot();
                 if ((changeTarget & CHANGE_FOREGROUND) != 0
                         && mGroup.areRuntimePermissionsGranted()) {
                     if (mGroup.areRuntimePermissionsGranted()) {
@@ -682,6 +706,7 @@
                         mGroup.getBackgroundPermissions().revokeRuntimePermissions(false);
                     }
                 }
+                logPermissionChanges(stateBefore);
             }
         }
 
@@ -737,6 +762,8 @@
      */
     private void onDenyAnyWay(@ChangeTarget int changeTarget) {
         boolean hasDefaultPermissions = false;
+        ArrayList<PermissionState> stateBefore = createPermissionSnapshot();
+
         if ((changeTarget & CHANGE_FOREGROUND) != 0) {
             if (mGroup.areRuntimePermissionsGranted()) {
                 SafetyNetLogger.logPermissionToggled(mGroup);
@@ -756,6 +783,7 @@
                         mGroup.getBackgroundPermissions().hasGrantedByDefaultPermission();
             }
         }
+        logPermissionChanges(stateBefore);
 
         if (hasDefaultPermissions || !mGroup.doesSupportRuntimePermissions()) {
             mHasConfirmedRevoke = true;
@@ -828,4 +856,81 @@
             }
         }
     }
+
+    private ArrayList<PermissionState> createPermissionSnapshot() {
+        ArrayList<PermissionState> permissionSnapshot = new ArrayList<>();
+        ArrayList<Permission> permissions = mGroup.getPermissions();
+        int numPermissions = permissions.size();
+
+        for (int i = 0; i < numPermissions; i++) {
+            Permission permission = permissions.get(i);
+            permissionSnapshot.add(new PermissionState(permission.getName(),
+                    permission.isGrantedIncludingAppOp()));
+        }
+
+        AppPermissionGroup permissionGroup = mGroup.getBackgroundPermissions();
+
+        if (permissionGroup == null) {
+            return permissionSnapshot;
+        }
+
+        permissions = mGroup.getBackgroundPermissions().getPermissions();
+        numPermissions = permissions.size();
+
+        for (int i = 0; i < numPermissions; i++) {
+            Permission permission = permissions.get(i);
+            permissionSnapshot.add(new PermissionState(permission.getName(),
+                    permission.isGrantedIncludingAppOp()));
+        }
+
+        return permissionSnapshot;
+    }
+
+    private void logPermissionChanges(ArrayList<PermissionState> previousPermissionSnapshot) {
+        long changeId = new Random().nextLong();
+        int numPermissions = previousPermissionSnapshot.size();
+        long sessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID);
+
+        for (int i = 0; i < numPermissions; i++) {
+            PermissionState permissionState = previousPermissionSnapshot.get(i);
+            boolean wasGranted = permissionState.permissionGranted;
+            Permission permission = mGroup.getPermission(permissionState.permissionName);
+
+            if (permission == null) {
+                if (mGroup.getBackgroundPermissions() == null) {
+                    continue;
+                }
+                permission = mGroup.getBackgroundPermissions().getPermission(
+                        permissionState.permissionName);
+            }
+
+            boolean isGranted = permission.isGrantedIncludingAppOp();
+
+            if (wasGranted != isGranted) {
+                logAppPermissionFragmentActionReported(sessionId, changeId,
+                        permissionState.permissionName, isGranted);
+            }
+        }
+    }
+
+    private void logAppPermissionFragmentActionReported(
+            long sessionId, long changeId, String permissionName, boolean isGranted) {
+        PermissionControllerStatsLog.write(APP_PERMISSION_FRAGMENT_ACTION_REPORTED, sessionId,
+                changeId, mGroup.getApp().applicationInfo.uid, mGroup.getApp().packageName,
+                permissionName, isGranted, /*flags=*/0, /*buttonPressed=*/0);
+        Log.v(LOG_TAG, "Permission changed via UI with sessionId=" + sessionId + " changeId="
+                + changeId + " uid=" + mGroup.getApp().applicationInfo.uid + " packageName="
+                + mGroup.getApp().packageName + " permission="
+                + permissionName + " isGranted=" + isGranted);
+    }
+
+    private static class PermissionState {
+        @NonNull public final String permissionName;
+        public final boolean permissionGranted;
+
+        PermissionState(@NonNull String permissionName, boolean permissionGranted) {
+            this.permissionName = permissionName;
+            this.permissionGranted = permissionGranted;
+        }
+    }
 }
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java
index fed489e..b26e30b 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java
@@ -17,6 +17,11 @@
 package com.android.permissioncontroller.permission.ui.auto;
 
 import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
+import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__ALLOWED;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__DENIED;
 import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_CALLER_NAME;
 
 import android.app.Activity;
@@ -27,9 +32,11 @@
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.util.Log;
+import android.view.View;
 import android.widget.Toast;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.fragment.app.Fragment;
 import androidx.lifecycle.ViewModelProvider;
 import androidx.preference.Preference;
@@ -37,6 +44,8 @@
 import androidx.preference.PreferenceGroup;
 import androidx.preference.PreferenceScreen;
 
+import com.android.car.ui.utils.ViewUtils;
+import com.android.permissioncontroller.PermissionControllerStatsLog;
 import com.android.permissioncontroller.R;
 import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
 import com.android.permissioncontroller.permission.model.AppPermissionGroup;
@@ -50,6 +59,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.Random;
 
 /** Screen to show the permissions for a specific application. */
 public class AutoAppPermissionsFragment extends AutoSettingsFrameFragment {
@@ -70,16 +80,17 @@
      * @return A new fragment
      */
     public static AutoAppPermissionsFragment newInstance(@NonNull String packageName,
-            @NonNull UserHandle userHandle) {
+            @NonNull UserHandle userHandle, long sessionId) {
         return setPackageNameAndUserHandle(new AutoAppPermissionsFragment(), packageName,
-                userHandle);
+                userHandle, sessionId);
     }
 
     private static <T extends Fragment> T setPackageNameAndUserHandle(@NonNull T fragment,
-            @NonNull String packageName, @NonNull UserHandle userHandle) {
+            @NonNull String packageName, @NonNull UserHandle userHandle, long sessionId) {
         Bundle arguments = new Bundle();
         arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
         arguments.putParcelable(Intent.EXTRA_USER, userHandle);
+        arguments.putLong(EXTRA_SESSION_ID, sessionId);
         fragment.setArguments(arguments);
         return fragment;
     }
@@ -128,7 +139,8 @@
     private void showAllPermissions() {
         Fragment frag = AutoAllAppPermissionsFragment.newInstance(
                 getArguments().getString(Intent.EXTRA_PACKAGE_NAME),
-                getArguments().getParcelable(Intent.EXTRA_USER));
+                getArguments().getParcelable(Intent.EXTRA_USER),
+                getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID));
         getFragmentManager().beginTransaction()
                 .replace(android.R.id.content, frag)
                 .addToBackStack("AllPerms")
@@ -229,7 +241,8 @@
                         frag = new AutoAppPermissionsFragment.AdditionalPermissionsFragment();
                 setPackageNameAndUserHandle(frag,
                         getArguments().getString(Intent.EXTRA_PACKAGE_NAME),
-                        getArguments().getParcelable(Intent.EXTRA_USER));
+                        getArguments().getParcelable(Intent.EXTRA_USER),
+                        getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID));
                 frag.setTargetFragment(AutoAppPermissionsFragment.this, 0);
                 getFragmentManager().beginTransaction()
                         .replace(android.R.id.content, frag)
@@ -260,6 +273,62 @@
         }
 
         setLoading(false);
+        logAppPermissionsFragmentView();
+    }
+
+    private void logAppPermissionsFragmentView() {
+        Context context = getPreferenceManager().getContext();
+        if (context == null) {
+            return;
+        }
+        String permissionSubtitleOnlyInForeground =
+                context.getString(R.string.permission_subtitle_only_in_foreground);
+
+
+        long sessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID);
+        long viewId = new Random().nextLong();
+
+        PreferenceCategory allowed = findPreference(KEY_ALLOWED_PERMISSIONS_GROUP);
+
+        int numAllowed = allowed.getPreferenceCount();
+        for (int i = 0; i < numAllowed; i++) {
+            Preference preference = allowed.getPreference(i);
+            if (preference.getSummary() == null) {
+                // R.string.no_permission_allowed was added to PreferenceCategory
+                continue;
+            }
+
+            int category = APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__ALLOWED;
+            if (permissionSubtitleOnlyInForeground.contentEquals(preference.getSummary())) {
+                category = APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND;
+            }
+
+            logAppPermissionsFragmentViewEntry(sessionId, viewId, preference.getKey(), category);
+        }
+
+        PreferenceCategory denied = findPreference(KEY_DENIED_PERMISSIONS_GROUP);
+
+        int numDenied = denied.getPreferenceCount();
+        for (int i = 0; i < numDenied; i++) {
+            Preference preference = denied.getPreference(i);
+            if (preference.getSummary() == null) {
+                // R.string.no_permission_denied was added to PreferenceCategory
+                continue;
+            }
+            logAppPermissionsFragmentViewEntry(sessionId, viewId, preference.getKey(),
+                    APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__DENIED);
+        }
+    }
+
+    private void logAppPermissionsFragmentViewEntry(
+            long sessionId, long viewId, String permissionGroupName, int category) {
+        PermissionControllerStatsLog.write(APP_PERMISSIONS_FRAGMENT_VIEWED, sessionId, viewId,
+                permissionGroupName, mAppPermissions.getPackageInfo().applicationInfo.uid,
+                mAppPermissions.getPackageInfo().packageName, category);
+        Log.v(LOG_TAG, "AutoAppPermissionFragment view logged with sessionId=" + sessionId
+                + " viewId=" + viewId + " permissionGroupName=" + permissionGroupName + " uid="
+                + mAppPermissions.getPackageInfo().applicationInfo.uid + " packageName="
+                + mAppPermissions.getPackageInfo().packageName + " category=" + category);
     }
 
     private Preference createPermissionPreference(Context context, AppPermissionGroup group) {
@@ -293,7 +362,7 @@
                 return getContext().getString(R.string.permission_subtitle_only_in_foreground);
             }
         }
-        return null;
+        return "";
     }
 
     /**
@@ -312,6 +381,16 @@
         }
 
         @Override
+        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+            super.onViewCreated(view, savedInstanceState);
+
+            // initially focus on focus parking view and then shift focus to recyclerview once it
+            // has loaded
+            ViewUtils.hideFocus(getListView().getRootView());
+            ViewUtils.initFocus((ViewUtils.LazyLayoutView) getListView());
+        }
+
+        @Override
         public void onCreatePreferences(Bundle bundle, String s) {
             setPreferenceScreen(mOuterFragment.mExtraScreen);
         }
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageCustomPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageCustomPermissionsFragment.java
deleted file mode 100644
index 0de51c8..0000000
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageCustomPermissionsFragment.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.permissioncontroller.permission.ui.auto;
-
-import com.android.permissioncontroller.R;
-
-/** Shows additional non-system permissions that can be granted/denied. */
-public class AutoManageCustomPermissionsFragment extends AutoManagePermissionsFragment {
-
-    @Override
-    protected int getScreenHeaderRes() {
-        return R.string.additional_permissions;
-    }
-
-    @Override
-    protected void updatePermissionsUi() {
-        updatePermissionsUi(/* addSystemPermissions= */ false);
-    }
-}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageOtherPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageOtherPermissionsFragment.java
new file mode 100644
index 0000000..e603399
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageOtherPermissionsFragment.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2021 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.permissioncontroller.permission.ui.auto;
+
+import android.app.Application;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.permissioncontroller.R;
+import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
+import com.android.permissioncontroller.permission.model.livedatatypes.PermGroupPackagesUiInfo;
+import com.android.permissioncontroller.permission.ui.model.ManagePermissionsViewModel;
+import com.android.permissioncontroller.permission.ui.model.PermissionGroupPreferenceUtils;
+
+import java.util.List;
+
+/**
+ * Shows additional non-system permissions that can be granted/denied.
+ *
+ * Largely based on
+ * {@link com.android.permissioncontroller.permission.ui.television.ManagePermissionsOtherFragment}.
+ */
+public class AutoManageOtherPermissionsFragment extends AutoSettingsFrameFragment {
+    private static final String KEY_UNUSED_CATEGORY = "category_unused";
+    private static final String KEY_ADDITIONAL_CATEGORY = "category_additional";
+
+    private PreferenceCategory mUnusedCategory;
+    private PreferenceCategory mAdditionalCategory;
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setLoading(true);
+        setHeaderLabel(getString(R.string.other_permissions_label));
+
+        final Application application = getActivity().getApplication();
+        final ViewModelProvider.Factory factory =
+                ViewModelProvider.AndroidViewModelFactory.getInstance(application);
+        final ManagePermissionsViewModel viewModel = new ViewModelProvider(this, factory)
+                .get(ManagePermissionsViewModel.class);
+
+        viewModel.getUnusedPermissionGroups().observe(this, this::onUnusedPermissionGroupsChanged);
+        viewModel.getAdditionalPermissionGroups().observe(this,
+                this::onAdditionalPermissionGroupsChanged);
+        viewModel.hasUnusedOrAdditionalPermissionGroups().observe(this,
+                this::onHasUnusedOrAdditionalPermissionGroups);
+    }
+
+    @Override
+    public void onCreatePreferences(Bundle bundle, String s) {
+        setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext()));
+    }
+
+    @Override
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+
+        final Context context = getPreferenceManager().getContext();
+        final PreferenceScreen screen = getPreferenceScreen();
+        // We add categories here, but make them invisible until the data is loaded.
+        mUnusedCategory = new PreferenceCategory(context);
+        mUnusedCategory.setKey(KEY_UNUSED_CATEGORY);
+        mUnusedCategory.setTitle(R.string.not_used_permissions_label);
+        mUnusedCategory.setSummary(R.string.not_used_permissions_description);
+        mUnusedCategory.setVisible(false);
+        screen.addPreference(mUnusedCategory);
+
+        mAdditionalCategory = new PreferenceCategory(context);
+        mAdditionalCategory.setKey(KEY_ADDITIONAL_CATEGORY);
+        mAdditionalCategory.setTitle(R.string.additional_permissions_label);
+        mAdditionalCategory.setSummary(R.string.additional_permissions_description);
+        mAdditionalCategory.setVisible(false);
+        screen.addPreference(mAdditionalCategory);
+    }
+
+    private void onUnusedPermissionGroupsChanged(List<PermGroupPackagesUiInfo> permissionGroups) {
+        updateCategory(mUnusedCategory, permissionGroups);
+    }
+
+    private void onAdditionalPermissionGroupsChanged(
+            List<PermGroupPackagesUiInfo> permissionGroups) {
+        updateCategory(mAdditionalCategory, permissionGroups);
+    }
+
+    private void onHasUnusedOrAdditionalPermissionGroups(Boolean hasPGs) {
+        if (!hasPGs) {
+            // There are not more permissions on this screen - go back.
+            getParentFragmentManager().popBackStack();
+        }
+    }
+
+    private void updateCategory(PreferenceCategory category,
+            List<PermGroupPackagesUiInfo> permissionGroups) {
+        final Context context = getPreferenceManager().getContext();
+        if (context == null) {
+            return;
+        }
+
+        PermissionGroupPreferenceUtils.updateGroupOfPermissionPreferences(context, category,
+                permissionGroups);
+        // Only show the category if it's not empty.
+        category.setVisible(!permissionGroups.isEmpty());
+
+        setLoading(false);
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManagePermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManagePermissionsFragment.java
deleted file mode 100644
index 49ea5eb..0000000
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManagePermissionsFragment.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.permissioncontroller.permission.ui.auto;
-
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
-import androidx.preference.Preference;
-
-import com.android.permissioncontroller.R;
-import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
-import com.android.permissioncontroller.permission.model.legacy.PermissionGroup;
-import com.android.permissioncontroller.permission.model.legacy.PermissionGroups;
-import com.android.permissioncontroller.permission.utils.Utils;
-
-import java.text.Collator;
-import java.util.ArrayList;
-
-/** Base class to show the list of permissions that can be granted/denied. */
-abstract class AutoManagePermissionsFragment extends AutoSettingsFrameFragment implements
-        PermissionGroups.PermissionsGroupsChangeCallback, Preference.OnPreferenceClickListener {
-
-    private static final String LOG_TAG = "ManagePermissionsFragment";
-
-    static final String OS_PKG = "android";
-
-    private PermissionGroups mPermissions;
-
-    private Collator mCollator;
-
-    @Override
-    public void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setLoading(true);
-
-        mPermissions = new PermissionGroups(getContext(), requireActivity().getLoaderManager(),
-                /* callback= */ this, /* getAppUiInfo= */ false,
-                /* getNonPlatformPermissions= */ true);
-        mCollator = Collator.getInstance(
-                getContext().getResources().getConfiguration().getLocales().get(0));
-
-        setHeaderLabel(getString(getScreenHeaderRes()));
-    }
-
-    @Override
-    public void onCreatePreferences(Bundle bundle, String s) {
-        setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext()));
-    }
-
-    @Override
-    public boolean onPreferenceClick(Preference preference) {
-        String key = preference.getKey();
-
-        PermissionGroup group = mPermissions.getGroup(key);
-        if (group == null) {
-            return false;
-        }
-
-        Intent intent = new Intent(Intent.ACTION_MANAGE_PERMISSION_APPS)
-                .putExtra(Intent.EXTRA_PERMISSION_NAME, key);
-        try {
-            getActivity().startActivity(intent);
-        } catch (ActivityNotFoundException e) {
-            Log.w(LOG_TAG, "No app to handle " + intent);
-        }
-
-        return true;
-    }
-
-    /** Returns the header string resource. */
-    @StringRes
-    protected abstract int getScreenHeaderRes();
-
-    /** Returns the current permissions. */
-    protected PermissionGroups getPermissions() {
-        return mPermissions;
-    }
-
-    @Override
-    public void onPermissionGroupsChanged() {
-        updatePermissionsUi();
-    }
-
-    /** Update the preferences to show the new {@link #getPermissions() permissions}. */
-    protected abstract void updatePermissionsUi();
-
-    /**
-     * Add preferences for all permissions of a type to the preference screen.
-     */
-    protected void updatePermissionsUi(boolean addSystemPermissions) {
-        Context context = getPreferenceManager().getContext();
-        if (context == null || getActivity() == null) {
-            return;
-        }
-
-        ArrayList<PermissionGroup> groups = new ArrayList<>(mPermissions.getGroups());
-        groups.sort((x, y) -> mCollator.compare(x.getLabel(), y.getLabel()));
-        getPreferenceScreen().removeAll();
-        getPreferenceScreen().setOrderingAsAdded(true);
-
-        // Use this to speed up getting the info for all of the PermissionApps below.
-        // Create a new one for each refresh to make sure it has fresh data.
-        for (int i = 0; i < groups.size(); i++) {
-            PermissionGroup group = groups.get(i);
-            boolean isSystemPermission = group.getDeclaringPackage().equals(OS_PKG);
-
-            if (addSystemPermissions == isSystemPermission) {
-                Preference preference = findPreference(group.getName());
-
-                if (preference == null) {
-                    preference = new Preference(context);
-                    preference.setOnPreferenceClickListener(this);
-                    preference.setKey(group.getName());
-                    preference.setIcon(Utils.applyTint(context, group.getIcon(),
-                            android.R.attr.colorControlNormal));
-                    preference.setTitle(group.getLabel());
-                    // Set blank summary so that no resizing/jumping happens when the summary is
-                    // loaded.
-                    preference.setSummary(" ");
-                    preference.setPersistent(false);
-                    getPreferenceScreen().addPreference(preference);
-                }
-                preference.setSummary(
-                        getString(R.string.app_permissions_group_summary, group.getGranted(),
-                                group.getTotal()));
-            }
-        }
-        if (getPreferenceScreen().getPreferenceCount() != 0) {
-            setLoading(false);
-        }
-    }
-}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageStandardPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageStandardPermissionsFragment.java
index c3d9ed2..8bbcde6 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageStandardPermissionsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageStandardPermissionsFragment.java
@@ -16,31 +16,43 @@
 
 package com.android.permissioncontroller.permission.ui.auto;
 
+
 import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
 import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
 
 import android.app.Application;
+import android.content.Context;
 import android.os.Bundle;
 
 import androidx.annotation.Nullable;
 import androidx.fragment.app.FragmentTransaction;
 import androidx.lifecycle.ViewModelProvider;
 import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
 
 import com.android.permissioncontroller.R;
-import com.android.permissioncontroller.permission.model.legacy.PermissionGroup;
+import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
+import com.android.permissioncontroller.permission.model.livedatatypes.PermGroupPackagesUiInfo;
 import com.android.permissioncontroller.permission.ui.UnusedAppsFragment;
+import com.android.permissioncontroller.permission.ui.model.ManagePermissionsViewModel;
 import com.android.permissioncontroller.permission.ui.model.ManageStandardPermissionsViewModel;
+import com.android.permissioncontroller.permission.ui.model.PermissionGroupPreferenceUtils;
 import com.android.permissioncontroller.permission.utils.Utils;
 
 import java.util.List;
 
-/** Shows the standard permissions that can be granted/denied. */
-public class AutoManageStandardPermissionsFragment extends AutoManagePermissionsFragment {
+/**
+ * Shows the standard permissions that can be granted/denied.
+ *
+ * Largely based on the implementation of
+ * {@link com.android.permissioncontroller.permission.ui.television.ManagePermissionsFragment}.
+ */
+public class AutoManageStandardPermissionsFragment extends AutoSettingsFrameFragment {
 
-    private static final String EXTRA_PREFS_KEY = "extra_prefs_key";
-    private static final String AUTO_REVOKE_KEY = "auto_revoke_key";
-    private ManageStandardPermissionsViewModel mViewModel;
+    private static final String KEY_OTHER_PERMISSIONS = "other_permissions";
+    private static final String KEY_AUTO_REVOKE = "auto_revoke_key";
+    private ManagePermissionsViewModel mManagePermissionsViewModel;
+    private ManageStandardPermissionsViewModel mManageStandardPermissionsViewModel;
 
     /** Returns a new instance of {@link AutoManageStandardPermissionsFragment}. */
     public static AutoManageStandardPermissionsFragment newInstance() {
@@ -50,84 +62,108 @@
     @Override
     public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        setLoading(true);
+        setHeaderLabel(getString(R.string.app_permission_manager));
 
         final Application application = getActivity().getApplication();
-        mViewModel = new ViewModelProvider(this,
+        final ViewModelProvider.Factory viewModelFactory =
+                ViewModelProvider.AndroidViewModelFactory.getInstance(application);
+        mManagePermissionsViewModel = new ViewModelProvider(this, viewModelFactory)
+                .get(ManagePermissionsViewModel.class);
+        mManageStandardPermissionsViewModel = new ViewModelProvider(this,
                 ViewModelProvider.AndroidViewModelFactory.getInstance(application))
                 .get(ManageStandardPermissionsViewModel.class);
-
-        mViewModel.getUiDataLiveData().observe(this, permissionGroups -> {
-            if (permissionGroups != null) {
-                updatePermissionsUi();
-            } else {
-                getActivity().finish();
-            }
-        });
-
-        mViewModel.getNumAutoRevoked().observe(this, show -> updatePermissionsUi());
+        mManagePermissionsViewModel.getUsedPermissionGroups().observe(this,
+                this::onPermissionGroupsChanged);
+        mManageStandardPermissionsViewModel.getNumAutoRevoked().observe(this,
+                this::onNumAutoRevokedChanged);
     }
 
     @Override
-    protected int getScreenHeaderRes() {
-        return R.string.app_permission_manager;
+    public void onCreatePreferences(Bundle bundle, String s) {
+        setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext()));
     }
 
-    @Override
-    protected void updatePermissionsUi() {
-        updatePermissionsUi(/* addSystemPermissions= */ true);
-
-        // Check if we need an additional permissions preference
-        List<PermissionGroup> groups = getPermissions().getGroups();
-        int numExtraPermissions = 0;
-        for (PermissionGroup group : groups) {
-            if (!group.getDeclaringPackage().equals(AutoManagePermissionsFragment.OS_PKG)) {
-                numExtraPermissions++;
-            }
+    private void onPermissionGroupsChanged(List<PermGroupPackagesUiInfo> permissionGroups) {
+        final Context context = getPreferenceManager().getContext();
+        if (context == null) {
+            return;
         }
 
-        Preference additionalPermissionsPreference = getPreferenceScreen().findPreference(
-                EXTRA_PREFS_KEY);
-        if (numExtraPermissions == 0) {
-            if (additionalPermissionsPreference != null) {
-                getPreferenceScreen().removePreference(additionalPermissionsPreference);
-            }
+        final PreferenceScreen screen = getPreferenceScreen();
+
+        // First check if "Other preferences" button exists. If it does: remove it, but hold on to
+        // the reference - we'll it add back later, after the preferences for the permission groups
+        // have been updated. If it does not exist: create and hold on to it.
+        Preference otherPermissionsPreference = screen.findPreference(KEY_OTHER_PERMISSIONS);
+        if (otherPermissionsPreference == null) {
+            otherPermissionsPreference = buildOtherPermissionsPreference(context);
         } else {
-            if (additionalPermissionsPreference == null) {
-                additionalPermissionsPreference = new Preference(
-                        getPreferenceManager().getContext());
-                additionalPermissionsPreference.setKey(EXTRA_PREFS_KEY);
-                additionalPermissionsPreference.setIcon(Utils.applyTint(getActivity(),
-                        R.drawable.ic_more_items,
-                        android.R.attr.colorControlNormal));
-                additionalPermissionsPreference.setTitle(R.string.additional_permissions);
-                additionalPermissionsPreference.setOnPreferenceClickListener(preference -> {
-                    AutoManageCustomPermissionsFragment frag =
-                            new AutoManageCustomPermissionsFragment();
-                    frag.setTargetFragment(AutoManageStandardPermissionsFragment.this,
-                            /* requestCode= */ 0);
-                    FragmentTransaction ft = getFragmentManager().beginTransaction();
-                    ft.replace(android.R.id.content, frag);
-                    ft.addToBackStack(null);
-                    ft.commit();
-                    return true;
-                });
-
-                getPreferenceScreen().addPreference(additionalPermissionsPreference);
-            }
-
-            additionalPermissionsPreference.setSummary(getResources().getQuantityString(
-                    R.plurals.additional_permissions_more, numExtraPermissions,
-                    numExtraPermissions));
+            screen.removePreference(otherPermissionsPreference);
+            // The PreferenceScreen is ordering items as added
+            // (see PreferenceGroup#setOrderingAsAdded()), which means that it assigns positional
+            // indexes ("order") to Preferences incrementally as they are added, BUT ONLY IF their
+            // current "order" is the DEFAULT_ORDER.
+            // However, when the Preferences are removed from the group they keep their "order" and
+            // thus when they are re-added to a group (same or another) their "order" does not get
+            // re-assigned, so they may show up at the position they previously were at.
+            // We want the otherPermissionsPreference to always be the last in the list, so reset
+            // its "order" to DEFAULT, so that we add last to the group, it indeed goes into the
+            // last position.
+            otherPermissionsPreference.setOrder(Preference.DEFAULT_ORDER);
         }
 
-        Integer numAutoRevoked = mViewModel.getNumAutoRevoked().getValue();
+        PermissionGroupPreferenceUtils.updateGroupOfPermissionPreferences(context, screen,
+                permissionGroups);
 
-        Preference autoRevokePreference = getPreferenceScreen().findPreference(AUTO_REVOKE_KEY);
+        screen.addPreference(otherPermissionsPreference);
+
+        // load initial auto-revoke count, if it is ready
+        Integer numAutoRevoked = mManageStandardPermissionsViewModel.getNumAutoRevoked().getValue();
+        onNumAutoRevokedChanged(numAutoRevoked);
+
+        setLoading(false);
+    }
+
+
+    private Preference buildOtherPermissionsPreference(Context context) {
+        final Preference preference = new Preference(context);
+        preference.setPersistent(false);
+        preference.setKey(KEY_OTHER_PERMISSIONS);
+        preference.setTitle(R.string.other_permissions_label);
+        preference.setIcon(
+                Utils.applyTint(
+                        context, R.drawable.ic_more_items, android.R.attr.colorControlNormal));
+        preference.setOnPreferenceClickListener(p -> {
+            AutoManageOtherPermissionsFragment frag =
+                    new AutoManageOtherPermissionsFragment();
+            frag.setTargetFragment(AutoManageStandardPermissionsFragment.this,
+                    /* requestCode= */ 0);
+            FragmentTransaction ft = getFragmentManager().beginTransaction();
+            ft.replace(android.R.id.content, frag);
+            ft.addToBackStack(null);
+            ft.commit();
+            return true;
+        });
+        // Make invisible for now and subscribe to the LiveData that tracks whether there are any
+        // unused or additional permissions.
+        preference.setVisible(false);
+        mManagePermissionsViewModel.hasUnusedOrAdditionalPermissionGroups().observe(this,
+                preference::setVisible);
+        return preference;
+    }
+
+    private void onNumAutoRevokedChanged(Integer numAutoRevoked) {
+        // to prevent ui jank, don't display auto-revoke until categories have loaded
+        if (mManagePermissionsViewModel.getUsedPermissionGroups().getValue() == null) {
+            return;
+        }
+        Preference autoRevokePreference = getPreferenceScreen().findPreference(KEY_AUTO_REVOKE);
         if (numAutoRevoked != null && numAutoRevoked != 0) {
             if (autoRevokePreference == null) {
                 autoRevokePreference = new Preference(getPreferenceManager().getContext());
                 autoRevokePreference.setOrder(-1);
-                autoRevokePreference.setKey(AUTO_REVOKE_KEY);
+                autoRevokePreference.setKey(KEY_AUTO_REVOKE);
                 autoRevokePreference.setSingleLineTitle(false);
                 autoRevokePreference.setIcon(R.drawable.ic_info_outline);
                 autoRevokePreference.setTitle(
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionAppsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionAppsFragment.java
index dfd5361..94fd6b9 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionAppsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionAppsFragment.java
@@ -16,82 +16,272 @@
 
 package com.android.permissioncontroller.permission.ui.auto;
 
+import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
+import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
+import static com.android.permissioncontroller.permission.ui.Category.ALLOWED;
+import static com.android.permissioncontroller.permission.ui.Category.ALLOWED_FOREGROUND;
+import static com.android.permissioncontroller.permission.ui.Category.ASK;
+import static com.android.permissioncontroller.permission.ui.Category.DENIED;
+import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_CALLER_NAME;
+
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Bundle;
+import android.os.UserHandle;
 import android.util.ArrayMap;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProvider;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceCategory;
-import androidx.preference.PreferenceGroup;
 
+import com.android.modules.utils.build.SdkLevel;
 import com.android.permissioncontroller.R;
 import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
-import com.android.permissioncontroller.permission.model.AppPermissionGroup;
-import com.android.permissioncontroller.permission.model.legacy.PermissionApps;
-import com.android.permissioncontroller.permission.model.legacy.PermissionApps.Callback;
-import com.android.permissioncontroller.permission.ui.handheld.PermissionAppsFragment;
-import com.android.permissioncontroller.permission.ui.handheld.PermissionControlPreference;
+import com.android.permissioncontroller.permission.model.AppPermissionUsage;
+import com.android.permissioncontroller.permission.model.PermissionUsages;
+import com.android.permissioncontroller.permission.ui.Category;
+import com.android.permissioncontroller.permission.ui.handheld.SmartIconLoadPackagePermissionPreference;
+import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel;
+import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModelFactory;
+import com.android.permissioncontroller.permission.utils.KotlinUtils;
 import com.android.permissioncontroller.permission.utils.Utils;
+import com.android.settingslib.utils.applications.AppUtils;
 
 import java.text.Collator;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
+import java.util.Random;
+
+import kotlin.Pair;
 
 /** Shows the list of applications which have (or do not have) the given permission. */
-public class AutoPermissionAppsFragment extends AutoSettingsFrameFragment implements Callback {
+public class AutoPermissionAppsFragment extends AutoSettingsFrameFragment implements
+        PermissionUsages.PermissionsUsagesChangeCallback {
 
-    private static final String KEY_SHOW_SYSTEM_PREFS = "_showSystem";
-    private static final String KEY_ALLOWED_PERMISSIONS_GROUP = "allowed_permissions_group";
-    private static final String KEY_ALLOWED_FOREGROUND_PERMISSIONS_GROUP =
-            "allowed_foreground_permissions_group";
-    private static final String KEY_DENIED_PERMISSIONS_GROUP = "denied_permissions_group";
-
-    private static final String SHOW_SYSTEM_KEY = AutoPermissionAppsFragment.class.getName()
-            + KEY_SHOW_SYSTEM_PREFS;
+    private static final String LOG_TAG = "AutoPermissionAppsFragment";
+    private static final String KEY_EMPTY = "_empty";
 
     /** Creates a new instance of {@link AutoPermissionAppsFragment} for the given permission. */
-    public static AutoPermissionAppsFragment newInstance(String permissionName) {
-        return setPermissionName(new AutoPermissionAppsFragment(), permissionName);
+    public static AutoPermissionAppsFragment newInstance(String permissionName, long sessionId) {
+        return setPermissionName(new AutoPermissionAppsFragment(), permissionName, sessionId);
     }
 
-    private static <T extends Fragment> T setPermissionName(T fragment, String permissionName) {
+    private static <T extends Fragment> T setPermissionName(
+            T fragment, String permissionName, long sessionId) {
         Bundle arguments = new Bundle();
         arguments.putString(Intent.EXTRA_PERMISSION_NAME, permissionName);
+        arguments.putLong(EXTRA_SESSION_ID, sessionId);
         fragment.setArguments(arguments);
         return fragment;
     }
 
-    private PermissionApps mPermissionApps;
-
-    private boolean mShowSystem;
-    private boolean mHasSystemApps;
+    private PermissionAppsViewModel mViewModel;
+    private PermissionUsages mPermissionUsages;
+    private List<AppPermissionUsage> mAppPermissionUsages = new ArrayList<>();
+    private String mPermGroupName;
 
     private Collator mCollator;
 
     @Override
     public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
-        if (savedInstanceState != null) {
-            mShowSystem = savedInstanceState.getBoolean(SHOW_SYSTEM_KEY);
-        }
-
         setLoading(true);
 
-        String groupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
-        mPermissionApps = new PermissionApps(getActivity(), groupName, /* callback= */ this);
-        mPermissionApps.refresh(/* getUiInfo= */ true);
+        mPermGroupName = getArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME);
+        if (mPermGroupName == null) {
+            mPermGroupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
+        }
+
+        Drawable icon = KotlinUtils.INSTANCE.getPermGroupIcon(getContext(), mPermGroupName);
+        CharSequence label = KotlinUtils.INSTANCE.getPermGroupLabel(getContext(), mPermGroupName);
+        CharSequence description = KotlinUtils.INSTANCE.getPermGroupDescription(getContext(),
+                mPermGroupName);
+
+        setHeaderLabel(label);
+        Preference header = new Preference(getContext());
+        header.setTitle(label);
+        header.setIcon(icon);
+        header.setSummary(Utils.getPermissionGroupDescriptionString(getContext(), mPermGroupName,
+                description));
+        getPreferenceScreen().addPreference(header);
 
         mCollator = Collator.getInstance(
                 getContext().getResources().getConfiguration().getLocales().get(0));
 
-        setShowSystemAppsToggle();
-        bindUi(mPermissionApps, groupName);
+        PermissionAppsViewModelFactory factory =
+                new PermissionAppsViewModelFactory(getActivity().getApplication(), mPermGroupName,
+                        this, new Bundle());
+        mViewModel = new ViewModelProvider(this, factory).get(PermissionAppsViewModel.class);
+
+        mViewModel.getCategorizedAppsLiveData().observe(this, this::onPackagesLoaded);
+        mViewModel.getShouldShowSystemLiveData().observe(this, this::updateMenu);
+        mViewModel.getHasSystemAppsLiveData().observe(this, this::hideSystemAppToggleIfNecessary);
+
+        // If the build type is below S, the app ops for permission usage can't be found. Thus, we
+        // shouldn't load permission usages, for them.
+        if (SdkLevel.isAtLeastS()) {
+            Context context = getPreferenceManager().getContext();
+            mPermissionUsages = new PermissionUsages(context);
+
+            long filterTimeBeginMillis = mViewModel.getFilterTimeBeginMillis();
+            mPermissionUsages.load(null, null, filterTimeBeginMillis, Long.MAX_VALUE,
+                    PermissionUsages.USAGE_FLAG_LAST, getActivity().getLoaderManager(),
+                    false, false, this, false);
+        }
+    }
+
+    private void updateMenu(Boolean showSystem) {
+        if (showSystem == null) {
+            showSystem = false;
+        }
+        // Show the opposite label from the current state.
+        String label;
+        if (showSystem) {
+            label = getString(R.string.menu_hide_system);
+        } else {
+            label = getString(R.string.menu_show_system);
+        }
+
+        boolean showSystemFinal = showSystem;
+        setAction(label, v -> mViewModel.updateShowSystem(!showSystemFinal));
+    }
+
+    /**
+     * Main differences between this phone implementation and this one are:
+     * <ul>
+     *     <li>No special handling for scoped storage</li>
+     * </ul>
+     */
+    private void onPackagesLoaded(Map<Category, List<Pair<String, UserHandle>>> categories) {
+        Preference additionalPermissionsPreference = getPreferenceScreen().findPreference(
+                ALLOWED_FOREGROUND.getCategoryName());
+        if (additionalPermissionsPreference == null) {
+            // This preference resources includes the "Ask" permission group. That's okay for Auto
+            // even though Auto doesn't support the one-time permission because the code later in
+            // this method will hide unused permission groups.
+            addPreferencesFromResource(R.xml.allowed_denied);
+        }
+        // Hide allowed foreground label by default, to avoid briefly showing it before updating
+        findPreference(ALLOWED_FOREGROUND.getCategoryName()).setVisible(false);
+        Context context = getPreferenceManager().getContext();
+
+        if (context == null || getActivity() == null || categories == null) {
+            return;
+        }
+
+        Map<String, Preference> existingPrefs = new ArrayMap<>();
+
+        // Start at 1 since the header preference will always be in the 0th index
+        for (int i = 1; i < getPreferenceScreen().getPreferenceCount(); i++) {
+            PreferenceCategory category = (PreferenceCategory)
+                    getPreferenceScreen().getPreference(i);
+            category.setOrderingAsAdded(true);
+            int numPreferences = category.getPreferenceCount();
+            for (int j = 0; j < numPreferences; j++) {
+                Preference preference = category.getPreference(j);
+                existingPrefs.put(preference.getKey(), preference);
+            }
+            category.removeAll();
+        }
+
+        long viewIdForLogging = new Random().nextLong();
+        long sessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID);
+
+        Boolean showAlways = mViewModel.getShowAllowAlwaysStringLiveData().getValue();
+        if (showAlways != null && showAlways) {
+            findPreference(ALLOWED.getCategoryName()).setTitle(R.string.allowed_always_header);
+        } else {
+            findPreference(ALLOWED.getCategoryName()).setTitle(R.string.allowed_header);
+        }
+
+        // A mapping of user + packageName to their last access timestamps for the permission group.
+        Map<String, Long> groupUsageLastAccessTime =
+                mViewModel.extractGroupUsageLastAccessTime(mAppPermissionUsages);
+
+        for (Category grantCategory : categories.keySet()) {
+            List<Pair<String, UserHandle>> packages = categories.get(grantCategory);
+            PreferenceCategory category = findPreference(grantCategory.getCategoryName());
+
+            // If this category is empty set up the empty preference.
+            if (packages.size() == 0) {
+                Preference empty = new Preference(context);
+                empty.setSelectable(false);
+                empty.setKey(category.getKey() + KEY_EMPTY);
+                if (grantCategory.equals(ALLOWED)) {
+                    empty.setTitle(getString(R.string.no_apps_allowed));
+                } else if (grantCategory.equals(ALLOWED_FOREGROUND)) {
+                    category.setVisible(false);
+                } else if (grantCategory.equals(ASK)) {
+                    category.setVisible(false);
+                } else {
+                    empty.setTitle(getString(R.string.no_apps_denied));
+                }
+                category.addPreference(empty);
+                continue;
+            } else if (grantCategory.equals(ALLOWED_FOREGROUND)) {
+                category.setVisible(true);
+            } else if (grantCategory.equals(ASK)) {
+                category.setVisible(true);
+            }
+
+            for (Pair<String, UserHandle> packageUserLabel : packages) {
+                String packageName = packageUserLabel.getFirst();
+                UserHandle user = packageUserLabel.getSecond();
+
+                String key = user + packageName;
+
+                Long lastAccessTime = groupUsageLastAccessTime.get(key);
+                Pair<String, Integer> summaryTimestamp = Utils
+                        .getPermissionLastAccessSummaryTimestamp(
+                                lastAccessTime, context, mPermGroupName);
+
+                Preference existingPref = existingPrefs.get(key);
+                if (existingPref != null) {
+                    updatePreferenceSummary(existingPref, summaryTimestamp);
+                    category.addPreference(existingPref);
+                    continue;
+                }
+
+                SmartIconLoadPackagePermissionPreference pref =
+                        new SmartIconLoadPackagePermissionPreference(getActivity().getApplication(),
+                                packageName, user, context);
+                pref.setKey(key);
+                pref.setTitle(KotlinUtils.INSTANCE.getPackageLabel(getActivity().getApplication(),
+                        packageName, user));
+                pref.setOnPreferenceClickListener((Preference p) -> {
+                    Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSION);
+                    intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+                    intent.putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, mPermGroupName);
+                    intent.putExtra(Intent.EXTRA_USER, user);
+                    intent.putExtra(EXTRA_CALLER_NAME, getClass().getName());
+                    intent.putExtra(EXTRA_SESSION_ID, sessionId);
+                    startActivity(intent);
+                    return true;
+                });
+                pref.setTitleContentDescription(AppUtils.getAppContentDescription(context,
+                        packageName, user.getIdentifier()));
+
+                updatePreferenceSummary(pref, summaryTimestamp);
+
+                category.addPreference(pref);
+                if (!mViewModel.getCreationLogged()) {
+                    logPermissionAppsFragmentCreated(packageName, user, viewIdForLogging,
+                            grantCategory.equals(ALLOWED), grantCategory.equals(ALLOWED_FOREGROUND),
+                            grantCategory.equals(DENIED));
+                }
+            }
+            KotlinUtils.INSTANCE.sortPreferenceGroup(category, this::comparePreference, false);
+        }
+
+        mViewModel.setCreationLogged(true);
+
+        setLoading(false);
     }
 
     @Override
@@ -99,206 +289,44 @@
         setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext()));
     }
 
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-
-        outState.putBoolean(SHOW_SYSTEM_KEY, mShowSystem);
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        mPermissionApps.refresh(/* getUiInfo= */ true);
-    }
-
-    private void setShowSystemAppsToggle() {
-        if (!mHasSystemApps) {
+    private void hideSystemAppToggleIfNecessary(Boolean hasSystemApps) {
+        if (hasSystemApps == null || !hasSystemApps) {
             setAction(/* label= */ null, /* onClickListener= */ null);
-            return;
         }
-
-        // Show the opposite label from the current state.
-        String label;
-        if (mShowSystem) {
-            label = getString(R.string.menu_hide_system);
-        } else {
-            label = getString(R.string.menu_show_system);
-        }
-
-        setAction(label, v -> {
-            mShowSystem = !mShowSystem;
-            if (mPermissionApps.getApps() != null) {
-                onPermissionsLoaded(mPermissionApps);
-            }
-            setShowSystemAppsToggle();
-        });
     }
 
-    private void bindUi(PermissionApps permissionApps, @NonNull String groupName) {
-        CharSequence label = permissionApps.getFullLabel();
-        setHeaderLabel(label);
-
-        Drawable icon = permissionApps.getIcon();
-        Preference header = new Preference(getContext());
-        header.setTitle(label);
-        header.setIcon(icon);
-        header.setSummary(Utils.getPermissionGroupDescriptionString(getContext(), groupName,
-                permissionApps.getDescription()));
-        getPreferenceScreen().addPreference(header);
-
-        PreferenceGroup allowed = new PreferenceCategory(getContext());
-        allowed.setKey(KEY_ALLOWED_PERMISSIONS_GROUP);
-        allowed.setTitle(R.string.allowed_header);
-        allowed.setVisible(false);
-        getPreferenceScreen().addPreference(allowed);
-
-        PreferenceGroup foreground = new PreferenceCategory(getContext());
-        foreground.setKey(KEY_ALLOWED_FOREGROUND_PERMISSIONS_GROUP);
-        foreground.setTitle(R.string.allowed_foreground_header);
-        foreground.setVisible(false);
-        getPreferenceScreen().addPreference(foreground);
-
-        PreferenceGroup denied = new PreferenceCategory(getContext());
-        denied.setKey(KEY_DENIED_PERMISSIONS_GROUP);
-        denied.setTitle(R.string.denied_header);
-        denied.setVisible(false);
-        getPreferenceScreen().addPreference(denied);
+    private void updatePreferenceSummary(Preference preference,
+            Pair<String, Integer> summaryTimestamp) {
+        String summary = mViewModel.getPreferenceSummary(getResources(), summaryTimestamp);
+        if (!summary.isEmpty()) {
+            preference.setSummary(summary);
+        }
     }
 
     @Override
-    public void onPermissionsLoaded(PermissionApps permissionApps) {
-        Context context = getPreferenceManager().getContext();
-
-        if (context == null || getActivity() == null) {
+    @RequiresApi(Build.VERSION_CODES.S)
+    public void onPermissionUsagesChanged() {
+        if (mPermissionUsages.getUsages().isEmpty()) {
+            return;
+        }
+        if (getContext() == null) {
+            // Async result has come in after our context is gone.
             return;
         }
 
-        PreferenceCategory allowed = findPreference(KEY_ALLOWED_PERMISSIONS_GROUP);
-        PreferenceCategory allowedForeground = findPreference(
-                KEY_ALLOWED_FOREGROUND_PERMISSIONS_GROUP);
-        PreferenceCategory denied = findPreference(KEY_DENIED_PERMISSIONS_GROUP);
+        mAppPermissionUsages = new ArrayList<>(mPermissionUsages.getUsages());
+        onPackagesLoaded(mViewModel.getCategorizedAppsLiveData().getValue());
+    }
 
-        allowed.setOrderingAsAdded(true);
-        allowedForeground.setOrderingAsAdded(true);
-        denied.setOrderingAsAdded(true);
+    private int comparePreference(Preference lhs, Preference rhs) {
+        return mViewModel.comparePreference(mCollator, lhs, rhs);
+    }
 
-        Map<String, Preference> existingPrefs = new ArrayMap<>();
-        int numPreferences = allowed.getPreferenceCount();
-        for (int i = 0; i < numPreferences; i++) {
-            Preference preference = allowed.getPreference(i);
-            existingPrefs.put(preference.getKey(), preference);
-        }
-        allowed.removeAll();
-        numPreferences = allowedForeground.getPreferenceCount();
-        for (int i = 0; i < numPreferences; i++) {
-            Preference preference = allowedForeground.getPreference(i);
-            existingPrefs.put(preference.getKey(), preference);
-        }
-        allowedForeground.removeAll();
-        numPreferences = denied.getPreferenceCount();
-        for (int i = 0; i < numPreferences; i++) {
-            Preference preference = denied.getPreference(i);
-            existingPrefs.put(preference.getKey(), preference);
-        }
-        denied.removeAll();
-
-        mHasSystemApps = false;
-        boolean hasPermissionWithBackgroundMode = false;
-
-        ArrayList<PermissionApps.PermissionApp> sortedApps = new ArrayList<>(
-                permissionApps.getApps());
-        sortedApps.sort((x, y) -> {
-            int result = mCollator.compare(x.getLabel(), y.getLabel());
-            if (result == 0) {
-                result = x.getUid() - y.getUid();
-            }
-            return result;
-        });
-
-        for (int i = 0; i < sortedApps.size(); i++) {
-            PermissionApps.PermissionApp app = sortedApps.get(i);
-            AppPermissionGroup group = app.getPermissionGroup();
-
-            hasPermissionWithBackgroundMode =
-                    hasPermissionWithBackgroundMode || group.hasPermissionWithBackgroundMode();
-
-            if (!Utils.shouldShowPermission(getContext(), group)) {
-                continue;
-            }
-
-            if (!app.getAppInfo().enabled) {
-                continue;
-            }
-
-            String key = app.getKey();
-            Preference existingPref = existingPrefs.get(key);
-            if (existingPref != null) {
-                // Without this, existing preferences remember their old order.
-                existingPref.setOrder(Preference.DEFAULT_ORDER);
-            }
-
-            boolean isSystemApp = !Utils.isGroupOrBgGroupUserSensitive(group);
-
-            if (isSystemApp) {
-                mHasSystemApps = true;
-            }
-
-            if (isSystemApp && !mShowSystem) {
-                continue;
-            }
-
-            PreferenceCategory category;
-            if (group.areRuntimePermissionsGranted()) {
-                if (!group.hasPermissionWithBackgroundMode()
-                        || (group.getBackgroundPermissions() != null
-                        && group.getBackgroundPermissions().areRuntimePermissionsGranted())) {
-                    category = allowed;
-                } else {
-                    category = allowedForeground;
-                }
-            } else {
-                category = denied;
-            }
-
-            if (existingPref != null) {
-                category.addPreference(existingPref);
-                continue;
-            }
-
-            PermissionControlPreference pref = new PermissionControlPreference(context, group,
-                    PermissionAppsFragment.class.getName());
-            pref.setKey(key);
-            pref.setIcon(app.getIcon());
-            pref.setTitle(Utils.getFullAppLabel(app.getAppInfo(), context));
-            pref.setEllipsizeEnd();
-            pref.useSmallerIcon();
-            category.addPreference(pref);
-        }
-
-        if (hasPermissionWithBackgroundMode) {
-            allowed.setTitle(R.string.allowed_always_header);
-        }
-
-        if (allowed.getPreferenceCount() == 0) {
-            Preference empty = new Preference(context);
-            empty.setTitle(R.string.no_apps_allowed);
-            empty.setSelectable(false);
-            allowed.addPreference(empty);
-        }
-        allowed.setVisible(true);
-
-        allowedForeground.setVisible(allowedForeground.getPreferenceCount() > 0);
-
-        if (denied.getPreferenceCount() == 0) {
-            Preference empty = new Preference(context);
-            empty.setTitle(R.string.no_apps_denied);
-            empty.setSelectable(false);
-            denied.addPreference(empty);
-        }
-        denied.setVisible(true);
-
-        setShowSystemAppsToggle();
-        setLoading(false);
+    private void logPermissionAppsFragmentCreated(String packageName, UserHandle user, long viewId,
+            boolean isAllowed, boolean isAllowedForeground, boolean isDenied) {
+        long sessionId = getArguments().getLong(EXTRA_SESSION_ID, 0);
+        mViewModel.logPermissionAppsFragmentCreated(packageName, user, viewId, isAllowed,
+                isAllowedForeground, isDenied, sessionId, getActivity().getApplication(),
+                mPermGroupName, LOG_TAG);
     }
 }
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsFragment.kt
index 2a62d22..11d5b7e 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsFragment.kt
@@ -20,10 +20,12 @@
 import android.os.Bundle
 import android.os.UserHandle
 import androidx.preference.Preference
+import androidx.preference.PreferenceCategory
 import com.android.permissioncontroller.R
 import com.android.permissioncontroller.auto.AutoSettingsFrameFragment
 import com.android.permissioncontroller.hibernation.isHibernationEnabled
 import com.android.permissioncontroller.permission.ui.UnusedAppsFragment
+import com.android.permissioncontroller.permission.ui.UnusedAppsFragment.Companion.INFO_MSG_CATEGORY
 import com.android.car.ui.utils.ViewUtils
 import com.android.car.ui.utils.ViewUtils.LazyLayoutView
 
@@ -34,6 +36,8 @@
     UnusedAppsFragment.Parent<AutoUnusedAppsPreference> {
 
     companion object {
+        private const val UNUSED_PREFERENCE_KEY = "unused_pref_row_key"
+
         /** Create a new instance of this fragment.  */
         @JvmStatic
         fun newInstance(): AutoUnusedAppsFragment {
@@ -97,4 +101,25 @@
     override fun setTitle(title: CharSequence) {
         headerLabel = title
     }
+
+    override fun setEmptyState(empty: Boolean) {
+        val infoMsgCategory =
+                preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)!!
+        val noUnusedAppsPreference: Preference? =
+                infoMsgCategory.findPreference<Preference>(UNUSED_PREFERENCE_KEY)
+        if (empty && noUnusedAppsPreference == null) {
+            infoMsgCategory.addPreference(createNoUnusedAppsPreference())
+        } else if (noUnusedAppsPreference != null) {
+            noUnusedAppsPreference.setVisible(empty)
+        }
+    }
+
+    private fun createNoUnusedAppsPreference(): Preference {
+        val preference = Preference(context)
+        preference.title = getString(R.string.zero_unused_apps)
+        preference.key = UNUSED_PREFERENCE_KEY
+        preference.isSelectable = false
+        preference.order = 0
+        return preference
+    }
 }
\ No newline at end of file
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java
index 9023a34..a66b15f 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java
@@ -160,6 +160,7 @@
         }
 
         CarUiContentListItem item = new CarUiContentListItem(CarUiContentListItem.Action.NONE);
+        item.setSecure(true);
         item.setTitle(mContext.getString(stringId));
         item.setOnItemClickedListener(i -> {
             mDialog.setOnDismissListener(null);
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java
index 999ca59..cd44832 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java
@@ -68,9 +68,9 @@
 import com.android.permissioncontroller.PermissionControllerStatsLog;
 import com.android.permissioncontroller.R;
 import com.android.permissioncontroller.permission.model.AppPermissionUsage;
+import com.android.permissioncontroller.permission.model.PermissionUsages;
 import com.android.permissioncontroller.permission.model.livedatatypes.HibernationSettingState;
 import com.android.permissioncontroller.permission.ui.Category;
-import com.android.permissioncontroller.permission.ui.handheld.dashboard.PermissionUsages;
 import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModel;
 import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModel.GroupUiInfo;
 import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModelFactory;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt
index 55686cf..eaf90de 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt
@@ -22,9 +22,11 @@
 import android.os.UserHandle
 import android.view.MenuItem
 import androidx.preference.Preference
+import androidx.preference.PreferenceCategory
 import com.android.permissioncontroller.R
 import com.android.permissioncontroller.hibernation.isHibernationEnabled
 import com.android.permissioncontroller.permission.ui.UnusedAppsFragment
+import com.android.permissioncontroller.permission.ui.UnusedAppsFragment.Companion.INFO_MSG_CATEGORY
 
 /**
  * Handheld wrapper, with customizations, around [UnusedAppsFragment].
@@ -109,4 +111,10 @@
     override fun setTitle(title: CharSequence) {
         requireActivity().setTitle(title)
     }
+
+    override fun setEmptyState(empty: Boolean) {
+        val infoMsgCategory =
+                preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)!!
+        infoMsgCategory.isVisible = !empty
+    }
 }
\ No newline at end of file
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java
index 4491557..b28f490 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java
@@ -17,23 +17,12 @@
 
 import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
 import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
-import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED;
-import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED;
-import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND;
-import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__DENIED;
-import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__UNDEFINED;
 import static com.android.permissioncontroller.permission.ui.Category.ALLOWED;
 import static com.android.permissioncontroller.permission.ui.Category.ALLOWED_FOREGROUND;
 import static com.android.permissioncontroller.permission.ui.Category.ASK;
 import static com.android.permissioncontroller.permission.ui.Category.DENIED;
 import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack;
 import static com.android.permissioncontroller.permission.ui.handheld.dashboard.UtilsKt.shouldShowPermissionsDashboard;
-import static com.android.permissioncontroller.permission.utils.Utils.LAST_24H_CONTENT_PROVIDER;
-import static com.android.permissioncontroller.permission.utils.Utils.LAST_24H_SENSOR_TODAY;
-import static com.android.permissioncontroller.permission.utils.Utils.LAST_24H_SENSOR_YESTERDAY;
-import static com.android.permissioncontroller.permission.utils.Utils.NOT_IN_LAST_24H;
-
-import static java.util.concurrent.TimeUnit.DAYS;
 
 import android.Manifest;
 import android.app.ActionBar;
@@ -46,7 +35,6 @@
 import android.os.Looper;
 import android.os.UserHandle;
 import android.util.ArrayMap;
-import android.util.Log;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
@@ -59,12 +47,11 @@
 import androidx.preference.PreferenceCategory;
 
 import com.android.modules.utils.build.SdkLevel;
-import com.android.permissioncontroller.PermissionControllerStatsLog;
 import com.android.permissioncontroller.R;
 import com.android.permissioncontroller.permission.model.AppPermissionUsage;
+import com.android.permissioncontroller.permission.model.PermissionUsages;
 import com.android.permissioncontroller.permission.ui.Category;
 import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity;
-import com.android.permissioncontroller.permission.ui.handheld.dashboard.PermissionUsages;
 import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel;
 import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModelFactory;
 import com.android.permissioncontroller.permission.utils.KotlinUtils;
@@ -73,9 +60,7 @@
 import com.android.settingslib.utils.applications.AppUtils;
 
 import java.text.Collator;
-import java.time.Instant;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Random;
@@ -98,7 +83,6 @@
     private static final String STORAGE_ALLOWED_FULL = "allowed_storage_full";
     private static final String STORAGE_ALLOWED_SCOPED = "allowed_storage_scoped";
     private static final int SHOW_LOAD_DELAY_MS = 200;
-    private static final int AGGREGATE_DATA_FILTER_BEGIN_DAYS = 1;
 
     private static final int MENU_PERMISSION_USAGE = MENU_HIDE_SYSTEM + 1;
 
@@ -167,9 +151,7 @@
             Context context = getPreferenceManager().getContext();
             mPermissionUsages = new PermissionUsages(context);
 
-            long filterTimeBeginMillis = Math.max(System.currentTimeMillis()
-                            - DAYS.toMillis(AGGREGATE_DATA_FILTER_BEGIN_DAYS),
-                    Instant.EPOCH.toEpochMilli());
+            long filterTimeBeginMillis = mViewModel.getFilterTimeBeginMillis();
             mPermissionUsages.load(null, null, filterTimeBeginMillis, Long.MAX_VALUE,
                     PermissionUsages.USAGE_FLAG_LAST, getActivity().getLoaderManager(),
                     false, false, this, false);
@@ -309,8 +291,8 @@
         }
 
         // A mapping of user + packageName to their last access timestamps for the permission group.
-        Map<String, Long> groupUsageLastAccessTime = new HashMap<>();
-        extractGroupUsageLastAccessTime(groupUsageLastAccessTime);
+        Map<String, Long> groupUsageLastAccessTime =
+                mViewModel.extractGroupUsageLastAccessTime(mAppPermissionUsages);
 
         for (Category grantCategory : categories.keySet()) {
             List<Pair<String, UserHandle>> packages = categories.get(grantCategory);
@@ -422,57 +404,12 @@
 
     private void updatePreferenceSummary(Preference preference,
             Pair<String, Integer> summaryTimestamp) {
-        @Utils.AppPermsLastAccessType int lastAccessType = summaryTimestamp.getSecond();
-
-        switch (lastAccessType) {
-            case LAST_24H_CONTENT_PROVIDER:
-                preference.setSummary(
-                        R.string.app_perms_content_provider);
-                break;
-            case LAST_24H_SENSOR_TODAY:
-                preference.setSummary(
-                        getString(R.string.app_perms_24h_access,
-                                summaryTimestamp.getFirst()));
-                break;
-            case LAST_24H_SENSOR_YESTERDAY:
-                preference.setSummary(
-                        getString(R.string.app_perms_24h_access_yest,
-                                summaryTimestamp.getFirst()));
-                break;
-            case NOT_IN_LAST_24H:
-            default:
+        String summary = mViewModel.getPreferenceSummary(getResources(), summaryTimestamp);
+        if (!summary.isEmpty()) {
+            preference.setSummary(summary);
         }
     }
 
-    private void extractGroupUsageLastAccessTime(Map<String, Long> accessTime) {
-        accessTime.clear();
-        long filterTimeBeginMillis = Math.max(System.currentTimeMillis()
-                - DAYS.toMillis(AGGREGATE_DATA_FILTER_BEGIN_DAYS), Instant.EPOCH.toEpochMilli());
-
-        int numApps = mAppPermissionUsages.size();
-        for (int appIndex = 0; appIndex < numApps; appIndex++) {
-            AppPermissionUsage appUsage = mAppPermissionUsages.get(appIndex);
-            String packageName = appUsage.getPackageName();
-
-            List<AppPermissionUsage.GroupUsage> appGroups = appUsage.getGroupUsages();
-            int numGroups = appGroups.size();
-            for (int groupIndex = 0; groupIndex < numGroups; groupIndex++) {
-                AppPermissionUsage.GroupUsage groupUsage = appGroups.get(groupIndex);
-                String groupName = groupUsage.getGroup().getName();
-                if (!mPermGroupName.equals(groupName)) {
-                    continue;
-                }
-
-                long lastAccessTime = groupUsage.getLastAccessTime();
-                if (lastAccessTime == 0 || lastAccessTime < filterTimeBeginMillis) {
-                    continue;
-                }
-
-                String key = groupUsage.getGroup().getUser() + packageName;
-                accessTime.put(key, lastAccessTime);
-            }
-        }
-    }
 
     private int comparePreference(Preference lhs, Preference rhs) {
         int result = mCollator.compare(lhs.getTitle().toString(),
@@ -486,27 +423,8 @@
     private void logPermissionAppsFragmentCreated(String packageName, UserHandle user, long viewId,
             boolean isAllowed, boolean isAllowedForeground, boolean isDenied) {
         long sessionId = getArguments().getLong(EXTRA_SESSION_ID, 0);
-
-        int category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__UNDEFINED;
-        if (isAllowed) {
-            category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED;
-        } else if (isAllowedForeground) {
-            category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND;
-        } else if (isDenied) {
-            category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__DENIED;
-        }
-
-        Integer uid = KotlinUtils.INSTANCE.getPackageUid(getActivity().getApplication(),
-                packageName, user);
-        if (uid == null) {
-            return;
-        }
-
-        PermissionControllerStatsLog.write(PERMISSION_APPS_FRAGMENT_VIEWED, sessionId, viewId,
-                mPermGroupName, uid, packageName, category);
-        Log.v(LOG_TAG, "PermissionAppsFragment created with sessionId=" + sessionId
-                + " permissionGroupName=" + mPermGroupName + " appUid="
-                + uid + " packageName=" + packageName
-                + " category=" + category);
+        mViewModel.logPermissionAppsFragmentCreated(packageName, user, viewId, isAllowed,
+                isAllowedForeground, isDenied, sessionId, getActivity().getApplication(),
+                mPermGroupName, LOG_TAG);
     }
 }
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionDetailsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionDetailsFragment.java
index 05e7292..9ab0dc4 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionDetailsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionDetailsFragment.java
@@ -58,6 +58,7 @@
 import com.android.permissioncontroller.R;
 import com.android.permissioncontroller.permission.model.AppPermissionGroup;
 import com.android.permissioncontroller.permission.model.AppPermissionUsage;
+import com.android.permissioncontroller.permission.model.PermissionUsages;
 import com.android.permissioncontroller.permission.model.legacy.PermissionApps;
 import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity;
 import com.android.permissioncontroller.permission.ui.handheld.SettingsWithLargeHeader;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionUsageV2Fragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionUsageV2Fragment.java
index caeca2d..acc39c2 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionUsageV2Fragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionUsageV2Fragment.java
@@ -52,6 +52,7 @@
 import com.android.permissioncontroller.permission.model.AppPermissionGroup;
 import com.android.permissioncontroller.permission.model.AppPermissionUsage;
 import com.android.permissioncontroller.permission.model.AppPermissionUsage.GroupUsage;
+import com.android.permissioncontroller.permission.model.PermissionUsages;
 import com.android.permissioncontroller.permission.model.legacy.PermissionApps;
 import com.android.permissioncontroller.permission.ui.handheld.PermissionUsageV2ControlPreference;
 import com.android.permissioncontroller.permission.ui.handheld.SettingsWithLargeHeader;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/AppPermissionActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/AppPermissionActivity.java
index fa1ce2f..6386e51 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/AppPermissionActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/AppPermissionActivity.java
@@ -19,6 +19,9 @@
 import static android.content.Intent.ACTION_MANAGE_APP_PERMISSION;
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 
+import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
+import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
+
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.PermissionGroupInfo;
@@ -42,6 +45,7 @@
 import com.android.permissioncontroller.permission.utils.Utils;
 
 import java.util.List;
+import java.util.Random;
 
 /**
  * Manage a single permission of a single app.
@@ -146,8 +150,13 @@
         if (DeviceUtils.isAuto(this)) {
             Fragment androidXFragment;
 
+            long sessionId = getIntent().getLongExtra(EXTRA_SESSION_ID, INVALID_SESSION_ID);
+            while (sessionId == INVALID_SESSION_ID) {
+                sessionId = new Random().nextLong();
+            }
+
             androidXFragment = AutoAppPermissionFragment.newInstance(packageName, permissionName,
-                    groupName, userHandle);
+                    groupName, userHandle, sessionId);
 
             getSupportFragmentManager().beginTransaction().replace(android.R.id.content,
                     androidXFragment).commit();
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManagePermissionsViewModel.kt
similarity index 96%
rename from PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsViewModel.kt
rename to PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManagePermissionsViewModel.kt
index 8d4e8c2..970569c 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManagePermissionsViewModel.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.permission.ui.television
+package com.android.permissioncontroller.permission.ui.model
 
 import android.app.Application
 import androidx.lifecycle.AndroidViewModel
@@ -23,7 +23,6 @@
 import com.android.permissioncontroller.permission.data.PermGroupsPackagesUiInfoLiveData
 import com.android.permissioncontroller.permission.data.StandardPermGroupNamesLiveData
 import com.android.permissioncontroller.permission.model.livedatatypes.PermGroupPackagesUiInfo
-import com.android.permissioncontroller.permission.ui.model.UsedCustomPermGroupNamesLiveData
 
 /**
  * A [androidx.lifecycle.ViewModel] for [ManagePermissionsFragment] and
@@ -35,6 +34,7 @@
  * [ManagePermissionsViewModel].
  */
 class ManagePermissionsViewModel(app: Application) : AndroidViewModel(app) {
+
     private val standardPermGroupsLiveData: LiveData<List<PermGroupPackagesUiInfo>> =
         MediatorLiveData<List<PermGroupPackagesUiInfo>>().apply {
             addSource(PermGroupsPackagesUiInfoLiveData(app, StandardPermGroupNamesLiveData)) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt
index c7a13db..c4473b1 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt
@@ -19,20 +19,29 @@
 import android.Manifest
 import android.app.Application
 import android.content.Intent
+import android.content.res.Resources
 import android.os.Bundle
 import android.os.UserHandle
+import android.util.Log
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.AbstractSavedStateViewModelFactory
 import androidx.lifecycle.MediatorLiveData
 import androidx.lifecycle.SavedStateHandle
 import androidx.lifecycle.ViewModel
 import androidx.navigation.fragment.findNavController
+import androidx.preference.Preference
 import androidx.savedstate.SavedStateRegistryOwner
+import com.android.permissioncontroller.PermissionControllerStatsLog
+import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED
+import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__UNDEFINED
+import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND
+import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__DENIED
 import com.android.permissioncontroller.R
 import com.android.permissioncontroller.permission.data.AllPackageInfosLiveData
 import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData
 import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData.FullStoragePackageState
 import com.android.permissioncontroller.permission.data.SinglePermGroupPackagesUiInfoLiveData
+import com.android.permissioncontroller.permission.model.AppPermissionUsage
 import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState
 import com.android.permissioncontroller.permission.ui.Category
 import com.android.permissioncontroller.permission.ui.LocationProviderInterceptDialog
@@ -40,8 +49,14 @@
 import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel.Companion.HAS_SYSTEM_APPS_KEY
 import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel.Companion.SHOULD_SHOW_SYSTEM_KEY
 import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel.Companion.SHOW_ALWAYS_ALLOWED
+import com.android.permissioncontroller.permission.utils.KotlinUtils.getPackageUid
 import com.android.permissioncontroller.permission.utils.LocationUtils
+import com.android.permissioncontroller.permission.utils.Utils
 import com.android.permissioncontroller.permission.utils.navigateSafe
+import java.text.Collator
+import java.time.Instant
+import java.util.concurrent.TimeUnit
+import kotlin.math.max
 
 /**
  * ViewModel for the PermissionAppsFragment. Has a liveData with all of the UI info for each
@@ -59,6 +74,7 @@
 ) : ViewModel() {
 
     companion object {
+        const val AGGREGATE_DATA_FILTER_BEGIN_DAYS = 1
         internal const val SHOULD_SHOW_SYSTEM_KEY = "showSystem"
         internal const val HAS_SYSTEM_APPS_KEY = "hasSystem"
         internal const val SHOW_ALWAYS_ALLOWED = "showAlways"
@@ -240,6 +256,110 @@
 
         fragment.findNavController().navigateSafe(R.id.perm_apps_to_app, args)
     }
+
+    fun getFilterTimeBeginMillis(): Long {
+        return max(System.currentTimeMillis() -
+                TimeUnit.DAYS.toMillis(AGGREGATE_DATA_FILTER_BEGIN_DAYS.toLong()),
+                Instant.EPOCH.toEpochMilli())
+    }
+
+    /**
+     * Return a mapping of user + packageName to their last access timestamps for the permission
+     * group.
+     */
+    fun extractGroupUsageLastAccessTime(appPermissionUsages: List<AppPermissionUsage>):
+            MutableMap<String, Long> {
+        val accessTime: MutableMap<String, Long> = HashMap()
+        val now = System.currentTimeMillis()
+        val filterTimeBeginMillis = max(
+                now - TimeUnit.DAYS.toMillis(AGGREGATE_DATA_FILTER_BEGIN_DAYS.toLong()),
+                Instant.EPOCH.toEpochMilli())
+        val numApps: Int = appPermissionUsages.size
+        for (appIndex in 0 until numApps) {
+            val appUsage: AppPermissionUsage = appPermissionUsages.get(appIndex)
+            val packageName = appUsage.packageName
+            val appGroups = appUsage.groupUsages
+            val numGroups = appGroups.size
+            for (groupIndex in 0 until numGroups) {
+                val groupUsage = appGroups[groupIndex]
+                val groupUsageGroupName = groupUsage.group.name
+                if (groupName != groupUsageGroupName) {
+                    continue
+                }
+                val lastAccessTime = groupUsage.lastAccessTime
+                if (lastAccessTime == 0L || lastAccessTime < filterTimeBeginMillis) {
+                    continue
+                }
+                val key = groupUsage.group.user.toString() + packageName
+                accessTime[key] = lastAccessTime
+            }
+        }
+        return accessTime
+    }
+
+    /**
+     * Return the String preference summary based on the last access time.
+     */
+    fun getPreferenceSummary(res: Resources, summaryTimestamp: Pair<String, Int>): String {
+        return when (summaryTimestamp.second) {
+            Utils.LAST_24H_CONTENT_PROVIDER -> res.getString(
+                    R.string.app_perms_content_provider)
+            Utils.LAST_24H_SENSOR_TODAY -> res.getString(R.string.app_perms_24h_access,
+                    summaryTimestamp.first)
+            Utils.LAST_24H_SENSOR_YESTERDAY -> res.getString(R.string.app_perms_24h_access_yest,
+                    summaryTimestamp.first)
+            else -> ""
+        }
+    }
+
+    /**
+     * Return two preferences to determine their ordering.
+     */
+    fun comparePreference(collator: Collator, lhs: Preference, rhs: Preference): Int {
+        var result: Int = collator.compare(lhs.title.toString(),
+                rhs.title.toString())
+        if (result == 0) {
+            result = lhs.key.compareTo(rhs.key)
+        }
+        return result
+    }
+
+    /**
+     * Log that the fragment was created.
+     */
+    fun logPermissionAppsFragmentCreated(
+        packageName: String,
+        user: UserHandle,
+        viewId: Long,
+        isAllowed: Boolean,
+        isAllowedForeground: Boolean,
+        isDenied: Boolean,
+        sessionId: Long,
+        application: Application,
+        permGroupName: String,
+        tag: String
+    ) {
+        var category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__UNDEFINED
+        when {
+            isAllowed -> {
+                category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED
+            }
+            isAllowedForeground -> {
+                category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND
+            }
+            isDenied -> {
+                category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__DENIED
+            }
+        }
+        val uid = getPackageUid(application,
+                packageName, user) ?: return
+        PermissionControllerStatsLog.write(
+                PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED, sessionId, viewId,
+                permGroupName, uid, packageName, category)
+        Log.v(tag, tag + " created with sessionId=" + sessionId +
+                " permissionGroupName=" + permGroupName + " appUid=" + uid +
+                " packageName=" + packageName + " category=" + category)
+    }
 }
 
 /**
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionGroupPreference.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreference.java
similarity index 94%
rename from PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionGroupPreference.java
rename to PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreference.java
index bb45470..b69fb66 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionGroupPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreference.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.permission.ui.television;
+package com.android.permissioncontroller.permission.ui.model;
 
 import android.content.Context;
 import android.content.Intent;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionGroupPreferenceUtils.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreferenceUtils.java
similarity index 89%
rename from PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionGroupPreferenceUtils.java
rename to PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreferenceUtils.java
index a9578ac..49a01ef 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionGroupPreferenceUtils.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreferenceUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.permission.ui.television;
+package com.android.permissioncontroller.permission.ui.model;
 
 import android.content.Context;
 
@@ -27,9 +27,16 @@
 import java.util.List;
 import java.util.Objects;
 
-final class PermissionGroupPreferenceUtils {
+/**
+ * Utils related to displaying permission groups in preferences.
+ */
+public final class PermissionGroupPreferenceUtils {
 
-    static void updateGroupOfPermissionPreferences(Context context, PreferenceGroup preferenceGroup,
+    /**
+     * Update a {@link PreferenceGroup} with the specified permission groups.
+     */
+    public static void updateGroupOfPermissionPreferences(Context context,
+            PreferenceGroup preferenceGroup,
             List<PermGroupPackagesUiInfo> permissionGroups) {
         if (!(permissionGroups instanceof ArrayList)) {
             permissionGroups = new ArrayList<>(permissionGroups);
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsFragment.java
index 51af4bc..38a899b 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsFragment.java
@@ -31,6 +31,8 @@
 
 import com.android.permissioncontroller.R;
 import com.android.permissioncontroller.permission.model.livedatatypes.PermGroupPackagesUiInfo;
+import com.android.permissioncontroller.permission.ui.model.ManagePermissionsViewModel;
+import com.android.permissioncontroller.permission.ui.model.PermissionGroupPreferenceUtils;
 import com.android.permissioncontroller.permission.utils.Utils;
 
 import java.util.List;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsOtherFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsOtherFragment.java
index 6e13dfa..629cf34 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsOtherFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsOtherFragment.java
@@ -32,6 +32,8 @@
 
 import com.android.permissioncontroller.R;
 import com.android.permissioncontroller.permission.model.livedatatypes.PermGroupPackagesUiInfo;
+import com.android.permissioncontroller.permission.ui.model.ManagePermissionsViewModel;
+import com.android.permissioncontroller.permission.ui.model.PermissionGroupPreferenceUtils;
 
 import java.util.List;