summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AndroidManifest.xml18
-rw-r--r--res/values/strings.xml59
-rw-r--r--res/xml/advanced_apps.xml20
-rw-r--r--res/xml/app_ops_permissions_details.xml (renamed from res/xml/usage_access_details.xml)11
-rw-r--r--src/com/android/settings/Settings.java3
-rw-r--r--src/com/android/settings/applications/AdvancedAppSettings.java65
-rw-r--r--src/com/android/settings/applications/AppStateAppOpsBridge.java301
-rw-r--r--src/com/android/settings/applications/AppStateOverlayBridge.java84
-rw-r--r--src/com/android/settings/applications/AppStateUsageBridge.java223
-rw-r--r--src/com/android/settings/applications/AppStateWriteSettingsBridge.java85
-rw-r--r--src/com/android/settings/applications/DrawOverlayDetails.java212
-rw-r--r--src/com/android/settings/applications/ManageApplications.java53
-rw-r--r--src/com/android/settings/applications/UsageAccessDetails.java31
-rw-r--r--src/com/android/settings/applications/WriteSettingsDetails.java211
14 files changed, 1146 insertions, 230 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 52889a68d..3367345dc 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2516,5 +2516,23 @@
</intent-filter>
</provider>
+ <activity android:name="Settings$OverlaySettingsActivity"
+ android:label="@string/draw_overlay_title"
+ android:taskAffinity="">
+ <intent-filter android:priority="1">
+ <action android:name="android.settings.action.MANAGE_OVERLAY_PERMISSION" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="Settings$WriteSettingsActivity"
+ android:label="@string/write_settings_title"
+ android:taskAffinity="">
+ <intent-filter android:priority="1">
+ <action android:name="android.settings.action.MANAGE_WRITE_SETTINGS" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
</application>
</manifest>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index d90f6e92d..36f19394a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6839,4 +6839,63 @@
<!-- Number of characters used for lock screen text [CHAR LIMIT=NONE] -->
<string name="accessibility_lock_screen_progress"><xliff:g id="count" example="1">%1$d</xliff:g> of <xliff:g id="count" example="1">%2$d</xliff:g> characters used</string>
+
+ <!-- System Alert Window settings -->
+ <!-- Title of Draw Overlay preference item [CHAR LIMIT=30] -->
+ <string name="draw_overlay_title">Apps that can draw overlay</string>
+ <!-- Title of draw overlay screen [CHAR LIMIT=30] -->
+ <string name="draw_overlay">Draw over other apps</string>
+ <!-- Settings title in main settings screen for SYSTEM_ALERT_WINDOW [CHAR LIMIT=45] -->
+ <string name="system_alert_window_settings">Draw over other apps</string>
+ <!-- Title for the apps with SYSTEM_ALERT_WINDOW permission/privilege [CHAR LIMIT=20] -->
+ <string name="system_alert_window_apps_title">Apps</string>
+ <!-- Title for the apps that are allowed to draw on top of other apps [CHAR LIMIT=60] -->
+ <string name="system_alert_window_access_title">Can draw overlays</string>
+ <!-- Label for setting which controls whether app can draw overlays [CHAR LIMIT=45] -->
+ <string name="permit_draw_overlay">Permit drawing overlays</string>
+ <!-- Link to the apps page for SYSTEM_ALERT_WINDOW settings [CHAR LIMIT=45] -->
+ <string name="app_overlay_permission_preference">App draw on top permission</string>
+ <!-- Description of allowing overlay setting [CHAR LIMIT=NONE] -->
+ <string name="allow_overlay_description">This permission allows an app to display on top of other apps you\u2019re using and may interfere with your use of the interface in other applications, or change what you think you are seeing in other applications.</string>
+
+ <!-- Keyword for SYSTEM_ALERT_WINDOW -->
+ <string name="keywords_system_alert_window">system alert window dialog draw on top other apps</string>
+ <!-- Main settings screen item's title to go into the overlay settings screen [CHAR LIMIT=25] -->
+ <string name="overlay_settings">Overlay settings</string>
+
+ <!-- Summary of number of apps currently can draw overlays [CHAR LIMIT=60] -->
+ <string name="system_alert_window_summary"><xliff:g id="count" example="10">%d</xliff:g> of <xliff:g id="count" example="10">%d</xliff:g> apps allowed to draw on top of other apps</string>
+
+ <!-- Label for showing apps that can draw overlays [CHAR LIMIT=45] -->
+ <string name="filter_overlay_apps">Apps with permission</string>
+ <!-- Summary of app allowed to draw overlay [CHAR LIMIT=60] -->
+ <string name="system_alert_window_on">Yes</string>
+ <!-- Summary of app not allowed to draw overlay [CHAR LIMIT=60] -->
+ <string name="system_alert_window_off">No</string>
+
+
+ <!-- Write Settings settings -->
+ <!-- Settings title in main settings screen for WRITE_SETTINGS [CHAR LIMIT=30] -->
+ <string name="write_settings">Write system settings</string>
+ <!-- Keyword for WRITE_SETTINGS -->
+ <string name="keywords_write_settings">write modify system settings</string>
+ <!-- Summary of number of apps currently can draw overlays [CHAR LIMIT=60] -->
+ <string name="write_settings_summary"><xliff:g id="count" example="10">%d</xliff:g> of <xliff:g id="count" example="10">%d</xliff:g> apps allowed to read or write system settings</string>
+
+ <!-- Label for showing apps that can write system settings [CHAR LIMIT=45] -->
+ <string name="filter_write_settings_apps">Can write system settings</string>
+ <!-- Title for the apps that are allowed to write system settings [CHAR LIMIT=60] -->
+ <string name="write_settings_title">Can write system settings</string>
+ <!-- Main settings screen item's title to go into the write system settings screen [CHAR LIMIT=25] -->
+ <string name="write_system_settings">Write system settings</string>
+ <!-- Link to the apps page for WRITE_SETTINGS settings [CHAR LIMIT=45] -->
+ <string name="write_settings_preference">App write system settings permission</string>
+ <!-- Label for setting which controls whether app can write system settings [CHAR LIMIT=45] -->
+ <string name="permit_write_settings">Allow write system settings</string>
+ <!-- Description of the write system settings [CHAR LIMIT=NONE] -->
+ <string name="write_settings_description">This permission allows an app to read or write system settings.</string>
+ <!-- Summary of app allowed to write system settings [CHAR LIMIT=45] -->
+ <string name="write_settings_on">Yes</string>
+ <!-- Summary of app not allowed to write system settings [CHAR LIMIT=45] -->
+ <string name="write_settings_off">No</string>
</resources>
diff --git a/res/xml/advanced_apps.xml b/res/xml/advanced_apps.xml
index ccdcbf096..70b68568c 100644
--- a/res/xml/advanced_apps.xml
+++ b/res/xml/advanced_apps.xml
@@ -43,6 +43,26 @@
settings:keywords="@string/keywords_default_apps" />
<PreferenceScreen
+ android:key="system_alert_window"
+ android:title="@string/system_alert_window_settings"
+ android:fragment="com.android.settings.applications.ManageApplications"
+ settings:keywords="@string/keywords_system_alert_window">
+ <extra
+ android:name="classname"
+ android:value="com.android.settings.Settings$OverlaySettingsActivity" />
+ </PreferenceScreen>
+
+ <PreferenceScreen
+ android:key="write_settings_apps"
+ android:title="@string/write_settings"
+ android:fragment="com.android.settings.applications.ManageApplications"
+ settings:keywords="@string/keywords_write_settings">
+ <extra
+ android:name="classname"
+ android:value="com.android.settings.Settings$WriteSettingsActivity" />
+ </PreferenceScreen>
+
+ <PreferenceScreen
android:key="high_power_apps"
android:title="@string/high_power_apps"
android:fragment="com.android.settings.applications.ManageApplications"
diff --git a/res/xml/usage_access_details.xml b/res/xml/app_ops_permissions_details.xml
index d8b3bb1af..c36f44e91 100644
--- a/res/xml/usage_access_details.xml
+++ b/res/xml/app_ops_permissions_details.xml
@@ -15,19 +15,16 @@
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
- android:title="@string/usage_access">
+ xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
<SwitchPreference
- android:key="usage_switch"
- android:title="@string/permit_usage_access" />
+ android:key="app_ops_settings_switch" />
<Preference
- android:key="app_usage_preference"
- android:title="@string/app_usage_preference" />
+ android:key="app_ops_settings_preference" />
<Preference
- android:summary="@string/usage_access_description"
+ android:key="app_ops_settings_description"
android:selectable="false" />
</PreferenceScreen>
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index ea4f77af9..f606193a7 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -114,5 +114,6 @@ public class Settings extends SettingsActivity {
public static class ApnSettingsActivity extends SettingsActivity { /* empty */ }
public static class WifiCallingSettingsActivity extends SettingsActivity { /* empty */ }
public static class MemorySettingsActivity extends SettingsActivity { /* empty */ }
+ public static class OverlaySettingsActivity extends SettingsActivity { /* empty */ }
+ public static class WriteSettingsActivity extends SettingsActivity { /* empty */ }
}
-
diff --git a/src/com/android/settings/applications/AdvancedAppSettings.java b/src/com/android/settings/applications/AdvancedAppSettings.java
index 7df269e44..54d38302f 100644
--- a/src/com/android/settings/applications/AdvancedAppSettings.java
+++ b/src/com/android/settings/applications/AdvancedAppSettings.java
@@ -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));
+ }
+ }
+ }
}
diff --git a/src/com/android/settings/applications/AppStateAppOpsBridge.java b/src/com/android/settings/applications/AppStateAppOpsBridge.java
new file mode 100644
index 000000000..20a00bdb0
--- /dev/null
+++ b/src/com/android/settings/applications/AppStateAppOpsBridge.java
@@ -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;
+ }
+ }
+}
diff --git a/src/com/android/settings/applications/AppStateOverlayBridge.java b/src/com/android/settings/applications/AppStateOverlayBridge.java
new file mode 100644
index 000000000..21586bcee
--- /dev/null
+++ b/src/com/android/settings/applications/AppStateOverlayBridge.java
@@ -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;
+ }
+ };
+}
diff --git a/src/com/android/settings/applications/AppStateUsageBridge.java b/src/com/android/settings/applications/AppStateUsageBridge.java
index c06492c0d..a1529017e 100644
--- a/src/com/android/settings/applications/AppStateUsageBridge.java
+++ b/src/com/android/settings/applications/AppStateUsageBridge.java
@@ -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;
- }
-
- @Override
- protected void loadAllExtraInfo() {
- SparseArray<ArrayMap<String, UsageState>> 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, 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;
- }
+ PermissionState permissionState = super.getPermissionInfo(pkg, uid);
+ return new UsageState(permissionState);
}
- 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 static class UsageState extends AppStateAppOpsBridge.PermissionState {
- 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;
}
}
diff --git a/src/com/android/settings/applications/AppStateWriteSettingsBridge.java b/src/com/android/settings/applications/AppStateWriteSettingsBridge.java
new file mode 100644
index 000000000..4ab737f7b
--- /dev/null
+++ b/src/com/android/settings/applications/AppStateWriteSettingsBridge.java
@@ -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;
+ }
+ };
+}
diff --git a/src/com/android/settings/applications/DrawOverlayDetails.java b/src/com/android/settings/applications/DrawOverlayDetails.java
new file mode 100644
index 000000000..078c2c5f0
--- /dev/null
+++ b/src/com/android/settings/applications/DrawOverlayDetails.java
@@ -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);
+ }
+}
diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
index 616c6e7c0..7b49dd312 100644
--- a/src/com/android/settings/applications/ManageApplications.java
+++ b/src/com/android/settings/applications/ManageApplications.java
@@ -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;
diff --git a/src/com/android/settings/applications/UsageAccessDetails.java b/src/com/android/settings/applications/UsageAccessDetails.java
index 6d5995b78..5317282cd 100644
--- a/src/com/android/settings/applications/UsageAccessDetails.java
+++ b/src/com/android/settings/applications/UsageAccessDetails.java
@@ -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);
}
}
diff --git a/src/com/android/settings/applications/WriteSettingsDetails.java b/src/com/android/settings/applications/WriteSettingsDetails.java
new file mode 100644
index 000000000..eeee90c14
--- /dev/null
+++ b/src/com/android/settings/applications/WriteSettingsDetails.java
@@ -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);
+ }
+}