Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1ce1a950 authored by Svet Ganov's avatar Svet Ganov
Browse files

Update PermissionController to use the new historical ops API

The historical ops API changed and we had to update the code
to use the new APIs. The new APIs now read history from the
disk which would be a problem with the current impl where we
make multiple calls into the app ops manager - one per app.
It is more efficient to make a single call for all data we
care about. This change adds a new mechanism to load the data
once and off the main thread as the IPC would hit the disk.

Test: manual

bug:111061782

Change-Id: I86113929fdd4a80fe620ed3eccd591712de30f47
parent 6fce37d8
Loading
Loading
Loading
Loading
+2 −1
Original line number Original line Diff line number Diff line
@@ -19,7 +19,8 @@
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    android:orientation="vertical"
    android:id="@+id/app_permission_root">


    <include layout="@layout/button_header" />
    <include layout="@layout/button_header" />


+2 −63
Original line number Original line Diff line number Diff line
@@ -54,12 +54,8 @@ import com.android.packageinstaller.permission.utils.LocationUtils;
import com.android.packageinstaller.permission.utils.Utils;
import com.android.packageinstaller.permission.utils.Utils;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.R;


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.Collator;
import java.text.Collator;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.List;


/**
/**
@@ -97,7 +93,6 @@ public final class AppPermissionGroup implements Comparable<AppPermissionGroup>
    private final ArrayMap<String, Permission> mPermissions = new ArrayMap<>();
    private final ArrayMap<String, Permission> mPermissions = new ArrayMap<>();
    private final String mIconPkg;
    private final String mIconPkg;
    private final int mIconResId;
    private final int mIconResId;
    private final List<AppPermissionUsage> mAppPermissionUsages;


    /** Delay changes until {@link #persistChanges} is called */
    /** Delay changes until {@link #persistChanges} is called */
    private final boolean mDelayChanges;
    private final boolean mDelayChanges;
@@ -206,7 +201,6 @@ public final class AppPermissionGroup implements Comparable<AppPermissionGroup>
                groupInfo.packageName, groupLabel, loadGroupDescription(context, groupInfo),
                groupInfo.packageName, groupLabel, loadGroupDescription(context, groupInfo),
                getRequest(groupInfo), getRequestDetail(groupInfo), getBackgroundRequest(groupInfo),
                getRequest(groupInfo), getRequestDetail(groupInfo), getBackgroundRequest(groupInfo),
                getBackgroundRequestDetail(groupInfo), groupInfo.packageName, groupInfo.icon,
                getBackgroundRequestDetail(groupInfo), groupInfo.packageName, groupInfo.icon,
                getAppUsages(context, packageInfo, groupInfo.name, groupLabel, permissionNames),
                userHandle, delayChanges);
                userHandle, delayChanges);


        // Parse and create permissions reqested by the app
        // Parse and create permissions reqested by the app
