Resurrect the ReviewOngoingPermissionsFragment

Resurrect the fragment, its data model, and allow the
PermissionController to handle its intent

Test: Manual
Bug: 159998363
Change-Id: I9a95528e333982e4f80a8c8805bedb58d6f77bf6
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index f8856ae..ccf18b8 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -122,6 +122,7 @@
                 <action android:name="android.intent.action.MANAGE_APP_PERMISSION" />
                 <action android:name="android.intent.action.MANAGE_PERMISSION_APPS" />
                 <action android:name="android.intent.action.MANAGE_PERMISSIONS" />
+                <action android:name="android.intent.action.REVIEW_PERMISSION_USAGE" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
@@ -132,6 +133,7 @@
                   android:theme="@android:style/Theme.NoDisplay">
             <intent-filter android:priority="1">
                 <action android:name="com.android.permissioncontroller.settingssearch.action.MANAGE_PERMISSION_APPS" />
+                <action android:name="com.android.permissioncontroller.settingssearch.action.REVIEW_PERMISSION_USAGE" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
@@ -160,6 +162,17 @@
                   android:excludeFromRecents="true"
                   android:theme="@style/PermissionDialog.FilterTouches" />
 
+        <activity android:name="com.android.permissioncontroller.permission.debug.ReviewOngoingUsageActivity"
+                  android:excludeFromRecents="true"
+                  android:theme="@style/PermissionDialog"
+                  android:launchMode="singleInstance"
+                  android:permission="android.permission.GRANT_RUNTIME_PERMISSIONS" >
+            <intent-filter android:priority="1">
+                <action android:name="android.intent.action.REVIEW_ONGOING_PERMISSION_USAGE" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="com.android.permissioncontroller.permission.ui.ReviewAccessibilityServicesActivity"
                   android:excludeFromRecents="true"
                   android:theme="@style/PermissionDialog.FilterTouches"
diff --git a/res/layout/ongoing_usage_dialog_content.xml b/res/layout/ongoing_usage_dialog_content.xml
new file mode 100644
index 0000000..219b4f9
--- /dev/null
+++ b/res/layout/ongoing_usage_dialog_content.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+            android:id="@+id/container"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            style="@style/PermissionUsageDialogContainerScrollView">
+
+    <LinearLayout
+        android:id="@+id/dialog_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        style="@style/PermissionUsageDialogContainerLayout">
+
+        <TextView
+            android:id="@+id/title"
+            style="@style/PermissionUsageDialogTitle"/>
+
+        <LinearLayout
+            android:id="@+id/items_container"
+            style="@style/PermissionUsageDialogItemsContainer"/>
+
+    </LinearLayout>
+
+</ScrollView>
diff --git a/res/layout/ongoing_usage_dialog_item.xml b/res/layout/ongoing_usage_dialog_item.xml
new file mode 100644
index 0000000..37ab9b4
--- /dev/null
+++ b/res/layout/ongoing_usage_dialog_item.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              style="@style/PermissionUsageDialogItemContainer">
+
+    <FrameLayout
+        style="@style/PermissionUsageDialogItemIconFrame">
+
+        <ImageView
+            android:id="@+id/app_icon"
+            style="@style/PermissionUsageDialogItemIcon"/>
+
+    </FrameLayout>
+
+    <TextView
+        android:id="@+id/app_name"
+        style="@style/PermissionUsageDialogItemAppName"/>
+
+    <LinearLayout
+        android:id="@+id/icons"
+        style="@style/PermissionUsageDialogItemIconsContainer"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9180ca9..a0ad216 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -740,6 +740,21 @@
     <!-- Label for the button to set an application as the default application [CHAR LIMIT=20] -->
     <string name="request_role_set_as_default">Set as default</string>
 
+    <!-- Action for accepting the Ongoing usage dialog [CHAR LIMIT=10]-->
+    <string name="ongoing_usage_dialog_ok">Got it</string>
+
+    <!-- Action on Ongoing usage dialog to open the Privacy settings. [CHAR LIMIT=23]-->
+    <string name="ongoing_usage_dialog_open_settings">Privacy settings</string>
+
+    <!-- Title for Ongoing usage dialog title when multiple apps are using app ops [CHAR LIMIT=NONE] -->
+    <string name="ongoing_usage_dialog_title">Apps using your <xliff:g id="types_list" example="camera( and location)">%s</xliff:g></string>
+
+    <!-- Separator for permissions. Include spaces before and after if needed [CHAR LIMIT=10] -->
+    <string name="ongoing_usage_dialog_separator">,\u0020</string>
+
+    <!-- Separator for permissions, before last type. Include spaces before and after if needed [CHAR LIMIT=10] -->
+    <string name="ongoing_usage_dialog_last_separator">\u0020and\u0020</string>
+
     <!-- Keyword in the Settings app's search functionality that can be used to find links to the default app management screens [CHAR LIMIT=none] -->
     <string name="default_app_search_keyword">default apps</string>
 
