Provide calling package to ContentProviders.

The calling package is important for ContentProviders that want to
grant Uri permissions as a side effect of operations, so offer it
through a new API.  Validates the provided package against the
calling UID before returning.

Bug: 10626527
Change-Id: I7277880eebbd48444c024bcf5f69199133cd59e4
diff --git a/api/current.txt b/api/current.txt
index fa29b8f..1443bee 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3172,6 +3172,7 @@
   public class AppOpsManager {
     method public int checkOp(int, int, java.lang.String);
     method public int checkOpNoThrow(int, int, java.lang.String);
+    method public void checkPackage(int, java.lang.String);
     method public void finishOp(int, int, java.lang.String);
     method public void finishOp(int);
     method public int noteOp(int, int, java.lang.String);
@@ -5624,6 +5625,7 @@
     method public android.os.Bundle call(java.lang.String, java.lang.String, android.os.Bundle);
     method public abstract int delete(android.net.Uri, java.lang.String, java.lang.String[]);
     method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public final java.lang.String getCallingPackage();
     method public final android.content.Context getContext();
     method public final android.content.pm.PathPermission[] getPathPermissions();
     method public final java.lang.String getReadPermission();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index fe09ce1..19a028d 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -616,6 +616,23 @@
     }
 
     /**
+     * Do a quick check to validate if a package name belongs to a UID.
+     *
+     * @throws SecurityException if the package name doesn't belong to the given
+     *             UID, or if ownership cannot be verified.
+     */
+    public void checkPackage(int uid, String packageName) {
+        try {
+            if (mService.checkPackage(uid, packageName) != MODE_ALLOWED) {
+                throw new SecurityException(
+                        "Package " + packageName + " does not belong to " + uid);
+            }
+        } catch (RemoteException e) {
+            throw new SecurityException("Unable to verify package ownership", e);
+        }
+    }
+
+    /**
      * Make note of an application performing an operation.  Note that you must pass
      * in both the uid and name of the application to be checked; this function will verify
      * that these two match, and if not, return {@link #MODE_IGNORED}.  If this call
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index b9121c7..24c396a 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -102,6 +102,8 @@
     private boolean mExported;
     private boolean mNoPerms;
 
+    private final ThreadLocal<String> mCallingPackage = new ThreadLocal<String>();
+
     private Transport mTransport = new Transport();
 
     /**
@@ -194,8 +196,14 @@
                 return rejectQuery(uri, projection, selection, selectionArgs, sortOrder,
                         CancellationSignal.fromTransport(cancellationSignal));
             }
-            return ContentProvider.this.query(uri, projection, selection, selectionArgs, sortOrder,
-                    CancellationSignal.fromTransport(cancellationSignal));
+            mCallingPackage.set(callingPkg);
+            try {
+                return ContentProvider.this.query(
+                        uri, projection, selection, selectionArgs, sortOrder,
+                        CancellationSignal.fromTransport(cancellationSignal));
+            } finally {
+                mCallingPackage.set(null);
+            }
         }
 
         @Override
@@ -208,7 +216,12 @@
             if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
                 return rejectInsert(uri, initialValues);
             }
-            return ContentProvider.this.insert(uri, initialValues);
+            mCallingPackage.set(callingPkg);
+            try {
+                return ContentProvider.this.insert(uri, initialValues);
+            } finally {
+                mCallingPackage.set(null);
+            }
         }
 
         @Override
@@ -216,7 +229,12 @@
             if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
                 return 0;
             }
-            return ContentProvider.this.bulkInsert(uri, initialValues);
+            mCallingPackage.set(callingPkg);
+            try {
+                return ContentProvider.this.bulkInsert(uri, initialValues);
+            } finally {
+                mCallingPackage.set(null);
+            }
         }
 
         @Override
@@ -238,7 +256,12 @@
                     }
                 }
             }
-            return ContentProvider.this.applyBatch(operations);
+            mCallingPackage.set(callingPkg);
+            try {
+                return ContentProvider.this.applyBatch(operations);
+            } finally {
+                mCallingPackage.set(null);
+            }
         }
 
         @Override
@@ -246,7 +269,12 @@
             if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
                 return 0;
             }
-            return ContentProvider.this.delete(uri, selection, selectionArgs);
+            mCallingPackage.set(callingPkg);
+            try {
+                return ContentProvider.this.delete(uri, selection, selectionArgs);
+            } finally {
+                mCallingPackage.set(null);
+            }
         }
 
         @Override
@@ -255,7 +283,12 @@
             if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
                 return 0;
             }
-            return ContentProvider.this.update(uri, values, selection, selectionArgs);
+            mCallingPackage.set(callingPkg);
+            try {
+                return ContentProvider.this.update(uri, values, selection, selectionArgs);
+            } finally {
+                mCallingPackage.set(null);
+            }
         }
 
         @Override
@@ -263,8 +296,13 @@
                 String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal)
                 throws FileNotFoundException {
             enforceFilePermission(callingPkg, uri, mode);
-            return ContentProvider.this.openFile(
-                    uri, mode, CancellationSignal.fromTransport(cancellationSignal));
+            mCallingPackage.set(callingPkg);
+            try {
+                return ContentProvider.this.openFile(
+                        uri, mode, CancellationSignal.fromTransport(cancellationSignal));
+            } finally {
+                mCallingPackage.set(null);
+            }
         }
 
         @Override
@@ -272,13 +310,23 @@
                 String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal)
                 throws FileNotFoundException {
             enforceFilePermission(callingPkg, uri, mode);
-            return ContentProvider.this.openAssetFile(
-                    uri, mode, CancellationSignal.fromTransport(cancellationSignal));
+            mCallingPackage.set(callingPkg);
+            try {
+                return ContentProvider.this.openAssetFile(
+                        uri, mode, CancellationSignal.fromTransport(cancellationSignal));
+            } finally {
+                mCallingPackage.set(null);
+            }
         }
 
         @Override
         public Bundle call(String callingPkg, String method, String arg, Bundle extras) {
-            return ContentProvider.this.callFromPackage(callingPkg, method, arg, extras);
+            mCallingPackage.set(callingPkg);
+            try {
+                return ContentProvider.this.call(method, arg, extras);
+            } finally {
+                mCallingPackage.set(null);
+            }
         }
 
         @Override
@@ -290,8 +338,13 @@
         public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri uri, String mimeType,
                 Bundle opts, ICancellationSignal cancellationSignal) throws FileNotFoundException {
             enforceFilePermission(callingPkg, uri, "r");
-            return ContentProvider.this.openTypedAssetFile(
-                    uri, mimeType, opts, CancellationSignal.fromTransport(cancellationSignal));
+            mCallingPackage.set(callingPkg);
+            try {
+                return ContentProvider.this.openTypedAssetFile(
+                        uri, mimeType, opts, CancellationSignal.fromTransport(cancellationSignal));
+            } finally {
+                mCallingPackage.set(null);
+            }
         }
 
         @Override
@@ -461,6 +514,28 @@
     }
 
     /**
+     * Return the package name of the caller that initiated the request being
+     * processed on the current thread. The returned package will have been
+     * verified to belong to the calling UID. Returns {@code null} if not
+     * currently processing a request.
+     * <p>
+     * This will always return {@code null} when processing
+     * {@link #getType(Uri)} or {@link #getStreamTypes(Uri, String)} requests.
+     *
+     * @see Binder#getCallingUid()
+     * @see Context#grantUriPermission(String, Uri, int)
+     * @throws SecurityException if the calling package doesn't belong to the
+     *             calling UID.
+     */
+    public final String getCallingPackage() {
+        final String pkg = mCallingPackage.get();
+        if (pkg != null) {
+            mTransport.mAppOpsManager.checkPackage(Binder.getCallingUid(), pkg);
+        }
+        return pkg;
+    }
+
+    /**
      * Change the permission required to read data from the content
      * provider.  This is normally set for you from its manifest information
      * when the provider is first created.
@@ -529,8 +604,6 @@
     /** @hide */
     public final void setAppOps(int readOp, int writeOp) {
         if (!mNoPerms) {
-            mTransport.mAppOpsManager = (AppOpsManager)mContext.getSystemService(
-                    Context.APP_OPS_SERVICE);
             mTransport.mReadOp = readOp;
             mTransport.mWriteOp = writeOp;
         }
@@ -1413,6 +1486,8 @@
          */
         if (mContext == null) {
             mContext = context;
+            mTransport.mAppOpsManager = (AppOpsManager) mContext.getSystemService(
+                    Context.APP_OPS_SERVICE);
             mMyUid = Process.myUid();
             if (info != null) {
                 setReadPermission(info.readPermission);
@@ -1452,15 +1527,6 @@
     }
 
     /**
-     * @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
      * model.
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index bdfb776..1b0fc4d 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -34,7 +34,6 @@
 import android.database.Cursor;
 import android.graphics.Point;
 import android.net.Uri;
-import android.os.Binder;
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.ParcelFileDescriptor;
@@ -42,8 +41,6 @@
 import android.provider.DocumentsContract.Document;
 import android.util.Log;
 
-import com.android.internal.util.ArrayUtils;
-
 import libcore.io.IoUtils;
 
 import java.io.FileNotFoundException;
@@ -332,15 +329,22 @@
         throw new UnsupportedOperationException("Update not supported");
     }
 
-    /** {@hide} */
+    /**
+     * Implementation is provided by the parent class. Can be overridden to
+     * provide additional functionality, but subclasses <em>must</em> always
+     * call the superclass. If the superclass returns {@code null}, the subclass
+     * may implement custom behavior.
+     *
+     * @see #openDocument(String, String, CancellationSignal)
+     * @see #deleteDocument(String)
+     */
     @Override
-    public final Bundle callFromPackage(
-            String callingPackage, String method, String arg, Bundle extras) {
+    public Bundle call(String method, String arg, Bundle extras) {
         final Context context = getContext();
 
         if (!method.startsWith("android:")) {
             // Let non-platform methods pass through
-            return super.callFromPackage(callingPackage, method, arg, extras);
+            return super.call(method, arg, extras);
         }
 
         final String documentId = extras.getString(Document.COLUMN_DOCUMENT_ID);
@@ -368,7 +372,7 @@
                 if (!callerHasManage) {
                     final Uri newDocumentUri = DocumentsContract.buildDocumentUri(
                             mAuthority, newDocumentId);
-                    context.grantUriPermission(callingPackage, newDocumentUri,
+                    context.grantUriPermission(getCallingPackage(), newDocumentUri,
                             Intent.FLAG_GRANT_READ_URI_PERMISSION
                             | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                             | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index b798a1a..9056641 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -23,6 +23,7 @@
     // These first methods are also called by native code, so must
     // be kept in sync with frameworks/native/include/binder/IAppOpsService.h
     int checkOperation(int code, int uid, String packageName);
+    int checkPackage(int uid, String packageName);
     int noteOperation(int code, int uid, String packageName);
     int startOperation(IBinder token, int code, int uid, String packageName);
     void finishOperation(IBinder token, int code, int uid, String packageName);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index a5dab33..bc02b0d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -567,8 +567,7 @@
      * Fast path that avoids the use of chatty remoted Cursors.
      */
     @Override
-    public Bundle callFromPackage(String callingPackage, String method, String request,
-            Bundle args) {
+    public Bundle call(String method, String request, Bundle args) {
         int callingUser = UserHandle.getCallingUserId();
         if (args != null) {
             int reqUser = args.getInt(Settings.CALL_METHOD_USER_KEY, callingUser);
@@ -623,7 +622,7 @@
 
         // Also need to take care of app op.
         if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SETTINGS, Binder.getCallingUid(),
-                callingPackage) != AppOpsManager.MODE_ALLOWED) {
+                getCallingPackage()) != AppOpsManager.MODE_ALLOWED) {
             return null;
         }
 
diff --git a/services/java/com/android/server/AppOpsService.java b/services/java/com/android/server/AppOpsService.java
index 7af95f3..c6c4a94 100644
--- a/services/java/com/android/server/AppOpsService.java
+++ b/services/java/com/android/server/AppOpsService.java
@@ -552,6 +552,17 @@
     }
 
     @Override
+    public int checkPackage(int uid, String packageName) {
+        synchronized (this) {
+            if (getOpsLocked(uid, packageName, true) != null) {
+                return AppOpsManager.MODE_ALLOWED;
+            } else {
+                return AppOpsManager.MODE_ERRORED;
+            }
+        }
+    }
+
+    @Override
     public int noteOperation(int code, int uid, String packageName) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
@@ -560,7 +571,7 @@
             if (ops == null) {
                 if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
                         + " package " + packageName);
-                return AppOpsManager.MODE_IGNORED;
+                return AppOpsManager.MODE_ERRORED;
             }
             Op op = getOpLocked(ops, code, true);
             if (op.duration == -1) {
@@ -594,7 +605,7 @@
             if (ops == null) {
                 if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid
                         + " package " + packageName);
-                return AppOpsManager.MODE_IGNORED;
+                return AppOpsManager.MODE_ERRORED;
             }
             Op op = getOpLocked(ops, code, true);
             final int switchCode = AppOpsManager.opToSwitch(code);
diff --git a/services/java/com/android/server/DropBoxManagerService.java b/services/java/com/android/server/DropBoxManagerService.java
index 5008270..29b04da 100644
--- a/services/java/com/android/server/DropBoxManagerService.java
+++ b/services/java/com/android/server/DropBoxManagerService.java
@@ -24,6 +24,7 @@
 import android.content.pm.PackageManager;
 import android.database.ContentObserver;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Debug;
 import android.os.DropBoxManager;
 import android.os.FileUtils;
@@ -265,8 +266,13 @@
     }
 
     public boolean isTagEnabled(String tag) {
-        return !"disabled".equals(Settings.Global.getString(
-                mContentResolver, Settings.Global.DROPBOX_TAG_PREFIX + tag));
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return !"disabled".equals(Settings.Global.getString(
+                    mContentResolver, Settings.Global.DROPBOX_TAG_PREFIX + tag));
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     public synchronized DropBoxManager.Entry getNextEntry(String tag, long millis) {