@@ -306,14 +300,12 @@ public final class AppPermissionGroup implements Comparable<AppPermissionGroup>


            if (permission.isBackgroundPermission()) {
            if (permission.isBackgroundPermission()) {
                if (group.getBackgroundPermissions() == null) {
                if (group.getBackgroundPermissions() == null) {
                    List<AppPermissionUsage> usages = getAppUsages(context, group.getApp(),
                            group.getName(), group.getLabel(), new String[] { group.getName() });
                    group.mBackgroundPermissions = new AppPermissionGroup(group.mContext,
                    group.mBackgroundPermissions = new AppPermissionGroup(group.mContext,
                            group.getApp(), group.getName(), group.getDeclaringPackage(),
                            group.getApp(), group.getName(), group.getDeclaringPackage(),
                            group.getLabel(), group.getDescription(), group.getRequest(),
                            group.getLabel(), group.getDescription(), group.getRequest(),
                            group.getRequestDetail(), group.getBackgroundRequest(),
                            group.getRequestDetail(), group.getBackgroundRequest(),
                            group.getBackgroundRequestDetail(), group.getIconPkg(),
                            group.getBackgroundRequestDetail(), group.getIconPkg(),
                            group.getIconResId(), usages, group.getUser(), delayChanges);
                            group.getIconResId(), group.getUser(), delayChanges);
                }
                }


                group.getBackgroundPermissions().addPermission(permission);
                group.getBackgroundPermissions().addPermission(permission);
@@ -365,53 +357,11 @@ public final class AppPermissionGroup implements Comparable<AppPermissionGroup>
        return description;
        return description;
    }
    }


    private static List<AppPermissionUsage> getAppUsages(Context context, PackageInfo packageInfo,
            String groupName, CharSequence groupLabel, String[] permissionNames) {
        try {
            AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(
                    AppOpsManager.class);

            // Get the appops for the given permissions.  Note that this does not get the appops
            // whose switch is this permission group.
            // TODO: Use the real API instead of reflection once the API is finalized.
            int[] ops = new int[permissionNames.length];
            Method permissionToOpCodeMethod = AppOpsManager.class.getMethod("permissionToOpCode",
                    String.class);
            for (int i = 0, numPerms = permissionNames.length; i < numPerms; i++) {
                ops[i] = (int) permissionToOpCodeMethod.invoke(null, permissionNames[i]);
            }
            List<AppOpsManager.PackageOps> pkgOps = appOpsManager.getOpsForPackage(
                    packageInfo.applicationInfo.uid, packageInfo.packageName, ops);
            if (pkgOps == null) {
                return Collections.emptyList();
            }

            // Convert each single appop into an AppPermissionUsage.
            List<AppPermissionUsage> appPermissionUsages = new ArrayList<>();
            int numPkgOps = pkgOps.size();
            for (int packageNum = 0; packageNum < numPkgOps; packageNum++) {
                AppOpsManager.PackageOps pkgOp = pkgOps.get(packageNum);
                List<AppOpsManager.OpEntry> curOps = pkgOp.getOps();
                int numOps = curOps.size();
                for (int opNum = 0; opNum < numOps; opNum++) {
                    AppOpsManager.OpEntry op = curOps.get(opNum);
                    AppPermissionUsage appPermissionUsage = new AppPermissionUsage(pkgOp, op,
                            groupName, groupLabel);
                    appPermissionUsages.add(appPermissionUsage);
                }
            }
            appPermissionUsages.sort(Comparator.comparing(AppPermissionUsage::getTime).reversed());
            return appPermissionUsages;
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            return Collections.emptyList();
        }
    }

    private AppPermissionGroup(Context context, PackageInfo packageInfo, String name,
    private AppPermissionGroup(Context context, PackageInfo packageInfo, String name,
            String declaringPackage, CharSequence label, CharSequence description,
            String declaringPackage, CharSequence label, CharSequence description,
            @StringRes int request, @StringRes int requestDetail,
            @StringRes int request, @StringRes int requestDetail,
            @StringRes int backgroundRequest, @StringRes int backgroundRequestDetail,
            @StringRes int backgroundRequest, @StringRes int backgroundRequestDetail,
            String iconPkg, int iconResId, List<AppPermissionUsage> appPermissionUsages,
            String iconPkg, int iconResId,
            UserHandle userHandle, boolean delayChanges) {
            UserHandle userHandle, boolean delayChanges) {
        mContext = context;
        mContext = context;
        mUserHandle = userHandle;
        mUserHandle = userHandle;
@@ -440,7 +390,6 @@ public final class AppPermissionGroup implements Comparable<AppPermissionGroup>
            mIconPkg = context.getPackageName();
            mIconPkg = context.getPackageName();
            mIconResId = R.drawable.ic_perm_device_info;
            mIconResId = R.drawable.ic_perm_device_info;
        }
        }
        mAppPermissionUsages = appPermissionUsages;
    }
    }


    public boolean doesSupportRuntimePermissions() {
    public boolean doesSupportRuntimePermissions() {
@@ -515,16 +464,6 @@ public final class AppPermissionGroup implements Comparable<AppPermissionGroup>
        return mLabel;
        return mLabel;
    }
    }


    /**
     * Get a list of the permission usages by this app, sorted by last access time, with the most
     * recent first.
     *
     * @return a sort list of this app's permission usages.
     */
    public List<AppPermissionUsage> getAppPermissionUsage() {
        return mAppPermissionUsages;
    }

    /**
    /**
     * @hide
     * @hide
     * @return The resource Id of the request string.
     * @return The resource Id of the request string.
+168 −36
Original line number Original line Diff line number Diff line
@@ -16,64 +16,196 @@


package com.android.packageinstaller.permission.model;
package com.android.packageinstaller.permission.model;


import android.app.AppOpsManager;
import android.app.AppOpsManager.HistoricalOp;
import android.app.AppOpsManager.HistoricalPackageOps;
import android.app.AppOpsManager.OpEntry;


import androidx.annotation.NonNull;
import android.app.AppOpsManager.PackageOps;


import java.lang.reflect.InvocationTargetException;
import androidx.annotation.NonNull;
import java.lang.reflect.Method;
import androidx.annotation.Nullable;
import com.android.packageinstaller.permission.model.PermissionApps.PermissionApp;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;


/**
/**
 * A single instance of an app accessing a permission.
 * 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 {
public final class AppPermissionUsage {
    private final @NonNull AppOpsManager.PackageOps mPkgOp;
    private final @NonNull List<GroupUsage> mGroupUsages = new ArrayList<>();
    private final @NonNull AppOpsManager.OpEntry mOp;
    private final @NonNull PermissionApp mPermissionApp;
    private final @NonNull String mPermissionGroupName;

    private final @NonNull CharSequence mPermissionGroupLabel;
    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));
        }
    }


    AppPermissionUsage(@NonNull AppOpsManager.PackageOps pkgOp, @NonNull AppOpsManager.OpEntry op,
    public @NonNull PermissionApp getApp() {
            @NonNull String permissionGroupName, @NonNull CharSequence permissionGroupLabel) {
        return mPermissionApp;
        mPkgOp = pkgOp;
        mOp = op;
        mPermissionGroupName = permissionGroupName;
        mPermissionGroupLabel = permissionGroupLabel;
    }
    }


    public @NonNull String getPackageName() {
    public @NonNull String getPackageName() {
        return mPkgOp.getPackageName();
        return mPermissionApp.getPackageName();
    }
    }


    public int getUid() {
    public int getUid() {
        return mPkgOp.getUid();
        return mPermissionApp.getUid();
    }
    }


    public long getTime() {
    public long getLastAccessTime() {
        return mOp.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 @NonNull String getPermissionGroupName() {
    public long getAccessCount() {
        return mPermissionGroupName;
        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 CharSequence getPermissionGroupLabel() {
    public @NonNull List<GroupUsage> getGroupUsages() {
        return mPermissionGroupLabel;
        return mGroupUsages;
    }
    }


    /**
    /**
     * Get the name of the permission (not the group) this represents.
     * Stats for permission usage of a permission group. This data is for a
     *
     * given time period, i.e. does not contain the full history.
     * @return the name of the permission this represents.
     */
     */
    public String getPermissionName() {
    public static class GroupUsage {
        // TODO: Replace reflection with a proper API (probably in AppOpsManager).
        private final @NonNull AppPermissionGroup mGroup;
        try {
        private final @Nullable PackageOps mLastUsage;
            Method getOpMethod = AppOpsManager.OpEntry.class.getMethod("getOp");
        private final @Nullable HistoricalPackageOps mHistoricalUsage;
            Method opToPermissionMethod = AppOpsManager.class.getMethod("opToPermission",

                    int.class);
        GroupUsage(@NonNull AppPermissionGroup group, @Nullable PackageOps lastUsage,
            return (String) opToPermissionMethod.invoke(null, (int) getOpMethod.invoke(mOp));
                @Nullable HistoricalPackageOps historicalUsage) {
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            mGroup = group;
            return null;
            mLastUsage = lastUsage;
            mHistoricalUsage = historicalUsage;
        }

        public long getLastAccessTime() {
            if (mLastUsage == null) {
                return 0;
            }
            long lastAccessTime = 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 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)) {
                        lastAccessTime = Math.max(lastAccessTime, op.getLastAccessTime());
                    }
                }
            }
            return lastAccessTime;
        }


        public long getForegroundAccessCount() {
            if (mHistoricalUsage == null) {
                return 0;
            }
            return extractAggregate(HistoricalOp::getForegroundAccessCount);
        }

        public long getBackgroundAccessCount() {
            if (mHistoricalUsage == null) {
                return 0;
            }
            return extractAggregate(HistoricalOp::getBackgroundAccessCount);
        }

        public long getAccessCount() {
            if (mHistoricalUsage == null) {
                return 0;
            }
            return extractAggregate((HistoricalOp op) ->
                op.getForegroundAccessCount() + op.getBackgroundAccessCount()
            );
        }

        public long getAccessDuration() {
            if (mHistoricalUsage == null) {
                return 0;
            }
            return extractAggregate((HistoricalOp op) ->
                    op.getForegroundAccessDuration() + op.getBackgroundAccessDuration()
            );
        }

        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;
        }

        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);
        }
        }
    }
    }
}
}
+43 −11
Original line number Original line Diff line number Diff line
@@ -48,6 +48,7 @@ public class PermissionApps {


    private final Context mContext;
    private final Context mContext;
    private final String mGroupName;
    private final String mGroupName;
    private final String mPackageName;
    private final PackageManager mPm;
    private final PackageManager mPm;
    private final Callback mCallback;
    private final Callback mCallback;


@@ -63,18 +64,23 @@ public class PermissionApps {
    private boolean mSkipUi;
    private boolean mSkipUi;
    private boolean mRefreshing;
    private boolean mRefreshing;


    public PermissionApps(Context context, String groupName, String packageName) {
        this(context, groupName, packageName, null, null, null);
    }

    public PermissionApps(Context context, String groupName, Callback callback) {
    public PermissionApps(Context context, String groupName, Callback callback) {
        this(context, groupName, callback, null, null);
        this(context, groupName, null, callback, null, null);
    }
    }


    public PermissionApps(Context context, String groupName, Callback callback,
    public PermissionApps(Context context, String groupName, String packageName,
            @Nullable PmCache pmCache, @Nullable AppDataCache appDataCache) {
            Callback callback, @Nullable PmCache pmCache, @Nullable AppDataCache appDataCache) {
        mPmCache = pmCache;
        mPmCache = pmCache;
        mAppDataCache = appDataCache;
        mAppDataCache = appDataCache;
        mContext = context;
        mContext = context;
        mPm = mContext.getPackageManager();
        mPm = mContext.getPackageManager();
        mGroupName = groupName;
        mGroupName = groupName;
        mCallback = callback;
        mCallback = callback;
        mPackageName = packageName;
        loadGroupInfo();
        loadGroupInfo();
    }
    }


@@ -93,10 +99,6 @@ public class PermissionApps {
     * @param getUiInfo If the UI info should be updated
     * @param getUiInfo If the UI info should be updated
     */
     */
    public void refresh(boolean getUiInfo) {
    public void refresh(boolean getUiInfo) {
        if (mCallback == null) {
            throw new IllegalStateException("callback needs to be set");
        }

        if (!mRefreshing) {
        if (!mRefreshing) {
            mRefreshing = true;
            mRefreshing = true;
            mSkipUi = !getUiInfo;
            mSkipUi = !getUiInfo;
@@ -161,6 +163,39 @@ public class PermissionApps {
        return mIcon;
        return mIcon;
    }
    }


    private @NonNull List<PackageInfo> getPackageInfos(@NonNull UserHandle user) {
        List<PackageInfo> apps = (mPmCache != null) ? mPmCache.getPackages(
                user.getIdentifier()) : null;
        if (apps != null) {
            if (mPackageName != null) {
                final int appCount = apps.size();
                for (int i = 0; i < appCount; i++) {
                    final PackageInfo app = apps.get(i);
                    if (mPackageName.equals(app.packageName)) {
                        apps = new ArrayList<>(1);
                        apps.add(app);
                        return apps;
                    }
                }
            }
            return apps;
        }
        if (mPackageName == null) {
            return mPm.getInstalledPackagesAsUser(PackageManager.GET_PERMISSIONS,
                    user.getIdentifier());
        } else {
            try {
                final PackageInfo packageInfo = mPm.getPackageInfo(mPackageName,
                        PackageManager.GET_PERMISSIONS);
                apps = new ArrayList<>(1);
                apps.add(packageInfo);
                return apps;
            } catch (NameNotFoundException e) {
                return Collections.emptyList();
            }
        }
    }

    private List<PermissionApp> loadPermissionApps() {
    private List<PermissionApp> loadPermissionApps() {
        PackageItemInfo groupInfo = Utils.getGroupInfo(mGroupName, mContext);
        PackageItemInfo groupInfo = Utils.getGroupInfo(mGroupName, mContext);
        if (groupInfo == null) {
        if (groupInfo == null) {
@@ -176,10 +211,7 @@ public class PermissionApps {


        UserManager userManager = mContext.getSystemService(UserManager.class);
        UserManager userManager = mContext.getSystemService(UserManager.class);
        for (UserHandle user : userManager.getUserProfiles()) {
        for (UserHandle user : userManager.getUserProfiles()) {
            List<PackageInfo> apps = mPmCache != null ? mPmCache.getPackages(user.getIdentifier())
            List<PackageInfo> apps = getPackageInfos(user);
                    : mPm.getInstalledPackagesAsUser(PackageManager.GET_PERMISSIONS,
                            user.getIdentifier());

            final int N = apps.size();
            final int N = apps.size();
            for (int i = 0; i < N; i++) {
            for (int i = 0; i < N; i++) {
                PackageInfo app = apps.get(i);
                PackageInfo app = apps.get(i);
+43 −11
Original line number Original line Diff line number Diff line
@@ -30,6 +30,7 @@ import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.UsesPermissionInfo;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Bundle;
import android.os.Handler;
import android.os.Handler;
@@ -140,6 +141,23 @@ public final class PermissionGroups implements LoaderCallbacks<List<PermissionGr
     */
     */
    public static @NonNull List<PermissionGroup> getAllPermissionGroups(@NonNull Context context,
    public static @NonNull List<PermissionGroup> getAllPermissionGroups(@NonNull Context context,
            @Nullable Supplier<Boolean> isCanceled, boolean getUiInfo) {
            @Nullable Supplier<Boolean> isCanceled, boolean getUiInfo) {
        return getPermissionGroups(context, isCanceled, getUiInfo, null, null);
    }

    /**
     * Return all permission groups in the system.
     *
     * @param context Context to use
     * @param isCanceled callback checked if the group resolution should be aborted
     * @param getUiInfo If the UI info for apps should be updated
     * @param groupName Optional group to filter for.
     * @param packageName Optional package to filter for.
     *
     * @return the list of all groups int the system
     */
    public static @NonNull List<PermissionGroup> getPermissionGroups(@NonNull Context context,
            @Nullable Supplier<Boolean> isCanceled, boolean getUiInfo, @Nullable String groupName,
            @Nullable String  packageName) {
        ArraySet<String> launcherPkgs = Utils.getLauncherPackages(context);
        ArraySet<String> launcherPkgs = Utils.getLauncherPackages(context);
        PermissionApps.PmCache pmCache = new PermissionApps.PmCache(
        PermissionApps.PmCache pmCache = new PermissionApps.PmCache(
                context.getPackageManager());
                context.getPackageManager());
@@ -150,7 +168,7 @@ public final class PermissionGroups implements LoaderCallbacks<List<PermissionGr
        Set<String> seenPermissions = new ArraySet<>();
        Set<String> seenPermissions = new ArraySet<>();


        PackageManager packageManager = context.getPackageManager();
        PackageManager packageManager = context.getPackageManager();
        List<PermissionGroupInfo> groupInfos = packageManager.getAllPermissionGroups(0);
        List<PermissionGroupInfo> groupInfos = getPermissionGroupInfos(context, groupName);


        for (PermissionGroupInfo groupInfo : groupInfos) {
        for (PermissionGroupInfo groupInfo : groupInfos) {
            // Mare sure we respond to cancellation.
            // Mare sure we respond to cancellation.
@@ -171,8 +189,7 @@ public final class PermissionGroups implements LoaderCallbacks<List<PermissionGr
            // Cache seen permissions and see if group has runtime permissions.
            // Cache seen permissions and see if group has runtime permissions.
            for (PermissionInfo groupPermission : groupPermissions) {
            for (PermissionInfo groupPermission : groupPermissions) {
                seenPermissions.add(groupPermission.name);
                seenPermissions.add(groupPermission.name);
                if ((groupPermission.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
                if (groupPermission.getProtection() == PermissionInfo.PROTECTION_DANGEROUS
                        == PermissionInfo.PROTECTION_DANGEROUS
                        && (groupPermission.flags & PermissionInfo.FLAG_INSTALLED) != 0
                        && (groupPermission.flags & PermissionInfo.FLAG_INSTALLED) != 0
                        && (groupPermission.flags & PermissionInfo.FLAG_REMOVED) == 0) {
                        && (groupPermission.flags & PermissionInfo.FLAG_REMOVED) == 0) {
                    hasRuntimePermissions = true;
                    hasRuntimePermissions = true;
@@ -187,8 +204,8 @@ public final class PermissionGroups implements LoaderCallbacks<List<PermissionGr
            CharSequence label = loadItemInfoLabel(context, groupInfo);
            CharSequence label = loadItemInfoLabel(context, groupInfo);
            Drawable icon = loadItemInfoIcon(context, groupInfo);
            Drawable icon = loadItemInfoIcon(context, groupInfo);


            PermissionApps permApps = new PermissionApps(context, groupInfo.name, null,
            PermissionApps permApps = new PermissionApps(context, groupInfo.name, packageName,
                    pmCache, appDataCache);
                    null, pmCache, appDataCache);
            permApps.refreshSync(getUiInfo);
            permApps.refreshSync(getUiInfo);


            // Create the group and add to the list.
            // Create the group and add to the list.
@@ -207,11 +224,11 @@ public final class PermissionGroups implements LoaderCallbacks<List<PermissionGr
        // We will filter out permissions that no package requests.
        // We will filter out permissions that no package requests.
        Set<String> requestedPermissions = new ArraySet<>();
        Set<String> requestedPermissions = new ArraySet<>();
        for (PackageInfo installedPackage : installedPackages) {
        for (PackageInfo installedPackage : installedPackages) {
            if (installedPackage.requestedPermissions == null) {
            if (installedPackage.usesPermissions == null) {
                continue;
                continue;
            }
            }
            for (String requestedPermission : installedPackage.requestedPermissions) {
            for (UsesPermissionInfo usedPermission : installedPackage.usesPermissions) {
                requestedPermissions.add(requestedPermission);
                requestedPermissions.add(usedPermission.name);
            }
            }
        }
        }


@@ -227,8 +244,7 @@ public final class PermissionGroups implements LoaderCallbacks<List<PermissionGr
                }
                }


                // We care only about installed runtime permissions.
                // We care only about installed runtime permissions.
                if ((permissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
                if (permissionInfo.getProtection() != PermissionInfo.PROTECTION_DANGEROUS
                        != PermissionInfo.PROTECTION_DANGEROUS
                        || (permissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0) {
                        || (permissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0) {
                    continue;
                    continue;
                }
                }
@@ -242,7 +258,7 @@ public final class PermissionGroups implements LoaderCallbacks<List<PermissionGr
                Drawable icon = loadItemInfoIcon(context, permissionInfo);
                Drawable icon = loadItemInfoIcon(context, permissionInfo);


                PermissionApps permApps = new PermissionApps(context, permissionInfo.name,
                PermissionApps permApps = new PermissionApps(context, permissionInfo.name,
                        null, pmCache, appDataCache);
                        packageName, null, pmCache, appDataCache);
                permApps.refreshSync(getUiInfo);
                permApps.refreshSync(getUiInfo);


                // Create the group and add to the list.
                // Create the group and add to the list.
@@ -269,6 +285,22 @@ public final class PermissionGroups implements LoaderCallbacks<List<PermissionGr
        return groups;
        return groups;
    }
    }


    private static @NonNull List<PermissionGroupInfo> getPermissionGroupInfos(
            @NonNull Context context, @Nullable String groupName) {
        if (groupName == null) {
            return context.getPackageManager().getAllPermissionGroups(0);
        }
        try {
            final PermissionGroupInfo groupInfo = context.getPackageManager()
                    .getPermissionGroupInfo(groupName, 0);
            final List<PermissionGroupInfo> groupInfos = new ArrayList<>(1);
            groupInfos.add(groupInfo);
            return groupInfos;
        } catch (PackageManager.NameNotFoundException e) {
            return Collections.emptyList();
        }
    }

    private static final class PermissionsLoader extends AsyncTaskLoader<List<PermissionGroup>>
    private static final class PermissionsLoader extends AsyncTaskLoader<List<PermissionGroup>>
            implements PackageManager.OnPermissionsChangedListener {
            implements PackageManager.OnPermissionsChangedListener {
        private final boolean mGetUiInfo;
        private final boolean mGetUiInfo;
Loading