blob: ecbde6c5b7d34950e1e5678db78148191ee9085d [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.packageinstaller.permission.ui.auto;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
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.PermissionInfo;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.Log;
import android.view.View;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.res.TypedArrayUtils;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
import com.android.packageinstaller.auto.AutoSettingsFrameFragment;
import com.android.packageinstaller.permission.model.AppPermissionGroup;
import com.android.packageinstaller.permission.model.Permission;
import com.android.packageinstaller.permission.utils.LocationUtils;
import com.android.packageinstaller.permission.utils.PackageRemovalMonitor;
import com.android.packageinstaller.permission.utils.SafetyNetLogger;
import com.android.packageinstaller.permission.utils.Utils;
import com.android.permissioncontroller.R;
import com.android.settingslib.RestrictedLockUtils;
import java.lang.annotation.Retention;
import java.util.List;
/** Settings related to a particular permission for the given app. */
public class AutoAppPermissionFragment extends AutoSettingsFrameFragment {
private static final String LOG_TAG = "AppPermissionFragment";
@Retention(SOURCE)
@IntDef(value = {CHANGE_FOREGROUND, CHANGE_BACKGROUND}, flag = true)
@interface ChangeTarget {
}
static final int CHANGE_FOREGROUND = 1;
static final int CHANGE_BACKGROUND = 2;
static final int CHANGE_BOTH = CHANGE_FOREGROUND | CHANGE_BACKGROUND;
@NonNull
private AppPermissionGroup mGroup;
@NonNull
private TwoStatePreference mAlwaysPermissionPreference;
@NonNull
private TwoStatePreference mForegroundOnlyPermissionPreference;
@NonNull
private TwoStatePreference mDenyPermissionPreference;
@NonNull
private AutoTwoTargetPreference mDetailsPreference;
private boolean mHasConfirmedRevoke;
/**
* Listens for changes to the permission of the app the permission is currently getting
* granted to. {@code null} when unregistered.
*/
@Nullable
private PackageManager.OnPermissionsChangedListener mPermissionChangeListener;
/**
* Listens for changes to the app the permission is currently getting granted to. {@code null}
* when unregistered.
*/
@Nullable
private PackageRemovalMonitor mPackageRemovalMonitor;
/**
* Returns a new {@link AutoAppPermissionFragment}.
*
* @param packageName the package name for which the permission is being changed
* @param permName the name of the permission being changed
* @param groupName the name of the permission group being changed
* @param userHandle the user for which the permission is being changed
*/
@NonNull
public static AutoAppPermissionFragment newInstance(@NonNull String packageName,
@NonNull String permName, @Nullable String groupName, @NonNull UserHandle userHandle) {
AutoAppPermissionFragment fragment = new AutoAppPermissionFragment();
Bundle arguments = new Bundle();
arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
if (groupName == null) {
arguments.putString(Intent.EXTRA_PERMISSION_NAME, permName);
} else {
arguments.putString(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName);
}
arguments.putParcelable(Intent.EXTRA_USER, userHandle);
fragment.setArguments(arguments);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHasConfirmedRevoke = false;
mGroup = getAppPermissionGroup();
if (mGroup == null) {
requireActivity().setResult(Activity.RESULT_CANCELED);
requireActivity().finish();
return;
}
setHeaderLabel(
getContext().getString(R.string.app_permission_title, mGroup.getFullLabel()));
}
private AppPermissionGroup getAppPermissionGroup() {
Activity activity = getActivity();
Context context = getPreferenceManager().getContext();
String packageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
String groupName = getArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME);
if (groupName == null) {
groupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
}
PackageItemInfo groupInfo = Utils.getGroupInfo(groupName, context);
List<PermissionInfo> groupPermInfos = Utils.getGroupPermissionInfos(groupName, context);
if (groupInfo == null || groupPermInfos == null) {
Log.i(LOG_TAG, "Illegal group: " + groupName);
return null;
}
UserHandle userHandle = getArguments().getParcelable(Intent.EXTRA_USER);
PackageInfo packageInfo = AutoPermissionsUtils.getPackageInfo(activity, packageName,
userHandle);
if (packageInfo == null) {
Log.i(LOG_TAG, "PackageInfo is null");
return null;
}
AppPermissionGroup group = AppPermissionGroup.create(context, packageInfo, groupInfo,
groupPermInfos, false);
if (group == null || !Utils.shouldShowPermission(context, group)) {
Log.i(LOG_TAG, "Illegal group: " + (group == null ? "null" : group.getName()));
return null;
}
return group;
}
@Override
public void onCreatePreferences(Bundle bundle, String s) {
setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext()));
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
PreferenceScreen screen = getPreferenceScreen();
screen.addPreference(
AutoPermissionsUtils.createHeaderPreference(getContext(),
mGroup.getApp().applicationInfo));
// Add permissions selector preferences.
PreferenceGroup permissionSelector = new PreferenceCategory(getContext());
permissionSelector.setTitle(
getContext().getString(R.string.app_permission_header, mGroup.getFullLabel()));
screen.addPreference(permissionSelector);
mAlwaysPermissionPreference = new SelectedPermissionPreference(getContext());
mAlwaysPermissionPreference.setTitle(R.string.app_permission_button_allow_always);
permissionSelector.addPreference(mAlwaysPermissionPreference);
mForegroundOnlyPermissionPreference = new SelectedPermissionPreference(getContext());
mForegroundOnlyPermissionPreference.setTitle(
R.string.app_permission_button_allow_foreground);
permissionSelector.addPreference(mForegroundOnlyPermissionPreference);
mDenyPermissionPreference = new SelectedPermissionPreference(getContext());
mDenyPermissionPreference.setTitle(R.string.app_permission_button_deny);
permissionSelector.addPreference(mDenyPermissionPreference);
mDetailsPreference = new AutoTwoTargetPreference(getContext());
screen.addPreference(mDetailsPreference);
}
@Override
public void onStart() {
super.onStart();
Activity activity = requireActivity();
mPermissionChangeListener = new PermissionChangeListener(
mGroup.getApp().applicationInfo.uid);
PackageManager pm = activity.getPackageManager();
pm.addOnPermissionsChangeListener(mPermissionChangeListener);
// Get notified when the package is removed.
String packageName = mGroup.getApp().packageName;
mPackageRemovalMonitor = new PackageRemovalMonitor(getContext(), packageName) {
@Override
public void onPackageRemoved() {
Log.w(LOG_TAG, packageName + " was uninstalled");
activity.setResult(Activity.RESULT_CANCELED);
activity.finish();
}
};
mPackageRemovalMonitor.register();
// Check if the package was removed while this activity was not started.
try {
activity.createPackageContextAsUser(packageName, /* flags= */ 0,
mGroup.getUser()).getPackageManager().getPackageInfo(packageName,
/* flags= */ 0);
} catch (PackageManager.NameNotFoundException e) {
Log.w(LOG_TAG, packageName + " was uninstalled while this activity was stopped", e);
activity.setResult(Activity.RESULT_CANCELED);
activity.finish();
}
// Re-create the permission group in case permissions have changed and update the UI.
mGroup = getAppPermissionGroup();
updateUi();
}
@Override
public void onStop() {
super.onStop();
if (mPackageRemovalMonitor != null) {
mPackageRemovalMonitor.unregister();
mPackageRemovalMonitor = null;
}
if (mPermissionChangeListener != null) {
getActivity().getPackageManager().removeOnPermissionsChangeListener(
mPermissionChangeListener);
mPermissionChangeListener = null;
}
}
private void updateUi() {
mDetailsPreference.setOnSecondTargetClickListener(null);
mDetailsPreference.setVisible(false);
if (mGroup.areRuntimePermissionsGranted()) {
if (!mGroup.hasPermissionWithBackgroundMode()
|| (mGroup.getBackgroundPermissions() != null
&& mGroup.getBackgroundPermissions().areRuntimePermissionsGranted())) {
setSelectedPermissionState(mAlwaysPermissionPreference);
} else {
setSelectedPermissionState(mForegroundOnlyPermissionPreference);
}
} else {
setSelectedPermissionState(mDenyPermissionPreference);
}
mAlwaysPermissionPreference.setOnPreferenceClickListener(
v -> requestChange(/* requestGrant= */true, CHANGE_BOTH));
mForegroundOnlyPermissionPreference.setOnPreferenceClickListener(v -> {
requestChange(/* requestGrant= */false, CHANGE_BACKGROUND);
requestChange(/* requestGrant= */true, CHANGE_FOREGROUND);
return true;
});
mDenyPermissionPreference.setOnPreferenceClickListener(
v -> requestChange(/* requestGrant= */ false, CHANGE_BOTH));
// Set the allow and foreground-only button states appropriately.
if (mGroup.hasPermissionWithBackgroundMode()) {
if (mGroup.getBackgroundPermissions() == null) {
mAlwaysPermissionPreference.setVisible(false);
} else {
mForegroundOnlyPermissionPreference.setVisible(true);
mAlwaysPermissionPreference.setTitle(R.string.app_permission_button_allow_always);
}
} else {
mForegroundOnlyPermissionPreference.setVisible(false);
mAlwaysPermissionPreference.setTitle(R.string.app_permission_button_allow);
}
// Handle the UI for various special cases.
if (isSystemFixed() || isPolicyFullyFixed() || isForegroundDisabledByPolicy()) {
// Disable changing permissions and potentially show administrator message.
mAlwaysPermissionPreference.setEnabled(false);
mForegroundOnlyPermissionPreference.setEnabled(false);
mDenyPermissionPreference.setEnabled(false);
RestrictedLockUtils.EnforcedAdmin admin = getAdmin();
if (admin != null) {
mDetailsPreference.setWidgetLayoutResource(R.layout.info_preference_widget);
mDetailsPreference.setOnSecondTargetClickListener(
preference -> RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
getContext(), admin));
}
updateDetailForFixedByPolicyPermissionGroup();
} else if (Utils.areGroupPermissionsIndividuallyControlled(getContext(),
mGroup.getName())) {
// If the permissions are individually controlled, also show a link to the page that
// lets you control them.
mDetailsPreference.setWidgetLayoutResource(R.layout.settings_preference_widget);
mDetailsPreference.setOnSecondTargetClickListener(
preference -> showAllPermissions(mGroup.getName()));
updateDetailForIndividuallyControlledPermissionGroup();
} else {
if (mGroup.hasPermissionWithBackgroundMode()) {
if (mGroup.getBackgroundPermissions() == null) {
// The group has background permissions but the app did not request any. I.e.
// The app can only switch between 'never" and "only in foreground".
mAlwaysPermissionPreference.setEnabled(false);
mDenyPermissionPreference.setOnPreferenceClickListener(v -> requestChange(false,
CHANGE_FOREGROUND));
} else {
if (isBackgroundPolicyFixed()) {
// If background policy is fixed, we only allow switching the foreground.
// Note that this assumes that the background policy is fixed to deny,
// since if it is fixed to grant, so is the foreground.
mAlwaysPermissionPreference.setEnabled(false);
setSelectedPermissionState(mForegroundOnlyPermissionPreference);
mDenyPermissionPreference.setOnPreferenceClickListener(
v -> requestChange(false, CHANGE_FOREGROUND));
updateDetailForFixedByPolicyPermissionGroup();
} else if (isForegroundPolicyFixed()) {
// Foreground permissions are fixed to allow (the first case above handles
// fixing to deny), so we only allow toggling background permissions.
mDenyPermissionPreference.setEnabled(false);
mAlwaysPermissionPreference.setOnPreferenceClickListener(
v -> requestChange(true, CHANGE_BACKGROUND));
mForegroundOnlyPermissionPreference.setOnPreferenceClickListener(
v -> requestChange(false, CHANGE_BACKGROUND));
updateDetailForFixedByPolicyPermissionGroup();
} else {
// The default tri-state case is handled by default.
}
}
} else {
// The default bi-state case is handled by default.
}
}
}
/**
* Set the given permission state as the only checked permission state.
*/
private void setSelectedPermissionState(@NonNull TwoStatePreference permissionState) {
permissionState.setChecked(true);
if (permissionState != mAlwaysPermissionPreference) {
mAlwaysPermissionPreference.setChecked(false);
}
if (permissionState != mForegroundOnlyPermissionPreference) {
mForegroundOnlyPermissionPreference.setChecked(false);
}
if (permissionState != mDenyPermissionPreference) {
mDenyPermissionPreference.setChecked(false);
}
}
/**
* Are any permissions of this group fixed by the system, i.e. not changeable by the user.
*
* @return {@code true} iff any permission is fixed
*/
private boolean isSystemFixed() {
return mGroup.isSystemFixed();
}
/**
* Is any foreground permissions of this group fixed by the policy, i.e. not changeable by the
* user.
*
* @return {@code true} iff any foreground permission is fixed
*/
private boolean isForegroundPolicyFixed() {
return mGroup.isPolicyFixed();
}
/**
* Is any background permissions of this group fixed by the policy, i.e. not changeable by the
* user.
*
* @return {@code true} iff any background permission is fixed
*/
private boolean isBackgroundPolicyFixed() {
return mGroup.getBackgroundPermissions() != null
&& mGroup.getBackgroundPermissions().isPolicyFixed();
}
/**
* Are there permissions fixed, so that the user cannot change the preference at all?
*
* @return {@code true} iff the permissions of this group are fixed
*/
private boolean isPolicyFullyFixed() {
return isForegroundPolicyFixed() && (mGroup.getBackgroundPermissions() == null
|| isBackgroundPolicyFixed());
}
/**
* Is the foreground part of this group disabled. If the foreground is disabled, there is no
* need to possible grant background access.
*
* @return {@code true} iff the permissions of this group are fixed
*/
private boolean isForegroundDisabledByPolicy() {
return isForegroundPolicyFixed() && !mGroup.areRuntimePermissionsGranted();
}
/**
* Get the app that acts as admin for this profile.
*
* @return The admin or {@code null} if there is no admin.
*/
@Nullable
private RestrictedLockUtils.EnforcedAdmin getAdmin() {
return RestrictedLockUtils.getProfileOrDeviceOwner(getContext(), mGroup.getUser());
}
/**
* Update the detail in the case the permission group has individually controlled permissions.
*/
private void updateDetailForIndividuallyControlledPermissionGroup() {
int revokedCount = 0;
List<Permission> permissions = mGroup.getPermissions();
int permissionCount = permissions.size();
for (int i = 0; i < permissionCount; i++) {
Permission permission = permissions.get(i);
if (!permission.isGrantedIncludingAppOp()) {
revokedCount++;
}
}
int resId;
if (revokedCount == 0) {
resId = R.string.permission_revoked_none;
} else if (revokedCount == permissionCount) {
resId = R.string.permission_revoked_all;
} else {
resId = R.string.permission_revoked_count;
}
mDetailsPreference.setSummary(getContext().getString(resId, revokedCount));
mDetailsPreference.setVisible(true);
}
/**
* Update the detail of a permission group that is at least partially fixed by policy.
*/
private void updateDetailForFixedByPolicyPermissionGroup() {
RestrictedLockUtils.EnforcedAdmin admin = getAdmin();
AppPermissionGroup backgroundGroup = mGroup.getBackgroundPermissions();
boolean hasAdmin = admin != null;
if (isSystemFixed()) {
// Permission is fully controlled by the system and cannot be switched
setDetail(R.string.permission_summary_enabled_system_fixed);
} else if (isForegroundDisabledByPolicy()) {
// Permission is fully controlled by policy and cannot be switched
if (hasAdmin) {
setDetail(R.string.disabled_by_admin);
} else {
// Disabled state will be displayed by switch, so no need to add text for that
setDetail(R.string.permission_summary_enforced_by_policy);
}
} else if (isPolicyFullyFixed()) {
// Permission is fully controlled by policy and cannot be switched
if (backgroundGroup == null) {
if (hasAdmin) {
setDetail(R.string.enabled_by_admin);
} else {
// Enabled state will be displayed by switch, so no need to add text for
// that
setDetail(R.string.permission_summary_enforced_by_policy);
}
} else {
if (backgroundGroup.areRuntimePermissionsGranted()) {
if (hasAdmin) {
setDetail(R.string.enabled_by_admin);
} else {
// Enabled state will be displayed by switch, so no need to add text for
// that
setDetail(R.string.permission_summary_enforced_by_policy);
}
} else {
if (hasAdmin) {
setDetail(
R.string.permission_summary_enabled_by_admin_foreground_only);
} else {
setDetail(
R.string.permission_summary_enabled_by_policy_foreground_only);
}
}
}
} else {
// Part of the permission group can still be switched
if (isBackgroundPolicyFixed()) {
if (backgroundGroup.areRuntimePermissionsGranted()) {
if (hasAdmin) {
setDetail(R.string.permission_summary_enabled_by_admin_background_only);
} else {
setDetail(R.string.permission_summary_enabled_by_policy_background_only);
}
} else {
if (hasAdmin) {
setDetail(R.string.permission_summary_disabled_by_admin_background_only);
} else {
setDetail(R.string.permission_summary_disabled_by_policy_background_only);
}
}
} else if (isForegroundPolicyFixed()) {
if (hasAdmin) {
setDetail(R.string.permission_summary_enabled_by_admin_foreground_only);
} else {
setDetail(R.string.permission_summary_enabled_by_policy_foreground_only);
}
}
}
}
/**
* Show the given string as informative text below permission picker preferences.
*
* @param strId the resourceId of the string to display.
*/
private void setDetail(int strId) {
mDetailsPreference.setSummary(strId);
mDetailsPreference.setVisible(true);
}
/**
* Show all individual permissions in this group in a new fragment.
*/
private void showAllPermissions(@NonNull String filterGroup) {
Fragment frag = AutoAllAppPermissionsFragment.newInstance(mGroup.getApp().packageName,
filterGroup, UserHandle.getUserHandleForUid(mGroup.getApp().applicationInfo.uid));
getFragmentManager().beginTransaction()
.replace(android.R.id.content, frag)
.addToBackStack("AllPerms")
.commit();
}
/**
* Request to grant/revoke permissions group.
*
* <p>Does <u>not</u> handle:
* <ul>
* <li>Individually granted permissions</li>
* <li>Permission groups with background permissions</li>
* </ul>
* <p><u>Does</u> handle:
* <ul>
* <li>Default grant permissions</li>
* </ul>
*
* @param requestGrant If this group should be granted
* @param changeTarget Which permission group (foreground/background/both) should be changed
* @return If the request was processed.
*/
private boolean requestChange(boolean requestGrant, @ChangeTarget int changeTarget) {
if (LocationUtils.isLocationGroupAndProvider(getContext(), mGroup.getName(),
mGroup.getApp().packageName)) {
LocationUtils.showLocationDialog(getContext(),
Utils.getAppLabel(mGroup.getApp().applicationInfo, getContext()));
// The request was denied, so update the buttons.
updateUi();
return false;
}
if (requestGrant) {
if ((changeTarget & CHANGE_FOREGROUND) != 0) {
if (!mGroup.areRuntimePermissionsGranted()) {
SafetyNetLogger.logPermissionToggled(mGroup);
}
mGroup.grantRuntimePermissions(false);
}
if ((changeTarget & CHANGE_BACKGROUND) != 0) {
if (mGroup.getBackgroundPermissions() != null) {
if (!mGroup.getBackgroundPermissions().areRuntimePermissionsGranted()) {
SafetyNetLogger.logPermissionToggled(mGroup.getBackgroundPermissions());
}
mGroup.getBackgroundPermissions().grantRuntimePermissions(false);
}
}
} else {
boolean showDefaultDenyDialog = false;
if ((changeTarget & CHANGE_FOREGROUND) != 0
&& mGroup.areRuntimePermissionsGranted()) {
showDefaultDenyDialog = mGroup.hasGrantedByDefaultPermission()
|| !mGroup.doesSupportRuntimePermissions()
|| mGroup.hasInstallToRuntimeSplit();
}
if ((changeTarget & CHANGE_BACKGROUND) != 0) {
if (mGroup.getBackgroundPermissions() != null
&& mGroup.getBackgroundPermissions().areRuntimePermissionsGranted()) {
AppPermissionGroup bgPerm = mGroup.getBackgroundPermissions();
showDefaultDenyDialog |= bgPerm.hasGrantedByDefaultPermission()
|| !bgPerm.doesSupportRuntimePermissions()
|| bgPerm.hasInstallToRuntimeSplit();
}
}
if (showDefaultDenyDialog && !mHasConfirmedRevoke) {
showDefaultDenyDialog(changeTarget);
updateUi();
return false;
} else {
if ((changeTarget & CHANGE_FOREGROUND) != 0
&& mGroup.areRuntimePermissionsGranted()) {
if (mGroup.areRuntimePermissionsGranted()) {
SafetyNetLogger.logPermissionToggled(mGroup);
}
mGroup.revokeRuntimePermissions(false);
}
if ((changeTarget & CHANGE_BACKGROUND) != 0) {
if (mGroup.getBackgroundPermissions() != null
&& mGroup.getBackgroundPermissions().areRuntimePermissionsGranted()) {
if (mGroup.getBackgroundPermissions().areRuntimePermissionsGranted()) {
SafetyNetLogger.logPermissionToggled(mGroup.getBackgroundPermissions());
}
mGroup.getBackgroundPermissions().revokeRuntimePermissions(false);
}
}
}
}
updateUi();
return true;
}
/**
* Show a dialog that warns the user that she/he is about to revoke permissions that were
* granted by default.
*
* <p>The order of operation to revoke a permission granted by default is:
* <ol>
* <li>{@code showDefaultDenyDialog}</li>
* <li>{@link DefaultDenyDialog#onCreateDialog}</li>
* <li>{@link AutoAppPermissionFragment#onDenyAnyWay}</li>
* </ol>
*
* @param changeTarget Whether background or foreground should be changed
*/
private void showDefaultDenyDialog(@ChangeTarget int changeTarget) {
Bundle args = new Bundle();
boolean showGrantedByDefaultWarning = false;
if ((changeTarget & CHANGE_FOREGROUND) != 0) {
showGrantedByDefaultWarning = mGroup.hasGrantedByDefaultPermission();
}
if ((changeTarget & CHANGE_BACKGROUND) != 0) {
if (mGroup.getBackgroundPermissions() != null) {
showGrantedByDefaultWarning |=
mGroup.getBackgroundPermissions().hasGrantedByDefaultPermission();
}
}
args.putInt(DefaultDenyDialog.MSG, showGrantedByDefaultWarning ? R.string.system_warning
: R.string.old_sdk_deny_warning);
args.putInt(DefaultDenyDialog.CHANGE_TARGET, changeTarget);
DefaultDenyDialog defaultDenyDialog = new DefaultDenyDialog();
defaultDenyDialog.setArguments(args);
defaultDenyDialog.setTargetFragment(this, 0);
defaultDenyDialog.show(getFragmentManager().beginTransaction(),
DefaultDenyDialog.class.getName());
}
/**
* Once we user has confirmed that he/she wants to revoke a permission that was granted by
* default, actually revoke the permissions.
*
* @param changeTarget whether to change foreground, background, or both.
* @see #showDefaultDenyDialog(int)
*/
void onDenyAnyWay(@ChangeTarget int changeTarget) {
boolean hasDefaultPermissions = false;
if ((changeTarget & CHANGE_FOREGROUND) != 0) {
if (mGroup.areRuntimePermissionsGranted()) {
SafetyNetLogger.logPermissionToggled(mGroup);
}
mGroup.revokeRuntimePermissions(false);
hasDefaultPermissions = mGroup.hasGrantedByDefaultPermission();
}
if ((changeTarget & CHANGE_BACKGROUND) != 0) {
if (mGroup.getBackgroundPermissions() != null) {
if (mGroup.getBackgroundPermissions().areRuntimePermissionsGranted()) {
SafetyNetLogger.logPermissionToggled(mGroup.getBackgroundPermissions());
}
mGroup.getBackgroundPermissions().revokeRuntimePermissions(false);
hasDefaultPermissions |=
mGroup.getBackgroundPermissions().hasGrantedByDefaultPermission();
}
}
if (hasDefaultPermissions || !mGroup.doesSupportRuntimePermissions()) {
mHasConfirmedRevoke = true;
}
updateUi();
}
/** Preference used to represent apps that can be picked as a default app. */
private static class SelectedPermissionPreference extends TwoStatePreference {
SelectedPermissionPreference(Context context) {
super(context, null, TypedArrayUtils.getAttr(context, R.attr.preferenceStyle,
android.R.attr.preferenceStyle));
setPersistent(false);
}
@Override
public void setChecked(boolean checked) {
super.setChecked(checked);
setSummary(checked ? getContext().getString(R.string.car_permission_selected) : null);
}
}
/**
* A dialog warning the user that they are about to deny a permission that was granted by
* default.
*
* @see #showDefaultDenyDialog(int)
*/
public static class DefaultDenyDialog extends DialogFragment {
private static final String MSG = DefaultDenyDialog.class.getName() + ".arg.msg";
private static final String CHANGE_TARGET = DefaultDenyDialog.class.getName()
+ ".arg.changeTarget";
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AutoAppPermissionFragment fragment = (AutoAppPermissionFragment) getTargetFragment();
AlertDialog.Builder b = new AlertDialog.Builder(getContext())
.setMessage(getArguments().getInt(MSG))
.setNegativeButton(R.string.cancel,
(dialog, which) -> fragment.updateUi())
.setPositiveButton(R.string.grant_dialog_button_deny_anyway,
(dialog, which) ->
fragment.onDenyAnyWay(getArguments().getInt(CHANGE_TARGET)));
return b.create();
}
}
/**
* A listener for permission changes.
*/
private class PermissionChangeListener implements PackageManager.OnPermissionsChangedListener {
private final int mUid;
PermissionChangeListener(int uid) {
mUid = uid;
}
@Override
public void onPermissionsChanged(int uid) {
if (uid == mUid) {
Log.w(LOG_TAG, "Permissions changed.");
mGroup = getAppPermissionGroup();
updateUi();
}
}
}
}