diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 2a59a1d254434..b92b9cea03419 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -61,8 +61,9 @@ public class AppOpsManager { public static final int OP_SEND_SMS = 20; public static final int OP_READ_ICC_SMS = 21; public static final int OP_WRITE_ICC_SMS = 22; + public static final int OP_WRITE_SETTINGS = 23; /** @hide */ - public static final int _NUM_OP = 23; + public static final int _NUM_OP = 24; /** * This maps each operation to the operation that serves as the @@ -96,6 +97,7 @@ public class AppOpsManager { OP_WRITE_SMS, OP_READ_SMS, OP_WRITE_SMS, + OP_WRITE_SETTINGS, }; /** @@ -126,6 +128,7 @@ public class AppOpsManager { "SEND_SMS", "READ_ICC_SMS", "WRITE_ICC_SMS", + "WRITE_SETTINGS", }; /** @@ -156,6 +159,7 @@ public class AppOpsManager { android.Manifest.permission.SEND_SMS, android.Manifest.permission.READ_SMS, android.Manifest.permission.WRITE_SMS, + android.Manifest.permission.WRITE_SETTINGS, }; public static int opToSwitch(int op) { diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index e66efd525ba88..90ee0ac8a390c 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -174,8 +174,8 @@ public abstract class ContentProvider implements ComponentCallbacks2 { */ class Transport extends ContentProviderNative { AppOpsManager mAppOpsManager = null; - int mReadOp = -1; - int mWriteOp = -1; + int mReadOp = AppOpsManager.OP_NONE; + int mWriteOp = AppOpsManager.OP_NONE; ContentProvider getContentProvider() { return ContentProvider.this; @@ -274,7 +274,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { @Override public Bundle call(String callingPkg, String method, String arg, Bundle extras) { - return ContentProvider.this.call(method, arg, extras); + return ContentProvider.this.callFromPackage(callingPkg, method, arg, extras); } @Override @@ -309,7 +309,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { private int enforceReadPermission(String callingPkg, Uri uri) throws SecurityException { enforceReadPermissionInner(uri); - if (mAppOpsManager != null) { + if (mReadOp != AppOpsManager.OP_NONE) { return mAppOpsManager.noteOp(mReadOp, Binder.getCallingUid(), callingPkg); } return AppOpsManager.MODE_ALLOWED; @@ -378,7 +378,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { private int enforceWritePermission(String callingPkg, Uri uri) throws SecurityException { enforceWritePermissionInner(uri); - if (mAppOpsManager != null) { + if (mWriteOp != AppOpsManager.OP_NONE) { return mAppOpsManager.noteOp(mWriteOp, Binder.getCallingUid(), callingPkg); } return AppOpsManager.MODE_ALLOWED; @@ -529,6 +529,11 @@ public abstract class ContentProvider implements ComponentCallbacks2 { mTransport.mWriteOp = writeOp; } + /** @hide */ + public AppOpsManager getAppOpsManager() { + return mTransport.mAppOpsManager; + } + /** * Implement this to initialize your content provider on startup. * This method is called for all registered content providers on the @@ -1188,6 +1193,15 @@ public abstract class ContentProvider implements ComponentCallbacks2 { return results; } + /** + * @hide + * Front-end to {@link #call(String, String, android.os.Bundle)} that provides the name + * of the calling package. + */ + public Bundle callFromPackage(String callingPackag, String method, String arg, Bundle extras) { + return call(method, arg, extras); + } + /** * Call a provider-defined method. This can be used to implement * interfaces that are cheaper and/or unnatural for a table-like diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index f18338a69541a..485908e0b6d74 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -23,6 +23,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import android.app.ActivityManager; +import android.app.AppOpsManager; import android.app.backup.BackupManager; import android.content.BroadcastReceiver; import android.content.ContentProvider; @@ -323,6 +324,7 @@ public class SettingsProvider extends ContentProvider { mBackupManager = new BackupManager(getContext()); mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE); + setAppOps(AppOpsManager.OP_NONE, AppOpsManager.OP_WRITE_SETTINGS); establishDbTracking(UserHandle.USER_OWNER); IntentFilter userFilter = new IntentFilter(); @@ -544,7 +546,8 @@ public class SettingsProvider extends ContentProvider { * Fast path that avoids the use of chatty remoted Cursors. */ @Override - public Bundle call(String method, String request, Bundle args) { + public Bundle callFromPackage(String callingPackage, String method, String request, + Bundle args) { int callingUser = UserHandle.getCallingUserId(); if (args != null) { int reqUser = args.getInt(Settings.CALL_METHOD_USER_KEY, callingUser); @@ -586,7 +589,22 @@ public class SettingsProvider extends ContentProvider { // Put methods - new value is in the args bundle under the key named by // the Settings.NameValueTable.VALUE static. final String newValue = (args == null) - ? null : args.getString(Settings.NameValueTable.VALUE); + ? null : args.getString(Settings.NameValueTable.VALUE); + + // Framework can't do automatic permission checking for calls, so we need + // to do it here. + if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.WRITE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException( + String.format("Permission denial: writing to settings requires %1$s", + android.Manifest.permission.WRITE_SETTINGS)); + } + + // Also need to take care of app op. + if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SETTINGS, Binder.getCallingUid(), + callingPackage) != AppOpsManager.MODE_ALLOWED) { + return null; + } final ContentValues values = new ContentValues(); values.put(Settings.NameValueTable.NAME, request); diff --git a/services/java/com/android/server/AppOpsService.java b/services/java/com/android/server/AppOpsService.java index 335917e20f007..748b3cbbac189 100644 --- a/services/java/com/android/server/AppOpsService.java +++ b/services/java/com/android/server/AppOpsService.java @@ -204,6 +204,7 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public void setMode(int code, int uid, String packageName, int mode) { verifyIncomingUid(uid); + verifyIncomingOp(code); synchronized (this) { Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, true); if (op != null) { @@ -218,6 +219,7 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public int checkOperation(int code, int uid, String packageName) { verifyIncomingUid(uid); + verifyIncomingOp(code); synchronized (this) { Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false); if (op == null) { @@ -230,6 +232,7 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public int noteOperation(int code, int uid, String packageName) { verifyIncomingUid(uid); + verifyIncomingOp(code); synchronized (this) { Ops ops = getOpsLocked(uid, packageName, true); if (ops == null) { @@ -261,6 +264,7 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public int startOperation(int code, int uid, String packageName) { verifyIncomingUid(uid); + verifyIncomingOp(code); synchronized (this) { Ops ops = getOpsLocked(uid, packageName, true); if (ops == null) { @@ -291,6 +295,7 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public void finishOperation(int code, int uid, String packageName) { verifyIncomingUid(uid); + verifyIncomingOp(code); synchronized (this) { Op op = getOpLocked(code, uid, packageName, true); if (op == null) { @@ -322,6 +327,13 @@ public class AppOpsService extends IAppOpsService.Stub { Binder.getCallingPid(), Binder.getCallingUid(), null); } + private void verifyIncomingOp(int op) { + if (op >= 0 && op < AppOpsManager._NUM_OP) { + return; + } + throw new IllegalArgumentException("Bad operation #" + op); + } + private Ops getOpsLocked(int uid, String packageName, boolean edit) { HashMap pkgOps = mUidOps.get(uid); if (pkgOps == null) { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index e682da788aade..8b643c0386480 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -1205,6 +1205,11 @@ public final class BridgeContext extends Context { } + @Override + public void sendBroadcast(Intent intent, String receiverPermission, int appOp) { + // pass + } + @Override public void sendOrderedBroadcast(Intent arg0, String arg1) { // pass @@ -1219,6 +1224,13 @@ public final class BridgeContext extends Context { } + @Override + public void sendOrderedBroadcast(Intent intent, String receiverPermission, int appOp, + BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, + String initialData, Bundle initialExtras) { + // pass + } + @Override public void sendBroadcastAsUser(Intent intent, UserHandle user) { // pass