Merge "Bug: 21589105 Rescope WRITE_SETTINGS permission (framework services perm check changes)" into mnc-dev
diff --git a/api/current.txt b/api/current.txt
index 7f788a5..72e0447 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -26404,6 +26404,7 @@
public final class Settings {
ctor public Settings();
+ method public static boolean canDrawOverlays(android.content.Context);
field public static final java.lang.String ACTION_ACCESSIBILITY_SETTINGS = "android.settings.ACCESSIBILITY_SETTINGS";
field public static final java.lang.String ACTION_ADD_ACCOUNT = "android.settings.ADD_ACCOUNT_SETTINGS";
field public static final java.lang.String ACTION_AIRPLANE_MODE_SETTINGS = "android.settings.AIRPLANE_MODE_SETTINGS";
@@ -26623,6 +26624,7 @@
public static final class Settings.System extends android.provider.Settings.NameValueTable {
ctor public Settings.System();
+ method public static boolean canWrite(android.content.Context);
method public static void getConfiguration(android.content.ContentResolver, android.content.res.Configuration);
method public static float getFloat(android.content.ContentResolver, java.lang.String, float);
method public static float getFloat(android.content.ContentResolver, java.lang.String) throws android.provider.Settings.SettingNotFoundException;
diff --git a/api/system-current.txt b/api/system-current.txt
index 4ef1719..65214b8 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -28463,6 +28463,7 @@
public final class Settings {
ctor public Settings();
+ method public static boolean canDrawOverlays(android.content.Context);
field public static final java.lang.String ACTION_ACCESSIBILITY_SETTINGS = "android.settings.ACCESSIBILITY_SETTINGS";
field public static final java.lang.String ACTION_ADD_ACCOUNT = "android.settings.ADD_ACCOUNT_SETTINGS";
field public static final java.lang.String ACTION_AIRPLANE_MODE_SETTINGS = "android.settings.AIRPLANE_MODE_SETTINGS";
@@ -28683,6 +28684,7 @@
public static final class Settings.System extends android.provider.Settings.NameValueTable {
ctor public Settings.System();
+ method public static boolean canWrite(android.content.Context);
method public static void getConfiguration(android.content.ContentResolver, android.content.res.Configuration);
method public static float getFloat(android.content.ContentResolver, java.lang.String, float);
method public static float getFloat(android.content.ContentResolver, java.lang.String) throws android.provider.Settings.SettingNotFoundException;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 976830fc..862d235 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -778,7 +778,7 @@
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
- AppOpsManager.MODE_ALLOWED,
+ AppOpsManager.MODE_DEFAULT, // OP_WRITE_SETTINGS
AppOpsManager.MODE_DEFAULT, // OP_SYSTEM_ALERT_WINDOW
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 894d805..10470aa 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -20,6 +20,7 @@
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.app.ActivityThread;
+import android.app.AppOpsManager;
import android.app.Application;
import android.app.SearchManager;
import android.app.WallpaperManager;
@@ -41,6 +42,7 @@
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.BatteryManager;
+import android.os.Binder;
import android.os.Bundle;
import android.os.DropBoxManager;
import android.os.IBinder;
@@ -564,13 +566,14 @@
"android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS";
/**
- * Activity Action: Show settings to toggle permission to draw on top of
- * other apps.
+ * Activity Action: Show screen for controlling which apps can draw on top of other apps.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
* safeguard against this.
* <p>
- * Input: Nothing.
+ * Input: Optionally, the Intent's data URI can specify the application package name to
+ * directly invoke the management GUI specific to the package name. For example
+ * "package:com.my.app".
* <p>
* Output: Nothing.
*/
@@ -579,13 +582,15 @@
"android.settings.action.MANAGE_OVERLAY_PERMISSION";
/**
- * Activity Action: Show settings to toggle apps' capablity to
- * to read/write system settings.
+ * Activity Action: Show screen for controlling which apps are allowed to write/modify
+ * system settings.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
* safeguard against this.
* <p>
- * Input: Nothing.
+ * Input: Optionally, the Intent's data URI can specify the application package name to
+ * directly invoke the management GUI specific to the package name. For example
+ * "package:com.my.app".
* <p>
* Output: Nothing.
*/
@@ -1386,6 +1391,23 @@
}
/**
+ * An app can use this method to check if it is currently allowed to draw on top of other
+ * apps. In order to be allowed to do so, an app must first declare the
+ * {@link android.Manifest.permission#SYSTEM_ALERT_WINDOW} permission in its manifest. If it
+ * is currently disallowed, it can prompt the user to grant it this capability through a
+ * management UI by sending an Intent with action
+ * {@link android.provider.Settings#ACTION_MANAGE_OVERLAY_PERMISSION}.
+ *
+ * @param context A context
+ * @return true if the calling app can draw on top of other apps, false otherwise.
+ */
+ public static boolean canDrawOverlays(Context context) {
+ int uid = Binder.getCallingUid();
+ return Settings.isCallingPackageAllowedToDrawOverlays(context, uid, Settings
+ .getPackageNameForUid(context, uid), false);
+ }
+
+ /**
* System settings, containing miscellaneous system preferences. This
* table holds simple name/value pairs. There are convenience
* functions for accessing individual settings entries.
@@ -3658,6 +3680,23 @@
@Deprecated
public static final String WIFI_WATCHDOG_PING_TIMEOUT_MS =
Secure.WIFI_WATCHDOG_PING_TIMEOUT_MS;
+
+ /**
+ * An app can use this method to check if it is currently allowed to write or modify system
+ * settings. In order to gain write access to the system settings, an app must declare the
+ * {@link android.Manifest.permission#WRITE_SETTINGS} permission in its manifest. If it is
+ * currently disallowed, it can prompt the user to grant it this capability through a
+ * management UI by sending an Intent with action
+ * {@link android.provider.Settings#ACTION_MANAGE_WRITE_SETTINGS}.
+ *
+ * @param context A context
+ * @return true if the calling app can write to system settings, false otherwise
+ */
+ public static boolean canWrite(Context context) {
+ int uid = Binder.getCallingUid();
+ return isCallingPackageAllowedToWriteSettings(context, uid, getPackageNameForUid(
+ context, uid), false);
+ }
}
/**
@@ -8205,4 +8244,121 @@
public static String getGTalkDeviceId(long androidId) {
return "android-" + Long.toHexString(androidId);
}
+
+ /**
+ * Performs a strict and comprehensive check of whether a calling package is allowed to
+ * write/modify system settings, as the condition differs for pre-M, M+, and
+ * privileged/preinstalled apps. If the provided uid does not match the
+ * callingPackage, a negative result will be returned.
+ * @hide
+ */
+ public static boolean isCallingPackageAllowedToWriteSettings(Context context, int uid,
+ String callingPackage, boolean throwException) {
+ return isCallingPackageAllowedToPerformAppOpsProtectedOperation(context, uid,
+ callingPackage, throwException, AppOpsManager.OP_WRITE_SETTINGS,
+ android.Manifest.permission.WRITE_SETTINGS, false);
+ }
+
+ /**
+ * Performs a strict and comprehensive check of whether a calling package is allowed to
+ * write/modify system settings, as the condition differs for pre-M, M+, and
+ * privileged/preinstalled apps. If the provided uid does not match the
+ * callingPackage, a negative result will be returned.
+ *
+ * Note: if the check is successful, the operation of this app will be updated to the
+ * current time.
+ * @hide
+ */
+ public static boolean checkAndNoteWriteSettingsOperation(Context context, int uid,
+ String callingPackage, boolean throwException) {
+ return isCallingPackageAllowedToPerformAppOpsProtectedOperation(context, uid,
+ callingPackage, throwException, AppOpsManager.OP_WRITE_SETTINGS,
+ android.Manifest.permission.WRITE_SETTINGS, true);
+ }
+
+ /**
+ * Performs a strict and comprehensive check of whether a calling package is allowed to
+ * draw on top of other apps, as the conditions differs for pre-M, M+, and
+ * privileged/preinstalled apps. If the provided uid does not match the callingPackage,
+ * a negative result will be returned.
+ * @hide
+ */
+ public static boolean isCallingPackageAllowedToDrawOverlays(Context context, int uid,
+ String callingPackage, boolean throwException) {
+ return isCallingPackageAllowedToPerformAppOpsProtectedOperation(context, uid,
+ callingPackage, throwException, AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
+ android.Manifest.permission.SYSTEM_ALERT_WINDOW, false);
+ }
+
+ /**
+ * Performs a strict and comprehensive check of whether a calling package is allowed to
+ * draw on top of other apps, as the conditions differs for pre-M, M+, and
+ * privileged/preinstalled apps. If the provided uid does not match the callingPackage,
+ * a negative result will be returned.
+ *
+ * Note: if the check is successful, the operation of this app will be updated to the
+ * current time.
+ * @hide
+ */
+ public static boolean checkAndNoteDrawOverlaysOperation(Context context, int uid, String
+ callingPackage, boolean throwException) {
+ return isCallingPackageAllowedToPerformAppOpsProtectedOperation(context, uid,
+ callingPackage, throwException, AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
+ android.Manifest.permission.SYSTEM_ALERT_WINDOW, true);
+ }
+
+ /**
+ * Helper method to perform a general and comprehensive check of whether an operation that is
+ * protected by appops can be performed by a caller or not. e.g. OP_SYSTEM_ALERT_WINDOW and
+ * OP_WRITE_SETTINGS
+ * @hide
+ */
+ public static boolean isCallingPackageAllowedToPerformAppOpsProtectedOperation(Context context,
+ int uid, String callingPackage, boolean throwException, int appOpsOpCode, String
+ permissionName, boolean makeNote) {
+ if (callingPackage == null) {
+ return false;
+ }
+
+ AppOpsManager appOpsMgr = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
+ int mode = AppOpsManager.MODE_DEFAULT;
+ if (makeNote) {
+ mode = appOpsMgr.noteOpNoThrow(appOpsOpCode, uid, callingPackage);
+ } else {
+ mode = appOpsMgr.checkOpNoThrow(appOpsOpCode, uid, callingPackage);
+ }
+
+ switch (mode) {
+ case AppOpsManager.MODE_ALLOWED:
+ return true;
+ case AppOpsManager.MODE_DEFAULT:
+ // this is the default operating mode after an app's installation
+ if (!throwException) {
+ return context.checkCallingOrSelfPermission(permissionName) ==
+ PackageManager.PERMISSION_GRANTED;
+ }
+ default:
+ // this is for all other cases trickled down here...
+ if (!throwException) {
+ return false;
+ }
+ }
+ throw new SecurityException(callingPackage + " was not granted "
+ + permissionName + " permission");
+ }
+
+ /**
+ * Retrieves a correponding package name for a given uid. It will query all
+ * packages that are associated with the given uid, but it will return only
+ * the zeroth result.
+ * Note: If package could not be found, a null is returned.
+ * @hide
+ */
+ public static String getPackageNameForUid(Context context, int uid) {
+ String[] packages = context.getPackageManager().getPackagesForUid(uid);
+ if (packages == null) {
+ return null;
+ }
+ return packages[0];
+ }
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 2a68252..3e9b122 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -643,11 +643,6 @@
// Make sure the caller can change the settings - treated as secure.
enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
- // Verify whether this operation is allowed for the calling package.
- if (!isAppOpWriteSettingsAllowedForCallingPackage()) {
- return false;
- }
-
// Resolve the userId on whose behalf the call is made.
final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
@@ -773,11 +768,6 @@
// Make sure the caller can change the settings.
enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
- // Verify whether this operation is allowed for the calling package.
- if (!isAppOpWriteSettingsAllowedForCallingPackage()) {
- return false;
- }
-
// Resolve the userId on whose behalf the call is made.
final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
@@ -904,14 +894,13 @@
private boolean mutateSystemSetting(String name, String value, int runAsUserId,
int operation) {
- // Check for permissions first.
- if (!hasPermissionsToMutateSystemSettings()) {
- return false;
- }
-
- // Verify whether this operation is allowed for the calling package.
- if (!isAppOpWriteSettingsAllowedForCallingPackage()) {
- return false;
+ if (!hasWriteSecureSettingsPermission()) {
+ // If the caller doesn't hold WRITE_SECURE_SETTINGS, we verify whether this
+ // operation is allowed for the calling package through appops.
+ if (!Settings.checkAndNoteWriteSettingsOperation(getContext(),
+ Binder.getCallingUid(), getCallingPackage(), true)) {
+ return false;
+ }
}
// Enforce what the calling package can mutate the system settings.
@@ -956,25 +945,13 @@
}
}
- private boolean hasPermissionsToMutateSystemSettings() {
+ private boolean hasWriteSecureSettingsPermission() {
// Write secure settings is a more protected permission. If caller has it we are good.
if (getContext().checkCallingOrSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
== PackageManager.PERMISSION_GRANTED) {
return true;
}
- // The write settings permission gates mutation of system settings.
- if (getContext().checkCallingOrSelfPermission(Manifest.permission.WRITE_SETTINGS)
- == PackageManager.PERMISSION_GRANTED) {
- return true;
- }
-
- // Excpet we let system apps change system settings without the permission.
- PackageInfo packageInfo = getCallingPackageInfoOrThrow();
- if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- return true;
- }
-
return false;
}
@@ -1102,15 +1079,6 @@
}
}
- private boolean isAppOpWriteSettingsAllowedForCallingPackage() {
- final int callingUid = Binder.getCallingUid();
-
- mAppOpsManager.checkPackage(Binder.getCallingUid(), getCallingPackage());
-
- return mAppOpsManager.noteOp(AppOpsManager.OP_WRITE_SETTINGS, callingUid,
- getCallingPackage()) == AppOpsManager.MODE_ALLOWED;
- }
-
private void enforceWritePermission(String permission) {
if (getContext().checkCallingOrSelfPermission(permission)
!= PackageManager.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 89e500e..783dea5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17201,8 +17201,7 @@
public void updatePersistentConfiguration(Configuration values) {
enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
"updateConfiguration()");
- enforceCallingPermission(android.Manifest.permission.WRITE_SETTINGS,
- "updateConfiguration()");
+ enforceWriteSettingsPermission("updateConfiguration()");
if (values == null) {
throw new NullPointerException("Configuration must not be null");
}
@@ -17214,6 +17213,25 @@
}
}
+ private void enforceWriteSettingsPermission(String func) {
+ int uid = Binder.getCallingUid();
+ if (uid == Process.ROOT_UID) {
+ return;
+ }
+
+ if (Settings.checkAndNoteWriteSettingsOperation(mContext, uid,
+ Settings.getPackageNameForUid(mContext, uid), false)) {
+ return;
+ }
+
+ String msg = "Permission Denial: " + func + " from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + uid
+ + " requires " + android.Manifest.permission.WRITE_SETTINGS;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
public void updateConfiguration(Configuration values) {
enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
"updateConfiguration()");
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 88476ce..b920f97 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -30,6 +30,7 @@
import com.android.server.Watchdog;
import android.Manifest;
+import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -3319,8 +3320,14 @@
*/
@Override // Binder call
public void setStayOnSetting(int val) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.WRITE_SETTINGS, null);
+ int uid = Binder.getCallingUid();
+ // if uid is of root's, we permit this operation straight away
+ if (uid != Process.ROOT_UID) {
+ if (!Settings.checkAndNoteWriteSettingsOperation(mContext, uid,
+ Settings.getPackageNameForUid(mContext, uid), true)) {
+ return;
+ }
+ }
final long ident = Binder.clearCallingIdentity();
try {