diff --git a/src/com/android/permissioncontroller/permission/debug/AppPermissionUsage.java b/src/com/android/permissioncontroller/permission/debug/AppPermissionUsage.java
new file mode 100644
index 0000000..e168ee8
--- /dev/null
+++ b/src/com/android/permissioncontroller/permission/debug/AppPermissionUsage.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2020 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.debug;
+
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.HistoricalOp;
+import android.app.AppOpsManager.HistoricalPackageOps;
+import android.app.AppOpsManager.OpEntry;
+import android.app.AppOpsManager.PackageOps;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.permissioncontroller.permission.model.AppPermissionGroup;
+import com.android.permissioncontroller.permission.model.Permission;
+import com.android.permissioncontroller.permission.model.legacy.PermissionApps.PermissionApp;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * Stats for permission usage of an app. This data is for a given time period,
+ * i.e. does not contain the full history.
+ */
+public final class AppPermissionUsage {
+    private final @NonNull List<GroupUsage> mGroupUsages = new ArrayList<>();
+    private final @NonNull PermissionApp mPermissionApp;
+
+    private AppPermissionUsage(@NonNull PermissionApp permissionApp,
+            @NonNull List<AppPermissionGroup> groups, @Nullable PackageOps lastUsage,
+            @Nullable HistoricalPackageOps historicalUsage) {
+        mPermissionApp = permissionApp;
+        final int groupCount = groups.size();
+        for (int i = 0; i < groupCount; i++) {
+            final AppPermissionGroup group = groups.get(i);
+            mGroupUsages.add(new GroupUsage(group, lastUsage, historicalUsage));
+        }
+    }
+
+    public @NonNull PermissionApp getApp() {
+        return mPermissionApp;
+    }
+
+    public @NonNull String getPackageName() {
+        return mPermissionApp.getPackageName();
+    }
+
+    public int getUid() {
+        return mPermissionApp.getUid();
+    }
+
+    public long getLastAccessTime() {
+        long lastAccessTime = 0;
+        final int permissionCount = mGroupUsages.size();
+        for (int i = 0; i < permissionCount; i++) {
+            final GroupUsage groupUsage = mGroupUsages.get(i);
+            lastAccessTime = Math.max(lastAccessTime, groupUsage.getLastAccessTime());
+        }
+        return lastAccessTime;
+    }
+
+    public long getAccessCount() {
+        long accessCount = 0;
+        final int permissionCount = mGroupUsages.size();
+        for (int i = 0; i < permissionCount; i++) {
+            final GroupUsage permission = mGroupUsages.get(i);
+            accessCount += permission.getAccessCount();
+        }
+        return accessCount;
+    }
+
+    public @NonNull List<GroupUsage> getGroupUsages() {
+        return mGroupUsages;
+    }
+
+    /**
+     * Stats for permission usage of a permission group. This data is for a
+     * given time period, i.e. does not contain the full history.
+     */
+    public static class GroupUsage {
+        private final @NonNull AppPermissionGroup mGroup;
+        private final @Nullable PackageOps mLastUsage;
+        private final @Nullable HistoricalPackageOps mHistoricalUsage;
+
+        GroupUsage(@NonNull AppPermissionGroup group, @Nullable PackageOps lastUsage,
+                @Nullable HistoricalPackageOps historicalUsage) {
+            mGroup = group;
+            mLastUsage = lastUsage;
+            mHistoricalUsage = historicalUsage;
+        }
+
+        public long getLastAccessTime() {
+            if (mLastUsage == null) {
+                return 0;
+            }
+            return lastAccessAggregate(
+                    (op) -> op.getLastAccessTime(AppOpsManager.OP_FLAGS_ALL_TRUSTED));
+        }
+
+        public long getLastAccessForegroundTime() {
+            if (mLastUsage == null) {
+                return 0;
+            }
+            return lastAccessAggregate(
+                    (op) -> op.getLastAccessForegroundTime(AppOpsManager.OP_FLAGS_ALL_TRUSTED));
+        }
+
+        public long getLastAccessBackgroundTime() {
+            if (mLastUsage == null) {
+                return 0;
+            }
+            return lastAccessAggregate(
+                    (op) -> op.getLastAccessBackgroundTime(AppOpsManager.OP_FLAGS_ALL_TRUSTED));
+        }
+
+        public long getForegroundAccessCount() {
+            if (mHistoricalUsage == null) {
+                return 0;
+            }
+            return extractAggregate((HistoricalOp op)
+                    -> op.getForegroundAccessCount(AppOpsManager.OP_FLAGS_ALL_TRUSTED));
+        }
+
+        public long getBackgroundAccessCount() {
+            if (mHistoricalUsage == null) {
+                return 0;
+            }
+            return extractAggregate((HistoricalOp op)
+                    -> op.getBackgroundAccessCount(AppOpsManager.OP_FLAGS_ALL_TRUSTED));
+        }
+
+        public long getAccessCount() {
+            if (mHistoricalUsage == null) {
+                return 0;
+            }
+            return extractAggregate((HistoricalOp op) ->
+                    op.getForegroundAccessCount(AppOpsManager.OP_FLAGS_ALL_TRUSTED)
+                            + op.getBackgroundAccessCount(AppOpsManager.OP_FLAGS_ALL_TRUSTED)
+            );
+        }
+
+        public long getAccessDuration() {
+            if (mHistoricalUsage == null) {
+                return 0;
+            }
+            return extractAggregate((HistoricalOp op) ->
+                    op.getForegroundAccessDuration(AppOpsManager.OP_FLAGS_ALL_TRUSTED)
+                            + op.getBackgroundAccessDuration(AppOpsManager.OP_FLAGS_ALL_TRUSTED)
+            );
+        }
+
+        public boolean isRunning() {
+            if (mLastUsage == null) {
+                return false;
+            }
+            final ArrayList<Permission> permissions = mGroup.getPermissions();
+            final int permissionCount = permissions.size();
+            for (int i = 0; i < permissionCount; i++) {
+                final Permission permission = permissions.get(i);
+                final String opName = permission.getAppOp();
+                final List<OpEntry> ops = mLastUsage.getOps();
+                final int opCount = ops.size();
+                for (int j = 0; j < opCount; j++) {
+                    final OpEntry op = ops.get(j);
+                    if (op.getOpStr().equals(opName) && op.isRunning()) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        private long extractAggregate(@NonNull Function<HistoricalOp, Long> extractor) {
+            long aggregate = 0;
+            final ArrayList<Permission> permissions = mGroup.getPermissions();
+            final int permissionCount = permissions.size();
+            for (int i = 0; i < permissionCount; i++) {
+                final Permission permission = permissions.get(i);
+                final String opName = permission.getAppOp();
+                final HistoricalOp historicalOp = mHistoricalUsage.getOp(opName);
+                if (historicalOp != null) {
+                    aggregate += extractor.apply(historicalOp);
+                }
+            }
+            return aggregate;
+        }
+
+        private long lastAccessAggregate(@NonNull Function<OpEntry, Long> extractor) {
+            long aggregate = 0;
+            final ArrayList<Permission> permissions = mGroup.getPermissions();
+            final int permissionCount = permissions.size();
+            for (int permissionNum = 0; permissionNum < permissionCount; permissionNum++) {
+                final Permission permission = permissions.get(permissionNum);
+                final String opName = permission.getAppOp();
+                final List<OpEntry> ops = mLastUsage.getOps();
+                final int opCount = ops.size();
+                for (int opNum = 0; opNum < opCount; opNum++) {
+                    final OpEntry op = ops.get(opNum);
+                    if (op.getOpStr().equals(opName)) {
+                        aggregate = Math.max(aggregate, extractor.apply(op));
+                    }
+                }
+            }
+            return aggregate;
+        }
+
+        public @NonNull AppPermissionGroup getGroup() {
+            return mGroup;
+        }
+    }
+
+    public static class Builder {
+        private final @NonNull List<AppPermissionGroup> mGroups = new ArrayList<>();
+        private final @NonNull PermissionApp mPermissionApp;
+        private @Nullable PackageOps mLastUsage;
+        private @Nullable HistoricalPackageOps mHistoricalUsage;
+
+        public Builder(@NonNull PermissionApp permissionApp) {
+            mPermissionApp = permissionApp;
+        }
+
+        public @NonNull Builder addGroup(@NonNull AppPermissionGroup group) {
+            mGroups.add(group);
+            return this;
+        }
+
+        public @NonNull Builder setLastUsage(@Nullable PackageOps lastUsage) {
+            mLastUsage = lastUsage;
+            return this;
+        }
+
+        public @NonNull Builder setHistoricalUsage(@Nullable HistoricalPackageOps historicalUsage) {
+            mHistoricalUsage = historicalUsage;
+            return this;
+        }
+
+        public @NonNull AppPermissionUsage build() {
+            if (mGroups.isEmpty()) {
+                throw new IllegalStateException("mGroups cannot be empty.");
+            }
+            return new AppPermissionUsage(mPermissionApp, mGroups, mLastUsage, mHistoricalUsage);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/permissioncontroller/permission/debug/PermissionUsages.java b/src/com/android/permissioncontroller/permission/debug/PermissionUsages.java
new file mode 100644
index 0000000..d45164e
--- /dev/null
+++ b/src/com/android/permissioncontroller/permission/debug/PermissionUsages.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2020 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.debug;
+
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.HistoricalOps;
+import android.app.AppOpsManager.HistoricalOpsRequest;
+import android.app.AppOpsManager.HistoricalPackageOps;
+import android.app.AppOpsManager.HistoricalUidOps;
+import android.app.AppOpsManager.PackageOps;
+import android.app.LoaderManager;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.AsyncTaskLoader;
+import android.content.Context;
+import android.content.Loader;
+import android.os.Bundle;
+import android.os.Process;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Pair;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.permissioncontroller.permission.debug.AppPermissionUsage.Builder;
+import com.android.permissioncontroller.permission.model.AppPermissionGroup;
+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;
+import com.android.permissioncontroller.permission.utils.Utils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Loads all permission usages for a set of apps and permission groups.
+ */
+public final class PermissionUsages implements LoaderCallbacks<List<AppPermissionUsage>> {
+    public static final int USAGE_FLAG_LAST = 1 << 0;
+    public static final int USAGE_FLAG_HISTORICAL = 1 << 2;
+
+    private final ArrayList<AppPermissionUsage> mUsages = new ArrayList<>();
+    private final @NonNull Context mContext;
+
+    private static final String KEY_FILTER_UID =  "KEY_FILTER_UID";
+    private static final String KEY_FILTER_PACKAGE_NAME =  "KEY_FILTER_PACKAGE_NAME";
+    private static final String KEY_FILTER_PERMISSION_GROUP =  "KEY_FILTER_PERMISSION_GROUP";
+    private static final String KEY_FILTER_BEGIN_TIME_MILLIS =  "KEY_FILTER_BEGIN_TIME_MILLIS";
+    private static final String KEY_FILTER_END_TIME_MILLIS =  "KEY_FILTER_END_TIME_MILLIS";
+    private static final String KEY_USAGE_FLAGS =  "KEY_USAGE_FLAGS";
+    private static final String KEY_GET_UI_INFO =  "KEY_GET_UI_INFO";
+    private static final String KEY_GET_NON_PLATFORM_PERMISSIONS =
+            "KEY_GET_NON_PLATFORM_PERMISSIONS";
+
+    private @Nullable PermissionsUsagesChangeCallback mCallback;
+
+    public interface PermissionsUsagesChangeCallback {
+        void onPermissionUsagesChanged();
+    }
+
+    public PermissionUsages(@NonNull Context context) {
+        mContext = context;
+    }
+
+    public void load(@Nullable String filterPackageName,
+            @Nullable String[] filterPermissionGroups, long filterBeginTimeMillis,
+            long filterEndTimeMillis, int usageFlags, @NonNull LoaderManager loaderManager,
+            boolean getUiInfo, boolean getNonPlatformPermissions,
+            @NonNull PermissionsUsagesChangeCallback callback, boolean sync) {
+        load(Process.INVALID_UID, filterPackageName, filterPermissionGroups, filterBeginTimeMillis,
+                filterEndTimeMillis, usageFlags, loaderManager, getUiInfo,
+                getNonPlatformPermissions, callback, sync);
+    }
+
+    public void load(int filterUid, @Nullable String filterPackageName,
+            @Nullable String[] filterPermissionGroups, long filterBeginTimeMillis,
+            long filterEndTimeMillis, int usageFlags, @NonNull LoaderManager loaderManager,
+            boolean getUiInfo, boolean getNonPlatformPermissions,
+            @NonNull PermissionsUsagesChangeCallback callback, boolean sync) {
+        mCallback = callback;
+        final Bundle args = new Bundle();
+        args.putInt(KEY_FILTER_UID, filterUid);
+        args.putString(KEY_FILTER_PACKAGE_NAME, filterPackageName);
+        args.putStringArray(KEY_FILTER_PERMISSION_GROUP, filterPermissionGroups);
+        args.putLong(KEY_FILTER_BEGIN_TIME_MILLIS, filterBeginTimeMillis);
+        args.putLong(KEY_FILTER_END_TIME_MILLIS, filterEndTimeMillis);
+        args.putInt(KEY_USAGE_FLAGS, usageFlags);
+        args.putBoolean(KEY_GET_UI_INFO, getUiInfo);
+        args.putBoolean(KEY_GET_NON_PLATFORM_PERMISSIONS, getNonPlatformPermissions);
+        if (sync) {
+            final UsageLoader loader = new UsageLoader(mContext, args);
+            final List<AppPermissionUsage> usages = loader.loadInBackground();
+            onLoadFinished(loader, usages);
+        } else {
+            loaderManager.restartLoader(1, args, this);
+        }
+    }
+
+    @Override
+    public Loader<List<AppPermissionUsage>> onCreateLoader(int id, Bundle args) {
+        return new UsageLoader(mContext, args);
+    }
+
+    @Override
+    public void onLoadFinished(@NonNull Loader<List<AppPermissionUsage>> loader,
+            List<AppPermissionUsage> usages) {
+        mUsages.clear();
+        mUsages.addAll(usages);
+        if (mCallback != null) {
+            mCallback.onPermissionUsagesChanged();
+        }
+    }
+
+    @Override
+    public void onLoaderReset(@NonNull Loader<List<AppPermissionUsage>> loader) {
+        mUsages.clear();
+        mCallback.onPermissionUsagesChanged();
+    }
+
+    public @NonNull List<AppPermissionUsage> getUsages() {
+        return mUsages;
+    }
+
+    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;
+        private @Nullable String[] mFilterPermissionGroups;
+        private final long mFilterBeginTimeMillis;
+        private final long mFilterEndTimeMillis;
+        private final int mUsageFlags;
+        private final boolean mGetUiInfo;
+        private final boolean mGetNonPlatformPermissions;
+
+        UsageLoader(@NonNull Context context, @NonNull Bundle args) {
+            super(context);
+            mFilterUid = args.getInt(KEY_FILTER_UID);
+            mFilterPackageName = args.getString(KEY_FILTER_PACKAGE_NAME);
+            mFilterPermissionGroups = args.getStringArray(KEY_FILTER_PERMISSION_GROUP);
+            mFilterBeginTimeMillis = args.getLong(KEY_FILTER_BEGIN_TIME_MILLIS);
+            mFilterEndTimeMillis = args.getLong(KEY_FILTER_END_TIME_MILLIS);
+            mUsageFlags = args.getInt(KEY_USAGE_FLAGS);
+            mGetUiInfo = args.getBoolean(KEY_GET_UI_INFO);
+            mGetNonPlatformPermissions = args.getBoolean(KEY_GET_NON_PLATFORM_PERMISSIONS);
+        }
+
+        @Override
+        protected void onStartLoading() {
+            forceLoad();
+        }
+
+        @Override
+        public @NonNull List<AppPermissionUsage> loadInBackground() {
+            final List<PermissionGroup> groups = PermissionGroups.getPermissionGroups(
+                    getContext(), this::isLoadInBackgroundCanceled, mGetUiInfo,
+                    mGetNonPlatformPermissions, mFilterPermissionGroups, mFilterPackageName);
+            if (!Utils.isPermissionUsageIconEnabled()) {
+                return Collections.emptyList();
+            }
+
+            if (groups.isEmpty()) {
+                return Collections.emptyList();
+            }
+
+            final List<AppPermissionUsage> usages = new ArrayList<>();
+            final ArraySet<String> opNames = new ArraySet<>();
+            final ArrayMap<Pair<Integer, String>, AppPermissionUsage.Builder> usageBuilders =
+                    new ArrayMap<>();
+
+            final int groupCount = groups.size();
+            for (int groupIdx = 0; groupIdx < groupCount; groupIdx++) {
+                final PermissionGroup group = groups.get(groupIdx);
+                // Filter out third party permissions
+                if (!group.getDeclaringPackage().equals(Utils.OS_PKG)) {
+                    continue;
+                }
+
+                groups.add(group);
+
+                final List<PermissionApp> permissionApps = group.getPermissionApps().getApps();
+                final int appCount = permissionApps.size();
+                for (int appIdx = 0; appIdx < appCount; appIdx++) {
+                    final PermissionApp permissionApp = permissionApps.get(appIdx);
+                    if (mFilterUid != Process.INVALID_UID
+                            && permissionApp.getAppInfo().uid != mFilterUid) {
+                        continue;
+                    }
+
+                    final AppPermissionGroup appPermGroup = permissionApp.getPermissionGroup();
+                    if (!Utils.shouldShowPermission(getContext(), appPermGroup)) {
+                        continue;
+                    }
+                    final Pair<Integer, String> usageKey = Pair.create(permissionApp.getUid(),
+                            permissionApp.getPackageName());
+                    AppPermissionUsage.Builder usageBuilder = usageBuilders.get(usageKey);
+                    if (usageBuilder == null) {
+                        usageBuilder = new Builder(permissionApp);
+                        usageBuilders.put(usageKey, usageBuilder);
+                    }
+                    usageBuilder.addGroup(appPermGroup);
+                    final List<Permission> permissions = appPermGroup.getPermissions();
+                    final int permCount = permissions.size();
+                    for (int permIdx = 0; permIdx < permCount; permIdx++) {
+                        final Permission permission = permissions.get(permIdx);
+                        final String opName = permission.getAppOp();
+                        if (opName != null) {
+                            opNames.add(opName);
+                        }
+                    }
+                }
+            }
+
+            if (usageBuilders.isEmpty()) {
+                return Collections.emptyList();
+            }
+
+            final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
+
+            // Get last usage data and put in a map for a quick lookup.
+            final ArrayMap<Pair<Integer, String>, PackageOps> lastUsages =
+                    new ArrayMap<>(usageBuilders.size());
+            final String[] opNamesArray = opNames.toArray(new String[opNames.size()]);
+            if ((mUsageFlags & USAGE_FLAG_LAST) != 0) {
+                final List<PackageOps> usageOps;
+                if (mFilterPackageName != null || mFilterUid != Process.INVALID_UID) {
+                    usageOps = appOpsManager.getOpsForPackage(mFilterUid, mFilterPackageName,
+                            opNamesArray);
+                } else {
+                    usageOps = appOpsManager.getPackagesForOps(opNamesArray);
+                }
+                if (usageOps != null && !usageOps.isEmpty()) {
+                    final int usageOpsCount = usageOps.size();
+                    for (int i = 0; i < usageOpsCount; i++) {
+                        final PackageOps usageOp = usageOps.get(i);
+                        lastUsages.put(Pair.create(usageOp.getUid(), usageOp.getPackageName()),
+                                usageOp);
+                    }
+                }
+            }
+
+            if (isLoadInBackgroundCanceled()) {
+                return Collections.emptyList();
+            }
+
+            // Get historical usage data and put in a map for a quick lookup
+            final ArrayMap<Pair<Integer, String>, HistoricalPackageOps> historicalUsages =
+                    new ArrayMap<>(usageBuilders.size());
+            if ((mUsageFlags & USAGE_FLAG_HISTORICAL) != 0) {
+                final AtomicReference<HistoricalOps> historicalOpsRef = new AtomicReference<>();
+                final CountDownLatch latch = new CountDownLatch(1);
+                final HistoricalOpsRequest request = new HistoricalOpsRequest.Builder(
+                        mFilterBeginTimeMillis, mFilterEndTimeMillis)
+                        .setUid(mFilterUid)
+                        .setPackageName(mFilterPackageName)
+                        .setOpNames(new ArrayList<>(opNames))
+                        .setFlags(AppOpsManager.OP_FLAGS_ALL_TRUSTED)
+                        .build();
+                appOpsManager.getHistoricalOps(request, Runnable::run,
+                        (HistoricalOps ops) -> {
+                            historicalOpsRef.set(ops);
+                            latch.countDown();
+                        });
+                try {
+                    latch.await(5, TimeUnit.DAYS);
+                } catch (InterruptedException ignored) { }
+
+                final HistoricalOps historicalOps = historicalOpsRef.get();
+                if (historicalOps != null) {
+                    final int uidCount = historicalOps.getUidCount();
+                    for (int i = 0; i < uidCount; i++) {
+                        final HistoricalUidOps uidOps = historicalOps.getUidOpsAt(i);
+                        final int packageCount = uidOps.getPackageCount();
+                        for (int j = 0; j < packageCount; j++) {
+                            final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(j);
+                            historicalUsages.put(
+                                    Pair.create(uidOps.getUid(), packageOps.getPackageName()),
+                                    packageOps);
+                        }
+                    }
+                }
+            }
+
+            // Construct the historical usages based on data we fetched
+            final int builderCount = usageBuilders.size();
+            for (int i = 0; i < builderCount; i++) {
+                final Pair<Integer, String> key = usageBuilders.keyAt(i);
+                final Builder usageBuilder = usageBuilders.valueAt(i);
+                final PackageOps lastUsage = lastUsages.get(key);
+                usageBuilder.setLastUsage(lastUsage);
+                final HistoricalPackageOps historicalUsage = historicalUsages.get(key);
+                usageBuilder.setHistoricalUsage(historicalUsage);
+                usages.add(usageBuilder.build());
+            }
+
+            return usages;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/permissioncontroller/permission/debug/ReviewOngoingUsageActivity.java b/src/com/android/permissioncontroller/permission/debug/ReviewOngoingUsageActivity.java
new file mode 100644
index 0000000..c48b10f
--- /dev/null
+++ b/src/com/android/permissioncontroller/permission/debug/ReviewOngoingUsageActivity.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 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.debug;
+
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.MenuItem;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.FragmentActivity;
+
+import com.android.permissioncontroller.DeviceUtils;
+
+/**
+ * A dialog listing the currently uses of camera, microphone, and location.
+ */
+public final class ReviewOngoingUsageActivity extends FragmentActivity {
+
+    // Number of milliseconds in the past to look for accesses if nothing was specified.
+    private static final long DEFAULT_MILLIS = 5000;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+
+        long numMillis = getIntent().getLongExtra(Intent.EXTRA_DURATION_MILLIS, DEFAULT_MILLIS);
+        getSupportFragmentManager().beginTransaction().replace(android.R.id.content,
+                ReviewOngoingUsageFragment.newInstance(numMillis)).commit();
+    }
+
+
+    @Override
+    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home:
+                // in automotive mode, there's no system wide back button, so need to add that
+                if (DeviceUtils.isAuto(this)) {
+                    onBackPressed();
+                } else {
+                    finish();
+                }
+                return true;
+            default:
+                return super.onOptionsItemSelected(item);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/permissioncontroller/permission/debug/ReviewOngoingUsageFragment.java b/src/com/android/permissioncontroller/permission/debug/ReviewOngoingUsageFragment.java
new file mode 100644
index 0000000..0174ec4
--- /dev/null
+++ b/src/com/android/permissioncontroller/permission/debug/ReviewOngoingUsageFragment.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2020 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.debug;
+
+import static android.Manifest.permission_group.CAMERA;
+import static android.Manifest.permission_group.LOCATION;
+import static android.Manifest.permission_group.MICROPHONE;
+
+import static com.android.permissioncontroller.PermissionControllerStatsLog.PRIVACY_INDICATORS_INTERACTED;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.PRIVACY_INDICATORS_INTERACTED__TYPE__DIALOG_DISMISS;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.PRIVACY_INDICATORS_INTERACTED__TYPE__DIALOG_LINE_ITEM;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.PRIVACY_INDICATORS_INTERACTED__TYPE__DIALOG_PRIVACY_SETTINGS;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.ArraySet;
+import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.preference.PreferenceFragmentCompat;
+
+import com.android.permissioncontroller.PermissionControllerStatsLog;
+import com.android.permissioncontroller.R;
+import com.android.permissioncontroller.permission.debug.AppPermissionUsage.GroupUsage;
+import com.android.permissioncontroller.permission.model.legacy.PermissionApps;
+import com.android.permissioncontroller.permission.model.legacy.PermissionApps.PermissionApp;
+import com.android.permissioncontroller.permission.utils.Utils;
+
+import java.text.Collator;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A dialog listing the currently uses of camera, microphone, and location.
+ */
+public class ReviewOngoingUsageFragment extends PreferenceFragmentCompat {
+
+    private @NonNull PermissionUsages mPermissionUsages;
+    private @Nullable AlertDialog mDialog;
+    private long mStartTime;
+
+    /**
+     * @return A new {@link ReviewOngoingUsageFragment}
+     */
+    public static ReviewOngoingUsageFragment newInstance(long numMillis) {
+        ReviewOngoingUsageFragment fragment = new ReviewOngoingUsageFragment();
+        Bundle arguments = new Bundle();
+        arguments.putLong(Intent.EXTRA_DURATION_MILLIS, numMillis);
+        fragment.setArguments(arguments);
+        return fragment;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (!Utils.isPermissionUsageIconEnabled()) {
+            getActivity().finish();
+            return;
+        }
+
+        long numMillis = getArguments().getLong(Intent.EXTRA_DURATION_MILLIS);
+
+        mPermissionUsages = new PermissionUsages(getActivity());
+        mStartTime = Math.max(System.currentTimeMillis() - numMillis, Instant.EPOCH.toEpochMilli());
+        mPermissionUsages.load(null, new String[]{CAMERA, LOCATION, MICROPHONE}, mStartTime,
+                Long.MAX_VALUE, PermissionUsages.USAGE_FLAG_LAST, getActivity().getLoaderManager(),
+                false, false, this::onPermissionUsagesLoaded, false);
+    }
+
+    private void onPermissionUsagesLoaded() {
+        if (getActivity() == null) {
+            return;
+        }
+
+        List<AppPermissionUsage> appPermissionUsages = mPermissionUsages.getUsages();
+
+        List<Pair<AppPermissionUsage, List<GroupUsage>>> usages = new ArrayList<>();
+        ArrayList<PermissionApp> permApps = new ArrayList<>();
+        int numApps = appPermissionUsages.size();
+        for (int appNum = 0; appNum < numApps; appNum++) {
+            AppPermissionUsage appUsage = appPermissionUsages.get(appNum);
+
+            List<GroupUsage> usedGroups = new ArrayList<>();
+            List<GroupUsage> appGroups = appUsage.getGroupUsages();
+            int numGroups = appGroups.size();
+            for (int groupNum = 0; groupNum < numGroups; groupNum++) {
+                GroupUsage groupUsage = appGroups.get(groupNum);
+                String groupName = groupUsage.getGroup().getName();
+
+                if (groupUsage.getLastAccessTime() < mStartTime && !groupUsage.isRunning()) {
+                    continue;
+                }
+                if (!Utils.isGroupOrBgGroupUserSensitive(groupUsage.getGroup())) {
+                    continue;
+                }
+
+                usedGroups.add(appGroups.get(groupNum));
+            }
+
+            if (!usedGroups.isEmpty()) {
+                usages.add(Pair.create(appUsage, usedGroups));
+                permApps.add(appUsage.getApp());
+            }
+        }
+
+        if (usages.isEmpty()) {
+            getActivity().finish();
+            return;
+        }
+
+        new PermissionApps.AppDataLoader(getActivity(), () -> showDialog(usages))
+                .execute(permApps.toArray(new PermissionApps.PermissionApp[permApps.size()]));
+    }
+
+    private void showDialog(@NonNull List<Pair<AppPermissionUsage, List<GroupUsage>>> usages) {
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+                .setView(createDialogView(usages))
+                .setPositiveButton(R.string.ongoing_usage_dialog_ok, (dialog, which) ->
+                        PermissionControllerStatsLog.write(PRIVACY_INDICATORS_INTERACTED,
+                                PRIVACY_INDICATORS_INTERACTED__TYPE__DIALOG_DISMISS, null))
+                .setOnDismissListener((dialog) -> getActivity().finish());
+        setNeutralButton(builder);
+        mDialog = builder.create();
+        mDialog.show();
+    }
+
+    protected void setNeutralButton(AlertDialog.Builder builder) {
+        builder.setNeutralButton(R.string.ongoing_usage_dialog_open_settings, (dialog, which) -> {
+            PermissionControllerStatsLog.write(PRIVACY_INDICATORS_INTERACTED,
+                    PRIVACY_INDICATORS_INTERACTED__TYPE__DIALOG_PRIVACY_SETTINGS, null);
+            startActivity(new Intent(Settings.ACTION_PRIVACY_SETTINGS).putExtra(
+                    Intent.EXTRA_DURATION_MILLIS, TimeUnit.MINUTES.toMillis(1)));
+        });
+    }
+
+    private @NonNull View createDialogView(
+            @NonNull List<Pair<AppPermissionUsage, List<GroupUsage>>> usages) {
+        Context context = getActivity();
+        LayoutInflater inflater = LayoutInflater.from(context);
+        View contentView = inflater.inflate(R.layout.ongoing_usage_dialog_content, null);
+        ViewGroup appsList = contentView.requireViewById(R.id.items_container);
+
+        // Compute all of the permission group labels that were used.
+        ArraySet<String> usedGroups = new ArraySet<>();
+        int numUsages = usages.size();
+        for (int usageNum = 0; usageNum < numUsages; usageNum++) {
+            List<GroupUsage> groups = usages.get(usageNum).second;
+            int numGroups = groups.size();
+            for (int groupNum = 0; groupNum < numGroups; groupNum++) {
+                usedGroups.add(groups.get(groupNum).getGroup().getLabel().toString().toLowerCase());
+            }
+        }
+
+        // Add the layout for each app.
+        for (int usageNum = 0; usageNum < numUsages; usageNum++) {
+            Pair<AppPermissionUsage, List<GroupUsage>> usage = usages.get(usageNum);
+            PermissionApp app = usage.first.getApp();
+            List<GroupUsage> groups = usage.second;
+
+            View itemView = inflater.inflate(R.layout.ongoing_usage_dialog_item, appsList, false);
+
+            ((TextView) itemView.requireViewById(R.id.app_name)).setText(app.getLabel());
+            ((ImageView) itemView.requireViewById(R.id.app_icon)).setImageDrawable(app.getIcon());
+
+            // Add the icons for the groups this app used as long as multiple groups were used by
+            // some app.
+            if (usedGroups.size() > 1) {
+                ViewGroup iconFrame = itemView.requireViewById(R.id.icons);
+                int numGroups = usages.get(usageNum).second.size();
+                for (int groupNum = 0; groupNum < numGroups; groupNum++) {
+                    ViewGroup group = (ViewGroup) inflater.inflate(R.layout.image_view, null);
+                    ((ImageView) group.requireViewById(R.id.icon)).setImageDrawable(
+                            Utils.applyTint(context, groups.get(groupNum).getGroup().getIconResId(),
+                                    android.R.attr.colorControlNormal));
+                    iconFrame.addView(group);
+                }
+                iconFrame.setVisibility(View.VISIBLE);
+            }
+
+            itemView.setOnClickListener((v) -> {
+                String packageName = app.getPackageName();
+                PermissionControllerStatsLog.write(PRIVACY_INDICATORS_INTERACTED,
+                        PRIVACY_INDICATORS_INTERACTED__TYPE__DIALOG_LINE_ITEM, packageName);
+                UserHandle user = UserHandle.getUserHandleForUid(app.getUid());
+                Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+                intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+                intent.putExtra(Intent.EXTRA_USER, user);
+                context.startActivity(intent);
+                mDialog.dismiss();
+            });
+
+            appsList.addView(itemView);
+        }
+
+        // Set the title of the dialog based on all of the permissions used.
+        StringBuilder titleBuilder = new StringBuilder();
+        int numGroups = usedGroups.size();
+        List<String> sortedGroups = new ArrayList<>(usedGroups);
+        Collator collator = Collator.getInstance(
+                getResources().getConfiguration().getLocales().get(0));
+        sortedGroups.sort(collator);
+        for (int i = 0; i < numGroups; i++) {
+            titleBuilder.append(sortedGroups.get(i));
+            if (i < numGroups - 2) {
+                titleBuilder.append(getString(R.string.ongoing_usage_dialog_separator));
+            } else if (i < numGroups - 1) {
+                titleBuilder.append(getString(R.string.ongoing_usage_dialog_last_separator));
+            }
+        }
+
+        ((TextView) contentView.requireViewById(R.id.title)).setText(
+                getString(R.string.ongoing_usage_dialog_title, titleBuilder.toString()));
+
+        return contentView;
+    }
+
+    @Override
+    public void onCreatePreferences(Bundle bundle, String s) {
+        // empty
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/permissioncontroller/permission/model/legacy/PermissionApps.java b/src/com/android/permissioncontroller/permission/model/legacy/PermissionApps.java
index e837ade..e493e08 100644
--- a/src/com/android/permissioncontroller/permission/model/legacy/PermissionApps.java
+++ b/src/com/android/permissioncontroller/permission/model/legacy/PermissionApps.java
@@ -421,6 +421,19 @@
             return mAppPermissionGroup;
         }
 
+        /**
+         * Load this app's label and icon if they were not previously loaded.
+         *
+         * @param appDataCache the cache of already-loaded labels and icons.
+         */
+        public void loadLabelAndIcon(@NonNull AppDataCache appDataCache) {
+            if (mInfo.packageName.equals(mLabel) || mIcon == null) {
+                Pair<String, Drawable> appData = appDataCache.getAppData(getUid(), mInfo);
+                mLabel = appData.first;
+                mIcon = appData.second;
+            }
+        }
+
         @Override
         public int compareTo(PermissionApp another) {
             final int result = mLabel.compareTo(another.mLabel);
@@ -520,4 +533,33 @@
     public interface Callback {
         void onPermissionsLoaded(PermissionApps permissionApps);
     }
+
+    /**
+     * Class used to asynchronously load apps' labels and icons.
+     */
+    public static class AppDataLoader extends AsyncTask<PermissionApp, Void, Void> {
+
+        private final Context mContext;
+        private final Runnable mCallback;
+
+        public AppDataLoader(Context context, Runnable callback) {
+            mContext = context;
+            mCallback = callback;
+        }
+
+        @Override
+        protected Void doInBackground(PermissionApp... args) {
+            AppDataCache appDataCache = new AppDataCache(mContext.getPackageManager(), mContext);
+            int numArgs = args.length;
+            for (int i = 0; i < numArgs; i++) {
+                args[i].loadLabelAndIcon(appDataCache);
+            }
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            mCallback.run();
+        }
+    }
 }
diff --git a/src/com/android/permissioncontroller/permission/model/legacy/PermissionGroup.java b/src/com/android/permissioncontroller/permission/model/legacy/PermissionGroup.java
index da1039d..6b951c9 100644
--- a/src/com/android/permissioncontroller/permission/model/legacy/PermissionGroup.java
+++ b/src/com/android/permissioncontroller/permission/model/legacy/PermissionGroup.java
@@ -78,6 +78,13 @@
         return mGranted;
     }
 
+    /**
+     * @return The PermissionApps object for this permission group.
+     */
+    public PermissionApps getPermissionApps() {
+        return mPermApps;
+    }
+
     @Override
     public int compareTo(PermissionGroup another) {
         return mLabel.toString().compareTo(another.mLabel.toString());
diff --git a/src/com/android/permissioncontroller/permission/utils/Utils.java b/src/com/android/permissioncontroller/permission/utils/Utils.java
index 65fdd59..01123ba 100644
--- a/src/com/android/permissioncontroller/permission/utils/Utils.java
+++ b/src/com/android/permissioncontroller/permission/utils/Utils.java
@@ -1141,4 +1141,15 @@
             return false;
         }
     }
+
+    /**
+     * Whether the Permissions Hub is enabled.
+     *
+     * @return whether the Permissions Hub is enabled.
+     */
+    public static boolean isPermissionUsageIconEnabled() {
+        return true;
+        //return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+        //        PROPERTY_PERMISSIONS_HUB_ENABLED, false);
+    }
 }