diff options
author | Nate Myren <ntmyren@google.com> | 2019-11-01 14:36:47 -0700 |
---|---|---|
committer | Nate Myren <ntmyren@google.com> | 2019-11-01 21:39:08 +0000 |
commit | 6ac1ace3308b35619c5067c012ddada80bc7b362 (patch) | |
tree | 1c7129dfe8c50ea398be71a956ba983a232e8ada /PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java | |
parent | df9488fa1f42971057fe9f3ea43b6b5f7149acf9 (diff) | |
download | Permission-6ac1ace3308b35619c5067c012ddada80bc7b362.tar.gz |
Refactor packageinstaller to permissioncontroller V2
renamed "packageinstaller" package to "permissioncontroller"
Fixes: 141707344
Test: flash new permissioncontroller, ensure UI still functions
correctly.
Change-Id: Ib58c0486cf148d1ca3167fdf8cb6d185eef78f64
Diffstat (limited to 'PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java')
-rw-r--r-- | PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java | 812 |
1 files changed, 812 insertions, 0 deletions
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java new file mode 100644 index 000000000..0125660e7 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java @@ -0,0 +1,812 @@ +/* + * 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.permissioncontroller.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 android.widget.RadioButton; + +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.PreferenceViewHolder; +import androidx.preference.TwoStatePreference; + +import com.android.permissioncontroller.R; +import com.android.permissioncontroller.auto.AutoSettingsFrameFragment; +import com.android.permissioncontroller.permission.model.AppPermissionGroup; +import com.android.permissioncontroller.permission.model.Permission; +import com.android.permissioncontroller.permission.utils.LocationUtils; +import com.android.permissioncontroller.permission.utils.PackageRemovalMonitor; +import com.android.permissioncontroller.permission.utils.SafetyNetLogger; +import com.android.permissioncontroller.permission.utils.Utils; +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); + setLayoutResource(R.layout.car_radio_button_preference); + setWidgetLayoutResource(R.layout.radio_button_preference_widget); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + + RadioButton radioButton = (RadioButton) holder.findViewById(R.id.radio_button); + radioButton.setChecked(isChecked()); + } + } + + /** + * 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(); + } + } + } +} |