diff options
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; } |