Merge "Bug: 21589105 Rescoping SYSTEM_ALERT_WINDOW and WRITE_SETTINGS permission to an explicit toggle to be enabled through Settings via Apps -> Advanced Apps." into mnc-dev
This commit is contained in:
@@ -15,11 +15,14 @@
|
||||
*/
|
||||
package com.android.settings.applications;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.os.Bundle;
|
||||
import android.os.AsyncTask;
|
||||
import android.preference.Preference;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.settings.R;
|
||||
@@ -40,11 +43,15 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements
|
||||
private static final String KEY_APP_PERM = "manage_perms";
|
||||
private static final String KEY_APP_DOMAIN_URLS = "domain_urls";
|
||||
private static final String KEY_HIGH_POWER_APPS = "high_power_apps";
|
||||
private static final String KEY_SYSTEM_ALERT_WINDOW = "system_alert_window";
|
||||
private static final String KEY_WRITE_SETTINGS_APPS = "write_settings_apps";
|
||||
|
||||
private Session mSession;
|
||||
private Preference mAppPermsPreference;
|
||||
private Preference mAppDomainURLsPreference;
|
||||
private Preference mHighPowerPreference;
|
||||
private Preference mSystemAlertWindowPreference;
|
||||
private Preference mWriteSettingsPreference;
|
||||
|
||||
private BroadcastReceiver mPermissionReceiver;
|
||||
|
||||
@@ -63,6 +70,8 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements
|
||||
mAppPermsPreference = findPreference(KEY_APP_PERM);
|
||||
mAppDomainURLsPreference = findPreference(KEY_APP_DOMAIN_URLS);
|
||||
mHighPowerPreference = findPreference(KEY_HIGH_POWER_APPS);
|
||||
mSystemAlertWindowPreference = findPreference(KEY_SYSTEM_ALERT_WINDOW);
|
||||
mWriteSettingsPreference = findPreference(KEY_WRITE_SETTINGS_APPS);
|
||||
updateUI();
|
||||
}
|
||||
|
||||
@@ -97,6 +106,16 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements
|
||||
}
|
||||
mPermissionReceiver = PermissionsSummaryHelper.getAppWithPermissionsCounts(getContext(),
|
||||
mPermissionCallback);
|
||||
|
||||
Activity activity = getActivity();
|
||||
ApplicationsState appState = ApplicationsState.getInstance(activity
|
||||
.getApplication());
|
||||
AppStateOverlayBridge overlayBridge = new AppStateOverlayBridge(activity,
|
||||
appState, null);
|
||||
AppStateWriteSettingsBridge writeSettingsBridge = new AppStateWriteSettingsBridge(
|
||||
activity, appState, null);
|
||||
new CountAppsWithOverlayPermission().execute(overlayBridge);
|
||||
new CountAppsWithWriteSettingsPermission().execute(writeSettingsBridge);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -159,4 +178,50 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private class CountAppsWithOverlayPermission extends
|
||||
AsyncTask<AppStateOverlayBridge, Void, Integer> {
|
||||
int numOfPackagesRequestedPermission = 0;
|
||||
|
||||
@Override
|
||||
protected Integer doInBackground(AppStateOverlayBridge... params) {
|
||||
AppStateOverlayBridge overlayBridge = params[0];
|
||||
numOfPackagesRequestedPermission = overlayBridge
|
||||
.getNumberOfPackagesWithPermission();
|
||||
return overlayBridge.getNumberOfPackagesCanDrawOverlay();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Integer result) {
|
||||
// checks if fragment is still there before updating the preference object
|
||||
if (isAdded()) {
|
||||
mSystemAlertWindowPreference.setSummary(getContext().getString(
|
||||
R.string.system_alert_window_summary, result,
|
||||
numOfPackagesRequestedPermission));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class CountAppsWithWriteSettingsPermission extends
|
||||
AsyncTask<AppStateWriteSettingsBridge, Void, Integer> {
|
||||
int numOfPackagesRequestedPermission = 0;
|
||||
|
||||
@Override
|
||||
protected Integer doInBackground(AppStateWriteSettingsBridge... params) {
|
||||
AppStateWriteSettingsBridge writeSettingsBridge = params[0];
|
||||
numOfPackagesRequestedPermission = writeSettingsBridge
|
||||
.getNumberOfPackagesWithPermission();
|
||||
return writeSettingsBridge.getNumberOfPackagesCanWriteSettings();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Integer result) {
|
||||
// checks if fragment is still there before updating the preference object
|
||||
if (isAdded()) {
|
||||
mWriteSettingsPreference.setSummary(getContext().getString(
|
||||
R.string.write_settings_summary, result,
|
||||
numOfPackagesRequestedPermission));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settingslib.applications.ApplicationsState;
|
||||
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
||||
import com.android.settingslib.applications.ApplicationsState.AppFilter;
|
||||
|
||||
/*
|
||||
* Connects info of apps that draw overlay to the ApplicationsState. Wraps around the generic
|
||||
* AppStateAppOpsBridge class to tailor to the semantics of SYSTEM_ALERT_WINDOW. Also provides app
|
||||
* filters that can use the info.
|
||||
*/
|
||||
public class AppStateOverlayBridge extends AppStateAppOpsBridge {
|
||||
|
||||
private static final String TAG = "AppStateOverlayBridge";
|
||||
private static final int APP_OPS_OP_CODE = AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
|
||||
private static final String PM_SYSTEM_ALERT_WINDOW = Manifest.permission.SYSTEM_ALERT_WINDOW;
|
||||
|
||||
public AppStateOverlayBridge(Context context, ApplicationsState appState, Callback callback) {
|
||||
super(context, appState, callback, APP_OPS_OP_CODE, PM_SYSTEM_ALERT_WINDOW);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateExtraInfo(AppEntry app, String pkg, int uid) {
|
||||
app.extraInfo = getOverlayInfo(pkg, uid);
|
||||
}
|
||||
|
||||
public OverlayState getOverlayInfo(String pkg, int uid) {
|
||||
PermissionState permissionState = super.getPermissionInfo(pkg, uid);
|
||||
return new OverlayState(permissionState);
|
||||
}
|
||||
|
||||
// TODO: figure out how to filter out system apps for this method
|
||||
public int getNumberOfPackagesWithPermission() {
|
||||
return super.getNumPackagesDeclaredPermission();
|
||||
}
|
||||
|
||||
// TODO: figure out how to filter out system apps for this method
|
||||
public int getNumberOfPackagesCanDrawOverlay() {
|
||||
return super.getNumPackagesAllowedByAppOps();
|
||||
}
|
||||
|
||||
public static class OverlayState {
|
||||
PermissionState mPermissionState;
|
||||
|
||||
public OverlayState(PermissionState permissionState) {
|
||||
mPermissionState = permissionState;
|
||||
}
|
||||
|
||||
public boolean isAllowed() {
|
||||
return mPermissionState.isPermissible();
|
||||
}
|
||||
}
|
||||
|
||||
public static final AppFilter FILTER_SYSTEM_ALERT_WINDOW = new AppFilter() {
|
||||
@Override
|
||||
public void init() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean filterApp(AppEntry info) {
|
||||
return info.extraInfo != null;
|
||||
}
|
||||
};
|
||||
}
|
@@ -16,65 +16,28 @@
|
||||
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.List;
|
||||
|
||||
/*
|
||||
* Connects app usage info to the ApplicationsState.
|
||||
* Also provides app filters that can use the info.
|
||||
* Connects app usage info to the ApplicationsState. Wraps around the generic AppStateAppOpsBridge
|
||||
* class to tailor to the semantics of PACKAGE_USAGE_STATS. Also provides app filters that can use
|
||||
* the info.
|
||||
*/
|
||||
public class AppStateUsageBridge extends AppStateBaseBridge {
|
||||
public class AppStateUsageBridge extends AppStateAppOpsBridge {
|
||||
|
||||
private static final String TAG = "AppStateUsageBridge";
|
||||
|
||||
private static final String[] PM_USAGE_STATS_PERMISSION = {
|
||||
Manifest.permission.PACKAGE_USAGE_STATS
|
||||
};
|
||||
|
||||
private static final int[] APP_OPS_OP_CODES = {
|
||||
AppOpsManager.OP_GET_USAGE_STATS
|
||||
};
|
||||
|
||||
private final IPackageManager mIPackageManager;
|
||||
private final UserManager mUserManager;
|
||||
private final List<UserHandle> mProfiles;
|
||||
private final AppOpsManager mAppOpsManager;
|
||||
private final Context mContext;
|
||||
private static final String PM_USAGE_STATS = Manifest.permission.PACKAGE_USAGE_STATS;
|
||||
private static final int APP_OPS_OP_CODE = AppOpsManager.OP_GET_USAGE_STATS;
|
||||
|
||||
public AppStateUsageBridge(Context context, ApplicationsState appState, Callback callback) {
|
||||
super(appState, callback);
|
||||
mContext = context;
|
||||
mIPackageManager = AppGlobals.getPackageManager();
|
||||
mUserManager = UserManager.get(context);
|
||||
mProfiles = mUserManager.getUserProfiles();
|
||||
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
|
||||
}
|
||||
|
||||
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;
|
||||
super(context, appState, callback, APP_OPS_OP_CODE, PM_USAGE_STATS);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -83,173 +46,17 @@ public class AppStateUsageBridge extends AppStateBaseBridge {
|
||||
}
|
||||
|
||||
public UsageState getUsageInfo(String pkg, int uid) {
|
||||
UsageState usageState = new UsageState(pkg, new UserHandle(UserHandle.getUserId(uid)));
|
||||
try {
|
||||
usageState.packageInfo = mIPackageManager.getPackageInfo(pkg,
|
||||
PackageManager.GET_PERMISSIONS, usageState.userHandle.getIdentifier());
|
||||
// Check permission state.
|
||||
String[] requestedPermissions = usageState.packageInfo.requestedPermissions;
|
||||
int[] permissionFlags = usageState.packageInfo.requestedPermissionsFlags;
|
||||
if (requestedPermissions != null) {
|
||||
for (int i = 0; i < requestedPermissions.length; i++) {
|
||||
if (Manifest.permission.PACKAGE_USAGE_STATS.equals(requestedPermissions[i])
|
||||
&& (permissionFlags[i] & PackageInfo.REQUESTED_PERMISSION_GRANTED)
|
||||
!= 0) {
|
||||
usageState.permissionGranted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check app op state.
|
||||
List<PackageOps> ops = mAppOpsManager.getOpsForPackage(uid, pkg, APP_OPS_OP_CODES);
|
||||
if (ops != null && ops.size() > 0 && ops.get(0).getOps().size() > 0) {
|
||||
usageState.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 usageState;
|
||||
PermissionState permissionState = super.getPermissionInfo(pkg, uid);
|
||||
return new UsageState(permissionState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadAllExtraInfo() {
|
||||
SparseArray<ArrayMap<String, UsageState>> entries = getEntries();
|
||||
public static class UsageState extends AppStateAppOpsBridge.PermissionState {
|
||||
|
||||
// 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, UsageState> userMap = entries.get(userId);
|
||||
app.extraInfo = userMap != null ? userMap.get(app.info.packageName) : null;
|
||||
}
|
||||
}
|
||||
|
||||
private SparseArray<ArrayMap<String, UsageState>> getEntries() {
|
||||
try {
|
||||
final String[] packages = mIPackageManager.getAppOpPermissionPackages(
|
||||
Manifest.permission.PACKAGE_USAGE_STATS);
|
||||
|
||||
if (packages == null) {
|
||||
// No packages are requesting permission to use the UsageStats API.
|
||||
return null;
|
||||
}
|
||||
|
||||
SparseArray<ArrayMap<String, UsageState>> entries = new SparseArray<>();
|
||||
for (final UserHandle profile : mProfiles) {
|
||||
final ArrayMap<String, UsageState> 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 UsageState newEntry = new UsageState(packageName, profile);
|
||||
entriesForProfile.put(packageName, newEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "PackageManager is dead. Can't get list of packages requesting "
|
||||
+ Manifest.permission.PACKAGE_USAGE_STATS, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void loadPermissionsStates(SparseArray<ArrayMap<String, UsageState>> entries) {
|
||||
// Load the packages that have been granted the PACKAGE_USAGE_STATS permission.
|
||||
try {
|
||||
for (final UserHandle profile : mProfiles) {
|
||||
final int profileId = profile.getIdentifier();
|
||||
final ArrayMap<String, UsageState> entriesForProfile = entries.get(profileId);
|
||||
if (entriesForProfile == null) {
|
||||
continue;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<PackageInfo> packageInfos = mIPackageManager
|
||||
.getPackagesHoldingPermissions(PM_USAGE_STATS_PERMISSION, 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 UsageState pe = entriesForProfile.get(packageInfo.packageName);
|
||||
if (pe != null) {
|
||||
pe.packageInfo = packageInfo;
|
||||
pe.permissionGranted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "PackageManager is dead. Can't get list of packages granted "
|
||||
+ Manifest.permission.PACKAGE_USAGE_STATS, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void loadAppOpsStates(SparseArray<ArrayMap<String, UsageState>> entries) {
|
||||
// Find out which packages have been granted permission from AppOps.
|
||||
final List<AppOpsManager.PackageOps> packageOps = mAppOpsManager.getPackagesForOps(
|
||||
APP_OPS_OP_CODES);
|
||||
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, UsageState> entriesForProfile = entries.get(userId);
|
||||
if (entriesForProfile == null) {
|
||||
continue;
|
||||
}
|
||||
final UsageState 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 UsageStats 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();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldIgnorePackage(String packageName) {
|
||||
return packageName.equals("android") || packageName.equals(mContext.getPackageName());
|
||||
}
|
||||
|
||||
public static class UsageState {
|
||||
public final String packageName;
|
||||
public final UserHandle userHandle;
|
||||
public PackageInfo packageInfo;
|
||||
public boolean permissionGranted;
|
||||
public int appOpMode;
|
||||
|
||||
public UsageState(String packageName, UserHandle userHandle) {
|
||||
this.packageName = packageName;
|
||||
this.appOpMode = AppOpsManager.MODE_DEFAULT;
|
||||
this.userHandle = userHandle;
|
||||
}
|
||||
|
||||
public boolean hasAccess() {
|
||||
if (appOpMode == AppOpsManager.MODE_DEFAULT) {
|
||||
return permissionGranted;
|
||||
}
|
||||
return appOpMode == AppOpsManager.MODE_ALLOWED;
|
||||
public UsageState(PermissionState permissionState) {
|
||||
super(permissionState.packageName, permissionState.userHandle);
|
||||
this.packageInfo = permissionState.packageInfo;
|
||||
this.appOpMode = permissionState.appOpMode;
|
||||
this.permissionDeclared = permissionState.permissionDeclared;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settingslib.applications.ApplicationsState;
|
||||
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
||||
import com.android.settingslib.applications.ApplicationsState.AppFilter;
|
||||
|
||||
/*
|
||||
* Connects info of apps that draw overlay to the ApplicationsState. Wraps around the generic
|
||||
* AppStateAppOpsBridge class to tailor to the semantics of SYSTEM_ALERT_WINDOW. Also provides app
|
||||
* filters that can use the info.
|
||||
*/
|
||||
public class AppStateWriteSettingsBridge extends AppStateAppOpsBridge {
|
||||
|
||||
private static final String TAG = "AppStateWriteSettingsBridge";
|
||||
private static final int APP_OPS_OP_CODE = AppOpsManager.OP_WRITE_SETTINGS;
|
||||
private static final String PM_WRITE_SETTINGS = Manifest.permission.WRITE_SETTINGS;
|
||||
|
||||
public AppStateWriteSettingsBridge(Context context, ApplicationsState appState, Callback
|
||||
callback) {
|
||||
super(context, appState, callback, APP_OPS_OP_CODE, PM_WRITE_SETTINGS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateExtraInfo(AppEntry app, String pkg, int uid) {
|
||||
app.extraInfo = getWriteSettingsInfo(pkg, uid);
|
||||
}
|
||||
|
||||
public WriteSettingsState getWriteSettingsInfo(String pkg, int uid) {
|
||||
PermissionState permissionState = super.getPermissionInfo(pkg, uid);
|
||||
return new WriteSettingsState(permissionState);
|
||||
}
|
||||
|
||||
// TODO: figure out how to filter out system apps for this method
|
||||
public int getNumberOfPackagesWithPermission() {
|
||||
return super.getNumPackagesDeclaredPermission();
|
||||
}
|
||||
|
||||
// TODO: figure out how to filter out system apps for this method
|
||||
public int getNumberOfPackagesCanWriteSettings() {
|
||||
return super.getNumPackagesAllowedByAppOps();
|
||||
}
|
||||
|
||||
public static class WriteSettingsState {
|
||||
PermissionState mPermissionState;
|
||||
|
||||
public WriteSettingsState(PermissionState permissionState) {
|
||||
mPermissionState = permissionState;
|
||||
}
|
||||
|
||||
public boolean canWrite() {
|
||||
return mPermissionState.isPermissible();
|
||||
}
|
||||
}
|
||||
|
||||
public static final AppFilter FILTER_WRITE_SETTINGS = new AppFilter() {
|
||||
@Override
|
||||
public void init() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean filterApp(AppEntry info) {
|
||||
return info.extraInfo != null;
|
||||
}
|
||||
};
|
||||
}
|
212
src/com/android/settings/applications/DrawOverlayDetails.java
Normal file
212
src/com/android/settings/applications/DrawOverlayDetails.java
Normal file
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* 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.app.AlertDialog;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceChangeListener;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.preference.SwitchPreference;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.settings.InstrumentedFragment;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.applications.AppStateOverlayBridge.OverlayState;
|
||||
import com.android.settingslib.applications.ApplicationsState;
|
||||
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DrawOverlayDetails extends AppInfoWithHeader implements OnPreferenceChangeListener,
|
||||
OnPreferenceClickListener {
|
||||
|
||||
private static final String KEY_APP_OPS_SETTINGS_SWITCH = "app_ops_settings_switch";
|
||||
private static final String KEY_APP_OPS_SETTINGS_PREFS = "app_ops_settings_preference";
|
||||
private static final String KEY_APP_OPS_SETTINGS_DESC = "app_ops_settings_description";
|
||||
private static final String LOG_TAG = "DrawOverlayDetails";
|
||||
|
||||
private static final int [] APP_OPS_OP_CODE = {
|
||||
AppOpsManager.OP_SYSTEM_ALERT_WINDOW
|
||||
};
|
||||
|
||||
// Use a bridge to get the overlay details but don't initialize it to connect with all state.
|
||||
// TODO: Break out this functionality into its own class.
|
||||
private AppStateOverlayBridge mOverlayBridge;
|
||||
private AppOpsManager mAppOpsManager;
|
||||
private SwitchPreference mSwitchPref;
|
||||
private Preference mOverlayPrefs;
|
||||
private Preference mOverlayDesc;
|
||||
private Intent mSettingsIntent;
|
||||
private OverlayState mOverlayState;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Context context = getActivity();
|
||||
mOverlayBridge = new AppStateOverlayBridge(context, mState, null);
|
||||
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
|
||||
|
||||
// find preferences
|
||||
addPreferencesFromResource(R.xml.app_ops_permissions_details);
|
||||
mSwitchPref = (SwitchPreference) findPreference(KEY_APP_OPS_SETTINGS_SWITCH);
|
||||
mOverlayPrefs = findPreference(KEY_APP_OPS_SETTINGS_PREFS);
|
||||
mOverlayDesc = findPreference(KEY_APP_OPS_SETTINGS_DESC);
|
||||
|
||||
// set title/summary for all of them
|
||||
getPreferenceScreen().setTitle(R.string.draw_overlay);
|
||||
mSwitchPref.setTitle(R.string.permit_draw_overlay);
|
||||
mOverlayPrefs.setTitle(R.string.app_overlay_permission_preference);
|
||||
mOverlayDesc.setSummary(R.string.allow_overlay_description);
|
||||
|
||||
// install event listeners
|
||||
mSwitchPref.setOnPreferenceChangeListener(this);
|
||||
mOverlayPrefs.setOnPreferenceClickListener(this);
|
||||
|
||||
mSettingsIntent = new Intent(Intent.ACTION_MAIN)
|
||||
.setAction(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
if (preference == mOverlayPrefs) {
|
||||
if (mSettingsIntent != null) {
|
||||
try {
|
||||
getActivity().startActivityAsUser(mSettingsIntent, new UserHandle(mUserId));
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Log.w(TAG, "Unable to launch app draw overlay settings " + mSettingsIntent, e);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
if (preference == mSwitchPref) {
|
||||
if (mOverlayState != null && (Boolean) newValue != mOverlayState.isAllowed()) {
|
||||
setCanDrawOverlay(!mOverlayState.isAllowed());
|
||||
refreshUi();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setCanDrawOverlay(boolean newState) {
|
||||
mAppOpsManager.setMode(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
|
||||
mPackageInfo.applicationInfo.uid, mPackageName, newState
|
||||
? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_ERRORED);
|
||||
canDrawOverlay(mPackageName);
|
||||
}
|
||||
|
||||
private boolean canDrawOverlay(String pkgName) {
|
||||
int result = mAppOpsManager.noteOpNoThrow(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
|
||||
mPackageInfo.applicationInfo.uid, pkgName);
|
||||
if (result == AppOpsManager.MODE_ALLOWED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean refreshUi() {
|
||||
mOverlayState = mOverlayBridge.getOverlayInfo(mPackageName,
|
||||
mPackageInfo.applicationInfo.uid);
|
||||
|
||||
boolean isAllowed = mOverlayState.isAllowed();
|
||||
mSwitchPref.setChecked(isAllowed);
|
||||
mOverlayPrefs.setEnabled(isAllowed);
|
||||
|
||||
ResolveInfo resolveInfo = mPm.resolveActivityAsUser(mSettingsIntent,
|
||||
PackageManager.GET_META_DATA, mUserId);
|
||||
if (resolveInfo == null) {
|
||||
if (findPreference(KEY_APP_OPS_SETTINGS_PREFS) != null) {
|
||||
getPreferenceScreen().removePreference(mOverlayPrefs);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AlertDialog createDialog(int id, int errorCode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getMetricsCategory() {
|
||||
return MetricsLogger.SYSTEM_ALERT_WINDOW_APPS;
|
||||
}
|
||||
|
||||
public static CharSequence getSummary(Context context, AppEntry entry) {
|
||||
return getSummary(context, entry.info.packageName);
|
||||
}
|
||||
|
||||
public static CharSequence getSummary(Context context, String pkg) {
|
||||
// first check if pkg is a system pkg
|
||||
boolean isSystem = false;
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
try {
|
||||
ApplicationInfo appInfo = packageManager.getApplicationInfo(pkg, 0);
|
||||
if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
|
||||
isSystem = true;
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// pkg doesn't even exist?
|
||||
Log.w(TAG, "Package " + pkg + " not found", e);
|
||||
return context.getString(R.string.system_alert_window_off);
|
||||
}
|
||||
|
||||
AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context
|
||||
.APP_OPS_SERVICE);
|
||||
List<AppOpsManager.PackageOps> packageOps = appOpsManager.getPackagesForOps(
|
||||
APP_OPS_OP_CODE);
|
||||
if (packageOps == null) {
|
||||
return context.getString(R.string.system_alert_window_off);
|
||||
}
|
||||
|
||||
int uid = isSystem ? 0 : -1;
|
||||
for (AppOpsManager.PackageOps packageOp : packageOps) {
|
||||
if (pkg.equals(packageOp.getPackageName())) {
|
||||
uid = packageOp.getUid();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (uid == -1) {
|
||||
return context.getString(R.string.system_alert_window_off);
|
||||
}
|
||||
|
||||
int mode = appOpsManager.noteOpNoThrow(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, uid, pkg);
|
||||
return context.getString((mode == AppOpsManager.MODE_ALLOWED) ?
|
||||
R.string.system_alert_window_on : R.string.system_alert_window_off);
|
||||
}
|
||||
}
|
@@ -59,8 +59,11 @@ import com.android.settings.Settings.HighPowerApplicationsActivity;
|
||||
import com.android.settings.Settings.NotificationAppListActivity;
|
||||
import com.android.settings.Settings.StorageUseActivity;
|
||||
import com.android.settings.Settings.UsageAccessSettingsActivity;
|
||||
import com.android.settings.Settings.OverlaySettingsActivity;
|
||||
import com.android.settings.Settings.WriteSettingsActivity;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
|
||||
import com.android.settings.applications.AppStateUsageBridge.UsageState;
|
||||
import com.android.settings.fuelgauge.HighPowerDetail;
|
||||
import com.android.settings.notification.AppNotificationSettings;
|
||||
@@ -124,6 +127,8 @@ public class ManageApplications extends InstrumentedFragment
|
||||
public static final int FILTER_APPS_WORK = 10;
|
||||
public static final int FILTER_APPS_WITH_DOMAIN_URLS = 11;
|
||||
public static final int FILTER_APPS_USAGE_ACCESS = 12;
|
||||
public static final int FILTER_APPS_WITH_OVERLAY = 13;
|
||||
public static final int FILTER_APPS_WRITE_SETTINGS = 14;
|
||||
|
||||
// This is the string labels for the filter modes above, the order must be kept in sync.
|
||||
public static final int[] FILTER_LABELS = new int[] {
|
||||
@@ -140,6 +145,8 @@ public class ManageApplications extends InstrumentedFragment
|
||||
R.string.filter_work_apps, // Work
|
||||
R.string.filter_with_domain_urls_apps, // Domain URLs
|
||||
R.string.filter_all_apps, // Usage access screen, never displayed
|
||||
R.string.filter_overlay_apps, // Apps with overlay permission
|
||||
R.string.filter_write_settings_apps, // Apps that can write system settings
|
||||
};
|
||||
// This is the actual mapping to filters from FILTER_ constants above, the order must
|
||||
// be kept in sync.
|
||||
@@ -157,6 +164,8 @@ public class ManageApplications extends InstrumentedFragment
|
||||
ApplicationsState.FILTER_WORK, // Work
|
||||
ApplicationsState.FILTER_WITH_DOMAIN_URLS, // Apps with Domain URLs
|
||||
AppStateUsageBridge.FILTER_APP_USAGE, // Apps with Domain URLs
|
||||
AppStateOverlayBridge.FILTER_SYSTEM_ALERT_WINDOW, // Apps that can draw overlays
|
||||
AppStateWriteSettingsBridge.FILTER_WRITE_SETTINGS, // Apps that can write system settings
|
||||
};
|
||||
|
||||
// sort order
|
||||
@@ -197,6 +206,8 @@ public class ManageApplications extends InstrumentedFragment
|
||||
public static final int LIST_TYPE_STORAGE = 3;
|
||||
public static final int LIST_TYPE_USAGE_ACCESS = 4;
|
||||
public static final int LIST_TYPE_HIGH_POWER = 5;
|
||||
public static final int LIST_TYPE_OVERLAY = 6;
|
||||
public static final int LIST_TYPE_WRITE_SETTINGS = 7;
|
||||
|
||||
private View mRootView;
|
||||
|
||||
@@ -254,6 +265,12 @@ public class ManageApplications extends InstrumentedFragment
|
||||
startApplicationDetailsActivity();
|
||||
}
|
||||
}
|
||||
} else if (className.equals(OverlaySettingsActivity.class.getName())) {
|
||||
mListType = LIST_TYPE_OVERLAY;
|
||||
getActivity().getActionBar().setTitle(R.string.system_alert_window_access_title);
|
||||
} else if (className.equals(WriteSettingsActivity.class.getName())) {
|
||||
mListType = LIST_TYPE_WRITE_SETTINGS;
|
||||
getActivity().getActionBar().setTitle(R.string.write_settings_title);
|
||||
} else {
|
||||
mListType = LIST_TYPE_MAIN;
|
||||
}
|
||||
@@ -365,6 +382,10 @@ public class ManageApplications extends InstrumentedFragment
|
||||
return FILTER_APPS_USAGE_ACCESS;
|
||||
case LIST_TYPE_HIGH_POWER:
|
||||
return FILTER_APPS_POWER_WHITELIST;
|
||||
case LIST_TYPE_OVERLAY:
|
||||
return FILTER_APPS_WITH_OVERLAY;
|
||||
case LIST_TYPE_WRITE_SETTINGS:
|
||||
return FILTER_APPS_WRITE_SETTINGS;
|
||||
default:
|
||||
return FILTER_APPS_ALL;
|
||||
}
|
||||
@@ -385,6 +406,10 @@ public class ManageApplications extends InstrumentedFragment
|
||||
return MetricsLogger.USAGE_ACCESS;
|
||||
case LIST_TYPE_HIGH_POWER:
|
||||
return MetricsLogger.APPLICATIONS_HIGH_POWER_APPS;
|
||||
case LIST_TYPE_OVERLAY:
|
||||
return MetricsLogger.SYSTEM_ALERT_WINDOW_APPS;
|
||||
case LIST_TYPE_WRITE_SETTINGS:
|
||||
return MetricsLogger.SYSTEM_ALERT_WINDOW_APPS;
|
||||
default:
|
||||
return MetricsLogger.VIEW_UNKNOWN;
|
||||
}
|
||||
@@ -439,7 +464,8 @@ public class ManageApplications extends InstrumentedFragment
|
||||
if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) {
|
||||
if (mListType == LIST_TYPE_NOTIFICATION) {
|
||||
mApplications.mExtraInfoBridge.forceUpdate(mCurrentPkgName, mCurrentUid);
|
||||
} else if (mListType == LIST_TYPE_HIGH_POWER) {
|
||||
} else if (mListType == LIST_TYPE_HIGH_POWER || mListType == LIST_TYPE_OVERLAY
|
||||
|| mListType == LIST_TYPE_WRITE_SETTINGS) {
|
||||
if (mFinishAfterDialog) {
|
||||
getActivity().onBackPressed();
|
||||
} else {
|
||||
@@ -471,6 +497,12 @@ public class ManageApplications extends InstrumentedFragment
|
||||
HighPowerDetail.show(this, mCurrentPkgName, INSTALLED_APP_DETAILS,
|
||||
mFinishAfterDialog);
|
||||
break;
|
||||
case LIST_TYPE_OVERLAY:
|
||||
startAppInfoFragment(DrawOverlayDetails.class, R.string.overlay_settings);
|
||||
break;
|
||||
case LIST_TYPE_WRITE_SETTINGS:
|
||||
startAppInfoFragment(WriteSettingsDetails.class, R.string.write_system_settings);
|
||||
break;
|
||||
// TODO: Figure out if there is a way where we can spin up the profile's settings
|
||||
// process ahead of time, to avoid a long load of data when user clicks on a managed app.
|
||||
// Maybe when they load the list of apps that contains managed profile apps.
|
||||
@@ -728,6 +760,10 @@ public class ManageApplications extends InstrumentedFragment
|
||||
mExtraInfoBridge = new AppStateUsageBridge(mContext, mState, this);
|
||||
} else if (mManageApplications.mListType == LIST_TYPE_HIGH_POWER) {
|
||||
mExtraInfoBridge = new AppStatePowerBridge(mState, this);
|
||||
} else if (mManageApplications.mListType == LIST_TYPE_OVERLAY) {
|
||||
mExtraInfoBridge = new AppStateOverlayBridge(mContext, mState, this);
|
||||
} else if (mManageApplications.mListType == LIST_TYPE_WRITE_SETTINGS) {
|
||||
mExtraInfoBridge = new AppStateWriteSettingsBridge(mContext, mState, this);
|
||||
} else {
|
||||
mExtraInfoBridge = null;
|
||||
}
|
||||
@@ -1026,8 +1062,9 @@ public class ManageApplications extends InstrumentedFragment
|
||||
|
||||
case LIST_TYPE_USAGE_ACCESS:
|
||||
if (holder.entry.extraInfo != null) {
|
||||
holder.summary.setText(((UsageState) holder.entry.extraInfo).hasAccess() ?
|
||||
R.string.switch_on_text : R.string.switch_off_text);
|
||||
holder.summary.setText((new UsageState((PermissionState)holder.entry
|
||||
.extraInfo)).isPermissible() ? R.string.switch_on_text :
|
||||
R.string.switch_off_text);
|
||||
} else {
|
||||
holder.summary.setText(null);
|
||||
}
|
||||
@@ -1037,6 +1074,16 @@ public class ManageApplications extends InstrumentedFragment
|
||||
holder.summary.setText(HighPowerDetail.getSummary(mContext, holder.entry));
|
||||
break;
|
||||
|
||||
case LIST_TYPE_OVERLAY:
|
||||
holder.summary.setText(DrawOverlayDetails.getSummary(mContext,
|
||||
holder.entry));
|
||||
break;
|
||||
|
||||
case LIST_TYPE_WRITE_SETTINGS:
|
||||
holder.summary.setText(WriteSettingsDetails.getSummary(mContext,
|
||||
holder.entry));
|
||||
break;
|
||||
|
||||
default:
|
||||
holder.updateSizeText(mManageApplications.mInvalidSizeStr, mWhichSize);
|
||||
break;
|
||||
|
@@ -40,8 +40,10 @@ import com.android.settings.applications.AppStateUsageBridge.UsageState;
|
||||
public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenceChangeListener,
|
||||
OnPreferenceClickListener {
|
||||
|
||||
private static final String KEY_USAGE_SWITCH = "usage_switch";
|
||||
private static final String KEY_USAGE_PREFS = "app_usage_preference";
|
||||
private static final String KEY_APP_OPS_PREFERENCE_SCREEN = "app_ops_preference_screen";
|
||||
private static final String KEY_APP_OPS_SETTINGS_SWITCH = "app_ops_settings_switch";
|
||||
private static final String KEY_APP_OPS_SETTINGS_PREFS = "app_ops_settings_preference";
|
||||
private static final String KEY_APP_OPS_SETTINGS_DESC = "app_ops_settings_description";
|
||||
|
||||
// Use a bridge to get the usage stats but don't initialize it to connect with all state.
|
||||
// TODO: Break out this functionality into its own class.
|
||||
@@ -49,6 +51,7 @@ public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenc
|
||||
private AppOpsManager mAppOpsManager;
|
||||
private SwitchPreference mSwitchPref;
|
||||
private Preference mUsagePrefs;
|
||||
private Preference mUsageDesc;
|
||||
private Intent mSettingsIntent;
|
||||
private UsageState mUsageState;
|
||||
private DevicePolicyManager mDpm;
|
||||
@@ -62,9 +65,15 @@ public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenc
|
||||
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
|
||||
mDpm = context.getSystemService(DevicePolicyManager.class);
|
||||
|
||||
addPreferencesFromResource(R.xml.usage_access_details);
|
||||
mSwitchPref = (SwitchPreference) findPreference(KEY_USAGE_SWITCH);
|
||||
mUsagePrefs = findPreference(KEY_USAGE_PREFS);
|
||||
addPreferencesFromResource(R.xml.app_ops_permissions_details);
|
||||
mSwitchPref = (SwitchPreference) findPreference(KEY_APP_OPS_SETTINGS_SWITCH);
|
||||
mUsagePrefs = findPreference(KEY_APP_OPS_SETTINGS_PREFS);
|
||||
mUsageDesc = findPreference(KEY_APP_OPS_SETTINGS_DESC);
|
||||
|
||||
getPreferenceScreen().setTitle(R.string.usage_access);
|
||||
mSwitchPref.setTitle(R.string.permit_usage_access);
|
||||
mUsagePrefs.setTitle(R.string.app_usage_preference);
|
||||
mUsageDesc.setSummary(R.string.usage_access_description);
|
||||
|
||||
mSwitchPref.setOnPreferenceChangeListener(this);
|
||||
mUsagePrefs.setOnPreferenceClickListener(this);
|
||||
@@ -92,8 +101,8 @@ public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenc
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
if (preference == mSwitchPref) {
|
||||
if (mUsageState != null && (Boolean) newValue != mUsageState.hasAccess()) {
|
||||
if (mUsageState.hasAccess() && mDpm.isProfileOwnerApp(mPackageName)) {
|
||||
if (mUsageState != null && (Boolean) newValue != mUsageState.isPermissible()) {
|
||||
if (mUsageState.isPermissible() && mDpm.isProfileOwnerApp(mPackageName)) {
|
||||
new AlertDialog.Builder(getContext())
|
||||
.setIcon(com.android.internal.R.drawable.ic_dialog_alert_material)
|
||||
.setTitle(android.R.string.dialog_alert_title)
|
||||
@@ -101,7 +110,7 @@ public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenc
|
||||
.setPositiveButton(R.string.okay, null)
|
||||
.show();
|
||||
}
|
||||
setHasAccess(!mUsageState.hasAccess());
|
||||
setHasAccess(!mUsageState.isPermissible());
|
||||
refreshUi();
|
||||
}
|
||||
return true;
|
||||
@@ -119,14 +128,14 @@ public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenc
|
||||
mUsageState = mUsageBridge.getUsageInfo(mPackageName,
|
||||
mPackageInfo.applicationInfo.uid);
|
||||
|
||||
boolean hasAccess = mUsageState.hasAccess();
|
||||
boolean hasAccess = mUsageState.isPermissible();
|
||||
mSwitchPref.setChecked(hasAccess);
|
||||
mUsagePrefs.setEnabled(hasAccess);
|
||||
|
||||
ResolveInfo resolveInfo = mPm.resolveActivityAsUser(mSettingsIntent,
|
||||
PackageManager.GET_META_DATA, mUserId);
|
||||
if (resolveInfo != null) {
|
||||
if (findPreference(KEY_USAGE_PREFS) == null) {
|
||||
if (findPreference(KEY_APP_OPS_SETTINGS_PREFS) == null) {
|
||||
getPreferenceScreen().addPreference(mUsagePrefs);
|
||||
}
|
||||
Bundle metaData = resolveInfo.activityInfo.metaData;
|
||||
@@ -138,7 +147,7 @@ public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenc
|
||||
metaData.getString(Settings.METADATA_USAGE_ACCESS_REASON));
|
||||
}
|
||||
} else {
|
||||
if (findPreference(KEY_USAGE_PREFS) != null) {
|
||||
if (findPreference(KEY_APP_OPS_SETTINGS_PREFS) != null) {
|
||||
getPreferenceScreen().removePreference(mUsagePrefs);
|
||||
}
|
||||
}
|
||||
|
211
src/com/android/settings/applications/WriteSettingsDetails.java
Normal file
211
src/com/android/settings/applications/WriteSettingsDetails.java
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* 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.app.AlertDialog;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceChangeListener;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.preference.SwitchPreference;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.settings.InstrumentedFragment;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.applications.AppStateWriteSettingsBridge.WriteSettingsState;
|
||||
import com.android.settingslib.applications.ApplicationsState;
|
||||
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class WriteSettingsDetails extends AppInfoWithHeader implements OnPreferenceChangeListener,
|
||||
OnPreferenceClickListener {
|
||||
|
||||
private static final String KEY_APP_OPS_PREFERENCE_SCREEN = "app_ops_preference_screen";
|
||||
private static final String KEY_APP_OPS_SETTINGS_SWITCH = "app_ops_settings_switch";
|
||||
private static final String KEY_APP_OPS_SETTINGS_PREFS = "app_ops_settings_preference";
|
||||
private static final String KEY_APP_OPS_SETTINGS_DESC = "app_ops_settings_description";
|
||||
private static final String LOG_TAG = "WriteSettingsDetails";
|
||||
|
||||
private static final int [] APP_OPS_OP_CODE = {
|
||||
AppOpsManager.OP_WRITE_SETTINGS
|
||||
};
|
||||
|
||||
// Use a bridge to get the overlay details but don't initialize it to connect with all state.
|
||||
// TODO: Break out this functionality into its own class.
|
||||
private AppStateWriteSettingsBridge mAppBridge;
|
||||
private AppOpsManager mAppOpsManager;
|
||||
private SwitchPreference mSwitchPref;
|
||||
private Preference mWriteSettingsPrefs;
|
||||
private Preference mWriteSettingsDesc;
|
||||
private Intent mSettingsIntent;
|
||||
private WriteSettingsState mWriteSettingsState;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Context context = getActivity();
|
||||
mAppBridge = new AppStateWriteSettingsBridge(context, mState, null);
|
||||
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
|
||||
|
||||
addPreferencesFromResource(R.xml.app_ops_permissions_details);
|
||||
mSwitchPref = (SwitchPreference) findPreference(KEY_APP_OPS_SETTINGS_SWITCH);
|
||||
mWriteSettingsPrefs = findPreference(KEY_APP_OPS_SETTINGS_PREFS);
|
||||
mWriteSettingsDesc = findPreference(KEY_APP_OPS_SETTINGS_DESC);
|
||||
|
||||
getPreferenceScreen().setTitle(R.string.write_settings);
|
||||
mSwitchPref.setTitle(R.string.permit_write_settings);
|
||||
mWriteSettingsPrefs.setTitle(R.string.write_settings_preference);
|
||||
mWriteSettingsDesc.setSummary(R.string.write_settings_description);
|
||||
|
||||
mSwitchPref.setOnPreferenceChangeListener(this);
|
||||
mWriteSettingsPrefs.setOnPreferenceClickListener(this);
|
||||
|
||||
mSettingsIntent = new Intent(Intent.ACTION_MAIN)
|
||||
.addCategory(Settings.INTENT_CATEGORY_USAGE_ACCESS_CONFIG)
|
||||
.setPackage(mPackageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
if (preference == mWriteSettingsPrefs) {
|
||||
if (mSettingsIntent != null) {
|
||||
try {
|
||||
getActivity().startActivityAsUser(mSettingsIntent, new UserHandle(mUserId));
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Log.w(TAG, "Unable to launch write system settings " + mSettingsIntent, e);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
if (preference == mSwitchPref) {
|
||||
if (mWriteSettingsState != null && (Boolean) newValue != mWriteSettingsState.canWrite()) {
|
||||
setCanWriteSettings(!mWriteSettingsState.canWrite());
|
||||
refreshUi();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setCanWriteSettings(boolean newState) {
|
||||
mAppOpsManager.setMode(AppOpsManager.OP_WRITE_SETTINGS,
|
||||
mPackageInfo.applicationInfo.uid, mPackageName, newState
|
||||
? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_ERRORED);
|
||||
canWriteSettings(mPackageName);
|
||||
}
|
||||
|
||||
private boolean canWriteSettings(String pkgName) {
|
||||
int result = mAppOpsManager.noteOpNoThrow(AppOpsManager.OP_WRITE_SETTINGS,
|
||||
mPackageInfo.applicationInfo.uid, pkgName);
|
||||
if (result == AppOpsManager.MODE_ALLOWED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean refreshUi() {
|
||||
mWriteSettingsState = mAppBridge.getWriteSettingsInfo(mPackageName,
|
||||
mPackageInfo.applicationInfo.uid);
|
||||
|
||||
boolean canWrite = mWriteSettingsState.canWrite();
|
||||
mSwitchPref.setChecked(canWrite);
|
||||
mWriteSettingsPrefs.setEnabled(canWrite);
|
||||
|
||||
ResolveInfo resolveInfo = mPm.resolveActivityAsUser(mSettingsIntent,
|
||||
PackageManager.GET_META_DATA, mUserId);
|
||||
if (resolveInfo == null) {
|
||||
if (findPreference(KEY_APP_OPS_SETTINGS_PREFS) != null) {
|
||||
getPreferenceScreen().removePreference(mWriteSettingsPrefs);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AlertDialog createDialog(int id, int errorCode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getMetricsCategory() {
|
||||
return MetricsLogger.SYSTEM_ALERT_WINDOW_APPS;
|
||||
}
|
||||
|
||||
public static CharSequence getSummary(Context context, AppEntry entry) {
|
||||
return getSummary(context, entry.info.packageName);
|
||||
}
|
||||
|
||||
public static CharSequence getSummary(Context context, String pkg) {
|
||||
// first check if pkg is a system pkg
|
||||
boolean isSystem = false;
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
try {
|
||||
ApplicationInfo appInfo = packageManager.getApplicationInfo(pkg, 0);
|
||||
if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
|
||||
isSystem = true;
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// pkg doesn't even exist?
|
||||
Log.w(TAG, "Package " + pkg + " not found", e);
|
||||
return context.getString(R.string.system_alert_window_off);
|
||||
}
|
||||
|
||||
AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context
|
||||
.APP_OPS_SERVICE);
|
||||
List<AppOpsManager.PackageOps> packageOps = appOpsManager.getPackagesForOps(
|
||||
APP_OPS_OP_CODE);
|
||||
if (packageOps == null) {
|
||||
return context.getString(R.string.system_alert_window_off);
|
||||
}
|
||||
|
||||
int uid = isSystem ? 0 : -1;
|
||||
for (AppOpsManager.PackageOps packageOp : packageOps) {
|
||||
if (pkg.equals(packageOp.getPackageName())) {
|
||||
uid = packageOp.getUid();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (uid == -1) {
|
||||
return context.getString(R.string.system_alert_window_off);
|
||||
}
|
||||
|
||||
int mode = appOpsManager.noteOpNoThrow(AppOpsManager.OP_WRITE_SETTINGS, uid, pkg);
|
||||
return context.getString((mode == AppOpsManager.MODE_ALLOWED) ?
|
||||
R.string.write_settings_on : R.string.write_settings_off);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user