parsedPermissions = new ArrayList<>(
- expandedPermissions.size());
- int numExpandedPerms = expandedPermissions.size();
- for (int i = 0; i < numExpandedPerms; i++) {
- parsedPermissions.add(new BackupPermissionState(expandedPermissions.get(i),
- "true".equals(parser.getAttributeValue(null, ATTR_IS_GRANTED)),
- "true".equals(parser.getAttributeValue(null, ATTR_USER_SET)),
- "true".equals(parser.getAttributeValue(null, ATTR_USER_FIXED)),
- "true".equals(parser.getAttributeValue(null, ATTR_WAS_REVIEWED))));
- }
-
- return parsedPermissions;
- }
-
- /**
- * Is the permission granted, also considering the app-op.
- *
- * This does not consider the review-required state of the permission.
- *
- * @param perm The permission that might be granted
- *
- * @return {@code true} iff the permission and app-op is granted
- */
- private static boolean isPermGrantedIncludingAppOp(@NonNull Permission perm) {
- return perm.isGranted() && (!perm.affectsAppOp() || perm.isAppOpAllowed());
- }
-
- /**
- * Get the state of a permission to back up.
- *
- * @param perm The permission to back up
- * @param appSupportsRuntimePermissions If the app supports runtimePermissions
- *
- * @return The state to back up or {@code null} if the permission does not need to be
- * backed up.
- */
- private static @Nullable BackupPermissionState fromPermission(@NonNull Permission perm,
- boolean appSupportsRuntimePermissions) {
- int grantFlags = perm.getFlags();
-
- if ((grantFlags & SYSTEM_RUNTIME_GRANT_MASK) != 0) {
- return null;
- }
-
- if (!perm.isUserSet() && perm.isGrantedByDefault()) {
- return null;
- }
-
- boolean permissionWasReviewed;
- boolean isNotInDefaultGrantState;
- if (appSupportsRuntimePermissions) {
- isNotInDefaultGrantState = isPermGrantedIncludingAppOp(perm);
- permissionWasReviewed = false;
- } else {
- isNotInDefaultGrantState = !isPermGrantedIncludingAppOp(perm);
- permissionWasReviewed = !perm.isReviewRequired();
- }
-
-// if (isNotInDefaultGrantState || perm.isUserSet() || perm.isUserFixed()
-// || permissionWasReviewed) {
-// return new BackupPermissionState(perm.getName(),
-// isPermGrantedIncludingAppOp(perm),
-// perm.isUserSet(), perm.isUserFixed(), permissionWasReviewed);
-// } else {
-// return null;
-// }
- if (perm.isUserSet() && isPermGrantedIncludingAppOp(perm)) {
- return new BackupPermissionState(perm.getName(), /* isGranted */ true,
- /* isUserSet */ true, perm.isUserFixed(), permissionWasReviewed);
- } else {
- return null;
- }
- }
-
- /**
- * Get the states of all permissions of a group to back up.
- *
- * @param group The group of the permissions to back up
- *
- * @return The state to back up. Empty list if no permissions in the group need to be backed
- * up
- */
- static @NonNull ArrayList fromPermissionGroup(
- @NonNull AppPermissionGroup group) {
- ArrayList permissionsToRestore = new ArrayList<>();
- List perms = group.getPermissions();
-
- boolean appSupportsRuntimePermissions =
- group.getApp().applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M;
-
- int numPerms = perms.size();
- for (int i = 0; i < numPerms; i++) {
- BackupPermissionState permState = fromPermission(perms.get(i),
- appSupportsRuntimePermissions);
- if (permState != null) {
- permissionsToRestore.add(permState);
- }
- }
-
- return permissionsToRestore;
- }
-
- /**
- * Write this state as XML.
- *
- * @param serializer The file to write to
- */
- void writeAsXml(@NonNull XmlSerializer serializer) throws IOException {
- serializer.startTag(null, TAG_PERMISSION);
-
- serializer.attribute(null, ATTR_PERMISSION_NAME, mPermissionName);
-
- if (mIsGranted) {
- serializer.attribute(null, ATTR_IS_GRANTED, "true");
- }
-
- if (mIsUserSet) {
- serializer.attribute(null, ATTR_USER_SET, "true");
- }
-
- if (mIsUserFixed) {
- serializer.attribute(null, ATTR_USER_FIXED, "true");
- }
-
- if (mWasReviewed) {
- serializer.attribute(null, ATTR_WAS_REVIEWED, "true");
- }
-
- serializer.endTag(null, TAG_PERMISSION);
- }
-
- /**
- * Restore this permission state.
- *
- * @param appPerms The {@link AppPermissions} to restore the state to
- * @param restoreBackgroundPerms if {@code true} only restore background permissions,
- * if {@code false} do not restore background permissions
- */
- void restore(@NonNull AppPermissions appPerms, boolean restoreBackgroundPerms) {
- AppPermissionGroup group = appPerms.getGroupForPermission(mPermissionName);
- if (group == null) {
- Log.w(LOG_TAG, "Could not find group for " + mPermissionName + " in "
- + appPerms.getPackageInfo().packageName);
- return;
- }
-
- if (restoreBackgroundPerms != group.isBackgroundGroup()) {
- return;
- }
-
- Permission perm = group.getPermission(mPermissionName);
- if (mWasReviewed) {
- perm.unsetReviewRequired();
- }
-
- // Don't grant or revoke fixed permission groups
- if (group.isSystemFixed() || group.isPolicyFixed()) {
- return;
- }
-
- if (!perm.isUserSet()) {
- if (mIsGranted) {
- group.grantRuntimePermissions(false, mIsUserFixed,
- new String[]{mPermissionName});
- } else {
- group.revokeRuntimePermissions(mIsUserFixed,
- new String[]{mPermissionName});
- }
-
- perm.setUserSet(mIsUserSet);
- }
- }
- }
-
- /**
- * State that needs to be backed up for a package.
- */
- private static class BackupPackageState {
- final @NonNull String mPackageName;
- final boolean mHasMultipleSigners;
- @NonNull Signature[] mSignatures;
- private final @NonNull ArrayList mPermissionsToRestore;
-
- private BackupPackageState(@NonNull String packageName, boolean hasMultipleSigners,
- @NonNull Signature[] signatures,
- @NonNull ArrayList permissionsToRestore) {
- mPackageName = packageName;
- mHasMultipleSigners = hasMultipleSigners;
- mSignatures = signatures;
- mPermissionsToRestore = permissionsToRestore;
- }
-
- /**
- * Parse a package state from XML.
- *
- * @param parser The data to read
- * @param context a context to use
- * @param backupPlatformVersion The platform version the backup was created on
- *
- * @return The state
- */
- static @NonNull BackupPackageState parseFromXml(@NonNull XmlPullParser parser,
- @NonNull Context context, int backupPlatformVersion)
- throws IOException, XmlPullParserException {
- String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
- if (packageName == null) {
- throw new XmlPullParserException("Found " + TAG_GRANT + " without "
- + ATTR_PACKAGE_NAME);
- }
-
- boolean hasMultipleSigners = Boolean.parseBoolean(
- parser.getAttributeValue(null, ATTR_HAS_MULTIPLE_SIGNERS));
- ArrayList signatureList = new ArrayList<>();
-
- ArrayList permissionsToRestore = new ArrayList<>();
-
- while (true) {
- switch (parser.next()) {
- case START_TAG:
- switch (parser.getName()) {
- case TAG_PERMISSION:
- try {
- permissionsToRestore.addAll(
- BackupPermissionState.parseFromXml(parser, context,
- backupPlatformVersion));
- } catch (XmlPullParserException e) {
- Log.e(LOG_TAG, "Could not parse permission for "
- + packageName, e);
- }
-
- skipToEndOfTag(parser);
- break;
- case TAG_SIGNATURE:
- signatureList.add(new Signature(
- parser.getAttributeValue(null, ATTR_SIGNATURE_VALUE)));
- skipToEndOfTag(parser);
- break;
- default:
- // ignore tag
- Log.w(LOG_TAG, "Found unexpected tag " + parser.getName()
- + " while restoring " + packageName);
- skipToEndOfTag(parser);
- }
-
- break;
- case END_TAG:
- Signature[] signatures = new Signature[signatureList.size()];
- for (int i = 0; i < signatureList.size(); i++) {
- signatures[i] = signatureList.get(i);
- }
- return new BackupPackageState(packageName, hasMultipleSigners, signatures,
- permissionsToRestore);
- case END_DOCUMENT:
- throw new XmlPullParserException("Could not parse state for "
- + packageName);
- }
- }
- }
-
- /**
- * Get the state of a package to back up.
- *
- * @param context A context to use
- * @param pkgInfo The package to back up.
- *
- * @return The state to back up or {@code null} if no permission of the package need to be
- * backed up.
- */
- static @Nullable BackupPackageState fromAppPermissions(@NonNull Context context,
- @NonNull PackageInfo pkgInfo) {
- AppPermissions appPerms = new AppPermissions(context, pkgInfo, false, null);
-
- ArrayList permissionsToRestore = new ArrayList<>();
- List groups = appPerms.getPermissionGroups();
-
- // Check if the package has signatures
- SigningInfo signingInfo = pkgInfo.signingInfo;
- Signature[] signatures;
- boolean hasMultipleSigners;
- if (signingInfo.hasMultipleSigners()) {
- hasMultipleSigners = true;
- signatures = signingInfo.getApkContentsSigners();
- } else {
- hasMultipleSigners = false;
- signatures = signingInfo.getSigningCertificateHistory();
- }
- if (signatures == null) {
- Slog.d(LOG_TAG, "Skipping " + pkgInfo.packageName + ", it's unsigned.");
- return null;
- }
-
- int numGroups = groups.size();
- for (int groupNum = 0; groupNum < numGroups; groupNum++) {
- AppPermissionGroup group = groups.get(groupNum);
-
- permissionsToRestore.addAll(BackupPermissionState.fromPermissionGroup(group));
-
- // Background permissions are in a subgroup that is not part of
- // {@link AppPermission#getPermissionGroups}. Hence add it explicitly here.
- if (group.getBackgroundPermissions() != null) {
- permissionsToRestore.addAll(BackupPermissionState.fromPermissionGroup(
- group.getBackgroundPermissions()));
- }
- }
-
- if (permissionsToRestore.size() == 0) {
- return null;
- }
-
- return new BackupPackageState(pkgInfo.packageName, hasMultipleSigners, signatures,
- permissionsToRestore);
- }
-
- /**
- * Write this state as XML.
- *
- * @param serializer The file to write to
- */
- void writeAsXml(@NonNull XmlSerializer serializer) throws IOException {
- if (mPermissionsToRestore.size() == 0) {
- return;
- }
-
- serializer.startTag(null, TAG_GRANT);
- serializer.attribute(null, ATTR_PACKAGE_NAME, mPackageName);
-
- // Add signing info
- serializer.attribute(null, ATTR_HAS_MULTIPLE_SIGNERS,
- String.valueOf(mHasMultipleSigners));
- for (Signature signature : mSignatures) {
- serializer.startTag(null, TAG_SIGNATURE);
- serializer.attribute(null, ATTR_SIGNATURE_VALUE, signature.toCharsString());
- serializer.endTag(null, TAG_SIGNATURE);
- }
-
- int numPerms = mPermissionsToRestore.size();
- for (int i = 0; i < numPerms; i++) {
- mPermissionsToRestore.get(i).writeAsXml(serializer);
- }
-
- serializer.endTag(null, TAG_GRANT);
- }
-
- /**
- * Restore this package state.
- *
- * @param context A context to use
- * @param pkgInfo The package to restore.
- */
- void restore(@NonNull Context context, @NonNull PackageInfo pkgInfo) {
- Slog.e(LOG_TAG, "Restoring permissions for package [" + mPackageName + "]");
-
- // Verify signature info
- try {
- if (mHasMultipleSigners && pkgInfo.signingInfo.hasMultipleSigners()) {
- // If both packages are signed by multi signers, check if two signature sets are
- // effectively matched.
- if (!Signature.areEffectiveMatch(mSignatures,
- pkgInfo.signingInfo.getApkContentsSigners())) {
- Slog.e(LOG_TAG, "Multi-signers signatures don't match for package ["
- + mPackageName + "], skipped.");
- return;
- }
- } else if (!mHasMultipleSigners && !pkgInfo.signingInfo.hasMultipleSigners()) {
- // If both packages are not signed by multi signers, check if two signature sets
- // have overlaps.
- Signature[] signatures = pkgInfo.signingInfo.getSigningCertificateHistory();
- if (signatures == null) {
- Slog.e(LOG_TAG, "The dest package is unsigned.");
- return;
- }
- boolean isMatched = false;
- for (int i = 0; i < mSignatures.length; i++) {
- for (int j = 0; j < signatures.length; j++) {
- isMatched = Signature.areEffectiveMatch(mSignatures[i], signatures[j]);
- }
- }
- if (!isMatched) {
- Slog.e(LOG_TAG, "Single signer signatures don't match for package ["
- + mPackageName + "], skipped.");
- return;
- }
- } else {
- Slog.e(LOG_TAG, "Number of signers don't match.");
- return;
- }
- } catch (CertificateException ce) {
- Slog.e(LOG_TAG, "Either the source or the dest package's bounced cert length "
- + "looks fishy, skipped package [" + pkgInfo.packageName + "]");
- }
-
- AppPermissions appPerms = new AppPermissions(context, pkgInfo, false, true, null);
-
- ArraySet affectedPermissions = new ArraySet<>();
- // Restore background permissions after foreground permissions as for pre-M apps bg
- // granted and fg revoked cannot be expressed.
- int numPerms = mPermissionsToRestore.size();
- for (int i = 0; i < numPerms; i++) {
- mPermissionsToRestore.get(i).restore(appPerms, false);
- affectedPermissions.add(mPermissionsToRestore.get(i).mPermissionName);
- }
- for (int i = 0; i < numPerms; i++) {
- mPermissionsToRestore.get(i).restore(appPerms, true);
- }
-
- int numGroups = appPerms.getPermissionGroups().size();
- for (int i = 0; i < numGroups; i++) {
- AppPermissionGroup group = appPerms.getPermissionGroups().get(i);
-
- // Only denied groups can be user fixed
- if (group.areRuntimePermissionsGranted()) {
- group.setUserFixed(false);
- }
-
- AppPermissionGroup bgGroup = group.getBackgroundPermissions();
- if (bgGroup != null) {
- // Only denied groups can be user fixed
- if (bgGroup.areRuntimePermissionsGranted()) {
- bgGroup.setUserFixed(false);
- }
- }
- }
-
- appPerms.persistChanges(true, affectedPermissions);
- }
- }
-}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/permbackup/model/AppPermissionGroup.java b/services/companion/java/com/android/server/companion/datatransfer/permbackup/model/AppPermissionGroup.java
deleted file mode 100644
index cf146ac7a48e8..0000000000000
--- a/services/companion/java/com/android/server/companion/datatransfer/permbackup/model/AppPermissionGroup.java
+++ /dev/null
@@ -1,1574 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.companion.datatransfer.permbackup.model;
-
-import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
-import static android.Manifest.permission.ACCESS_FINE_LOCATION;
-import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.MODE_FOREGROUND;
-import static android.app.AppOpsManager.MODE_IGNORED;
-import static android.app.AppOpsManager.OPSTR_LEGACY_STORAGE;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.StringRes;
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.app.Application;
-import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageItemInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PermissionGroupInfo;
-import android.content.pm.PermissionInfo;
-import android.os.Build;
-import android.os.UserHandle;
-import android.permission.PermissionManager;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import com.android.server.companion.datatransfer.permbackup.utils.ArrayUtils;
-import com.android.server.companion.datatransfer.permbackup.utils.LocationUtils;
-import com.android.server.companion.datatransfer.permbackup.utils.SoftRestrictedPermissionPolicy;
-import com.android.server.companion.datatransfer.permbackup.utils.Utils;
-
-import java.text.Collator;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * All permissions of a permission group that are requested by an app.
- *
- * Some permissions only grant access to the protected resource while the app is running in the
- * foreground. These permissions are considered "split" into this foreground and a matching
- * "background" permission.
- *
- *
All background permissions of the group are not in the main group and will not be affected
- * by operations on the group. The background permissions can be found in the {@link
- * #getBackgroundPermissions() background permissions group}.
- */
-public final class AppPermissionGroup implements Comparable {
- private static final String LOG_TAG = AppPermissionGroup.class.getSimpleName();
- private static final String PLATFORM_PACKAGE_NAME = "android";
-
- private static final String KILL_REASON_APP_OP_CHANGE = "Permission related app op changed";
-
- /**
- * Importance level to define the threshold for whether a package is in a state which resets the
- * timer on its one-time permission session
- */
- private static final int ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_RESET_TIMER =
- ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
-
- /**
- * Importance level to define the threshold for whether a package is in a state which keeps its
- * one-time permission session alive after the timer ends
- */
- private static final int ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE =
- ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
-
- private final Context mContext;
- private final UserHandle mUserHandle;
- private final PackageManager mPackageManager;
- private final AppOpsManager mAppOps;
- private final ActivityManager mActivityManager;
- private final Collator mCollator;
-
- private final PackageInfo mPackageInfo;
- private final String mName;
- private final String mDeclaringPackage;
- private final CharSequence mLabel;
- private final CharSequence mFullLabel;
- private final @StringRes int mRequest;
- private final @StringRes int mRequestDetail;
- private final @StringRes int mBackgroundRequest;
- private final @StringRes int mBackgroundRequestDetail;
- private final @StringRes int mUpgradeRequest;
- private final @StringRes int mUpgradeRequestDetail;
- private final CharSequence mDescription;
- private final ArrayMap mPermissions = new ArrayMap<>();
- private final String mIconPkg;
- private final int mIconResId;
-
- /** Delay changes until {@link #persistChanges} is called */
- private final boolean mDelayChanges;
-
- /**
- * Some permissions are split into foreground and background permission. All non-split and
- * foreground permissions are in {@link #mPermissions}, all background permissions are in
- * this field.
- */
- private AppPermissionGroup mBackgroundPermissions;
-
- private final boolean mAppSupportsRuntimePermissions;
- private final boolean mIsEphemeralApp;
- private final boolean mIsNonIsolatedStorage;
- private boolean mContainsEphemeralPermission;
- private boolean mContainsPreRuntimePermission;
-
- /**
- * Does this group contain at least one permission that is split into a foreground and
- * background permission? This does not necessarily mean that the app also requested the
- * background permission.
- */
- private boolean mHasPermissionWithBackgroundMode;
-
- private boolean mTriggerLocationAccessCheckOnPersist;
-
- private boolean mIsSelfRevoked;
-
- /**
- * Create the app permission group.
- *
- * @param context the {@code Context} to retrieve system services.
- * @param packageInfo package information about the app.
- * @param permissionName the name of the permission this object represents.
- * @param delayChanges whether to delay changes until {@link #persistChanges} is called.
- *
- * @return the AppPermissionGroup.
- */
- public static AppPermissionGroup create(Context context, PackageInfo packageInfo,
- String permissionName, boolean delayChanges) {
- PermissionInfo permissionInfo;
- try {
- permissionInfo = context.getPackageManager().getPermissionInfo(permissionName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- return null;
- }
-
- if ((permissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
- != PermissionInfo.PROTECTION_DANGEROUS
- || (permissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0
- || (permissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0) {
- return null;
- }
-
- String group = Utils.getGroupOfPermission(permissionInfo);
- PackageItemInfo groupInfo = permissionInfo;
- if (group != null) {
- try {
- groupInfo = context.getPackageManager().getPermissionGroupInfo(group, 0);
- } catch (PackageManager.NameNotFoundException e) {
- /* ignore */
- }
- }
-
- List permissionInfos = null;
- if (groupInfo instanceof PermissionGroupInfo) {
- try {
- permissionInfos = Utils.getPermissionInfosForGroup(context.getPackageManager(),
- groupInfo.name);
- } catch (PackageManager.NameNotFoundException e) {
- /* ignore */
- }
- }
-
- return create(context, packageInfo, groupInfo, permissionInfos, delayChanges);
- }
-
- /**
- * Create the app permission group.
- *
- * @param app the current application
- * @param packageName the name of the package
- * @param permissionGroupName the name of the permission group
- * @param user the user of the package
- * @param delayChanges whether to delay changes until {@link #persistChanges} is called.
- *
- * @return the AppPermissionGroup.
- */
- public static AppPermissionGroup create(Application app, String packageName,
- String permissionGroupName, UserHandle user, boolean delayChanges) {
- try {
- PackageInfo packageInfo = Utils.getUserContext(app, user).getPackageManager()
- .getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
- PackageItemInfo groupInfo = Utils.getGroupInfo(permissionGroupName, app);
- if (groupInfo == null) {
- return null;
- }
-
- List permissionInfos = null;
- if (groupInfo instanceof PermissionGroupInfo) {
- permissionInfos = Utils.getPermissionInfosForGroup(app.getPackageManager(),
- groupInfo.name);
- }
- return create(app, packageInfo, groupInfo, permissionInfos, delayChanges);
- } catch (PackageManager.NameNotFoundException e) {
- return null;
- }
- }
-
- /**
- * Create the app permission group.
- *
- * @param context the {@code Context} to retrieve system services.
- * @param packageInfo package information about the app.
- * @param groupInfo the information about the group created.
- * @param permissionInfos the information about the permissions belonging to the group.
- * @param delayChanges whether to delay changes until {@link #persistChanges} is called.
- *
- * @return the AppPermissionGroup.
- */
- public static AppPermissionGroup create(Context context, PackageInfo packageInfo,
- PackageItemInfo groupInfo, List permissionInfos, boolean delayChanges) {
- PackageManager packageManager = context.getPackageManager();
- CharSequence groupLabel = groupInfo.loadLabel(packageManager);
- CharSequence fullGroupLabel = groupInfo.loadSafeLabel(packageManager, 0,
- TextUtils.SAFE_STRING_FLAG_TRIM | TextUtils.SAFE_STRING_FLAG_FIRST_LINE);
- return create(context, packageInfo, groupInfo, permissionInfos, groupLabel,
- fullGroupLabel, delayChanges);
- }
-
- /**
- * Create the app permission group.
- *
- * @param context the {@code Context} to retrieve system services.
- * @param packageInfo package information about the app.
- * @param groupInfo the information about the group created.
- * @param permissionInfos the information about the permissions belonging to the group.
- * @param groupLabel the label of the group.
- * @param fullGroupLabel the untruncated label of the group.
- * @param delayChanges whether to delay changes until {@link #persistChanges} is called.
- *
- * @return the AppPermissionGroup.
- */
- public static AppPermissionGroup create(Context context, PackageInfo packageInfo,
- PackageItemInfo groupInfo, List permissionInfos,
- CharSequence groupLabel, CharSequence fullGroupLabel, boolean delayChanges) {
- PackageManager packageManager = context.getPackageManager();
- UserHandle userHandle = UserHandle.getUserHandleForUid(packageInfo.applicationInfo.uid);
-
- if (groupInfo instanceof PermissionInfo) {
- permissionInfos = new ArrayList<>();
- permissionInfos.add((PermissionInfo) groupInfo);
- }
-
- if (permissionInfos == null || permissionInfos.isEmpty()) {
- return null;
- }
-
- AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
-
- AppPermissionGroup group = new AppPermissionGroup(context, packageInfo, groupInfo.name,
- groupInfo.packageName, groupLabel, fullGroupLabel,
- /* description */ null, /* request */ 0,
- /* requestDetail */ 0, /* backgroundRequest */ 0,
- /* backgroundRequestDetail */ 0, /* upgradeRequest */0,
- /* upgradeRequestDetail */ 0, groupInfo.packageName, groupInfo.icon,
- userHandle, delayChanges, appOpsManager);
-
- final Set exemptedRestrictedPermissions = context.getPackageManager()
- .getWhitelistedRestrictedPermissions(packageInfo.packageName,
- Utils.FLAGS_PERMISSION_WHITELIST_ALL);
-
- // Parse and create permissions requested by the app
- ArrayMap allPermissions = new ArrayMap<>();
- final int permissionCount = packageInfo.requestedPermissions == null ? 0
- : packageInfo.requestedPermissions.length;
- String packageName = packageInfo.packageName;
- for (int i = 0; i < permissionCount; i++) {
- String requestedPermission = packageInfo.requestedPermissions[i];
-
- PermissionInfo requestedPermissionInfo = null;
-
- for (PermissionInfo permissionInfo : permissionInfos) {
- if (requestedPermission.equals(permissionInfo.name)) {
- requestedPermissionInfo = permissionInfo;
- break;
- }
- }
-
- if (requestedPermissionInfo == null) {
- continue;
- }
-
- // Collect only runtime permissions.
- if ((requestedPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
- != PermissionInfo.PROTECTION_DANGEROUS) {
- continue;
- }
-
- // Don't allow toggling non-platform permission groups for legacy apps via app ops.
- if (packageInfo.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1
- && !PLATFORM_PACKAGE_NAME.equals(groupInfo.packageName)) {
- continue;
- }
-
- final boolean granted = (packageInfo.requestedPermissionsFlags[i]
- & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0;
-
- final String appOp = PLATFORM_PACKAGE_NAME.equals(requestedPermissionInfo.packageName)
- ? AppOpsManager.permissionToOp(requestedPermissionInfo.name) : null;
-
- final boolean appOpAllowed;
- if (appOp == null) {
- appOpAllowed = false;
- } else {
- int appOpsMode = appOpsManager.unsafeCheckOpRaw(appOp,
- packageInfo.applicationInfo.uid, packageName);
- appOpAllowed = appOpsMode == MODE_ALLOWED || appOpsMode == MODE_FOREGROUND;
- }
-
- final int flags = packageManager.getPermissionFlags(
- requestedPermission, packageName, userHandle);
-
- Permission permission = new Permission(requestedPermission, requestedPermissionInfo,
- granted, appOp, appOpAllowed, flags);
-
- if (requestedPermissionInfo.backgroundPermission != null) {
- group.mHasPermissionWithBackgroundMode = true;
- }
-
- allPermissions.put(requestedPermission, permission);
- }
-
- int numPermissions = allPermissions.size();
- if (numPermissions == 0) {
- return null;
- }
-
- // Link up foreground and background permissions
- for (int i = 0; i < allPermissions.size(); i++) {
- Permission permission = allPermissions.valueAt(i);
-
- if (permission.getBackgroundPermissionName() != null) {
- Permission backgroundPermission = allPermissions.get(
- permission.getBackgroundPermissionName());
-
- if (backgroundPermission != null) {
- backgroundPermission.addForegroundPermissions(permission);
- permission.setBackgroundPermission(backgroundPermission);
-
- // The background permissions isAppOpAllowed refers to the background state of
- // the foregound permission's appOp. Hence we can only set it once we know the
- // matching foreground permission.
- // @see #allowAppOp
- if (context.getSystemService(AppOpsManager.class).unsafeCheckOpRaw(
- permission.getAppOp(), packageInfo.applicationInfo.uid,
- packageInfo.packageName) == MODE_ALLOWED) {
- backgroundPermission.setAppOpAllowed(true);
- }
- }
- }
- }
-
- // Add permissions found to this group
- for (int i = 0; i < numPermissions; i++) {
- Permission permission = allPermissions.valueAt(i);
-
- if ((!permission.isHardRestricted()
- || exemptedRestrictedPermissions.contains(permission.getName()))
- && (!permission.isSoftRestricted()
- || SoftRestrictedPermissionPolicy.shouldShow(packageInfo, permission))) {
- if (permission.isBackgroundPermission()) {
- if (group.getBackgroundPermissions() == null) {
- group.mBackgroundPermissions = new AppPermissionGroup(group.mContext,
- group.getApp(), group.getName(), group.getDeclaringPackage(),
- group.getLabel(), group.getFullLabel(), group.getDescription(),
- group.getRequest(), group.getRequestDetail(),
- group.getBackgroundRequest(), group.getBackgroundRequestDetail(),
- group.getUpgradeRequest(), group.getUpgradeRequestDetail(),
- group.getIconPkg(), group.getIconResId(), group.getUser(),
- delayChanges, appOpsManager);
- }
-
- group.getBackgroundPermissions().addPermission(permission);
- } else {
- group.addPermission(permission);
- }
- }
- }
-
- if (group.getPermissions().isEmpty()) {
- return null;
- }
-
- return group;
- }
-
- private AppPermissionGroup(Context context, PackageInfo packageInfo, String name,
- String declaringPackage, CharSequence label, CharSequence fullLabel,
- CharSequence description, @StringRes int request, @StringRes int requestDetail,
- @StringRes int backgroundRequest, @StringRes int backgroundRequestDetail,
- @StringRes int upgradeRequest, @StringRes int upgradeRequestDetail,
- String iconPkg, int iconResId, UserHandle userHandle, boolean delayChanges,
- @NonNull AppOpsManager appOpsManager) {
- int targetSDK = packageInfo.applicationInfo.targetSdkVersion;
-
- mContext = context;
- mUserHandle = userHandle;
- mPackageManager = mContext.getPackageManager();
- mPackageInfo = packageInfo;
- mAppSupportsRuntimePermissions = targetSDK > Build.VERSION_CODES.LOLLIPOP_MR1;
- mIsEphemeralApp = packageInfo.applicationInfo.isInstantApp();
- mAppOps = appOpsManager;
- mActivityManager = context.getSystemService(ActivityManager.class);
- mDeclaringPackage = declaringPackage;
- mName = name;
- mLabel = label;
- mFullLabel = fullLabel;
- mDescription = description;
- mCollator = Collator.getInstance(
- context.getResources().getConfiguration().getLocales().get(0));
- mRequest = request;
- mRequestDetail = requestDetail;
- mBackgroundRequest = backgroundRequest;
- mBackgroundRequestDetail = backgroundRequestDetail;
- mUpgradeRequest = upgradeRequest;
- mUpgradeRequestDetail = upgradeRequestDetail;
- mDelayChanges = delayChanges;
- if (iconResId != 0) {
- mIconPkg = iconPkg;
- mIconResId = iconResId;
- } else {
- mIconPkg = context.getPackageName();
- mIconResId = 0; // doesn't matter to CDM
- }
-
- mIsNonIsolatedStorage = targetSDK < Build.VERSION_CODES.P
- || (targetSDK < Build.VERSION_CODES.R
- && mAppOps.unsafeCheckOpNoThrow(OPSTR_LEGACY_STORAGE,
- packageInfo.applicationInfo.uid, packageInfo.packageName) == MODE_ALLOWED);
- }
-
- boolean doesSupportRuntimePermissions() {
- return mAppSupportsRuntimePermissions;
- }
-
- boolean isGrantingAllowed() {
- return (!mIsEphemeralApp || mContainsEphemeralPermission)
- && (mAppSupportsRuntimePermissions || mContainsPreRuntimePermission);
- }
-
- boolean isReviewRequired() {
- if (mAppSupportsRuntimePermissions) {
- return false;
- }
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- Permission permission = mPermissions.valueAt(i);
- if (permission.isReviewRequired()) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Are any of the permissions in this group user sensitive.
- *
- * @return {@code true} if any of the permissions in the group is user sensitive.
- */
- public boolean isUserSensitive() {
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- Permission permission = mPermissions.valueAt(i);
- if (permission.isUserSensitive()) {
- return true;
- }
- }
- return false;
- }
-
- void unsetReviewRequired() {
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- Permission permission = mPermissions.valueAt(i);
- if (permission.isReviewRequired()) {
- permission.unsetReviewRequired();
- }
- }
-
- if (!mDelayChanges) {
- persistChanges(false);
- }
- }
-
- boolean hasGrantedByDefaultPermission() {
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- Permission permission = mPermissions.valueAt(i);
- if (permission.isGrantedByDefault()) {
- return true;
- }
- }
- return false;
- }
-
- public PackageInfo getApp() {
- return mPackageInfo;
- }
-
- String getName() {
- return mName;
- }
-
- String getDeclaringPackage() {
- return mDeclaringPackage;
- }
-
- String getIconPkg() {
- return mIconPkg;
- }
-
- int getIconResId() {
- return mIconResId;
- }
-
- CharSequence getLabel() {
- return mLabel;
- }
-
- /**
- * Get the full un-ellipsized label of the permission group.
- *
- * @return the full label of the group.
- */
- public CharSequence getFullLabel() {
- return mFullLabel;
- }
-
- /**
- * @hide
- * @return The resource Id of the request string.
- */
- public @StringRes int getRequest() {
- return mRequest;
- }
-
- /**
- * Get the (subtitle) message explaining to the user that the permission is only granted to
- * the apps running in the foreground.
- *
- * @return the message or 0 if unset
- */
- public @StringRes int getRequestDetail() {
- return mRequestDetail;
- }
-
- /**
- * Get the title of the dialog explaining to the user that the permission is granted while
- * the app is in background and in foreground.
- *
- * @return the message or 0 if unset
- */
- public @StringRes int getBackgroundRequest() {
- return mBackgroundRequest;
- }
-
- /**
- * Get the (subtitle) message explaining to the user that the she/he is about to allow the
- * app to have background access.
- *
- * @return the message or 0 if unset
- */
- public @StringRes int getBackgroundRequestDetail() {
- return mBackgroundRequestDetail;
- }
-
- /**
- * Get the title of the dialog explaining to the user that the permission, which was
- * previously only granted for foreground, is granted while the app is in background and in
- * foreground.
- *
- * @return the message or 0 if unset
- */
- public @StringRes int getUpgradeRequest() {
- return mUpgradeRequest;
- }
-
- /**
- * Get the (subtitle) message explaining to the user that the she/he is about to allow the
- * app to have background access while currently having foreground only.
- *
- * @return the message or 0 if unset
- */
- public @StringRes int getUpgradeRequestDetail() {
- return mUpgradeRequestDetail;
- }
-
- public CharSequence getDescription() {
- return mDescription;
- }
-
- public UserHandle getUser() {
- return mUserHandle;
- }
-
- /**
- * Check if the group contains the permission.
- */
- public boolean hasPermission(String permission) {
- return mPermissions.get(permission) != null;
- }
-
- /**
- * Return a permission if in this group.
- *
- * @param permissionName The name of the permission
- *
- * @return The permission
- */
- public @Nullable Permission getPermission(@NonNull String permissionName) {
- return mPermissions.get(permissionName);
- }
-
- /**
- * Check if at least one of the permissions in the entire permission group should be considered
- * granted.
- */
- public boolean areRuntimePermissionsGranted() {
- return areRuntimePermissionsGranted(null);
- }
-
- /**
- * Check if at least one of the permissions in the filterPermissions should be considered
- * granted.
- */
- public boolean areRuntimePermissionsGranted(String[] filterPermissions) {
- return areRuntimePermissionsGranted(filterPermissions, false);
- }
-
- /**
- * @param filterPermissions the permissions to check for, null for all in this group
- * @param asOneTime add the requirement that at least one of the granted permissions must have
- * the ONE_TIME flag to return true
- */
- public boolean areRuntimePermissionsGranted(String[] filterPermissions, boolean asOneTime) {
- return areRuntimePermissionsGranted(filterPermissions, asOneTime, true);
- }
-
- /**
- * Returns true if at least one of the permissions in filterPermissions (or the entire
- * permission group if null) should be considered granted and satisfy the requirements
- * described by asOneTime and includingAppOp.
- *
- * @param filterPermissions the permissions to check for, null for all in this group
- * @param asOneTime add the requirement that the granted permission must have the ONE_TIME flag
- * @param includingAppOp add the requirement that if the granted permissions has a
- * corresponding AppOp, it must be allowed.
- */
- public boolean areRuntimePermissionsGranted(String[] filterPermissions, boolean asOneTime,
- boolean includingAppOp) {
- if (LocationUtils.isLocationGroupAndProvider(mContext, mName, mPackageInfo.packageName)) {
- return LocationUtils.isLocationEnabled(mContext) && !asOneTime;
- }
- // The permission of the extra location controller package is determined by the status of
- // the controller package itself.
- if (LocationUtils.isLocationGroupAndControllerExtraPackage(
- mContext, mName, mPackageInfo.packageName)) {
- return LocationUtils.isExtraLocationControllerPackageEnabled(mContext) && !asOneTime;
- }
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- Permission permission = mPermissions.valueAt(i);
- if (filterPermissions != null
- && !ArrayUtils.contains(filterPermissions, permission.getName())) {
- continue;
- }
- boolean isGranted = includingAppOp ? permission.isGrantedIncludingAppOp()
- : permission.isGranted();
- if (isGranted && (!asOneTime || permission.isOneTime())) {
- return true;
- }
- }
- if (mBackgroundPermissions != null) {
- // If asOneTime is true and none of the foreground permissions are one-time, but some
- // background permissions are, then we still want to return true.
- return mBackgroundPermissions.areRuntimePermissionsGranted(filterPermissions,
- asOneTime, includingAppOp);
- }
- return false;
- }
-
- boolean grantRuntimePermissions(boolean setByTheUser, boolean fixedByTheUser) {
- return grantRuntimePermissions(setByTheUser, fixedByTheUser, null);
- }
-
- /**
- * Set mode of an app-op if needed.
- *
- * @param op The op to set
- * @param uid The uid the app-op belongs to
- * @param mode The new mode
- *
- * @return {@code true} iff app-op was changed
- */
- private boolean setAppOpMode(@NonNull String op, int uid, int mode) {
- int currentMode = mAppOps.unsafeCheckOpRaw(op, uid, mPackageInfo.packageName);
- if (currentMode == mode) {
- return false;
- }
-
- mAppOps.setUidMode(op, uid, mode);
- return true;
- }
-
- /**
- * Allow the app op for a permission/uid.
- *
- * There are three cases:
- *
- * - The permission is not split into foreground/background
- * - The app op matching the permission will be set to {@link AppOpsManager#MODE_ALLOWED}
- * - The permission is a foreground permission:
- * - The background permission permission is granted
- * - The app op matching the permission will be set to {@link AppOpsManager#MODE_ALLOWED}
- * - The background permission permission is not granted
- * - The app op matching the permission will be set to
- * {@link AppOpsManager#MODE_FOREGROUND}
- *
- * - The permission is a background permission:
- * - All granted foreground permissions for this background permission will be set to
- * {@link AppOpsManager#MODE_ALLOWED}
- *
- *
- * @param permission The permission which has an appOps that should be allowed
- * @param uid The uid of the process the app op is for
- *
- * @return {@code true} iff app-op was changed
- */
- private boolean allowAppOp(Permission permission, int uid) {
- boolean wasChanged = false;
-
- if (permission.isBackgroundPermission()) {
- ArrayList foregroundPermissions = permission.getForegroundPermissions();
-
- int numForegroundPermissions = foregroundPermissions.size();
- for (int i = 0; i < numForegroundPermissions; i++) {
- Permission foregroundPermission = foregroundPermissions.get(i);
- if (foregroundPermission.isAppOpAllowed()) {
- wasChanged |= setAppOpMode(foregroundPermission.getAppOp(), uid, MODE_ALLOWED);
- }
- }
- } else {
- if (permission.hasBackgroundPermission()) {
- Permission backgroundPermission = permission.getBackgroundPermission();
-
- if (backgroundPermission == null) {
- // The app requested a permission that has a background permission but it did
- // not request the background permission, hence it can never get background
- // access
- wasChanged = setAppOpMode(permission.getAppOp(), uid, MODE_FOREGROUND);
- } else {
- if (backgroundPermission.isAppOpAllowed()) {
- wasChanged = setAppOpMode(permission.getAppOp(), uid, MODE_ALLOWED);
- } else {
- wasChanged = setAppOpMode(permission.getAppOp(), uid, MODE_FOREGROUND);
- }
- }
- } else {
- wasChanged = setAppOpMode(permission.getAppOp(), uid, MODE_ALLOWED);
- }
- }
-
- return wasChanged;
- }
-
- /**
- * Kills the app the permissions belong to (and all apps sharing the same uid)
- *
- * @param reason The reason why the apps are killed
- */
- private void killApp(String reason) {
- mActivityManager.killUid(mPackageInfo.applicationInfo.uid, reason);
- }
-
- /**
- * Grant permissions of the group.
- *
- * This also automatically grants all app ops for permissions that have app ops.
- *
This does only grant permissions in {@link #mPermissions}, i.e. usually not
- * the background permissions.
- *
- * @param setByTheUser If the user has made the decision. This does not unset the flag
- * @param fixedByTheUser If the user requested that she/he does not want to be asked again
- * @param filterPermissions If {@code null} all permissions of the group will be granted.
- * Otherwise only permissions in {@code filterPermissions} will be
- * granted.
- *
- * @return {@code true} iff all permissions of this group could be granted.
- */
- public boolean grantRuntimePermissions(boolean setByTheUser, boolean fixedByTheUser,
- String[] filterPermissions) {
- boolean killApp = false;
- boolean wasAllGranted = true;
-
- // We toggle permissions only to apps that support runtime
- // permissions, otherwise we toggle the app op corresponding
- // to the permission if the permission is granted to the app.
- for (Permission permission : mPermissions.values()) {
- if (filterPermissions != null
- && !ArrayUtils.contains(filterPermissions, permission.getName())) {
- continue;
- }
-
- if (!permission.isGrantingAllowed(mIsEphemeralApp, mAppSupportsRuntimePermissions)) {
- // Skip unallowed permissions.
- continue;
- }
-
- boolean wasGranted = permission.isGrantedIncludingAppOp();
-
- if (mAppSupportsRuntimePermissions) {
- // Do not touch permissions fixed by the system.
- if (permission.isSystemFixed()) {
- wasAllGranted = false;
- break;
- }
-
- // Ensure the permission app op is enabled before the permission grant.
- if (permission.affectsAppOp() && !permission.isAppOpAllowed()) {
- permission.setAppOpAllowed(true);
- }
-
- // Grant the permission if needed.
- if (!permission.isGranted()) {
- permission.setGranted(true);
- }
-
- // Update the permission flags.
- if (!fixedByTheUser) {
- if (permission.isUserFixed()) {
- permission.setUserFixed(false);
- }
- if (setByTheUser) {
- if (!permission.isUserSet()) {
- permission.setUserSet(true);
- }
- }
- } else {
- if (!permission.isUserFixed()) {
- permission.setUserFixed(true);
- }
- if (permission.isUserSet()) {
- permission.setUserSet(false);
- }
- }
- if (permission.isReviewRequired()) {
- permission.unsetReviewRequired();
- }
- } else {
- // Legacy apps cannot have a not granted permission but just in case.
- if (!permission.isGranted()) {
- continue;
- }
-
- // If the permissions has no corresponding app op, then it is a
- // third-party one and we do not offer toggling of such permissions.
- if (permission.affectsAppOp()) {
- if (!permission.isAppOpAllowed()) {
- permission.setAppOpAllowed(true);
-
- // Legacy apps do not know that they have to retry access to a
- // resource due to changes in runtime permissions (app ops in this
- // case). Therefore, we restart them on app op change, so they
- // can pick up the change.
- killApp = true;
- }
-
- // Mark that the permission is not kept granted only for compatibility.
- if (permission.isRevokedCompat()) {
- permission.setRevokedCompat(false);
- }
- }
-
- // Granting a permission explicitly means the user already
- // reviewed it so clear the review flag on every grant.
- if (permission.isReviewRequired()) {
- permission.unsetReviewRequired();
- }
- }
-
- // If we newly grant background access to the fine location, double-guess the user some
- // time later if this was really the right choice.
- if (!wasGranted && permission.isGrantedIncludingAppOp()) {
- if (permission.getName().equals(ACCESS_FINE_LOCATION)) {
- Permission bgPerm = permission.getBackgroundPermission();
- if (bgPerm != null) {
- if (bgPerm.isGrantedIncludingAppOp()) {
- mTriggerLocationAccessCheckOnPersist = true;
- }
- }
- } else if (permission.getName().equals(ACCESS_BACKGROUND_LOCATION)) {
- ArrayList fgPerms = permission.getForegroundPermissions();
- if (fgPerms != null) {
- int numFgPerms = fgPerms.size();
- for (int fgPermNum = 0; fgPermNum < numFgPerms; fgPermNum++) {
- Permission fgPerm = fgPerms.get(fgPermNum);
-
- if (fgPerm.getName().equals(ACCESS_FINE_LOCATION)) {
- if (fgPerm.isGrantedIncludingAppOp()) {
- mTriggerLocationAccessCheckOnPersist = true;
- }
-
- break;
- }
- }
- }
- }
- }
- }
-
- if (!mDelayChanges) {
- persistChanges(false);
-
- if (killApp) {
- killApp(KILL_REASON_APP_OP_CHANGE);
- }
- }
-
- return wasAllGranted;
- }
-
- boolean revokeRuntimePermissions(boolean fixedByTheUser) {
- return revokeRuntimePermissions(fixedByTheUser, null);
- }
-
- /**
- * Disallow the app op for a permission/uid.
- *
- * There are three cases:
- *
- * - The permission is not split into foreground/background
- * - The app op matching the permission will be set to {@link AppOpsManager#MODE_IGNORED}
- * - The permission is a foreground permission:
- * - The app op matching the permission will be set to {@link AppOpsManager#MODE_IGNORED}
- * - The permission is a background permission:
- * - All granted foreground permissions for this background permission will be set to
- * {@link AppOpsManager#MODE_FOREGROUND}
- *
- *
- * @param permission The permission which has an appOps that should be disallowed
- * @param uid The uid of the process the app op if for
- *
- * @return {@code true} iff app-op was changed
- */
- private boolean disallowAppOp(Permission permission, int uid) {
- boolean wasChanged = false;
-
- if (permission.isBackgroundPermission()) {
- ArrayList foregroundPermissions = permission.getForegroundPermissions();
-
- int numForegroundPermissions = foregroundPermissions.size();
- for (int i = 0; i < numForegroundPermissions; i++) {
- Permission foregroundPermission = foregroundPermissions.get(i);
- if (foregroundPermission.isAppOpAllowed()) {
- wasChanged |= setAppOpMode(foregroundPermission.getAppOp(), uid,
- MODE_FOREGROUND);
- }
- }
- } else {
- wasChanged = setAppOpMode(permission.getAppOp(), uid, MODE_IGNORED);
- }
-
- return wasChanged;
- }
-
- /**
- * Revoke permissions of the group.
- *
- * This also disallows all app ops for permissions that have app ops.
- *
This does only revoke permissions in {@link #mPermissions}, i.e. usually not
- * the background permissions.
- *
- * @param fixedByTheUser If the user requested that she/he does not want to be asked again
- * @param filterPermissions If {@code null} all permissions of the group will be revoked.
- * Otherwise only permissions in {@code filterPermissions} will be
- * revoked.
- *
- * @return {@code true} iff all permissions of this group could be revoked.
- */
- public boolean revokeRuntimePermissions(boolean fixedByTheUser, String[] filterPermissions) {
- boolean killApp = false;
- boolean wasAllRevoked = true;
-
- // We toggle permissions only to apps that support runtime
- // permissions, otherwise we toggle the app op corresponding
- // to the permission if the permission is granted to the app.
- for (Permission permission : mPermissions.values()) {
- if (filterPermissions != null
- && !ArrayUtils.contains(filterPermissions, permission.getName())) {
- continue;
- }
-
- // Do not touch permissions fixed by the system.
- if (permission.isSystemFixed()) {
- wasAllRevoked = false;
- break;
- }
-
- if (mAppSupportsRuntimePermissions) {
- // Revoke the permission if needed.
- if (permission.isGranted()) {
- permission.setGranted(false);
- }
-
- // Update the permission flags.
- if (fixedByTheUser) {
- // Take a note that the user fixed the permission.
- if (permission.isUserSet() || !permission.isUserFixed()) {
- permission.setUserSet(false);
- permission.setUserFixed(true);
- }
- } else {
- if (!permission.isUserSet() || permission.isUserFixed()) {
- permission.setUserSet(true);
- permission.setUserFixed(false);
- }
- }
-
- if (permission.affectsAppOp()) {
- permission.setAppOpAllowed(false);
- }
- } else {
- // Legacy apps cannot have a non-granted permission but just in case.
- if (!permission.isGranted()) {
- continue;
- }
-
- // If the permission has no corresponding app op, then it is a
- // third-party one and we do not offer toggling of such permissions.
- if (permission.affectsAppOp()) {
- if (permission.isAppOpAllowed()) {
- permission.setAppOpAllowed(false);
-
- // Disabling an app op may put the app in a situation in which it
- // has a handle to state it shouldn't have, so we have to kill the
- // app. This matches the revoke runtime permission behavior.
- killApp = true;
- }
-
- // Mark that the permission is kept granted only for compatibility.
- if (!permission.isRevokedCompat()) {
- permission.setRevokedCompat(true);
- }
- }
- }
- }
-
- if (!mDelayChanges) {
- persistChanges(false);
-
- if (killApp) {
- killApp(KILL_REASON_APP_OP_CHANGE);
- }
- }
-
- return wasAllRevoked;
- }
-
- /**
- * Mark permissions in this group as policy fixed.
- *
- * @param filterPermissions The permissions to mark
- */
- public void setPolicyFixed(@NonNull String[] filterPermissions) {
- for (String permissionName : filterPermissions) {
- Permission permission = mPermissions.get(permissionName);
-
- if (permission != null) {
- permission.setPolicyFixed(true);
- }
- }
-
- if (!mDelayChanges) {
- persistChanges(false);
- }
- }
-
- /**
- * Set the user-fixed flag for all permissions in this group.
- *
- * @param isUsedFixed if the flag should be set or not
- */
- public void setUserFixed(boolean isUsedFixed) {
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- Permission permission = mPermissions.valueAt(i);
- permission.setUserFixed(isUsedFixed);
- }
-
- if (!mDelayChanges) {
- persistChanges(false);
- }
- }
-
- /**
- * Mark this group as having been self-revoked.
- */
- public void setSelfRevoked() {
- mIsSelfRevoked = true;
- }
-
- /**
- * Set the one-time flag for all permissions in this group.
- *
- * @param isOneTime if the flag should be set or not
- */
- public void setOneTime(boolean isOneTime) {
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- Permission permission = mPermissions.valueAt(i);
- permission.setOneTime(isOneTime);
- }
-
- if (!mDelayChanges) {
- persistChanges(false);
- }
- }
-
- /**
- * Set the user-set flag for all permissions in this group.
- *
- * @param isUserSet if the flag should be set or not
- */
- public void setUserSet(boolean isUserSet) {
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- Permission permission = mPermissions.valueAt(i);
- permission.setUserSet(isUserSet);
- }
-
- if (!mDelayChanges) {
- persistChanges(false);
- }
- }
-
- /**
- * Get all permissions in the group.
- */
- public ArrayList getPermissions() {
- return new ArrayList<>(mPermissions.values());
- }
-
- /**
- * @return An {@link AppPermissionGroup}-object that contains all background permissions for
- * this group.
- */
- public AppPermissionGroup getBackgroundPermissions() {
- return mBackgroundPermissions;
- }
-
- /**
- * @return {@code true} iff the app request at least one permission in this group that has a
- * background permission. It is possible that the app does not request the matching background
- * permission and hence will only ever get foreground access, never background access.
- */
- public boolean hasPermissionWithBackgroundMode() {
- return mHasPermissionWithBackgroundMode;
- }
-
- /**
- * Is the group a storage permission group that is referring to an app that does not have
- * isolated storage
- *
- * @return {@code true} iff this is a storage group on an app that does not have isolated
- * storage
- */
- public boolean isNonIsolatedStorage() {
- return mIsNonIsolatedStorage;
- }
-
- /**
- * Whether this is group that contains all the background permission for regular permission
- * group.
- *
- * @return {@code true} iff this is a background permission group.
- *
- * @see #getBackgroundPermissions()
- */
- public boolean isBackgroundGroup() {
- return mPermissions.valueAt(0).isBackgroundPermission();
- }
-
- /**
- * Whether this group supports one-time permissions
- * @return {@code true} iff this group supports one-time permissions
- */
- public boolean supportsOneTimeGrant() {
- return Utils.supportsOneTimeGrant(getName());
- }
-
- int getFlags() {
- int flags = 0;
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- Permission permission = mPermissions.valueAt(i);
- flags |= permission.getFlags();
- }
- return flags;
- }
-
- boolean isUserFixed() {
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- Permission permission = mPermissions.valueAt(i);
- if (permission.isUserFixed()) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Check if there's a permission in the group is policy fixed.
- */
- public boolean isPolicyFixed() {
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- Permission permission = mPermissions.valueAt(i);
- if (permission.isPolicyFixed()) {
- return true;
- }
- }
- return false;
- }
-
- boolean isUserSet() {
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- Permission permission = mPermissions.valueAt(i);
- if (permission.isUserSet()) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Check if there's a permission in the group is system fixed.
- */
- public boolean isSystemFixed() {
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- Permission permission = mPermissions.valueAt(i);
- if (permission.isSystemFixed()) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * @return Whether any of the permissions in this group is one-time
- */
- public boolean isOneTime() {
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- Permission permission = mPermissions.valueAt(i);
- if (permission.isOneTime()) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * @return Whether at least one permission is granted and every granted permission is one-time
- */
- public boolean isStrictlyOneTime() {
- boolean oneTimePermissionFound = false;
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- Permission permission = mPermissions.valueAt(i);
- if (permission.isGranted()) {
- if (!permission.isOneTime()) {
- return false;
- }
- oneTimePermissionFound = true;
- }
- }
- return oneTimePermissionFound;
- }
-
- @Override
- public int compareTo(AppPermissionGroup another) {
- final int result = mCollator.compare(mLabel.toString(), another.mLabel.toString());
- if (result == 0) {
- // Unbadged before badged.
- return mPackageInfo.applicationInfo.uid
- - another.mPackageInfo.applicationInfo.uid;
- }
- return result;
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof AppPermissionGroup)) {
- return false;
- }
-
- AppPermissionGroup other = (AppPermissionGroup) o;
-
- boolean equal = mName.equals(other.mName)
- && mPackageInfo.packageName.equals(other.mPackageInfo.packageName)
- && mUserHandle.equals(other.mUserHandle)
- && mPermissions.equals(other.mPermissions);
- if (!equal) {
- return false;
- }
-
- if (mBackgroundPermissions != null && other.getBackgroundPermissions() != null) {
- return mBackgroundPermissions.getPermissions().equals(
- other.getBackgroundPermissions().getPermissions());
- }
- return mBackgroundPermissions == other.getBackgroundPermissions();
- }
-
- @Override
- public int hashCode() {
- ArrayList backgroundPermissions = new ArrayList<>();
- if (mBackgroundPermissions != null) {
- backgroundPermissions = mBackgroundPermissions.getPermissions();
- }
- return Objects.hash(mName, mPackageInfo.packageName, mUserHandle, mPermissions,
- backgroundPermissions);
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append(getClass().getSimpleName());
- builder.append("{name=").append(mName);
- if (mBackgroundPermissions != null) {
- builder.append(", }");
- }
- if (!mPermissions.isEmpty()) {
- builder.append(", }");
- } else {
- builder.append('}');
- }
- return builder.toString();
- }
-
- private void addPermission(Permission permission) {
- mPermissions.put(permission.getName(), permission);
- if (permission.isEphemeral()) {
- mContainsEphemeralPermission = true;
- }
- if (!permission.isRuntimeOnly()) {
- mContainsPreRuntimePermission = true;
- }
- }
-
- /**
- * If the changes to this group were delayed, persist them to the platform.
- *
- * @param mayKillBecauseOfAppOpsChange If the app these permissions belong to may be killed if
- * app ops change. If this is set to {@code false} the
- * caller has to make sure to kill the app if needed.
- */
- public void persistChanges(boolean mayKillBecauseOfAppOpsChange) {
- persistChanges(mayKillBecauseOfAppOpsChange, null, null);
- }
-
- /**
- * If the changes to this group were delayed, persist them to the platform.
- *
- * @param mayKillBecauseOfAppOpsChange If the app these permissions belong to may be killed if
- * app ops change. If this is set to {@code false} the
- * caller has to make sure to kill the app if needed.
- * @param revokeReason If any permissions are getting revoked, the reason for revoking them.
- */
- public void persistChanges(boolean mayKillBecauseOfAppOpsChange, String revokeReason) {
- persistChanges(mayKillBecauseOfAppOpsChange, revokeReason, null);
- }
-
- /**
- * If the changes to this group were delayed, persist them to the platform.
- *
- * @param mayKillBecauseOfAppOpsChange If the app these permissions belong to may be killed if
- * app ops change. If this is set to {@code false} the
- * caller has to make sure to kill the app if needed.
- * @param revokeReason If any permissions are getting revoked, the reason for revoking them.
- * @param filterPermissions If provided, only persist state for the given permissions
- */
- public void persistChanges(boolean mayKillBecauseOfAppOpsChange, String revokeReason,
- Set filterPermissions) {
- int uid = mPackageInfo.applicationInfo.uid;
-
- int numPermissions = mPermissions.size();
- boolean shouldKillApp = false;
-
- for (int i = 0; i < numPermissions; i++) {
- Permission permission = mPermissions.valueAt(i);
-
- if (filterPermissions != null && !filterPermissions.contains(permission.getName())) {
- continue;
- }
-
- if (!permission.isSystemFixed()) {
- if (permission.isGranted()) {
- mPackageManager.grantRuntimePermission(mPackageInfo.packageName,
- permission.getName(), mUserHandle);
- } else {
- boolean isCurrentlyGranted = mContext.checkPermission(permission.getName(), -1,
- uid) == PERMISSION_GRANTED;
-
- if (isCurrentlyGranted) {
- if (revokeReason == null) {
- mPackageManager.revokeRuntimePermission(mPackageInfo.packageName,
- permission.getName(), mUserHandle);
- } else {
- mPackageManager.revokeRuntimePermission(mPackageInfo.packageName,
- permission.getName(), mUserHandle, revokeReason);
- }
- }
- }
- }
-
-// int flags = (permission.isUserSet() ? PackageManager.FLAG_PERMISSION_USER_SET : 0)
-// | (permission.isUserFixed() ? PackageManager.FLAG_PERMISSION_USER_FIXED : 0)
-// | (permission.isRevokedCompat()
-// ? PackageManager.FLAG_PERMISSION_REVOKED_COMPAT : 0)
-// | (permission.isPolicyFixed() ?
-// PackageManager.FLAG_PERMISSION_POLICY_FIXED : 0)
-// | (permission.isReviewRequired()
-// ? PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED : 0)
-// | (permission.isOneTime() ? PackageManager.FLAG_PERMISSION_ONE_TIME : 0)
-// | (permission.isSelectedLocationAccuracy()
-// ? PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY : 0);
-
-// mPackageManager.updatePermissionFlags(permission.getName(),
-// mPackageInfo.packageName,
-// PackageManager.FLAG_PERMISSION_USER_SET
-// | PackageManager.FLAG_PERMISSION_USER_FIXED
-// | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT
-// | PackageManager.FLAG_PERMISSION_POLICY_FIXED
-// | (permission.isReviewRequired()
-// ? 0 : PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED)
-// | PackageManager.FLAG_PERMISSION_ONE_TIME
-// | PackageManager.FLAG_PERMISSION_AUTO_REVOKED // clear auto revoke
-// | PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY,
-// flags, mUserHandle);
-
- if (permission.affectsAppOp()) {
- if (!permission.isSystemFixed()) {
- // Enabling/Disabling an app op may put the app in a situation in which it has
- // a handle to state it shouldn't have, so we have to kill the app. This matches
- // the revoke runtime permission behavior.
- if (permission.isAppOpAllowed()) {
- boolean wasChanged = allowAppOp(permission, uid);
- shouldKillApp |= wasChanged && !mAppSupportsRuntimePermissions;
- } else {
- shouldKillApp |= disallowAppOp(permission, uid);
- }
- }
- }
- }
-
- if (mayKillBecauseOfAppOpsChange && shouldKillApp) {
- killApp(KILL_REASON_APP_OP_CHANGE);
- }
-
-// if (mTriggerLocationAccessCheckOnPersist) {
-// new LocationAccessCheck(mContext, null).checkLocationAccessSoon();
-// mTriggerLocationAccessCheckOnPersist = false;
-// }
-
-// String packageName = mPackageInfo.packageName;
-// if (areRuntimePermissionsGranted(null, true, false)) {
-// // Required to read device config in Utils.getOneTimePermissions*().
-// final long token = Binder.clearCallingIdentity();
-// try {
-// if (SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
-// mContext.getSystemService(PermissionManager.class)
-// .startOneTimePermissionSession(packageName,
-// Utils.getOneTimePermissionsTimeout(),
-// Utils.getOneTimePermissionsKilledDelay(mIsSelfRevoked),
-// ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_RESET_TIMER,
-// ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE);
-// } else {
-// mContext.getSystemService(PermissionManager.class)
-// .startOneTimePermissionSession(packageName,
-// Utils.getOneTimePermissionsTimeout(),
-// ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_RESET_TIMER,
-// ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE);
-// }
-// } finally {
-// Binder.restoreCallingIdentity(token);
-// }
-// } else {
-// mContext.getSystemService(PermissionManager.class)
-// .stopOneTimePermissionSession(packageName);
-// }
- }
-
- /**
- * Check if permission group contains a runtime permission that split from an installed
- * permission and the split happened in an Android version higher than app's targetSdk.
- *
- * @return {@code true} if there is such permission, {@code false} otherwise
- */
- public boolean hasInstallToRuntimeSplit() {
- PermissionManager permissionManager =
- (PermissionManager) mContext.getSystemService(PermissionManager.class);
-
- int numSplitPerms = permissionManager.getSplitPermissions().size();
- for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
- PermissionManager.SplitPermissionInfo spi =
- permissionManager.getSplitPermissions().get(splitPermNum);
- String splitPerm = spi.getSplitPermission();
-
- PermissionInfo pi;
- try {
- pi = mPackageManager.getPermissionInfo(splitPerm, 0);
- } catch (NameNotFoundException e) {
- Log.w(LOG_TAG, "No such permission: " + splitPerm, e);
- continue;
- }
-
- // Skip if split permission is not "install" permission.
- if (pi.getProtection() != pi.PROTECTION_NORMAL) {
- continue;
- }
-
- List newPerms = spi.getNewPermissions();
- int numNewPerms = newPerms.size();
- for (int newPermNum = 0; newPermNum < numNewPerms; newPermNum++) {
- String newPerm = newPerms.get(newPermNum);
-
- if (!hasPermission(newPerm)) {
- continue;
- }
-
- try {
- pi = mPackageManager.getPermissionInfo(newPerm, 0);
- } catch (NameNotFoundException e) {
- Log.w(LOG_TAG, "No such permission: " + newPerm, e);
- continue;
- }
-
- // Skip if new permission is not "runtime" permission.
- if (pi.getProtection() != pi.PROTECTION_DANGEROUS) {
- continue;
- }
-
- if (mPackageInfo.applicationInfo.targetSdkVersion < spi.getTargetSdk()) {
- return true;
- }
- }
- }
- return false;
- }
-}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/permbackup/model/AppPermissions.java b/services/companion/java/com/android/server/companion/datatransfer/permbackup/model/AppPermissions.java
deleted file mode 100644
index 9ef8d532dbfcc..0000000000000
--- a/services/companion/java/com/android/server/companion/datatransfer/permbackup/model/AppPermissions.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.companion.datatransfer.permbackup.model;
-
-import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.os.UserHandle;
-import android.util.ArrayMap;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-/**
- * An app that requests permissions.
- *
- * Allows to query all permission groups of the app and which permission belongs to which group.
- */
-public final class AppPermissions {
- /**
- * All permission groups the app requests. Background permission groups are attached to their
- * foreground groups.
- */
- private final ArrayList mGroups = new ArrayList<>();
-
- /** Cache: group name -> group */
- private final ArrayMap mGroupNameToGroup = new ArrayMap<>();
-
- /** Cache: permission name -> group. Might point to background group */
- private final ArrayMap mPermissionNameToGroup = new ArrayMap<>();
-
- private final Context mContext;
-
- private final CharSequence mAppLabel;
-
- private final Runnable mOnErrorCallback;
-
- private final boolean mSortGroups;
-
- /** Do not actually commit changes to the platform until {@link #persistChanges} is called */
- private final boolean mDelayChanges;
-
- private PackageInfo mPackageInfo;
-
- public AppPermissions(Context context, PackageInfo packageInfo, boolean sortGroups,
- Runnable onErrorCallback) {
- this(context, packageInfo, sortGroups, false, onErrorCallback);
- }
-
- public AppPermissions(Context context, PackageInfo packageInfo, boolean sortGroups,
- boolean delayChanges, Runnable onErrorCallback) {
- mContext = context;
- mPackageInfo = packageInfo;
- mAppLabel = null; // doesn't matter for CDM
- mSortGroups = sortGroups;
- mDelayChanges = delayChanges;
- mOnErrorCallback = onErrorCallback;
- loadPermissionGroups();
- }
-
- public PackageInfo getPackageInfo() {
- return mPackageInfo;
- }
-
- /**
- * Refresh package info and permission groups.
- */
- public void refresh() {
- loadPackageInfo();
- loadPermissionGroups();
- }
-
- public CharSequence getAppLabel() {
- return mAppLabel;
- }
-
- /**
- * Get permission group by name.
- */
- public AppPermissionGroup getPermissionGroup(String name) {
- return mGroupNameToGroup.get(name);
- }
-
- public List getPermissionGroups() {
- return mGroups;
- }
-
- /**
- * Check if the group is review required.
- */
- public boolean isReviewRequired() {
- final int groupCount = mGroups.size();
- for (int i = 0; i < groupCount; i++) {
- AppPermissionGroup group = mGroups.get(i);
- if (group.isReviewRequired()) {
- return true;
- }
- }
- return false;
- }
-
- private void loadPackageInfo() {
- try {
- mPackageInfo = mContext.createPackageContextAsUser(mPackageInfo.packageName, 0,
- UserHandle.getUserHandleForUid(mPackageInfo.applicationInfo.uid))
- .getPackageManager().getPackageInfo(mPackageInfo.packageName,
- PackageManager.GET_PERMISSIONS);
- } catch (PackageManager.NameNotFoundException e) {
- if (mOnErrorCallback != null) {
- mOnErrorCallback.run();
- }
- }
- }
-
- /**
- * Add all individual permissions of the {@code group} to the {@link #mPermissionNameToGroup}
- * lookup table.
- *
- * @param group The group of permissions to add
- */
- private void addAllPermissions(AppPermissionGroup group) {
- ArrayList perms = group.getPermissions();
-
- int numPerms = perms.size();
- for (int permNum = 0; permNum < numPerms; permNum++) {
- mPermissionNameToGroup.put(perms.get(permNum).getName(), group);
- }
- }
-
- private void loadPermissionGroups() {
- mGroups.clear();
- mGroupNameToGroup.clear();
- mPermissionNameToGroup.clear();
-
- if (mPackageInfo.requestedPermissions != null) {
- for (String requestedPerm : mPackageInfo.requestedPermissions) {
- if (getGroupForPermission(requestedPerm) == null) {
- AppPermissionGroup group = AppPermissionGroup.create(mContext, mPackageInfo,
- requestedPerm, mDelayChanges);
- if (group == null) {
- continue;
- }
-
- mGroups.add(group);
- mGroupNameToGroup.put(group.getName(), group);
-
- addAllPermissions(group);
-
- AppPermissionGroup backgroundGroup = group.getBackgroundPermissions();
- if (backgroundGroup != null) {
- addAllPermissions(backgroundGroup);
- }
- }
- }
-
- if (mSortGroups) {
- Collections.sort(mGroups);
- }
- }
- }
-
- /**
- * Find the group a permission belongs to.
- *
- * The group found might be a background group.
- *
- * @param permission The name of the permission
- *
- * @return The group the permission belongs to
- */
- public AppPermissionGroup getGroupForPermission(String permission) {
- return mPermissionNameToGroup.get(permission);
- }
-
- /**
- * If the changes to the permission groups were delayed, persist them now.
- *
- * @param mayKillBecauseOfAppOpsChange If the app may be killed if app ops change. If this is
- * set to {@code false} the caller has to make sure to kill
- * the app if needed.
- */
- public void persistChanges(boolean mayKillBecauseOfAppOpsChange) {
- persistChanges(mayKillBecauseOfAppOpsChange, null);
- }
-
- /**
- * If the changes to the permission groups were delayed, persist them now.
- *
- * @param mayKillBecauseOfAppOpsChange If the app may be killed if app ops change. If this is
- * set to {@code false} the caller has to make sure to kill
- * the app if needed.
- * @param filterPermissions If provided, only persist state for the given permissions
- */
- public void persistChanges(boolean mayKillBecauseOfAppOpsChange,
- Set filterPermissions) {
- if (mDelayChanges) {
- int numGroups = mGroups.size();
-
- for (int i = 0; i < numGroups; i++) {
- AppPermissionGroup group = mGroups.get(i);
- group.persistChanges(mayKillBecauseOfAppOpsChange, null, filterPermissions);
-
- AppPermissionGroup backgroundGroup = group.getBackgroundPermissions();
- if (backgroundGroup != null) {
- backgroundGroup.persistChanges(mayKillBecauseOfAppOpsChange, null,
- filterPermissions);
- }
- }
- }
- }
-}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/permbackup/model/Permission.java b/services/companion/java/com/android/server/companion/datatransfer/permbackup/model/Permission.java
deleted file mode 100644
index 2bec970a1deac..0000000000000
--- a/services/companion/java/com/android/server/companion/datatransfer/permbackup/model/Permission.java
+++ /dev/null
@@ -1,414 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.companion.datatransfer.permbackup.model;
-
-import android.annotation.NonNull;
-import android.content.pm.PackageManager;
-import android.content.pm.PermissionInfo;
-
-import java.util.ArrayList;
-import java.util.Objects;
-
-/**
- * A permission and its properties.
- *
- * @see AppPermissionGroup
- */
-public final class Permission {
- private final @NonNull PermissionInfo mPermissionInfo;
- private final String mName;
- private final String mBackgroundPermissionName;
- private final String mAppOp;
-
- private boolean mGranted;
- private boolean mAppOpAllowed;
- private int mFlags;
- private boolean mIsEphemeral;
- private boolean mIsRuntimeOnly;
- private Permission mBackgroundPermission;
- private ArrayList mForegroundPermissions;
- private boolean mWhitelisted;
-
- public Permission(String name, @NonNull PermissionInfo permissionInfo, boolean granted,
- String appOp, boolean appOpAllowed, int flags) {
- mPermissionInfo = permissionInfo;
- mName = name;
- mBackgroundPermissionName = permissionInfo.backgroundPermission;
- mGranted = granted;
- mAppOp = appOp;
- mAppOpAllowed = appOpAllowed;
- mFlags = flags;
- mIsEphemeral =
- (permissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0;
- mIsRuntimeOnly =
- (permissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0;
- }
-
- /**
- * Mark this permission as background permission for {@code foregroundPermissions}.
- *
- * @param foregroundPermission The foreground permission
- */
- public void addForegroundPermissions(Permission foregroundPermission) {
- if (mForegroundPermissions == null) {
- mForegroundPermissions = new ArrayList<>(1);
- }
- mForegroundPermissions.add(foregroundPermission);
- }
-
- /**
- * Mark this permission as foreground permission for {@code backgroundPermission}.
- *
- * @param backgroundPermission The background permission
- */
- public void setBackgroundPermission(Permission backgroundPermission) {
- mBackgroundPermission = backgroundPermission;
- }
-
- public PermissionInfo getPermissionInfo() {
- return mPermissionInfo;
- }
-
- public String getName() {
- return mName;
- }
-
- public String getAppOp() {
- return mAppOp;
- }
-
- public int getFlags() {
- return mFlags;
- }
-
- boolean isHardRestricted() {
- return (mPermissionInfo.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0;
- }
-
- boolean isSoftRestricted() {
- return (mPermissionInfo.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0;
- }
-
- /**
- * Does this permission affect app ops.
- *
- * I.e. does this permission have a matching app op or is this a background permission. All
- * background permissions affect the app op of its assigned foreground permission.
- *
- * @return {@code true} if this permission affects app ops
- */
- public boolean affectsAppOp() {
- return mAppOp != null || isBackgroundPermission();
- }
-
- /**
- * Check if the permission is granted.
- *
- *
This ignores the state of the app-op. I.e. for apps not handling runtime permissions, this
- * always returns {@code true}.
- *
- * @return If the permission is granted
- */
- public boolean isGranted() {
- return mGranted;
- }
-
- /**
- * Check if the permission is granted, also considering the state of the app-op.
- *
- *
For the UI, check the grant state of the whole group via
- * {@link AppPermissionGroup#areRuntimePermissionsGranted}.
- *
- * @return {@code true} if the permission (and the app-op) is granted.
- */
- public boolean isGrantedIncludingAppOp() {
- return mGranted && (!affectsAppOp() || isAppOpAllowed()) && !isReviewRequired();
- }
-
- public boolean isReviewRequired() {
- return (mFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0;
- }
-
- /**
- * Unset review required flag.
- */
- public void unsetReviewRequired() {
- mFlags &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
- }
-
- public void setGranted(boolean mGranted) {
- this.mGranted = mGranted;
- }
-
- public boolean isAppOpAllowed() {
- return mAppOpAllowed;
- }
-
- /**
- * Check if it's user fixed.
- */
- public boolean isUserFixed() {
- return (mFlags & PackageManager.FLAG_PERMISSION_USER_FIXED) != 0;
- }
-
- /**
- * Set user fixed flag.
- */
- public void setUserFixed(boolean userFixed) {
- if (userFixed) {
- mFlags |= PackageManager.FLAG_PERMISSION_USER_FIXED;
- } else {
- mFlags &= ~PackageManager.FLAG_PERMISSION_USER_FIXED;
- }
- }
-
- /**
- * Sets the one-time permission flag
- * @param oneTime true to set the flag, false to unset it
- */
- public void setOneTime(boolean oneTime) {
- if (oneTime) {
- mFlags |= PackageManager.FLAG_PERMISSION_ONE_TIME;
- } else {
- mFlags &= ~PackageManager.FLAG_PERMISSION_ONE_TIME;
- }
- }
-
- public boolean isSelectedLocationAccuracy() {
- return (mFlags & PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY) != 0;
- }
-
- /**
- * Sets the selected-location-accuracy permission flag
- * @param selectedLocationAccuracy true to set the flag, false to unset it
- */
- public void setSelectedLocationAccuracy(boolean selectedLocationAccuracy) {
- if (selectedLocationAccuracy) {
- mFlags |= PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY;
- } else {
- mFlags &= ~PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY;
- }
- }
-
- public boolean isSystemFixed() {
- return (mFlags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0;
- }
-
- public boolean isPolicyFixed() {
- return (mFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0;
- }
-
- public boolean isUserSet() {
- return (mFlags & PackageManager.FLAG_PERMISSION_USER_SET) != 0;
- }
-
- public boolean isGrantedByDefault() {
- return (mFlags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0;
- }
-
- /**
- * Is the permission user sensitive, i.e. should it always be shown to the user.
- *
- *
Non-sensitive permission are usually hidden behind a setting in an overflow menu or
- * some other kind of flag.
- *
- * @return {@code true} if the permission is user sensitive.
- */
- public boolean isUserSensitive() {
- if (isGrantedIncludingAppOp()) {
- return (mFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0;
- } else {
- return (mFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED) != 0;
- }
- }
-
- /**
- * If this permission is split into a foreground and background permission, this is the name
- * of the background permission.
- *
- * @return The name of the background permission or {@code null} if the permission is not split
- */
- public String getBackgroundPermissionName() {
- return mBackgroundPermissionName;
- }
-
- /**
- * @return If this permission is split into a foreground and background permission,
- * returns the background permission
- */
- public Permission getBackgroundPermission() {
- return mBackgroundPermission;
- }
-
- /**
- * @return If this permission is split into a foreground and background permission,
- * returns the foreground permission
- */
- public ArrayList getForegroundPermissions() {
- return mForegroundPermissions;
- }
-
- /**
- * @return {@code true} iff this is the foreground permission of a background-foreground-split
- * permission
- */
- public boolean hasBackgroundPermission() {
- return mBackgroundPermissionName != null;
- }
-
- /**
- * @return {@code true} iff this is the background permission of a background-foreground-split
- * permission
- */
- public boolean isBackgroundPermission() {
- return mForegroundPermissions != null;
- }
-
- /**
- * @see PackageManager#FLAG_PERMISSION_ONE_TIME
- */
- public boolean isOneTime() {
- return (mFlags & PackageManager.FLAG_PERMISSION_ONE_TIME) != 0;
- }
-
- /**
- * Set userSet flag.
- */
- public void setUserSet(boolean userSet) {
- if (userSet) {
- mFlags |= PackageManager.FLAG_PERMISSION_USER_SET;
- } else {
- mFlags &= ~PackageManager.FLAG_PERMISSION_USER_SET;
- }
- }
-
- /**
- * Set policy fixed flag.
- */
- public void setPolicyFixed(boolean policyFixed) {
- if (policyFixed) {
- mFlags |= PackageManager.FLAG_PERMISSION_POLICY_FIXED;
- } else {
- mFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED;
- }
- }
-
- /**
- * Check if the permission is revoke compat.
- */
- public boolean isRevokedCompat() {
- return (mFlags & PackageManager.FLAG_PERMISSION_REVOKED_COMPAT) != 0;
- }
-
- /**
- * Set revoke compat flag.
- */
- public void setRevokedCompat(boolean revokedCompat) {
- if (revokedCompat) {
- mFlags |= PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
- } else {
- mFlags &= ~PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
- }
- }
-
- /**
- * Set app op allowed flag.
- */
- public void setAppOpAllowed(boolean mAppOpAllowed) {
- this.mAppOpAllowed = mAppOpAllowed;
- }
-
- /**
- * Check if it's ephemeral.
- */
- public boolean isEphemeral() {
- return mIsEphemeral;
- }
-
- /**
- * Check if it's runtime only.
- */
- public boolean isRuntimeOnly() {
- return mIsRuntimeOnly;
- }
-
- /**
- * Check if it's granting allowed.
- */
- public boolean isGrantingAllowed(boolean isEphemeralApp, boolean supportsRuntimePermissions) {
- return (!isEphemeralApp || isEphemeral())
- && (supportsRuntimePermissions || !isRuntimeOnly());
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof Permission)) {
- return false;
- }
-
- Permission other = (Permission) o;
-
- if (!Objects.equals(getName(), other.getName()) || getFlags() != other.getFlags()
- || isGranted() != other.isGranted()) {
- return false;
- }
-
-
- // Only compare permission names, in order to avoid recursion
- if (getBackgroundPermission() != null && other.getBackgroundPermission() != null) {
- if (!Objects.equals(getBackgroundPermissionName(),
- other.getBackgroundPermissionName())) {
- return false;
- }
- } else if (getBackgroundPermission() != other.getBackgroundPermission()) {
- return false;
- }
-
- if (getForegroundPermissions() != null && other.getForegroundPermissions() != null) {
- ArrayList others = other.getForegroundPermissions();
- if (getForegroundPermissions().size() != others.size()) {
- return false;
- }
- for (int i = 0; i < others.size(); i++) {
- if (!getForegroundPermissions().get(i).getName().equals(others.get(i).getName())) {
- return false;
- }
- }
- } else if (getForegroundPermissions() != null || other.getForegroundPermissions() != null) {
- return false;
- }
-
- return Objects.equals(getAppOp(), other.getAppOp())
- && isAppOpAllowed() == other.isAppOpAllowed();
- }
-
- @Override
- public int hashCode() {
- ArrayList linkedPermissionNames = new ArrayList<>();
- if (mBackgroundPermission != null) {
- linkedPermissionNames.add(mBackgroundPermission.getName());
- }
- if (mForegroundPermissions != null) {
- for (Permission linkedPermission: mForegroundPermissions) {
- if (linkedPermission != null) {
- linkedPermissionNames.add(linkedPermission.getName());
- }
- }
- }
- return Objects.hash(mName, mFlags, mGranted, mAppOp, mAppOpAllowed, linkedPermissionNames);
- }
-}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/permbackup/utils/ArrayUtils.java b/services/companion/java/com/android/server/companion/datatransfer/permbackup/utils/ArrayUtils.java
deleted file mode 100644
index 7027528fc2030..0000000000000
--- a/services/companion/java/com/android/server/companion/datatransfer/permbackup/utils/ArrayUtils.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.companion.datatransfer.permbackup.utils;
-
-import android.annotation.Nullable;
-
-import java.util.Objects;
-
-/**
- * Utils for array manipulation.
- */
-public final class ArrayUtils {
- private ArrayUtils() { /* cannot be instantiated */ }
-
- /**
- * Checks if an array is null or has no elements.
- *
- * @param array the array to check for
- *
- * @return whether the array is null or has no elements.
- */
- public static boolean isEmpty(@Nullable T[] array) {
- return array == null || array.length == 0;
- }
-
- /**
- * Checks that value is present as at least one of the elements of the array.
- * @param array the array to check in
- * @param value the value to check for
- * @return true if the value is present in the array
- */
- public static boolean contains(T[] array, T value) {
- return indexOf(array, value) != -1;
- }
-
- /**
- * Return first index of {@code value} in {@code array}, or {@code -1} if
- * not found.
- */
- public static int indexOf(T[] array, T value) {
- if (array == null) return -1;
- for (int i = 0; i < array.length; i++) {
- if (Objects.equals(array[i], value)) return i;
- }
- return -1;
- }
-}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/permbackup/utils/LocationUtils.java b/services/companion/java/com/android/server/companion/datatransfer/permbackup/utils/LocationUtils.java
deleted file mode 100644
index 9402e46d16d9a..0000000000000
--- a/services/companion/java/com/android/server/companion/datatransfer/permbackup/utils/LocationUtils.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.companion.datatransfer.permbackup.utils;
-
-import static android.location.LocationManager.EXTRA_LOCATION_ENABLED;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.location.LocationManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.Log;
-
-import java.util.ArrayList;
-
-/**
- * Utils for location service.
- */
-public class LocationUtils {
-
- public static final String LOCATION_PERMISSION = Manifest.permission_group.LOCATION;
- public static final String ACTIVITY_RECOGNITION_PERMISSION =
- Manifest.permission_group.ACTIVITY_RECOGNITION;
-
- private static final String TAG = LocationUtils.class.getSimpleName();
- private static final long LOCATION_UPDATE_DELAY_MS = 1000;
- private static final Handler sMainHandler = new Handler(Looper.getMainLooper());
-
-
- /** Start the settings page for the location controller extra package. */
- public static void startLocationControllerExtraPackageSettings(@NonNull Context context,
- @NonNull UserHandle user) {
- try {
- context.startActivityAsUser(new Intent(
- Settings.ACTION_LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS), user);
- } catch (ActivityNotFoundException e) {
- // In rare cases where location controller extra package is set, but
- // no activity exists to handle the location controller extra package settings
- // intent, log an error instead of crashing permission controller.
- Log.e(TAG, "No activity to handle "
- + "android.settings.LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS");
- }
- }
-
- /**
- * Check if location is enabled.
- */
- public static boolean isLocationEnabled(Context context) {
- return context.getSystemService(LocationManager.class).isLocationEnabled();
- }
-
- /** Checks if the provided package is a location provider. */
- public static boolean isLocationProvider(Context context, String packageName) {
- return context.getSystemService(LocationManager.class).isProviderPackage(packageName);
- }
-
- /**
- * Check if group is location and the package is a location provider.
- */
- public static boolean isLocationGroupAndProvider(Context context, String groupName,
- String packageName) {
- return LOCATION_PERMISSION.equals(groupName) && isLocationProvider(context, packageName);
- }
-
- /**
- * Check if group is location and package is extra location controller.
- */
- public static boolean isLocationGroupAndControllerExtraPackage(@NonNull Context context,
- @NonNull String groupName, @NonNull String packageName) {
- return (LOCATION_PERMISSION.equals(groupName)
- || ACTIVITY_RECOGNITION_PERMISSION.equals(groupName))
- && packageName.equals(context.getSystemService(LocationManager.class)
- .getExtraLocationControllerPackage());
- }
-
- /** Returns whether the location controller extra package is enabled. */
- public static boolean isExtraLocationControllerPackageEnabled(Context context) {
- try {
- return context.getSystemService(LocationManager.class)
- .isExtraLocationControllerPackageEnabled();
- } catch (Exception e) {
- return false;
- }
-
- }
-
- /**
- * A Listener which responds to enabling or disabling of location on the device
- */
- public interface LocationListener {
-
- /**
- * A callback run any time we receive a broadcast stating the location enable state has
- * changed.
- * @param enabled Whether or not location is enabled
- */
- void onLocationStateChange(boolean enabled);
- }
-
- private static final ArrayList sLocationListeners = new ArrayList<>();
-
- private static BroadcastReceiver sLocationBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- boolean isEnabled = intent.getBooleanExtra(EXTRA_LOCATION_ENABLED, true);
- sMainHandler.postDelayed(() -> {
- synchronized (sLocationListeners) {
- for (LocationListener l : sLocationListeners) {
- l.onLocationStateChange(isEnabled);
- }
- }
- }, LOCATION_UPDATE_DELAY_MS);
- }
- };
-}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/permbackup/utils/SoftRestrictedPermissionPolicy.java b/services/companion/java/com/android/server/companion/datatransfer/permbackup/utils/SoftRestrictedPermissionPolicy.java
deleted file mode 100644
index d4940502f1e29..0000000000000
--- a/services/companion/java/com/android/server/companion/datatransfer/permbackup/utils/SoftRestrictedPermissionPolicy.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.companion.datatransfer.permbackup.utils;
-
-import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
-import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
-
-import android.annotation.NonNull;
-import android.content.pm.PackageInfo;
-import android.os.Build;
-
-import com.android.server.companion.datatransfer.permbackup.model.Permission;
-
-/**
- * The behavior of soft restricted permissions is different for each permission. This class collects
- * the policies in one place.
- *
- * This is the twin of {@link com.android.server.policy.SoftRestrictedPermissionPolicy}
- */
-public abstract class SoftRestrictedPermissionPolicy {
-
- /**
- * Check if the permission should be shown in the UI.
- *
- * @param pkg the package the permission belongs to
- * @param permission the permission
- *
- * @return {@code true} iff the permission should be shown in the UI.
- */
- public static boolean shouldShow(@NonNull PackageInfo pkg, @NonNull Permission permission) {
- switch (permission.getName()) {
- case READ_EXTERNAL_STORAGE:
- case WRITE_EXTERNAL_STORAGE: {
- boolean isWhiteListed =
- (permission.getFlags() & Utils.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)
- != 0;
- int targetSDK = pkg.applicationInfo.targetSdkVersion;
-
- return isWhiteListed || targetSDK >= Build.VERSION_CODES.Q;
- }
- default:
- return true;
- }
- }
-
- /**
- * Check if the permission should be shown in the UI.
- *
- * @param pkg the LightPackageInfo the permission belongs to
- * @param permissionName the name of the permission
- * @param permissionFlags the PermissionController flags (not the PermissionInfo flags) for
- * the permission
- *
- * @return {@code true} iff the permission should be shown in the UI.
- */
- public static boolean shouldShow(@NonNull PackageInfo pkg, @NonNull String permissionName,
- int permissionFlags) {
- switch (permissionName) {
- case READ_EXTERNAL_STORAGE:
- case WRITE_EXTERNAL_STORAGE: {
- boolean isWhiteListed =
- (permissionFlags & Utils.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
- return isWhiteListed || pkg.applicationInfo.targetSdkVersion
- >= Build.VERSION_CODES.Q;
- }
- default:
- return true;
- }
- }
-}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/permbackup/utils/Utils.java b/services/companion/java/com/android/server/companion/datatransfer/permbackup/utils/Utils.java
deleted file mode 100644
index 9350549d233e2..0000000000000
--- a/services/companion/java/com/android/server/companion/datatransfer/permbackup/utils/Utils.java
+++ /dev/null
@@ -1,819 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.companion.datatransfer.permbackup.utils;
-
-import static android.Manifest.permission_group.ACTIVITY_RECOGNITION;
-import static android.Manifest.permission_group.CALENDAR;
-import static android.Manifest.permission_group.CALL_LOG;
-import static android.Manifest.permission_group.CAMERA;
-import static android.Manifest.permission_group.CONTACTS;
-import static android.Manifest.permission_group.LOCATION;
-import static android.Manifest.permission_group.MICROPHONE;
-import static android.Manifest.permission_group.NEARBY_DEVICES;
-import static android.Manifest.permission_group.NOTIFICATIONS;
-import static android.Manifest.permission_group.PHONE;
-import static android.Manifest.permission_group.READ_MEDIA_AURAL;
-import static android.Manifest.permission_group.READ_MEDIA_VISUAL;
-import static android.Manifest.permission_group.SENSORS;
-import static android.Manifest.permission_group.SMS;
-import static android.Manifest.permission_group.STORAGE;
-import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.OPSTR_LEGACY_STORAGE;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED;
-import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.Manifest;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.AppOpsManager;
-import android.app.Application;
-import android.app.role.RoleManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageItemInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PermissionInfo;
-import android.content.pm.ResolveInfo;
-import android.hardware.SensorPrivacyManager;
-import android.os.Build;
-import android.os.Process;
-import android.os.UserHandle;
-import android.provider.DeviceConfig;
-import android.provider.Settings;
-import android.text.format.DateFormat;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-
-import com.android.server.companion.datatransfer.permbackup.model.AppPermissionGroup;
-
-import java.lang.annotation.Retention;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-
-/**
- * Util class for BackupHelper
- */
-public final class Utils {
-
- @Retention(SOURCE)
- @IntDef(value = {LAST_24H_SENSOR_TODAY, LAST_24H_SENSOR_YESTERDAY,
- LAST_24H_CONTENT_PROVIDER, NOT_IN_LAST_7D})
- public @interface AppPermsLastAccessType {}
- public static final int LAST_24H_SENSOR_TODAY = 1;
- public static final int LAST_24H_SENSOR_YESTERDAY = 2;
- public static final int LAST_24H_CONTENT_PROVIDER = 3;
- public static final int LAST_7D_SENSOR = 4;
- public static final int LAST_7D_CONTENT_PROVIDER = 5;
- public static final int NOT_IN_LAST_7D = 6;
-
- private static final List SENSOR_DATA_PERMISSIONS = List.of(
- Manifest.permission_group.LOCATION,
- Manifest.permission_group.CAMERA,
- Manifest.permission_group.MICROPHONE
- );
-
- public static final List STORAGE_SUPERGROUP_PERMISSIONS =
-// (SDK_INT < Build.VERSION_CODES.TIRAMISU) ? List.of() :
- List.of(
- Manifest.permission_group.STORAGE,
- Manifest.permission_group.READ_MEDIA_AURAL,
- Manifest.permission_group.READ_MEDIA_VISUAL
- );
-
- private static final String LOG_TAG = "Utils";
-
- public static final String OS_PKG = "android";
-
- public static final float DEFAULT_MAX_LABEL_SIZE_PX = 500f;
-
- /** The time an app needs to be unused in order to be hibernated */
- public static final String PROPERTY_HIBERNATION_UNUSED_THRESHOLD_MILLIS =
- "auto_revoke_unused_threshold_millis2";
-
- /** The frequency of running the job for hibernating apps */
- public static final String PROPERTY_HIBERNATION_CHECK_FREQUENCY_MILLIS =
- "auto_revoke_check_frequency_millis";
-
- /** Whether hibernation targets apps that target a pre-S SDK */
- public static final String PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS =
- "app_hibernation_targets_pre_s_apps";
-
- /** Whether or not app hibernation is enabled on the device **/
- public static final String PROPERTY_APP_HIBERNATION_ENABLED = "app_hibernation_enabled";
-
- /** Whether to show the Permissions Hub. */
- private static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled";
-
- /** The timeout for one-time permissions */
- private static final String PROPERTY_ONE_TIME_PERMISSIONS_TIMEOUT_MILLIS =
- "one_time_permissions_timeout_millis";
-
- /** The delay before ending a one-time permission session when all processes are dead */
- private static final String PROPERTY_ONE_TIME_PERMISSIONS_KILLED_DELAY_MILLIS =
- "one_time_permissions_killed_delay_millis";
-
- /** Whether to show location access check notifications. */
- private static final String PROPERTY_LOCATION_ACCESS_CHECK_ENABLED =
- "location_access_check_enabled";
-
- /** The time an app needs to be unused in order to be hibernated */
- public static final String PROPERTY_PERMISSION_DECISIONS_CHECK_OLD_FREQUENCY_MILLIS =
- "permission_decisions_check_old_frequency_millis";
-
- /** The time an app needs to be unused in order to be hibernated */
- public static final String PROPERTY_PERMISSION_DECISIONS_MAX_DATA_AGE_MILLIS =
- "permission_decisions_max_data_age_millis";
-
- /** Whether or not warning banner is displayed when device sensors are off **/
- public static final String PROPERTY_WARNING_BANNER_DISPLAY_ENABLED = "warning_banner_enabled";
-
- /** All permission whitelists. */
- public static final int FLAGS_PERMISSION_WHITELIST_ALL =
- PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
- | PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
- | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER;
-
- /** All permission restriction exemptions. */
- public static final int FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT =
- FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT
- | FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT
- | FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
-
- /**
- * The default length of the timeout for one-time permissions
- */
- public static final long ONE_TIME_PERMISSIONS_TIMEOUT_MILLIS = 1 * 60 * 1000; // 1 minute
-
- /**
- * The default length to wait before ending a one-time permission session after all processes
- * are dead.
- */
- public static final long ONE_TIME_PERMISSIONS_KILLED_DELAY_MILLIS = 5 * 1000;
-
- /** Mapping permission -> group for all dangerous platform permissions */
- private static final ArrayMap PLATFORM_PERMISSIONS;
-
- /** Mapping group -> permissions for all dangerous platform permissions */
- private static final ArrayMap> PLATFORM_PERMISSION_GROUPS;
-
- /** Set of groups that will be able to receive one-time grant */
- private static final ArraySet ONE_TIME_PERMISSION_GROUPS;
-
- /** Permission -> Sensor codes */
- private static final ArrayMap PERM_SENSOR_CODES;
-
- public static final int FLAGS_ALWAYS_USER_SENSITIVE =
- FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
- | FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED;
-
- private static final String SYSTEM_PKG = "android";
-
- private static final String SYSTEM_AMBIENT_AUDIO_INTELLIGENCE =
- "android.app.role.SYSTEM_AMBIENT_AUDIO_INTELLIGENCE";
- private static final String SYSTEM_UI_INTELLIGENCE =
- "android.app.role.SYSTEM_UI_INTELLIGENCE";
- private static final String SYSTEM_AUDIO_INTELLIGENCE =
- "android.app.role.SYSTEM_AUDIO_INTELLIGENCE";
- private static final String SYSTEM_NOTIFICATION_INTELLIGENCE =
- "android.app.role.SYSTEM_NOTIFICATION_INTELLIGENCE";
- private static final String SYSTEM_TEXT_INTELLIGENCE =
- "android.app.role.SYSTEM_TEXT_INTELLIGENCE";
- private static final String SYSTEM_VISUAL_INTELLIGENCE =
- "android.app.role.SYSTEM_VISUAL_INTELLIGENCE";
-
- // TODO: theianchen Using hardcoded values here as a WIP solution for now.
- private static final String[] EXEMPTED_ROLES = {
- SYSTEM_AMBIENT_AUDIO_INTELLIGENCE,
- SYSTEM_UI_INTELLIGENCE,
- SYSTEM_AUDIO_INTELLIGENCE,
- SYSTEM_NOTIFICATION_INTELLIGENCE,
- SYSTEM_TEXT_INTELLIGENCE,
- SYSTEM_VISUAL_INTELLIGENCE,
- };
-
- static {
- PLATFORM_PERMISSIONS = new ArrayMap<>();
-
- PLATFORM_PERMISSIONS.put(Manifest.permission.READ_CONTACTS, CONTACTS);
- PLATFORM_PERMISSIONS.put(Manifest.permission.WRITE_CONTACTS, CONTACTS);
- PLATFORM_PERMISSIONS.put(Manifest.permission.GET_ACCOUNTS, CONTACTS);
-
- PLATFORM_PERMISSIONS.put(Manifest.permission.READ_CALENDAR, CALENDAR);
- PLATFORM_PERMISSIONS.put(Manifest.permission.WRITE_CALENDAR, CALENDAR);
-
- PLATFORM_PERMISSIONS.put(Manifest.permission.SEND_SMS, SMS);
- PLATFORM_PERMISSIONS.put(Manifest.permission.RECEIVE_SMS, SMS);
- PLATFORM_PERMISSIONS.put(Manifest.permission.READ_SMS, SMS);
- PLATFORM_PERMISSIONS.put(Manifest.permission.RECEIVE_MMS, SMS);
- PLATFORM_PERMISSIONS.put(Manifest.permission.RECEIVE_WAP_PUSH, SMS);
- PLATFORM_PERMISSIONS.put(Manifest.permission.READ_CELL_BROADCASTS, SMS);
-
- // If permissions are added to the Storage group, they must be added to the
- // STORAGE_PERMISSIONS list in PermissionManagerService in frameworks/base
- PLATFORM_PERMISSIONS.put(Manifest.permission.READ_EXTERNAL_STORAGE, STORAGE);
- PLATFORM_PERMISSIONS.put(Manifest.permission.WRITE_EXTERNAL_STORAGE, STORAGE);
-// if (SDK_INT < Build.VERSION_CODES.TIRAMISU) {
-// PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_MEDIA_LOCATION, STORAGE);
-// }
-
-// if (SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- PLATFORM_PERMISSIONS.put(Manifest.permission.READ_MEDIA_AUDIO, READ_MEDIA_AURAL);
- PLATFORM_PERMISSIONS.put(Manifest.permission.READ_MEDIA_IMAGES, READ_MEDIA_VISUAL);
- PLATFORM_PERMISSIONS.put(Manifest.permission.READ_MEDIA_VIDEO, READ_MEDIA_VISUAL);
- PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_MEDIA_LOCATION, READ_MEDIA_VISUAL);
-// }
-
- PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_FINE_LOCATION, LOCATION);
- PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_COARSE_LOCATION, LOCATION);
- PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_BACKGROUND_LOCATION, LOCATION);
-
-// if (SDK_INT >= Build.VERSION_CODES.S) {
- PLATFORM_PERMISSIONS.put(Manifest.permission.BLUETOOTH_ADVERTISE, NEARBY_DEVICES);
- PLATFORM_PERMISSIONS.put(Manifest.permission.BLUETOOTH_CONNECT, NEARBY_DEVICES);
- PLATFORM_PERMISSIONS.put(Manifest.permission.BLUETOOTH_SCAN, NEARBY_DEVICES);
- PLATFORM_PERMISSIONS.put(Manifest.permission.UWB_RANGING, NEARBY_DEVICES);
-// }
-// if (SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- PLATFORM_PERMISSIONS.put(Manifest.permission.NEARBY_WIFI_DEVICES, NEARBY_DEVICES);
-// }
-
- PLATFORM_PERMISSIONS.put(Manifest.permission.READ_CALL_LOG, CALL_LOG);
- PLATFORM_PERMISSIONS.put(Manifest.permission.WRITE_CALL_LOG, CALL_LOG);
- PLATFORM_PERMISSIONS.put(Manifest.permission.PROCESS_OUTGOING_CALLS, CALL_LOG);
-
- PLATFORM_PERMISSIONS.put(Manifest.permission.READ_PHONE_STATE, PHONE);
- PLATFORM_PERMISSIONS.put(Manifest.permission.READ_PHONE_NUMBERS, PHONE);
- PLATFORM_PERMISSIONS.put(Manifest.permission.CALL_PHONE, PHONE);
- PLATFORM_PERMISSIONS.put(Manifest.permission.ADD_VOICEMAIL, PHONE);
- PLATFORM_PERMISSIONS.put(Manifest.permission.USE_SIP, PHONE);
- PLATFORM_PERMISSIONS.put(Manifest.permission.ANSWER_PHONE_CALLS, PHONE);
- PLATFORM_PERMISSIONS.put(Manifest.permission.ACCEPT_HANDOVER, PHONE);
-
- PLATFORM_PERMISSIONS.put(Manifest.permission.RECORD_AUDIO, MICROPHONE);
-// if (SDK_INT >= Build.VERSION_CODES.S) {
- PLATFORM_PERMISSIONS.put(Manifest.permission.RECORD_BACKGROUND_AUDIO, MICROPHONE);
-// }
-
- PLATFORM_PERMISSIONS.put(Manifest.permission.ACTIVITY_RECOGNITION, ACTIVITY_RECOGNITION);
-
- PLATFORM_PERMISSIONS.put(Manifest.permission.CAMERA, CAMERA);
-// if (SDK_INT >= Build.VERSION_CODES.S) {
- PLATFORM_PERMISSIONS.put(Manifest.permission.BACKGROUND_CAMERA, CAMERA);
-// }
-
- PLATFORM_PERMISSIONS.put(Manifest.permission.BODY_SENSORS, SENSORS);
-
-// if (SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- PLATFORM_PERMISSIONS.put(Manifest.permission.POST_NOTIFICATIONS, NOTIFICATIONS);
- PLATFORM_PERMISSIONS.put(Manifest.permission.BODY_SENSORS_BACKGROUND, SENSORS);
-// }
-
- PLATFORM_PERMISSION_GROUPS = new ArrayMap<>();
- int numPlatformPermissions = PLATFORM_PERMISSIONS.size();
- for (int i = 0; i < numPlatformPermissions; i++) {
- String permission = PLATFORM_PERMISSIONS.keyAt(i);
- String permissionGroup = PLATFORM_PERMISSIONS.valueAt(i);
-
- ArrayList permissionsOfThisGroup = PLATFORM_PERMISSION_GROUPS.get(
- permissionGroup);
- if (permissionsOfThisGroup == null) {
- permissionsOfThisGroup = new ArrayList<>();
- PLATFORM_PERMISSION_GROUPS.put(permissionGroup, permissionsOfThisGroup);
- }
-
- permissionsOfThisGroup.add(permission);
- }
-
- ONE_TIME_PERMISSION_GROUPS = new ArraySet<>();
- ONE_TIME_PERMISSION_GROUPS.add(LOCATION);
- ONE_TIME_PERMISSION_GROUPS.add(CAMERA);
- ONE_TIME_PERMISSION_GROUPS.add(MICROPHONE);
-
- PERM_SENSOR_CODES = new ArrayMap<>();
-// if (SDK_INT >= Build.VERSION_CODES.S) {
- PERM_SENSOR_CODES.put(CAMERA, SensorPrivacyManager.Sensors.CAMERA);
- PERM_SENSOR_CODES.put(MICROPHONE, SensorPrivacyManager.Sensors.MICROPHONE);
-// }
-
- }
-
- private Utils() {
- /* do nothing - hide constructor */
- }
-
- private static ArrayMap sUserContexts = new ArrayMap<>();
-
- /**
- * Creates and caches a PackageContext for the requested user, or returns the previously cached
- * value. The package of the PackageContext is the application's package.
- *
- * @param app The currently running application
- * @param user The desired user for the context
- *
- * @return The generated or cached Context for the requested user
- *
- * @throws PackageManager.NameNotFoundException If the app has no package name attached
- */
- public static @NonNull Context getUserContext(Application app, UserHandle user) throws
- PackageManager.NameNotFoundException {
- if (!sUserContexts.containsKey(user)) {
- sUserContexts.put(user, app.getApplicationContext()
- .createPackageContextAsUser(app.getPackageName(), 0, user));
- }
- return sUserContexts.get(user);
- }
-
- /**
- * Returns true if a permission is dangerous, installed, and not removed
- * @param permissionInfo The permission we wish to check
- * @return If all of the conditions are met
- */
- public static boolean isPermissionDangerousInstalledNotRemoved(PermissionInfo permissionInfo) {
- return permissionInfo != null
- && permissionInfo.getProtection() == PermissionInfo.PROTECTION_DANGEROUS
- && (permissionInfo.flags & PermissionInfo.FLAG_INSTALLED) != 0
- && (permissionInfo.flags & PermissionInfo.FLAG_REMOVED) == 0;
- }
-
- /**
- * Get permission group a platform permission belongs to, or null if the permission is not a
- * platform permission.
- *
- * @param permission the permission to resolve
- *
- * @return The group the permission belongs to
- */
- public static @Nullable String getGroupOfPlatformPermission(@NonNull String permission) {
- return PLATFORM_PERMISSIONS.get(permission);
- }
-
- /**
- * Get name of the permission group a permission belongs to.
- *
- * @param permission the {@link PermissionInfo info} of the permission to resolve
- *
- * @return The group the permission belongs to
- */
- public static @Nullable String getGroupOfPermission(@NonNull PermissionInfo permission) {
- String groupName = Utils.getGroupOfPlatformPermission(permission.name);
- if (groupName == null) {
- groupName = permission.group;
- }
-
- return groupName;
- }
-
- /**
- * Get the names for all platform permissions belonging to a group.
- *
- * @param group the group
- *
- * @return The permission names or an empty list if the
- * group is not does not have platform runtime permissions
- */
- public static @NonNull List getPlatformPermissionNamesOfGroup(@NonNull String group) {
- final ArrayList permissions = PLATFORM_PERMISSION_GROUPS.get(group);
- return (permissions != null) ? permissions : Collections.emptyList();
- }
-
- /**
- * Get the {@link PermissionInfo infos} for all platform permissions belonging to a group.
- *
- * @param pm Package manager to use to resolve permission infos
- * @param group the group
- *
- * @return The infos for platform permissions belonging to the group or an empty list if the
- * group is not does not have platform runtime permissions
- */
- public static @NonNull List getPlatformPermissionsOfGroup(
- @NonNull PackageManager pm, @NonNull String group) {
- ArrayList permInfos = new ArrayList<>();
-
- ArrayList permissions = PLATFORM_PERMISSION_GROUPS.get(group);
- if (permissions == null) {
- return Collections.emptyList();
- }
-
- int numPermissions = permissions.size();
- for (int i = 0; i < numPermissions; i++) {
- String permName = permissions.get(i);
- PermissionInfo permInfo;
- try {
- permInfo = pm.getPermissionInfo(permName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- throw new IllegalStateException(permName + " not defined by platform", e);
- }
-
- permInfos.add(permInfo);
- }
-
- return permInfos;
- }
-
- /**
- * Get the {@link PermissionInfo infos} for all permission infos belonging to a group.
- *
- * @param pm Package manager to use to resolve permission infos
- * @param group the group
- *
- * @return The infos of permissions belonging to the group or an empty list if the group
- * does not have runtime permissions
- */
- public static @NonNull List getPermissionInfosForGroup(
- @NonNull PackageManager pm, @NonNull String group)
- throws PackageManager.NameNotFoundException {
- List permissions = pm.queryPermissionsByGroup(group, 0);
- permissions.addAll(getPlatformPermissionsOfGroup(pm, group));
-
- /*
- * If the undefined group is requested, the package manager will return all platform
- * permissions, since they are marked as Undefined in the manifest. Do not return these
- * permissions.
- */
- if (group.equals(Manifest.permission_group.UNDEFINED)) {
- List undefinedPerms = new ArrayList<>();
- for (PermissionInfo permissionInfo : permissions) {
- String permGroup = getGroupOfPlatformPermission(permissionInfo.name);
- if (permGroup == null || permGroup.equals(Manifest.permission_group.UNDEFINED)) {
- undefinedPerms.add(permissionInfo);
- }
- }
- return undefinedPerms;
- }
-
- return permissions;
- }
-
- /**
- * Get the {@link PermissionInfo infos} for all runtime installed permission infos belonging to
- * a group.
- *
- * @param pm Package manager to use to resolve permission infos
- * @param group the group
- *
- * @return The infos of installed runtime permissions belonging to the group or an empty list
- * if the group does not have runtime permissions
- */
- public static @NonNull List getInstalledRuntimePermissionInfosForGroup(
- @NonNull PackageManager pm, @NonNull String group)
- throws PackageManager.NameNotFoundException {
- List permissions = pm.queryPermissionsByGroup(group, 0);
- permissions.addAll(getPlatformPermissionsOfGroup(pm, group));
-
- List installedRuntime = new ArrayList<>();
- for (PermissionInfo permissionInfo: permissions) {
- if (permissionInfo.getProtection() == PermissionInfo.PROTECTION_DANGEROUS
- && (permissionInfo.flags & PermissionInfo.FLAG_INSTALLED) != 0
- && (permissionInfo.flags & PermissionInfo.FLAG_REMOVED) == 0) {
- installedRuntime.add(permissionInfo);
- }
- }
-
- /*
- * If the undefined group is requested, the package manager will return all platform
- * permissions, since they are marked as Undefined in the manifest. Do not return these
- * permissions.
- */
- if (group.equals(Manifest.permission_group.UNDEFINED)) {
- List undefinedPerms = new ArrayList<>();
- for (PermissionInfo permissionInfo : installedRuntime) {
- String permGroup = getGroupOfPlatformPermission(permissionInfo.name);
- if (permGroup == null || permGroup.equals(Manifest.permission_group.UNDEFINED)) {
- undefinedPerms.add(permissionInfo);
- }
- }
- return undefinedPerms;
- }
-
- return installedRuntime;
- }
-
- /**
- * Get the {@link PackageItemInfo infos} for the given permission group.
- *
- * @param groupName the group
- * @param context the {@code Context} to retrieve {@code PackageManager}
- *
- * @return The info of permission group or null if the group does not have runtime permissions.
- */
- public static @Nullable PackageItemInfo getGroupInfo(@NonNull String groupName,
- @NonNull Context context) {
- try {
- return context.getPackageManager().getPermissionGroupInfo(groupName, 0);
- } catch (NameNotFoundException e) {
- /* ignore */
- }
- try {
- return context.getPackageManager().getPermissionInfo(groupName, 0);
- } catch (NameNotFoundException e) {
- /* ignore */
- }
- return null;
- }
-
- /**
- * Get the {@link PermissionInfo infos} for all permission infos belonging to a group.
- *
- * @param groupName the group
- * @param context the {@code Context} to retrieve {@code PackageManager}
- *
- * @return The infos of permissions belonging to the group or null if the group does not have
- * runtime permissions.
- */
- public static @Nullable List getGroupPermissionInfos(@NonNull String groupName,
- @NonNull Context context) {
- try {
- return Utils.getPermissionInfosForGroup(context.getPackageManager(), groupName);
- } catch (NameNotFoundException e) {
- /* ignore */
- }
- try {
- PermissionInfo permissionInfo = context.getPackageManager()
- .getPermissionInfo(groupName, 0);
- List permissions = new ArrayList<>();
- permissions.add(permissionInfo);
- return permissions;
- } catch (NameNotFoundException e) {
- /* ignore */
- }
- return null;
- }
-
- /**
- * Get the names of the platform permission groups.
- *
- * @return the names of the platform permission groups.
- */
- public static List getPlatformPermissionGroups() {
- return new ArrayList<>(PLATFORM_PERMISSION_GROUPS.keySet());
- }
-
- /**
- * Get the names of the runtime platform permissions
- *
- * @return the names of the runtime platform permissions.
- */
- public static List getRuntimePlatformPermissionNames() {
- return new ArrayList<>(PLATFORM_PERMISSIONS.keySet());
- }
-
- /**
- * Is the permissions a platform runtime permission
- *
- * @return the names of the runtime platform permissions.
- */
- public static boolean isRuntimePlatformPermission(@NonNull String permission) {
- return PLATFORM_PERMISSIONS.containsKey(permission);
- }
-
- /**
- * Is the group or background group user sensitive?
- *
- * @param group The group that might be user sensitive
- *
- * @return {@code true} if the group (or it's subgroup) is user sensitive.
- */
- public static boolean isGroupOrBgGroupUserSensitive(AppPermissionGroup group) {
- return group.isUserSensitive() || (group.getBackgroundPermissions() != null
- && group.getBackgroundPermissions().isUserSensitive());
- }
-
- /**
- * Whether or not the given package has non-isolated storage permissions
- * @param context The current context
- * @param packageName The package name to check
- * @return True if the package has access to non-isolated storage, false otherwise
- * @throws NameNotFoundException
- */
- public static boolean isNonIsolatedStorage(@NonNull Context context,
- @NonNull String packageName) throws NameNotFoundException {
- PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packageName, 0);
- AppOpsManager manager = context.getSystemService(AppOpsManager.class);
-
-
- return packageInfo.applicationInfo.targetSdkVersion < Build.VERSION_CODES.P
- || (packageInfo.applicationInfo.targetSdkVersion < Build.VERSION_CODES.R
- && manager.unsafeCheckOpNoThrow(OPSTR_LEGACY_STORAGE,
- packageInfo.applicationInfo.uid, packageInfo.packageName) == MODE_ALLOWED);
- }
-
- /**
- * Build a string representing the given time if it happened on the current day and the date
- * otherwise.
- *
- * @param context the context.
- * @param lastAccessTime the time in milliseconds.
- *
- * @return a string representing the time or date of the given time or null if the time is 0.
- */
- public static @Nullable String getAbsoluteTimeString(@NonNull Context context,
- long lastAccessTime) {
- if (lastAccessTime == 0) {
- return null;
- }
- if (isToday(lastAccessTime)) {
- return DateFormat.getTimeFormat(context).format(lastAccessTime);
- } else {
- return DateFormat.getMediumDateFormat(context).format(lastAccessTime);
- }
- }
-
- /**
- * Check whether the given time (in milliseconds) is in the current day.
- *
- * @param time the time in milliseconds
- *
- * @return whether the given time is in the current day.
- */
- private static boolean isToday(long time) {
- Calendar today = Calendar.getInstance(Locale.getDefault());
- today.setTimeInMillis(System.currentTimeMillis());
- today.set(Calendar.HOUR_OF_DAY, 0);
- today.set(Calendar.MINUTE, 0);
- today.set(Calendar.SECOND, 0);
- today.set(Calendar.MILLISECOND, 0);
-
- Calendar date = Calendar.getInstance(Locale.getDefault());
- date.setTimeInMillis(time);
- return !date.before(today);
- }
-
- /**
- * Whether the Location Access Check is enabled.
- *
- * @return {@code true} iff the Location Access Check is enabled.
- */
- public static boolean isLocationAccessCheckEnabled() {
- return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_LOCATION_ACCESS_CHECK_ENABLED, true);
- }
-
- /**
- * Get one time permissions timeout
- */
- public static long getOneTimePermissionsTimeout() {
- return DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS,
- PROPERTY_ONE_TIME_PERMISSIONS_TIMEOUT_MILLIS, ONE_TIME_PERMISSIONS_TIMEOUT_MILLIS);
- }
-
- /**
- * Returns the delay in milliseconds before revoking permissions at the end of a one-time
- * permission session if all processes have been killed.
- * If the session was triggered by a self-revocation, then revocation should happen
- * immediately. For a regular one-time permission session, a grace period allows a quick
- * app restart without losing the permission.
- * @param isSelfRevoked If true, return the delay for a self-revocation session. Otherwise,
- * return delay for a regular one-time permission session.
- */
- public static long getOneTimePermissionsKilledDelay(boolean isSelfRevoked) {
- if (isSelfRevoked) {
- // For a self-revoked session, we revoke immediately when the process dies.
- return 0;
- }
- return DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS,
- PROPERTY_ONE_TIME_PERMISSIONS_KILLED_DELAY_MILLIS,
- ONE_TIME_PERMISSIONS_KILLED_DELAY_MILLIS);
- }
-
- /**
- * Whether the permission group supports one-time
- * @param permissionGroup The permission group to check
- * @return {@code true} iff the group supports one-time
- */
- public static boolean supportsOneTimeGrant(String permissionGroup) {
- return ONE_TIME_PERMISSION_GROUPS.contains(permissionGroup);
- }
-
- /**
- * Checks whether a package has an active one-time permission according to the system server's
- * flags
- *
- * @param context the {@code Context} to retrieve {@code PackageManager}
- * @param packageName The package to check for
- * @return Whether a package has an active one-time permission
- */
- public static boolean hasOneTimePermissions(Context context, String packageName) {
- String[] permissions;
- PackageManager pm = context.getPackageManager();
- try {
- permissions = pm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS)
- .requestedPermissions;
- } catch (NameNotFoundException e) {
- Log.w(LOG_TAG, "Checking for one-time permissions in nonexistent package");
- return false;
- }
- if (permissions == null) {
- return false;
- }
- for (String permissionName : permissions) {
- if ((pm.getPermissionFlags(permissionName, packageName, Process.myUserHandle())
- & PackageManager.FLAG_PERMISSION_ONE_TIME) != 0
- && pm.checkPermission(permissionName, packageName)
- == PackageManager.PERMISSION_GRANTED) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Gets the label of the Settings application
- *
- * @param pm The packageManager used to get the activity resolution
- *
- * @return The CharSequence title of the settings app
- */
- @Nullable
- public static CharSequence getSettingsLabelForNotifications(PackageManager pm) {
- // We pretend we're the Settings app sending the notification, so figure out its name.
- Intent openSettingsIntent = new Intent(Settings.ACTION_SETTINGS);
- ResolveInfo resolveInfo = pm.resolveActivity(openSettingsIntent, MATCH_SYSTEM_ONLY);
- if (resolveInfo == null) {
- return null;
- }
- return pm.getApplicationLabel(resolveInfo.activityInfo.applicationInfo);
- }
-
- /**
- * Get all the exempted packages.
- */
- public static Set getExemptedPackages(@NonNull RoleManager roleManager) {
- Set exemptedPackages = new HashSet<>();
-
- exemptedPackages.add(SYSTEM_PKG);
- for (int i = 0; i < EXEMPTED_ROLES.length; i++) {
- exemptedPackages.addAll(roleManager.getRoleHolders(EXEMPTED_ROLES[i]));
- }
-
- return exemptedPackages;
- }
-
- /**
- * Returns if the permission group is Camera or Microphone (status bar indicators).
- **/
- public static boolean isStatusBarIndicatorPermission(@NonNull String permissionGroupName) {
- return CAMERA.equals(permissionGroupName) || MICROPHONE.equals(permissionGroupName);
- }
-
- /**
- * Navigate to notification settings for all apps
- * @param context The current Context
- */
- public static void navigateToNotificationSettings(@NonNull Context context) {
- Intent notificationIntent = new Intent(Settings.ACTION_ALL_APPS_NOTIFICATION_SETTINGS);
- context.startActivity(notificationIntent);
- }
-
- /**
- * Navigate to notification settings for an app
- * @param context The current Context
- * @param packageName The package to navigate to
- * @param user Specifies the user of the package which should be navigated to. If null, the
- * current user is used.
- */
- public static void navigateToAppNotificationSettings(@NonNull Context context,
- @NonNull String packageName, @NonNull UserHandle user) {
- Intent notificationIntent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
- notificationIntent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
- context.startActivityAsUser(notificationIntent, user);
- }
-
- /**
- * Returns if a card should be shown if the sensor is blocked
- **/
- public static boolean shouldDisplayCardIfBlocked(@NonNull String permissionGroupName) {
- return DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_PRIVACY, PROPERTY_WARNING_BANNER_DISPLAY_ENABLED, true) && (
- CAMERA.equals(permissionGroupName) || MICROPHONE.equals(permissionGroupName)
- || LOCATION.equals(permissionGroupName));
- }
-}