Merge "Import translations. DO NOT MERGE"
diff --git a/api/current.txt b/api/current.txt
index 71b6b14..ac367f6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6592,6 +6592,7 @@
method public abstract int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.content.pm.PackageInfo getPackageInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract java.lang.String[] getPackagesForUid(int);
+ method public abstract java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int);
method public abstract android.content.pm.PermissionGroupInfo getPermissionGroupInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.content.pm.PermissionInfo getPermissionInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract int getPreferredActivities(java.util.List<android.content.IntentFilter>, java.util.List<android.content.ComponentName>, java.lang.String);
@@ -21689,6 +21690,7 @@
method public int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.PackageInfo getPackageInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] getPackagesForUid(int);
+ method public java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int);
method public android.content.pm.PermissionGroupInfo getPermissionGroupInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.PermissionInfo getPermissionInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public int getPreferredActivities(java.util.List<android.content.IntentFilter>, java.util.List<android.content.ComponentName>, java.lang.String);
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 4baea77d..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 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.insert(mUri, mContentValues);
+ provider.insert(null, mUri, mContentValues);
}
}
@@ -372,7 +372,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.delete(mUri, mWhere, null);
+ provider.delete(null, mUri, mWhere, null);
}
}
@@ -389,7 +389,7 @@
@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 @@
@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 @@
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 @@
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 @@
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 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/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 03d1a3f..2ef3944 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -444,6 +444,28 @@
@SuppressWarnings("unchecked")
@Override
+ public List<PackageInfo> getPackagesHoldingPermissions(
+ String[] permissions, int flags) {
+ final int userId = mContext.getUserId();
+ try {
+ final List<PackageInfo> packageInfos = new ArrayList<PackageInfo>();
+ PackageInfo lastItem = null;
+ ParceledListSlice<PackageInfo> slice;
+
+ do {
+ final String lastKey = lastItem != null ? lastItem.packageName : null;
+ slice = mPM.getPackagesHoldingPermissions(permissions, flags, lastKey, userId);
+ lastItem = slice.populateList(packageInfos, PackageInfo.CREATOR);
+ } while (!slice.isLastSlice());
+
+ return packageInfos;
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
public List<ApplicationInfo> getInstalledApplications(int flags) {
final int userId = mContext.getUserId();
try {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 99ac0d6..4925bf1 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -623,7 +623,9 @@
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
@@ -1403,12 +1405,13 @@
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
warnIfCallingFromSystemProcess();
- return bindService(service, conn, flags, UserHandle.getUserId(Process.myUid()));
+ return bindServiceAsUser(service, conn, flags, Process.myUserHandle());
}
/** @hide */
@Override
- public boolean bindService(Intent service, ServiceConnection conn, int flags, int userHandle) {
+ public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
+ UserHandle user) {
IServiceConnection sd;
if (conn == null) {
throw new IllegalArgumentException("connection is null");
@@ -1430,7 +1433,7 @@
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(),
service, service.resolveTypeIfNeeded(getContentResolver()),
- sd, flags, userHandle);
+ sd, flags, user.getIdentifier());
if (res < 0) {
throw new SecurityException(
"Not allowed to bind to service " + service);
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 @@
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 @@
* @hide
*/
class Transport extends ContentProviderNative {
+ AppOpsManager mAppOpsManager = null;
+ int mReadOp = -1;
+ int mWriteOp = -1;
+
ContentProvider getContentProvider() {
return ContentProvider.this;
}
@@ -182,10 +187,11 @@
}
@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 @@
}
@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 @@
}
@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 @@
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 @@
+ ", 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 @@
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 @@
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 @@
IContentProvider contentProvider, boolean stable) {
mContentProvider = contentProvider;
mContentResolver = contentResolver;
+ mPackageName = contentResolver.mPackageName;
mStable = stable;
}
@@ -81,8 +83,8 @@
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 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 @@
/** 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 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 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 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 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 @@
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 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 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 @@
{
data.enforceInterface(IContentProvider.descriptor);
+ String callingPkg = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
// String[] projection
@@ -110,8 +111,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
{
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 @@
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 @@
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 @@
try {
data.writeInterfaceToken(IContentProvider.descriptor);
+ data.writeString(callingPkg);
url.writeToParcel(data, 0);
int length = 0;
if (projection != null) {
@@ -413,13 +424,14 @@
}
}
- 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 @@
}
}
- 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 @@
}
}
- 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 @@
}
}
- 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 @@
}
}
- 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 @@
}
}
- 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 @@
}
}
- 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 @@
}
}
- 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 @@
}
}
- 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 ContentResolver(Context context) {
mContext = context;
+ mPackageName = context.getPackageName();
}
/** @hide */
@@ -392,7 +393,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
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 @@
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/Context.java b/core/java/android/content/Context.java
index 9510257..14f2847 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1677,7 +1677,7 @@
* argument for use by system server and other multi-user aware code.
* @hide
*/
- public boolean bindService(Intent service, ServiceConnection conn, int flags, int userHandle) {
+ public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags, UserHandle user) {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 84ad667..6a61884 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -475,8 +475,9 @@
/** @hide */
@Override
- public boolean bindService(Intent service, ServiceConnection conn, int flags, int userHandle) {
- return mBase.bindService(service, conn, flags, userHandle);
+ public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
+ UserHandle user) {
+ return mBase.bindServiceAsUser(service, conn, flags, user);
}
@Override
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 @@
* @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/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index b9e432c..4c9c278 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -130,6 +130,15 @@
ParceledListSlice getInstalledPackages(int flags, in String lastRead, in int userId);
/**
+ * This implements getPackagesHoldingPermissions via a "last returned row"
+ * mechanism that is not exposed in the API. This is to get around the IPC
+ * limit that kicks in when flags are included that bloat up the data
+ * returned.
+ */
+ ParceledListSlice getPackagesHoldingPermissions(in String[] permissions,
+ int flags, in String lastRead, int userId);
+
+ /**
* This implements getInstalledApplications via a "last returned row"
* mechanism that is not exposed in the API. This is to get around the IPC
* limit that kicks in when flags are included that bloat up the data
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index cdd9195..a69f220 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1512,11 +1512,43 @@
* @see #GET_SERVICES
* @see #GET_SIGNATURES
* @see #GET_UNINSTALLED_PACKAGES
- *
*/
public abstract List<PackageInfo> getInstalledPackages(int flags);
/**
+ * Return a List of all installed packages that are currently
+ * holding any of the given permissions.
+ *
+ * @param flags Additional option flags. Use any combination of
+ * {@link #GET_ACTIVITIES},
+ * {@link #GET_GIDS},
+ * {@link #GET_CONFIGURATIONS},
+ * {@link #GET_INSTRUMENTATION},
+ * {@link #GET_PERMISSIONS},
+ * {@link #GET_PROVIDERS},
+ * {@link #GET_RECEIVERS},
+ * {@link #GET_SERVICES},
+ * {@link #GET_SIGNATURES},
+ * {@link #GET_UNINSTALLED_PACKAGES} to modify the data returned.
+ *
+ * @return Returns a List of PackageInfo objects, one for each installed
+ * application that is holding any of the permissions that were provided.
+ *
+ * @see #GET_ACTIVITIES
+ * @see #GET_GIDS
+ * @see #GET_CONFIGURATIONS
+ * @see #GET_INSTRUMENTATION
+ * @see #GET_PERMISSIONS
+ * @see #GET_PROVIDERS
+ * @see #GET_RECEIVERS
+ * @see #GET_SERVICES
+ * @see #GET_SIGNATURES
+ * @see #GET_UNINSTALLED_PACKAGES
+ */
+ public abstract List<PackageInfo> getPackagesHoldingPermissions(
+ String[] permissions, int flags);
+
+ /**
* Return a List of all packages that are installed on the device, for a specific user.
* Requesting a list of installed packages for another user
* will require the permission INTERACT_ACROSS_USERS_FULL.
@@ -1742,14 +1774,14 @@
/**
* Return a List of all application packages that are installed on the
* device. If flag GET_UNINSTALLED_PACKAGES has been set, a list of all
- * applications including those deleted with DONT_DELETE_DATA(partially
+ * applications including those deleted with DONT_DELETE_DATA (partially
* installed apps with data directory) will be returned.
*
* @param flags Additional option flags. Use any combination of
* {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
* {@link #GET_UNINSTALLED_PACKAGES} to modify the data returned.
*
- * @return A List of ApplicationInfo objects, one for each application that
+ * @return Returns a List of ApplicationInfo objects, one for each application that
* is installed on the device. In the unlikely case of there being
* no installed applications, an empty list is returned.
* If flag GET_UNINSTALLED_PACKAGES is set, a list of all
diff --git a/core/java/android/ddm/DdmHandleGlTracing.java b/core/java/android/ddm/DdmHandleGlTracing.java
deleted file mode 100644
index 511cf44..0000000
--- a/core/java/android/ddm/DdmHandleGlTracing.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2012 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.ddm;
-
-import android.opengl.GLUtils;
-
-import org.apache.harmony.dalvik.ddmc.Chunk;
-import org.apache.harmony.dalvik.ddmc.ChunkHandler;
-import org.apache.harmony.dalvik.ddmc.DdmServer;
-
-import java.nio.ByteBuffer;
-
-public class DdmHandleGlTracing extends ChunkHandler {
- /** GL TRace control packets. Packet data controls starting/stopping the trace. */
- public static final int CHUNK_GLTR = type("GLTR");
-
- private static final DdmHandleGlTracing sInstance = new DdmHandleGlTracing();
-
- /** singleton, do not instantiate. */
- private DdmHandleGlTracing() {}
-
- public static void register() {
- DdmServer.registerHandler(CHUNK_GLTR, sInstance);
- }
-
- @Override
- public void connected() {
- }
-
- @Override
- public void disconnected() {
- }
-
- @Override
- public Chunk handleChunk(Chunk request) {
- int type = request.type;
-
- if (type != CHUNK_GLTR) {
- throw new RuntimeException("Unknown packet " + ChunkHandler.name(type));
- }
-
- ByteBuffer in = wrapChunk(request);
- GLUtils.setTracingLevel(in.getInt());
- return null; // empty response
- }
-}
diff --git a/core/java/android/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java
index 70ad648..842a482 100644
--- a/core/java/android/ddm/DdmHandleHello.java
+++ b/core/java/android/ddm/DdmHandleHello.java
@@ -36,7 +36,10 @@
private static DdmHandleHello mInstance = new DdmHandleHello();
- private static final String[] NATIVE_FEATURES = new String[] { "opengl-tracing" };
+ private static final String[] FRAMEWORK_FEATURES = new String[] {
+ "opengl-tracing",
+ "view-hierarchy",
+ };
/* singleton, do not instantiate */
private DdmHandleHello() {}
@@ -155,22 +158,22 @@
if (false)
Log.v("ddm-heap", "Got feature list request");
- int size = 4 + 4 * (vmFeatures.length + NATIVE_FEATURES.length);
+ int size = 4 + 4 * (vmFeatures.length + FRAMEWORK_FEATURES.length);
for (int i = vmFeatures.length-1; i >= 0; i--)
size += vmFeatures[i].length() * 2;
- for (int i = NATIVE_FEATURES.length-1; i>= 0; i--)
- size += NATIVE_FEATURES[i].length() * 2;
+ for (int i = FRAMEWORK_FEATURES.length-1; i>= 0; i--)
+ size += FRAMEWORK_FEATURES[i].length() * 2;
ByteBuffer out = ByteBuffer.allocate(size);
out.order(ChunkHandler.CHUNK_ORDER);
- out.putInt(vmFeatures.length + NATIVE_FEATURES.length);
+ out.putInt(vmFeatures.length + FRAMEWORK_FEATURES.length);
for (int i = vmFeatures.length-1; i >= 0; i--) {
out.putInt(vmFeatures[i].length());
putString(out, vmFeatures[i]);
}
- for (int i = NATIVE_FEATURES.length-1; i >= 0; i--) {
- out.putInt(NATIVE_FEATURES[i].length());
- putString(out, NATIVE_FEATURES[i]);
+ for (int i = FRAMEWORK_FEATURES.length-1; i >= 0; i--) {
+ out.putInt(FRAMEWORK_FEATURES[i].length());
+ putString(out, FRAMEWORK_FEATURES[i]);
}
return new Chunk(CHUNK_FEAT, out);
diff --git a/core/java/android/ddm/DdmHandleViewDebug.java b/core/java/android/ddm/DdmHandleViewDebug.java
new file mode 100644
index 0000000..a0578fb
--- /dev/null
+++ b/core/java/android/ddm/DdmHandleViewDebug.java
@@ -0,0 +1,315 @@
+/*
+ * 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.ddm;
+
+import android.opengl.GLUtils;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewRootImpl;
+import android.view.WindowManagerGlobal;
+
+import org.apache.harmony.dalvik.ddmc.Chunk;
+import org.apache.harmony.dalvik.ddmc.ChunkHandler;
+import org.apache.harmony.dalvik.ddmc.DdmServer;
+
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+
+/**
+ * Handle various requests related to profiling / debugging of the view system.
+ * Support for these features are advertised via {@link DdmHandleHello}.
+ */
+public class DdmHandleViewDebug extends ChunkHandler {
+ /** Enable/Disable tracing of OpenGL calls. */
+ public static final int CHUNK_VUGL = type("VUGL");
+
+ /** List {@link ViewRootImpl}'s of this process. */
+ private static final int CHUNK_VULW = type("VULW");
+
+ /** Operation on view root, first parameter in packet should be one of VURT_* constants */
+ private static final int CHUNK_VURT = type("VURT");
+
+ /** Dump view hierarchy. */
+ private static final int VURT_DUMP_HIERARCHY = 1;
+
+ /** Capture View Layers. */
+ private static final int VURT_CAPTURE_LAYERS = 2;
+
+ /**
+ * Generic View Operation, first parameter in the packet should be one of the
+ * VUOP_* constants below.
+ */
+ private static final int CHUNK_VUOP = type("VUOP");
+
+ /** Capture View. */
+ private static final int VUOP_CAPTURE_VIEW = 1;
+
+ /** Obtain the Display List corresponding to the view. */
+ private static final int VUOP_DUMP_DISPLAYLIST = 2;
+
+ /** Invalidate View. */
+ private static final int VUOP_INVALIDATE_VIEW = 3;
+
+ /** Re-layout given view. */
+ private static final int VUOP_LAYOUT_VIEW = 4;
+
+ /** Profile a view. */
+ private static final int VUOP_PROFILE_VIEW = 5;
+
+ /** Error code indicating operation specified in chunk is invalid. */
+ private static final int ERR_INVALID_OP = -1;
+
+ /** Error code indicating that the parameters are invalid. */
+ private static final int ERR_INVALID_PARAM = -2;
+
+ private static final DdmHandleViewDebug sInstance = new DdmHandleViewDebug();
+
+ /** singleton, do not instantiate. */
+ private DdmHandleViewDebug() {}
+
+ public static void register() {
+ DdmServer.registerHandler(CHUNK_VUGL, sInstance);
+ DdmServer.registerHandler(CHUNK_VULW, sInstance);
+ DdmServer.registerHandler(CHUNK_VURT, sInstance);
+ DdmServer.registerHandler(CHUNK_VUOP, sInstance);
+ }
+
+ @Override
+ public void connected() {
+ }
+
+ @Override
+ public void disconnected() {
+ }
+
+ @Override
+ public Chunk handleChunk(Chunk request) {
+ int type = request.type;
+
+ if (type == CHUNK_VUGL) {
+ return handleOpenGlTrace(request);
+ } else if (type == CHUNK_VULW) {
+ return listWindows();
+ }
+
+ ByteBuffer in = wrapChunk(request);
+ int op = in.getInt();
+
+ View rootView = getRootView(in);
+ if (rootView == null) {
+ return createFailChunk(ERR_INVALID_PARAM, "Invalid View Root");
+ }
+
+ if (type == CHUNK_VURT) {
+ if (op == VURT_DUMP_HIERARCHY)
+ return dumpHierarchy(rootView, in);
+ else if (op == VURT_CAPTURE_LAYERS)
+ return captureLayers(rootView);
+ else
+ return createFailChunk(ERR_INVALID_OP, "Unknown view root operation: " + op);
+ }
+
+ final View targetView = getTargetView(rootView, in);
+ if (targetView == null) {
+ return createFailChunk(ERR_INVALID_PARAM, "Invalid target view");
+ }
+
+ if (type == CHUNK_VUOP) {
+ switch (op) {
+ case VUOP_CAPTURE_VIEW:
+ return captureView(rootView, targetView);
+ case VUOP_DUMP_DISPLAYLIST:
+ return dumpDisplayLists(rootView, targetView);
+ case VUOP_INVALIDATE_VIEW:
+ return invalidateView(rootView, targetView);
+ case VUOP_LAYOUT_VIEW:
+ return layoutView(rootView, targetView);
+ case VUOP_PROFILE_VIEW:
+ return profileView(rootView, targetView);
+ default:
+ return createFailChunk(ERR_INVALID_OP, "Unknown view operation: " + op);
+ }
+ } else {
+ throw new RuntimeException("Unknown packet " + ChunkHandler.name(type));
+ }
+ }
+
+ private Chunk handleOpenGlTrace(Chunk request) {
+ ByteBuffer in = wrapChunk(request);
+ GLUtils.setTracingLevel(in.getInt());
+ return null; // empty response
+ }
+
+ /** Returns the list of windows owned by this client. */
+ private Chunk listWindows() {
+ String[] windowNames = WindowManagerGlobal.getInstance().getViewRootNames();
+
+ int responseLength = 4; // # of windows
+ for (String name : windowNames) {
+ responseLength += 4; // length of next window name
+ responseLength += name.length() * 2; // window name
+ }
+
+ ByteBuffer out = ByteBuffer.allocate(responseLength);
+ out.order(ChunkHandler.CHUNK_ORDER);
+
+ out.putInt(windowNames.length);
+ for (String name : windowNames) {
+ out.putInt(name.length());
+ putString(out, name);
+ }
+
+ return new Chunk(CHUNK_VULW, out);
+ }
+
+ private View getRootView(ByteBuffer in) {
+ try {
+ int viewRootNameLength = in.getInt();
+ String viewRootName = getString(in, viewRootNameLength);
+ return WindowManagerGlobal.getInstance().getRootView(viewRootName);
+ } catch (BufferUnderflowException e) {
+ return null;
+ }
+ }
+
+ private View getTargetView(View root, ByteBuffer in) {
+ int viewLength;
+ String viewName;
+
+ try {
+ viewLength = in.getInt();
+ viewName = getString(in, viewLength);
+ } catch (BufferUnderflowException e) {
+ return null;
+ }
+
+ return ViewDebug.findView(root, viewName);
+ }
+
+ /**
+ * Returns the view hierarchy and/or view properties starting at the provided view.
+ * Based on the input options, the return data may include:
+ * - just the view hierarchy
+ * - view hierarchy & the properties for each of the views
+ * - just the view properties for a specific view.
+ * TODO: Currently this only returns views starting at the root, need to fix so that
+ * it can return properties of any view.
+ */
+ private Chunk dumpHierarchy(View rootView, ByteBuffer in) {
+ boolean skipChildren = in.getInt() > 0;
+ boolean includeProperties = in.getInt() > 0;
+
+ ByteArrayOutputStream b = new ByteArrayOutputStream(1024);
+ try {
+ ViewDebug.dump(rootView, skipChildren, includeProperties, b);
+ } catch (IOException e) {
+ return createFailChunk(1, "Unexpected error while obtaining view hierarchy: "
+ + e.getMessage());
+ }
+
+ byte[] data = b.toByteArray();
+ return new Chunk(CHUNK_VURT, data, 0, data.length);
+ }
+
+ /** Returns a buffer with region details & bitmap of every single view. */
+ private Chunk captureLayers(View rootView) {
+ ByteArrayOutputStream b = new ByteArrayOutputStream(1024);
+ DataOutputStream dos = new DataOutputStream(b);
+ try {
+ ViewDebug.captureLayers(rootView, dos);
+ } catch (IOException e) {
+ return createFailChunk(1, "Unexpected error while obtaining view hierarchy: "
+ + e.getMessage());
+ } finally {
+ try {
+ dos.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+
+ byte[] data = b.toByteArray();
+ return new Chunk(CHUNK_VURT, data, 0, data.length);
+ }
+
+ private Chunk captureView(View rootView, View targetView) {
+ ByteArrayOutputStream b = new ByteArrayOutputStream(1024);
+ try {
+ ViewDebug.capture(rootView, b, targetView);
+ } catch (IOException e) {
+ return createFailChunk(1, "Unexpected error while capturing view: "
+ + e.getMessage());
+ }
+
+ byte[] data = b.toByteArray();
+ return new Chunk(CHUNK_VUOP, data, 0, data.length);
+ }
+
+ /** Returns the display lists corresponding to the provided view. */
+ private Chunk dumpDisplayLists(final View rootView, final View targetView) {
+ rootView.post(new Runnable() {
+ @Override
+ public void run() {
+ ViewDebug.outputDisplayList(rootView, targetView);
+ }
+ });
+ return null;
+ }
+
+ /** Invalidates provided view. */
+ private Chunk invalidateView(final View rootView, final View targetView) {
+ targetView.postInvalidate();
+ return null;
+ }
+
+ /** Lays out provided view. */
+ private Chunk layoutView(View rootView, final View targetView) {
+ rootView.post(new Runnable() {
+ @Override
+ public void run() {
+ targetView.requestLayout();
+ }
+ });
+ return null;
+ }
+
+ /** Profiles provided view. */
+ private Chunk profileView(View rootView, final View targetView) {
+ ByteArrayOutputStream b = new ByteArrayOutputStream(32 * 1024);
+ BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(b), 32 * 1024);
+ try {
+ ViewDebug.profileViewAndChildren(targetView, bw);
+ } catch (IOException e) {
+ return createFailChunk(1, "Unexpected error while profiling view: " + e.getMessage());
+ } finally {
+ try {
+ bw.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+
+ byte[] data = b.toByteArray();
+ return new Chunk(CHUNK_VUOP, data, 0, data.length);
+ }
+}
diff --git a/core/java/android/ddm/DdmRegister.java b/core/java/android/ddm/DdmRegister.java
index 2c82967..e0faa51 100644
--- a/core/java/android/ddm/DdmRegister.java
+++ b/core/java/android/ddm/DdmRegister.java
@@ -51,7 +51,7 @@
DdmHandleNativeHeap.register();
DdmHandleProfiling.register();
DdmHandleExit.register();
- DdmHandleGlTracing.register();
+ DdmHandleViewDebug.register();
DdmServer.registrationComplete();
}
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index d0e5019..1044931 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -352,4 +352,14 @@
void setFirewallEgressSourceRule(String addr, boolean allow);
void setFirewallEgressDestRule(String addr, int port, boolean allow);
void setFirewallUidRule(int uid, boolean allow);
+
+ /**
+ * Set a process (pid) to use the name servers associated with the specified interface.
+ */
+ void setDnsIfaceForPid(String iface, int pid);
+
+ /**
+ * Clear a process (pid) from being associated with an interface.
+ */
+ void clearDnsIfaceForPid(int pid);
}
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 @@
"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 @@
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 @@
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 @@
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/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 023e58f..6e28268 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -406,7 +406,7 @@
view = view.getRootView();
if (REMOTE_COMMAND_DUMP.equalsIgnoreCase(command)) {
- dump(view, clientStream);
+ dump(view, false, true, clientStream);
} else if (REMOTE_COMMAND_CAPTURE_LAYERS.equalsIgnoreCase(command)) {
captureLayers(view, new DataOutputStream(clientStream));
} else {
@@ -425,7 +425,8 @@
}
}
- private static View findView(View root, String parameter) {
+ /** @hide */
+ public static View findView(View root, String parameter) {
// Look by type/hashcode
if (parameter.indexOf('@') != -1) {
final String[] ids = parameter.split("@");
@@ -488,7 +489,8 @@
}
}
- private static void profileViewAndChildren(final View view, BufferedWriter out)
+ /** @hide */
+ public static void profileViewAndChildren(final View view, BufferedWriter out)
throws IOException {
profileViewAndChildren(view, out, true);
}
@@ -623,7 +625,8 @@
return duration[0];
}
- private static void captureLayers(View root, final DataOutputStream clientStream)
+ /** @hide */
+ public static void captureLayers(View root, final DataOutputStream clientStream)
throws IOException {
try {
@@ -695,10 +698,21 @@
view.getViewRootImpl().outputDisplayList(view);
}
+ /** @hide */
+ public static void outputDisplayList(View root, View target) {
+ root.getViewRootImpl().outputDisplayList(target);
+ }
+
private static void capture(View root, final OutputStream clientStream, String parameter)
throws IOException {
final View captureView = findView(root, parameter);
+ capture(root, clientStream, captureView);
+ }
+
+ /** @hide */
+ public static void capture(View root, final OutputStream clientStream, View captureView)
+ throws IOException {
Bitmap b = performViewCapture(captureView, false);
if (b == null) {
@@ -752,14 +766,20 @@
return null;
}
- private static void dump(View root, OutputStream clientStream) throws IOException {
+ /**
+ * Dumps the view hierarchy starting from the given view.
+ * @hide
+ */
+ public static void dump(View root, boolean skipChildren, boolean includeProperties,
+ OutputStream clientStream) throws IOException {
BufferedWriter out = null;
try {
out = new BufferedWriter(new OutputStreamWriter(clientStream, "utf-8"), 32 * 1024);
View view = root.getRootView();
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
- dumpViewHierarchyWithProperties(group.getContext(), group, out, 0);
+ dumpViewHierarchy(group.getContext(), group, out, 0,
+ skipChildren, includeProperties);
}
out.write("DONE.");
out.newLine();
@@ -804,9 +824,13 @@
return view.getClass().getName().equals(className) && view.hashCode() == hashCode;
}
- private static void dumpViewHierarchyWithProperties(Context context, ViewGroup group,
- BufferedWriter out, int level) {
- if (!dumpViewWithProperties(context, group, out, level)) {
+ private static void dumpViewHierarchy(Context context, ViewGroup group,
+ BufferedWriter out, int level, boolean skipChildren, boolean includeProperties) {
+ if (!dumpView(context, group, out, level, includeProperties)) {
+ return;
+ }
+
+ if (skipChildren) {
return;
}
@@ -814,9 +838,10 @@
for (int i = 0; i < count; i++) {
final View view = group.getChildAt(i);
if (view instanceof ViewGroup) {
- dumpViewHierarchyWithProperties(context, (ViewGroup) view, out, level + 1);
+ dumpViewHierarchy(context, (ViewGroup) view, out, level + 1, skipChildren,
+ includeProperties);
} else {
- dumpViewWithProperties(context, view, out, level + 1);
+ dumpView(context, view, out, level + 1, includeProperties);
}
}
if (group instanceof HierarchyHandler) {
@@ -824,8 +849,8 @@
}
}
- private static boolean dumpViewWithProperties(Context context, View view,
- BufferedWriter out, int level) {
+ private static boolean dumpView(Context context, View view,
+ BufferedWriter out, int level, boolean includeProperties) {
try {
for (int i = 0; i < level; i++) {
@@ -835,7 +860,9 @@
out.write('@');
out.write(Integer.toHexString(view.hashCode()));
out.write(' ');
- dumpViewProperties(context, view, out);
+ if (includeProperties) {
+ dumpViewProperties(context, view, out);
+ }
out.newLine();
} catch (IOException e) {
Log.w("View", "Error while dumping hierarchy tree");
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index e8945aa..7eb26fa 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -160,6 +160,29 @@
}
}
+ public String[] getViewRootNames() {
+ synchronized (mLock) {
+ if (mRoots == null) return new String[0];
+ String[] mViewRoots = new String[mRoots.length];
+ int i = 0;
+ for (ViewRootImpl root : mRoots) {
+ mViewRoots[i++] = getWindowName(root);
+ }
+ return mViewRoots;
+ }
+ }
+
+ public View getRootView(String name) {
+ synchronized (mLock) {
+ if (mRoots == null) return null;
+ for (ViewRootImpl root : mRoots) {
+ if (name.equals(getWindowName(root))) return root.getView();
+ }
+ }
+
+ return null;
+ }
+
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
if (view == null) {
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/java/com/android/internal/os/SamplingProfilerIntegration.java b/core/java/com/android/internal/os/SamplingProfilerIntegration.java
index df0fcd9..6429aa4 100644
--- a/core/java/com/android/internal/os/SamplingProfilerIntegration.java
+++ b/core/java/com/android/internal/os/SamplingProfilerIntegration.java
@@ -106,7 +106,7 @@
}
ThreadGroup group = Thread.currentThread().getThreadGroup();
- SamplingProfiler.ThreadSet threadSet = SamplingProfiler.newThreadGroupTheadSet(group);
+ SamplingProfiler.ThreadSet threadSet = SamplingProfiler.newThreadGroupThreadSet(group);
samplingProfiler = new SamplingProfiler(samplingProfilerDepth, threadSet);
samplingProfiler.start(samplingProfilerMilliseconds);
startMillis = System.currentTimeMillis();
diff --git a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
index 1752e5b..ce31c5b 100644
--- a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
+++ b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
@@ -62,7 +62,9 @@
uint16_t glyph = codepoint;
paint->getTextWidths(&glyph, sizeof(glyph), &skWidth, &skBounds);
+#if DEBUG_GLYPHS
ALOGD("returned glyph for %i: width = %f", codepoint, skWidth);
+#endif
if (width)
*width = SkScalarToHBFixed(skWidth);
if (extents) {
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index d7df402..6d3c878 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -810,6 +810,9 @@
case HB_SCRIPT_CYRILLIC:
case HB_SCRIPT_HANGUL:
case HB_SCRIPT_INHERITED:
+ case HB_SCRIPT_HAN:
+ case HB_SCRIPT_KATAKANA:
+ case HB_SCRIPT_HIRAGANA:
return false;
default:
return true;
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-am/strings.xml b/core/res/res/values-am/strings.xml
index 5b8200e..9fa0a06 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -316,7 +316,7 @@
<string name="permlab_updateBatteryStats" msgid="3719689764536379557">"የባትሪ ስታስቲክስን ይቀይራል"</string>
<string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"የተሰበሰቡ የባትሪ ስታስቲክሶችን እንዲቀይር ለመተግበሪያው ያስችለዋል። ለመደበኛ መተግበሪያዎች ጥቅም አይደለም።"</string>
<string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"የመተግበሪያ ክወናዎች ስታቲስቲክስን ይቀይሩ"</string>
- <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"መተግበሪያው የተሰበሰቡ የክወና ስታስቲክሶችን እንዲቀይር ይፈቅድለታል። ለመደበኛ መተግበሪያዎች ጥቅም ያልሆነ።"</string>
+ <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"መተግበሪያው የተሰበሰቡ የክወና ስታስቲክሶችን እንዲቀይር ይፈቅድሎታል። ለመደበኛ መተግበሪያዎች ጥቅም ያልሆነ።"</string>
<string name="permlab_backup" msgid="470013022865453920">"የስርዓት መጠባበቂያን ተቆጣጠር እናእነበረበት መልስ"</string>
<string name="permdesc_backup" msgid="6912230525140589891">"የስርዓቱን ምትኬ እና እንደነበር መልስ መንገዶችን ለመቆጣጠር ለመተግበሪያው ይፈቅዳሉ፡፡ በመደበኛ መተግበሪያዎች ለመጠቀም አይሆንም፡፡"</string>
<string name="permlab_confirm_full_backup" msgid="5557071325804469102">"የሙሉ መጠበቂያ ወይም እነበረበት መልስ ከዋኝ አረጋግጥ"</string>
@@ -943,7 +943,7 @@
</plurals>
<plurals name="in_num_hours">
<item quantity="one" msgid="7164353342477769999">"በ 1 ሰዓት"</item>
- <item quantity="other" msgid="547290677353727389">"በ <xliff:g id="COUNT">%d</xliff:g> ሰዓታት"</item>
+ <item quantity="other" msgid="547290677353727389">"በ <xliff:g id="COUNT">%d</xliff:g> ሰዓቶች"</item>
</plurals>
<plurals name="in_num_days">
<item quantity="one" msgid="5413088743009839518">"ነገ"</item>
@@ -975,7 +975,7 @@
</plurals>
<plurals name="abbrev_in_num_hours">
<item quantity="one" msgid="3274708118124045246">"በ 1 ሰዓት"</item>
- <item quantity="other" msgid="3705373766798013406">"በ <xliff:g id="COUNT">%d</xliff:g> ሰዓታት"</item>
+ <item quantity="other" msgid="3705373766798013406">"በ <xliff:g id="COUNT">%d</xliff:g> ሰዓቶች"</item>
</plurals>
<plurals name="abbrev_in_num_days">
<item quantity="one" msgid="2178576254385739855">"ነገ"</item>
@@ -1006,7 +1006,7 @@
</plurals>
<plurals name="duration_hours">
<item quantity="one" msgid="8917467491248809972">"1 ሰዓት"</item>
- <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> ሰዓታት"</item>
+ <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> ሰዓቶች"</item>
</plurals>
<string name="VideoView_error_title" msgid="3534509135438353077">"የቪዲዮ ችግር"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"ይቅርታ፣ ይህ ቪዲዮ በዚህ መሣሪያ ለመልቀቅ ትክክል አይደለም።"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 590dfee..8fc1450 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -315,8 +315,8 @@
<string name="permdesc_batteryStats" msgid="5897346582882915114">"Permite a una aplicación leer los datos actuales de uso de batería de bajo nivel. Puede permitir a la aplicación buscar información detallada sobre las aplicaciones que usas."</string>
<string name="permlab_updateBatteryStats" msgid="3719689764536379557">"modificar las estadísticas de la batería"</string>
<string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Permite a la aplicación modificar las estadísticas recopiladas de la batería. Las aplicaciones normales no deben utilizarlo."</string>
- <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"Modificar estadísticas de operaciones de aplicación"</string>
- <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Permite que la aplicación modifique las estadísticas recopiladas de operación de la aplicación. Las aplicaciones normales no deben utilizar este permiso."</string>
+ <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modificar estadísticas de uso de aplicaciones"</string>
+ <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Permite que la aplicación modifique las estadísticas recopiladas sobre el uso de aplicaciones. Las aplicaciones normales no deben utilizar este permiso."</string>
<string name="permlab_backup" msgid="470013022865453920">"copia de seguridad y restauración del sistema de control"</string>
<string name="permdesc_backup" msgid="6912230525140589891">"Permite que la aplicación controle el mecanismo de copia de seguridad y restauración del sistema. Las aplicaciones normales no deben utilizar este permiso."</string>
<string name="permlab_confirm_full_backup" msgid="5557071325804469102">"Confirmar una copia completa de seguridad o una operación de restauración"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 7d18dc9..d3eaa61 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -315,7 +315,7 @@
<string name="permdesc_batteryStats" msgid="5897346582882915114">"Consente a un\'applicazione di leggere i dati di utilizzo della batteria di basso livello correnti. Potrebbe consentire all\'applicazione di trovare informazioni dettagliate sulle applicazioni che utilizzi."</string>
<string name="permlab_updateBatteryStats" msgid="3719689764536379557">"modifica statistiche batteria"</string>
<string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Consente all\'applicazione di modificare le statistiche raccolte sulla batteria. Da non usare per normali applicazioni."</string>
- <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modifica statistiche funzion. app"</string>
+ <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modifica statistiche funzionamento app"</string>
<string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Consente all\'applicazione di modificare le statistiche raccolte sul funzionamento dell\'applicazione. Da non usare per normali applicazioni."</string>
<string name="permlab_backup" msgid="470013022865453920">"controllo del backup di sistema e ripristino"</string>
<string name="permdesc_backup" msgid="6912230525140589891">"Consente all\'applicazione di controllare il meccanismo di backup e di ripristino del sistema. Da non usare per normali applicazioni."</string>
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/docs/html/develop/index.jd b/docs/html/develop/index.jd
index b9c7fa1..57fa498 100644
--- a/docs/html/develop/index.jd
+++ b/docs/html/develop/index.jd
@@ -51,7 +51,7 @@
<li class="item carousel-home">
<div class="col-8">
<img
-src="http://4.bp.blogspot.com/-lfjzgG5Dqrk/UHMThRtpRwI/AAAAAAAABpk/h4d3nsmkgPM/s400/mint.png"
+src="//lh4.ggpht.com/-lfjzgG5Dqrk/UHMThRtpRwI/AAAAAAAABpk/h4d3nsmkgPM/s400/mint.png"
class="play no-shadow no-transform" />
</div>
<div class="content-right col-6">
@@ -65,7 +65,7 @@
<li class="item carousel-home">
<div class="col-8">
<img
-src="http://1.bp.blogspot.com/-6K1kfNOdek8/T72bXvtTSQI/AAAAAAAABmw/kYzmJt0_328/s1600/google-play-subscriptions.png" class="play"></div>
+src="//lh4.ggpht.com/-6K1kfNOdek8/T72bXvtTSQI/AAAAAAAABmw/kYzmJt0_328/s1600/google-play-subscriptions.png" class="play"></div>
<div class="content-right col-6">
<h2>In-app Subscriptions with Trials</h2>
<p>You can now set up a <strong>free trial period</strong> for any Google Play in-app subscription, making it easy for users try your subscriber content before automatically converting to a full subscription. Free trials give you a new way to bring users into your products and engage them effectively. </p>
@@ -77,7 +77,7 @@
<li class="item carousel-home">
<div class="col-8">
<img
-src="http://2.bp.blogspot.com/-MgN5DnoO5XU/UHYGYzTcAOI/AAAAAAAABs4/jTS7sKkfBcM/s1600/pubsites.png" class="play"></div>
+src="//lh4.ggpht.com/-MgN5DnoO5XU/UHYGYzTcAOI/AAAAAAAABs4/jTS7sKkfBcM/s1600/pubsites.png" class="play"></div>
<div class="content-right col-6">
<p class="title-intro">From the blog:</p>
<h2>New Google Play Developer Console</h2>
@@ -90,7 +90,7 @@
<li class="item carousel-home">
<div class="col-8">
<img
-src="http://4.bp.blogspot.com/-g05If_eKKRQ/UAcrVLI-OYI/AAAAAAAAAr8/AWvunVb5S-w/s320/nexus7.png"
+src="//lh4.ggpht.com/-g05If_eKKRQ/UAcrVLI-OYI/AAAAAAAAAr8/AWvunVb5S-w/s320/nexus7.png"
class="play no-shadow no-transform" />
</div>
<div class="content-right col-6">
@@ -127,7 +127,7 @@
<p>You can take advantage of the auth APIs in Google Play services to let your back end know which app is calling and for which user....</p>
</a></li>
<li><a href="//android-developers.blogspot.com/2012/12/daydream-interactive-screen-savers.html">
- <div class="feed-image" style="background:url('//3.bp.blogspot.com/-wVsUOo4xGE0/UNy9mZ1nmMI/AAAAAAAAB4w/f6rhyLn5KbI/s1600/daydream-example.jpg') no-repeat 0 0;background-position:right top;"></div>
+ <div class="feed-image" style="background:url('//lh4.ggpht.com/-wVsUOo4xGE0/UNy9mZ1nmMI/AAAAAAAAB4w/f6rhyLn5KbI/s1600/daydream-example.jpg') no-repeat 0 0;background-position:right top;"></div>
<h4>Daydream: Interactive Screen Savers</h4>
<p>Daydream is an interactive screen-saver mode introduced in Android 4.2. Learn how to add Daydreams to your apps...</p>
</a></li>
@@ -344,7 +344,7 @@
/* Request the playlist feeds from YouTube */
function showDevelopersLivePlaylist() {
- var playlistId = "PLB7B9B23D864A55C3"; /* The App Clinic */
+ var playlistId = "PLWz5rJ2EKKc_XOgcRukSoKKjewFJZrKV0"; /* DevBytes */
var script = "<script type='text/javascript' src='//gdata.youtube.com/feeds/api/playlists/"
+ playlistId +
"?v=2&alt=json-in-script&max-results=10&callback=renderDevelopersLivePlaylist&orderby=published'><\/script > ";
diff --git a/docs/html/guide/topics/resources/more-resources.jd b/docs/html/guide/topics/resources/more-resources.jd
index 9fa1a2d..b5f449a 100644
--- a/docs/html/guide/topics/resources/more-resources.jd
+++ b/docs/html/guide/topics/resources/more-resources.jd
@@ -575,7 +575,7 @@
</dl>
</dd>
<dt id="integer-array-item-element"><code><item></code></dt>
- <dd>An integer. The value can be a referenced to another
+ <dd>An integer. The value can be a reference to another
integer resource. Must be a child of a {@code <integer-array>} element.
<p>No attributes.</p>
</dd>
diff --git a/docs/html/guide/topics/resources/string-resource.jd b/docs/html/guide/topics/resources/string-resource.jd
index 27761e2..5a96ba1 100644
--- a/docs/html/guide/topics/resources/string-resource.jd
+++ b/docs/html/guide/topics/resources/string-resource.jd
@@ -303,7 +303,7 @@
</dd>
<dt id="plurals-item-element"><code><item></code></dt>
- <dd>A plural or singular string. The value can be a referenced to another
+ <dd>A plural or singular string. The value can be a reference to another
string resource. Must be a child of a {@code <plurals>} element. Beware that you must
escape apostrophes and quotation marks. See <a href="#FormattingAndStyling">Formatting and
Styling</a>, below, for information about to properly style and format your strings.
diff --git a/docs/html/index.jd b/docs/html/index.jd
index cf06324..afda7a9 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -22,7 +22,7 @@
<script type="text/javascript">
var params = { allowScriptAccess: "always" };
var atts = { id: "ytapiplayer" };
- swfobject.embedSWF("http://www.youtube.com/v/RRelFvc6Czo?enablejsapi=1&playerapiid=ytplayer&version=3&HD=1;rel=0;showinfo=0;modestbranding;origin=developer.android.com;autohide=1",
+ swfobject.embedSWF("//www.youtube.com/v/RRelFvc6Czo?enablejsapi=1&playerapiid=ytplayer&version=3&HD=1;rel=0;showinfo=0;modestbranding;origin=developer.android.com;autohide=1",
"ytapiplayer", "600", "338", "8", null, null, params, atts);
// Callback used to pause/resume carousel based on video state
diff --git a/docs/html/training/articles/smp.jd b/docs/html/training/articles/smp.jd
index 53d7879..d46787d 100644
--- a/docs/html/training/articles/smp.jd
+++ b/docs/html/training/articles/smp.jd
@@ -628,7 +628,7 @@
<p>The “loop_until” seen in previous examples has been expanded to show the load
of B into reg0. reg1 is assigned the numeric value 8, and reg2 is loaded from
-the address [A+reg1] (same location that thread 1 is accessing).</p>
+the address [A+reg1] (the same location that thread 1 is accessing).</p>
<p>This will not behave correctly because the load from B could be observed
after the load from [A+reg1]. We can fix this with a load/load barrier after
@@ -640,7 +640,7 @@
<th>Thread 2</th>
</tr>
<tr>
-<td><code>A = 41<br />
+<td><code>[A+8] = 41<br />
<em>store/store barrier</em><br />
B = 1 // “A is ready”</code></td>
<td><code>loop:<br />
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 @@
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 @@
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 @@
private int mNativeContext;
private Context mContext;
+ private String mPackageName;
private IContentProvider mMediaProvider;
private Uri mAudioUri;
private Uri mVideoUri;
@@ -388,6 +389,7 @@
public MediaScanner(Context c) {
native_setup();
mContext = c;
+ mPackageName = c.getPackageName();
mBitmapOptions.inSampleSize = 1;
mBitmapOptions.inJustDecodeBounds = true;
@@ -961,7 +963,7 @@
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 @@
}
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 @@
// 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 @@
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 @@
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 @@
// 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 @@
try {
Cursor c = mMediaProvider.query(
+ mPackageName,
mThumbsUri,
new String [] { "_data" },
null,
@@ -1225,11 +1230,13 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
native_setup();
mContext = context;
+ mPackageName = context.getPackageName();
mMediaProvider = context.getContentResolver().acquireProvider("media");
mVolumeName = volumeName;
mMediaStoragePath = storagePath;
@@ -263,7 +265,7 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
private final MtpDatabase mDatabase;
private final IContentProvider mProvider;
+ private final String mPackageName;
private final String mVolumeName;
private final Uri mUri;
@@ -65,10 +66,11 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
index ad3c342..8c76421 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
@@ -38,6 +38,7 @@
private MediaInserter mMediaInserter;
private static final int TEST_BUFFER_SIZE = 10;
private IContentProvider mMockProvider;
+ private String mPackageName;
private int mFilesCounter;
private int mAudioCounter;
@@ -83,7 +84,9 @@
protected void setUp() throws Exception {
super.setUp();
mMockProvider = EasyMock.createMock(IContentProvider.class);
- mMediaInserter = new MediaInserter(mMockProvider, TEST_BUFFER_SIZE);
+ mMediaInserter = new MediaInserter(mMockProvider,
+ mPackageName, TEST_BUFFER_SIZE);
+ mPackageName = getInstrumentation().getContext().getPackageName();
mFilesCounter = 0;
mAudioCounter = 0;
mVideoCounter = 0;
@@ -144,7 +147,7 @@
@SmallTest
public void testInsertContentsEqualToBufferSize() throws Exception {
- EasyMock.expect(mMockProvider.bulkInsert(
+ EasyMock.expect(mMockProvider.bulkInsert(mPackageName,
(Uri) EasyMock.anyObject(), (ContentValues[]) EasyMock.anyObject())).andReturn(1);
EasyMock.expectLastCall().times(4);
EasyMock.replay(mMockProvider);
@@ -159,7 +162,7 @@
@SmallTest
public void testInsertContentsMoreThanBufferSize() throws Exception {
- EasyMock.expect(mMockProvider.bulkInsert(
+ EasyMock.expect(mMockProvider.bulkInsert(mPackageName,
(Uri) EasyMock.anyObject(), (ContentValues[]) EasyMock.anyObject())).andReturn(1);
EasyMock.expectLastCall().times(4);
EasyMock.replay(mMockProvider);
@@ -183,7 +186,7 @@
@SmallTest
public void testFlushAllWithSomeContents() throws Exception {
- EasyMock.expect(mMockProvider.bulkInsert(
+ EasyMock.expect(mMockProvider.bulkInsert(mPackageName,
(Uri) EasyMock.anyObject(), (ContentValues[]) EasyMock.anyObject())).andReturn(1);
EasyMock.expectLastCall().times(4);
EasyMock.replay(mMockProvider);
@@ -199,7 +202,7 @@
@SmallTest
public void testInsertContentsAfterFlushAll() throws Exception {
- EasyMock.expect(mMockProvider.bulkInsert(
+ EasyMock.expect(mMockProvider.bulkInsert(mPackageName,
(Uri) EasyMock.anyObject(), (ContentValues[]) EasyMock.anyObject())).andReturn(1);
EasyMock.expectLastCall().times(8);
EasyMock.replay(mMockProvider);
@@ -220,16 +223,20 @@
@SmallTest
public void testInsertContentsWithDifferentSizePerContentType() throws Exception {
- EasyMock.expect(mMockProvider.bulkInsert(MediaUriMatcher.expectMediaUri(sFilesUri),
+ EasyMock.expect(mMockProvider.bulkInsert(mPackageName,
+ MediaUriMatcher.expectMediaUri(sFilesUri),
(ContentValues[]) EasyMock.anyObject())).andReturn(1);
EasyMock.expectLastCall().times(1);
- EasyMock.expect(mMockProvider.bulkInsert(MediaUriMatcher.expectMediaUri(sAudioUri),
+ EasyMock.expect(mMockProvider.bulkInsert(mPackageName,
+ MediaUriMatcher.expectMediaUri(sAudioUri),
(ContentValues[]) EasyMock.anyObject())).andReturn(1);
EasyMock.expectLastCall().times(2);
- EasyMock.expect(mMockProvider.bulkInsert(MediaUriMatcher.expectMediaUri(sVideoUri),
+ EasyMock.expect(mMockProvider.bulkInsert(mPackageName,
+ MediaUriMatcher.expectMediaUri(sVideoUri),
(ContentValues[]) EasyMock.anyObject())).andReturn(1);
EasyMock.expectLastCall().times(3);
- EasyMock.expect(mMockProvider.bulkInsert(MediaUriMatcher.expectMediaUri(sImagesUri),
+ EasyMock.expect(mMockProvider.bulkInsert(mPackageName,
+ MediaUriMatcher.expectMediaUri(sImagesUri),
(ContentValues[]) EasyMock.anyObject())).andReturn(1);
EasyMock.expectLastCall().times(4);
EasyMock.replay(mMockProvider);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 03de89bd..17b0b50 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -81,18 +81,18 @@
SaveImageInBackgroundData> {
private static final String SCREENSHOTS_DIR_NAME = "Screenshots";
private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png";
- private static final String SCREENSHOT_FILE_PATH_TEMPLATE = "%s/%s/%s";
private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
- private int mNotificationId;
- private NotificationManager mNotificationManager;
- private Notification.Builder mNotificationBuilder;
- private String mImageFileName;
- private String mImageFilePath;
- private long mImageTime;
- private BigPictureStyle mNotificationStyle;
- private int mImageWidth;
- private int mImageHeight;
+ private final int mNotificationId;
+ private final NotificationManager mNotificationManager;
+ private final Notification.Builder mNotificationBuilder;
+ private final File mScreenshotDir;
+ private final String mImageFileName;
+ private final String mImageFilePath;
+ private final long mImageTime;
+ private final BigPictureStyle mNotificationStyle;
+ private final int mImageWidth;
+ private final int mImageHeight;
// WORKAROUND: We want the same notification across screenshots that we update so that we don't
// spam a user's notification drawer. However, we only show the ticker for the saving state
@@ -108,11 +108,11 @@
// Prepare all the output metadata
mImageTime = System.currentTimeMillis();
String imageDate = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date(mImageTime));
- String imageDir = Environment.getExternalStoragePublicDirectory(
- Environment.DIRECTORY_PICTURES).getAbsolutePath();
mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate);
- mImageFilePath = String.format(SCREENSHOT_FILE_PATH_TEMPLATE, imageDir,
- SCREENSHOTS_DIR_NAME, mImageFileName);
+
+ mScreenshotDir = new File(Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_PICTURES), SCREENSHOTS_DIR_NAME);
+ mImageFilePath = new File(mScreenshotDir, mImageFileName).getAbsolutePath();
// Create the large notification icon
mImageWidth = data.image.getWidth();
@@ -175,6 +175,9 @@
Resources r = context.getResources();
try {
+ // Create screenshot directory if it doesn't exist
+ mScreenshotDir.mkdirs();
+
// Save the screenshot to the MediaStore
ContentValues values = new ContentValues();
ContentResolver resolver = context.getContentResolver();
@@ -217,13 +220,21 @@
resolver.update(uri, values, null, null);
params[0].imageUri = uri;
+ params[0].image = null;
params[0].result = 0;
} catch (Exception e) {
// IOException/UnsupportedOperationException may be thrown if external storage is not
// mounted
+ params[0].imageUri = null;
+ params[0].image = null;
params[0].result = 1;
}
+ // Recycle the bitmap data
+ if (image != null) {
+ image.recycle();
+ }
+
return params[0];
}
@@ -458,6 +469,10 @@
// Save the screenshot once we have a bit of time now
saveScreenshotInWorkerThread(finisher);
mWindowManager.removeView(mScreenshotLayout);
+
+ // Clear any references to the bitmap
+ mScreenBitmap = null;
+ mScreenshotView.setImageBitmap(null);
}
});
mScreenshotLayout.post(new Runnable() {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 71e80831d..b5cbdd1 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -3349,8 +3349,8 @@
@Override
public void onServiceDisconnected(ComponentName name) {}
};
- if (mContext.bindService(
- intent, conn, Context.BIND_AUTO_CREATE, UserHandle.USER_CURRENT)) {
+ if (mContext.bindServiceAsUser(
+ intent, conn, Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
mScreenshotConnection = conn;
mHandler.postDelayed(mScreenshotTimeout, 10000);
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java b/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
index 830471a..e58eb5b 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
@@ -128,10 +128,10 @@
if (!mBoundToService) {
Log.d(TAG, "Binding to Face Unlock service for user="
+ mLockPatternUtils.getCurrentUser());
- mContext.bindService(new Intent(IFaceLockInterface.class.getName()),
+ mContext.bindServiceAsUser(new Intent(IFaceLockInterface.class.getName()),
mConnection,
Context.BIND_AUTO_CREATE,
- mLockPatternUtils.getCurrentUser());
+ new UserHandle(mLockPatternUtils.getCurrentUser()));
mBoundToService = true;
} else {
Log.w(TAG, "Attempt to bind to Face Unlock when already bound");
diff --git a/services/java/com/android/server/AppOpsService.java b/services/java/com/android/server/AppOpsService.java
index 539194c..1712806 100644
--- a/services/java/com/android/server/AppOpsService.java
+++ b/services/java/com/android/server/AppOpsService.java
@@ -18,15 +18,22 @@
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.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 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,13 +115,78 @@
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 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) {
+ 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);
+ }
+ }
+ }
+ }
+ return res;
+ }
+
+ @Override
+ public int checkOperation(int code, int uid, String packageName) {
+ uid = handleIncomingUid(uid);
+ synchronized (this) {
+ Op op = getOpLocked(code, uid, packageName, false);
+ if (op == null) {
+ return AppOpsManager.MODE_ALLOWED;
+ }
+ }
+ return AppOpsManager.MODE_ALLOWED;
}
@Override
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 AppOpsManager.MODE_IGNORED;
}
@@ -99,16 +204,15 @@
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, "Starting 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 = -1;
+ op.nesting++;
}
return AppOpsManager.MODE_ALLOWED;
}
@@ -117,52 +221,21 @@
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, "Ignoring finishing op not started: uid " + uid + " pkg " + packageName
- + " code " + code + " time=" + op.time + " duration=" + op.duration);
- return;
- }
- op.duration = (int)(System.currentTimeMillis() - op.time);
- }
- }
-
- @Override
- public int noteTimedOperation(int code, int uid, String packageName, int duration) {
- uid = handleIncomingUid(uid);
- 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);
- }
- op.time = System.currentTimeMillis();
- op.duration = duration;
- }
- return AppOpsManager.MODE_ALLOWED;
- }
-
- @Override
- public void earlyFinishOperation(int code, int uid, String packageName) {
- uid = handleIncomingUid(uid);
- synchronized (this) {
- Op op = getOpLocked(code, uid, packageName);
- 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);
+ }
+ op.nesting = 0;
+ } else {
+ op.nesting--;
}
}
}
@@ -179,14 +252,20 @@
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 +286,205 @@
} 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/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
index e1e9eaf..d1829ab 100644
--- a/services/java/com/android/server/AppWidgetServiceImpl.java
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -718,7 +718,8 @@
final long token = Binder.clearCallingIdentity();
try {
conn = new ServiceConnectionProxy(key, connection);
- mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
+ mContext.bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE,
+ new UserHandle(userId));
mBoundRemoteViewsServices.put(key, conn);
} finally {
Binder.restoreCallingIdentity(token);
@@ -806,7 +807,8 @@
// RemoteViewsService.
final long token = Binder.clearCallingIdentity();
try {
- mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
+ mContext.bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE,
+ new UserHandle(userId));
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1104,7 +1106,8 @@
// Bind to the service and call onDataSetChanged()
final long token = Binder.clearCallingIdentity();
try {
- mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
+ mContext.bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE,
+ new UserHandle(userId));
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 7ac314b..88ea1f1 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -836,8 +836,8 @@
if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
if (DEBUG) Slog.v(TAG, "Binding to Google transport");
Intent intent = new Intent().setComponent(transportComponent);
- context.bindService(intent, mGoogleConnection, Context.BIND_AUTO_CREATE,
- UserHandle.USER_OWNER);
+ context.bindServiceAsUser(intent, mGoogleConnection, Context.BIND_AUTO_CREATE,
+ UserHandle.OWNER);
} else {
Slog.w(TAG, "Possible Google transport spoof: ignoring " + info);
}
diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java
index 5a2088c..33e712a 100755
--- a/services/java/com/android/server/BluetoothManagerService.java
+++ b/services/java/com/android/server/BluetoothManagerService.java
@@ -611,8 +611,8 @@
Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
Intent i = new Intent(IBluetooth.class.getName());
- if (!mContext.bindService(i, mConnection,
- Context.BIND_AUTO_CREATE, UserHandle.USER_CURRENT)) {
+ if (!mContext.bindServiceAsUser(i, mConnection,
+ Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
Log.e(TAG, "fail to bind to: " + IBluetooth.class.getName());
} else {
@@ -959,8 +959,8 @@
mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
mConnection.setGetNameAddressOnly(false);
Intent i = new Intent(IBluetooth.class.getName());
- if (!mContext.bindService(i, mConnection,Context.BIND_AUTO_CREATE,
- UserHandle.USER_CURRENT)) {
+ if (!mContext.bindServiceAsUser(i, mConnection,Context.BIND_AUTO_CREATE,
+ UserHandle.CURRENT)) {
mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
Log.e(TAG, "Fail to bind to: " + IBluetooth.class.getName());
} else {
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 4cfe5d5..2ccde3b 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -183,7 +183,7 @@
* A per Net list of the PID's that requested access to the net
* used both as a refcount and for per-PID DNS selection
*/
- private List mNetRequestersPids[];
+ private List<Integer> mNetRequestersPids[];
// priority order of the nettrackers
// (excluding dynamically set mNetworkPreference)
@@ -200,7 +200,6 @@
private int mDefaultConnectionSequence = 0;
private Object mDnsLock = new Object();
- private int mNumDnsEntries;
private boolean mDnsOverridden = false;
private boolean mTestMode;
@@ -508,15 +507,14 @@
}
}
- mNetRequestersPids = new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE+1];
+ mNetRequestersPids =
+ (List<Integer> [])new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE+1];
for (int i : mPriorityList) {
- mNetRequestersPids[i] = new ArrayList();
+ mNetRequestersPids[i] = new ArrayList<Integer>();
}
mFeatureUsers = new ArrayList<FeatureUser>();
- mNumDnsEntries = 0;
-
mTestMode = SystemProperties.get("cm.test.mode").equals("true")
&& SystemProperties.get("ro.build.type").equals("eng");
@@ -1317,6 +1315,7 @@
Integer currentPid = new Integer(pid);
mNetRequestersPids[usedNetworkType].remove(currentPid);
reassessPidDns(pid, true);
+ flushVmDnsCache();
if (mNetRequestersPids[usedNetworkType].size() != 0) {
if (VDBG) {
log("stopUsingNetworkFeature: net " + networkType + ": " + feature +
@@ -1698,9 +1697,8 @@
* in accordance with network preference policies.
*/
if (!mNetConfigs[prevNetType].isDefault()) {
- List pids = mNetRequestersPids[prevNetType];
- for (int i = 0; i<pids.size(); i++) {
- Integer pid = (Integer)pids.get(i);
+ List<Integer> pids = mNetRequestersPids[prevNetType];
+ for (Integer pid : pids) {
// will remove them because the net's no longer connected
// need to do this now as only now do we know the pids and
// can properly null things that are no longer referenced.
@@ -2250,6 +2248,7 @@
}
}
if (resetDns) {
+ flushVmDnsCache();
if (VDBG) log("resetting DNS cache for " + iface);
try {
mNetd.flushInterfaceDnsCache(iface);
@@ -2416,9 +2415,10 @@
* on the highest priority active net which this process requested.
* If there aren't any, clear it out
*/
- private void reassessPidDns(int myPid, boolean doBump)
+ private void reassessPidDns(int pid, boolean doBump)
{
- if (VDBG) log("reassessPidDns for pid " + myPid);
+ if (VDBG) log("reassessPidDns for pid " + pid);
+ Integer myPid = new Integer(pid);
for(int i : mPriorityList) {
if (mNetConfigs[i].isDefault()) {
continue;
@@ -2428,61 +2428,25 @@
!nt.isTeardownRequested()) {
LinkProperties p = nt.getLinkProperties();
if (p == null) continue;
- List pids = mNetRequestersPids[i];
- for (int j=0; j<pids.size(); j++) {
- Integer pid = (Integer)pids.get(j);
- if (pid.intValue() == myPid) {
- Collection<InetAddress> dnses = p.getDnses();
- writePidDns(dnses, myPid);
- if (doBump) {
- bumpDns();
- }
- return;
+ if (mNetRequestersPids[i].contains(myPid)) {
+ try {
+ mNetd.setDnsIfaceForPid(p.getInterfaceName(), pid);
+ } catch (Exception e) {
+ Slog.e(TAG, "exception reasseses pid dns: " + e);
}
+ return;
}
}
}
// nothing found - delete
- for (int i = 1; ; i++) {
- String prop = "net.dns" + i + "." + myPid;
- if (SystemProperties.get(prop).length() == 0) {
- if (doBump) {
- bumpDns();
- }
- return;
- }
- SystemProperties.set(prop, "");
+ try {
+ mNetd.clearDnsIfaceForPid(pid);
+ } catch (Exception e) {
+ Slog.e(TAG, "exception clear interface from pid: " + e);
}
}
- // return true if results in a change
- private boolean writePidDns(Collection <InetAddress> dnses, int pid) {
- int j = 1;
- boolean changed = false;
- for (InetAddress dns : dnses) {
- String dnsString = dns.getHostAddress();
- if (changed || !dnsString.equals(SystemProperties.get("net.dns" + j + "." + pid))) {
- changed = true;
- SystemProperties.set("net.dns" + j + "." + pid, dns.getHostAddress());
- }
- j++;
- }
- return changed;
- }
-
- private void bumpDns() {
- /*
- * Bump the property that tells the name resolver library to reread
- * the DNS server list from the properties.
- */
- String propVal = SystemProperties.get("net.dnschange");
- int n = 0;
- if (propVal.length() != 0) {
- try {
- n = Integer.parseInt(propVal);
- } catch (NumberFormatException e) {}
- }
- SystemProperties.set("net.dnschange", "" + (n+1));
+ private void flushVmDnsCache() {
/*
* Tell the VMs to toss their DNS caches
*/
@@ -2501,56 +2465,23 @@
}
// Caller must grab mDnsLock.
- private boolean updateDns(String network, String iface,
+ private void updateDns(String network, String iface,
Collection<InetAddress> dnses, String domains) {
- boolean changed = false;
int last = 0;
if (dnses.size() == 0 && mDefaultDns != null) {
- ++last;
- String value = mDefaultDns.getHostAddress();
- if (!value.equals(SystemProperties.get("net.dns1"))) {
- if (DBG) {
- loge("no dns provided for " + network + " - using " + value);
- }
- changed = true;
- SystemProperties.set("net.dns1", value);
+ dnses = new ArrayList();
+ dnses.add(mDefaultDns);
+ if (DBG) {
+ loge("no dns provided for " + network + " - using " + mDefaultDns.getHostAddress());
}
- } else {
- for (InetAddress dns : dnses) {
- ++last;
- String key = "net.dns" + last;
- String value = dns.getHostAddress();
- if (!changed && value.equals(SystemProperties.get(key))) {
- continue;
- }
- if (VDBG) {
- log("adding dns " + value + " for " + network);
- }
- changed = true;
- SystemProperties.set(key, value);
- }
- }
- for (int i = last + 1; i <= mNumDnsEntries; ++i) {
- String key = "net.dns" + i;
- if (VDBG) log("erasing " + key);
- changed = true;
- SystemProperties.set(key, "");
- }
- mNumDnsEntries = last;
- if (SystemProperties.get("net.dns.search").equals(domains) == false) {
- SystemProperties.set("net.dns.search", domains);
- changed = true;
}
- if (changed) {
- try {
- mNetd.setDnsServersForInterface(iface, NetworkUtils.makeStrings(dnses), domains);
- mNetd.setDefaultInterfaceForDns(iface);
- } catch (Exception e) {
- if (DBG) loge("exception setting default dns interface: " + e);
- }
+ try {
+ mNetd.setDnsServersForInterface(iface, NetworkUtils.makeStrings(dnses), domains);
+ mNetd.setDefaultInterfaceForDns(iface);
+ } catch (Exception e) {
+ if (DBG) loge("exception setting default dns interface: " + e);
}
- return changed;
}
private void handleDnsConfigurationChange(int netType) {
@@ -2560,12 +2491,11 @@
LinkProperties p = nt.getLinkProperties();
if (p == null) return;
Collection<InetAddress> dnses = p.getDnses();
- boolean changed = false;
if (mNetConfigs[netType].isDefault()) {
String network = nt.getNetworkInfo().getTypeName();
synchronized (mDnsLock) {
if (!mDnsOverridden) {
- changed = updateDns(network, p.getInterfaceName(), dnses, p.getDomains());
+ updateDns(network, p.getInterfaceName(), dnses, p.getDomains());
}
}
} else {
@@ -2576,13 +2506,16 @@
if (DBG) loge("exception setting dns servers: " + e);
}
// set per-pid dns for attached secondary nets
- List pids = mNetRequestersPids[netType];
- for (int y=0; y< pids.size(); y++) {
- Integer pid = (Integer)pids.get(y);
- changed = writePidDns(dnses, pid.intValue());
+ List<Integer> pids = mNetRequestersPids[netType];
+ for (Integer pid : pids) {
+ try {
+ mNetd.setDnsIfaceForPid(p.getInterfaceName(), pid);
+ } catch (Exception e) {
+ Slog.e(TAG, "exception setting interface for pid: " + e);
+ }
}
}
- if (changed) bumpDns();
+ flushVmDnsCache();
}
}
@@ -2641,7 +2574,7 @@
pw.increaseIndent();
for (int net : mPriorityList) {
String pidString = net + ": ";
- for (Object pid : mNetRequestersPids[net]) {
+ for (Integer pid : mNetRequestersPids[net]) {
pidString = pidString + pid.toString() + ", ";
}
pw.println(pidString);
@@ -3351,14 +3284,10 @@
String domains = buffer.toString().trim();
// Apply DNS changes.
- boolean changed = false;
synchronized (mDnsLock) {
- changed = updateDns("VPN", "VPN", addresses, domains);
+ updateDns("VPN", "VPN", addresses, domains);
mDnsOverridden = true;
}
- if (changed) {
- bumpDns();
- }
// Temporarily disable the default proxy.
synchronized (mDefaultProxyLock) {
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 3aa04b3..2f12112 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -888,7 +888,8 @@
Slog.e(TAG, "--- bind failed: service = " + service + ", conn = " + conn);
return false;
}
- return mContext.bindService(service, conn, flags, mSettings.getCurrentUserId());
+ return mContext.bindServiceAsUser(service, conn, flags,
+ new UserHandle(mSettings.getCurrentUserId()));
}
@Override
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 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 @@
}
}
+ 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 @@
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 @@
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 @@
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/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 0a54593..de8a44b 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -1468,6 +1468,32 @@
}
@Override
+ public void setDnsIfaceForPid(String iface, int pid) throws IllegalStateException {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ try {
+ String cmd = "resolver setifaceforpid " + iface + " " + pid;
+
+ mConnector.execute(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Error communicating with native deamon to set interface for pid" + iface, e);
+ }
+ }
+
+ @Override
+ public void clearDnsIfaceForPid(int pid) throws IllegalStateException {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ try {
+ String cmd = "resolver clearifaceforpid " + pid;
+
+ mConnector.execute(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Error communicating with native deamon to clear interface for pid " + pid, e);
+ }
+ }
+
+ /** {@inheritDoc} */
public void monitor() {
if (mConnector != null) {
mConnector.monitor();
diff --git a/services/java/com/android/server/ServiceWatcher.java b/services/java/com/android/server/ServiceWatcher.java
index 2e7c6d1..bca7faa 100644
--- a/services/java/com/android/server/ServiceWatcher.java
+++ b/services/java/com/android/server/ServiceWatcher.java
@@ -176,8 +176,8 @@
mPackageName = packageName;
mVersion = version;
if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ")");
- mContext.bindService(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
- | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_VISIBLE, mCurrentUserId);
+ mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
+ | Context.BIND_NOT_VISIBLE, new UserHandle(mCurrentUserId));
}
public static boolean isSignatureMatch(Signature[] signatures,
diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java
index d0d8428..7dd9988 100644
--- a/services/java/com/android/server/TextServicesManagerService.java
+++ b/services/java/com/android/server/TextServicesManagerService.java
@@ -248,7 +248,8 @@
Slog.e(TAG, "--- bind failed: service = " + service + ", conn = " + conn);
return false;
}
- return mContext.bindService(service, conn, flags, mSettings.getCurrentUserId());
+ return mContext.bindServiceAsUser(service, conn, flags,
+ new UserHandle(mSettings.getCurrentUserId()));
}
private void unbindServiceLocked() {
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 21a1956..6823f136 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -885,7 +885,8 @@
Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
0, null, new UserHandle(serviceUserId)));
- if (!mContext.bindService(intent, newConn, Context.BIND_AUTO_CREATE, serviceUserId)) {
+ if (!mContext.bindServiceAsUser(intent, newConn, Context.BIND_AUTO_CREATE,
+ new UserHandle(serviceUserId))) {
String msg = "Unable to bind service: "
+ componentName;
if (fromUser) {
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 3e3e7dc..0725df0 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1592,7 +1592,8 @@
*/
public boolean bind() {
if (!mIsAutomation && mService == null) {
- return mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE, mUserId);
+ return mContext.bindServiceAsUser(mIntent, this, Context.BIND_AUTO_CREATE,
+ new UserHandle(mUserId));
}
return false;
}
diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java
index 150df9e..88603dc 100644
--- a/services/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/java/com/android/server/accounts/AccountManagerService.java
@@ -1903,7 +1903,8 @@
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
}
- if (!mContext.bindService(intent, this, Context.BIND_AUTO_CREATE, mAccounts.userId)) {
+ if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
+ new UserHandle(mAccounts.userId))) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 8297988e..d8e199b 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1623,7 +1623,7 @@
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 @@
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/services/java/com/android/server/content/SyncManager.java b/services/java/com/android/server/content/SyncManager.java
index cd66cf2..b3f9bf1 100644
--- a/services/java/com/android/server/content/SyncManager.java
+++ b/services/java/com/android/server/content/SyncManager.java
@@ -1061,10 +1061,10 @@
mContext, 0, new Intent(Settings.ACTION_SYNC_SETTINGS), 0,
null, new UserHandle(userId)));
mBound = true;
- final boolean bindResult = mContext.bindService(intent, this,
+ final boolean bindResult = mContext.bindServiceAsUser(intent, this,
Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
| Context.BIND_ALLOW_OOM_MANAGEMENT,
- mSyncOperation.userId);
+ new UserHandle(mSyncOperation.userId));
if (!bindResult) {
mBound = false;
}
diff --git a/services/java/com/android/server/dreams/DreamController.java b/services/java/com/android/server/dreams/DreamController.java
index 45ae2c5..85ef33e 100644
--- a/services/java/com/android/server/dreams/DreamController.java
+++ b/services/java/com/android/server/dreams/DreamController.java
@@ -116,8 +116,8 @@
intent.setComponent(name);
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
try {
- if (!mContext.bindService(intent, mCurrentDream,
- Context.BIND_AUTO_CREATE, userId)) {
+ if (!mContext.bindServiceAsUser(intent, mCurrentDream,
+ Context.BIND_AUTO_CREATE, new UserHandle(userId))) {
Slog.e(TAG, "Unable to bind dream service: " + intent);
stopDream();
return;
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 2238f17..9b3795a 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -482,8 +482,8 @@
" DefaultContainerService");
Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
- if (mContext.bindService(service, mDefContainerConn,
- Context.BIND_AUTO_CREATE, UserHandle.USER_OWNER)) {
+ if (mContext.bindServiceAsUser(service, mDefContainerConn,
+ Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
mBound = true;
return true;
@@ -2944,6 +2944,74 @@
}
@Override
+ public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
+ String[] permissions, int flags, String lastRead, int userId) {
+ if (!sUserManager.exists(userId)) return null;
+ final ParceledListSlice<PackageInfo> list = new ParceledListSlice<PackageInfo>();
+ final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
+
+ // writer
+ synchronized (mPackages) {
+ ArrayList<String> keysList = new ArrayList<String>();
+ if (listUninstalled) {
+ for (PackageSetting ps : mSettings.mPackages.values()) {
+ for (String perm : permissions) {
+ if (ps.grantedPermissions.contains(perm)) {
+ keysList.add(ps.name);
+ break;
+ }
+ }
+ }
+ } else {
+ for (PackageParser.Package pkg : mPackages.values()) {
+ PackageSetting ps = (PackageSetting)pkg.mExtras;
+ if (ps != null) {
+ for (String perm : permissions) {
+ if (ps.grantedPermissions.contains(perm)) {
+ keysList.add(ps.name);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ String[] keys = new String[keysList.size()];
+ keysList.toArray(keys);
+ Arrays.sort(keys);
+ int i = getContinuationPoint(keys, lastRead);
+ final int N = keys.length;
+
+ while (i < N) {
+ final String packageName = keys[i++];
+
+ PackageInfo pi = null;
+ if (listUninstalled) {
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps != null) {
+ pi = generatePackageInfoFromSettingsLPw(ps.name, flags, userId);
+ }
+ } else {
+ final PackageParser.Package p = mPackages.get(packageName);
+ if (p != null) {
+ pi = generatePackageInfo(p, flags, userId);
+ }
+ }
+
+ if (pi != null && list.append(pi)) {
+ break;
+ }
+ }
+
+ if (i == N) {
+ list.setLastSlice(true);
+ }
+ }
+
+ return list;
+ }
+
+ @Override
public ParceledListSlice<ApplicationInfo> getInstalledApplications(int flags,
String lastRead, int userId) {
if (!sUserManager.exists(userId)) return null;
@@ -8409,8 +8477,8 @@
users = new int[] { userId };
}
final ClearStorageConnection conn = new ClearStorageConnection();
- if (mContext.bindService(
- containerIntent, conn, Context.BIND_AUTO_CREATE, UserHandle.USER_OWNER)) {
+ if (mContext.bindServiceAsUser(
+ containerIntent, conn, Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
try {
for (int curUser : users) {
long timeout = SystemClock.uptimeMillis() + 5000;
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 @@
*/
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 @@
}
@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 @@
}
@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/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 1f815e7..3097811 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -411,7 +411,8 @@
/** @hide */
@Override
- public boolean bindService(Intent service, ServiceConnection conn, int flags, int userId) {
+ public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
+ UserHandle user) {
throw new UnsupportedOperationException();
}
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 @@
* @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 @@
}
@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 @@
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 @@
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/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 5ee52de..20a26ab 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -146,6 +146,12 @@
throw new UnsupportedOperationException();
}
+ @Override
+ public List<PackageInfo> getPackagesHoldingPermissions(String[] permissions,
+ int flags) {
+ throw new UnsupportedOperationException();
+ }
+
/** @hide */
@Override
public List<PackageInfo> getInstalledPackages(int flags, int userId) {
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index f0c3b22..c7cd975 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -237,7 +237,7 @@
Log.i(TAG, "Service disconnected " + name);
}
};
- if (bindService(intent, conn, Context.BIND_AUTO_CREATE, 0)) {
+ if (bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
mConnections.add(conn);
} else {
Toast.makeText(ActivityTestMain.this, "Failed to bind",
@@ -260,7 +260,8 @@
Log.i(TAG, "Service disconnected " + name);
}
};
- if (bindService(intent, conn, Context.BIND_AUTO_CREATE, mSecondUser)) {
+ if (bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE,
+ new UserHandle(mSecondUser))) {
mConnections.add(conn);
} else {
Toast.makeText(ActivityTestMain.this, "Failed to bind",
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 @@
*/
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 @@
}
@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 @@
}
@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;
}