Use PermissionControllerManager to get permission groups

The mapping of permissions and permission groups may be changed upon
mainline module update, so we cannot reliably read from the platform.
PermissionControllerManager is created for this purpose, so we use it
to prevent from the outdated mapping instead of mapping them manually.

Bug: 117978938
Test: robotests

Change-Id: If8682796b8a30dee3b73572e977fade48d07eb2b
This commit is contained in:
Yi-Ling Chuang
2019-04-24 21:15:11 +08:00
parent 9a4fa9bf0d
commit 0789b5d226
2 changed files with 86 additions and 214 deletions

View File

@@ -16,38 +16,47 @@ package com.android.settings.applications;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.icu.text.ListFormatter; import android.icu.text.ListFormatter;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.core.BasePreferenceController; import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.applications.PermissionsSummaryHelper;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
public class AppPermissionsPreferenceController extends BasePreferenceController { public class AppPermissionsPreferenceController extends BasePreferenceController {
private static final String TAG = "AppPermissionPrefCtrl"; private static final String TAG = "AppPermissionPrefCtrl";
private static final String[] PERMISSION_GROUPS = new String[]{ private static int NUM_PACKAGE_TO_CHECK = 3;
"android.permission-group.LOCATION",
"android.permission-group.MICROPHONE",
"android.permission-group.CAMERA",
"android.permission-group.SMS",
"android.permission-group.CONTACTS",
"android.permission-group.PHONE"};
private static final int NUM_PERMISSION_TO_USE = 3;
private final PackageManager mPackageManager; private final PackageManager mPackageManager;
private final Set<CharSequence> mPermissionGroups;
private final PermissionsSummaryHelper.PermissionsResultCallback mPermissionsCallback =
new PermissionsSummaryHelper.PermissionsResultCallback() {
@Override
public void onPermissionSummaryResult(int standardGrantedPermissionCount,
int requestedPermissionCount, int additionalGrantedPermissionCount,
List<CharSequence> grantedGroupLabels) {
updateSummary(grantedGroupLabels);
}
};
@VisibleForTesting
int mNumPackageChecked;
private Preference mPreference;
public AppPermissionsPreferenceController(Context context, String preferenceKey) { public AppPermissionsPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey); super(context, preferenceKey);
mPackageManager = context.getPackageManager(); mPackageManager = context.getPackageManager();
mPermissionGroups = new ArraySet<>();
} }
@Override @Override
@@ -55,72 +64,41 @@ public class AppPermissionsPreferenceController extends BasePreferenceController
return AVAILABLE; return AVAILABLE;
} }
/*
Summary text looks like: Apps using Permission1, Permission2, Permission3
The 3 permissions are the first three from the list which any app has granted:
Location, Microphone, Camera, Sms, Contacts, and Phone
*/
@Override @Override
public CharSequence getSummary() { public void updateState(Preference preference) {
final Set<String> permissions = getAllPermissionsInGroups(); mPreference = preference;
Set<String> grantedPermissionGroups = getGrantedPermissionGroups(permissions); mNumPackageChecked = 0;
int count = 0; queryPermissionSummary();
final List<String> summaries = new ArrayList<>();
for (String group : PERMISSION_GROUPS) {
if (!grantedPermissionGroups.contains(group)) {
continue;
}
summaries.add(getPermissionGroupLabel(group).toString().toLowerCase());
if (++count >= NUM_PERMISSION_TO_USE) {
break;
}
}
return count > 0 ? mContext.getString(R.string.app_permissions_summary,
ListFormatter.getInstance().format(summaries)) : null;
} }
private Set<String> getGrantedPermissionGroups(Set<String> permissions) { @VisibleForTesting
ArraySet<String> grantedPermissionGroups = new ArraySet<>(); void queryPermissionSummary() {
List<PackageInfo> installedPackages = final List<PackageInfo> installedPackages =
mPackageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS); mPackageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS);
for (PackageInfo installedPackage : installedPackages) { // Here we only get the first three apps and check their permissions.
if (installedPackage.permissions == null) { final List<PackageInfo> packagesWithPermission = installedPackages.stream()
continue; .filter(pInfo -> pInfo.permissions != null)
} .limit(NUM_PACKAGE_TO_CHECK)
for (PermissionInfo permissionInfo : installedPackage.permissions) { .collect(Collectors.toList());
if (permissions.contains(permissionInfo.name)
&& !grantedPermissionGroups.contains(permissionInfo.group)) { for (PackageInfo installedPackage : packagesWithPermission) {
grantedPermissionGroups.add(permissionInfo.group); PermissionsSummaryHelper.getPermissionSummary(mContext,
} installedPackage.packageName, mPermissionsCallback);
}
} }
return grantedPermissionGroups;
} }
private CharSequence getPermissionGroupLabel(String group) { @VisibleForTesting
try { void updateSummary(List<CharSequence> grantedGroupLabels) {
final PermissionGroupInfo groupInfo = mPackageManager.getPermissionGroupInfo(group, 0); mPermissionGroups.addAll(grantedGroupLabels);
return groupInfo.loadLabel(mPackageManager); mNumPackageChecked++;
} catch (NameNotFoundException e) {
Log.e(TAG, "Error getting permissions label.", e);
}
return group;
}
private Set<String> getAllPermissionsInGroups() { if (mNumPackageChecked < NUM_PACKAGE_TO_CHECK) {
ArraySet<String> result = new ArraySet<>(); return;
for (String group : PERMISSION_GROUPS) {
try {
final List<PermissionInfo> permissions =
mPackageManager.queryPermissionsByGroup(group, 0);
for (PermissionInfo permissionInfo : permissions) {
result.add(permissionInfo.name);
}
} catch (NameNotFoundException e) {
Log.e(TAG, "Error getting permissions in group " + group, e);
}
} }
return result; final CharSequence summary = !mPermissionGroups.isEmpty()
? mContext.getString(R.string.app_permissions_summary,
ListFormatter.getInstance().format(mPermissionGroups).toLowerCase())
: null;
mPreference.setSummary(summary);
} }
} }

