diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java index 395cf1805b63d..70745ba0b3684 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java +++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java @@ -24,8 +24,6 @@ import static android.content.ComponentName.createRelative; import static com.android.server.companion.Utils.prepareForIpc; -import static java.nio.charset.StandardCharsets.UTF_8; - import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.PendingIntent; @@ -41,23 +39,17 @@ import android.os.Bundle; import android.os.Handler; import android.os.ResultReceiver; import android.os.UserHandle; +import android.permission.PermissionControllerManager; import android.util.Slog; -import android.util.Xml; import com.android.server.companion.AssociationStore; import com.android.server.companion.CompanionDeviceManagerService; import com.android.server.companion.PermissionsUtils; -import com.android.server.companion.datatransfer.permbackup.BackupHelper; import com.android.server.companion.proto.CompanionMessage; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; /** * This processor builds user consent intent for a given SystemDataTransferRequest and processes the @@ -83,6 +75,8 @@ public class SystemDataTransferProcessor { private final AssociationStore mAssociationStore; private final SystemDataTransferRequestStore mSystemDataTransferRequestStore; private final CompanionMessageProcessor mCompanionMessageProcessor; + private final PermissionControllerManager mPermissionControllerManager; + private final ExecutorService mExecutor; public SystemDataTransferProcessor(CompanionDeviceManagerService service, AssociationStore associationStore, @@ -93,6 +87,8 @@ public class SystemDataTransferProcessor { mSystemDataTransferRequestStore = systemDataTransferRequestStore; mCompanionMessageProcessor = companionMessageProcessor; mCompanionMessageProcessor.setListener(this::onCompleteMessageReceived); + mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class); + mExecutor = Executors.newSingleThreadExecutor(); } /** @@ -180,23 +176,13 @@ public class SystemDataTransferProcessor { // TODO: Establish a secure channel - final long callingIdentityToken = Binder.clearCallingIdentity(); - // Start permission sync + final long callingIdentityToken = Binder.clearCallingIdentity(); try { - BackupHelper backupHelper = new BackupHelper(mContext, UserHandle.of(userId)); - XmlSerializer serializer = Xml.newSerializer(); - ByteArrayOutputStream backup = new ByteArrayOutputStream(); - serializer.setOutput(backup, UTF_8.name()); - - backupHelper.writeState(serializer); - - serializer.flush(); - - mCompanionMessageProcessor.paginateAndDispatchMessagesToApp(backup.toByteArray(), - CompanionMessage.PERMISSION_SYNC, packageName, userId, associationId); - } catch (IOException ioe) { - Slog.e(LOG_TAG, "Error while writing permission state."); + mPermissionControllerManager.getRuntimePermissionBackup(UserHandle.of(userId), + mExecutor, + backup -> mCompanionMessageProcessor.paginateAndDispatchMessagesToApp(backup, + CompanionMessage.PERMISSION_SYNC, packageName, userId, associationId)); } finally { Binder.restoreCallingIdentity(callingIdentityToken); } @@ -219,21 +205,11 @@ public class SystemDataTransferProcessor { private void processPermissionSyncMessage(CompanionMessageInfo messageInfo) { Slog.i(LOG_TAG, "Applying permissions."); // Start applying permissions + UserHandle user = mContext.getUser(); final long callingIdentityToken = Binder.clearCallingIdentity(); try { - BackupHelper backupHelper = new BackupHelper(mContext, mContext.getUser()); - XmlPullParser parser = Xml.newPullParser(); - ByteArrayInputStream stream = new ByteArrayInputStream( - messageInfo.getData()); - parser.setInput(stream, UTF_8.name()); - - backupHelper.restoreState(parser); - } catch (IOException e) { - Slog.e(LOG_TAG, "IOException reading message: " - + new String(messageInfo.getData())); - } catch (XmlPullParserException e) { - Slog.e(LOG_TAG, "Error parsing message: " - + new String(messageInfo.getData())); + mPermissionControllerManager.stageAndApplyRuntimePermissionsBackup( + messageInfo.getData(), user); } finally { Slog.i(LOG_TAG, "Permissions applied."); Binder.restoreCallingIdentity(callingIdentityToken); diff --git a/services/companion/java/com/android/server/companion/datatransfer/permbackup/BackupHelper.java b/services/companion/java/com/android/server/companion/datatransfer/permbackup/BackupHelper.java deleted file mode 100644 index 5e3c4c7834fe0..0000000000000 --- a/services/companion/java/com/android/server/companion/datatransfer/permbackup/BackupHelper.java +++ /dev/null @@ -1,782 +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; - -import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED; -import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; -import static android.content.pm.PackageManager.GET_PERMISSIONS; -import static android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES; - -import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; -import static org.xmlpull.v1.XmlPullParser.END_TAG; -import static org.xmlpull.v1.XmlPullParser.START_TAG; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.Signature; -import android.content.pm.SigningInfo; -import android.os.Build; -import android.os.UserHandle; -import android.permission.PermissionManager; -import android.permission.PermissionManager.SplitPermissionInfo; -import android.util.ArraySet; -import android.util.Log; -import android.util.Slog; - -import com.android.server.companion.datatransfer.permbackup.model.AppPermissionGroup; -import com.android.server.companion.datatransfer.permbackup.model.AppPermissions; -import com.android.server.companion.datatransfer.permbackup.model.Permission; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - -import java.io.IOException; -import java.security.cert.CertificateException; -import java.util.ArrayList; -import java.util.List; - -/** - * Helper for creating and restoring permission backups. - */ -public class BackupHelper { - private static final String LOG_TAG = BackupHelper.class.getSimpleName(); - - private static final String TAG_PERMISSION_BACKUP = "perm-grant-backup"; - private static final String ATTR_PLATFORM_VERSION = "version"; - - private static final String TAG_ALL_GRANTS = "rt-grants"; - - private static final String TAG_GRANT = "grant"; - private static final String ATTR_PACKAGE_NAME = "pkg"; - private static final String ATTR_HAS_MULTIPLE_SIGNERS = "multi-signers"; - - private static final String TAG_SIGNATURE = "sig"; - private static final String ATTR_SIGNATURE_VALUE = "v"; - - private static final String TAG_PERMISSION = "perm"; - private static final String ATTR_PERMISSION_NAME = "name"; - private static final String ATTR_IS_GRANTED = "g"; - private static final String ATTR_USER_SET = "set"; - private static final String ATTR_USER_FIXED = "fixed"; - private static final String ATTR_WAS_REVIEWED = "was-reviewed"; - - /** Flags of permissions to not back up */ - private static final int SYSTEM_RUNTIME_GRANT_MASK = FLAG_PERMISSION_POLICY_FIXED - | FLAG_PERMISSION_SYSTEM_FIXED; - - /** Make sure only one user can change the delayed permissions at a time */ - private static final Object sLock = new Object(); - - private final Context mContext; - - /** - * Create a new backup utils for a user. - * - * @param context A context to use - * @param user The user that is backed up / restored - */ - public BackupHelper(@NonNull Context context, @NonNull UserHandle user) { - try { - mContext = context.createPackageContextAsUser(context.getPackageName(), 0, user); - } catch (PackageManager.NameNotFoundException doesNotHappen) { - throw new IllegalStateException(); - } - } - - /** - * Forward parser and skip everything up to the end of the current tag. - * - * @param parser The parser to forward - */ - private static void skipToEndOfTag(@NonNull XmlPullParser parser) - throws IOException, XmlPullParserException { - int numOpenTags = 1; - while (numOpenTags > 0) { - switch (parser.next()) { - case START_TAG: - numOpenTags++; - break; - case END_TAG: - numOpenTags--; - break; - default: - // ignore - } - } - } - - /** - * Forward parser to a given direct sub-tag. - * - * @param parser The parser to forward - * @param tag The tag to search for - */ - private void skipToTag(@NonNull XmlPullParser parser, @NonNull String tag) - throws IOException, XmlPullParserException { - int type; - do { - type = parser.next(); - - switch (type) { - case START_TAG: - if (!parser.getName().equals(tag)) { - skipToEndOfTag(parser); - } - - return; - } - } while (type != END_DOCUMENT); - } - - /** - * Read a XML file and return the packages stored in it. - * - * @param parser The file to read - * - * @return The packages in this file - */ - private @NonNull ArrayList parseFromXml(@NonNull XmlPullParser parser) - throws IOException, XmlPullParserException { - ArrayList pkgStates = new ArrayList<>(); - - skipToTag(parser, TAG_PERMISSION_BACKUP); - - int backupPlatformVersion; - try { - backupPlatformVersion = Integer.parseInt( - parser.getAttributeValue(null, ATTR_PLATFORM_VERSION)); - } catch (NumberFormatException ignored) { - // Platforms P and before did not store the platform version - backupPlatformVersion = Build.VERSION_CODES.P; - } - - skipToTag(parser, TAG_ALL_GRANTS); - - if (parser.getEventType() != START_TAG && !parser.getName().equals(TAG_ALL_GRANTS)) { - throw new XmlPullParserException("Could not find " + TAG_PERMISSION_BACKUP + " > " - + TAG_ALL_GRANTS); - } - - // Read packages to restore from xml - int type; - do { - type = parser.next(); - - switch (type) { - case START_TAG: - switch (parser.getName()) { - case TAG_GRANT: - try { - pkgStates.add(BackupPackageState.parseFromXml(parser, mContext, - backupPlatformVersion)); - } catch (XmlPullParserException e) { - Log.e(LOG_TAG, "Could not parse permissions ", e); - skipToEndOfTag(parser); - } - break; - default: - // ignore tag - Log.w(LOG_TAG, "Found unexpected tag " + parser.getName() - + " during restore"); - skipToEndOfTag(parser); - } - } - } while (type != END_DOCUMENT); - - return pkgStates; - } - - /** - * Try to restore the permission state from XML. - * - * @param parser The xml to read - */ - public void restoreState(@NonNull XmlPullParser parser) throws IOException, - XmlPullParserException { - ArrayList pkgStates = parseFromXml(parser); - - ArrayList packagesToRestoreLater = new ArrayList<>(); - int numPkgStates = pkgStates.size(); - if (numPkgStates > 0) { - // Try to restore packages - for (int i = 0; i < numPkgStates; i++) { - BackupPackageState pkgState = pkgStates.get(i); - - PackageInfo pkgInfo; - try { - pkgInfo = mContext.getPackageManager().getPackageInfo(pkgState.mPackageName, - GET_PERMISSIONS | GET_SIGNING_CERTIFICATES); - } catch (PackageManager.NameNotFoundException ignored) { - packagesToRestoreLater.add(pkgState); - continue; - } - - pkgState.restore(mContext, pkgInfo); - } - } - -// synchronized (sLock) { -// writeDelayedStorePkgsLocked(packagesToRestoreLater); -// } - } - - /** - * Write a xml file for the given packages. - * - * @param serializer The file to write to - * @param pkgs The packages to write - */ - private static void writePkgsAsXml(@NonNull XmlSerializer serializer, - @NonNull ArrayList pkgs) throws IOException { - serializer.startDocument(null, true); - - serializer.startTag(null, TAG_PERMISSION_BACKUP); - -// if (SDK_INT >= Build.VERSION_CODES.Q) { - // STOPSHIP: Remove compatibility code once Q SDK level is declared - serializer.attribute(null, ATTR_PLATFORM_VERSION, - Integer.valueOf(Build.VERSION_CODES.Q).toString()); -// } else { -// serializer.attribute(null, ATTR_PLATFORM_VERSION, -// Integer.valueOf(SDK_INT).toString()); -// } - - serializer.startTag(null, TAG_ALL_GRANTS); - - int numPkgs = pkgs.size(); - for (int i = 0; i < numPkgs; i++) { - BackupPackageState packageState = pkgs.get(i); - - if (packageState != null) { - packageState.writeAsXml(serializer); - } - } - - serializer.endTag(null, TAG_ALL_GRANTS); - serializer.endTag(null, TAG_PERMISSION_BACKUP); - - serializer.endDocument(); - } - - /** - * Write the state of all packages as XML. - * - * @param serializer The xml to write to - */ - public void writeState(@NonNull XmlSerializer serializer) throws IOException { - List pkgs = mContext.getPackageManager().getInstalledPackages( - GET_PERMISSIONS | GET_SIGNING_CERTIFICATES); - ArrayList backupPkgs = new ArrayList<>(); - - int numPkgs = pkgs.size(); - for (int i = 0; i < numPkgs; i++) { - BackupPackageState packageState = BackupPackageState.fromAppPermissions(mContext, - pkgs.get(i)); - - if (packageState != null) { - backupPkgs.add(packageState); - } - } - - writePkgsAsXml(serializer, backupPkgs); - } - - /** - * State that needs to be backed up for a permission. - */ - private static class BackupPermissionState { - private final @NonNull String mPermissionName; - private final boolean mIsGranted; - private final boolean mIsUserSet; - private final boolean mIsUserFixed; - private final boolean mWasReviewed; - - private BackupPermissionState(@NonNull String permissionName, boolean isGranted, - boolean isUserSet, boolean isUserFixed, boolean wasReviewed) { - mPermissionName = permissionName; - mIsGranted = isGranted; - mIsUserSet = isUserSet; - mIsUserFixed = isUserFixed; - mWasReviewed = wasReviewed; - } - - /** - * 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 List parseFromXml(@NonNull XmlPullParser parser, - @NonNull Context context, int backupPlatformVersion) - throws XmlPullParserException { - String permName = parser.getAttributeValue(null, ATTR_PERMISSION_NAME); - if (permName == null) { - throw new XmlPullParserException("Found " + TAG_PERMISSION + " without " - + ATTR_PERMISSION_NAME); - } - - ArrayList expandedPermissions = new ArrayList<>(); - expandedPermissions.add(permName); - - List splitPerms = context.getSystemService( - PermissionManager.class).getSplitPermissions(); - - // Expand the properties to permissions that were split between the platform version the - // backup was taken and the current version. - int numSplitPerms = splitPerms.size(); - for (int i = 0; i < numSplitPerms; i++) { - SplitPermissionInfo splitPerm = splitPerms.get(i); - if (backupPlatformVersion < splitPerm.getTargetSdk() - && permName.equals(splitPerm.getSplitPermission())) { - expandedPermissions.addAll(splitPerm.getNewPermissions()); - } - } - - ArrayList 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)); - } -}