Bug: 21589105 Rescoping SYSTEM_ALERT_WINDOW and WRITE_SETTINGS permission to an
explicit toggle to be enabled through Settings via Apps -> Advanced Apps. Added new and refactored an old xml to define the UX for two new Preferences in Advanced Settings. Modified the existing AdvancedAppSettings to add control flow for two new settings. Also enriched ManageApplications to handle these cases. Added additional strings in xml/values/strings.xml to support these settings. Also defined new classes to handle these the toggle of these permissions per app. Refactored codes from AppStateUsageBridge to a generic AppStateAppOpsBridge so that future usages related to AppOps can inherit from this class. Change-Id: I43b81282a063e05844c7805556a6d05cfc02bcdb
This commit is contained in:
301
src/com/android/settings/applications/AppStateAppOpsBridge.java
Normal file
301
src/com/android/settings/applications/AppStateAppOpsBridge.java
Normal file
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* 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.settings.applications;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.AppGlobals;
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.AppOpsManager.PackageOps;
|
||||
import android.content.Context;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.settingslib.applications.ApplicationsState;
|
||||
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
||||
import com.android.settingslib.applications.ApplicationsState.AppFilter;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
* Connects app ops info to the ApplicationsState. Makes use of AppOpsManager to
|
||||
* determine further permission level.
|
||||
*/
|
||||
public abstract class AppStateAppOpsBridge extends AppStateBaseBridge {
|
||||
|
||||
private static final String TAG = "AppStateAppOpsBridge";
|
||||
|
||||
private final IPackageManager mIPackageManager;
|
||||
private final UserManager mUserManager;
|
||||
private final List<UserHandle> mProfiles;
|
||||
private final AppOpsManager mAppOpsManager;
|
||||
private final Context mContext;
|
||||
private final int[] mAppOpsOpCodes;
|
||||
private final String[] mPermissions;
|
||||
|
||||
public AppStateAppOpsBridge(Context context, ApplicationsState appState, Callback callback,
|
||||
int appOpsOpCode, String permissionName) {
|
||||
super(appState, callback);
|
||||
mContext = context;
|
||||
mIPackageManager = AppGlobals.getPackageManager();
|
||||
mUserManager = UserManager.get(context);
|
||||
mProfiles = mUserManager.getUserProfiles();
|
||||
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
|
||||
mAppOpsOpCodes = new int[] {appOpsOpCode};
|
||||
mPermissions = new String[] {permissionName};
|
||||
}
|
||||
|
||||
private boolean isThisUserAProfileOfCurrentUser(final int userId) {
|
||||
final int profilesMax = mProfiles.size();
|
||||
for (int i = 0; i < profilesMax; i++) {
|
||||
if (mProfiles.get(i).getIdentifier() == userId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected abstract void updateExtraInfo(AppEntry app, String pkg, int uid);
|
||||
|
||||
public PermissionState getPermissionInfo(String pkg, int uid) {
|
||||
PermissionState permissionState = new PermissionState(pkg, new UserHandle(UserHandle
|
||||
.getUserId(uid)));
|
||||
try {
|
||||
permissionState.packageInfo = mIPackageManager.getPackageInfo(pkg,
|
||||
PackageManager.GET_PERMISSIONS, permissionState.userHandle.getIdentifier());
|
||||
// Check static permission state (whatever that is declared in package manifest)
|
||||
String[] requestedPermissions = permissionState.packageInfo.requestedPermissions;
|
||||
int[] permissionFlags = permissionState.packageInfo.requestedPermissionsFlags;
|
||||
if (requestedPermissions != null) {
|
||||
for (int i = 0; i < requestedPermissions.length; i++) {
|
||||
if (mPermissions[0].equals(requestedPermissions[i]) &&
|
||||
(permissionFlags[i] & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0) {
|
||||
permissionState.permissionDeclared = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check app op state.
|
||||
List<PackageOps> ops = mAppOpsManager.getOpsForPackage(uid, pkg, mAppOpsOpCodes);
|
||||
if (ops != null && ops.size() > 0 && ops.get(0).getOps().size() > 0) {
|
||||
permissionState.appOpMode = ops.get(0).getOps().get(0).getMode();
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "PackageManager is dead. Can't get package info " + pkg, e);
|
||||
}
|
||||
return permissionState;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadAllExtraInfo() {
|
||||
SparseArray<ArrayMap<String, PermissionState>> entries = getEntries();
|
||||
|
||||
// Load state info.
|
||||
loadPermissionsStates(entries);
|
||||
loadAppOpsStates(entries);
|
||||
|
||||
// Map states to application info.
|
||||
List<AppEntry> apps = mAppSession.getAllApps();
|
||||
final int N = apps.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
AppEntry app = apps.get(i);
|
||||
int userId = UserHandle.getUserId(app.info.uid);
|
||||
ArrayMap<String, PermissionState> userMap = entries.get(userId);
|
||||
app.extraInfo = userMap != null ? userMap.get(app.info.packageName) : null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets a sparse array that describes every user on the device and all the associated packages
|
||||
* of each user, together with the packages available for that user.
|
||||
*/
|
||||
private SparseArray<ArrayMap<String, PermissionState>> getEntries() {
|
||||
try {
|
||||
final String[] packages = mIPackageManager.getAppOpPermissionPackages(mPermissions[0]);
|
||||
|
||||
if (packages == null) {
|
||||
// No packages are requesting permission as specified by mPermissions.
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a sparse array that maps profileIds to an ArrayMap that maps package names to
|
||||
// an associated PermissionState object
|
||||
SparseArray<ArrayMap<String, PermissionState>> entries = new SparseArray<>();
|
||||
for (final UserHandle profile : mProfiles) {
|
||||
final ArrayMap<String, PermissionState> entriesForProfile = new ArrayMap<>();
|
||||
final int profileId = profile.getIdentifier();
|
||||
entries.put(profileId, entriesForProfile);
|
||||
for (final String packageName : packages) {
|
||||
final boolean isAvailable = mIPackageManager.isPackageAvailable(packageName,
|
||||
profileId);
|
||||
if (!shouldIgnorePackage(packageName) && isAvailable) {
|
||||
final PermissionState newEntry = new PermissionState(packageName, profile);
|
||||
entriesForProfile.put(packageName, newEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "PackageManager is dead. Can't get list of packages requesting "
|
||||
+ mPermissions[0], e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This method will set the packageInfo and permissionDeclared field of the associated
|
||||
* PermissionState, which describes a particular package.
|
||||
*/
|
||||
private void loadPermissionsStates(SparseArray<ArrayMap<String, PermissionState>> entries) {
|
||||
// Load the packages that have been granted the permission specified in mPermission.
|
||||
try {
|
||||
for (final UserHandle profile : mProfiles) {
|
||||
final int profileId = profile.getIdentifier();
|
||||
final ArrayMap<String, PermissionState> entriesForProfile = entries.get(profileId);
|
||||
if (entriesForProfile == null) {
|
||||
continue;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<PackageInfo> packageInfos = mIPackageManager
|
||||
.getPackagesHoldingPermissions(mPermissions, 0, profileId).getList();
|
||||
final int packageInfoCount = packageInfos != null ? packageInfos.size() : 0;
|
||||
for (int i = 0; i < packageInfoCount; i++) {
|
||||
final PackageInfo packageInfo = packageInfos.get(i);
|
||||
final PermissionState pe = entriesForProfile.get(packageInfo.packageName);
|
||||
if (pe != null) {
|
||||
pe.packageInfo = packageInfo;
|
||||
pe.permissionDeclared = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "PackageManager is dead. Can't get list of packages granted "
|
||||
+ mPermissions[0], e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This method will set the appOpMode field of the associated PermissionState, which describes
|
||||
* a particular package.
|
||||
*/
|
||||
private void loadAppOpsStates(SparseArray<ArrayMap<String, PermissionState>> entries) {
|
||||
// Find out which packages have been granted permission from AppOps.
|
||||
final List<AppOpsManager.PackageOps> packageOps = mAppOpsManager.getPackagesForOps(
|
||||
mAppOpsOpCodes);
|
||||
final int packageOpsCount = packageOps != null ? packageOps.size() : 0;
|
||||
for (int i = 0; i < packageOpsCount; i++) {
|
||||
final AppOpsManager.PackageOps packageOp = packageOps.get(i);
|
||||
final int userId = UserHandle.getUserId(packageOp.getUid());
|
||||
if (!isThisUserAProfileOfCurrentUser(userId)) {
|
||||
// This AppOp does not belong to any of this user's profiles.
|
||||
continue;
|
||||
}
|
||||
|
||||
final ArrayMap<String, PermissionState> entriesForProfile = entries.get(userId);
|
||||
if (entriesForProfile == null) {
|
||||
continue;
|
||||
}
|
||||
final PermissionState pe = entriesForProfile.get(packageOp.getPackageName());
|
||||
if (pe == null) {
|
||||
Log.w(TAG, "AppOp permission exists for package " + packageOp.getPackageName()
|
||||
+ " of user " + userId + " but package doesn't exist or did not request "
|
||||
+ mPermissions[0] + " access");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (packageOp.getOps().size() < 1) {
|
||||
Log.w(TAG, "No AppOps permission exists for package " + packageOp.getPackageName());
|
||||
continue;
|
||||
}
|
||||
pe.appOpMode = packageOp.getOps().get(0).getMode();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for packages that should be ignored for further processing
|
||||
*/
|
||||
private boolean shouldIgnorePackage(String packageName) {
|
||||
return packageName.equals("android") || packageName.equals(mContext.getPackageName());
|
||||
}
|
||||
|
||||
public int getNumPackagesDeclaredPermission() {
|
||||
SparseArray<ArrayMap<String, PermissionState>> entries = getEntries();
|
||||
if (entries == null) {
|
||||
return 0;
|
||||
}
|
||||
final ArrayMap<String, PermissionState> entriesForProfile = entries.get(mUserManager
|
||||
.getUserHandle());
|
||||
if (entriesForProfile == null) {
|
||||
return 0;
|
||||
}
|
||||
return entriesForProfile.size();
|
||||
}
|
||||
|
||||
public int getNumPackagesAllowedByAppOps() {
|
||||
SparseArray<ArrayMap<String, PermissionState>> entries = getEntries();
|
||||
if (entries == null) {
|
||||
return 0;
|
||||
}
|
||||
loadPermissionsStates(entries);
|
||||
loadAppOpsStates(entries);
|
||||
final ArrayMap<String, PermissionState> entriesForProfile = entries.get(mUserManager
|
||||
.getUserHandle());
|
||||
if (entriesForProfile == null) {
|
||||
return 0;
|
||||
}
|
||||
Collection<PermissionState> permStates = entriesForProfile.values();
|
||||
int result = 0;
|
||||
for (PermissionState permState : permStates) {
|
||||
if (permState.isPermissible()) {
|
||||
result++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static class PermissionState {
|
||||
public final String packageName;
|
||||
public final UserHandle userHandle;
|
||||
public PackageInfo packageInfo;
|
||||
public boolean permissionDeclared;
|
||||
public int appOpMode;
|
||||
|
||||
public PermissionState(String packageName, UserHandle userHandle) {
|
||||
this.packageName = packageName;
|
||||
this.appOpMode = AppOpsManager.MODE_DEFAULT;
|
||||
this.userHandle = userHandle;
|
||||
}
|
||||
|
||||
public boolean isPermissible() {
|
||||
// defining the default behavior as permissible as long as the package requested this
|
||||
// permission (this means pre-M gets approval during install time; M apps gets approval
|
||||
// during runtime.
|
||||
if (appOpMode == AppOpsManager.MODE_DEFAULT) {
|
||||
return permissionDeclared;
|
||||
}
|
||||
return appOpMode == AppOpsManager.MODE_ALLOWED;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user