View File

@@ -18,130 +18,38 @@ package com.android.settings.applications;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import androidx.preference.Preference; import androidx.preference.Preference;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class AppPermissionsPreferenceControllerTest { public class AppPermissionsPreferenceControllerTest {
private static final String PERM_LOCATION = "android.permission-group.LOCATION";
private static final String PERM_MICROPHONE = "android.permission-group.MICROPHONE";
private static final String PERM_CAMERA = "android.permission-group.CAMERA";
private static final String PERM_SMS = "android.permission-group.SMS";
private static final String PERM_CONTACTS = "android.permission-group.CONTACTS";
private static final String PERM_PHONE = "android.permission-group.PHONE";
private static final String LABEL_LOCATION = "Location";
private static final String LABEL_MICROPHONE = "Microphone";
private static final String LABEL_CAMERA = "Camera";
private static final String LABEL_SMS = "Sms";
private static final String LABEL_CONTACTS = "Contacts";
private static final String LABEL_PHONE = "Phone";
@Mock
private Preference mPreference;
@Mock
private PackageManager mPackageManager;
@Mock
private PermissionGroupInfo mGroupLocation;
@Mock
private PermissionGroupInfo mGroupMic;
@Mock
private PermissionGroupInfo mGroupCamera;
@Mock
private PermissionGroupInfo mGroupSms;
@Mock
private PermissionGroupInfo mGroupContacts;
@Mock
private PermissionGroupInfo mGroupPhone;
private Context mContext; private Context mContext;
private AppPermissionsPreferenceController mController; private AppPermissionsPreferenceController mController;
private PermissionInfo mPermLocation; private Preference mPreference;
private PermissionInfo mPermMic;
private PermissionInfo mPermCamera;
private PermissionInfo mPermSms;
private PermissionInfo mPermContacts;
private PermissionInfo mPermPhone;
@Before @Before
public void setUp() throws NameNotFoundException { public void setUp() throws NameNotFoundException {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application); mContext = RuntimeEnvironment.application;
when(mContext.getPackageManager()).thenReturn(mPackageManager); mPreference = spy(new Preference(mContext));
// create permission groups
when(mPackageManager.getPermissionGroupInfo(eq(PERM_LOCATION), anyInt()))
.thenReturn(mGroupLocation);
when(mGroupLocation.loadLabel(mPackageManager)).thenReturn(LABEL_LOCATION);
when(mPackageManager.getPermissionGroupInfo(eq(PERM_MICROPHONE), anyInt()))
.thenReturn(mGroupMic);
when(mGroupMic.loadLabel(mPackageManager)).thenReturn(LABEL_MICROPHONE);
when(mPackageManager.getPermissionGroupInfo(eq(PERM_CAMERA), anyInt()))
.thenReturn(mGroupCamera);
when(mGroupCamera.loadLabel(mPackageManager)).thenReturn(LABEL_CAMERA);
when(mPackageManager.getPermissionGroupInfo(eq(PERM_SMS), anyInt())).thenReturn(mGroupSms);
when(mGroupSms.loadLabel(mPackageManager)).thenReturn(LABEL_SMS);
when(mPackageManager.getPermissionGroupInfo(eq(PERM_CONTACTS), anyInt()))
.thenReturn(mGroupContacts);
when(mGroupContacts.loadLabel(mPackageManager)).thenReturn(LABEL_CONTACTS);
when(mPackageManager.getPermissionGroupInfo(eq(PERM_PHONE), anyInt()))
.thenReturn(mGroupPhone);
when(mGroupPhone.loadLabel(mPackageManager)).thenReturn(LABEL_PHONE);
// create permissions
mPermLocation = new PermissionInfo();
mPermLocation.name = "Permission1";
mPermLocation.group = PERM_LOCATION;
mPermMic = new PermissionInfo();
mPermMic.name = "Permission2";
mPermMic.group = PERM_MICROPHONE;
mPermCamera = new PermissionInfo();
mPermCamera.name = "Permission3";
mPermCamera.group = PERM_CAMERA;
mPermSms = new PermissionInfo();
mPermSms.name = "Permission4";
mPermSms.group = PERM_SMS;
mPermContacts = new PermissionInfo();
mPermContacts.name = "Permission4";
mPermContacts.group = PERM_CONTACTS;
mPermPhone = new PermissionInfo();
mPermPhone.name = "Permission4";
mPermPhone.group = PERM_PHONE;
final List<PermissionInfo> permissions = new ArrayList<>();
permissions.add(mPermLocation);
permissions.add(mPermMic);
permissions.add(mPermCamera);
permissions.add(mPermSms);
permissions.add(mPermContacts);
permissions.add(mPermPhone);
when(mPackageManager.queryPermissionsByGroup(anyString(), anyInt()))
.thenReturn(permissions);
mController = spy(new AppPermissionsPreferenceController(mContext, "pref_key")); mController = spy(new AppPermissionsPreferenceController(mContext, "pref_key"));
} }
@@ -151,65 +59,51 @@ public class AppPermissionsPreferenceControllerTest {
} }
@Test @Test
public void updateState_noGrantedPermissions_shouldNotSetSummary() { public void updateState_shouldResetNumPackageChecked() {
final List<PackageInfo> installedPackages = new ArrayList<>(); doNothing().when(mController).queryPermissionSummary();
final PackageInfo info = new PackageInfo(); mController.mNumPackageChecked = 3;
installedPackages.add(info);
when(mPackageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS))
.thenReturn(installedPackages);
mController.updateState(mPreference); mController.updateState(mPreference);
verify(mPreference, never()).setSummary(anyString()); assertThat(mController.mNumPackageChecked).isEqualTo(0);
} }
@Test @Test
public void updateState_hasPermissions_shouldSetSummary() { public void updateSummary_noGrantedPermission_shouldSetNullSummary() {
final List<PackageInfo> installedPackages = new ArrayList<>(); doNothing().when(mController).queryPermissionSummary();
final PackageInfo info = new PackageInfo();
installedPackages.add(info);
when(mPackageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS))
.thenReturn(installedPackages);
PermissionInfo[] permissions = new PermissionInfo[4];
info.permissions = permissions;
permissions[0] = mPermLocation;
permissions[1] = mPermMic;
permissions[2] = mPermCamera;
permissions[3] = mPermSms;
mController.updateState(mPreference); mController.updateState(mPreference);
mController.mNumPackageChecked = 2;
verify(mPreference).setSummary("Apps using location, microphone, and camera"); mController.updateSummary(new ArrayList<>());
permissions[0] = mPermPhone; assertThat(mPreference.getSummary()).isNull();
permissions[1] = mPermMic; }
permissions[2] = mPermCamera;
permissions[3] = mPermSms; @Test
public void updateSummary_hasPermissionGroups_shouldSetPermissionAsSummary() {
doNothing().when(mController).queryPermissionSummary();
mController.updateState(mPreference); mController.updateState(mPreference);
final String permission = "location";
final ArrayList<CharSequence> labels = new ArrayList<>();
labels.add(permission);
final String summary = "Apps using " + permission;
mController.mNumPackageChecked = 2;
verify(mPreference).setSummary("Apps using microphone, camera, and sms"); mController.updateSummary(labels);
permissions[0] = mPermPhone; assertThat(mPreference.getSummary()).isEqualTo(summary);
permissions[1] = mPermMic; }
permissions[2] = mPermContacts;
permissions[3] = mPermSms; @Test
public void updateSummary_notReachCallbackCount_shouldNotSetSummary() {
doNothing().when(mController).queryPermissionSummary();
mController.updateState(mPreference); mController.updateState(mPreference);
final String permission = "location";
final ArrayList<CharSequence> labels = new ArrayList<>();
labels.add(permission);
verify(mPreference).setSummary("Apps using microphone, sms, and contacts"); mController.updateSummary(labels);
permissions = new PermissionInfo[2]; verify(mPreference, never()).setSummary(anyString());
info.permissions = permissions;
permissions[0] = mPermLocation;
permissions[1] = mPermCamera;
mController.updateState(mPreference);
verify(mPreference).setSummary("Apps using location and camera");
permissions = new PermissionInfo[1];
info.permissions = permissions;
permissions[0] = mPermCamera;
mController.updateState(mPreference);
verify(mPreference).setSummary("Apps using camera");
} }
} }