summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2013-01-14 17:38:02 -0800
committerDianne Hackborn <hackbod@google.com>2013-01-16 12:11:01 -0800
commit35654b61e8fe7bc85afcb076ddbb590d51c5865f (patch)
tree0f42a90b4deaa0156d84df5d79b78cd6f2ac8807
parent8a8b047f2d28f6b2d728731a7e71eeaf16f89700 (diff)
downloadframeworks_base-35654b61e8fe7bc85afcb076ddbb590d51c5865f.zip
frameworks_base-35654b61e8fe7bc85afcb076ddbb590d51c5865f.tar.gz
frameworks_base-35654b61e8fe7bc85afcb076ddbb590d51c5865f.tar.bz2
More work on App Ops service.
Implemented reading and writing state to retain information across boots, API to retrieve state from it, improved location manager interaction to monitor both coarse and fine access and only note operations when location data is being delivered back to app (not when it is just registering to get the data at some time in the future). Also implement tracking of read/write ops on contacts and the call log. This involved tweaking the content provider protocol to pass over the name of the calling package, and some infrastructure in the ContentProvider transport to note incoming calls with the app ops service. The contacts provider and call log provider turn this on for themselves. This also implements some of the mechanics of being able to ignore incoming provider calls... all that is left are some new APIs for the real content provider implementation to be involved with providing the correct behavior for query() (return an empty cursor with the right columns) and insert() (need to figure out what URI to return). Change-Id: I36ebbcd63dee58264a480f3d3786891ca7cbdb4c
-rw-r--r--cmds/content/src/com/android/commands/content/Content.java8
-rw-r--r--cmds/settings/src/com/android/commands/settings/SettingsCmd.java4
-rw-r--r--core/java/android/app/AppOpsManager.aidl20
-rw-r--r--core/java/android/app/AppOpsManager.java166
-rw-r--r--core/java/android/app/ContextImpl.java4
-rw-r--r--core/java/android/content/ContentProvider.java105
-rw-r--r--core/java/android/content/ContentProviderClient.java24
-rw-r--r--core/java/android/content/ContentProviderNative.java67
-rw-r--r--core/java/android/content/ContentResolver.java29
-rw-r--r--core/java/android/content/IContentProvider.java27
-rw-r--r--core/java/android/provider/Settings.java18
-rw-r--r--core/java/com/android/internal/app/IAppOpsService.aidl6
-rw-r--r--core/res/AndroidManifest.xml7
-rw-r--r--core/res/res/values/strings.xml6
-rw-r--r--media/java/android/media/MediaInserter.java10
-rw-r--r--media/java/android/media/MediaScanner.java50
-rw-r--r--media/java/android/mtp/MtpDatabase.java43
-rw-r--r--media/java/android/mtp/MtpPropertyGroup.java16
-rw-r--r--services/java/com/android/server/AppOpsService.java366
-rw-r--r--services/java/com/android/server/LocationManagerService.java44
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java4
-rw-r--r--test-runner/src/android/test/mock/MockContentProvider.java31
-rw-r--r--test-runner/src/android/test/mock/MockIContentProvider.java27
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java34
24 files changed, 868 insertions, 248 deletions
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 4baea77..2ae2071 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -358,7 +358,7 @@ public class Content {
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.insert(mUri, mContentValues);
+ provider.insert(null, mUri, mContentValues);
}
}
@@ -372,7 +372,7 @@ public class Content {
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.delete(mUri, mWhere, null);
+ provider.delete(null, mUri, mWhere, null);
}
}
@@ -389,7 +389,7 @@ public class Content {
@Override
public void onExecute(IContentProvider provider) throws Exception {
- Cursor cursor = provider.query(mUri, mProjection, mWhere, null, mSortOrder, null);
+ Cursor cursor = provider.query(null, mUri, mProjection, mWhere, null, mSortOrder, null);
if (cursor == null) {
System.out.println("No result found.");
return;
@@ -451,7 +451,7 @@ public class Content {
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.update(mUri, mContentValues, mWhere, null);
+ provider.update(null, mUri, mContentValues, mWhere, null);
}
}
diff --git a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java
index 0c69f01..dce0a75 100644
--- a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java
+++ b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java
@@ -180,7 +180,7 @@ public final class SettingsCmd {
try {
Bundle arg = new Bundle();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- Bundle b = provider.call(callGetCommand, key, arg);
+ Bundle b = provider.call(null, callGetCommand, key, arg);
if (b != null) {
result = b.getPairValue();
}
@@ -205,7 +205,7 @@ public final class SettingsCmd {
Bundle arg = new Bundle();
arg.putString(Settings.NameValueTable.VALUE, value);
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- provider.call(callPutCommand, key, arg);
+ provider.call(null, callPutCommand, key, arg);
} catch (RemoteException e) {
System.err.println("Can't set key " + key + " in " + table + " for user " + userHandle);
}
diff --git a/core/java/android/app/AppOpsManager.aidl b/core/java/android/app/AppOpsManager.aidl
new file mode 100644
index 0000000..4b97a15
--- /dev/null
+++ b/core/java/android/app/AppOpsManager.aidl
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2013, 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 android.app;
+
+parcelable AppOpsManager.PackageOps;
+parcelable AppOpsManager.OpEntry;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 7210df4..4cea6a0 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -18,7 +18,12 @@ package android.app;
import com.android.internal.app.IAppOpsService;
+import java.util.ArrayList;
+import java.util.List;
+
import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
@@ -31,24 +36,177 @@ public class AppOpsManager {
public static final int MODE_IGNORED = 1;
public static final int MODE_ERRORED = 2;
- public static final int OP_LOCATION = 0;
- public static final int OP_GPS = 1;
- public static final int OP_VIBRATE = 2;
+ public static final int OP_COARSE_LOCATION = 0;
+ public static final int OP_FINE_LOCATION = 1;
+ public static final int OP_GPS = 2;
+ public static final int OP_VIBRATE = 3;
+ public static final int OP_READ_CONTACTS = 4;
+ public static final int OP_WRITE_CONTACTS = 5;
+ public static final int OP_READ_CALL_LOG = 6;
+ public static final int OP_WRITE_CALL_LOG = 7;
public static String opToString(int op) {
switch (op) {
- case OP_LOCATION: return "LOCATION";
+ case OP_COARSE_LOCATION: return "COARSE_LOCATION";
+ case OP_FINE_LOCATION: return "FINE_LOCATION";
case OP_GPS: return "GPS";
case OP_VIBRATE: return "VIBRATE";
+ case OP_READ_CONTACTS: return "READ_CONTACTS";
+ case OP_WRITE_CONTACTS: return "WRITE_CONTACTS";
+ case OP_READ_CALL_LOG: return "READ_CALL_LOG";
+ case OP_WRITE_CALL_LOG: return "WRITE_CALL_LOG";
default: return "Unknown(" + op + ")";
}
}
+ public static class PackageOps implements Parcelable {
+ private final String mPackageName;
+ private final int mUid;
+ private final List<OpEntry> mEntries;
+
+ public PackageOps(String packageName, int uid, List<OpEntry> entries) {
+ mPackageName = packageName;
+ mUid = uid;
+ mEntries = entries;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public int getUid() {
+ return mUid;
+ }
+
+ public List<OpEntry> getOps() {
+ return mEntries;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mPackageName);
+ dest.writeInt(mUid);
+ dest.writeInt(mEntries.size());
+ for (int i=0; i<mEntries.size(); i++) {
+ mEntries.get(i).writeToParcel(dest, flags);
+ }
+ }
+
+ PackageOps(Parcel source) {
+ mPackageName = source.readString();
+ mUid = source.readInt();
+ mEntries = new ArrayList<OpEntry>();
+ final int N = source.readInt();
+ for (int i=0; i<N; i++) {
+ mEntries.add(OpEntry.CREATOR.createFromParcel(source));
+ }
+ }
+
+ public static final Creator<PackageOps> CREATOR = new Creator<PackageOps>() {
+ @Override public PackageOps createFromParcel(Parcel source) {
+ return new PackageOps(source);
+ }
+
+ @Override public PackageOps[] newArray(int size) {
+ return new PackageOps[size];
+ }
+ };
+ }
+
+ public static class OpEntry implements Parcelable {
+ private final int mOp;
+ private final long mTime;
+ private final int mDuration;
+
+ public OpEntry(int op, long time, int duration) {
+ mOp = op;
+ mTime = time;
+ mDuration = duration;
+ }
+
+ public int getOp() {
+ return mOp;
+ }
+
+ public long getTime() {
+ return mTime;
+ }
+
+ public boolean isRunning() {
+ return mDuration == -1;
+ }
+
+ public int getDuration() {
+ return mDuration == -1 ? (int)(System.currentTimeMillis()-mTime) : mDuration;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mOp);
+ dest.writeLong(mTime);
+ dest.writeInt(mDuration);
+ }
+
+ OpEntry(Parcel source) {
+ mOp = source.readInt();
+ mTime = source.readLong();
+ mDuration = source.readInt();
+ }
+
+ public static final Creator<OpEntry> CREATOR = new Creator<OpEntry>() {
+ @Override public OpEntry createFromParcel(Parcel source) {
+ return new OpEntry(source);
+ }
+
+ @Override public OpEntry[] newArray(int size) {
+ return new OpEntry[size];
+ }
+ };
+ }
+
public AppOpsManager(Context context, IAppOpsService service) {
mContext = context;
mService = service;
}
+ public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
+ try {
+ return mService.getPackagesForOps(ops);
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
+
+ public int checkOp(int op, int uid, String packageName) {
+ try {
+ int mode = mService.checkOperation(op, uid, packageName);
+ if (mode == MODE_ERRORED) {
+ throw new SecurityException("Operation not allowed");
+ }
+ return mode;
+ } catch (RemoteException e) {
+ }
+ return MODE_IGNORED;
+ }
+
+ public int checkOpNoThrow(int op, int uid, String packageName) {
+ try {
+ return mService.checkOperation(op, uid, packageName);
+ } catch (RemoteException e) {
+ }
+ return MODE_IGNORED;
+ }
+
public int noteOp(int op, int uid, String packageName) {
try {
int mode = mService.noteOperation(op, uid, packageName);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 99ac0d6..1d394e8 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -623,7 +623,9 @@ class ContextImpl extends Context {
if (mPackageInfo != null) {
return mPackageInfo.getPackageName();
}
- throw new RuntimeException("Not supported in system context");
+ // No mPackageInfo means this is a Context for the system itself,
+ // and this here is its name.
+ return "android";
}
@Override
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index c1411b0..a6f7abc 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -18,6 +18,7 @@ package android.content;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import android.app.AppOpsManager;
import android.content.pm.PackageManager;
import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
@@ -172,6 +173,10 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
* @hide
*/
class Transport extends ContentProviderNative {
+ AppOpsManager mAppOpsManager = null;
+ int mReadOp = -1;
+ int mWriteOp = -1;
+
ContentProvider getContentProvider() {
return ContentProvider.this;
}
@@ -182,10 +187,11 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
}
@Override
- public Cursor query(Uri uri, String[] projection,
+ public Cursor query(String callingPkg, Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder,
ICancellationSignal cancellationSignal) {
- enforceReadPermission(uri);
+ // XXX need content provider to help return correct result.
+ enforceReadPermission(callingPkg, uri);
return ContentProvider.this.query(uri, projection, selection, selectionArgs, sortOrder,
CancellationSignal.fromTransport(cancellationSignal));
}
@@ -196,63 +202,75 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
}
@Override
- public Uri insert(Uri uri, ContentValues initialValues) {
- enforceWritePermission(uri);
+ public Uri insert(String callingPkg, Uri uri, ContentValues initialValues) {
+ // XXX need content provider to help return correct result.
+ enforceWritePermission(callingPkg, uri);
return ContentProvider.this.insert(uri, initialValues);
}
@Override
- public int bulkInsert(Uri uri, ContentValues[] initialValues) {
- enforceWritePermission(uri);
+ public int bulkInsert(String callingPkg, Uri uri, ContentValues[] initialValues) {
+ if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+ return 0;
+ }
return ContentProvider.this.bulkInsert(uri, initialValues);
}
@Override
- public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
+ public ContentProviderResult[] applyBatch(String callingPkg,
+ ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
for (ContentProviderOperation operation : operations) {
if (operation.isReadOperation()) {
- enforceReadPermission(operation.getUri());
+ if (enforceReadPermission(callingPkg, operation.getUri())
+ != AppOpsManager.MODE_ALLOWED) {
+ throw new OperationApplicationException("App op not allowed", 0);
+ }
}
if (operation.isWriteOperation()) {
- enforceWritePermission(operation.getUri());
+ if (enforceWritePermission(callingPkg, operation.getUri())
+ != AppOpsManager.MODE_ALLOWED) {
+ throw new OperationApplicationException("App op not allowed", 0);
+ }
}
}
return ContentProvider.this.applyBatch(operations);
}
@Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- enforceWritePermission(uri);
+ public int delete(String callingPkg, Uri uri, String selection, String[] selectionArgs) {
+ if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+ return 0;
+ }
return ContentProvider.this.delete(uri, selection, selectionArgs);
}
@Override
- public int update(Uri uri, ContentValues values, String selection,
+ public int update(String callingPkg, Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
- enforceWritePermission(uri);
+ if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+ return 0;
+ }
return ContentProvider.this.update(uri, values, selection, selectionArgs);
}
@Override
- public ParcelFileDescriptor openFile(Uri uri, String mode)
+ public ParcelFileDescriptor openFile(String callingPkg, Uri uri, String mode)
throws FileNotFoundException {
- if (mode != null && mode.startsWith("rw")) enforceWritePermission(uri);
- else enforceReadPermission(uri);
+ enforceFilePermission(callingPkg, uri, mode);
return ContentProvider.this.openFile(uri, mode);
}
@Override
- public AssetFileDescriptor openAssetFile(Uri uri, String mode)
+ public AssetFileDescriptor openAssetFile(String callingPkg, Uri uri, String mode)
throws FileNotFoundException {
- if (mode != null && mode.startsWith("rw")) enforceWritePermission(uri);
- else enforceReadPermission(uri);
+ enforceFilePermission(callingPkg, uri, mode);
return ContentProvider.this.openAssetFile(uri, mode);
}
@Override
- public Bundle call(String method, String arg, Bundle extras) {
+ public Bundle call(String callingPkg, String method, String arg, Bundle extras) {
return ContentProvider.this.call(method, arg, extras);
}
@@ -262,9 +280,9 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
}
@Override
- public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeType, Bundle opts)
- throws FileNotFoundException {
- enforceReadPermission(uri);
+ public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri uri, String mimeType,
+ Bundle opts) throws FileNotFoundException {
+ enforceFilePermission(callingPkg, uri, "r");
return ContentProvider.this.openTypedAssetFile(uri, mimeType, opts);
}
@@ -273,7 +291,28 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
return CancellationSignal.createTransport();
}
- private void enforceReadPermission(Uri uri) throws SecurityException {
+ private void enforceFilePermission(String callingPkg, Uri uri, String mode)
+ throws FileNotFoundException, SecurityException {
+ if (mode != null && mode.startsWith("rw")) {
+ if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+ throw new FileNotFoundException("App op not allowed");
+ }
+ } else {
+ if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+ throw new FileNotFoundException("App op not allowed");
+ }
+ }
+ }
+
+ private int enforceReadPermission(String callingPkg, Uri uri) throws SecurityException {
+ enforceReadPermissionInner(uri);
+ if (mAppOpsManager != null) {
+ return mAppOpsManager.noteOp(mReadOp, Binder.getCallingUid(), callingPkg);
+ }
+ return AppOpsManager.MODE_ALLOWED;
+ }
+
+ private void enforceReadPermissionInner(Uri uri) throws SecurityException {
final Context context = getContext();
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
@@ -334,7 +373,15 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
+ ", uid=" + uid + failReason);
}
- private void enforceWritePermission(Uri uri) throws SecurityException {
+ private int enforceWritePermission(String callingPkg, Uri uri) throws SecurityException {
+ enforceWritePermissionInner(uri);
+ if (mAppOpsManager != null) {
+ return mAppOpsManager.noteOp(mWriteOp, Binder.getCallingUid(), callingPkg);
+ }
+ return AppOpsManager.MODE_ALLOWED;
+ }
+
+ private void enforceWritePermissionInner(Uri uri) throws SecurityException {
final Context context = getContext();
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
@@ -471,6 +518,14 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
return mPathPermissions;
}
+ /** @hide */
+ public final void setAppOps(int readOp, int writeOp) {
+ mTransport.mAppOpsManager = (AppOpsManager)mContext.getSystemService(
+ Context.APP_OPS_SERVICE);
+ mTransport.mReadOp = readOp;
+ mTransport.mWriteOp = writeOp;
+ }
+
/**
* Implement this to initialize your content provider on startup.
* This method is called for all registered content providers on the
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 204f963..8dffac7 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -45,6 +45,7 @@ import java.util.ArrayList;
public class ContentProviderClient {
private final IContentProvider mContentProvider;
private final ContentResolver mContentResolver;
+ private final String mPackageName;
private final boolean mStable;
private boolean mReleased;
@@ -55,6 +56,7 @@ public class ContentProviderClient {
IContentProvider contentProvider, boolean stable) {
mContentProvider = contentProvider;
mContentResolver = contentResolver;
+ mPackageName = contentResolver.mPackageName;
mStable = stable;
}
@@ -81,8 +83,8 @@ public class ContentProviderClient {
cancellationSignal.setRemote(remoteCancellationSignal);
}
try {
- return mContentProvider.query(url, projection, selection, selectionArgs, sortOrder,
- remoteCancellationSignal);
+ return mContentProvider.query(mPackageName, url, projection, selection, selectionArgs,
+ sortOrder, remoteCancellationSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -119,7 +121,7 @@ public class ContentProviderClient {
public Uri insert(Uri url, ContentValues initialValues)
throws RemoteException {
try {
- return mContentProvider.insert(url, initialValues);
+ return mContentProvider.insert(mPackageName, url, initialValues);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -131,7 +133,7 @@ public class ContentProviderClient {
/** See {@link ContentProvider#bulkInsert ContentProvider.bulkInsert} */
public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException {
try {
- return mContentProvider.bulkInsert(url, initialValues);
+ return mContentProvider.bulkInsert(mPackageName, url, initialValues);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -144,7 +146,7 @@ public class ContentProviderClient {
public int delete(Uri url, String selection, String[] selectionArgs)
throws RemoteException {
try {
- return mContentProvider.delete(url, selection, selectionArgs);
+ return mContentProvider.delete(mPackageName, url, selection, selectionArgs);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -157,7 +159,7 @@ public class ContentProviderClient {
public int update(Uri url, ContentValues values, String selection,
String[] selectionArgs) throws RemoteException {
try {
- return mContentProvider.update(url, values, selection, selectionArgs);
+ return mContentProvider.update(mPackageName, url, values, selection, selectionArgs);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -176,7 +178,7 @@ public class ContentProviderClient {
public ParcelFileDescriptor openFile(Uri url, String mode)
throws RemoteException, FileNotFoundException {
try {
- return mContentProvider.openFile(url, mode);
+ return mContentProvider.openFile(mPackageName, url, mode);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -195,7 +197,7 @@ public class ContentProviderClient {
public AssetFileDescriptor openAssetFile(Uri url, String mode)
throws RemoteException, FileNotFoundException {
try {
- return mContentProvider.openAssetFile(url, mode);
+ return mContentProvider.openAssetFile(mPackageName, url, mode);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -209,7 +211,7 @@ public class ContentProviderClient {
String mimeType, Bundle opts)
throws RemoteException, FileNotFoundException {
try {
- return mContentProvider.openTypedAssetFile(uri, mimeType, opts);
+ return mContentProvider.openTypedAssetFile(mPackageName, uri, mimeType, opts);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -222,7 +224,7 @@ public class ContentProviderClient {
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException {
try {
- return mContentProvider.applyBatch(operations);
+ return mContentProvider.applyBatch(mPackageName, operations);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -235,7 +237,7 @@ public class ContentProviderClient {
public Bundle call(String method, String arg, Bundle extras)
throws RemoteException {
try {
- return mContentProvider.call(method, arg, extras);
+ return mContentProvider.call(mPackageName, method, arg, extras);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index 384ba57..6f822c1 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -81,6 +81,7 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
{
data.enforceInterface(IContentProvider.descriptor);
+ String callingPkg = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
// String[] projection
@@ -110,8 +111,8 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(
data.readStrongBinder());
- Cursor cursor = query(url, projection, selection, selectionArgs, sortOrder,
- cancellationSignal);
+ Cursor cursor = query(callingPkg, url, projection, selection, selectionArgs,
+ sortOrder, cancellationSignal);
if (cursor != null) {
try {
CursorToBulkCursorAdaptor adaptor = new CursorToBulkCursorAdaptor(
@@ -150,10 +151,11 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
case INSERT_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
+ String callingPkg = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
ContentValues values = ContentValues.CREATOR.createFromParcel(data);
- Uri out = insert(url, values);
+ Uri out = insert(callingPkg, url, values);
reply.writeNoException();
Uri.writeToParcel(reply, out);
return true;
@@ -162,10 +164,11 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
case BULK_INSERT_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
+ String callingPkg = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
ContentValues[] values = data.createTypedArray(ContentValues.CREATOR);
- int count = bulkInsert(url, values);
+ int count = bulkInsert(callingPkg, url, values);
reply.writeNoException();
reply.writeInt(count);
return true;
@@ -174,13 +177,14 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
case APPLY_BATCH_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
+ String callingPkg = data.readString();
final int numOperations = data.readInt();
final ArrayList<ContentProviderOperation> operations =
new ArrayList<ContentProviderOperation>(numOperations);
for (int i = 0; i < numOperations; i++) {
operations.add(i, ContentProviderOperation.CREATOR.createFromParcel(data));
}
- final ContentProviderResult[] results = applyBatch(operations);
+ final ContentProviderResult[] results = applyBatch(callingPkg, operations);
reply.writeNoException();
reply.writeTypedArray(results, 0);
return true;
@@ -189,11 +193,12 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
case DELETE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
+ String callingPkg = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
String selection = data.readString();
String[] selectionArgs = data.readStringArray();
- int count = delete(url, selection, selectionArgs);
+ int count = delete(callingPkg, url, selection, selectionArgs);
reply.writeNoException();
reply.writeInt(count);
@@ -203,12 +208,13 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
case UPDATE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
+ String callingPkg = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
ContentValues values = ContentValues.CREATOR.createFromParcel(data);
String selection = data.readString();
String[] selectionArgs = data.readStringArray();
- int count = update(url, values, selection, selectionArgs);
+ int count = update(callingPkg, url, values, selection, selectionArgs);
reply.writeNoException();
reply.writeInt(count);
@@ -218,11 +224,12 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
case OPEN_FILE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
+ String callingPkg = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
String mode = data.readString();
ParcelFileDescriptor fd;
- fd = openFile(url, mode);
+ fd = openFile(callingPkg, url, mode);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
@@ -237,11 +244,12 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
case OPEN_ASSET_FILE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
+ String callingPkg = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
String mode = data.readString();
AssetFileDescriptor fd;
- fd = openAssetFile(url, mode);
+ fd = openAssetFile(callingPkg, url, mode);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
@@ -257,11 +265,12 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
{
data.enforceInterface(IContentProvider.descriptor);
+ String callingPkg = data.readString();
String method = data.readString();
String stringArg = data.readString();
Bundle args = data.readBundle();
- Bundle responseBundle = call(method, stringArg, args);
+ Bundle responseBundle = call(callingPkg, method, stringArg, args);
reply.writeNoException();
reply.writeBundle(responseBundle);
@@ -283,12 +292,13 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
case OPEN_TYPED_ASSET_FILE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
+ String callingPkg = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
String mimeType = data.readString();
Bundle opts = data.readBundle();
AssetFileDescriptor fd;
- fd = openTypedAssetFile(url, mimeType, opts);
+ fd = openTypedAssetFile(callingPkg, url, mimeType, opts);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
@@ -337,7 +347,7 @@ final class ContentProviderProxy implements IContentProvider
return mRemote;
}
- public Cursor query(Uri url, String[] projection, String selection,
+ public Cursor query(String callingPkg, Uri url, String[] projection, String selection,
String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal)
throws RemoteException {
BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
@@ -346,6 +356,7 @@ final class ContentProviderProxy implements IContentProvider
try {
data.writeInterfaceToken(IContentProvider.descriptor);
+ data.writeString(callingPkg);
url.writeToParcel(data, 0);
int length = 0;
if (projection != null) {
@@ -413,13 +424,14 @@ final class ContentProviderProxy implements IContentProvider
}
}
- public Uri insert(Uri url, ContentValues values) throws RemoteException
+ public Uri insert(String callingPkg, Uri url, ContentValues values) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
+ data.writeString(callingPkg);
url.writeToParcel(data, 0);
values.writeToParcel(data, 0);
@@ -434,12 +446,13 @@ final class ContentProviderProxy implements IContentProvider
}
}
- public int bulkInsert(Uri url, ContentValues[] values) throws RemoteException {
+ public int bulkInsert(String callingPkg, Uri url, ContentValues[] values) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
+ data.writeString(callingPkg);
url.writeToParcel(data, 0);
data.writeTypedArray(values, 0);
@@ -454,12 +467,14 @@ final class ContentProviderProxy implements IContentProvider
}
}
- public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
- throws RemoteException, OperationApplicationException {
+ public ContentProviderResult[] applyBatch(String callingPkg,
+ ArrayList<ContentProviderOperation> operations)
+ throws RemoteException, OperationApplicationException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
+ data.writeString(callingPkg);
data.writeInt(operations.size());
for (ContentProviderOperation operation : operations) {
operation.writeToParcel(data, 0);
@@ -476,13 +491,14 @@ final class ContentProviderProxy implements IContentProvider
}
}
- public int delete(Uri url, String selection, String[] selectionArgs)
+ public int delete(String callingPkg, Uri url, String selection, String[] selectionArgs)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
+ data.writeString(callingPkg);
url.writeToParcel(data, 0);
data.writeString(selection);
data.writeStringArray(selectionArgs);
@@ -498,13 +514,14 @@ final class ContentProviderProxy implements IContentProvider
}
}
- public int update(Uri url, ContentValues values, String selection,
+ public int update(String callingPkg, Uri url, ContentValues values, String selection,
String[] selectionArgs) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
+ data.writeString(callingPkg);
url.writeToParcel(data, 0);
values.writeToParcel(data, 0);
data.writeString(selection);
@@ -521,13 +538,14 @@ final class ContentProviderProxy implements IContentProvider
}
}
- public ParcelFileDescriptor openFile(Uri url, String mode)
+ public ParcelFileDescriptor openFile(String callingPkg, Uri url, String mode)
throws RemoteException, FileNotFoundException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
+ data.writeString(callingPkg);
url.writeToParcel(data, 0);
data.writeString(mode);
@@ -543,13 +561,14 @@ final class ContentProviderProxy implements IContentProvider
}
}
- public AssetFileDescriptor openAssetFile(Uri url, String mode)
+ public AssetFileDescriptor openAssetFile(String callingPkg, Uri url, String mode)
throws RemoteException, FileNotFoundException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
+ data.writeString(callingPkg);
url.writeToParcel(data, 0);
data.writeString(mode);
@@ -566,13 +585,14 @@ final class ContentProviderProxy implements IContentProvider
}
}
- public Bundle call(String method, String request, Bundle args)
+ public Bundle call(String callingPkg, String method, String request, Bundle args)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
+ data.writeString(callingPkg);
data.writeString(method);
data.writeString(request);
data.writeBundle(args);
@@ -609,13 +629,14 @@ final class ContentProviderProxy implements IContentProvider
}
}
- public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts)
- throws RemoteException, FileNotFoundException {
+ public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri url, String mimeType,
+ Bundle opts) throws RemoteException, FileNotFoundException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
+ data.writeString(callingPkg);
url.writeToParcel(data, 0);
data.writeString(mimeType);
data.writeBundle(opts);
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 86bf8c2..51c9aa5 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -207,6 +207,7 @@ public abstract class ContentResolver {
public ContentResolver(Context context) {
mContext = context;
+ mPackageName = context.getPackageName();
}
/** @hide */
@@ -392,7 +393,7 @@ public abstract class ContentResolver {
cancellationSignal.setRemote(remoteCancellationSignal);
}
try {
- qCursor = unstableProvider.query(uri, projection,
+ qCursor = unstableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
} catch (DeadObjectException e) {
// The remote process has died... but we only hold an unstable
@@ -403,7 +404,7 @@ public abstract class ContentResolver {
if (stableProvider == null) {
return null;
}
- qCursor = stableProvider.query(uri, projection,
+ qCursor = stableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
}
if (qCursor == null) {
@@ -651,7 +652,7 @@ public abstract class ContentResolver {
try {
try {
- fd = unstableProvider.openAssetFile(uri, mode);
+ fd = unstableProvider.openAssetFile(mPackageName, uri, mode);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -665,7 +666,7 @@ public abstract class ContentResolver {
if (stableProvider == null) {
throw new FileNotFoundException("No content provider: " + uri);
}
- fd = stableProvider.openAssetFile(uri, mode);
+ fd = stableProvider.openAssetFile(mPackageName, uri, mode);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -743,7 +744,7 @@ public abstract class ContentResolver {
try {
try {
- fd = unstableProvider.openTypedAssetFile(uri, mimeType, opts);
+ fd = unstableProvider.openTypedAssetFile(mPackageName, uri, mimeType, opts);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -757,7 +758,7 @@ public abstract class ContentResolver {
if (stableProvider == null) {
throw new FileNotFoundException("No content provider: " + uri);
}
- fd = stableProvider.openTypedAssetFile(uri, mimeType, opts);
+ fd = stableProvider.openTypedAssetFile(mPackageName, uri, mimeType, opts);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -892,7 +893,7 @@ public abstract class ContentResolver {
}
try {
long startTime = SystemClock.uptimeMillis();
- Uri createdRow = provider.insert(url, values);
+ Uri createdRow = provider.insert(mPackageName, url, values);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
return createdRow;
@@ -953,7 +954,7 @@ public abstract class ContentResolver {
}
try {
long startTime = SystemClock.uptimeMillis();
- int rowsCreated = provider.bulkInsert(url, values);
+ int rowsCreated = provider.bulkInsert(mPackageName, url, values);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "bulkinsert", null /* where */);
return rowsCreated;
@@ -984,7 +985,7 @@ public abstract class ContentResolver {
}
try {
long startTime = SystemClock.uptimeMillis();
- int rowsDeleted = provider.delete(url, where, selectionArgs);
+ int rowsDeleted = provider.delete(mPackageName, url, where, selectionArgs);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "delete", where);
return rowsDeleted;
@@ -1018,7 +1019,7 @@ public abstract class ContentResolver {
}
try {
long startTime = SystemClock.uptimeMillis();
- int rowsUpdated = provider.update(uri, values, where, selectionArgs);
+ int rowsUpdated = provider.update(mPackageName, uri, values, where, selectionArgs);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, uri, "update", where);
return rowsUpdated;
@@ -1057,7 +1058,7 @@ public abstract class ContentResolver {
throw new IllegalArgumentException("Unknown URI " + uri);
}
try {
- return provider.call(method, arg, extras);
+ return provider.call(mPackageName, method, arg, extras);
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
@@ -1955,7 +1956,13 @@ public abstract class ContentResolver {
return sContentService;
}
+ /** @hide */
+ public String getPackageName() {
+ return mPackageName;
+ }
+
private static IContentService sContentService;
private final Context mContext;
+ final String mPackageName;
private static final String TAG = "ContentResolver";
}
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index eeba1e0..62b79f0 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -34,30 +34,33 @@ import java.util.ArrayList;
* @hide
*/
public interface IContentProvider extends IInterface {
- public Cursor query(Uri url, String[] projection, String selection,
+ public Cursor query(String callingPkg, Uri url, String[] projection, String selection,
String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal)
throws RemoteException;
public String getType(Uri url) throws RemoteException;
- public Uri insert(Uri url, ContentValues initialValues)
+ public Uri insert(String callingPkg, Uri url, ContentValues initialValues)
throws RemoteException;
- public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException;
- public int delete(Uri url, String selection, String[] selectionArgs)
+ public int bulkInsert(String callingPkg, Uri url, ContentValues[] initialValues)
throws RemoteException;
- public int update(Uri url, ContentValues values, String selection,
+ public int delete(String callingPkg, Uri url, String selection, String[] selectionArgs)
+ throws RemoteException;
+ public int update(String callingPkg, Uri url, ContentValues values, String selection,
String[] selectionArgs) throws RemoteException;
- public ParcelFileDescriptor openFile(Uri url, String mode)
+ public ParcelFileDescriptor openFile(String callingPkg, Uri url, String mode)
throws RemoteException, FileNotFoundException;
- public AssetFileDescriptor openAssetFile(Uri url, String mode)
+ public AssetFileDescriptor openAssetFile(String callingPkg, Uri url, String mode)
throws RemoteException, FileNotFoundException;
- public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
- throws RemoteException, OperationApplicationException;
- public Bundle call(String method, String arg, Bundle extras) throws RemoteException;
+ public ContentProviderResult[] applyBatch(String callingPkg,
+ ArrayList<ContentProviderOperation> operations)
+ throws RemoteException, OperationApplicationException;
+ public Bundle call(String callingPkg, String method, String arg, Bundle extras)
+ throws RemoteException;
public ICancellationSignal createCancellationSignal() throws RemoteException;
// Data interchange.
public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException;
- public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts)
- throws RemoteException, FileNotFoundException;
+ public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri url, String mimeType,
+ Bundle opts) throws RemoteException, FileNotFoundException;
/* IPC constants */
static final String descriptor = "android.content.IContentProvider";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 441214c..3d850cf 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -456,6 +456,18 @@ public final class Settings {
"android.settings.APPLICATION_DETAILS_SETTINGS";
/**
+ * @hide
+ * Activity Action: Show the "app ops" settings screen.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_APP_OPS_SETTINGS =
+ "android.settings.APP_OPS_SETTINGS";
+
+ /**
* Activity Action: Show settings for system update functionality.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
@@ -774,7 +786,7 @@ public final class Settings {
arg.putString(Settings.NameValueTable.VALUE, value);
arg.putInt(CALL_METHOD_USER_KEY, userHandle);
IContentProvider cp = lazyGetProvider(cr);
- cp.call(mCallSetCommand, name, arg);
+ cp.call(cr.getPackageName(), mCallSetCommand, name, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
return false;
@@ -821,7 +833,7 @@ public final class Settings {
args = new Bundle();
args.putInt(CALL_METHOD_USER_KEY, userHandle);
}
- Bundle b = cp.call(mCallGetCommand, name, args);
+ Bundle b = cp.call(cr.getPackageName(), mCallGetCommand, name, args);
if (b != null) {
String value = b.getPairValue();
// Don't update our cache for reads of other users' data
@@ -846,7 +858,7 @@ public final class Settings {
Cursor c = null;
try {
- c = cp.query(mUri, SELECT_VALUE, NAME_EQ_PLACEHOLDER,
+ c = cp.query(cr.getPackageName(), mUri, SELECT_VALUE, NAME_EQ_PLACEHOLDER,
new String[]{name}, null, null);
if (c == null) {
Log.w(TAG, "Can't get key " + name + " from " + mUri);
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index c934587..827dba6 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -16,10 +16,12 @@
package com.android.internal.app;
+import android.app.AppOpsManager;
+
interface IAppOpsService {
+ List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops);
+ int checkOperation(int code, int uid, String packageName);
int noteOperation(int code, int uid, String packageName);
int startOperation(int code, int uid, String packageName);
void finishOperation(int code, int uid, String packageName);
- int noteTimedOperation(int code, int uid, String packageName, int duration);
- void earlyFinishOperation(int code, int uid, String packageName);
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e357255..a69870b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1606,6 +1606,13 @@
android:description="@string/permdesc_updateBatteryStats"
android:protectionLevel="signature|system" />
+ <!-- @hide Allows an application to collect battery statistics -->
+ <permission android:name="android.permission.GET_APP_OPS_STATS"
+ android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+ android:label="@string/permlab_getAppOpsStats"
+ android:description="@string/permdesc_getAppOpsStats"
+ android:protectionLevel="signature|system|development" />
+
<!-- Allows an application to update application operation statistics. Not for
use by third party apps. @hide -->
<permission android:name="android.permission.UPDATE_APP_OPS_STATS"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6a93860..fa26089 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -843,6 +843,12 @@
collected battery statistics. Not for use by normal apps.</string>
<!-- [CHAR LIMIT=NONE] Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_getAppOpsStats">retrieve app ops statistics</string>
+ <!-- [CHAR LIMIT=NONE] Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_getAppOpsStats">Allows the app to retrieve
+ collected application operation statistics. Not for use by normal apps.</string>
+
+ <!-- [CHAR LIMIT=NONE] Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_updateAppOpsStats">modify app ops statistics</string>
<!-- [CHAR LIMIT=NONE] Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_updateAppOpsStats">Allows the app to modify
diff --git a/media/java/android/media/MediaInserter.java b/media/java/android/media/MediaInserter.java
index 7fcbffa..41b369d 100644
--- a/media/java/android/media/MediaInserter.java
+++ b/media/java/android/media/MediaInserter.java
@@ -37,11 +37,13 @@ public class MediaInserter {
private final HashMap<Uri, List<ContentValues>> mPriorityRowMap =
new HashMap<Uri, List<ContentValues>>();
- private IContentProvider mProvider;
- private int mBufferSizePerUri;
+ private final IContentProvider mProvider;
+ private final String mPackageName;
+ private final int mBufferSizePerUri;
- public MediaInserter(IContentProvider provider, int bufferSizePerUri) {
+ public MediaInserter(IContentProvider provider, String packageName, int bufferSizePerUri) {
mProvider = provider;
+ mPackageName = packageName;
mBufferSizePerUri = bufferSizePerUri;
}
@@ -88,7 +90,7 @@ public class MediaInserter {
if (!list.isEmpty()) {
ContentValues[] valuesArray = new ContentValues[list.size()];
valuesArray = list.toArray(valuesArray);
- mProvider.bulkInsert(tableUri, valuesArray);
+ mProvider.bulkInsert(mPackageName, tableUri, valuesArray);
list.clear();
}
}
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 7768a61..619e71c 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -303,6 +303,7 @@ public class MediaScanner
private int mNativeContext;
private Context mContext;
+ private String mPackageName;
private IContentProvider mMediaProvider;
private Uri mAudioUri;
private Uri mVideoUri;
@@ -388,6 +389,7 @@ public class MediaScanner
public MediaScanner(Context c) {
native_setup();
mContext = c;
+ mPackageName = c.getPackageName();
mBitmapOptions.inSampleSize = 1;
mBitmapOptions.inJustDecodeBounds = true;
@@ -961,7 +963,7 @@ public class MediaScanner
if (inserter != null) {
inserter.flushAll();
}
- result = mMediaProvider.insert(tableUri, values);
+ result = mMediaProvider.insert(mPackageName, tableUri, values);
} else if (entry.mFormat == MtpConstants.FORMAT_ASSOCIATION) {
inserter.insertwithPriority(tableUri, values);
} else {
@@ -993,7 +995,7 @@ public class MediaScanner
}
values.put(FileColumns.MEDIA_TYPE, mediaType);
}
- mMediaProvider.update(result, values, null, null);
+ mMediaProvider.update(mPackageName, result, values, null, null);
}
if(needToSetSettings) {
@@ -1082,7 +1084,8 @@ public class MediaScanner
// filesystem is mounted and unmounted while the scanner is running).
Uri.Builder builder = mFilesUri.buildUpon();
builder.appendQueryParameter(MediaStore.PARAM_DELETE_DATA, "false");
- MediaBulkDeleter deleter = new MediaBulkDeleter(mMediaProvider, builder.build());
+ MediaBulkDeleter deleter = new MediaBulkDeleter(mMediaProvider, mPackageName,
+ builder.build());
// Build the list of files from the content provider
try {
@@ -1101,7 +1104,7 @@ public class MediaScanner
c.close();
c = null;
}
- c = mMediaProvider.query(limitUri, FILES_PRESCAN_PROJECTION,
+ c = mMediaProvider.query(mPackageName, limitUri, FILES_PRESCAN_PROJECTION,
where, selectionArgs, MediaStore.Files.FileColumns._ID, null);
if (c == null) {
break;
@@ -1142,7 +1145,8 @@ public class MediaScanner
if (path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
deleter.flush();
String parent = new File(path).getParent();
- mMediaProvider.call(MediaStore.UNHIDE_CALL, parent, null);
+ mMediaProvider.call(mPackageName, MediaStore.UNHIDE_CALL,
+ parent, null);
}
}
}
@@ -1160,7 +1164,7 @@ public class MediaScanner
// compute original size of images
mOriginalCount = 0;
- c = mMediaProvider.query(mImagesUri, ID_PROJECTION, null, null, null, null);
+ c = mMediaProvider.query(mPackageName, mImagesUri, ID_PROJECTION, null, null, null, null);
if (c != null) {
mOriginalCount = c.getCount();
c.close();
@@ -1191,6 +1195,7 @@ public class MediaScanner
try {
Cursor c = mMediaProvider.query(
+ mPackageName,
mThumbsUri,
new String [] { "_data" },
null,
@@ -1225,11 +1230,13 @@ public class MediaScanner
static class MediaBulkDeleter {
StringBuilder whereClause = new StringBuilder();
ArrayList<String> whereArgs = new ArrayList<String>(100);
- IContentProvider mProvider;
- Uri mBaseUri;
+ final IContentProvider mProvider;
+ final String mPackageName;
+ final Uri mBaseUri;
- public MediaBulkDeleter(IContentProvider provider, Uri baseUri) {
+ public MediaBulkDeleter(IContentProvider provider, String packageName, Uri baseUri) {
mProvider = provider;
+ mPackageName = packageName;
mBaseUri = baseUri;
}
@@ -1248,7 +1255,8 @@ public class MediaScanner
if (size > 0) {
String [] foo = new String [size];
foo = whereArgs.toArray(foo);
- int numrows = mProvider.delete(mBaseUri, MediaStore.MediaColumns._ID + " IN (" +
+ int numrows = mProvider.delete(mPackageName, mBaseUri,
+ MediaStore.MediaColumns._ID + " IN (" +
whereClause.toString() + ")", foo);
//Log.i("@@@@@@@@@", "rows deleted: " + numrows);
whereClause.setLength(0);
@@ -1301,7 +1309,7 @@ public class MediaScanner
if (ENABLE_BULK_INSERTS) {
// create MediaInserter for bulk inserts
- mMediaInserter = new MediaInserter(mMediaProvider, 500);
+ mMediaInserter = new MediaInserter(mMediaProvider, mPackageName, 500);
}
for (int i = 0; i < directories.length; i++) {
@@ -1433,8 +1441,8 @@ public class MediaScanner
values.put(Files.FileColumns.DATE_MODIFIED, lastModifiedSeconds);
try {
String[] whereArgs = new String[] { Integer.toString(objectHandle) };
- mMediaProvider.update(Files.getMtpObjectsUri(volumeName), values, "_id=?",
- whereArgs);
+ mMediaProvider.update(mPackageName, Files.getMtpObjectsUri(volumeName), values,
+ "_id=?", whereArgs);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in scanMtpFile", e);
}
@@ -1450,8 +1458,8 @@ public class MediaScanner
FileEntry entry = makeEntryFor(path);
if (entry != null) {
- fileList = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
- null, null, null, null);
+ fileList = mMediaProvider.query(mPackageName, mFilesUri,
+ FILES_PRESCAN_PROJECTION, null, null, null, null);
processPlayList(entry, fileList);
}
} else {
@@ -1480,7 +1488,7 @@ public class MediaScanner
try {
where = Files.FileColumns.DATA + "=?";
selectionArgs = new String[] { path };
- c = mMediaProvider.query(mFilesUriNoNotify, FILES_PRESCAN_PROJECTION,
+ c = mMediaProvider.query(mPackageName, mFilesUriNoNotify, FILES_PRESCAN_PROJECTION,
where, selectionArgs, null, null);
if (c.moveToFirst()) {
long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
@@ -1592,7 +1600,7 @@ public class MediaScanner
values.clear();
values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, Integer.valueOf(index));
values.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, Long.valueOf(entry.bestmatchid));
- mMediaProvider.insert(playlistUri, values);
+ mMediaProvider.insert(mPackageName, playlistUri, values);
index++;
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in MediaScanner.processCachedPlaylist()", e);
@@ -1757,16 +1765,16 @@ public class MediaScanner
if (rowId == 0) {
values.put(MediaStore.Audio.Playlists.DATA, path);
- uri = mMediaProvider.insert(mPlaylistsUri, values);
+ uri = mMediaProvider.insert(mPackageName, mPlaylistsUri, values);
rowId = ContentUris.parseId(uri);
membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY);
} else {
uri = ContentUris.withAppendedId(mPlaylistsUri, rowId);
- mMediaProvider.update(uri, values, null, null);
+ mMediaProvider.update(mPackageName, uri, values, null, null);
// delete members of existing playlist
membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY);
- mMediaProvider.delete(membersUri, null, null);
+ mMediaProvider.delete(mPackageName, membersUri, null, null);
}
String playListDirectory = path.substring(0, lastSlash + 1);
@@ -1788,7 +1796,7 @@ public class MediaScanner
try {
// use the files uri and projection because we need the format column,
// but restrict the query to just audio files
- fileList = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
+ fileList = mMediaProvider.query(mPackageName, mFilesUri, FILES_PRESCAN_PROJECTION,
"media_type=2", null, null, null);
while (iterator.hasNext()) {
FileEntry entry = iterator.next();
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 487585e..ea12803 100644
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -48,6 +48,7 @@ public class MtpDatabase {
private static final String TAG = "MtpDatabase";
private final Context mContext;
+ private final String mPackageName;
private final IContentProvider mMediaProvider;
private final String mVolumeName;
private final Uri mObjectsUri;
@@ -123,6 +124,7 @@ public class MtpDatabase {
native_setup();
mContext = context;
+ mPackageName = context.getPackageName();
mMediaProvider = context.getContentResolver().acquireProvider("media");
mVolumeName = volumeName;
mMediaStoragePath = storagePath;
@@ -263,7 +265,7 @@ public class MtpDatabase {
if (path != null) {
Cursor c = null;
try {
- c = mMediaProvider.query(mObjectsUri, ID_PROJECTION, PATH_WHERE,
+ c = mMediaProvider.query(mPackageName, mObjectsUri, ID_PROJECTION, PATH_WHERE,
new String[] { path }, null, null);
if (c != null && c.getCount() > 0) {
Log.w(TAG, "file already exists in beginSendObject: " + path);
@@ -288,7 +290,7 @@ public class MtpDatabase {
values.put(Files.FileColumns.DATE_MODIFIED, modified);
try {
- Uri uri = mMediaProvider.insert(mObjectsUri, values);
+ Uri uri = mMediaProvider.insert(mPackageName, mObjectsUri, values);
if (uri != null) {
return Integer.parseInt(uri.getPathSegments().get(2));
} else {
@@ -323,7 +325,8 @@ public class MtpDatabase {
values.put(Files.FileColumns.DATE_MODIFIED, System.currentTimeMillis() / 1000);
values.put(MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, handle);
try {
- Uri uri = mMediaProvider.insert(Audio.Playlists.EXTERNAL_CONTENT_URI, values);
+ Uri uri = mMediaProvider.insert(mPackageName,
+ Audio.Playlists.EXTERNAL_CONTENT_URI, values);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in endSendObject", e);
}
@@ -431,7 +434,8 @@ public class MtpDatabase {
}
}
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION, where, whereArgs, null, null);
+ return mMediaProvider.query(mPackageName, mObjectsUri, ID_PROJECTION, where,
+ whereArgs, null, null);
}
private int[] getObjectList(int storageID, int format, int parent) {
@@ -676,14 +680,16 @@ public class MtpDatabase {
propertyGroup = mPropertyGroupsByFormat.get(format);
if (propertyGroup == null) {
int[] propertyList = getSupportedObjectProperties(format);
- propertyGroup = new MtpPropertyGroup(this, mMediaProvider, mVolumeName, propertyList);
+ propertyGroup = new MtpPropertyGroup(this, mMediaProvider, mPackageName,
+ mVolumeName, propertyList);
mPropertyGroupsByFormat.put(new Integer(format), propertyGroup);
}
} else {
propertyGroup = mPropertyGroupsByProperty.get(property);
if (propertyGroup == null) {
int[] propertyList = new int[] { (int)property };
- propertyGroup = new MtpPropertyGroup(this, mMediaProvider, mVolumeName, propertyList);
+ propertyGroup = new MtpPropertyGroup(this, mMediaProvider, mPackageName,
+ mVolumeName, propertyList);
mPropertyGroupsByProperty.put(new Integer((int)property), propertyGroup);
}
}
@@ -698,7 +704,8 @@ public class MtpDatabase {
String path = null;
String[] whereArgs = new String[] { Integer.toString(handle) };
try {
- c = mMediaProvider.query(mObjectsUri, PATH_PROJECTION, ID_WHERE, whereArgs, null, null);
+ c = mMediaProvider.query(mPackageName, mObjectsUri, PATH_PROJECTION, ID_WHERE,
+ whereArgs, null, null);
if (c != null && c.moveToNext()) {
path = c.getString(1);
}
@@ -740,7 +747,7 @@ public class MtpDatabase {
try {
// note - we are relying on a special case in MediaProvider.update() to update
// the paths for all children in the case where this is a directory.
- updated = mMediaProvider.update(mObjectsUri, values, ID_WHERE, whereArgs);
+ updated = mMediaProvider.update(mPackageName, mObjectsUri, values, ID_WHERE, whereArgs);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in mMediaProvider.update", e);
}
@@ -757,7 +764,7 @@ public class MtpDatabase {
if (oldFile.getName().startsWith(".") && !newPath.startsWith(".")) {
// directory was unhidden
try {
- mMediaProvider.call(MediaStore.UNHIDE_CALL, newPath, null);
+ mMediaProvider.call(mPackageName, MediaStore.UNHIDE_CALL, newPath, null);
} catch (RemoteException e) {
Log.e(TAG, "failed to unhide/rescan for " + newPath);
}
@@ -767,7 +774,7 @@ public class MtpDatabase {
if (oldFile.getName().toLowerCase(Locale.US).equals(".nomedia")
&& !newPath.toLowerCase(Locale.US).equals(".nomedia")) {
try {
- mMediaProvider.call(MediaStore.UNHIDE_CALL, oldFile.getParent(), null);
+ mMediaProvider.call(mPackageName, MediaStore.UNHIDE_CALL, oldFile.getParent(), null);
} catch (RemoteException e) {
Log.e(TAG, "failed to unhide/rescan for " + newPath);
}
@@ -836,7 +843,7 @@ public class MtpDatabase {
char[] outName, long[] outModified) {
Cursor c = null;
try {
- c = mMediaProvider.query(mObjectsUri, OBJECT_INFO_PROJECTION,
+ c = mMediaProvider.query(mPackageName, mObjectsUri, OBJECT_INFO_PROJECTION,
ID_WHERE, new String[] { Integer.toString(handle) }, null, null);
if (c != null && c.moveToNext()) {
outStorageFormatParent[0] = c.getInt(1);
@@ -878,7 +885,7 @@ public class MtpDatabase {
}
Cursor c = null;
try {
- c = mMediaProvider.query(mObjectsUri, PATH_FORMAT_PROJECTION,
+ c = mMediaProvider.query(mPackageName, mObjectsUri, PATH_FORMAT_PROJECTION,
ID_WHERE, new String[] { Integer.toString(handle) }, null, null);
if (c != null && c.moveToNext()) {
String path = c.getString(1);
@@ -909,7 +916,7 @@ public class MtpDatabase {
Cursor c = null;
try {
- c = mMediaProvider.query(mObjectsUri, PATH_FORMAT_PROJECTION,
+ c = mMediaProvider.query(mPackageName, mObjectsUri, PATH_FORMAT_PROJECTION,
ID_WHERE, new String[] { Integer.toString(handle) }, null, null);
if (c != null && c.moveToNext()) {
// don't convert to media path here, since we will be matching
@@ -932,7 +939,7 @@ public class MtpDatabase {
if (format == MtpConstants.FORMAT_ASSOCIATION) {
// recursive case - delete all children first
Uri uri = Files.getMtpObjectsUri(mVolumeName);
- int count = mMediaProvider.delete(uri,
+ int count = mMediaProvider.delete(mPackageName, uri,
// the 'like' makes it use the index, the 'lower()' makes it correct
// when the path contains sqlite wildcard characters
"_data LIKE ?1 AND lower(substr(_data,1,?2))=lower(?3)",
@@ -940,12 +947,12 @@ public class MtpDatabase {
}
Uri uri = Files.getMtpObjectsUri(mVolumeName, handle);
- if (mMediaProvider.delete(uri, null, null) > 0) {
+ if (mMediaProvider.delete(mPackageName, uri, null, null) > 0) {
if (format != MtpConstants.FORMAT_ASSOCIATION
&& path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
try {
String parentPath = path.substring(0, path.lastIndexOf("/"));
- mMediaProvider.call(MediaStore.UNHIDE_CALL, parentPath, null);
+ mMediaProvider.call(mPackageName, MediaStore.UNHIDE_CALL, parentPath, null);
} catch (RemoteException e) {
Log.e(TAG, "failed to unhide/rescan for " + path);
}
@@ -968,7 +975,7 @@ public class MtpDatabase {
Uri uri = Files.getMtpReferencesUri(mVolumeName, handle);
Cursor c = null;
try {
- c = mMediaProvider.query(uri, ID_PROJECTION, null, null, null, null);
+ c = mMediaProvider.query(mPackageName, uri, ID_PROJECTION, null, null, null, null);
if (c == null) {
return null;
}
@@ -1002,7 +1009,7 @@ public class MtpDatabase {
valuesList[i] = values;
}
try {
- if (mMediaProvider.bulkInsert(uri, valuesList) > 0) {
+ if (mMediaProvider.bulkInsert(mPackageName, uri, valuesList) > 0) {
return MtpConstants.RESPONSE_OK;
}
} catch (RemoteException e) {
diff --git a/media/java/android/mtp/MtpPropertyGroup.java b/media/java/android/mtp/MtpPropertyGroup.java
index dab5454..48da40f 100644
--- a/media/java/android/mtp/MtpPropertyGroup.java
+++ b/media/java/android/mtp/MtpPropertyGroup.java
@@ -50,6 +50,7 @@ class MtpPropertyGroup {
private final MtpDatabase mDatabase;
private final IContentProvider mProvider;
+ private final String mPackageName;
private final String mVolumeName;
private final Uri mUri;
@@ -65,10 +66,11 @@ class MtpPropertyGroup {
private static final String PARENT_WHERE = Files.FileColumns.PARENT + "=?";
private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND " + FORMAT_WHERE;
// constructs a property group for a list of properties
- public MtpPropertyGroup(MtpDatabase database, IContentProvider provider, String volume,
- int[] properties) {
+ public MtpPropertyGroup(MtpDatabase database, IContentProvider provider, String packageName,
+ String volume, int[] properties) {
mDatabase = database;
mProvider = provider;
+ mPackageName = packageName;
mVolumeName = volume;
mUri = Files.getMtpObjectsUri(volume);
@@ -189,7 +191,7 @@ class MtpPropertyGroup {
Cursor c = null;
try {
// for now we are only reading properties from the "objects" table
- c = mProvider.query(mUri,
+ c = mProvider.query(mPackageName, mUri,
new String [] { Files.FileColumns._ID, column },
ID_WHERE, new String[] { Integer.toString(id) }, null, null);
if (c != null && c.moveToNext()) {
@@ -209,7 +211,7 @@ class MtpPropertyGroup {
private String queryAudio(int id, String column) {
Cursor c = null;
try {
- c = mProvider.query(Audio.Media.getContentUri(mVolumeName),
+ c = mProvider.query(mPackageName, Audio.Media.getContentUri(mVolumeName),
new String [] { Files.FileColumns._ID, column },
ID_WHERE, new String[] { Integer.toString(id) }, null, null);
if (c != null && c.moveToNext()) {
@@ -230,7 +232,7 @@ class MtpPropertyGroup {
Cursor c = null;
try {
Uri uri = Audio.Genres.getContentUriForAudioId(mVolumeName, id);
- c = mProvider.query(uri,
+ c = mProvider.query(mPackageName, uri,
new String [] { Files.FileColumns._ID, Audio.GenresColumns.NAME },
null, null, null, null);
if (c != null && c.moveToNext()) {
@@ -252,7 +254,7 @@ class MtpPropertyGroup {
Cursor c = null;
try {
// for now we are only reading properties from the "objects" table
- c = mProvider.query(mUri,
+ c = mProvider.query(mPackageName, mUri,
new String [] { Files.FileColumns._ID, column },
ID_WHERE, new String[] { Integer.toString(id) }, null, null);
if (c != null && c.moveToNext()) {
@@ -323,7 +325,7 @@ class MtpPropertyGroup {
try {
// don't query if not necessary
if (depth > 0 || handle == 0xFFFFFFFF || mColumns.length > 1) {
- c = mProvider.query(mUri, mColumns, where, whereArgs, null, null);
+ c = mProvider.query(mPackageName, mUri, mColumns, where, whereArgs, null, null);
if (c == null) {
return new MtpPropertyList(0, MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
}
diff --git a/services/java/com/android/server/AppOpsService.java b/services/java/com/android/server/AppOpsService.java
index 539194c..aff994c 100644
--- a/services/java/com/android/server/AppOpsService.java
+++ b/services/java/com/android/server/AppOpsService.java
@@ -18,15 +18,22 @@ package com.android.server;
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.AsyncTask;
import android.os.Binder;
-import android.os.Environment;
+import android.os.Handler;
import android.os.Process;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -34,23 +41,53 @@ import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
+import android.util.Xml;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
public class AppOpsService extends IAppOpsService.Stub {
static final String TAG = "AppOps";
+ static final boolean DEBUG = false;
+
+ // Write at most every 30 minutes.
+ static final long WRITE_DELAY = DEBUG ? 1000 : 30*60*1000;
Context mContext;
final AtomicFile mFile;
+ final Handler mHandler;
+
+ boolean mWriteScheduled;
+ final Runnable mWriteRunner = new Runnable() {
+ public void run() {
+ synchronized (AppOpsService.this) {
+ mWriteScheduled = false;
+ AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
+ @Override protected Void doInBackground(Void... params) {
+ writeState();
+ return null;
+ }
+ };
+ task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
+ }
+ }
+ };
final SparseArray<HashMap<String, Ops>> mUidOps
= new SparseArray<HashMap<String, Ops>>();
final static class Ops extends SparseArray<Op> {
public final String packageName;
+ public final int uid;
- public Ops(String _packageName) {
+ public Ops(String _packageName, int _uid) {
packageName = _packageName;
+ uid = _uid;
}
}
@@ -58,14 +95,17 @@ public class AppOpsService extends IAppOpsService.Stub {
public final int op;
public int duration;
public long time;
+ public int nesting;
public Op(int _op) {
op = _op;
}
}
- public AppOpsService() {
- mFile = new AtomicFile(new File(Environment.getSecureDataDirectory(), "appops.xml"));
+ public AppOpsService(File storagePath) {
+ mFile = new AtomicFile(storagePath);
+ mHandler = new Handler();
+ readState();
}
public void publish(Context context) {
@@ -75,94 +115,126 @@ public class AppOpsService extends IAppOpsService.Stub {
public void shutdown() {
Slog.w(TAG, "Writing app ops before shutdown...");
+ boolean doWrite = false;
+ synchronized (this) {
+ if (mWriteScheduled) {
+ mWriteScheduled = false;
+ doWrite = true;
+ }
+ }
+ if (doWrite) {
+ writeState();
+ }
}
@Override
- public int noteOperation(int code, int uid, String packageName) {
- uid = handleIncomingUid(uid);
+ public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
+ mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid(), null);
+ ArrayList<AppOpsManager.PackageOps> res = null;
synchronized (this) {
- Op op = getOpLocked(code, uid, packageName);
- if (op == null) {
- return AppOpsManager.MODE_IGNORED;
- }
- if (op.duration == -1) {
- Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
- + " code " + code + " time=" + op.time + " duration=" + op.duration);
+ for (int i=0; i<mUidOps.size(); i++) {
+ HashMap<String, Ops> packages = mUidOps.valueAt(i);
+ for (Ops pkgOps : packages.values()) {
+ ArrayList<AppOpsManager.OpEntry> resOps = null;
+ if (ops == null) {
+ resOps = new ArrayList<AppOpsManager.OpEntry>();
+ for (int j=0; j<pkgOps.size(); j++) {
+ Op curOp = pkgOps.valueAt(j);
+ resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.time,
+ curOp.duration));
+ }
+ } else {
+ for (int j=0; j<ops.length; j++) {
+ Op curOp = pkgOps.get(ops[j]);
+ if (curOp != null) {
+ if (resOps == null) {
+ resOps = new ArrayList<AppOpsManager.OpEntry>();
+ }
+ resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.time,
+ curOp.duration));
+ }
+ }
+ }
+ if (resOps != null) {
+ if (res == null) {
+ res = new ArrayList<AppOpsManager.PackageOps>();
+ }
+ AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
+ pkgOps.packageName, pkgOps.uid, resOps);
+ res.add(resPackage);
+ }
+ }
}
- op.time = System.currentTimeMillis();
- op.duration = 0;
}
- return AppOpsManager.MODE_ALLOWED;
+ return res;
}
@Override
- public int startOperation(int code, int uid, String packageName) {
+ public int checkOperation(int code, int uid, String packageName) {
uid = handleIncomingUid(uid);
synchronized (this) {
- Op op = getOpLocked(code, uid, packageName);
+ Op op = getOpLocked(code, uid, packageName, false);
if (op == null) {
- return AppOpsManager.MODE_IGNORED;
- }
- if (op.duration == -1) {
- Slog.w(TAG, "Starting op not finished: uid " + uid + " pkg " + packageName
- + " code " + code + " time=" + op.time + " duration=" + op.duration);
+ return AppOpsManager.MODE_ALLOWED;
}
- op.time = System.currentTimeMillis();
- op.duration = -1;
}
return AppOpsManager.MODE_ALLOWED;
}
@Override
- public void finishOperation(int code, int uid, String packageName) {
+ public int noteOperation(int code, int uid, String packageName) {
uid = handleIncomingUid(uid);
synchronized (this) {
- Op op = getOpLocked(code, uid, packageName);
+ Op op = getOpLocked(code, uid, packageName, true);
if (op == null) {
- return;
+ return AppOpsManager.MODE_IGNORED;
}
- if (op.duration != -1) {
- Slog.w(TAG, "Ignoring finishing op not started: uid " + uid + " pkg " + packageName
+ if (op.duration == -1) {
+ Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
+ " code " + code + " time=" + op.time + " duration=" + op.duration);
- return;
}
- op.duration = (int)(System.currentTimeMillis() - op.time);
+ op.time = System.currentTimeMillis();
+ op.duration = 0;
}
+ return AppOpsManager.MODE_ALLOWED;
}
@Override
- public int noteTimedOperation(int code, int uid, String packageName, int duration) {
+ public int startOperation(int code, int uid, String packageName) {
uid = handleIncomingUid(uid);
synchronized (this) {
- Op op = getOpLocked(code, uid, packageName);
+ Op op = getOpLocked(code, uid, packageName, true);
if (op == null) {
return AppOpsManager.MODE_IGNORED;
}
- if (op.duration == -1) {
- Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
- + " code " + code + " time=" + op.time + " duration=" + op.duration);
+ if (op.nesting == 0) {
+ op.time = System.currentTimeMillis();
+ op.duration = -1;
}
- op.time = System.currentTimeMillis();
- op.duration = duration;
+ op.nesting++;
}
return AppOpsManager.MODE_ALLOWED;
}
@Override
- public void earlyFinishOperation(int code, int uid, String packageName) {
+ public void finishOperation(int code, int uid, String packageName) {
uid = handleIncomingUid(uid);
synchronized (this) {
- Op op = getOpLocked(code, uid, packageName);
+ Op op = getOpLocked(code, uid, packageName, true);
if (op == null) {
return;
}
- if (op.duration != -1) {
- Slog.w(TAG, "Noting timed op not finished: uid " + uid + " pkg " + packageName
- + " code " + code + " time=" + op.time + " duration=" + op.duration);
- }
- int newDuration = (int)(System.currentTimeMillis() - op.time);
- if (newDuration < op.duration) {
- op.duration = newDuration;
+ if (op.nesting <= 1) {
+ if (op.nesting == 1) {
+ op.duration = (int)(System.currentTimeMillis() - op.time);
+ } else {
+ Slog.w(TAG, "Finishing op nesting under-run: uid " + uid + " pkg " + packageName
+ + " code " + code + " time=" + op.time + " duration=" + op.duration
+ + " nesting=" + op.nesting);
+ }
+ } else {
+ op.nesting--;
}
}
}
@@ -179,14 +251,20 @@ public class AppOpsService extends IAppOpsService.Stub {
return uid;
}
- private Op getOpLocked(int code, int uid, String packageName) {
+ private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
HashMap<String, Ops> pkgOps = mUidOps.get(uid);
if (pkgOps == null) {
+ if (!edit) {
+ return null;
+ }
pkgOps = new HashMap<String, Ops>();
mUidOps.put(uid, pkgOps);
}
Ops ops = pkgOps.get(packageName);
if (ops == null) {
+ if (!edit) {
+ return null;
+ }
// This is the first time we have seen this package name under this uid,
// so let's make sure it is valid.
final long ident = Binder.clearCallingIdentity();
@@ -207,17 +285,205 @@ public class AppOpsService extends IAppOpsService.Stub {
} finally {
Binder.restoreCallingIdentity(ident);
}
- ops = new Ops(packageName);
+ ops = new Ops(packageName, uid);
pkgOps.put(packageName, ops);
}
Op op = ops.get(code);
if (op == null) {
+ if (!edit) {
+ return null;
+ }
op = new Op(code);
ops.put(code, op);
}
+ if (edit && !mWriteScheduled) {
+ mWriteScheduled = true;
+ mHandler.postDelayed(mWriteRunner, WRITE_DELAY);
+ }
return op;
}
+ void readState() {
+ synchronized (mFile) {
+ synchronized (this) {
+ FileInputStream stream;
+ try {
+ stream = mFile.openRead();
+ } catch (FileNotFoundException e) {
+ Slog.i(TAG, "No existing app ops " + mFile.getBaseFile() + "; starting empty");
+ return;
+ }
+ boolean success = false;
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, null);
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ ;
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ throw new IllegalStateException("no start tag found");
+ }
+
+ int outerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals("pkg")) {
+ readPackage(parser);
+ } else {
+ Slog.w(TAG, "Unknown element under <app-ops>: "
+ + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ success = true;
+ } catch (IllegalStateException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } catch (NullPointerException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } catch (XmlPullParserException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } catch (IndexOutOfBoundsException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } finally {
+ if (!success) {
+ mUidOps.clear();
+ }
+ try {
+ stream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+ }
+
+ void readPackage(XmlPullParser parser) throws NumberFormatException,
+ XmlPullParserException, IOException {
+ String pkgName = parser.getAttributeValue(null, "n");
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals("uid")) {
+ readUid(parser, pkgName);
+ } else {
+ Slog.w(TAG, "Unknown element under <pkg>: "
+ + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ }
+
+ void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException,
+ XmlPullParserException, IOException {
+ int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals("op")) {
+ Op op = new Op(Integer.parseInt(parser.getAttributeValue(null, "n")));
+ op.time = Long.parseLong(parser.getAttributeValue(null, "t"));
+ op.duration = Integer.parseInt(parser.getAttributeValue(null, "d"));
+ HashMap<String, Ops> pkgOps = mUidOps.get(uid);
+ if (pkgOps == null) {
+ pkgOps = new HashMap<String, Ops>();
+ mUidOps.put(uid, pkgOps);
+ }
+ Ops ops = pkgOps.get(pkgName);
+ if (ops == null) {
+ ops = new Ops(pkgName, uid);
+ pkgOps.put(pkgName, ops);
+ }
+ ops.put(op.op, op);
+ } else {
+ Slog.w(TAG, "Unknown element under <pkg>: "
+ + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ }
+
+ void writeState() {
+ synchronized (mFile) {
+ List<AppOpsManager.PackageOps> allOps = getPackagesForOps(null);
+
+ FileOutputStream stream;
+ try {
+ stream = mFile.startWrite();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to write state: " + e);
+ return;
+ }
+
+ try {
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(stream, "utf-8");
+ out.startDocument(null, true);
+ out.startTag(null, "app-ops");
+
+ if (allOps != null) {
+ String lastPkg = null;
+ for (int i=0; i<allOps.size(); i++) {
+ AppOpsManager.PackageOps pkg = allOps.get(i);
+ if (!pkg.getPackageName().equals(lastPkg)) {
+ if (lastPkg != null) {
+ out.endTag(null, "pkg");
+ }
+ lastPkg = pkg.getPackageName();
+ out.startTag(null, "pkg");
+ out.attribute(null, "n", lastPkg);
+ }
+ out.startTag(null, "uid");
+ out.attribute(null, "n", Integer.toString(pkg.getUid()));
+ List<AppOpsManager.OpEntry> ops = pkg.getOps();
+ for (int j=0; j<ops.size(); j++) {
+ AppOpsManager.OpEntry op = ops.get(j);
+ out.startTag(null, "op");
+ out.attribute(null, "n", Integer.toString(op.getOp()));
+ out.attribute(null, "t", Long.toString(op.getTime()));
+ out.attribute(null, "d", Integer.toString(op.getDuration()));
+ out.endTag(null, "op");
+ }
+ out.endTag(null, "uid");
+ }
+ if (lastPkg != null) {
+ out.endTag(null, "pkg");
+ }
+ }
+
+ out.endTag(null, "app-ops");
+ out.endDocument();
+ mFile.finishWrite(stream);
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to write state, restoring backup.", e);
+ mFile.failWrite(stream);
+ }
+ }
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 946ed78..b351fc2 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -529,8 +529,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
public boolean callLocationChangedLocked(Location location) {
- if (mAppOps.noteOpNoThrow(AppOpsManager.OP_LOCATION, mUid, mPackageName)
- != AppOpsManager.MODE_ALLOWED) {
+ if (!reportLocationAccessNoThrow(mUid, mPackageName, mAllowedResolutionLevel)) {
return true;
}
if (mListener != null) {
@@ -803,6 +802,36 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
+ boolean reportLocationAccessNoThrow(int uid, String packageName, int allowedResolutionLevel) {
+ int op;
+ if (allowedResolutionLevel != RESOLUTION_LEVEL_NONE) {
+ if (allowedResolutionLevel == RESOLUTION_LEVEL_COARSE) {
+ op = AppOpsManager.OP_COARSE_LOCATION;
+ } else {
+ op = AppOpsManager.OP_FINE_LOCATION;
+ }
+ if (mAppOps.noteOpNoThrow(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ boolean checkLocationAccess(int uid, String packageName, int allowedResolutionLevel) {
+ int op;
+ if (allowedResolutionLevel != RESOLUTION_LEVEL_NONE) {
+ if (allowedResolutionLevel == RESOLUTION_LEVEL_COARSE) {
+ op = AppOpsManager.OP_COARSE_LOCATION;
+ } else {
+ op = AppOpsManager.OP_FINE_LOCATION;
+ }
+ if (mAppOps.checkOp(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
+ return false;
+ }
+ }
+ return true;
+ }
+
/**
* Returns all providers by name, including passive, but excluding
* fused, also including ones that are not permitted to
@@ -1199,7 +1228,7 @@ public class LocationManagerService extends ILocationManager.Stub {
try {
// We don't check for MODE_IGNORED here; we will do that when we go to deliver
// a location.
- mAppOps.noteOp(AppOpsManager.OP_LOCATION, uid, packageName);
+ checkLocationAccess(uid, packageName, allowedResolutionLevel);
Receiver recevier = checkListenerOrIntent(listener, intent, pid, uid, packageName);
synchronized (mLock) {
@@ -1311,8 +1340,7 @@ public class LocationManagerService extends ILocationManager.Stub {
final int uid = Binder.getCallingUid();
final long identity = Binder.clearCallingIdentity();
try {
- if (mAppOps.noteOp(AppOpsManager.OP_LOCATION, uid, packageName)
- != AppOpsManager.MODE_ALLOWED) {
+ if (!reportLocationAccessNoThrow(uid, packageName, allowedResolutionLevel)) {
return null;
}
@@ -1403,14 +1431,14 @@ public class LocationManagerService extends ILocationManager.Stub {
if (mGpsStatusProvider == null) {
return false;
}
- checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
+ int allowedResolutionLevel = getCallerAllowedResolutionLevel();
+ checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
LocationManager.GPS_PROVIDER);
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
- if (mAppOps.noteOp(AppOpsManager.OP_LOCATION, uid, packageName)
- != AppOpsManager.MODE_ALLOWED) {
+ if (checkLocationAccess(uid, packageName, allowedResolutionLevel)) {
return false;
}
} finally {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 8297988..d8e199b 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1623,7 +1623,7 @@ public final class ActivityManagerService extends ActivityManagerNative
mUsageStatsService = new UsageStatsService(new File(
systemDir, "usagestats").toString());
- mAppOpsService = new AppOpsService();
+ mAppOpsService = new AppOpsService(new File(systemDir, "appops.xml"));
mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0"));
// User 0 is the first and only user that runs at boot.
@@ -7105,7 +7105,7 @@ public final class ActivityManagerService extends ActivityManagerNative
sCallerIdentity.set(new Identity(
Binder.getCallingPid(), Binder.getCallingUid()));
try {
- pfd = cph.provider.openFile(uri, "r");
+ pfd = cph.provider.openFile(null, uri, "r");
} catch (FileNotFoundException e) {
// do nothing; pfd will be returned null
} finally {
diff --git a/test-runner/src/android/test/mock/MockContentProvider.java b/test-runner/src/android/test/mock/MockContentProvider.java
index 1e41416..373f24a 100644
--- a/test-runner/src/android/test/mock/MockContentProvider.java
+++ b/test-runner/src/android/test/mock/MockContentProvider.java
@@ -53,18 +53,20 @@ public class MockContentProvider extends ContentProvider {
*/
private class InversionIContentProvider implements IContentProvider {
@Override
- public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
+ public ContentProviderResult[] applyBatch(String callingPackage,
+ ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException {
return MockContentProvider.this.applyBatch(operations);
}
@Override
- public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException {
+ public int bulkInsert(String callingPackage, Uri url, ContentValues[] initialValues)
+ throws RemoteException {
return MockContentProvider.this.bulkInsert(url, initialValues);
}
@Override
- public int delete(Uri url, String selection, String[] selectionArgs)
+ public int delete(String callingPackage, Uri url, String selection, String[] selectionArgs)
throws RemoteException {
return MockContentProvider.this.delete(url, selection, selectionArgs);
}
@@ -75,37 +77,39 @@ public class MockContentProvider extends ContentProvider {
}
@Override
- public Uri insert(Uri url, ContentValues initialValues) throws RemoteException {
+ public Uri insert(String callingPackage, Uri url, ContentValues initialValues)
+ throws RemoteException {
return MockContentProvider.this.insert(url, initialValues);
}
@Override
- public AssetFileDescriptor openAssetFile(Uri url, String mode) throws RemoteException,
- FileNotFoundException {
+ public AssetFileDescriptor openAssetFile(String callingPackage, Uri url, String mode)
+ throws RemoteException, FileNotFoundException {
return MockContentProvider.this.openAssetFile(url, mode);
}
@Override
- public ParcelFileDescriptor openFile(Uri url, String mode) throws RemoteException,
- FileNotFoundException {
+ public ParcelFileDescriptor openFile(String callingPackage, Uri url, String mode)
+ throws RemoteException, FileNotFoundException {
return MockContentProvider.this.openFile(url, mode);
}
@Override
- public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
+ public Cursor query(String callingPackage, Uri url, String[] projection, String selection,
+ String[] selectionArgs,
String sortOrder, ICancellationSignal cancellationSignal) throws RemoteException {
return MockContentProvider.this.query(url, projection, selection,
selectionArgs, sortOrder);
}
@Override
- public int update(Uri url, ContentValues values, String selection, String[] selectionArgs)
- throws RemoteException {
+ public int update(String callingPackage, Uri url, ContentValues values, String selection,
+ String[] selectionArgs) throws RemoteException {
return MockContentProvider.this.update(url, values, selection, selectionArgs);
}
@Override
- public Bundle call(String method, String request, Bundle args)
+ public Bundle call(String callingPackage, String method, String request, Bundle args)
throws RemoteException {
return MockContentProvider.this.call(method, request, args);
}
@@ -121,7 +125,8 @@ public class MockContentProvider extends ContentProvider {
}
@Override
- public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts)
+ public AssetFileDescriptor openTypedAssetFile(String callingPackage, Uri url,
+ String mimeType, Bundle opts)
throws RemoteException, FileNotFoundException {
return MockContentProvider.this.openTypedAssetFile(url, mimeType, opts);
}
diff --git a/test-runner/src/android/test/mock/MockIContentProvider.java b/test-runner/src/android/test/mock/MockIContentProvider.java
index 9fcfc22..2b4fce6 100644
--- a/test-runner/src/android/test/mock/MockIContentProvider.java
+++ b/test-runner/src/android/test/mock/MockIContentProvider.java
@@ -41,12 +41,12 @@ import java.util.ArrayList;
* @hide - @hide because this exposes bulkQuery() and call(), which must also be hidden.
*/
public class MockIContentProvider implements IContentProvider {
- public int bulkInsert(Uri url, ContentValues[] initialValues) {
+ public int bulkInsert(String callingPackage, Uri url, ContentValues[] initialValues) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@SuppressWarnings("unused")
- public int delete(Uri url, String selection, String[] selectionArgs)
+ public int delete(String callingPackage, Uri url, String selection, String[] selectionArgs)
throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -56,23 +56,26 @@ public class MockIContentProvider implements IContentProvider {
}
@SuppressWarnings("unused")
- public Uri insert(Uri url, ContentValues initialValues) throws RemoteException {
+ public Uri insert(String callingPackage, Uri url, ContentValues initialValues)
+ throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
- public ParcelFileDescriptor openFile(Uri url, String mode) {
+ public ParcelFileDescriptor openFile(String callingPackage, Uri url, String mode) {
throw new UnsupportedOperationException("unimplemented mock method");
}
- public AssetFileDescriptor openAssetFile(Uri uri, String mode) {
+ public AssetFileDescriptor openAssetFile(String callingPackage, Uri uri, String mode) {
throw new UnsupportedOperationException("unimplemented mock method");
}
- public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) {
+ public ContentProviderResult[] applyBatch(String callingPackage,
+ ArrayList<ContentProviderOperation> operations) {
throw new UnsupportedOperationException("unimplemented mock method");
}
- public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
+ public Cursor query(String callingPackage, Uri url, String[] projection, String selection,
+ String[] selectionArgs,
String sortOrder, ICancellationSignal cancellationSignal) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -82,12 +85,12 @@ public class MockIContentProvider implements IContentProvider {
throw new UnsupportedOperationException("unimplemented mock method");
}
- public int update(Uri url, ContentValues values, String selection, String[] selectionArgs)
- throws RemoteException {
+ public int update(String callingPackage, Uri url, ContentValues values, String selection,
+ String[] selectionArgs) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
- public Bundle call(String method, String request, Bundle args)
+ public Bundle call(String callingPackage, String method, String request, Bundle args)
throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -100,8 +103,8 @@ public class MockIContentProvider implements IContentProvider {
throw new UnsupportedOperationException("unimplemented mock method");
}
- public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts)
- throws RemoteException, FileNotFoundException {
+ public AssetFileDescriptor openTypedAssetFile(String callingPackage, Uri url, String mimeType,
+ Bundle opts) throws RemoteException, FileNotFoundException {
throw new UnsupportedOperationException("unimplemented mock method");
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
index f770ccc..4aea38f 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
@@ -40,26 +40,30 @@ import java.util.ArrayList;
*/
public final class BridgeContentProvider implements IContentProvider {
@Override
- public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> arg0)
+ public ContentProviderResult[] applyBatch(String callingPackage,
+ ArrayList<ContentProviderOperation> arg0)
throws RemoteException, OperationApplicationException {
// TODO Auto-generated method stub
return null;
}
@Override
- public int bulkInsert(Uri arg0, ContentValues[] arg1) throws RemoteException {
+ public int bulkInsert(String callingPackage, Uri arg0, ContentValues[] arg1)
+ throws RemoteException {
// TODO Auto-generated method stub
return 0;
}
@Override
- public Bundle call(String arg0, String arg1, Bundle arg2) throws RemoteException {
+ public Bundle call(String callingPackage, String arg0, String arg1, Bundle arg2)
+ throws RemoteException {
// TODO Auto-generated method stub
return null;
}
@Override
- public int delete(Uri arg0, String arg1, String[] arg2) throws RemoteException {
+ public int delete(String callingPackage, Uri arg0, String arg1, String[] arg2)
+ throws RemoteException {
// TODO Auto-generated method stub
return 0;
}
@@ -71,35 +75,35 @@ public final class BridgeContentProvider implements IContentProvider {
}
@Override
- public Uri insert(Uri arg0, ContentValues arg1) throws RemoteException {
+ public Uri insert(String callingPackage, Uri arg0, ContentValues arg1) throws RemoteException {
// TODO Auto-generated method stub
return null;
}
@Override
- public AssetFileDescriptor openAssetFile(Uri arg0, String arg1) throws RemoteException,
- FileNotFoundException {
+ public AssetFileDescriptor openAssetFile(String callingPackage, Uri arg0, String arg1)
+ throws RemoteException, FileNotFoundException {
// TODO Auto-generated method stub
return null;
}
@Override
- public ParcelFileDescriptor openFile(Uri arg0, String arg1) throws RemoteException,
- FileNotFoundException {
+ public ParcelFileDescriptor openFile(String callingPackage, Uri arg0, String arg1)
+ throws RemoteException, FileNotFoundException {
// TODO Auto-generated method stub
return null;
}
@Override
- public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3, String arg4,
- ICancellationSignal arg5) throws RemoteException {
+ public Cursor query(String callingPackage, Uri arg0, String[] arg1, String arg2, String[] arg3,
+ String arg4, ICancellationSignal arg5) throws RemoteException {
// TODO Auto-generated method stub
return null;
}
@Override
- public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3)
- throws RemoteException {
+ public int update(String callingPackage, Uri arg0, ContentValues arg1, String arg2,
+ String[] arg3) throws RemoteException {
// TODO Auto-generated method stub
return 0;
}
@@ -117,8 +121,8 @@ public final class BridgeContentProvider implements IContentProvider {
}
@Override
- public AssetFileDescriptor openTypedAssetFile(Uri arg0, String arg1, Bundle arg2)
- throws RemoteException, FileNotFoundException {
+ public AssetFileDescriptor openTypedAssetFile(String callingPackage, Uri arg0, String arg1,
+ Bundle arg2) throws RemoteException, FileNotFoundException {
// TODO Auto-generated method stub
return null;
}