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>&lt;item&gt;</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 &lt;integer-array&gt;} 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>&lt;item&gt;</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 &lt;plurals&gt;} 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;
     }