/* * Copyright (C) 2015 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.handheld; import android.app.ActionBar; import android.app.AlertDialog; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; 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.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import android.view.MenuItem; import android.widget.Switch; import androidx.annotation.NonNull; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceGroup; import com.android.packageinstaller.permission.model.AppPermissionGroup; import com.android.packageinstaller.permission.model.Permission; import com.android.packageinstaller.permission.utils.ArrayUtils; import com.android.packageinstaller.permission.utils.Utils; import com.android.permissioncontroller.R; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * Show and manage individual permissions for an app. * *
Shows the list of individual runtime and non-runtime permissions the app has requested.
*/
public final class AllAppPermissionsFragment extends SettingsWithLargeHeader {
private static final String LOG_TAG = "AllAppPermissionsFragment";
private static final String KEY_OTHER = "other_perms";
private List For foreground or non background-foreground permissions this returns the group
* {@link AppPermissionGroup} the permission is in. For background permisisons this returns
* the group the matching foreground
*
* @param packageInfo Package information about the app
* @param permission The permission that belongs to a group
*
* @return the group the permissions belongs to
*/
private AppPermissionGroup getPermissionForegroundGroup(PackageInfo packageInfo,
String permission) {
AppPermissionGroup appPermissionGroup = null;
if (mGroups != null) {
final int groupCount = mGroups.size();
for (int i = 0; i < groupCount; i++) {
AppPermissionGroup currentPermissionGroup = mGroups.get(i);
if (currentPermissionGroup.hasPermission(permission)) {
appPermissionGroup = currentPermissionGroup;
break;
}
if (currentPermissionGroup.getBackgroundPermissions() != null
&& currentPermissionGroup.getBackgroundPermissions().hasPermission(
permission)) {
appPermissionGroup = currentPermissionGroup.getBackgroundPermissions();
break;
}
}
}
if (appPermissionGroup == null) {
appPermissionGroup = AppPermissionGroup.create(
getContext(), packageInfo, permission, false);
if (mGroups == null) {
mGroups = new ArrayList<>();
}
mGroups.add(appPermissionGroup);
}
return appPermissionGroup;
}
private static final class MyMultiTargetSwitchPreference extends MultiTargetSwitchPreference {
MyMultiTargetSwitchPreference(Context context, String permission,
AppPermissionGroup appPermissionGroup) {
super(context);
setChecked(appPermissionGroup.areRuntimePermissionsGranted(
new String[] {permission}));
setSwitchOnClickListener(v -> {
Switch switchView = (Switch) v;
if (switchView.isChecked()) {
appPermissionGroup.grantRuntimePermissions(false,
new String[]{permission});
// We are granting a permission from a group but since this is an
// individual permission control other permissions in the group may
// be revoked, hence we need to mark them user fixed to prevent the
// app from requesting a non-granted permission and it being granted
// because another permission in the group is granted. This applies
// only to apps that support runtime permissions.
if (appPermissionGroup.doesSupportRuntimePermissions()) {
int grantedCount = 0;
String[] revokedPermissionsToFix = null;
final int permissionCount = appPermissionGroup.getPermissions().size();
for (int i = 0; i < permissionCount; i++) {
Permission current = appPermissionGroup.getPermissions().get(i);
if (!current.isGrantedIncludingAppOp()) {
if (!current.isUserFixed()) {
revokedPermissionsToFix = ArrayUtils.appendString(
revokedPermissionsToFix, current.getName());
}
} else {
grantedCount++;
}
}
if (revokedPermissionsToFix != null) {
// If some permissions were not granted then they should be fixed.
appPermissionGroup.revokeRuntimePermissions(true,
revokedPermissionsToFix);
} else if (appPermissionGroup.getPermissions().size() == grantedCount) {
// If all permissions are granted then they should not be fixed.
appPermissionGroup.grantRuntimePermissions(false);
}
}
} else {
appPermissionGroup.revokeRuntimePermissions(true,
new String[]{permission});
// If we just revoked the last permission we need to clear
// the user fixed state as now the app should be able to
// request them at runtime if supported.
if (appPermissionGroup.doesSupportRuntimePermissions()
&& !appPermissionGroup.areRuntimePermissionsGranted()) {
appPermissionGroup.revokeRuntimePermissions(false);
}
}
});
}
}
}