Merge "Second pass tying into dns cache per interface"
diff --git a/Android.mk b/Android.mk
index 462d6ac..f45f977 100644
--- a/Android.mk
+++ b/Android.mk
@@ -123,7 +123,6 @@
 	core/java/android/hardware/usb/IUsbManager.aidl \
 	core/java/android/net/IConnectivityManager.aidl \
 	core/java/android/net/INetworkManagementEventObserver.aidl \
-	core/java/android/net/IThrottleManager.aidl \
 	core/java/android/net/INetworkPolicyListener.aidl \
 	core/java/android/net/INetworkPolicyManager.aidl \
 	core/java/android/net/INetworkStatsService.aidl \
diff --git a/api/current.txt b/api/current.txt
index 1cf14aa..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);
@@ -28963,7 +28965,9 @@
     ctor public QuickContactBadge(android.content.Context, android.util.AttributeSet);
     ctor public QuickContactBadge(android.content.Context, android.util.AttributeSet, int);
     method public void assignContactFromEmail(java.lang.String, boolean);
+    method public void assignContactFromEmail(java.lang.String, boolean, android.os.Bundle);
     method public void assignContactFromPhone(java.lang.String, boolean);
+    method public void assignContactFromPhone(java.lang.String, boolean, android.os.Bundle);
     method public void assignContactUri(android.net.Uri);
     method public void onClick(android.view.View);
     method public void setExcludeMimes(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/AlertDialog.java b/core/java/android/app/AlertDialog.java
index 6ab2bd1..10d5e25 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -90,7 +90,7 @@
 
     /**
      * Special theme constant for {@link #AlertDialog(Context, int)}: use
-     * the device's default alert theme with a dark background.
+     * the device's default alert theme with a light background.
      */
     public static final int THEME_DEVICE_DEFAULT_LIGHT = 5;
     
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 8ef708c..4925bf1 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -63,8 +63,6 @@
 import android.net.IConnectivityManager;
 import android.net.INetworkPolicyManager;
 import android.net.NetworkPolicyManager;
-import android.net.ThrottleManager;
-import android.net.IThrottleManager;
 import android.net.Uri;
 import android.net.nsd.INsdManager;
 import android.net.nsd.NsdManager;
@@ -474,12 +472,6 @@
                     return new TelephonyManager(ctx.getOuterContext());
                 }});
 
-        registerService(THROTTLE_SERVICE, new StaticServiceFetcher() {
-                public Object createStaticService() {
-                    IBinder b = ServiceManager.getService(THROTTLE_SERVICE);
-                    return new ThrottleManager(IThrottleManager.Stub.asInterface(b));
-                }});
-
         registerService(UI_MODE_SERVICE, new ServiceFetcher() {
                 public Object createService(ContextImpl ctx) {
                     return new UiModeManager();
@@ -631,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
@@ -1411,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");
@@ -1438,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/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index b351811..66fc816 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -139,9 +139,9 @@
     public static final int USES_POLICY_DISABLE_CAMERA = 8;
 
     /**
-     * A type of policy that this device admin can use: disables use of keyguard widgets.
+     * A type of policy that this device admin can use: disables use of keyguard features.
      *
-     * <p>To control this policy, the device admin must have a "disable-keyguard-widgets"
+     * <p>To control this policy, the device admin must have a "disable-keyguard-features"
      * tag in the "uses-policies" section of its meta-data.
      */
     public static final int USES_POLICY_DISABLE_KEYGUARD_FEATURES = 9;
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index 1500b00..4f42d50 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -117,7 +117,7 @@
  *
  * <ul>
  * <li><p>The Intent namespace is global.  Make sure that Intent action names and
- * other strings are written in a namespace you own, or else you may inadvertantly
+ * other strings are written in a namespace you own, or else you may inadvertently
  * conflict with other applications.
  * <li><p>When you use {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)},
  * <em>any</em> application may send broadcasts to that registered receiver.  You can
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 41d470b..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.");
     }
 
@@ -1993,17 +1993,6 @@
 
     /**
      * Use with {@link #getSystemService} to retrieve a {@link
-     * android.net.ThrottleManager} for handling management of
-     * throttling.
-     *
-     * @hide
-     * @see #getSystemService
-     * @see android.net.ThrottleManager
-     */
-    public static final String THROTTLE_SERVICE = "throttle";
-
-    /**
-     * Use with {@link #getSystemService} to retrieve a {@link
      * android.os.IUpdateLock} for managing runtime sequences that
      * must not be interrupted by headless OTA application or similar.
      *
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/hardware/SensorEventListener.java b/core/java/android/hardware/SensorEventListener.java
index 716d0d4..677d244 100644
--- a/core/java/android/hardware/SensorEventListener.java
+++ b/core/java/android/hardware/SensorEventListener.java
@@ -30,7 +30,7 @@
      * 
      * <p><b>NOTE:</b> The application doesn't own the
      * {@link android.hardware.SensorEvent event}
-     * object passed as a parameter and therefore cannot hold on o it.
+     * object passed as a parameter and therefore cannot hold on to it.
      * The object may be part of an internal pool and may be reused by
      * the framework.
      *
diff --git a/core/java/android/net/IThrottleManager.aidl b/core/java/android/net/IThrottleManager.aidl
deleted file mode 100644
index a12469d..0000000
--- a/core/java/android/net/IThrottleManager.aidl
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * Copyright (c) 2010, 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.net;
-
-import android.os.IBinder;
-
-/**
- * Interface that answers queries about data transfer amounts and throttling
- */
-/** {@hide} */
-interface IThrottleManager
-{
-    long getByteCount(String iface, int dir, int period, int ago);
-
-    int getThrottle(String iface);
-
-    long getResetTime(String iface);
-
-    long getPeriodStartTime(String iface);
-
-    long getCliffThreshold(String iface, int cliff);
-
-    int getCliffLevel(String iface, int cliff);
-
-    String getHelpUri();
-}
diff --git a/core/java/android/net/ThrottleManager.java b/core/java/android/net/ThrottleManager.java
deleted file mode 100644
index 5fdac58..0000000
--- a/core/java/android/net/ThrottleManager.java
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright (C) 2008 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.net;
-
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.os.Binder;
-import android.os.RemoteException;
-
-/**
- * Class that handles throttling.  It provides read/write numbers per interface
- * and methods to apply throttled rates.
- * {@hide}
- */
-public class ThrottleManager
-{
-    /**
-     * Broadcast each polling period to indicate new data counts.
-     *
-     * Includes four extras:
-     * EXTRA_CYCLE_READ - a long of the read bytecount for the current cycle
-     * EXTRA_CYCLE_WRITE -a long of the write bytecount for the current cycle
-     * EXTRA_CYLCE_START -a long of MS for the cycle start time
-     * EXTRA_CYCLE_END   -a long of MS for the cycle stop time
-     * {@hide}
-     */
-    public static final String THROTTLE_POLL_ACTION = "android.net.thrott.POLL_ACTION";
-    /**
-     * The lookup key for a long for the read bytecount for this period.  Retrieve with
-     * {@link android.content.Intent#getLongExtra(String)}.
-     * {@hide}
-     */
-    public static final String EXTRA_CYCLE_READ = "cycleRead";
-    /**
-     * contains a long of the number of bytes written in the cycle
-     * {@hide}
-     */
-    public static final String EXTRA_CYCLE_WRITE = "cycleWrite";
-    /**
-     * contains a long of the number of bytes read in the cycle
-     * {@hide}
-     */
-    public static final String EXTRA_CYCLE_START = "cycleStart";
-    /**
-     * contains a long of the ms since 1970 used to init a calendar, etc for the end
-     * of the cycle
-     * {@hide}
-     */
-    public static final String EXTRA_CYCLE_END = "cycleEnd";
-
-    /**
-     * Broadcast when the thottle level changes.
-     * {@hide}
-     */
-    public static final String THROTTLE_ACTION = "android.net.thrott.THROTTLE_ACTION";
-    /**
-     * int of the current bandwidth in TODO
-     * {@hide}
-     */
-    public static final String EXTRA_THROTTLE_LEVEL = "level";
-
-    /**
-     * Broadcast on boot and whenever the settings change.
-     * {@hide}
-     */
-    public static final String POLICY_CHANGED_ACTION = "android.net.thrott.POLICY_CHANGED_ACTION";
-
-    // {@hide}
-    public static final int DIRECTION_TX = 0;
-    // {@hide}
-    public static final int DIRECTION_RX = 1;
-
-    // {@hide}
-    public static final int PERIOD_CYCLE  = 0;
-    // {@hide}
-    public static final int PERIOD_YEAR   = 1;
-    // {@hide}
-    public static final int PERIOD_MONTH  = 2;
-    // {@hide}
-    public static final int PERIOD_WEEK   = 3;
-    // @hide
-    public static final int PERIOD_7DAY   = 4;
-    // @hide
-    public static final int PERIOD_DAY    = 5;
-    // @hide
-    public static final int PERIOD_24HOUR = 6;
-    // @hide
-    public static final int PERIOD_HOUR   = 7;
-    // @hide
-    public static final int PERIOD_60MIN  = 8;
-    // @hide
-    public static final int PERIOD_MINUTE = 9;
-    // @hide
-    public static final int PERIOD_60SEC  = 10;
-    // @hide
-    public static final int PERIOD_SECOND = 11;
-
-    
-
-    /**
-     * returns a long of the ms from the epoch to the time the current cycle ends for the
-     * named interface
-     * {@hide}
-     */
-    public long getResetTime(String iface) {
-        try {
-            return mService.getResetTime(iface);
-        } catch (RemoteException e) {
-            return -1;
-        }
-    }
-
-    /**
-     * returns a long of the ms from the epoch to the time the current cycle started for the
-     * named interface
-     * {@hide}
-     */
-    public long getPeriodStartTime(String iface) {
-        try {
-            return mService.getPeriodStartTime(iface);
-        } catch (RemoteException e) {
-            return -1;
-        }
-    }
-
-    /**
-     * returns a long of the byte count either read or written on the named interface
-     * for the period described.  Direction is either DIRECTION_RX or DIRECTION_TX and
-     * period may only be PERIOD_CYCLE for the current cycle (other periods may be supported
-     * in the future).  Ago indicates the number of periods in the past to lookup - 0 means
-     * the current period, 1 is the last one, 2 was two periods ago..
-     * {@hide}
-     */
-    public long getByteCount(String iface, int direction, int period, int ago) {
-        try {
-            return mService.getByteCount(iface, direction, period, ago);
-        } catch (RemoteException e) {
-            return -1;
-        }
-    }
-
-    /**
-     * returns the number of bytes read+written after which a particular cliff
-     * takes effect on the named iface.  Currently only cliff #1 is supported (1 step)
-     * {@hide}
-     */
-    public long getCliffThreshold(String iface, int cliff) {
-        try {
-            return mService.getCliffThreshold(iface, cliff);
-        } catch (RemoteException e) {
-            return -1;
-        }
-    }
-
-    /**
-     * returns the thottling bandwidth (bps) for a given cliff # on the named iface.
-     * only cliff #1 is currently supported.
-     * {@hide}
-     */
-    public int getCliffLevel(String iface, int cliff) {
-        try {
-            return mService.getCliffLevel(iface, cliff);
-        } catch (RemoteException e) {
-            return -1;
-        }
-    }
-
-    /**
-     * returns the help URI for throttling
-     * {@hide}
-     */
-    public String getHelpUri() {
-        try {
-            return mService.getHelpUri();
-        } catch (RemoteException e) {
-            return null;
-        }
-    }
-
-
-    private IThrottleManager mService;
-
-    /**
-     * Don't allow use of default constructor.
-     */
-    @SuppressWarnings({"UnusedDeclaration"})
-    private ThrottleManager() {
-    }
-
-    /**
-     * {@hide}
-     */
-    public ThrottleManager(IThrottleManager service) {
-        if (service == null) {
-            throw new IllegalArgumentException(
-                "ThrottleManager() cannot be constructed with null service");
-        }
-        mService = service;
-    }
-}
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index cb0863e..1044931 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -306,23 +306,6 @@
     boolean isBandwidthControlEnabled();
 
     /**
-     * Configures bandwidth throttling on an interface.
-     */
-    void setInterfaceThrottle(String iface, int rxKbps, int txKbps);
-
-    /**
-     * Returns the currently configured RX throttle values
-     * for the specified interface
-     */
-    int getInterfaceRxThrottle(String iface);
-
-    /**
-     * Returns the currently configured TX throttle values
-     * for the specified interface
-     */
-    int getInterfaceTxThrottle(String iface);
-
-    /**
      * Sets idletimer for an interface.
      *
      * This either initializes a new idletimer or increases its
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 788ab74..d69fef0 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -2087,6 +2087,10 @@
                                         + "Parcelable.Creator object called "
                                         + " CREATOR on class " + name);
                 }
+                catch (NullPointerException e) {
+                    throw new BadParcelableException("Parcelable protocol requires "
+                            + "the CREATOR object to be static on class " + name);
+                }
                 if (creator == null) {
                     throw new BadParcelableException("Parcelable protocol requires a "
                                         + "Parcelable.Creator object called "
diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java
index ba77df7..69118fe 100644
--- a/core/java/android/os/WorkSource.java
+++ b/core/java/android/os/WorkSource.java
@@ -1,6 +1,6 @@
 package android.os;
 
-import com.android.internal.util.ArrayUtils;
+import android.util.Log;
 
 import java.util.Arrays;
 
@@ -10,8 +10,12 @@
  * defined; this is an opaque container.
  */
 public class WorkSource implements Parcelable {
+    static final String TAG = "WorkSource";
+    static final boolean DEBUG = true;
+
     int mNum;
     int[] mUids;
+    String[] mNames;
 
     /**
      * Internal statics to avoid object allocations in some operations.
@@ -47,8 +51,10 @@
         mNum = orig.mNum;
         if (orig.mUids != null) {
             mUids = orig.mUids.clone();
+            mNames = orig.mNames != null ? orig.mNames.clone() : null;
         } else {
             mUids = null;
+            mNames = null;
         }
     }
 
@@ -56,11 +62,23 @@
     public WorkSource(int uid) {
         mNum = 1;
         mUids = new int[] { uid, 0 };
+        mNames = null;
+    }
+
+    /** @hide */
+    public WorkSource(int uid, String name) {
+        if (name == null) {
+            throw new NullPointerException("Name can't be null");
+        }
+        mNum = 1;
+        mUids = new int[] { uid, 0 };
+        mNames = new String[] { name, null };
     }
 
     WorkSource(Parcel in) {
         mNum = in.readInt();
         mUids = in.createIntArray();
+        mNames = in.createStringArray();
     }
 
     /** @hide */
@@ -73,6 +91,11 @@
         return mUids[index];
     }
 
+    /** @hide */
+    public String getName(int index) {
+        return mNames != null ? mNames[index] : null;
+    }
+
     /**
      * Clear this WorkSource to be empty.
      */
@@ -91,6 +114,11 @@
         for (int i = 0; i < mNum; i++) {
             result = ((result << 4) | (result >>> 28)) ^ mUids[i];
         }
+        if (mNames != null) {
+            for (int i = 0; i < mNum; i++) {
+                result = ((result << 4) | (result >>> 28)) ^ mNames[i].hashCode();
+            }
+        }
         return result;
     }
 
@@ -106,10 +134,15 @@
         }
         final int[] uids1 = mUids;
         final int[] uids2 = other.mUids;
+        final String[] names1 = mNames;
+        final String[] names2 = other.mNames;
         for (int i=0; i<N; i++) {
             if (uids1[i] != uids2[i]) {
                 return true;
             }
+            if (names1 != null && names2 != null && !names1[i].equals(names2[i])) {
+                return true;
+            }
         }
         return false;
     }
@@ -131,8 +164,18 @@
             } else {
                 mUids = other.mUids.clone();
             }
+            if (other.mNames != null) {
+                if (mNames != null && mNames.length >= mNum) {
+                    System.arraycopy(other.mNames, 0, mNames, 0, mNum);
+                } else {
+                    mNames = other.mNames.clone();
+                }
+            } else {
+                mNames = null;
+            }
         } else {
             mUids = null;
+            mNames = null;
         }
     }
 
@@ -141,6 +184,22 @@
         mNum = 1;
         if (mUids == null) mUids = new int[2];
         mUids[0] = uid;
+        mNames = null;
+    }
+
+    /** @hide */
+    public void set(int uid, String name) {
+        if (name == null) {
+            throw new NullPointerException("Name can't be null");
+        }
+        mNum = 1;
+        if (mUids == null) {
+            mUids = new int[2];
+            mNames = new String[2];
+        }
+        mUids[0] = uid;
+        mNames[0] = name;
+        mNames = null;
     }
 
     /** @hide */
@@ -182,10 +241,49 @@
 
     /** @hide */
     public boolean add(int uid) {
-        synchronized (sTmpWorkSource) {
-            sTmpWorkSource.mUids[0] = uid;
-            return updateLocked(sTmpWorkSource, false, false);
+        if (mNum <= 0) {
+            mNames = null;
+            insert(0, uid);
+            return true;
         }
+        if (mNames != null) {
+            throw new IllegalArgumentException("Adding without name to named " + this);
+        }
+        int i = Arrays.binarySearch(mUids, 0, mNum, uid);
+        if (DEBUG) Log.d(TAG, "Adding uid " + uid + " to " + this + ": binsearch res = " + i);
+        if (i >= 0) {
+            return false;
+        }
+        insert(-i-1, uid);
+        return true;
+    }
+
+    /** @hide */
+    public boolean add(int uid, String name) {
+        if (mNum <= 0) {
+            insert(0, uid, name);
+            return true;
+        }
+        if (mNames == null) {
+            throw new IllegalArgumentException("Adding name to unnamed " + this);
+        }
+        int i;
+        for (i=0; i<mNum; i++) {
+            if (mUids[i] > uid) {
+                break;
+            }
+            if (mUids[i] == uid) {
+                int diff = mNames[i].compareTo(name); 
+                if (diff > 0) {
+                    break;
+                }
+                if (diff == 0) {
+                    return false;
+                }
+            }
+        }
+        insert(i, uid, name);
+        return true;
     }
 
     /** @hide */
@@ -199,19 +297,102 @@
     }
 
     public boolean remove(WorkSource other) {
+        if (mNum <= 0 || other.mNum <= 0) {
+            return false;
+        }
+        if (mNames == null && other.mNames == null) {
+            return removeUids(other);
+        } else {
+            if (mNames == null) {
+                throw new IllegalArgumentException("Other " + other + " has names, but target "
+                        + this + " does not");
+            }
+            if (other.mNames == null) {
+                throw new IllegalArgumentException("Target " + this + " has names, but other "
+                        + other + " does not");
+            }
+            return removeUidsAndNames(other);
+        }
+    }
+
+    /** @hide */
+    public WorkSource stripNames() {
+        if (mNum <= 0) {
+            return new WorkSource();
+        }
+        WorkSource result = new WorkSource();
+        int lastUid = -1;
+        for (int i=0; i<mNum; i++) {
+            int uid = mUids[i];
+            if (i == 0 || lastUid != uid) {
+                result.add(uid);
+            }
+        }
+        return result;
+    }
+
+    private boolean removeUids(WorkSource other) {
         int N1 = mNum;
         final int[] uids1 = mUids;
         final int N2 = other.mNum;
         final int[] uids2 = other.mUids;
         boolean changed = false;
-        int i1 = 0;
-        for (int i2=0; i2<N2 && i1<N1; i2++) {
+        int i1 = 0, i2 = 0;
+        if (DEBUG) Log.d(TAG, "Remove " + other + " from " + this);
+        while (i1 < N1 && i2 < N2) {
+            if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
+                    + " of " + N2);
             if (uids2[i2] == uids1[i1]) {
+                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
+                        + ": remove " + uids1[i1]);
                 N1--;
+                changed = true;
                 if (i1 < N1) System.arraycopy(uids1, i1+1, uids1, i1, N1-i1);
-            }
-            while (i1 < N1 && uids2[i2] > uids1[i1]) {
+                i2++;
+            } else if (uids2[i2] > uids1[i1]) {
+                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i1");
                 i1++;
+            } else {
+                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i2");
+                i2++;
+            }
+        }
+
+        mNum = N1;
+
+        return changed;
+    }
+
+    private boolean removeUidsAndNames(WorkSource other) {
+        int N1 = mNum;
+        final int[] uids1 = mUids;
+        final String[] names1 = mNames;
+        final int N2 = other.mNum;
+        final int[] uids2 = other.mUids;
+        final String[] names2 = other.mNames;
+        boolean changed = false;
+        int i1 = 0, i2 = 0;
+        if (DEBUG) Log.d(TAG, "Remove " + other + " from " + this);
+        while (i1 < N1 && i2 < N2) {
+            if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
+                    + " of " + N2 + ": " + uids1[i1] + " " + names1[i1]);
+            if (uids2[i2] == uids1[i1] && names2[i2].equals(names1[i1])) {
+                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
+                        + ": remove " + uids1[i1] + " " + names1[i1]);
+                N1--;
+                changed = true;
+                if (i1 < N1) {
+                    System.arraycopy(uids1, i1+1, uids1, i1, N1-i1);
+                    System.arraycopy(names1, i1+1, names1, i1, N1-i1);
+                }
+                i2++;
+            } else if (uids2[i2] > uids1[i1]
+                    || (uids2[i2] == uids1[i1] && names2[i2].compareTo(names1[i1]) > 0)) {
+                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i1");
+                i1++;
+            } else {
+                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i2");
+                i2++;
             }
         }
 
@@ -221,20 +402,50 @@
     }
 
     private boolean updateLocked(WorkSource other, boolean set, boolean returnNewbs) {
+        if (mNames == null && other.mNames == null) {
+            return updateUidsLocked(other, set, returnNewbs);
+        } else {
+            if (mNum > 0 && mNames == null) {
+                throw new IllegalArgumentException("Other " + other + " has names, but target "
+                        + this + " does not");
+            }
+            if (other.mNum > 0 && other.mNames == null) {
+                throw new IllegalArgumentException("Target " + this + " has names, but other "
+                        + other + " does not");
+            }
+            return updateUidsAndNamesLocked(other, set, returnNewbs);
+        }
+    }
+
+    private static WorkSource addWork(WorkSource cur, int newUid) {
+        if (cur == null) {
+            return new WorkSource(newUid);
+        }
+        cur.insert(cur.mNum, newUid);
+        return cur;
+    }
+
+    private boolean updateUidsLocked(WorkSource other, boolean set, boolean returnNewbs) {
         int N1 = mNum;
         int[] uids1 = mUids;
         final int N2 = other.mNum;
         final int[] uids2 = other.mUids;
         boolean changed = false;
-        int i1 = 0;
-        for (int i2=0; i2<N2; i2++) {
-            if (i1 >= N1 || uids2[i2] < uids1[i1]) {
+        int i1 = 0, i2 = 0;
+        if (DEBUG) Log.d(TAG, "Update " + this + " with " + other + " set=" + set
+                + " returnNewbs=" + returnNewbs);
+        while (i1 < N1 || i2 < N2) {
+            if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
+                    + " of " + N2);
+            if (i1 >= N1 || (i2 < N2 && uids2[i2] < uids1[i1])) {
                 // Need to insert a new uid.
+                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
+                        + ": insert " + uids2[i2]);
                 changed = true;
                 if (uids1 == null) {
                     uids1 = new int[4];
                     uids1[0] = uids2[i2];
-                } else if (i1 >= uids1.length) {
+                } else if (N1 >= uids1.length) {
                     int[] newuids = new int[(uids1.length*3)/2];
                     if (i1 > 0) System.arraycopy(uids1, 0, newuids, 0, i1);
                     if (i1 < N1) System.arraycopy(uids1, i1, newuids, i1+1, N1-i1);
@@ -245,39 +456,37 @@
                     uids1[i1] = uids2[i2];
                 }
                 if (returnNewbs) {
-                    if (sNewbWork == null) {
-                        sNewbWork = new WorkSource(uids2[i2]);
-                    } else {
-                        sNewbWork.addLocked(uids2[i2]);
-                    }
+                    sNewbWork = addWork(sNewbWork, uids2[i2]);
                 }
                 N1++;
                 i1++;
+                i2++;
             } else {
                 if (!set) {
                     // Skip uids that already exist or are not in 'other'.
-                    do {
-                        i1++;
-                    } while (i1 < N1 && uids2[i2] >= uids1[i1]);
+                    if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip");
+                    if (i2 < N2 && uids2[i2] == uids1[i1]) {
+                        i2++;
+                    }
+                    i1++;
                 } else {
                     // Remove any uids that don't exist in 'other'.
                     int start = i1;
-                    while (i1 < N1 && uids2[i2] > uids1[i1]) {
-                        if (sGoneWork == null) {
-                            sGoneWork = new WorkSource(uids1[i1]);
-                        } else {
-                            sGoneWork.addLocked(uids1[i1]);
-                        }
+                    while (i1 < N1 && (i2 >= N2 || uids2[i2] > uids1[i1])) {
+                        if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + ": remove " + uids1[i1]);
+                        sGoneWork = addWork(sGoneWork, uids1[i1]);
                         i1++;
                     }
                     if (start < i1) {
-                        System.arraycopy(uids1, i1, uids1, start, i1-start);
+                        System.arraycopy(uids1, i1, uids1, start, N1-i1);
                         N1 -= i1-start;
                         i1 = start;
                     }
                     // If there is a matching uid, skip it.
-                    if (i1 < N1 && uids2[i1] == uids1[i1]) {
+                    if (i1 < N1 && i2 < N2 && uids2[i2] == uids1[i1]) {
+                        if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip");
                         i1++;
+                        i2++;
                     }
                 }
             }
@@ -289,21 +498,145 @@
         return changed;
     }
 
-    private void addLocked(int uid) {
+    /**
+     * Returns 0 if equal, negative if 'this' is before 'other', positive if 'this' is after 'other'.
+     */
+    private int compare(WorkSource other, int i1, int i2) {
+        final int diff = mUids[i1] - other.mUids[i2];
+        if (diff != 0) {
+            return diff;
+        }
+        return mNames[i1].compareTo(other.mNames[i2]);
+    }
+
+    private static WorkSource addWork(WorkSource cur, int newUid, String newName) {
+        if (cur == null) {
+            return new WorkSource(newUid, newName);
+        }
+        cur.insert(cur.mNum, newUid, newName);
+        return cur;
+    }
+
+    private boolean updateUidsAndNamesLocked(WorkSource other, boolean set, boolean returnNewbs) {
+        final int N2 = other.mNum;
+        final int[] uids2 = other.mUids;
+        String[] names2 = other.mNames;
+        boolean changed = false;
+        int i1 = 0, i2 = 0;
+        if (DEBUG) Log.d(TAG, "Update " + this + " with " + other + " set=" + set
+                + " returnNewbs=" + returnNewbs);
+        while (i1 < mNum || i2 < N2) {
+            if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + mNum + ", other @ " + i2
+                    + " of " + N2);
+            int diff = -1;
+            if (i1 >= mNum || (i2 < N2 && (diff=compare(other, i1, i2)) > 0)) {
+                // Need to insert a new uid.
+                changed = true;
+                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum
+                        + ": insert " + uids2[i2] + " " + names2[i2]);
+                insert(i1, uids2[i2], names2[i2]);
+                if (returnNewbs) {
+                    sNewbWork = addWork(sNewbWork, uids2[i2], names2[i2]);
+                }
+                i1++;
+                i2++;
+            } else {
+                if (!set) {
+                    if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum + ": skip");
+                    if (i2 < N2 && diff == 0) {
+                        i2++;
+                    }
+                    i1++;
+                } else {
+                    // Remove any uids that don't exist in 'other'.
+                    int start = i1;
+                    while (diff < 0) {
+                        if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + ": remove " + mUids[i1]
+                                + " " + mNames[i1]);
+                        sGoneWork = addWork(sGoneWork, mUids[i1], mNames[i1]);
+                        i1++;
+                        if (i1 >= mNum) {
+                            break;
+                        }
+                        diff = i2 < N2 ? compare(other, i1, i2) : -1;
+                    }
+                    if (start < i1) {
+                        System.arraycopy(mUids, i1, mUids, start, mNum-i1);
+                        System.arraycopy(mNames, i1, mNames, start, mNum-i1);
+                        mNum -= i1-start;
+                        i1 = start;
+                    }
+                    // If there is a matching uid, skip it.
+                    if (i1 < mNum && diff == 0) {
+                        if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum + ": skip");
+                        i1++;
+                        i2++;
+                    }
+                }
+            }
+        }
+
+        return changed;
+    }
+
+    private void insert(int index, int uid)  {
+        if (DEBUG) Log.d(TAG, "Insert in " + this + " @ " + index + " uid " + uid);
         if (mUids == null) {
             mUids = new int[4];
             mUids[0] = uid;
             mNum = 1;
-            return;
-        }
-        if (mNum >= mUids.length) {
+        } else if (mNum >= mUids.length) {
             int[] newuids = new int[(mNum*3)/2];
-            System.arraycopy(mUids, 0, newuids, 0, mNum);
+            if (index > 0) {
+                System.arraycopy(mUids, 0, newuids, 0, index);
+            }
+            if (index < mNum) {
+                System.arraycopy(mUids, index, newuids, index+1, mNum-index);
+            }
             mUids = newuids;
+            mUids[index] = uid;
+            mNum++;
+        } else {
+            if (index < mNum) {
+                System.arraycopy(mUids, index, mUids, index+1, mNum-index);
+            }
+            mUids[index] = uid;
+            mNum++;
         }
+    }
 
-        mUids[mNum] = uid;
-        mNum++;
+    private void insert(int index, int uid, String name)  {
+        if (mUids == null) {
+            mUids = new int[4];
+            mUids[0] = uid;
+            mNames = new String[4];
+            mNames[0] = name;
+            mNum = 1;
+        } else if (mNum >= mUids.length) {
+            int[] newuids = new int[(mNum*3)/2];
+            String[] newnames = new String[(mNum*3)/2];
+            if (index > 0) {
+                System.arraycopy(mUids, 0, newuids, 0, index);
+                System.arraycopy(mNames, 0, newnames, 0, index);
+            }
+            if (index < mNum) {
+                System.arraycopy(mUids, index, newuids, index+1, mNum-index);
+                System.arraycopy(mNames, index, newnames, index+1, mNum-index);
+            }
+            mUids = newuids;
+            mNames = newnames;
+            mUids[index] = uid;
+            mNames[index] = name;
+            mNum++;
+        } else {
+            if (index < mNum) {
+                System.arraycopy(mUids, index, mUids, index+1, mNum-index);
+                System.arraycopy(mNames, index, mNames, index+1, mNum-index);
+            }
+            mUids[index] = uid;
+            mNames[index] = name;
+            mNum++;
+        }
     }
 
     @Override
@@ -315,19 +648,24 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mNum);
         dest.writeIntArray(mUids);
+        dest.writeStringArray(mNames);
     }
 
     @Override
     public String toString() {
         StringBuilder result = new StringBuilder();
-        result.append("{WorkSource: uids=[");
+        result.append("WorkSource{");
         for (int i = 0; i < mNum; i++) {
             if (i != 0) {
                 result.append(", ");
             }
             result.append(mUids[i]);
+            if (mNames != null) {
+                result.append(" ");
+                result.append(mNames[i]);
+            }
         }
-        result.append("]}");
+        result.append("}");
         return result.toString();
     }
 
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 5b49ba3..8f54a38 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -4695,7 +4695,8 @@
          * The content:// style URI for this table. Append the phone number you want to lookup
          * to this URI and query it to perform a lookup. For example:
          * <pre>
-         * Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_URI, Uri.encode(phoneNumber));
+         * Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
+         *         Uri.encode(phoneNumber));
          * </pre>
          */
         public static final Uri CONTENT_FILTER_URI = Uri.withAppendedPath(AUTHORITY_URI,
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4dbc4b4..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);
@@ -2610,6 +2622,7 @@
             MOVED_TO_GLOBAL.add(Settings.Global.ADB_ENABLED);
             MOVED_TO_GLOBAL.add(Settings.Global.ASSISTED_GPS_ENABLED);
             MOVED_TO_GLOBAL.add(Settings.Global.BLUETOOTH_ON);
+            MOVED_TO_GLOBAL.add(Settings.Global.BUGREPORT_IN_POWER_MENU);
             MOVED_TO_GLOBAL.add(Settings.Global.CDMA_CELL_BROADCAST_SMS);
             MOVED_TO_GLOBAL.add(Settings.Global.CDMA_ROAMING_MODE);
             MOVED_TO_GLOBAL.add(Settings.Global.CDMA_SUBSCRIPTION_MODE);
@@ -3081,8 +3094,10 @@
         /**
          * When the user has enable the option to have a "bug report" command
          * in the power menu.
+         * @deprecated Use {@link android.provider.Settings.Global#BUGREPORT_IN_POWER_MENU} instead
          * @hide
          */
+        @Deprecated
         public static final String BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu";
 
         /**
@@ -4037,7 +4052,7 @@
          * @hide
          */
         public static final String[] SETTINGS_TO_BACKUP = {
-            BUGREPORT_IN_POWER_MENU,
+            BUGREPORT_IN_POWER_MENU,                            // moved to global
             ALLOW_MOCK_LOCATION,
             PARENTAL_CONTROL_ENABLED,
             PARENTAL_CONTROL_REDIRECT_URL,
@@ -4316,6 +4331,13 @@
         public static final String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
 
         /**
+         * When the user has enable the option to have a "bug report" command
+         * in the power menu.
+         * @hide
+         */
+        public static final String BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu";
+
+        /**
          * Whether ADB is enabled.
          */
         public static final String ADB_ENABLED = "adb_enabled";
@@ -5359,6 +5381,7 @@
          * @hide
          */
         public static final String[] SETTINGS_TO_BACKUP = {
+            BUGREPORT_IN_POWER_MENU,
             STAY_ON_WHILE_PLUGGED_IN,
             MODE_RINGER,
             AUTO_TIME,
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index 352cb22..7e230ac 100644
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -31,6 +31,8 @@
 import java.util.TimeZone;
 import java.text.SimpleDateFormat;
 
+import libcore.icu.LocaleData;
+
 /**
     Utility class for producing strings with formatted date/time.
 
@@ -265,16 +267,9 @@
      * @return the {@link java.text.DateFormat} object that properly formats the time.
      */
     public static java.text.DateFormat getTimeFormat(Context context) {
-        boolean b24 = is24HourFormat(context);
-        int res;
-
-        if (b24) {
-            res = R.string.twenty_four_hour_time_format;
-        } else {
-            res = R.string.twelve_hour_time_format;
-        }
-
-        return new java.text.SimpleDateFormat(context.getString(res));
+        LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
+        boolean is24 = is24HourFormat(context);
+        return new java.text.SimpleDateFormat(is24 ? d.timeFormat24 : d.timeFormat12);
     }
 
     /**
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 7739ff7..e0cf3b2 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -433,20 +433,16 @@
 
     @Override
     public boolean clipPath(Path path) {
-        // TODO: Implement
-        path.computeBounds(mPathBounds, true);
-        return nClipRect(mRenderer, mPathBounds.left, mPathBounds.top,
-                mPathBounds.right, mPathBounds.bottom, Region.Op.INTERSECT.nativeInt);
+        return nClipPath(mRenderer, path.mNativePath, Region.Op.INTERSECT.nativeInt);
     }
 
     @Override
     public boolean clipPath(Path path, Region.Op op) {
-        // TODO: Implement
-        path.computeBounds(mPathBounds, true);
-        return nClipRect(mRenderer, mPathBounds.left, mPathBounds.top,
-                mPathBounds.right, mPathBounds.bottom, op.nativeInt);
+        return nClipPath(mRenderer, path.mNativePath, op.nativeInt);
     }
 
+    private static native boolean nClipPath(int renderer, int path, int op);
+
     @Override
     public boolean clipRect(float left, float top, float right, float bottom) {
         return nClipRect(mRenderer, left, top, right, bottom, Region.Op.INTERSECT.nativeInt);
@@ -465,8 +461,8 @@
         return nClipRect(mRenderer, left, top, right, bottom, Region.Op.INTERSECT.nativeInt);
     }
     
-    private static native boolean nClipRect(int renderer, int left, int top, int right, int bottom,
-            int op);
+    private static native boolean nClipRect(int renderer, int left, int top,
+            int right, int bottom, int op);
 
     @Override
     public boolean clipRect(Rect rect) {
@@ -492,20 +488,16 @@
 
     @Override
     public boolean clipRegion(Region region) {
-        // TODO: Implement
-        region.getBounds(mClipBounds);
-        return nClipRect(mRenderer, mClipBounds.left, mClipBounds.top,
-                mClipBounds.right, mClipBounds.bottom, Region.Op.INTERSECT.nativeInt);
+        return nClipRegion(mRenderer, region.mNativeRegion, Region.Op.INTERSECT.nativeInt);
     }
 
     @Override
     public boolean clipRegion(Region region, Region.Op op) {
-        // TODO: Implement
-        region.getBounds(mClipBounds);
-        return nClipRect(mRenderer, mClipBounds.left, mClipBounds.top,
-                mClipBounds.right, mClipBounds.bottom, op.nativeInt);
+        return nClipRegion(mRenderer, region.mNativeRegion, op.nativeInt);
     }
 
+    private static native boolean nClipRegion(int renderer, int region, int op);
+
     @Override
     public boolean getClipBounds(Rect bounds) {
         return nGetClipBounds(mRenderer, bounds);
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 7ecdcbe..0d45bbc 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * 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.
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-
 package android.view;
 
 import android.content.ComponentCallbacks2;
@@ -174,6 +173,17 @@
     public static final String DEBUG_SHOW_OVERDRAW_PROPERTY = "debug.hwui.show_overdraw";
 
     /**
+     * Turn on to allow region clipping (see
+     * {@link android.graphics.Canvas#clipPath(android.graphics.Path)} and
+     * {@link android.graphics.Canvas#clipRegion(android.graphics.Region)}.
+     *
+     * When this option is turned on a stencil buffer is always required.
+     * If this option is off a stencil buffer is only created when the overdraw
+     * debugging mode is turned on.
+     */
+    private static final boolean REGION_CLIPPING_ENABLED = false;
+
+    /**
      * A process can set this flag to false to prevent the use of hardware
      * rendering.
      * 
@@ -876,10 +886,12 @@
                 changed = true;
                 mShowOverdraw = value;
 
-                if (surface != null && isEnabled()) {
-                    if (validate()) {
-                        sEglConfig = loadEglConfig();
-                        invalidate(surface);
+                if (!REGION_CLIPPING_ENABLED) {
+                    if (surface != null && isEnabled()) {
+                        if (validate()) {
+                            sEglConfig = loadEglConfig();
+                            invalidate(surface);
+                        }
                     }
                 }
             }
@@ -1752,6 +1764,11 @@
 
         @Override
         int[] getConfig(boolean dirtyRegions) {
+            //noinspection PointlessBooleanExpression
+            final int stencilSize = mShowOverdraw || REGION_CLIPPING_ENABLED ?
+                    GLES20Canvas.getStencilSize() : 0;
+            final int swapBehavior = dirtyRegions ? EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
+
             return new int[] {
                     EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
                     EGL_RED_SIZE, 8,
@@ -1760,14 +1777,12 @@
                     EGL_ALPHA_SIZE, 8,
                     EGL_DEPTH_SIZE, 0,
                     EGL_CONFIG_CAVEAT, EGL_NONE,
-                    // TODO: Find a better way to choose the stencil size
-                    EGL_STENCIL_SIZE, mShowOverdraw ? GLES20Canvas.getStencilSize() : 0,
-                    EGL_SURFACE_TYPE, EGL_WINDOW_BIT |
-                            (dirtyRegions ? EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0),
+                    EGL_STENCIL_SIZE, stencilSize,
+                    EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
                     EGL_NONE
             };
         }
-        
+
         @Override
         void initCaches() {
             GLES20Canvas.initCaches();
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/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 6a96893..1ae69fea 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -229,6 +229,13 @@
     int mLastSystemUiVisibility;
     int mClientWindowLayoutFlags;
 
+    /** @hide */
+    public static final int EVENT_NOT_HANDLED = 0;
+    /** @hide */
+    public static final int EVENT_HANDLED = 1;
+    /** @hide */
+    public static final int EVENT_IN_PROGRESS = 2;
+
     // Pool of queued input events.
     private static final int MAX_QUEUED_INPUT_EVENT_POOL_SIZE = 10;
     private QueuedInputEvent mQueuedInputEventPool;
@@ -3240,19 +3247,19 @@
         return false;
     }
 
-    private void deliverInputEvent(QueuedInputEvent q) {
+    private int deliverInputEvent(QueuedInputEvent q) {
         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent");
         try {
             if (q.mEvent instanceof KeyEvent) {
-                deliverKeyEvent(q);
+                return deliverKeyEvent(q);
             } else {
                 final int source = q.mEvent.getSource();
                 if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
-                    deliverPointerEvent(q);
+                    return deliverPointerEvent(q);
                 } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
-                    deliverTrackballEvent(q);
+                    return deliverTrackballEvent(q);
                 } else {
-                    deliverGenericMotionEvent(q);
+                    return deliverGenericMotionEvent(q);
                 }
             }
         } finally {
@@ -3260,7 +3267,25 @@
         }
     }
 
-    private void deliverPointerEvent(QueuedInputEvent q) {
+    private int deliverInputEventPostIme(QueuedInputEvent q) {
+        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEventPostIme");
+        try {
+            if (q.mEvent instanceof KeyEvent) {
+                return deliverKeyEventPostIme(q);
+            } else {
+                final int source = q.mEvent.getSource();
+                if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+                    return deliverTrackballEventPostIme(q);
+                } else {
+                    return deliverGenericMotionEventPostIme(q);
+                }
+            }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+        }
+    }
+
+    private int deliverPointerEvent(QueuedInputEvent q) {
         final MotionEvent event = (MotionEvent)q.mEvent;
         final boolean isTouchEvent = event.isTouchEvent();
         if (mInputEventConsistencyVerifier != null) {
@@ -3273,8 +3298,7 @@
 
         // If there is no view, then the event will not be handled.
         if (mView == null || !mAdded) {
-            finishInputEvent(q, false);
-            return;
+            return EVENT_NOT_HANDLED;
         }
 
         // Translate the pointer event for compatibility, if needed.
@@ -3307,16 +3331,10 @@
         if (MEASURE_LATENCY) {
             lt.sample("B Dispatched PointerEvents ", System.nanoTime() - event.getEventTimeNano());
         }
-        if (handled) {
-            finishInputEvent(q, true);
-            return;
-        }
-
-        // Pointer event was unhandled.
-        finishInputEvent(q, false);
+        return handled ? EVENT_HANDLED : EVENT_NOT_HANDLED;
     }
 
-    private void deliverTrackballEvent(QueuedInputEvent q) {
+    private int deliverTrackballEvent(QueuedInputEvent q) {
         final MotionEvent event = (MotionEvent)q.mEvent;
         if (mInputEventConsistencyVerifier != null) {
             mInputEventConsistencyVerifier.onTrackballEvent(event, 0);
@@ -3335,24 +3353,25 @@
                     if (DEBUG_IMF)
                         Log.v(TAG, "Sending trackball event to IME: seq="
                                 + seq + " event=" + event);
-                    imm.dispatchTrackballEvent(mView.getContext(), seq, event,
+                    int result = imm.dispatchTrackballEvent(mView.getContext(), seq, event,
                             mInputMethodCallback);
-                    return;
+                    if (result != EVENT_NOT_HANDLED) {
+                        return result;
+                    }
                 }
             }
         }
 
         // Not dispatching to IME, continue with post IME actions.
-        deliverTrackballEventPostIme(q);
+        return deliverTrackballEventPostIme(q);
     }
 
-    private void deliverTrackballEventPostIme(QueuedInputEvent q) {
+    private int deliverTrackballEventPostIme(QueuedInputEvent q) {
         final MotionEvent event = (MotionEvent) q.mEvent;
 
         // If there is no view, then the event will not be handled.
         if (mView == null || !mAdded) {
-            finishInputEvent(q, false);
-            return;
+            return EVENT_NOT_HANDLED;
         }
 
         // Deliver the trackball event to the view.
@@ -3362,10 +3381,8 @@
             // event into a key event, touch mode will not exit, so we exit
             // touch mode here.
             ensureTouchMode(false);
-
-            finishInputEvent(q, true);
             mLastTrackballTime = Integer.MIN_VALUE;
-            return;
+            return EVENT_HANDLED;
         }
 
         // Translate the trackball event into DPAD keys and try to deliver those.
@@ -3473,10 +3490,10 @@
 
         // Unfortunately we can't tell whether the application consumed the keys, so
         // we always consider the trackball event handled.
-        finishInputEvent(q, true);
+        return EVENT_HANDLED;
     }
 
-    private void deliverGenericMotionEvent(QueuedInputEvent q) {
+    private int deliverGenericMotionEvent(QueuedInputEvent q) {
         final MotionEvent event = (MotionEvent)q.mEvent;
         if (mInputEventConsistencyVerifier != null) {
             mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
@@ -3494,18 +3511,20 @@
                     if (DEBUG_IMF)
                         Log.v(TAG, "Sending generic motion event to IME: seq="
                                 + seq + " event=" + event);
-                    imm.dispatchGenericMotionEvent(mView.getContext(), seq, event,
+                    int result = imm.dispatchGenericMotionEvent(mView.getContext(), seq, event,
                             mInputMethodCallback);
-                    return;
+                    if (result != EVENT_NOT_HANDLED) {
+                        return result;
+                    }
                 }
             }
         }
 
         // Not dispatching to IME, continue with post IME actions.
-        deliverGenericMotionEventPostIme(q);
+        return deliverGenericMotionEventPostIme(q);
     }
 
-    private void deliverGenericMotionEventPostIme(QueuedInputEvent q) {
+    private int deliverGenericMotionEventPostIme(QueuedInputEvent q) {
         final MotionEvent event = (MotionEvent) q.mEvent;
         final int source = event.getSource();
         final boolean isJoystick = (source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0;
@@ -3519,8 +3538,7 @@
               //Convert TouchPad motion into a TrackBall event
               mSimulatedTrackball.updateTrackballDirection(this, event);
             }
-            finishInputEvent(q, false);
-            return;
+            return EVENT_NOT_HANDLED;
         }
 
         // Deliver the event to the view.
@@ -3531,22 +3549,21 @@
               //Convert TouchPad motion into a TrackBall event
               mSimulatedTrackball.updateTrackballDirection(this, event);
             }
-            finishInputEvent(q, true);
-            return;
+            return EVENT_HANDLED;
         }
 
         if (isJoystick) {
             // Translate the joystick event into DPAD keys and try to deliver
             // those.
             updateJoystickDirection(event, true);
-            finishInputEvent(q, true);
-        } else if (isTouchPad) {
+            return EVENT_HANDLED;
+        }
+        if (isTouchPad) {
             //Convert TouchPad motion into a TrackBall event
             mSimulatedTrackball.updateTrackballDirection(this, event);
-            finishInputEvent(q, true);
-        } else {
-            finishInputEvent(q, false);
+            return EVENT_HANDLED;
         }
+        return EVENT_NOT_HANDLED;
     }
 
     private void updateJoystickDirection(MotionEvent event, boolean synthesizeNewKeys) {
@@ -3690,7 +3707,7 @@
         return false;
     }
 
-    private void deliverKeyEvent(QueuedInputEvent q) {
+    private int deliverKeyEvent(QueuedInputEvent q) {
         final KeyEvent event = (KeyEvent)q.mEvent;
         if (mInputEventConsistencyVerifier != null) {
             mInputEventConsistencyVerifier.onKeyEvent(event, 0);
@@ -3701,8 +3718,7 @@
 
             // Perform predispatching before the IME.
             if (mView.dispatchKeyEventPreIme(event)) {
-                finishInputEvent(q, true);
-                return;
+                return EVENT_HANDLED;
             }
 
             // Dispatch to the IME before propagating down the view hierarchy.
@@ -3713,81 +3729,30 @@
                     final int seq = event.getSequenceNumber();
                     if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
                             + seq + " event=" + event);
-                    imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback);
-                    return;
+                    int result = imm.dispatchKeyEvent(mView.getContext(), seq, event,
+                            mInputMethodCallback);
+                    if (result != EVENT_NOT_HANDLED) {
+                        return result;
+                    }
                 }
             }
         }
 
         // Not dispatching to IME, continue with post IME actions.
-        deliverKeyEventPostIme(q);
+        return deliverKeyEventPostIme(q);
     }
 
-    void handleImeFinishedEvent(int seq, boolean handled) {
-        final QueuedInputEvent q = mCurrentInputEvent;
-        if (q != null && q.mEvent.getSequenceNumber() == seq) {
-            if (DEBUG_IMF) {
-                Log.v(TAG, "IME finished event: seq=" + seq
-                        + " handled=" + handled + " event=" + q);
-            }
-            if (handled) {
-                finishInputEvent(q, true);
-            } else {
-                if (q.mEvent instanceof KeyEvent) {
-                    KeyEvent event = (KeyEvent)q.mEvent;
-                    if (event.getAction() != KeyEvent.ACTION_UP) {
-                        // If the window doesn't currently have input focus, then drop
-                        // this event.  This could be an event that came back from the
-                        // IME dispatch but the window has lost focus in the meantime.
-                        if (!mAttachInfo.mHasWindowFocus) {
-                            Slog.w(TAG, "Dropping event due to no window focus: " + event);
-                            finishInputEvent(q, true);
-                            return;
-                        }
-                    }
-                    deliverKeyEventPostIme(q);
-                } else {
-                    MotionEvent event = (MotionEvent)q.mEvent;
-                    if (event.getAction() != MotionEvent.ACTION_CANCEL
-                            && event.getAction() != MotionEvent.ACTION_UP) {
-                        // If the window doesn't currently have input focus, then drop
-                        // this event.  This could be an event that came back from the
-                        // IME dispatch but the window has lost focus in the meantime.
-                        if (!mAttachInfo.mHasWindowFocus) {
-                            Slog.w(TAG, "Dropping event due to no window focus: " + event);
-                            finishInputEvent(q, true);
-                            return;
-                        }
-                    }
-                    final int source = q.mEvent.getSource();
-                    if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
-                        deliverTrackballEventPostIme(q);
-                    } else {
-                        deliverGenericMotionEventPostIme(q);
-                    }
-                }
-            }
-        } else {
-            if (DEBUG_IMF) {
-                Log.v(TAG, "IME finished event: seq=" + seq
-                        + " handled=" + handled + ", event not found!");
-            }
-        }
-    }
-
-    private void deliverKeyEventPostIme(QueuedInputEvent q) {
+    private int deliverKeyEventPostIme(QueuedInputEvent q) {
         final KeyEvent event = (KeyEvent)q.mEvent;
 
         // If the view went away, then the event will not be handled.
         if (mView == null || !mAdded) {
-            finishInputEvent(q, false);
-            return;
+            return EVENT_NOT_HANDLED;
         }
 
         // If the key's purpose is to exit touch mode then we consume it and consider it handled.
         if (checkForLeavingTouchModeAndConsume(event)) {
-            finishInputEvent(q, true);
-            return;
+            return EVENT_HANDLED;
         }
 
         // Make sure the fallback event policy sees all keys that will be delivered to the
@@ -3796,8 +3761,7 @@
 
         // Deliver the key to the view hierarchy.
         if (mView.dispatchKeyEvent(event)) {
-            finishInputEvent(q, true);
-            return;
+            return EVENT_HANDLED;
         }
 
         // If the Control modifier is held, try to interpret the key as a shortcut.
@@ -3806,15 +3770,13 @@
                 && event.getRepeatCount() == 0
                 && !KeyEvent.isModifierKey(event.getKeyCode())) {
             if (mView.dispatchKeyShortcutEvent(event)) {
-                finishInputEvent(q, true);
-                return;
+                return EVENT_HANDLED;
             }
         }
 
         // Apply the fallback event policy.
         if (mFallbackEventHandler.dispatchKeyEvent(event)) {
-            finishInputEvent(q, true);
-            return;
+            return EVENT_HANDLED;
         }
 
         // Handle automatic focus changes.
@@ -3867,22 +3829,20 @@
                         if (v.requestFocus(direction, mTempRect)) {
                             playSoundEffect(SoundEffectConstants
                                     .getContantForFocusDirection(direction));
-                            finishInputEvent(q, true);
-                            return;
+                            return EVENT_HANDLED;
                         }
                     }
 
                     // Give the focused view a last chance to handle the dpad key.
                     if (mView.dispatchUnhandledMove(focused, direction)) {
-                        finishInputEvent(q, true);
-                        return;
+                        return EVENT_HANDLED;
                     }
                 }
             }
         }
 
         // Key was unhandled.
-        finishInputEvent(q, false);
+        return EVENT_NOT_HANDLED;
     }
 
     /* drag/drop */
@@ -4403,7 +4363,11 @@
             mFirstPendingInputEvent = q.mNext;
             q.mNext = null;
             mCurrentInputEvent = q;
-            deliverInputEvent(q);
+
+            final int result = deliverInputEvent(q);
+            if (result != EVENT_IN_PROGRESS) {
+                finishCurrentInputEvent(result == EVENT_HANDLED);
+            }
         }
 
         // We are done processing all input events that we can process right now
@@ -4414,10 +4378,42 @@
         }
     }
 
-    private void finishInputEvent(QueuedInputEvent q, boolean handled) {
-        if (q != mCurrentInputEvent) {
-            throw new IllegalStateException("finished input event out of order");
+    void handleImeFinishedEvent(int seq, boolean handled) {
+        final QueuedInputEvent q = mCurrentInputEvent;
+        if (q != null && q.mEvent.getSequenceNumber() == seq) {
+            if (DEBUG_IMF) {
+                Log.v(TAG, "IME finished event: seq=" + seq
+                        + " handled=" + handled + " event=" + q);
+            }
+
+            if (!handled) {
+                // If the window doesn't currently have input focus, then drop
+                // this event.  This could be an event that came back from the
+                // IME dispatch but the window has lost focus in the meantime.
+                if (!mAttachInfo.mHasWindowFocus && !isTerminalInputEvent(q.mEvent)) {
+                    Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
+                } else {
+                    final int result = deliverInputEventPostIme(q);
+                    if (result == EVENT_HANDLED) {
+                        handled = true;
+                    }
+                }
+            }
+            finishCurrentInputEvent(handled);
+
+            // Immediately start processing the next input event.
+            doProcessInputEvents();
+        } else {
+            if (DEBUG_IMF) {
+                Log.v(TAG, "IME finished event: seq=" + seq
+                        + " handled=" + handled + ", event not found!");
+            }
         }
+    }
+
+    private void finishCurrentInputEvent(boolean handled) {
+        final QueuedInputEvent q = mCurrentInputEvent;
+        mCurrentInputEvent = null;
 
         if (q.mReceiver != null) {
             q.mReceiver.finishInputEvent(q.mEvent, handled);
@@ -4426,10 +4422,18 @@
         }
 
         recycleQueuedInputEvent(q);
+    }
 
-        mCurrentInputEvent = null;
-        if (mFirstPendingInputEvent != null) {
-            scheduleProcessInputEvents();
+    private static boolean isTerminalInputEvent(InputEvent event) {
+        if (event instanceof KeyEvent) {
+            final KeyEvent keyEvent = (KeyEvent)event;
+            return keyEvent.getAction() == KeyEvent.ACTION_UP;
+        } else {
+            final MotionEvent motionEvent = (MotionEvent)event;
+            final int action = motionEvent.getAction();
+            return action == MotionEvent.ACTION_UP
+                    || action == MotionEvent.ACTION_CANCEL
+                    || action == MotionEvent.ACTION_HOVER_EXIT;
         }
     }
 
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/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 4a3f846..d258f4d 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -334,7 +334,7 @@
     
     class H extends Handler {
         H(Looper looper) {
-            super(looper);
+            super(looper, null, true);
         }
         
         @Override
@@ -1565,38 +1565,36 @@
     /**
      * @hide
      */
-    public void dispatchKeyEvent(Context context, int seq, KeyEvent key,
+    public int dispatchKeyEvent(Context context, int seq, KeyEvent key,
             FinishedEventCallback callback) {
-        boolean handled = false;
         synchronized (mH) {
             if (DEBUG) Log.d(TAG, "dispatchKeyEvent");
 
             if (mCurMethod != null) {
                 if (key.getAction() == KeyEvent.ACTION_DOWN
-                        && key.getKeyCode() == KeyEvent.KEYCODE_SYM) {
+                        && key.getKeyCode() == KeyEvent.KEYCODE_SYM
+                        && key.getRepeatCount() == 0) {
                     showInputMethodPickerLocked();
-                    handled = true;
-                } else {
-                    try {
-                        if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod);
-                        final long startTime = SystemClock.uptimeMillis();
-                        enqueuePendingEventLocked(startTime, seq, mCurId, callback);
-                        mCurMethod.dispatchKeyEvent(seq, key, mInputMethodCallback);
-                        return;
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e);
-                    }
+                    return ViewRootImpl.EVENT_HANDLED;
+                }
+                try {
+                    if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod);
+                    final long startTime = SystemClock.uptimeMillis();
+                    enqueuePendingEventLocked(startTime, seq, mCurId, callback);
+                    mCurMethod.dispatchKeyEvent(seq, key, mInputMethodCallback);
+                    return ViewRootImpl.EVENT_IN_PROGRESS;
+                } catch (RemoteException e) {
+                    Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e);
                 }
             }
         }
-
-        callback.finishedEvent(seq, handled);
+        return ViewRootImpl.EVENT_NOT_HANDLED;
     }
 
     /**
      * @hide
      */
-    public void dispatchTrackballEvent(Context context, int seq, MotionEvent motion,
+    public int dispatchTrackballEvent(Context context, int seq, MotionEvent motion,
             FinishedEventCallback callback) {
         synchronized (mH) {
             if (DEBUG) Log.d(TAG, "dispatchTrackballEvent");
@@ -1607,20 +1605,19 @@
                     final long startTime = SystemClock.uptimeMillis();
                     enqueuePendingEventLocked(startTime, seq, mCurId, callback);
                     mCurMethod.dispatchTrackballEvent(seq, motion, mInputMethodCallback);
-                    return;
+                    return ViewRootImpl.EVENT_IN_PROGRESS;
                 } catch (RemoteException e) {
                     Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e);
                 }
             }
         }
-
-        callback.finishedEvent(seq, false);
+        return ViewRootImpl.EVENT_NOT_HANDLED;
     }
 
     /**
      * @hide
      */
-    public void dispatchGenericMotionEvent(Context context, int seq, MotionEvent motion,
+    public int dispatchGenericMotionEvent(Context context, int seq, MotionEvent motion,
             FinishedEventCallback callback) {
         synchronized (mH) {
             if (DEBUG) Log.d(TAG, "dispatchGenericMotionEvent");
@@ -1631,14 +1628,13 @@
                     final long startTime = SystemClock.uptimeMillis();
                     enqueuePendingEventLocked(startTime, seq, mCurId, callback);
                     mCurMethod.dispatchGenericMotionEvent(seq, motion, mInputMethodCallback);
-                    return;
+                    return ViewRootImpl.EVENT_IN_PROGRESS;
                 } catch (RemoteException e) {
                     Log.w(TAG, "IME died: " + mCurId + " dropping generic motion: " + motion, e);
                 }
             }
         }
-
-        callback.finishedEvent(seq, false);
+        return ViewRootImpl.EVENT_NOT_HANDLED;
     }
 
     void finishedEvent(int seq, boolean handled) {
diff --git a/core/java/android/widget/DateTimeView.java b/core/java/android/widget/DateTimeView.java
index 6e6e4af..af6bbcb 100644
--- a/core/java/android/widget/DateTimeView.java
+++ b/core/java/android/widget/DateTimeView.java
@@ -189,15 +189,7 @@
     }
 
     private DateFormat getTimeFormat() {
-        int res;
-        Context context = getContext();
-        if (android.text.format.DateFormat.is24HourFormat(context)) {
-            res = R.string.twenty_four_hour_time_format;
-        } else {
-            res = R.string.twelve_hour_time_format;
-        }
-        String format = context.getString(res);
-        return new SimpleDateFormat(format);
+        return android.text.format.DateFormat.getTimeFormat(getContext());
     }
 
     private DateFormat getDateFormat() {
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index b71649a..1460737 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -42,7 +42,7 @@
 
 /**
  * <p>A popup window that can be used to display an arbitrary view. The popup
- * windows is a floating container that appears on top of the current
+ * window is a floating container that appears on top of the current
  * activity.</p>
  * 
  * @see android.widget.AutoCompleteTextView
diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java
index 786afe2..368f6ad 100644
--- a/core/java/android/widget/QuickContactBadge.java
+++ b/core/java/android/widget/QuickContactBadge.java
@@ -27,6 +27,7 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.os.Bundle;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Intents;
@@ -50,6 +51,7 @@
     private Drawable mOverlay;
     private QueryHandler mQueryHandler;
     private Drawable mDefaultAvatar;
+    private Bundle mExtras = null;
 
     protected String[] mExcludeMimes = null;
 
@@ -58,6 +60,8 @@
     static final private int TOKEN_EMAIL_LOOKUP_AND_TRIGGER = 2;
     static final private int TOKEN_PHONE_LOOKUP_AND_TRIGGER = 3;
 
+    static final private String EXTRA_URI_CONTENT = "uri_content";
+
     static final String[] EMAIL_LOOKUP_PROJECTION = new String[] {
         RawContacts.CONTACT_ID,
         Contacts.LOOKUP_KEY,
@@ -175,7 +179,26 @@
      * until this view is clicked.
      */
     public void assignContactFromEmail(String emailAddress, boolean lazyLookup) {
+        assignContactFromEmail(emailAddress, lazyLookup, null);
+    }
+
+    /**
+     * Assign a contact based on an email address. This should only be used when
+     * the contact's URI is not available, as an extra query will have to be
+     * performed to lookup the URI based on the email.
+
+     @param emailAddress The email address of the contact.
+     @param lazyLookup If this is true, the lookup query will not be performed
+     until this view is clicked.
+     @param extras A bundle of extras to populate the contact edit page with if the contact
+     is not found and the user chooses to add the email address to an existing contact or
+     create a new contact. Uses the same string constants as those found in
+     {@link android.provider.ContactsContract.Intents.Insert}
+    */
+
+    public void assignContactFromEmail(String emailAddress, boolean lazyLookup, Bundle extras) {
         mContactEmail = emailAddress;
+        mExtras = extras;
         if (!lazyLookup) {
             mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP, null,
                     Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(mContactEmail)),
@@ -186,6 +209,7 @@
         }
     }
 
+
     /**
      * Assign a contact based on a phone number. This should only be used when
      * the contact's URI is not available, as an extra query will have to be
@@ -196,7 +220,25 @@
      * until this view is clicked.
      */
     public void assignContactFromPhone(String phoneNumber, boolean lazyLookup) {
+        assignContactFromPhone(phoneNumber, lazyLookup, new Bundle());
+    }
+
+    /**
+     * Assign a contact based on a phone number. This should only be used when
+     * the contact's URI is not available, as an extra query will have to be
+     * performed to lookup the URI based on the phone number.
+     *
+     * @param phoneNumber The phone number of the contact.
+     * @param lazyLookup If this is true, the lookup query will not be performed
+     * until this view is clicked.
+     * @param extras A bundle of extras to populate the contact edit page with if the contact
+     * is not found and the user chooses to add the phone number to an existing contact or
+     * create a new contact. Uses the same string constants as those found in
+     * {@link android.provider.ContactsContract.Intents.Insert}
+     */
+    public void assignContactFromPhone(String phoneNumber, boolean lazyLookup, Bundle extras) {
         mContactPhone = phoneNumber;
+        mExtras = extras;
         if (!lazyLookup) {
             mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP, null,
                     Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, mContactPhone),
@@ -213,15 +255,21 @@
 
     @Override
     public void onClick(View v) {
+        // If contact has been assigned, mExtras should no longer be null, but do a null check
+        // anyway just in case assignContactFromPhone or Email was called with a null bundle or
+        // wasn't assigned previously.
+        final Bundle extras = (mExtras == null) ? new Bundle() : mExtras;
         if (mContactUri != null) {
             QuickContact.showQuickContact(getContext(), QuickContactBadge.this, mContactUri,
                     QuickContact.MODE_LARGE, mExcludeMimes);
         } else if (mContactEmail != null) {
-            mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP_AND_TRIGGER, mContactEmail,
+            extras.putString(EXTRA_URI_CONTENT, mContactEmail);
+            mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP_AND_TRIGGER, extras,
                     Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(mContactEmail)),
                     EMAIL_LOOKUP_PROJECTION, null, null, null);
         } else if (mContactPhone != null) {
-            mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP_AND_TRIGGER, mContactPhone,
+            extras.putString(EXTRA_URI_CONTENT, mContactPhone);
+            mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP_AND_TRIGGER, extras,
                     Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, mContactPhone),
                     PHONE_LOOKUP_PROJECTION, null, null, null);
         } else {
@@ -262,12 +310,12 @@
             Uri lookupUri = null;
             Uri createUri = null;
             boolean trigger = false;
-
+            Bundle extras = (cookie != null) ? (Bundle) cookie : new Bundle();
             try {
                 switch(token) {
                     case TOKEN_PHONE_LOOKUP_AND_TRIGGER:
                         trigger = true;
-                        createUri = Uri.fromParts("tel", (String)cookie, null);
+                        createUri = Uri.fromParts("tel", extras.getString(EXTRA_URI_CONTENT), null);
 
                         //$FALL-THROUGH$
                     case TOKEN_PHONE_LOOKUP: {
@@ -281,7 +329,8 @@
                     }
                     case TOKEN_EMAIL_LOOKUP_AND_TRIGGER:
                         trigger = true;
-                        createUri = Uri.fromParts("mailto", (String)cookie, null);
+                        createUri = Uri.fromParts("mailto",
+                                extras.getString(EXTRA_URI_CONTENT), null);
 
                         //$FALL-THROUGH$
                     case TOKEN_EMAIL_LOOKUP: {
@@ -309,6 +358,10 @@
             } else if (createUri != null) {
                 // Prompt user to add this person to contacts
                 final Intent intent = new Intent(Intents.SHOW_OR_CREATE_CONTACT, createUri);
+                if (extras != null) {
+                    extras.remove(EXTRA_URI_CONTENT);
+                    intent.putExtras(extras);
+                }
                 getContext().startActivity(intent);
             }
         }
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/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
new file mode 100644
index 0000000..4d41e42
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -0,0 +1,796 @@
+/*
+ * 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 com.android.internal.inputmethod;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.text.TextUtils;
+import android.util.Pair;
+import android.util.Slog;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * InputMethodManagerUtils contains some static methods that provides IME informations.
+ * This methods are supposed to be used in both the framework and the Settings application.
+ */
+public class InputMethodUtils {
+    public static final boolean DEBUG = false;
+    public static final int NOT_A_SUBTYPE_ID = -1;
+    public static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
+    public static final String SUBTYPE_MODE_VOICE = "voice";
+    private static final String TAG = "InputMethodUtils";
+    private static final Locale ENGLISH_LOCALE = new Locale("en");
+    private static final String NOT_A_SUBTYPE_ID_STR = String.valueOf(NOT_A_SUBTYPE_ID);
+    private static final String TAG_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE =
+            "EnabledWhenDefaultIsNotAsciiCapable";
+    private static final String TAG_ASCII_CAPABLE = "AsciiCapable";
+
+    private InputMethodUtils() {
+        // This utility class is not publicly instantiable.
+    }
+
+    public static boolean isSystemIme(InputMethodInfo inputMethod) {
+        return (inputMethod.getServiceInfo().applicationInfo.flags
+                & ApplicationInfo.FLAG_SYSTEM) != 0;
+    }
+
+    public static boolean isSystemImeThatHasEnglishSubtype(InputMethodInfo imi) {
+        if (!isSystemIme(imi)) {
+            return false;
+        }
+        return containsSubtypeOf(imi, ENGLISH_LOCALE.getLanguage());
+    }
+
+    // TODO: Rename isSystemDefaultImeThatHasCurrentLanguageSubtype
+    public static boolean isValidSystemDefaultIme(
+            boolean isSystemReady, InputMethodInfo imi, Context context) {
+        if (!isSystemReady) {
+            return false;
+        }
+        if (!isSystemIme(imi)) {
+            return false;
+        }
+        if (imi.getIsDefaultResourceId() != 0) {
+            try {
+                Resources res = context.createPackageContext(
+                        imi.getPackageName(), 0).getResources();
+                if (res.getBoolean(imi.getIsDefaultResourceId())
+                        && containsSubtypeOf(imi, context.getResources().getConfiguration().
+                                locale.getLanguage())) {
+                    return true;
+                }
+            } catch (PackageManager.NameNotFoundException ex) {
+            } catch (Resources.NotFoundException ex) {
+            }
+        }
+        if (imi.getSubtypeCount() == 0) {
+            Slog.w(TAG, "Found no subtypes in a system IME: " + imi.getPackageName());
+        }
+        return false;
+    }
+
+    public static boolean isDefaultEnabledIme(
+            boolean isSystemReady, InputMethodInfo imi, Context context) {
+        return isValidSystemDefaultIme(isSystemReady, imi, context)
+                || isSystemImeThatHasEnglishSubtype(imi);
+    }
+
+    private static boolean containsSubtypeOf(InputMethodInfo imi, String language) {
+        final int N = imi.getSubtypeCount();
+        for (int i = 0; i < N; ++i) {
+            if (imi.getSubtypeAt(i).getLocale().startsWith(language)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static ArrayList<InputMethodSubtype> getSubtypes(InputMethodInfo imi) {
+        ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+        final int subtypeCount = imi.getSubtypeCount();
+        for (int i = 0; i < subtypeCount; ++i) {
+            subtypes.add(imi.getSubtypeAt(i));
+        }
+        return subtypes;
+    }
+
+    public static ArrayList<InputMethodSubtype> getOverridingImplicitlyEnabledSubtypes(
+            InputMethodInfo imi, String mode) {
+        ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+        final int subtypeCount = imi.getSubtypeCount();
+        for (int i = 0; i < subtypeCount; ++i) {
+            final InputMethodSubtype subtype = imi.getSubtypeAt(i);
+            if (subtype.overridesImplicitlyEnabledSubtype() && subtype.getMode().equals(mode)) {
+                subtypes.add(subtype);
+            }
+        }
+        return subtypes;
+    }
+
+    public static InputMethodInfo getMostApplicableDefaultIME(
+            List<InputMethodInfo> enabledImes) {
+        if (enabledImes != null && enabledImes.size() > 0) {
+            // We'd prefer to fall back on a system IME, since that is safer.
+            int i = enabledImes.size();
+            int firstFoundSystemIme = -1;
+            while (i > 0) {
+                i--;
+                final InputMethodInfo imi = enabledImes.get(i);
+                if (InputMethodUtils.isSystemImeThatHasEnglishSubtype(imi)
+                        && !imi.isAuxiliaryIme()) {
+                    return imi;
+                }
+                if (firstFoundSystemIme < 0 && InputMethodUtils.isSystemIme(imi)
+                        && !imi.isAuxiliaryIme()) {
+                    firstFoundSystemIme = i;
+                }
+            }
+            return enabledImes.get(Math.max(firstFoundSystemIme, 0));
+        }
+        return null;
+    }
+
+    public static boolean isValidSubtypeId(InputMethodInfo imi, int subtypeHashCode) {
+        return getSubtypeIdFromHashCode(imi, subtypeHashCode) != NOT_A_SUBTYPE_ID;
+    }
+
+    public static int getSubtypeIdFromHashCode(InputMethodInfo imi, int subtypeHashCode) {
+        if (imi != null) {
+            final int subtypeCount = imi.getSubtypeCount();
+            for (int i = 0; i < subtypeCount; ++i) {
+                InputMethodSubtype ims = imi.getSubtypeAt(i);
+                if (subtypeHashCode == ims.hashCode()) {
+                    return i;
+                }
+            }
+        }
+        return NOT_A_SUBTYPE_ID;
+    }
+
+    private static ArrayList<InputMethodSubtype> getImplicitlyApplicableSubtypesLocked(
+            Resources res, InputMethodInfo imi) {
+        final List<InputMethodSubtype> subtypes = InputMethodUtils.getSubtypes(imi);
+        final String systemLocale = res.getConfiguration().locale.toString();
+        if (TextUtils.isEmpty(systemLocale)) return new ArrayList<InputMethodSubtype>();
+        final HashMap<String, InputMethodSubtype> applicableModeAndSubtypesMap =
+                new HashMap<String, InputMethodSubtype>();
+        final int N = subtypes.size();
+        for (int i = 0; i < N; ++i) {
+            // scan overriding implicitly enabled subtypes.
+            InputMethodSubtype subtype = subtypes.get(i);
+            if (subtype.overridesImplicitlyEnabledSubtype()) {
+                final String mode = subtype.getMode();
+                if (!applicableModeAndSubtypesMap.containsKey(mode)) {
+                    applicableModeAndSubtypesMap.put(mode, subtype);
+                }
+            }
+        }
+        if (applicableModeAndSubtypesMap.size() > 0) {
+            return new ArrayList<InputMethodSubtype>(applicableModeAndSubtypesMap.values());
+        }
+        for (int i = 0; i < N; ++i) {
+            final InputMethodSubtype subtype = subtypes.get(i);
+            final String locale = subtype.getLocale();
+            final String mode = subtype.getMode();
+            // When system locale starts with subtype's locale, that subtype will be applicable
+            // for system locale
+            // For instance, it's clearly applicable for cases like system locale = en_US and
+            // subtype = en, but it is not necessarily considered applicable for cases like system
+            // locale = en and subtype = en_US.
+            // We just call systemLocale.startsWith(locale) in this function because there is no
+            // need to find applicable subtypes aggressively unlike
+            // findLastResortApplicableSubtypeLocked.
+            if (systemLocale.startsWith(locale)) {
+                final InputMethodSubtype applicableSubtype = applicableModeAndSubtypesMap.get(mode);
+                // If more applicable subtypes are contained, skip.
+                if (applicableSubtype != null) {
+                    if (systemLocale.equals(applicableSubtype.getLocale())) continue;
+                    if (!systemLocale.equals(locale)) continue;
+                }
+                applicableModeAndSubtypesMap.put(mode, subtype);
+            }
+        }
+        final InputMethodSubtype keyboardSubtype
+                = applicableModeAndSubtypesMap.get(SUBTYPE_MODE_KEYBOARD);
+        final ArrayList<InputMethodSubtype> applicableSubtypes = new ArrayList<InputMethodSubtype>(
+                applicableModeAndSubtypesMap.values());
+        if (keyboardSubtype != null && !keyboardSubtype.containsExtraValueKey(TAG_ASCII_CAPABLE)) {
+            for (int i = 0; i < N; ++i) {
+                final InputMethodSubtype subtype = subtypes.get(i);
+                final String mode = subtype.getMode();
+                if (SUBTYPE_MODE_KEYBOARD.equals(mode) && subtype.containsExtraValueKey(
+                        TAG_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)) {
+                    applicableSubtypes.add(subtype);
+                }
+            }
+        }
+        if (keyboardSubtype == null) {
+            InputMethodSubtype lastResortKeyboardSubtype = findLastResortApplicableSubtypeLocked(
+                    res, subtypes, SUBTYPE_MODE_KEYBOARD, systemLocale, true);
+            if (lastResortKeyboardSubtype != null) {
+                applicableSubtypes.add(lastResortKeyboardSubtype);
+            }
+        }
+        return applicableSubtypes;
+    }
+
+    private static List<InputMethodSubtype> getEnabledInputMethodSubtypeList(
+            Context context, InputMethodInfo imi, List<InputMethodSubtype> enabledSubtypes,
+            boolean allowsImplicitlySelectedSubtypes) {
+        if (allowsImplicitlySelectedSubtypes && enabledSubtypes.isEmpty()) {
+            enabledSubtypes = InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+                    context.getResources(), imi);
+        }
+        return InputMethodSubtype.sort(context, 0, imi, enabledSubtypes);
+    }
+
+    /**
+     * If there are no selected subtypes, tries finding the most applicable one according to the
+     * given locale.
+     * @param subtypes this function will search the most applicable subtype in subtypes
+     * @param mode subtypes will be filtered by mode
+     * @param locale subtypes will be filtered by locale
+     * @param canIgnoreLocaleAsLastResort if this function can't find the most applicable subtype,
+     * it will return the first subtype matched with mode
+     * @return the most applicable subtypeId
+     */
+    public static InputMethodSubtype findLastResortApplicableSubtypeLocked(
+            Resources res, List<InputMethodSubtype> subtypes, String mode, String locale,
+            boolean canIgnoreLocaleAsLastResort) {
+        if (subtypes == null || subtypes.size() == 0) {
+            return null;
+        }
+        if (TextUtils.isEmpty(locale)) {
+            locale = res.getConfiguration().locale.toString();
+        }
+        final String language = locale.substring(0, 2);
+        boolean partialMatchFound = false;
+        InputMethodSubtype applicableSubtype = null;
+        InputMethodSubtype firstMatchedModeSubtype = null;
+        final int N = subtypes.size();
+        for (int i = 0; i < N; ++i) {
+            InputMethodSubtype subtype = subtypes.get(i);
+            final String subtypeLocale = subtype.getLocale();
+            // An applicable subtype should match "mode". If mode is null, mode will be ignored,
+            // and all subtypes with all modes can be candidates.
+            if (mode == null || subtypes.get(i).getMode().equalsIgnoreCase(mode)) {
+                if (firstMatchedModeSubtype == null) {
+                    firstMatchedModeSubtype = subtype;
+                }
+                if (locale.equals(subtypeLocale)) {
+                    // Exact match (e.g. system locale is "en_US" and subtype locale is "en_US")
+                    applicableSubtype = subtype;
+                    break;
+                } else if (!partialMatchFound && subtypeLocale.startsWith(language)) {
+                    // Partial match (e.g. system locale is "en_US" and subtype locale is "en")
+                    applicableSubtype = subtype;
+                    partialMatchFound = true;
+                }
+            }
+        }
+
+        if (applicableSubtype == null && canIgnoreLocaleAsLastResort) {
+            return firstMatchedModeSubtype;
+        }
+
+        // The first subtype applicable to the system locale will be defined as the most applicable
+        // subtype.
+        if (DEBUG) {
+            if (applicableSubtype != null) {
+                Slog.d(TAG, "Applicable InputMethodSubtype was found: "
+                        + applicableSubtype.getMode() + "," + applicableSubtype.getLocale());
+            }
+        }
+        return applicableSubtype;
+    }
+
+    public static boolean canAddToLastInputMethod(InputMethodSubtype subtype) {
+        if (subtype == null) return true;
+        return !subtype.isAuxiliary();
+    }
+
+    /**
+     * Utility class for putting and getting settings for InputMethod
+     * TODO: Move all putters and getters of settings to this class.
+     */
+    public static class InputMethodSettings {
+        // The string for enabled input method is saved as follows:
+        // example: ("ime0;subtype0;subtype1;subtype2:ime1:ime2;subtype0")
+        private static final char INPUT_METHOD_SEPARATER = ':';
+        private static final char INPUT_METHOD_SUBTYPE_SEPARATER = ';';
+        private final TextUtils.SimpleStringSplitter mInputMethodSplitter =
+                new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATER);
+
+        private final TextUtils.SimpleStringSplitter mSubtypeSplitter =
+                new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATER);
+
+        private final Resources mRes;
+        private final ContentResolver mResolver;
+        private final HashMap<String, InputMethodInfo> mMethodMap;
+        private final ArrayList<InputMethodInfo> mMethodList;
+
+        private String mEnabledInputMethodsStrCache;
+        private int mCurrentUserId;
+
+        private static void buildEnabledInputMethodsSettingString(
+                StringBuilder builder, Pair<String, ArrayList<String>> pair) {
+            String id = pair.first;
+            ArrayList<String> subtypes = pair.second;
+            builder.append(id);
+            // Inputmethod and subtypes are saved in the settings as follows:
+            // ime0;subtype0;subtype1:ime1;subtype0:ime2:ime3;subtype0;subtype1
+            for (String subtypeId: subtypes) {
+                builder.append(INPUT_METHOD_SUBTYPE_SEPARATER).append(subtypeId);
+            }
+        }
+
+        public InputMethodSettings(
+                Resources res, ContentResolver resolver,
+                HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
+                int userId) {
+            setCurrentUserId(userId);
+            mRes = res;
+            mResolver = resolver;
+            mMethodMap = methodMap;
+            mMethodList = methodList;
+        }
+
+        public void setCurrentUserId(int userId) {
+            if (DEBUG) {
+                Slog.d(TAG, "--- Swtich the current user from " + mCurrentUserId + " to "
+                        + userId + ", new ime = " + getSelectedInputMethod());
+            }
+            // IMMS settings are kept per user, so keep track of current user
+            mCurrentUserId = userId;
+        }
+
+        public List<InputMethodInfo> getEnabledInputMethodListLocked() {
+            return createEnabledInputMethodListLocked(
+                    getEnabledInputMethodsAndSubtypeListLocked());
+        }
+
+        public List<Pair<InputMethodInfo, ArrayList<String>>>
+                getEnabledInputMethodAndSubtypeHashCodeListLocked() {
+            return createEnabledInputMethodAndSubtypeHashCodeListLocked(
+                    getEnabledInputMethodsAndSubtypeListLocked());
+        }
+
+        public List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(
+                Context context, InputMethodInfo imi, boolean allowsImplicitlySelectedSubtypes) {
+            List<InputMethodSubtype> enabledSubtypes =
+                    getEnabledInputMethodSubtypeListLocked(imi);
+            if (allowsImplicitlySelectedSubtypes && enabledSubtypes.isEmpty()) {
+                enabledSubtypes = InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+                        context.getResources(), imi);
+            }
+            return InputMethodSubtype.sort(context, 0, imi, enabledSubtypes);
+        }
+
+        private List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(
+                InputMethodInfo imi) {
+            List<Pair<String, ArrayList<String>>> imsList =
+                    getEnabledInputMethodsAndSubtypeListLocked();
+            ArrayList<InputMethodSubtype> enabledSubtypes =
+                    new ArrayList<InputMethodSubtype>();
+            if (imi != null) {
+                for (Pair<String, ArrayList<String>> imsPair : imsList) {
+                    InputMethodInfo info = mMethodMap.get(imsPair.first);
+                    if (info != null && info.getId().equals(imi.getId())) {
+                        final int subtypeCount = info.getSubtypeCount();
+                        for (int i = 0; i < subtypeCount; ++i) {
+                            InputMethodSubtype ims = info.getSubtypeAt(i);
+                            for (String s: imsPair.second) {
+                                if (String.valueOf(ims.hashCode()).equals(s)) {
+                                    enabledSubtypes.add(ims);
+                                }
+                            }
+                        }
+                        break;
+                    }
+                }
+            }
+            return enabledSubtypes;
+        }
+
+        // At the initial boot, the settings for input methods are not set,
+        // so we need to enable IME in that case.
+        public void enableAllIMEsIfThereIsNoEnabledIME() {
+            if (TextUtils.isEmpty(getEnabledInputMethodsStr())) {
+                StringBuilder sb = new StringBuilder();
+                final int N = mMethodList.size();
+                for (int i = 0; i < N; i++) {
+                    InputMethodInfo imi = mMethodList.get(i);
+                    Slog.i(TAG, "Adding: " + imi.getId());
+                    if (i > 0) sb.append(':');
+                    sb.append(imi.getId());
+                }
+                putEnabledInputMethodsStr(sb.toString());
+            }
+        }
+
+        public List<Pair<String, ArrayList<String>>> getEnabledInputMethodsAndSubtypeListLocked() {
+            ArrayList<Pair<String, ArrayList<String>>> imsList
+                    = new ArrayList<Pair<String, ArrayList<String>>>();
+            final String enabledInputMethodsStr = getEnabledInputMethodsStr();
+            if (TextUtils.isEmpty(enabledInputMethodsStr)) {
+                return imsList;
+            }
+            mInputMethodSplitter.setString(enabledInputMethodsStr);
+            while (mInputMethodSplitter.hasNext()) {
+                String nextImsStr = mInputMethodSplitter.next();
+                mSubtypeSplitter.setString(nextImsStr);
+                if (mSubtypeSplitter.hasNext()) {
+                    ArrayList<String> subtypeHashes = new ArrayList<String>();
+                    // The first element is ime id.
+                    String imeId = mSubtypeSplitter.next();
+                    while (mSubtypeSplitter.hasNext()) {
+                        subtypeHashes.add(mSubtypeSplitter.next());
+                    }
+                    imsList.add(new Pair<String, ArrayList<String>>(imeId, subtypeHashes));
+                }
+            }
+            return imsList;
+        }
+
+        public void appendAndPutEnabledInputMethodLocked(String id, boolean reloadInputMethodStr) {
+            if (reloadInputMethodStr) {
+                getEnabledInputMethodsStr();
+            }
+            if (TextUtils.isEmpty(mEnabledInputMethodsStrCache)) {
+                // Add in the newly enabled input method.
+                putEnabledInputMethodsStr(id);
+            } else {
+                putEnabledInputMethodsStr(
+                        mEnabledInputMethodsStrCache + INPUT_METHOD_SEPARATER + id);
+            }
+        }
+
+        /**
+         * Build and put a string of EnabledInputMethods with removing specified Id.
+         * @return the specified id was removed or not.
+         */
+        public boolean buildAndPutEnabledInputMethodsStrRemovingIdLocked(
+                StringBuilder builder, List<Pair<String, ArrayList<String>>> imsList, String id) {
+            boolean isRemoved = false;
+            boolean needsAppendSeparator = false;
+            for (Pair<String, ArrayList<String>> ims: imsList) {
+                String curId = ims.first;
+                if (curId.equals(id)) {
+                    // We are disabling this input method, and it is
+                    // currently enabled.  Skip it to remove from the
+                    // new list.
+                    isRemoved = true;
+                } else {
+                    if (needsAppendSeparator) {
+                        builder.append(INPUT_METHOD_SEPARATER);
+                    } else {
+                        needsAppendSeparator = true;
+                    }
+                    buildEnabledInputMethodsSettingString(builder, ims);
+                }
+            }
+            if (isRemoved) {
+                // Update the setting with the new list of input methods.
+                putEnabledInputMethodsStr(builder.toString());
+            }
+            return isRemoved;
+        }
+
+        private List<InputMethodInfo> createEnabledInputMethodListLocked(
+                List<Pair<String, ArrayList<String>>> imsList) {
+            final ArrayList<InputMethodInfo> res = new ArrayList<InputMethodInfo>();
+            for (Pair<String, ArrayList<String>> ims: imsList) {
+                InputMethodInfo info = mMethodMap.get(ims.first);
+                if (info != null) {
+                    res.add(info);
+                }
+            }
+            return res;
+        }
+
+        private List<Pair<InputMethodInfo, ArrayList<String>>>
+                createEnabledInputMethodAndSubtypeHashCodeListLocked(
+                        List<Pair<String, ArrayList<String>>> imsList) {
+            final ArrayList<Pair<InputMethodInfo, ArrayList<String>>> res
+                    = new ArrayList<Pair<InputMethodInfo, ArrayList<String>>>();
+            for (Pair<String, ArrayList<String>> ims : imsList) {
+                InputMethodInfo info = mMethodMap.get(ims.first);
+                if (info != null) {
+                    res.add(new Pair<InputMethodInfo, ArrayList<String>>(info, ims.second));
+                }
+            }
+            return res;
+        }
+
+        private void putEnabledInputMethodsStr(String str) {
+            Settings.Secure.putStringForUser(
+                    mResolver, Settings.Secure.ENABLED_INPUT_METHODS, str, mCurrentUserId);
+            mEnabledInputMethodsStrCache = str;
+            if (DEBUG) {
+                Slog.d(TAG, "putEnabledInputMethodStr: " + str);
+            }
+        }
+
+        private String getEnabledInputMethodsStr() {
+            mEnabledInputMethodsStrCache = Settings.Secure.getStringForUser(
+                    mResolver, Settings.Secure.ENABLED_INPUT_METHODS, mCurrentUserId);
+            if (DEBUG) {
+                Slog.d(TAG, "getEnabledInputMethodsStr: " + mEnabledInputMethodsStrCache
+                        + ", " + mCurrentUserId);
+            }
+            return mEnabledInputMethodsStrCache;
+        }
+
+        private void saveSubtypeHistory(
+                List<Pair<String, String>> savedImes, String newImeId, String newSubtypeId) {
+            StringBuilder builder = new StringBuilder();
+            boolean isImeAdded = false;
+            if (!TextUtils.isEmpty(newImeId) && !TextUtils.isEmpty(newSubtypeId)) {
+                builder.append(newImeId).append(INPUT_METHOD_SUBTYPE_SEPARATER).append(
+                        newSubtypeId);
+                isImeAdded = true;
+            }
+            for (Pair<String, String> ime: savedImes) {
+                String imeId = ime.first;
+                String subtypeId = ime.second;
+                if (TextUtils.isEmpty(subtypeId)) {
+                    subtypeId = NOT_A_SUBTYPE_ID_STR;
+                }
+                if (isImeAdded) {
+                    builder.append(INPUT_METHOD_SEPARATER);
+                } else {
+                    isImeAdded = true;
+                }
+                builder.append(imeId).append(INPUT_METHOD_SUBTYPE_SEPARATER).append(
+                        subtypeId);
+            }
+            // Remove the last INPUT_METHOD_SEPARATER
+            putSubtypeHistoryStr(builder.toString());
+        }
+
+        private void addSubtypeToHistory(String imeId, String subtypeId) {
+            List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistoryLocked();
+            for (Pair<String, String> ime: subtypeHistory) {
+                if (ime.first.equals(imeId)) {
+                    if (DEBUG) {
+                        Slog.v(TAG, "Subtype found in the history: " + imeId + ", "
+                                + ime.second);
+                    }
+                    // We should break here
+                    subtypeHistory.remove(ime);
+                    break;
+                }
+            }
+            if (DEBUG) {
+                Slog.v(TAG, "Add subtype to the history: " + imeId + ", " + subtypeId);
+            }
+            saveSubtypeHistory(subtypeHistory, imeId, subtypeId);
+        }
+
+        private void putSubtypeHistoryStr(String str) {
+            if (DEBUG) {
+                Slog.d(TAG, "putSubtypeHistoryStr: " + str);
+            }
+            Settings.Secure.putStringForUser(
+                    mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, str, mCurrentUserId);
+        }
+
+        public Pair<String, String> getLastInputMethodAndSubtypeLocked() {
+            // Gets the first one from the history
+            return getLastSubtypeForInputMethodLockedInternal(null);
+        }
+
+        public String getLastSubtypeForInputMethodLocked(String imeId) {
+            Pair<String, String> ime = getLastSubtypeForInputMethodLockedInternal(imeId);
+            if (ime != null) {
+                return ime.second;
+            } else {
+                return null;
+            }
+        }
+
+        private Pair<String, String> getLastSubtypeForInputMethodLockedInternal(String imeId) {
+            List<Pair<String, ArrayList<String>>> enabledImes =
+                    getEnabledInputMethodsAndSubtypeListLocked();
+            List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistoryLocked();
+            for (Pair<String, String> imeAndSubtype : subtypeHistory) {
+                final String imeInTheHistory = imeAndSubtype.first;
+                // If imeId is empty, returns the first IME and subtype in the history
+                if (TextUtils.isEmpty(imeId) || imeInTheHistory.equals(imeId)) {
+                    final String subtypeInTheHistory = imeAndSubtype.second;
+                    final String subtypeHashCode =
+                            getEnabledSubtypeHashCodeForInputMethodAndSubtypeLocked(
+                                    enabledImes, imeInTheHistory, subtypeInTheHistory);
+                    if (!TextUtils.isEmpty(subtypeHashCode)) {
+                        if (DEBUG) {
+                            Slog.d(TAG, "Enabled subtype found in the history: " + subtypeHashCode);
+                        }
+                        return new Pair<String, String>(imeInTheHistory, subtypeHashCode);
+                    }
+                }
+            }
+            if (DEBUG) {
+                Slog.d(TAG, "No enabled IME found in the history");
+            }
+            return null;
+        }
+
+        private String getEnabledSubtypeHashCodeForInputMethodAndSubtypeLocked(List<Pair<String,
+                ArrayList<String>>> enabledImes, String imeId, String subtypeHashCode) {
+            for (Pair<String, ArrayList<String>> enabledIme: enabledImes) {
+                if (enabledIme.first.equals(imeId)) {
+                    final ArrayList<String> explicitlyEnabledSubtypes = enabledIme.second;
+                    final InputMethodInfo imi = mMethodMap.get(imeId);
+                    if (explicitlyEnabledSubtypes.size() == 0) {
+                        // If there are no explicitly enabled subtypes, applicable subtypes are
+                        // enabled implicitly.
+                        // If IME is enabled and no subtypes are enabled, applicable subtypes
+                        // are enabled implicitly, so needs to treat them to be enabled.
+                        if (imi != null && imi.getSubtypeCount() > 0) {
+                            List<InputMethodSubtype> implicitlySelectedSubtypes =
+                                    getImplicitlyApplicableSubtypesLocked(mRes, imi);
+                            if (implicitlySelectedSubtypes != null) {
+                                final int N = implicitlySelectedSubtypes.size();
+                                for (int i = 0; i < N; ++i) {
+                                    final InputMethodSubtype st = implicitlySelectedSubtypes.get(i);
+                                    if (String.valueOf(st.hashCode()).equals(subtypeHashCode)) {
+                                        return subtypeHashCode;
+                                    }
+                                }
+                            }
+                        }
+                    } else {
+                        for (String s: explicitlyEnabledSubtypes) {
+                            if (s.equals(subtypeHashCode)) {
+                                // If both imeId and subtypeId are enabled, return subtypeId.
+                                try {
+                                    final int hashCode = Integer.valueOf(subtypeHashCode);
+                                    // Check whether the subtype id is valid or not
+                                    if (isValidSubtypeId(imi, hashCode)) {
+                                        return s;
+                                    } else {
+                                        return NOT_A_SUBTYPE_ID_STR;
+                                    }
+                                } catch (NumberFormatException e) {
+                                    return NOT_A_SUBTYPE_ID_STR;
+                                }
+                            }
+                        }
+                    }
+                    // If imeId was enabled but subtypeId was disabled.
+                    return NOT_A_SUBTYPE_ID_STR;
+                }
+            }
+            // If both imeId and subtypeId are disabled, return null
+            return null;
+        }
+
+        private List<Pair<String, String>> loadInputMethodAndSubtypeHistoryLocked() {
+            ArrayList<Pair<String, String>> imsList = new ArrayList<Pair<String, String>>();
+            final String subtypeHistoryStr = getSubtypeHistoryStr();
+            if (TextUtils.isEmpty(subtypeHistoryStr)) {
+                return imsList;
+            }
+            mInputMethodSplitter.setString(subtypeHistoryStr);
+            while (mInputMethodSplitter.hasNext()) {
+                String nextImsStr = mInputMethodSplitter.next();
+                mSubtypeSplitter.setString(nextImsStr);
+                if (mSubtypeSplitter.hasNext()) {
+                    String subtypeId = NOT_A_SUBTYPE_ID_STR;
+                    // The first element is ime id.
+                    String imeId = mSubtypeSplitter.next();
+                    while (mSubtypeSplitter.hasNext()) {
+                        subtypeId = mSubtypeSplitter.next();
+                        break;
+                    }
+                    imsList.add(new Pair<String, String>(imeId, subtypeId));
+                }
+            }
+            return imsList;
+        }
+
+        private String getSubtypeHistoryStr() {
+            if (DEBUG) {
+                Slog.d(TAG, "getSubtypeHistoryStr: " + Settings.Secure.getStringForUser(
+                        mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, mCurrentUserId));
+            }
+            return Settings.Secure.getStringForUser(
+                    mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, mCurrentUserId);
+        }
+
+        public void putSelectedInputMethod(String imeId) {
+            if (DEBUG) {
+                Slog.d(TAG, "putSelectedInputMethodStr: " + imeId + ", "
+                        + mCurrentUserId);
+            }
+            Settings.Secure.putStringForUser(
+                    mResolver, Settings.Secure.DEFAULT_INPUT_METHOD, imeId, mCurrentUserId);
+        }
+
+        public void putSelectedSubtype(int subtypeId) {
+            if (DEBUG) {
+                Slog.d(TAG, "putSelectedInputMethodSubtypeStr: " + subtypeId + ", "
+                        + mCurrentUserId);
+            }
+            Settings.Secure.putIntForUser(mResolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
+                    subtypeId, mCurrentUserId);
+        }
+
+        public String getDisabledSystemInputMethods() {
+            return Settings.Secure.getStringForUser(
+                    mResolver, Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS, mCurrentUserId);
+        }
+
+        public String getSelectedInputMethod() {
+            if (DEBUG) {
+                Slog.d(TAG, "getSelectedInputMethodStr: " + Settings.Secure.getStringForUser(
+                        mResolver, Settings.Secure.DEFAULT_INPUT_METHOD, mCurrentUserId)
+                        + ", " + mCurrentUserId);
+            }
+            return Settings.Secure.getStringForUser(
+                    mResolver, Settings.Secure.DEFAULT_INPUT_METHOD, mCurrentUserId);
+        }
+
+        public boolean isSubtypeSelected() {
+            return getSelectedInputMethodSubtypeHashCode() != NOT_A_SUBTYPE_ID;
+        }
+
+        private int getSelectedInputMethodSubtypeHashCode() {
+            try {
+                return Settings.Secure.getIntForUser(
+                        mResolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, mCurrentUserId);
+            } catch (SettingNotFoundException e) {
+                return NOT_A_SUBTYPE_ID;
+            }
+        }
+
+        public int getCurrentUserId() {
+            return mCurrentUserId;
+        }
+
+        public int getSelectedInputMethodSubtypeId(String selectedImiId) {
+            final InputMethodInfo imi = mMethodMap.get(selectedImiId);
+            if (imi == null) {
+                return NOT_A_SUBTYPE_ID;
+            }
+            final int subtypeHashCode = getSelectedInputMethodSubtypeHashCode();
+            return getSubtypeIdFromHashCode(imi, subtypeHashCode);
+        }
+
+        public void saveCurrentInputMethodAndSubtypeToHistory(
+                String curMethodId, InputMethodSubtype currentSubtype) {
+            String subtypeId = NOT_A_SUBTYPE_ID_STR;
+            if (currentSubtype != null) {
+                subtypeId = String.valueOf(currentSubtype.hashCode());
+            }
+            if (canAddToLastInputMethod(currentSubtype)) {
+                addSubtypeToHistory(curMethodId, subtypeId);
+            }
+        }
+    }
+}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index d3ba11a..d705024 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -91,7 +91,7 @@
 	android/graphics/DrawFilter.cpp \
 	android/graphics/CreateJavaOutputStreamAdaptor.cpp \
 	android/graphics/Graphics.cpp \
-	android/graphics/HarfbuzzSkia.cpp \
+	android/graphics/HarfBuzzNGFaceSkia.cpp \
 	android/graphics/Interpolator.cpp \
 	android/graphics/LayerRasterizer.cpp \
 	android/graphics/MaskFilter.cpp \
@@ -170,8 +170,7 @@
 	external/icu4c/i18n \
 	external/icu4c/common \
 	external/jpeg \
-	external/harfbuzz/contrib \
-	external/harfbuzz/src \
+	external/harfbuzz_ng/src \
 	external/zlib \
 	frameworks/opt/emoji \
 	libcore/include
@@ -206,7 +205,7 @@
 	libwpa_client \
 	libjpeg \
 	libusbhost \
-	libharfbuzz \
+	libharfbuzz_ng \
 	libz
 
 ifeq ($(USE_OPENGL_RENDERER),true)
diff --git a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
new file mode 100644
index 0000000..1752e5b
--- /dev/null
+++ b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "TextLayoutCache"
+
+#include "HarfBuzzNGFaceSkia.h"
+
+#include <cutils/log.h>
+#include <SkFontHost.h>
+#include <SkPaint.h>
+#include <SkPath.h>
+#include <SkPoint.h>
+#include <SkRect.h>
+#include <SkUtils.h>
+
+#include <hb.h>
+
+namespace android {
+
+// Our implementation of the callbacks which Harfbuzz requires by using Skia
+// calls. See the Harfbuzz source for references about what these callbacks do.
+
+struct HarfBuzzFontData {
+    HarfBuzzFontData(SkPaint* paint) : m_paint(paint) { }
+    SkPaint* m_paint;
+};
+
+static void SkiaGetGlyphWidthAndExtents(SkPaint* paint, hb_codepoint_t codepoint, hb_position_t* width, hb_glyph_extents_t* extents)
+{
+    ALOG_ASSERT(codepoint <= 0xFFFF);
+    paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+    SkScalar skWidth;
+    SkRect skBounds;
+    uint16_t glyph = codepoint;
+
+    paint->getTextWidths(&glyph, sizeof(glyph), &skWidth, &skBounds);
+    ALOGD("returned glyph for %i: width = %f", codepoint, skWidth);
+    if (width)
+        *width = SkScalarToHBFixed(skWidth);
+    if (extents) {
+        // Invert y-axis because Skia is y-grows-down but we set up harfbuzz to be y-grows-up.
+        extents->x_bearing = SkScalarToHBFixed(skBounds.fLeft);
+        extents->y_bearing = SkScalarToHBFixed(-skBounds.fTop);
+        extents->width = SkScalarToHBFixed(skBounds.width());
+        extents->height = SkScalarToHBFixed(-skBounds.height());
+    }
+}
+
+static hb_bool_t harfbuzzGetGlyph(hb_font_t* hbFont, void* fontData, hb_codepoint_t unicode, hb_codepoint_t variationSelector, hb_codepoint_t* glyph, void* userData)
+{
+    HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
+
+    if (unicode > 0x10ffff) {
+        unicode = 0xfffd;
+    }
+    SkPaint* paint = hbFontData->m_paint;
+    // It would be better to use kUTF32_TextEncoding directly
+    paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
+    uint16_t glyph16;
+    uint16_t unichar[2];
+    size_t size = SkUTF16_FromUnichar(unicode, unichar);
+    paint->textToGlyphs(unichar, size * sizeof(*unichar), &glyph16);
+    *glyph = glyph16;
+    return !!*glyph;
+}
+
+static hb_position_t harfbuzzGetGlyphHorizontalAdvance(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, void* userData)
+{
+    HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
+    hb_position_t advance = 0;
+
+    SkiaGetGlyphWidthAndExtents(hbFontData->m_paint, glyph, &advance, 0);
+    return advance;
+}
+
+static hb_bool_t harfbuzzGetGlyphHorizontalOrigin(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, hb_position_t* x, hb_position_t* y, void* userData)
+{
+    // Just return true, following the way that Harfbuzz-FreeType
+    // implementation does.
+    return true;
+}
+
+static hb_bool_t harfbuzzGetGlyphExtents(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, hb_glyph_extents_t* extents, void* userData)
+{
+    HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
+
+    SkiaGetGlyphWidthAndExtents(hbFontData->m_paint, glyph, 0, extents);
+    return true;
+}
+
+static hb_font_funcs_t* harfbuzzSkiaGetFontFuncs()
+{
+    static hb_font_funcs_t* harfbuzzSkiaFontFuncs = 0;
+
+    // We don't set callback functions which we can't support.
+    // Harfbuzz will use the fallback implementation if they aren't set.
+    if (!harfbuzzSkiaFontFuncs) {
+        harfbuzzSkiaFontFuncs = hb_font_funcs_create();
+        hb_font_funcs_set_glyph_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyph, 0, 0);
+        hb_font_funcs_set_glyph_h_advance_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyphHorizontalAdvance, 0, 0);
+        hb_font_funcs_set_glyph_h_origin_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyphHorizontalOrigin, 0, 0);
+        hb_font_funcs_set_glyph_extents_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyphExtents, 0, 0);
+        hb_font_funcs_make_immutable(harfbuzzSkiaFontFuncs);
+    }
+    return harfbuzzSkiaFontFuncs;
+}
+
+hb_blob_t* harfbuzzSkiaReferenceTable(hb_face_t* face, hb_tag_t tag, void* userData)
+{
+    SkTypeface* typeface = reinterpret_cast<SkTypeface*>(userData);
+    SkFontID uniqueID = typeface->uniqueID();
+
+    const size_t tableSize = SkFontHost::GetTableSize(uniqueID, tag);
+    if (!tableSize)
+        return 0;
+
+    char* buffer = reinterpret_cast<char*>(malloc(tableSize));
+    if (!buffer)
+        return 0;
+    size_t actualSize = SkFontHost::GetTableData(uniqueID, tag, 0, tableSize, buffer);
+    if (tableSize != actualSize) {
+        free(buffer);
+        return 0;
+    }
+
+    return hb_blob_create(const_cast<char*>(buffer), tableSize,
+                          HB_MEMORY_MODE_WRITABLE, buffer, free);
+}
+
+static void destroyHarfBuzzFontData(void* data) {
+    delete (HarfBuzzFontData*)data;
+}
+
+hb_font_t* createFont(hb_face_t* face, SkPaint* paint, float sizeX, float sizeY) {
+    hb_font_t* font = hb_font_create(face);
+    
+    // Note: this needs to be reworked when we do subpixels
+    int x_ppem = floor(sizeX + 0.5);
+    int y_ppem = floor(sizeY + 0.5);
+    hb_font_set_ppem(font, x_ppem, y_ppem); 
+    hb_font_set_scale(font, HBFloatToFixed(sizeX), HBFloatToFixed(sizeY));
+
+    HarfBuzzFontData* data = new HarfBuzzFontData(paint);
+    hb_font_set_funcs(font, harfbuzzSkiaGetFontFuncs(), data, destroyHarfBuzzFontData);
+
+    return font;
+}
+
+} // namespace android
diff --git a/core/jni/android/graphics/HarfbuzzSkia.h b/core/jni/android/graphics/HarfBuzzNGFaceSkia.h
similarity index 71%
rename from core/jni/android/graphics/HarfbuzzSkia.h
rename to core/jni/android/graphics/HarfBuzzNGFaceSkia.h
index 2772f4d..7b71ecc 100644
--- a/core/jni/android/graphics/HarfbuzzSkia.h
+++ b/core/jni/android/graphics/HarfBuzzNGFaceSkia.h
@@ -24,31 +24,35 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef HarfbuzzSkia_h
-#define HarfbuzzSkia_h
+#ifndef HarfBuzzNGFaceSkia_h
+#define HarfBuzzNGFaceSkia_h
 
-#include "SkScalar.h"
-#include "SkTypeface.h"
-#include "SkPaint.h"
+#include <SkScalar.h>
+#include <SkPaint.h>
 
-extern "C" {
-#include "harfbuzz-shaper.h"
-}
+#include <hb.h>
 
 namespace android {
 
-static inline float HBFixedToFloat(HB_Fixed v) {
-    // Harfbuzz uses 26.6 fixed point values for pixel offsets
-    return v * (1.0f / 64);
+static inline float
+HBFixedToFloat (hb_position_t v)
+{
+    return scalbnf (v, -8);
 }
 
-static inline HB_Fixed SkScalarToHBFixed(SkScalar value) {
-    // HB_Fixed is a 26.6 fixed point format.
-    return SkScalarToFloat(value) * 64.0f;
+static inline hb_position_t
+HBFloatToFixed (float v)
+{
+    return scalbnf (v, +8);
 }
 
-HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag, HB_Byte* buffer, HB_UInt* len);
-extern const HB_FontClass harfbuzzSkiaClass;
+static inline hb_position_t SkScalarToHBFixed(SkScalar value) {
+    return HBFloatToFixed(SkScalarToFloat(value));
+}
+
+hb_blob_t* harfbuzzSkiaReferenceTable(hb_face_t* face, hb_tag_t tag, void* userData);
+
+hb_font_t* createFont(hb_face_t* face, SkPaint* paint, float sizeX, float sizeY);
 
 }  // namespace android
 
diff --git a/core/jni/android/graphics/HarfbuzzSkia.cpp b/core/jni/android/graphics/HarfbuzzSkia.cpp
deleted file mode 100644
index 7e08379..0000000
--- a/core/jni/android/graphics/HarfbuzzSkia.cpp
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright 2011, The Android Open Source Project
- * Copyright 2011, Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#define LOG_TAG "HarfbuzzSkia"
-
-#include "HarfbuzzSkia.h"
-
-#include "SkFontHost.h"
-
-#include "SkPaint.h"
-#include "SkPath.h"
-#include "SkPoint.h"
-#include "SkRect.h"
-#include "SkTypeface.h"
-
-#include <utils/Log.h>
-
-extern "C" {
-#include "harfbuzz-shaper.h"
-}
-
-// This file implements the callbacks which Harfbuzz requires by using Skia
-// calls. See the Harfbuzz source for references about what these callbacks do.
-
-namespace android {
-
-static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length,
-        HB_Glyph* glyphs, hb_uint32* glyphsSize, HB_Bool isRTL)
-{
-    SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
-    paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
-
-    uint16_t* skiaGlyphs = reinterpret_cast<uint16_t*>(glyphs);
-    int numGlyphs = paint->textToGlyphs(characters, length * sizeof(uint16_t), skiaGlyphs);
-
-    // HB_Glyph is 32-bit, but Skia outputs only 16-bit numbers. So our
-    // |glyphs| array needs to be converted.
-    for (int i = numGlyphs - 1; i >= 0; --i) {
-        glyphs[i] = skiaGlyphs[i];
-    }
-
-    *glyphsSize = numGlyphs;
-    return 1;
-}
-
-static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, hb_uint32 numGlyphs,
-        HB_Fixed* advances, int flags)
-{
-    SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
-    paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-
-    uint16_t* glyphs16 = new uint16_t[numGlyphs];
-    if (!glyphs16)
-        return;
-    for (unsigned i = 0; i < numGlyphs; ++i)
-        glyphs16[i] = glyphs[i];
-    SkScalar* scalarAdvances = reinterpret_cast<SkScalar*>(advances);
-    paint->getTextWidths(glyphs16, numGlyphs * sizeof(uint16_t), scalarAdvances);
-
-    // The |advances| values which Skia outputs are SkScalars, which are floats
-    // in Chromium. However, Harfbuzz wants them in 26.6 fixed point format.
-    // These two formats are both 32-bits long.
-    for (unsigned i = 0; i < numGlyphs; ++i) {
-        advances[i] = SkScalarToHBFixed(scalarAdvances[i]);
-#if DEBUG_ADVANCES
-        ALOGD("glyphsToAdvances -- advances[%d]=%d", i, advances[i]);
-#endif
-    }
-    delete glyphs16;
-}
-
-static HB_Bool canRender(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length)
-{
-    SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
-    paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
-
-    uint16_t* glyphs16 = new uint16_t[length];
-    int numGlyphs = paint->textToGlyphs(characters, length * sizeof(uint16_t), glyphs16);
-
-    bool result = true;
-    for (int i = 0; i < numGlyphs; ++i) {
-        if (!glyphs16[i]) {
-            result = false;
-            break;
-        }
-    }
-    delete glyphs16;
-    return result;
-}
-
-static HB_Error getOutlinePoint(HB_Font hbFont, HB_Glyph glyph, int flags, hb_uint32 point,
-        HB_Fixed* xPos, HB_Fixed* yPos, hb_uint32* resultingNumPoints)
-{
-    if (flags & HB_ShaperFlag_UseDesignMetrics)
-        // This is requesting pre-hinted positions. We can't support this.
-        return HB_Err_Invalid_Argument;
-
-    SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
-    paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-
-    uint16_t glyph16 = glyph;
-    SkPath path;
-    paint->getTextPath(&glyph16, sizeof(glyph16), 0, 0, &path);
-    uint32_t numPoints = path.getPoints(0, 0);
-    if (point >= numPoints)
-        return HB_Err_Invalid_SubTable;
-    SkPoint* points = static_cast<SkPoint*>(malloc(sizeof(SkPoint) * (point + 1)));
-    if (!points)
-        return HB_Err_Invalid_SubTable;
-    // Skia does let us get a single point from the path.
-    path.getPoints(points, point + 1);
-    *xPos = SkScalarToHBFixed(points[point].fX);
-    *yPos = SkScalarToHBFixed(points[point].fY);
-    *resultingNumPoints = numPoints;
-    delete points;
-
-    return HB_Err_Ok;
-}
-
-static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, HB_GlyphMetrics* metrics)
-{
-    SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
-    paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-
-    uint16_t glyph16 = glyph;
-    SkScalar width;
-    SkRect bounds;
-    paint->getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds);
-
-    metrics->x = SkScalarToHBFixed(bounds.fLeft);
-    metrics->y = SkScalarToHBFixed(bounds.fTop);
-    metrics->width = SkScalarToHBFixed(bounds.width());
-    metrics->height = SkScalarToHBFixed(bounds.height());
-
-    metrics->xOffset = SkScalarToHBFixed(width);
-    // We can't actually get the |y| correct because Skia doesn't export
-    // the vertical advance. However, nor we do ever render vertical text at
-    // the moment so it's unimportant.
-    metrics->yOffset = 0;
-}
-
-static HB_Fixed getFontMetric(HB_Font hbFont, HB_FontMetric metric)
-{
-    SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
-
-    SkPaint::FontMetrics skiaMetrics;
-    paint->getFontMetrics(&skiaMetrics);
-
-    switch (metric) {
-    case HB_FontAscent:
-        return SkScalarToHBFixed(-skiaMetrics.fAscent);
-    // We don't support getting the rest of the metrics and Harfbuzz doesn't seem to need them.
-    default:
-        return 0;
-    }
-    return 0;
-}
-
-const HB_FontClass harfbuzzSkiaClass = {
-    stringToGlyphs,
-    glyphsToAdvances,
-    canRender,
-    getOutlinePoint,
-    getGlyphMetrics,
-    getFontMetric,
-};
-
-HB_Error harfbuzzSkiaGetTable(void* font, const HB_Tag tag, HB_Byte* buffer, HB_UInt* len)
-{
-    SkTypeface* typeface = static_cast<SkTypeface*>(font);
-
-    if (!typeface) {
-        ALOGD("Typeface cannot be null");
-        return HB_Err_Invalid_Argument;
-    }
-    const size_t tableSize = SkFontHost::GetTableSize(typeface->uniqueID(), tag);
-    if (!tableSize)
-        return HB_Err_Invalid_Argument;
-    // If Harfbuzz specified a NULL buffer then it's asking for the size of the table.
-    if (!buffer) {
-        *len = tableSize;
-        return HB_Err_Ok;
-    }
-
-    if (*len < tableSize)
-        return HB_Err_Invalid_Argument;
-    SkFontHost::GetTableData(typeface->uniqueID(), tag, 0, tableSize, buffer);
-    return HB_Err_Ok;
-}
-
-}  // namespace android
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index dce48a3..6d3c878 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -22,13 +22,10 @@
 #include "TextLayout.h"
 #include "SkFontHost.h"
 #include "SkTypeface_android.h"
+#include "HarfBuzzNGFaceSkia.h"
 #include <unicode/unistr.h>
-#include <unicode/normlzr.h>
 #include <unicode/uchar.h>
-
-extern "C" {
-  #include "harfbuzz-unicode.h"
-}
+#include <hb-icu.h>
 
 namespace android {
 
@@ -341,20 +338,10 @@
     return mElapsedTime;
 }
 
-TextLayoutShaper::TextLayoutShaper() : mShaperItemGlyphArraySize(0) {
+TextLayoutShaper::TextLayoutShaper() {
     init();
 
-    mFontRec.klass = &harfbuzzSkiaClass;
-    mFontRec.userData = 0;
-
-    // Note that the scaling values (x_ and y_ppem, x_ and y_scale) will be set
-    // below, when the paint transform and em unit of the actual shaping font
-    // are known.
-
-    memset(&mShaperItem, 0, sizeof(mShaperItem));
-
-    mShaperItem.font = &mFontRec;
-    mShaperItem.font->userData = &mShapingPaint;
+    mBuffer = hb_buffer_create();
 }
 
 void TextLayoutShaper::init() {
@@ -366,8 +353,9 @@
 }
 
 TextLayoutShaper::~TextLayoutShaper() {
+    hb_buffer_destroy(mBuffer);
+
     unrefTypefaces();
-    deleteShaperItemGlyphArrays();
 }
 
 void TextLayoutShaper::computeValues(TextLayoutValue* value, const SkPaint* paint, const UChar* chars,
@@ -394,7 +382,7 @@
         bool forceLTR = false;
         bool forceRTL = false;
 
-        switch (dirFlags) {
+        switch (dirFlags & kBidi_Mask) {
             case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level
             case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level
             case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break;
@@ -478,7 +466,7 @@
                             ALOGD("Processing Bidi Run = %d -- run-start = %d, run-len = %d, isRTL = %d",
                                     i, startRun, lengthRun, isRTL);
 #endif
-                            computeRunValues(paint, chars + startRun, lengthRun, isRTL,
+                            computeRunValues(paint, chars, startRun, lengthRun, contextCount, isRTL,
                                     outAdvances, outTotalAdvance, outGlyphs, outPos);
 
                         }
@@ -502,7 +490,7 @@
             ALOGD("Using a SINGLE BiDi Run "
                     "-- run-start = %d, run-len = %d, isRTL = %d", start, count, isRTL);
 #endif
-            computeRunValues(paint, chars + start, count, isRTL,
+            computeRunValues(paint, chars, start, count, contextCount, isRTL,
                     outAdvances, outTotalAdvance, outGlyphs, outPos);
         }
 
@@ -512,18 +500,197 @@
 #endif
 }
 
-static void logGlyphs(HB_ShaperItem shaperItem) {
-    ALOGD("         -- glyphs count=%d", shaperItem.num_glyphs);
-    for (size_t i = 0; i < shaperItem.num_glyphs; i++) {
-        ALOGD("         -- glyph[%d] = %d, offset.x = %0.2f, offset.y = %0.2f", i,
-                shaperItem.glyphs[i],
-                HBFixedToFloat(shaperItem.offsets[i].x),
-                HBFixedToFloat(shaperItem.offsets[i].y));
+#define HB_IsHighSurrogate(ucs) \
+    (((ucs) & 0xfc00) == 0xd800)
+
+#define HB_IsLowSurrogate(ucs) \
+    (((ucs) & 0xfc00) == 0xdc00)
+
+#ifndef HB_SurrogateToUcs4
+#define HB_SurrogateToUcs4_(high, low) \
+    (((hb_codepoint_t)(high))<<10) + (low) - 0x35fdc00;
+#endif
+
+#define HB_InvalidCodePoint ~0u
+
+hb_codepoint_t
+utf16_to_code_point(const uint16_t *chars, size_t len, ssize_t *iter) {
+  const uint16_t v = chars[(*iter)++];
+  if (HB_IsHighSurrogate(v)) {
+    // surrogate pair
+    if (size_t(*iter) >= len) {
+      // the surrogate is incomplete.
+      return HB_InvalidCodePoint;
+    }
+    const uint16_t v2 = chars[(*iter)++];
+    if (!HB_IsLowSurrogate(v2)) {
+      // invalidate surrogate pair.
+      (*iter)--;
+      return HB_InvalidCodePoint;
+    }
+
+    return HB_SurrogateToUcs4(v, v2);
+  }
+
+  if (HB_IsLowSurrogate(v)) {
+    // this isn't a valid code point
+    return HB_InvalidCodePoint;
+  }
+
+  return v;
+}
+
+hb_codepoint_t
+utf16_to_code_point_prev(const uint16_t *chars, size_t len, ssize_t *iter) {
+  const uint16_t v = chars[(*iter)--];
+  if (HB_IsLowSurrogate(v)) {
+    // surrogate pair
+    if (*iter < 0) {
+      // the surrogate is incomplete.
+      return HB_InvalidCodePoint;
+    }
+    const uint16_t v2 = chars[(*iter)--];
+    if (!HB_IsHighSurrogate(v2)) {
+      // invalidate surrogate pair.
+      (*iter)++;
+      return HB_InvalidCodePoint;
+    }
+
+    return HB_SurrogateToUcs4(v2, v);
+  }
+
+  if (HB_IsHighSurrogate(v)) {
+    // this isn't a valid code point
+    return HB_InvalidCodePoint;
+  }
+
+  return v;
+}
+
+struct ScriptRun {
+    hb_script_t script;
+    size_t pos;
+    size_t length;
+};
+
+hb_script_t code_point_to_script(hb_codepoint_t codepoint) {
+    static hb_unicode_funcs_t* u;
+    if (!u) {
+        u = hb_icu_get_unicode_funcs();
+    }
+    return hb_unicode_script(u, codepoint);
+}
+
+bool
+hb_utf16_script_run_next(ScriptRun* run, const uint16_t *chars, size_t len, ssize_t *iter) {
+  if (size_t(*iter) == len)
+    return false;
+
+  run->pos = *iter;
+  const uint32_t init_cp = utf16_to_code_point(chars, len, iter);
+  const hb_script_t init_script = code_point_to_script(init_cp);
+  hb_script_t current_script = init_script;
+  run->script = init_script;
+
+  for (;;) {
+    if (size_t(*iter) == len)
+      break;
+    const ssize_t prev_iter = *iter;
+    const uint32_t cp = utf16_to_code_point(chars, len, iter);
+    const hb_script_t script = code_point_to_script(cp);
+
+    if (script != current_script) {
+        /* BEGIN android-changed
+           The condition was not correct by doing "a == b == constant"
+           END android-changed */
+      if (current_script == HB_SCRIPT_INHERITED && init_script == HB_SCRIPT_INHERITED) {
+        // If we started off as inherited, we take whatever we can find.
+        run->script = script;
+        current_script = script;
+        continue;
+      } else if (script == HB_SCRIPT_INHERITED) {
+        continue;
+      } else {
+        *iter = prev_iter;
+        break;
+      }
+    }
+  }
+
+  if (run->script == HB_SCRIPT_INHERITED)
+    run->script = HB_SCRIPT_COMMON;
+
+  run->length = *iter - run->pos;
+  return true;
+}
+
+bool
+hb_utf16_script_run_prev(ScriptRun* run, const uint16_t *chars, size_t len, ssize_t *iter) {
+  if (*iter == -1)
+    return false;
+
+  const size_t ending_index = *iter;
+  const uint32_t init_cp = utf16_to_code_point_prev(chars, len, iter);
+  const hb_script_t init_script = code_point_to_script(init_cp);
+  hb_script_t current_script = init_script;
+  run->script = init_script;
+
+  for (;;) {
+    if (*iter < 0)
+      break;
+    const ssize_t prev_iter = *iter;
+    const uint32_t cp = utf16_to_code_point_prev(chars, len, iter);
+    const hb_script_t script = code_point_to_script(cp);
+
+    if (script != current_script) {
+      if (current_script == HB_SCRIPT_INHERITED && init_script == HB_SCRIPT_INHERITED) {
+        // If we started off as inherited, we take whatever we can find.
+        run->script = script;
+        current_script = script;
+        continue;
+      } else if (script == HB_SCRIPT_INHERITED) {
+        /* BEGIN android-changed
+           We apply the same fix for Chrome to Android.
+           Chrome team will talk with upsteam about it.
+           Just assume that whatever follows this combining character is within
+           the same script.  This is incorrect if you had language1 + combining
+           char + language 2, but that is rare and this code is suspicious
+           anyway.
+           END android-changed */
+        continue;
+      } else {
+        *iter = prev_iter;
+        break;
+      }
+    }
+  }
+
+  if (run->script == HB_SCRIPT_INHERITED)
+    run->script = HB_SCRIPT_COMMON;
+
+  run->pos = *iter + 1;
+  run->length = ending_index - *iter;
+  return true;
+}
+
+
+static void logGlyphs(hb_buffer_t* buffer) {
+    unsigned int numGlyphs;
+    hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, &numGlyphs);
+    hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(buffer, NULL);
+    ALOGD("         -- glyphs count=%d", numGlyphs);
+    for (size_t i = 0; i < numGlyphs; i++) {
+        ALOGD("         -- glyph[%d] = %d, cluster = %u, advance = %0.2f, offset.x = %0.2f, offset.y = %0.2f", i,
+                info[i].codepoint,
+                info[i].cluster,
+                HBFixedToFloat(positions[i].x_advance),
+                HBFixedToFloat(positions[i].x_offset),
+                HBFixedToFloat(positions[i].y_offset));
     }
 }
 
-void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* chars,
-        size_t count, bool isRTL,
+void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* contextChars,
+        size_t start, size_t count, size_t contextCount, bool isRTL,
         Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
         Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) {
     if (!count) {
@@ -535,95 +702,9 @@
     for (size_t i = 0; i < count; i++) {
         outAdvances->add(0);
     }
-    UErrorCode error = U_ZERO_ERROR;
-    bool useNormalizedString = false;
-    for (ssize_t i = count - 1; i >= 0; --i) {
-        UChar ch1 = chars[i];
-        if (::ublock_getCode(ch1) == UBLOCK_COMBINING_DIACRITICAL_MARKS) {
-            // So we have found a diacritic, let's get now the main code point which is paired
-            // with it. As we can have several diacritics in a row, we need to iterate back again
-#if DEBUG_GLYPHS
-            ALOGD("The BiDi run '%s' is containing a Diacritic at position %d",
-                    String8(chars, count).string(), int(i));
-#endif
-            ssize_t j = i - 1;
-            for (; j >= 0;  --j) {
-                UChar ch2 = chars[j];
-                if (::ublock_getCode(ch2) != UBLOCK_COMBINING_DIACRITICAL_MARKS) {
-                    break;
-                }
-            }
-
-            // We could not found the main code point, so we will just use the initial chars
-            if (j < 0) {
-                break;
-            }
-
-#if DEBUG_GLYPHS
-            ALOGD("Found main code point at index %d", int(j));
-#endif
-            // We found the main code point, so we can normalize the "chunk" and fill
-            // the remaining with ZWSP so that the Paint.getTextWidth() APIs will still be able
-            // to get one advance per char
-            mBuffer.remove();
-            Normalizer::normalize(UnicodeString(chars + j, i - j + 1),
-                    UNORM_NFC, 0 /* no options */, mBuffer, error);
-            if (U_SUCCESS(error)) {
-                if (!useNormalizedString) {
-                    useNormalizedString = true;
-                    mNormalizedString.setTo(false /* not terminated*/, chars, count);
-                }
-                // Set the normalized chars
-                for (ssize_t k = j; k < j + mBuffer.length(); ++k) {
-                    mNormalizedString.setCharAt(k, mBuffer.charAt(k - j));
-                }
-                // Fill the remain part with ZWSP (ZWNJ and ZWJ would lead to weird results
-                // because some fonts are missing those glyphs)
-                for (ssize_t k = j + mBuffer.length(); k <= i; ++k) {
-                    mNormalizedString.setCharAt(k, UNICODE_ZWSP);
-                }
-            }
-            i = j - 1;
-        }
-    }
-
-    // Reverse "BiDi mirrored chars" in RTL mode only
-    // See: http://www.unicode.org/Public/6.0.0/ucd/extracted/DerivedBinaryProperties.txt
-    // This is a workaround because Harfbuzz is not able to do mirroring in all cases and
-    // script-run splitting with Harfbuzz is splitting on parenthesis
-    if (isRTL) {
-        for (ssize_t i = 0; i < ssize_t(count); i++) {
-            UChar32 ch = chars[i];
-            if (!u_isMirrored(ch)) continue;
-            if (!useNormalizedString) {
-                useNormalizedString = true;
-                mNormalizedString.setTo(false /* not terminated*/, chars, count);
-            }
-            UChar result =  (UChar) u_charMirror(ch);
-            mNormalizedString.setCharAt(i, result);
-#if DEBUG_GLYPHS
-            ALOGD("Rewriting codepoint '%d' to '%d' at position %d",
-                    ch, mNormalizedString[i], int(i));
-#endif
-        }
-    }
-
-#if DEBUG_GLYPHS
-    if (useNormalizedString) {
-        ALOGD("Will use normalized string '%s', length = %d",
-                    String8(mNormalizedString.getTerminatedBuffer(),
-                            mNormalizedString.length()).string(),
-                    mNormalizedString.length());
-    } else {
-        ALOGD("Normalization is not needed or cannot be done, using initial string");
-    }
-#endif
-
-    assert(mNormalizedString.length() == count);
 
     // Set the string properties
-    mShaperItem.string = useNormalizedString ? mNormalizedString.getTerminatedBuffer() : chars;
-    mShaperItem.stringLength = count;
+    const UChar* chars = contextChars + start;
 
     // Define shaping paint properties
     mShapingPaint.setTextSize(paint->getTextSize());
@@ -637,130 +718,66 @@
 
     // Split the BiDi run into Script runs. Harfbuzz will populate the pos, length and script
     // into the shaperItem
-    ssize_t indexFontRun = isRTL ? mShaperItem.stringLength - 1 : 0;
-    unsigned numCodePoints = 0;
+    ssize_t indexFontRun = isRTL ? count - 1 : 0;
     jfloat totalAdvance = *outTotalAdvance;
+    ScriptRun run;  // relative to chars
     while ((isRTL) ?
-            hb_utf16_script_run_prev(&numCodePoints, &mShaperItem.item, mShaperItem.string,
-                    mShaperItem.stringLength, &indexFontRun):
-            hb_utf16_script_run_next(&numCodePoints, &mShaperItem.item, mShaperItem.string,
-                    mShaperItem.stringLength, &indexFontRun)) {
-
-        ssize_t startScriptRun = mShaperItem.item.pos;
-        size_t countScriptRun = mShaperItem.item.length;
-        ssize_t endScriptRun = startScriptRun + countScriptRun;
+            hb_utf16_script_run_prev(&run, chars, count, &indexFontRun):
+            hb_utf16_script_run_next(&run, chars, count, &indexFontRun)) {
 
 #if DEBUG_GLYPHS
         ALOGD("-------- Start of Script Run --------");
         ALOGD("Shaping Script Run with");
         ALOGD("         -- isRTL = %d", isRTL);
-        ALOGD("         -- HB script = %d", mShaperItem.item.script);
-        ALOGD("         -- startFontRun = %d", int(startScriptRun));
-        ALOGD("         -- endFontRun = %d", int(endScriptRun));
-        ALOGD("         -- countFontRun = %d", countScriptRun);
-        ALOGD("         -- run = '%s'", String8(chars + startScriptRun, countScriptRun).string());
+        ALOGD("         -- HB script = %c%c%c%c", HB_UNTAG(run.script));
+        ALOGD("         -- run.pos = %d", int(run.pos));
+        ALOGD("         -- run.length = %d", int(run.length));
+        ALOGD("         -- run = '%s'", String8(chars + run.pos, run.length).string());
         ALOGD("         -- string = '%s'", String8(chars, count).string());
 #endif
 
+        hb_buffer_reset(mBuffer);
+        // Note: if we want to set unicode functions, etc., this is the place.
+        
+        hb_buffer_set_direction(mBuffer, isRTL ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
+        hb_buffer_set_script(mBuffer, run.script);
+        // Should set language here (for bug 7004056)
+        hb_buffer_add_utf16(mBuffer, contextChars, contextCount, start + run.pos, run.length);
+
         // Initialize Harfbuzz Shaper and get the base glyph count for offsetting the glyphIDs
         // and shape the Font run
-        size_t glyphBaseCount = shapeFontRun(paint, isRTL);
+        size_t glyphBaseCount = shapeFontRun(paint);
+        unsigned int numGlyphs;
+        hb_glyph_info_t* info = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
+        hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(mBuffer, NULL);
 
 #if DEBUG_GLYPHS
         ALOGD("Got from Harfbuzz");
         ALOGD("         -- glyphBaseCount = %d", glyphBaseCount);
-        ALOGD("         -- num_glypth = %d", mShaperItem.num_glyphs);
-        ALOGD("         -- kerning_applied = %d", mShaperItem.kerning_applied);
+        ALOGD("         -- num_glyph = %d", numGlyphs);
         ALOGD("         -- isDevKernText = %d", paint->isDevKernText());
+        ALOGD("         -- initial totalAdvance = %f", totalAdvance);
 
-        logGlyphs(mShaperItem);
+        logGlyphs(mBuffer);
 #endif
 
-        if (mShaperItem.advances == NULL || mShaperItem.num_glyphs == 0) {
-#if DEBUG_GLYPHS
-            ALOGD("Advances array is empty or num_glypth = 0");
-#endif
-            continue;
+        for (size_t i = 0; i < numGlyphs; i++) {
+            size_t cluster = info[i].cluster - start;
+            float xAdvance = HBFixedToFloat(positions[i].x_advance);
+            outAdvances->replaceAt(outAdvances->itemAt(cluster) + xAdvance, cluster);
+            outGlyphs->add(info[i].codepoint + glyphBaseCount);
+            float xo = HBFixedToFloat(positions[i].x_offset);
+            float yo = -HBFixedToFloat(positions[i].y_offset);
+            outPos->add(totalAdvance + xo + yo * skewX);
+            outPos->add(yo);
+            totalAdvance += xAdvance;
         }
-
-#if DEBUG_GLYPHS
-        ALOGD("Returned logclusters");
-        for (size_t i = 0; i < mShaperItem.num_glyphs; i++) {
-            ALOGD("         -- lc[%d] = %d, hb-adv[%d] = %0.2f", i, mShaperItem.log_clusters[i],
-                    i, HBFixedToFloat(mShaperItem.advances[i]));
-        }
-#endif
-        jfloat totalFontRunAdvance = 0;
-        size_t clusterStart = 0;
-        for (size_t i = 0; i < countScriptRun; i++) {
-            size_t cluster = mShaperItem.log_clusters[i];
-            size_t clusterNext = i == countScriptRun - 1 ? mShaperItem.num_glyphs :
-                mShaperItem.log_clusters[i + 1];
-            if (cluster != clusterNext) {
-                jfloat advance = 0;
-                // The advance for the cluster is the sum of the advances of all glyphs within
-                // the cluster.
-                for (size_t j = cluster; j < clusterNext; j++) {
-                    advance += HBFixedToFloat(mShaperItem.advances[j]);
-                }
-                totalFontRunAdvance += advance;
-                outAdvances->replaceAt(advance, startScriptRun + clusterStart);
-                clusterStart = i + 1;
-            }
-        }
-
-#if DEBUG_ADVANCES
-        ALOGD("Returned advances");
-        for (size_t i = 0; i < countScriptRun; i++) {
-            ALOGD("         -- hb-adv[%d] = %0.2f, log_clusters = %d, total = %0.2f", i,
-                    (*outAdvances)[i], mShaperItem.log_clusters[i], totalFontRunAdvance);
-        }
-#endif
-
-        // Get Glyphs and reverse them in place if RTL
-        if (outGlyphs) {
-            size_t countGlyphs = mShaperItem.num_glyphs;
-#if DEBUG_GLYPHS
-            ALOGD("Returned script run glyphs -- count = %d", countGlyphs);
-#endif
-            for (size_t i = 0; i < countGlyphs; i++) {
-                jchar glyph = glyphBaseCount +
-                        (jchar) mShaperItem.glyphs[(!isRTL) ? i : countGlyphs - 1 - i];
-#if DEBUG_GLYPHS
-                ALOGD("         -- glyph[%d] = %d", i, glyph);
-#endif
-                outGlyphs->add(glyph);
-            }
-        }
-
-        // Get glyph positions (and reverse them in place if RTL)
-        if (outPos) {
-            size_t countGlyphs = mShaperItem.num_glyphs;
-            jfloat x = totalAdvance;
-            for (size_t i = 0; i < countGlyphs; i++) {
-                size_t index = (!isRTL) ? i : countGlyphs - 1 - i;
-                float xo = HBFixedToFloat(mShaperItem.offsets[index].x);
-                float yo = HBFixedToFloat(mShaperItem.offsets[index].y);
-                // Apply skewX component of transform to position offsets. Note
-                // that scale has already been applied through x_ and y_scale
-                // set in the mFontRec.
-                outPos->add(x + xo + yo * skewX);
-                outPos->add(yo);
-#if DEBUG_GLYPHS
-                ALOGD("         -- hb adv[%d] = %f, log_cluster[%d] = %d",
-                        index, HBFixedToFloat(mShaperItem.advances[index]),
-                        index, mShaperItem.log_clusters[index]);
-#endif
-                x += HBFixedToFloat(mShaperItem.advances[index]);
-            }
-        }
-
-        totalAdvance += totalFontRunAdvance;
     }
 
     *outTotalAdvance = totalAdvance;
 
 #if DEBUG_GLYPHS
+    ALOGD("         -- final totalAdvance = %f", totalAdvance);
     ALOGD("-------- End of Script Run --------");
 #endif
 }
@@ -774,37 +791,36 @@
  * for the default font live in a global cache.
  */
 SkTypeface* TextLayoutShaper::typefaceForScript(const SkPaint* paint, SkTypeface* typeface,
-        HB_Script script) {
+        hb_script_t script) {
     SkTypeface::Style currentStyle = SkTypeface::kNormal;
     if (typeface) {
         currentStyle = typeface->style();
     }
-    typeface = SkCreateTypefaceForScript(script, currentStyle);
+    typeface = SkCreateTypefaceForScriptNG(script, currentStyle);
 #if DEBUG_GLYPHS
     ALOGD("Using Harfbuzz Script %d, Style %d", script, currentStyle);
 #endif
     return typeface;
 }
 
-bool TextLayoutShaper::isComplexScript(HB_Script script) {
+bool TextLayoutShaper::isComplexScript(hb_script_t script) {
     switch (script) {
-    case HB_Script_Common:
-    case HB_Script_Greek:
-    case HB_Script_Cyrillic:
-    case HB_Script_Hangul:
-    case HB_Script_Inherited:
+    case HB_SCRIPT_COMMON:
+    case HB_SCRIPT_GREEK:
+    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;
     }
 }
 
-size_t TextLayoutShaper::shapeFontRun(const SkPaint* paint, bool isRTL) {
-    // Reset kerning
-    mShaperItem.kerning_applied = false;
-
+size_t TextLayoutShaper::shapeFontRun(const SkPaint* paint) {
     // Update Harfbuzz Shaper
-    mShaperItem.item.bidiLevel = isRTL;
 
     SkTypeface* typeface = paint->getTypeface();
 
@@ -813,19 +829,21 @@
     // when we are shaping any script that needs to use a fallback Font.
     // If we are a "common" script we dont need to shift
     size_t baseGlyphCount = 0;
-    SkUnichar firstUnichar = 0;
-    if (isComplexScript(mShaperItem.item.script)) {
-        const uint16_t* text16 = (const uint16_t*) (mShaperItem.string + mShaperItem.item.pos);
-        const uint16_t* text16End = text16 + mShaperItem.item.length;
-        firstUnichar = SkUTF16_NextUnichar(&text16);
-        while (firstUnichar == ' ' && text16 < text16End) {
-            firstUnichar = SkUTF16_NextUnichar(&text16);
+    hb_codepoint_t firstUnichar = 0;
+    if (isComplexScript(hb_buffer_get_script(mBuffer))) {
+        unsigned int numGlyphs;
+        hb_glyph_info_t* info = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
+        for (size_t i = 0; i < numGlyphs; i++) {
+            firstUnichar = info[i].codepoint;
+            if (firstUnichar != ' ') {
+                break;
+            }
         }
         baseGlyphCount = paint->getBaseGlyphCount(firstUnichar);
     }
 
     if (baseGlyphCount != 0) {
-        typeface = typefaceForScript(paint, typeface, mShaperItem.item.script);
+        typeface = typefaceForScript(paint, typeface, hb_buffer_get_script(mBuffer));
         if (!typeface) {
             typeface = mDefaultTypeface;
             SkSafeRef(typeface);
@@ -844,94 +862,44 @@
     }
 
     mShapingPaint.setTypeface(typeface);
-    mShaperItem.face = getCachedHBFace(typeface);
+    hb_face_t* face = referenceCachedHBFace(typeface);
 
-    int textSize = paint->getTextSize();
-    float scaleX = paint->getTextScaleX();
-    mFontRec.x_ppem = floor(scaleX * textSize + 0.5);
-    mFontRec.y_ppem = textSize;
-    uint32_t unitsPerEm = SkFontHost::GetUnitsPerEm(typeface->uniqueID());
-    // x_ and y_scale are the conversion factors from font design space
-    // (unitsPerEm) to 1/64th of device pixels in 16.16 format.
-    const int kDevicePixelFraction = 64;
-    const int kMultiplyFor16Dot16 = 1 << 16;
-    float emScale = kDevicePixelFraction * kMultiplyFor16Dot16 / (float)unitsPerEm;
-    mFontRec.x_scale = emScale * scaleX * textSize;
-    mFontRec.y_scale = emScale * textSize;
+    float sizeY = paint->getTextSize();
+    float sizeX = sizeY * paint->getTextScaleX();
+    hb_font_t* font = createFont(face, &mShapingPaint, sizeX, sizeY);
+    hb_face_destroy(face);
 
 #if DEBUG_GLYPHS
-    ALOGD("Run typeface = %p, uniqueID = %d, hb_face = %p",
-            typeface, typeface->uniqueID(), mShaperItem.face);
+    ALOGD("Run typeface = %p, uniqueID = %d, face = %p",
+            typeface, typeface->uniqueID(), face);
 #endif
     SkSafeUnref(typeface);
 
-    // Shape
-    assert(mShaperItem.item.length > 0); // Harfbuzz will overwrite other memory if length is 0.
-    size_t size = mShaperItem.item.length * 3 / 2;
-    while (!doShaping(size)) {
-        // We overflowed our glyph arrays. Resize and retry.
-        // HB_ShapeItem fills in shaperItem.num_glyphs with the needed size.
-        size = mShaperItem.num_glyphs * 2;
-    }
+    hb_shape(font, mBuffer, NULL, 0);
+    hb_font_destroy(font);
+
     return baseGlyphCount;
 }
 
-bool TextLayoutShaper::doShaping(size_t size) {
-    if (size > mShaperItemGlyphArraySize) {
-        deleteShaperItemGlyphArrays();
-        createShaperItemGlyphArrays(size);
-    }
-    mShaperItem.num_glyphs = mShaperItemGlyphArraySize;
-    memset(mShaperItem.offsets, 0, mShaperItem.num_glyphs * sizeof(HB_FixedPoint));
-    return HB_ShapeItem(&mShaperItem);
-}
-
-void TextLayoutShaper::createShaperItemGlyphArrays(size_t size) {
-#if DEBUG_GLYPHS
-    ALOGD("Creating Glyph Arrays with size = %d", size);
-#endif
-    mShaperItemGlyphArraySize = size;
-
-    // These arrays are all indexed by glyph.
-    mShaperItem.glyphs = new HB_Glyph[size];
-    mShaperItem.attributes = new HB_GlyphAttributes[size];
-    mShaperItem.advances = new HB_Fixed[size];
-    mShaperItem.offsets = new HB_FixedPoint[size];
-
-    // Although the log_clusters array is indexed by character, Harfbuzz expects that
-    // it is big enough to hold one element per glyph.  So we allocate log_clusters along
-    // with the other glyph arrays above.
-    mShaperItem.log_clusters = new unsigned short[size];
-}
-
-void TextLayoutShaper::deleteShaperItemGlyphArrays() {
-    delete[] mShaperItem.glyphs;
-    delete[] mShaperItem.attributes;
-    delete[] mShaperItem.advances;
-    delete[] mShaperItem.offsets;
-    delete[] mShaperItem.log_clusters;
-}
-
-HB_Face TextLayoutShaper::getCachedHBFace(SkTypeface* typeface) {
+hb_face_t* TextLayoutShaper::referenceCachedHBFace(SkTypeface* typeface) {
     SkFontID fontId = typeface->uniqueID();
     ssize_t index = mCachedHBFaces.indexOfKey(fontId);
     if (index >= 0) {
-        return mCachedHBFaces.valueAt(index);
+        return hb_face_reference(mCachedHBFaces.valueAt(index));
     }
-    HB_Face face = HB_NewFace(typeface, harfbuzzSkiaGetTable);
-    if (face) {
+    // TODO: destroy function
+    hb_face_t* face = hb_face_create_for_tables(harfbuzzSkiaReferenceTable, typeface, NULL);
 #if DEBUG_GLYPHS
-        ALOGD("Created HB_NewFace %p from paint typeface = %p", face, typeface);
+    ALOGD("Created HB_NewFace %p from paint typeface = %p", face, typeface);
 #endif
-        mCachedHBFaces.add(fontId, face);
-    }
-    return face;
+    mCachedHBFaces.add(fontId, face);
+    return hb_face_reference(face);
 }
 
 void TextLayoutShaper::purgeCaches() {
     size_t cacheSize = mCachedHBFaces.size();
     for (size_t i = 0; i < cacheSize; i++) {
-        HB_FreeFace(mCachedHBFaces.valueAt(i));
+        hb_face_destroy(mCachedHBFaces.valueAt(i));
     }
     mCachedHBFaces.clear();
     unrefTypefaces();
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index 22de523..704c773 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -28,18 +28,17 @@
 #include <utils/RefBase.h>
 #include <utils/Singleton.h>
 
-#include <SkPaint.h>
-#include <SkTemplates.h>
-#include <SkUtils.h>
 #include <SkAutoKern.h>
 #include <SkLanguage.h>
+#include <SkPaint.h>
+#include <SkTemplates.h>
+#include <SkTypeface.h>
+#include <SkUtils.h>
 
 #include <unicode/ubidi.h>
-#include <unicode/ushape.h>
 #include <unicode/unistr.h>
 
-#include "HarfbuzzSkia.h"
-#include "harfbuzz-shaper.h"
+#include <hb.h>
 
 #include <android_runtime/AndroidRuntime.h>
 
@@ -189,14 +188,9 @@
 
 private:
     /**
-     * Harfbuzz shaper item
+     * Harfbuzz buffer for shaping
      */
-    HB_ShaperItem mShaperItem;
-
-    /**
-     * Harfbuzz font
-     */
-    HB_FontRec mFontRec;
+    hb_buffer_t* mBuffer;
 
     /**
      * Skia Paint used for shaping
@@ -211,30 +205,15 @@
     /**
      * Cache of Harfbuzz faces
      */
-    KeyedVector<SkFontID, HB_Face> mCachedHBFaces;
-
-    /**
-     * Cache of glyph array size
-     */
-    size_t mShaperItemGlyphArraySize;
-
-    /**
-     * Buffer for containing the ICU normalized form of a run
-     */
-    UnicodeString mNormalizedString;
-
-    /**
-     * Buffer for normalizing a piece of a run with ICU
-     */
-    UnicodeString mBuffer;
+    KeyedVector<SkFontID, hb_face_t*> mCachedHBFaces;
 
     void init();
     void unrefTypefaces();
 
     SkTypeface* typefaceForScript(const SkPaint* paint, SkTypeface* typeface,
-        HB_Script script);
+        hb_script_t script);
 
-    size_t shapeFontRun(const SkPaint* paint, bool isRTL);
+    size_t shapeFontRun(const SkPaint* paint);
 
     void computeValues(const SkPaint* paint, const UChar* chars,
             size_t start, size_t count, size_t contextCount, int dirFlags,
@@ -242,17 +221,14 @@
             Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos);
 
     void computeRunValues(const SkPaint* paint, const UChar* chars,
-            size_t count, bool isRTL,
+            size_t start, size_t count, size_t contextCount, bool isRTL,
             Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
             Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos);
 
-    SkTypeface* getCachedTypeface(SkTypeface** typeface, HB_Script script, SkTypeface::Style style);
-    HB_Face getCachedHBFace(SkTypeface* typeface);
+    SkTypeface* setCachedTypeface(SkTypeface** typeface, hb_script_t script, SkTypeface::Style style);
+    hb_face_t* referenceCachedHBFace(SkTypeface* typeface);
 
-    bool doShaping(size_t size);
-    void createShaperItemGlyphArrays(size_t size);
-    void deleteShaperItemGlyphArrays();
-    bool isComplexScript(HB_Script script);
+    bool isComplexScript(hb_script_t script);
 
 }; // TextLayoutShaper
 
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 7d886da..de14826 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -275,6 +275,16 @@
     return renderer->clipRect(float(left), float(top), float(right), float(bottom), op);
 }
 
+static bool android_view_GLES20Canvas_clipPath(JNIEnv* env, jobject clazz,
+        OpenGLRenderer* renderer, SkPath* path, SkRegion::Op op) {
+    return renderer->clipPath(path, op);
+}
+
+static bool android_view_GLES20Canvas_clipRegion(JNIEnv* env, jobject clazz,
+        OpenGLRenderer* renderer, SkRegion* region, SkRegion::Op op) {
+    return renderer->clipRegion(region, op);
+}
+
 static bool android_view_GLES20Canvas_getClipBounds(JNIEnv* env, jobject clazz,
         OpenGLRenderer* renderer, jobject rect) {
     const android::uirenderer::Rect& bounds(renderer->getClipBounds());
@@ -961,6 +971,8 @@
     { "nQuickReject",       "(IFFFFI)Z",       (void*) android_view_GLES20Canvas_quickReject },
     { "nClipRect",          "(IFFFFI)Z",       (void*) android_view_GLES20Canvas_clipRectF },
     { "nClipRect",          "(IIIIII)Z",       (void*) android_view_GLES20Canvas_clipRect },
+    { "nClipPath",          "(III)Z",          (void*) android_view_GLES20Canvas_clipPath },
+    { "nClipRegion",        "(III)Z",          (void*) android_view_GLES20Canvas_clipRegion },
 
     { "nTranslate",         "(IFF)V",          (void*) android_view_GLES20Canvas_translate },
     { "nRotate",            "(IF)V",           (void*) android_view_GLES20Canvas_rotate },
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/drawable-ldrtl-hdpi/ic_lock_airplane_mode_off.png b/core/res/res/drawable-ldrtl-hdpi/ic_lock_airplane_mode_off.png
new file mode 100644
index 0000000..7b1eee6
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/ic_lock_airplane_mode_off.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/ic_menu_cc.png b/core/res/res/drawable-ldrtl-hdpi/ic_menu_cc.png
new file mode 100644
index 0000000..aedf9b1
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/ic_menu_cc.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/stat_sys_adb.png b/core/res/res/drawable-ldrtl-hdpi/stat_sys_adb.png
new file mode 100644
index 0000000..a7dc29d
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/stat_sys_adb.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-ldpi/ic_lock_airplane_mode_off.png b/core/res/res/drawable-ldrtl-ldpi/ic_lock_airplane_mode_off.png
new file mode 100644
index 0000000..c093420
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-ldpi/ic_lock_airplane_mode_off.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-ldpi/ic_menu_cc.png b/core/res/res/drawable-ldrtl-ldpi/ic_menu_cc.png
new file mode 100644
index 0000000..1f21884
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-ldpi/ic_menu_cc.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-ldpi/stat_sys_adb.png b/core/res/res/drawable-ldrtl-ldpi/stat_sys_adb.png
new file mode 100644
index 0000000..d726b7a
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-ldpi/stat_sys_adb.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/ic_lock_airplane_mode_off.png b/core/res/res/drawable-ldrtl-mdpi/ic_lock_airplane_mode_off.png
new file mode 100644
index 0000000..cba5500
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/ic_lock_airplane_mode_off.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/ic_menu_cc.png b/core/res/res/drawable-ldrtl-mdpi/ic_menu_cc.png
new file mode 100644
index 0000000..0335dec
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/ic_menu_cc.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/stat_sys_adb.png b/core/res/res/drawable-ldrtl-mdpi/stat_sys_adb.png
new file mode 100644
index 0000000..265c421
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/stat_sys_adb.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/ic_lock_airplane_mode_off.png b/core/res/res/drawable-ldrtl-xhdpi/ic_lock_airplane_mode_off.png
new file mode 100644
index 0000000..6820a23
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/ic_lock_airplane_mode_off.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/ic_menu_cc.png b/core/res/res/drawable-ldrtl-xhdpi/ic_menu_cc.png
new file mode 100644
index 0000000..c8690bd
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/ic_menu_cc.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/stat_sys_adb.png b/core/res/res/drawable-ldrtl-xhdpi/stat_sys_adb.png
new file mode 100644
index 0000000..e342556
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/stat_sys_adb.png
Binary files differ
diff --git a/core/res/res/values-af/donottranslate-cldr.xml b/core/res/res/values-af/donottranslate-cldr.xml
index 77b7f7c..ca693b6 100644
--- a/core/res/res/values-af/donottranslate-cldr.xml
+++ b/core/res/res/values-af/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%Y/%m/%d</string>
     <string name="numeric_date_format">yyyy/MM/dd</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 14d2412..ad64eb6 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Laat \'n program toe om die huidige lae-vlak batteryverbruikdata te lees. Kan die program toelaat om gedetailleerde inligting te vind oor watter programme jy gebruik."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"verander batterystatistieke"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Laat die program toe om versamelde battery-statistieke te verander. Nie vir gebruik deur normale programme nie."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"verander programwerking-statistieke"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Laat die program toe om versamelde programwerking-statistieke te verander. Nie vir gebruik deur normale programme nie."</string>
     <string name="permlab_backup" msgid="470013022865453920">"beheerstelsel-rugsteun en -teruglaai"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Laat die program toe om die stelsel se rugsteun-en-teruglaai-meganisme te beheer. Nie vir gebruik deur normale programme nie."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"bevestig \'n volledige rugsteun- of teruglaaihandeling"</string>
diff --git a/core/res/res/values-am/donottranslate-cldr.xml b/core/res/res/values-am/donottranslate-cldr.xml
index b3b76f9..a7cb1c7 100644
--- a/core/res/res/values-am/donottranslate-cldr.xml
+++ b/core/res/res/values-am/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%d/%m/%Y</string>
     <string name="numeric_date_format">dd/MM/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index d9b9940..5b8200e 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"አንድ መተግበሪያ የአሁኑን የዝቅተኛ-ደረጃ ባትሪ አጠቃቀም ውሂብን እንዲያነብ ያስችላል። መተግበሪያው ስለሚጠቀሟቸው መተግበሪያዎች ዝርዝር መረጃ እንዲያገኝ ሊያስችለው ይችላል።"</string>
     <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="permlab_backup" msgid="470013022865453920">"የስርዓት መጠባበቂያን ተቆጣጠር እናእነበረበት መልስ"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"የስርዓቱን ምትኬ  እና እንደነበር መልስ መንገዶችን ለመቆጣጠር ለመተግበሪያው ይፈቅዳሉ፡፡ በመደበኛ መተግበሪያዎች ለመጠቀም አይሆንም፡፡"</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"የሙሉ መጠበቂያ ወይም እነበረበት መልስ ከዋኝ አረጋግጥ"</string>
diff --git a/core/res/res/values-ar-rEG/donottranslate-cldr.xml b/core/res/res/values-ar-rEG/donottranslate-cldr.xml
index 155480d..7480756 100644
--- a/core/res/res/values-ar-rEG/donottranslate-cldr.xml
+++ b/core/res/res/values-ar-rEG/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%-e‏/%-m‏/%Y</string>
     <string name="numeric_date_format">d‏/M‏/yyyy</string>
     <string name="numeric_date_template">"%s‏/%s‏/%s"</string>
diff --git a/core/res/res/values-ar/donottranslate-cldr.xml b/core/res/res/values-ar/donottranslate-cldr.xml
index 135963b..44d8a26 100644
--- a/core/res/res/values-ar/donottranslate-cldr.xml
+++ b/core/res/res/values-ar/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%-e‏/%-m‏/%Y</string>
     <string name="numeric_date_format">d‏/M‏/yyyy</string>
     <string name="numeric_date_template">"%s‏/%s‏/%s"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index a93c75f..d919829 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"للسماح لتطبيق بقراءة البيانات الحالية التي تستهلك مستوى منخفضًا من البطارية. قد يتيح التطبيق معرفة معلومات تفصيلية عن التطبيقات التي تستخدمها."</string>
     <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="permlab_backup" msgid="470013022865453920">"التحكم في النسخة الاحتياطية للنظام واستعادتها"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"للسماح للتطبيق بالتحكم في النسخة الاحتياطية للنظام وآلية الاستعادة. ليس للاستخدام بواسطة التطبيقات العادية."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"تأكيد إجراء عملية نسخ احتياطي أو استرداد كاملة"</string>
diff --git a/core/res/res/values-be/donottranslate-cldr.xml b/core/res/res/values-be/donottranslate-cldr.xml
index a346c83..926b4bb 100644
--- a/core/res/res/values-be/donottranslate-cldr.xml
+++ b/core/res/res/values-be/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k.%M</string>
     <string name="hour_minute_ampm">%-l.%M %p</string>
     <string name="hour_minute_cap_ampm">%-l.%M %p</string>
-    <string name="twelve_hour_time_format">h.mm a</string>
-    <string name="twenty_four_hour_time_format">H.mm</string>
     <string name="numeric_date">%-e.%-m.%Y</string>
     <string name="numeric_date_format">d.M.yyyy</string>
     <string name="numeric_date_template">"%s.%s.%s"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 89fba51..0b37f93 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Прыкладанне можа счытваць бягучыя звесткi пры нiзкi зарад акумулятара. Прыкладанне можа знайсцi падрабязную iнфармацы. пра прыкладаннi, якiя вы выкарыстоўваеце."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"змяняць статыстыку батарэі"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Дазваляе прыкладанням змяняць сабраную статыстыку батарэi. Не патрабуецца для звычайных прыкладанняў."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"змяняць статыстыку выкарыстання прыкладання"</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>
diff --git a/core/res/res/values-bg/donottranslate-cldr.xml b/core/res/res/values-bg/donottranslate-cldr.xml
index 9c1ae2c..e7ad610 100644
--- a/core/res/res/values-bg/donottranslate-cldr.xml
+++ b/core/res/res/values-bg/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%d.%m.%Y</string>
     <string name="numeric_date_format">dd.MM.yyyy</string>
     <string name="numeric_date_template">"%s.%s.%s"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index f65fc9d..9bb46c1 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Разрешава на приложението да чете текущите данни за работа при ниско ниво на батерията. Може да му разреши да намери подробна информация за ползваните от вас приложения."</string>
     <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="permlab_backup" msgid="470013022865453920">"контролиране на създаването и възстановяването на резервни копия на системата"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Разрешава на приложението да контролира системния механизъм за създаване и възстановяване на резервни копия. Не е предназначено за нормални приложения."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"потвърждаване на пълно резервно копие или възстановяване на операцията"</string>
diff --git a/core/res/res/values-ca/donottranslate-cldr.xml b/core/res/res/values-ca/donottranslate-cldr.xml
index 84e7e79..0872125 100644
--- a/core/res/res/values-ca/donottranslate-cldr.xml
+++ b/core/res/res/values-ca/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%d/%m/%Y</string>
     <string name="numeric_date_format">dd/MM/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 28e692b..eeda2cc 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Permet que l\'aplicació llegeixi l\'ús de dades actual quan hi ha poca bateria. Pot permetre que l\'aplicació recopili informació detallada sobre les aplicacions que fas servir."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"modifica les estadístiques de la bateria"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Permet que l\'aplicació modifiqui les estadístiques d\'ús de la bateria recopilades. No ho poden fer servir les aplicacions normals."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modifica les estadístiques d\'ús de l\'aplicació"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Permet que l\'aplicació modifiqui les estadístiques d\'ús de l\'aplicació recopilades. No indicat per a les aplicacions normals."</string>
     <string name="permlab_backup" msgid="470013022865453920">"controlar la còpia de seguretat i restauració del sistema"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Permet que l\'aplicació controli el mecanisme de còpia de seguretat i restauració del sistema. No indicat per a les aplicacions normals."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"confirmar una operació de còpia de seguretat completa o de restauració"</string>
diff --git a/core/res/res/values-cs/donottranslate-cldr.xml b/core/res/res/values-cs/donottranslate-cldr.xml
index ff7902d..bbf42f0 100644
--- a/core/res/res/values-cs/donottranslate-cldr.xml
+++ b/core/res/res/values-cs/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%-e. %-m. %Y</string>
     <string name="numeric_date_format">d. M. yyyy</string>
     <string name="numeric_date_template">"%s. %s. %s"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 4bc8571..f39e602 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Umožňuje aplikaci číst aktuální podrobné údaje o využití baterie. Aplikace to může využít k získání podrobných informací o tom, které aplikace používáte."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"změna statistických údajů o baterii"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Umožňuje aplikaci upravit shromážděné statistiky o baterii. Toto oprávnění není určeno pro běžné aplikace."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"upravit statistiky operací aplikace"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Umožňuje aplikaci upravit shromážděné statistiky operací aplikace. Toto oprávnění není určeno pro běžné aplikace."</string>
     <string name="permlab_backup" msgid="470013022865453920">"ovládání zálohování a obnovy systému"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Umožňuje aplikaci řídit mechanismy zálohování a obnovení systému. Toto oprávnění není určeno pro běžné aplikace."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"potvrzení operace úplné zálohy nebo úplného obnovení"</string>
@@ -325,8 +327,8 @@
     <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Umožňuje aplikaci využívat jiné aplikace nebo části uživatelského rozhraní. Aplikace tak může zasahovat do používání rozhraní jakékoli aplikace a měnit rozhraní zobrazené v jiných aplikacích."</string>
     <string name="permlab_setAnimationScale" msgid="2805103241153907174">"změna globální rychlosti animace"</string>
     <string name="permdesc_setAnimationScale" msgid="7690063428924343571">"Umožňuje aplikaci kdykoliv globálně změnit rychlost animací (rychlejší či pomalejší animace)."</string>
-    <string name="permlab_manageAppTokens" msgid="1286505717050121370">"správa klíčů aplikací"</string>
-    <string name="permdesc_manageAppTokens" msgid="8043431713014395671">"Umožňuje aplikaci vytvořit a spravovat vlastní klíče a současně obejít pořadí vykreslování. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
+    <string name="permlab_manageAppTokens" msgid="1286505717050121370">"správa aplikačních tokenů"</string>
+    <string name="permdesc_manageAppTokens" msgid="8043431713014395671">"Umožňuje aplikaci vytvořit a spravovat vlastní tokeny a současně obejít pořadí vykreslování. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
     <string name="permlab_freezeScreen" msgid="4708181184441880175">"zmrazit obrazovku"</string>
     <string name="permdesc_freezeScreen" msgid="8558923789222670064">"Povoluje aplikaci dočasně zmrazit obrazovku pro přechod do režimu celé obrazovky."</string>
     <string name="permlab_injectEvents" msgid="1378746584023586600">"používání kláves a tlačítek"</string>
@@ -534,7 +536,7 @@
     <string name="permlab_manageAccounts" msgid="4983126304757177305">"přidání nebo odebrání účtů"</string>
     <string name="permdesc_manageAccounts" msgid="8698295625488292506">"Umožňuje aplikaci provádět operace, jako je přidávání nebo odebírání účtů nebo mazání jejich hesel."</string>
     <string name="permlab_useCredentials" msgid="235481396163877642">"používání účtů v zařízení"</string>
-    <string name="permdesc_useCredentials" msgid="7984227147403346422">"Umožňuje aplikaci požadovat ověřovací klíče."</string>
+    <string name="permdesc_useCredentials" msgid="7984227147403346422">"Umožňuje aplikaci požadovat ověřovací tokeny."</string>
     <string name="permlab_accessNetworkState" msgid="4951027964348974773">"zobrazení síťových připojení"</string>
     <string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Umožňuje aplikaci zobrazit informace o síťových připojeních, například o tom, které sítě jsou k dispozici a které jsou připojené."</string>
     <string name="permlab_createNetworkSockets" msgid="8018758136404323658">"úplný přístup k síti"</string>
diff --git a/core/res/res/values-da/donottranslate-cldr.xml b/core/res/res/values-da/donottranslate-cldr.xml
index d5b9878..427fd0c 100644
--- a/core/res/res/values-da/donottranslate-cldr.xml
+++ b/core/res/res/values-da/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%H:%M</string>
     <string name="hour_minute_ampm">%-l.%M %p</string>
     <string name="hour_minute_cap_ampm">%-l.%M %^p</string>
-    <string name="twelve_hour_time_format">h.mm a</string>
-    <string name="twenty_four_hour_time_format">HH:mm</string>
     <string name="numeric_date">%d/%m/%Y</string>
     <string name="numeric_date_format">dd/MM/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 09fee8f..45f551c 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Tillader, at en applikation læser de aktuelle data for batteriforbruget. Kan tillade, at applikationen henter detaljerede oplysninger om, hvilke apps du bruger."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"rediger batteristatistikker"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Tillader, at appen kan ændre indsamlede batteristatistikker. Anvendes ikke af normale apps."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"lav ændringer i statistik for handlinger i appen"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Tillader, at appen kan ændre indsamlede statistikker for handlinger i applikationen. Dette kan ikke bruges af almindelige apps."</string>
     <string name="permlab_backup" msgid="470013022865453920">"kontroller sikkerhedskopiering af system, og gendan"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Tillader, at appen kan kontrollere systemets sikkerhedskopi og gendannelsesmekanisme. Kan ikke anvendes af normale apps."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"bekræfte en komplet sikkerhedskopi, eller gendan drift"</string>
diff --git a/core/res/res/values-de/donottranslate-cldr.xml b/core/res/res/values-de/donottranslate-cldr.xml
index 9bdd8a3..850fe26 100644
--- a/core/res/res/values-de/donottranslate-cldr.xml
+++ b/core/res/res/values-de/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%d.%m.%Y</string>
     <string name="numeric_date_format">dd.MM.yyyy</string>
     <string name="numeric_date_template">"%s.%s.%s"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 05db7a0..1ef42f6 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Ermöglicht einer Anwendung, den momentan niedrigen Akkustand zu erkennen. Unter Umständen erhält die App detaillierte Informationen darüber, welche Apps Sie verwenden."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"Akkudaten ändern"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Ermöglicht der App, erfasste Akkudaten zu ändern. Nicht für normale Apps vorgesehen."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"App-Vorgangsstatistiken ändern"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Ermöglicht der App, erfasste App-Vorgangsstatistiken zu ändern. Kann nicht von normalen Apps verwendet werden"</string>
     <string name="permlab_backup" msgid="470013022865453920">"Systemsicherung und -wiederherstellung kontrollieren"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Ermöglicht der App, den Sicherungs- und Wiederherstellungsmechanismus des Systems zu steuern. Nicht für normale Apps vorgesehen."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"Vollständige Sicherung oder Wiederherstellung bestätigen"</string>
diff --git a/core/res/res/values-el/donottranslate-cldr.xml b/core/res/res/values-el/donottranslate-cldr.xml
index a0c69b5..dfa199d 100644
--- a/core/res/res/values-el/donottranslate-cldr.xml
+++ b/core/res/res/values-el/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%d/%m/%Y</string>
     <string name="numeric_date_format">dd/MM/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 8b918c6..f3333dd 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Επιτρέπει σε μια εφαρμογή να διαβάζει τα δεδομένα τρέχουσας χαμηλού επιπέδου χρήσης μπαταρίας. Ενδέχεται να επιτρέπει στην εφαρμογή να εντοπίσει λεπτομερείς πληροφορίες σχετικά με τις εφαρμογές που χρησιμοποιείτε."</string>
     <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="permlab_backup" msgid="470013022865453920">"αντίγραφο ασφαλείας και επαναφορά συστήματος"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Επιτρέπει στην εφαρμογή τον έλεγχο του μηχανισμού δημιουργίας αντιγράφων ασφάλειας και ανάκτησης. Δεν προορίζεται για χρήση με συνήθεις εφαρμογές."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"επιβεβαίωση δημιουργίας πλήρους αντιγράφου ασφαλείας ή επαναφοράς λειτουργίας"</string>
diff --git a/core/res/res/values-en-rAU/donottranslate-cldr.xml b/core/res/res/values-en-rAU/donottranslate-cldr.xml
index 947fe92..7ccff4d 100644
--- a/core/res/res/values-en-rAU/donottranslate-cldr.xml
+++ b/core/res/res/values-en-rAU/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%H:%M</string>
     <string name="hour_minute_ampm">%-l:%M%p</string>
     <string name="hour_minute_cap_ampm">%-l:%M%^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">HH:mm</string>
     <string name="numeric_date">%-e/%m/%Y</string>
     <string name="numeric_date_format">d/MM/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-en-rCA/donottranslate-cldr.xml b/core/res/res/values-en-rCA/donottranslate-cldr.xml
index 1845f28..21e9b7e 100644
--- a/core/res/res/values-en-rCA/donottranslate-cldr.xml
+++ b/core/res/res/values-en-rCA/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%H:%M</string>
     <string name="hour_minute_ampm">%-l:%M%p</string>
     <string name="hour_minute_cap_ampm">%-l:%M%^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">HH:mm</string>
     <string name="numeric_date">%Y-%m-%d</string>
     <string name="numeric_date_format">yyyy-MM-dd</string>
     <string name="numeric_date_template">"%s-%s-%s"</string>
diff --git a/core/res/res/values-en-rGB/donottranslate-cldr.xml b/core/res/res/values-en-rGB/donottranslate-cldr.xml
index a10dfa50..22a8e41 100644
--- a/core/res/res/values-en-rGB/donottranslate-cldr.xml
+++ b/core/res/res/values-en-rGB/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%H:%M</string>
     <string name="hour_minute_ampm">%-l:%M%p</string>
     <string name="hour_minute_cap_ampm">%-l:%M%^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">HH:mm</string>
     <string name="numeric_date">%d/%m/%Y</string>
     <string name="numeric_date_format">dd/MM/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 2c802af..538dd66 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Allows an application to read the current low-level battery use data. May allow the application to find out detailed information about which apps you use."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"modify battery statistics"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Allows the app to modify collected battery statistics. Not for use by normal apps."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modify app ops statistics"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Allows the app to modify collected component usage statistics. Not for use by normal apps."</string>
     <string name="permlab_backup" msgid="470013022865453920">"control system back up and restore"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Allows the app to control the system\'s backup and restore mechanism. Not for use by normal apps."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"confirm a full backup or restore operation"</string>
diff --git a/core/res/res/values-en-rIE/donottranslate-cldr.xml b/core/res/res/values-en-rIE/donottranslate-cldr.xml
index 65cab99..c88d86c 100644
--- a/core/res/res/values-en-rIE/donottranslate-cldr.xml
+++ b/core/res/res/values-en-rIE/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%H:%M</string>
     <string name="hour_minute_ampm">%-l:%M%p</string>
     <string name="hour_minute_cap_ampm">%-l:%M%^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">HH:mm</string>
     <string name="numeric_date">%d/%m/%Y</string>
     <string name="numeric_date_format">dd/MM/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-en-rIN/donottranslate-cldr.xml b/core/res/res/values-en-rIN/donottranslate-cldr.xml
index 48942fe..3b7a0ca 100644
--- a/core/res/res/values-en-rIN/donottranslate-cldr.xml
+++ b/core/res/res/values-en-rIN/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%H:%M</string>
     <string name="hour_minute_ampm">%-l:%M%p</string>
     <string name="hour_minute_cap_ampm">%-l:%M%^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">HH:mm</string>
     <string name="numeric_date">%d/%m/%Y</string>
     <string name="numeric_date_format">dd/MM/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-en-rNZ/donottranslate-cldr.xml b/core/res/res/values-en-rNZ/donottranslate-cldr.xml
index 117dda8..9156376 100644
--- a/core/res/res/values-en-rNZ/donottranslate-cldr.xml
+++ b/core/res/res/values-en-rNZ/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%H:%M</string>
     <string name="hour_minute_ampm">%-l:%M%p</string>
     <string name="hour_minute_cap_ampm">%-l:%M%^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">HH:mm</string>
     <string name="numeric_date">%-e/%m/%Y</string>
     <string name="numeric_date_format">d/MM/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-en-rUS/donottranslate-cldr.xml b/core/res/res/values-en-rUS/donottranslate-cldr.xml
index 0587c165..4d37d47 100644
--- a/core/res/res/values-en-rUS/donottranslate-cldr.xml
+++ b/core/res/res/values-en-rUS/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%H:%M</string>
     <string name="hour_minute_ampm">%-l:%M%p</string>
     <string name="hour_minute_cap_ampm">%-l:%M%^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">HH:mm</string>
     <string name="numeric_date">%-m/%-e/%Y</string>
     <string name="numeric_date_format">M/d/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-en-rZA/donottranslate-cldr.xml b/core/res/res/values-en-rZA/donottranslate-cldr.xml
index 48ebc6e..5ae38c1 100644
--- a/core/res/res/values-en-rZA/donottranslate-cldr.xml
+++ b/core/res/res/values-en-rZA/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%H:%M</string>
     <string name="hour_minute_ampm">%-l:%M%p</string>
     <string name="hour_minute_cap_ampm">%-l:%M%^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">HH:mm</string>
     <string name="numeric_date">%Y/%m/%d</string>
     <string name="numeric_date_format">yyyy/MM/dd</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-es-rUS/donottranslate-cldr.xml b/core/res/res/values-es-rUS/donottranslate-cldr.xml
index 9224786..2339066 100644
--- a/core/res/res/values-es-rUS/donottranslate-cldr.xml
+++ b/core/res/res/values-es-rUS/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%-m/%-e/%Y</string>
     <string name="numeric_date_format">M/d/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 22f429d..590dfee 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -315,6 +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_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>
@@ -546,7 +548,7 @@
     <string name="permlab_changeTetherState" msgid="5952584964373017960">"cambiar la conectividad de anclaje a red"</string>
     <string name="permdesc_changeTetherState" msgid="1524441344412319780">"Permite que la aplicación cambie el estado de la conectividad de anclaje a la red."</string>
     <string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"cambiar la configuración del uso de datos del fondo"</string>
-    <string name="permdesc_changeBackgroundDataSetting" msgid="5347729578468744379">"Permite que la aplicación cambe la configuración de uso de los datos de referencia."</string>
+    <string name="permdesc_changeBackgroundDataSetting" msgid="5347729578468744379">"Permite que la aplicación cambe la configuración de uso de los datos en segundo plano."</string>
     <string name="permlab_accessWifiState" msgid="5202012949247040011">"ver conexiones Wi-Fi"</string>
     <string name="permdesc_accessWifiState" msgid="5002798077387803726">"Permite que la aplicación vea información sobre la conexión a redes Wi-Fi, por ejemplo, si la conexión Wi-Fi está activada y cuál es el nombre de los dispositivos Wi-Fi conectados."</string>
     <string name="permlab_changeWifiState" msgid="6550641188749128035">"conectarse y desconectarse de la red Wi-Fi"</string>
diff --git a/core/res/res/values-es/donottranslate-cldr.xml b/core/res/res/values-es/donottranslate-cldr.xml
index 0a680b6..dd8a8fa 100644
--- a/core/res/res/values-es/donottranslate-cldr.xml
+++ b/core/res/res/values-es/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%d/%m/%Y</string>
     <string name="numeric_date_format">dd/MM/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index d695225..17a7e86 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Permite que una aplicación consulte los datos actuales de uso de batería de nivel inferior. Puede permitir que la aplicación obtenga información detallada sobre las aplicaciones que utilizas."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"modificar estadísticas de la batería"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Permite que la aplicación modifique las estadísticas recopiladas sobre la batería. Las aplicaciones normales no deben usar este permiso."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modificar estadísticas de operaciones de aplicaciones"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Permite que la aplicación modifique las estadísticas recopiladas sobre operaciones de aplicaciones. Las aplicaciones normales no deben usar este permiso."</string>
     <string name="permlab_backup" msgid="470013022865453920">"controlar las copias de seguridad y las restauraciones del sistema"</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 usar este permiso."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"confirmar restauración o copia de seguridad completa"</string>
diff --git a/core/res/res/values-et/donottranslate-cldr.xml b/core/res/res/values-et/donottranslate-cldr.xml
index bf269dd..912df10 100644
--- a/core/res/res/values-et/donottranslate-cldr.xml
+++ b/core/res/res/values-et/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%d.%m.%Y</string>
     <string name="numeric_date_format">dd.MM.yyyy</string>
     <string name="numeric_date_template">"%s.%s.%s"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 5536457..8b4408a 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Lubab rakendusel lugeda madala akutaseme kasutusandmeid. Võib lubada rakendusel hankida üksikasjalikku teavet selle kohta, mis rakendusi te kasutate."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"aku statistika muutmine"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Võimaldab rakendusel muuta aku kohta kogutud statistikat. Mitte kasutada tavarakenduste puhul."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"rakenduse tööstatistika muutmine"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Võimaldab rakendusel muuta kogutud rakenduse tööstatistikat. Mitte kasutada tavarakenduste puhul."</string>
     <string name="permlab_backup" msgid="470013022865453920">"juhi süsteemi varundust ja taastet"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Võimaldab rakendusel juhtida süsteemi varundus- ja taastemehhanismi. Mitte kasutada tavarakenduste puhul."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"täieliku varukoopia kinnitamine või toimingu taastamine"</string>
diff --git a/core/res/res/values-fa/donottranslate-cldr.xml b/core/res/res/values-fa/donottranslate-cldr.xml
index 402311a..e1c8fec 100644
--- a/core/res/res/values-fa/donottranslate-cldr.xml
+++ b/core/res/res/values-fa/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%Y/%-m/%-e</string>
     <string name="numeric_date_format">yyyy/M/d</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 77d9a50..3f2da2c 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"به یک برنامه کاربردی اجازه می‌دهد که داده‌های استفاده کننده از میزان باتری کم کنونی را بخواند. این کار ممکن است به برنامه این امکان را بدهد که اطلاعات جزئی درباره برنامه‌هایی که استفاده می‌کنید، بدست آورد."</string>
     <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="permlab_backup" msgid="470013022865453920">"کنترل نسخهٔ پشتیبان سیستم و بازیابی"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"به برنامه اجازه می‎دهد پشتیبان سیستم را کنترل کند و مکانیستم را بازیابی کند. برای استفاده برنامه‎های عادی نیست."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"تهیهٔ نسخهٔ پشتیبان کامل را تأیید کرده یا عملیات را بازیابی کنید"</string>
diff --git a/core/res/res/values-fi-rFI/donottranslate-cldr.xml b/core/res/res/values-fi-rFI/donottranslate-cldr.xml
index 04f0a77..32b7803 100644
--- a/core/res/res/values-fi-rFI/donottranslate-cldr.xml
+++ b/core/res/res/values-fi-rFI/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k.%M</string>
     <string name="hour_minute_ampm">%-l.%M %p</string>
     <string name="hour_minute_cap_ampm">%-l.%M %^p</string>
-    <string name="twelve_hour_time_format">h.mm a</string>
-    <string name="twenty_four_hour_time_format">H.mm</string>
     <string name="numeric_date">%-e.%-m.%Y</string>
     <string name="numeric_date_format">d.M.yyyy</string>
     <string name="numeric_date_template">"%s.%s.%s"</string>
diff --git a/core/res/res/values-fi/donottranslate-cldr.xml b/core/res/res/values-fi/donottranslate-cldr.xml
index 543595f..df6f78f 100644
--- a/core/res/res/values-fi/donottranslate-cldr.xml
+++ b/core/res/res/values-fi/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k.%M</string>
     <string name="hour_minute_ampm">%-l.%M %p</string>
     <string name="hour_minute_cap_ampm">%-l.%M %^p</string>
-    <string name="twelve_hour_time_format">h.mm a</string>
-    <string name="twenty_four_hour_time_format">H.mm</string>
     <string name="numeric_date">%-e.%-m.%Y</string>
     <string name="numeric_date_format">d.M.yyyy</string>
     <string name="numeric_date_template">"%s.%s.%s"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 47d865f..7573f41 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Antaa sovelluksen lukea nykyisiä alhaisen tason akunkäyttötietoja. Sovellus saattaa saada tietoonsa, mitä sovelluksia käytät."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"muokkaa akkutilastoja"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Antaa sovelluksen muokata kerättyjä akkutilastoja. Ei tavallisten sovellusten käyttöön."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"sovellusten käyttötilastojen muokkaus"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Antaa sovelluksen muokata kerättyjä sovellusten käyttötilastoja. Ei tavallisten sovellusten käyttöön."</string>
     <string name="permlab_backup" msgid="470013022865453920">"hallitse järjestelmän varmuuskopiointia ja palauttamista"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Antaa sovelluksen hallita järjestelmän varmuuskopiointi- ja palautusmekanismia. Ei tavallisten sovellusten käyttöön."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"vahvista täysi varmuuskopiointi tai palauta toiminto"</string>
diff --git a/core/res/res/values-fr/donottranslate-cldr.xml b/core/res/res/values-fr/donottranslate-cldr.xml
index ff10aec..5698514 100644
--- a/core/res/res/values-fr/donottranslate-cldr.xml
+++ b/core/res/res/values-fr/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%d/%m/%Y</string>
     <string name="numeric_date_format">dd/MM/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 97fcecf..e4aad26 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Permet à une application de lire les données de consommation actuelles indiquant le faible niveau de la batterie. Permet éventuellement à l\'application d\'obtenir des informations détaillées sur les applications que vous utilisez."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"modifier les statistiques de la batterie"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Permet à l\'application de modifier les statistiques collectées concernant la batterie. Les applications standards ne doivent pas utiliser cette fonctionnalité."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modifier les statistiques de fonctionnement des applications"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Permet à l\'application de modifier les statistiques de fonctionnement des applications collectées. Les applications standards ne doivent pas utiliser cette fonctionnalité."</string>
     <string name="permlab_backup" msgid="470013022865453920">"contrôler la sauvegarde et la restauration du système"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Permet à l\'application de contrôler le mécanisme de sauvegarde et de restauration du système. Les applications standards ne doivent pas utiliser cette fonctionnalité."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"confirmer une sauvegarde complète ou une restauration"</string>
diff --git a/core/res/res/values-hi-rIN/donottranslate-cldr.xml b/core/res/res/values-hi-rIN/donottranslate-cldr.xml
index 5371e15..72b89c2 100644
--- a/core/res/res/values-hi-rIN/donottranslate-cldr.xml
+++ b/core/res/res/values-hi-rIN/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%-e-%-m-%Y</string>
     <string name="numeric_date_format">d-M-yyyy</string>
     <string name="numeric_date_template">"%s-%s-%s"</string>
diff --git a/core/res/res/values-hi/donottranslate-cldr.xml b/core/res/res/values-hi/donottranslate-cldr.xml
index 8043169..3e6138e 100644
--- a/core/res/res/values-hi/donottranslate-cldr.xml
+++ b/core/res/res/values-hi/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%-e-%-m-%Y</string>
     <string name="numeric_date_format">d-M-yyyy</string>
     <string name="numeric_date_template">"%s-%s-%s"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index a90fbd9..f4e2d3e 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"एप्लिकेशन को वर्तमान निम्न-स्तरीय बैटरी उपयोग डेटा पढ़ने देती है. एप्लिकेशन को आपके द्वारा उपयोग किए जाने वाले एप्लिकेशन के बारे में विस्तृत जानकारी ढूंढने दे सकती है."</string>
     <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="permlab_backup" msgid="470013022865453920">"सिस्‍टम बैकअप नियंत्रित और पुनर्स्‍थापित करें"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"एप्लिकेशन को सिस्टम के बैकअप को नियंत्रित और क्रियाविधि को पुर्नस्थापित करने देता है. सामान्‍य एप्‍लिकेशन द्वारा उपयोग करने के लिए नहीं."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"पूर्ण बैकअप या पुनर्स्‍थापना कार्यवाही की पुष्टि करें"</string>
diff --git a/core/res/res/values-hr-rHR/donottranslate-cldr.xml b/core/res/res/values-hr-rHR/donottranslate-cldr.xml
index 57e0572..d6d20e7 100644
--- a/core/res/res/values-hr-rHR/donottranslate-cldr.xml
+++ b/core/res/res/values-hr-rHR/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%-e.%-m.%Y.</string>
     <string name="numeric_date_format">d.M.yyyy.</string>
     <string name="numeric_date_template">"%s.%s.%s."</string>
diff --git a/core/res/res/values-hr/donottranslate-cldr.xml b/core/res/res/values-hr/donottranslate-cldr.xml
index 9e4b225..8703981 100644
--- a/core/res/res/values-hr/donottranslate-cldr.xml
+++ b/core/res/res/values-hr/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%-e.%-m.%Y.</string>
     <string name="numeric_date_format">d.M.yyyy.</string>
     <string name="numeric_date_template">"%s.%s.%s."</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index c930f02..a7085f4 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Aplikacija može očitavati podatke o trenutačnoj potrošnji baterije na niskoj razini. Tako može doznati detaljne informacije o aplikacijama koje upotrebljavate."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"izmjena statistike o bateriji"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Omogućuje aplikaciji promjenu prikupljene statistike o potrošnji baterije. Nije namijenjena uobičajenim aplikacijama."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"izmjena statistike o radu aplikacije"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Aplikaciji omogućuje promjenu prikupljene statistike o radu aplikacije. Nije namijenjena uobičajenim aplikacijama."</string>
     <string name="permlab_backup" msgid="470013022865453920">"sigurnosna kopija i oporavak nadzornog sustava"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Omogućuje aplikaciji upravljanje mehanizmom stvaranja sigurnosnih kopija i obnove sustava. Nije namijenjena uobičajenim aplikacijama."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"potvrditi postupak izrade sigurnosne kopije ili obnove"</string>
diff --git a/core/res/res/values-hu-rHU/donottranslate-cldr.xml b/core/res/res/values-hu-rHU/donottranslate-cldr.xml
index 5f4b8aa..59eb75a 100644
--- a/core/res/res/values-hu-rHU/donottranslate-cldr.xml
+++ b/core/res/res/values-hu-rHU/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%H:%M</string>
     <string name="hour_minute_ampm">%p %-l:%M</string>
     <string name="hour_minute_cap_ampm">%^p %-l:%M</string>
-    <string name="twelve_hour_time_format">a h:mm</string>
-    <string name="twenty_four_hour_time_format">HH:mm</string>
     <string name="numeric_date">%Y.%m.%d.</string>
     <string name="numeric_date_format">yyyy.MM.dd.</string>
     <string name="numeric_date_template">"%s.%s.%s."</string>
diff --git a/core/res/res/values-hu/donottranslate-cldr.xml b/core/res/res/values-hu/donottranslate-cldr.xml
index c925b82..525b0c0 100644
--- a/core/res/res/values-hu/donottranslate-cldr.xml
+++ b/core/res/res/values-hu/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%H:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">HH:mm</string>
     <string name="numeric_date">%Y.%m.%d.</string>
     <string name="numeric_date_format">yyyy.MM.dd.</string>
     <string name="numeric_date_template">"%s.%s.%s."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 2ee3c37..840ed06 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Lehetővé teszi egy alkalmazás számára, hogy leolvassa az aktuális alacsony szintű akkumulátorhasználatra vonatkozó adatokat. Ezáltal az alkalmazás részletes adatokhoz jut az Ön által használt alkalmazásokról."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"akkumulátorstatisztikák módosítása"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Lehetővé teszi az alkalmazás számára az összegyűjtött akkumulátorhasználati statisztikák módosítását. Normál alkalmazások nem használhatják."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"alkalmazásműveleti statisztikák módosítása"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Lehetővé teszi az alkalmazás számára az összegyűjtött alkalmazásműveleti statisztikák módosítását. Normál alkalmazások nem használhatják."</string>
     <string name="permlab_backup" msgid="470013022865453920">"rendszer biztonsági mentésének és helyreállításának vezérlése"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Lehetővé teszi az alkalmazás számára a rendszer biztonsági mentési és visszaállítási mechanizmusának vezérlését. Normál alkalmazások nem használhatják."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"teljes biztonsági mentés vagy helyreállítási művelet megerősítése"</string>
diff --git a/core/res/res/values-in-rID/donottranslate-cldr.xml b/core/res/res/values-in-rID/donottranslate-cldr.xml
index 8b13bcf..aedc2c7 100644
--- a/core/res/res/values-in-rID/donottranslate-cldr.xml
+++ b/core/res/res/values-in-rID/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%d/%m/%Y</string>
     <string name="numeric_date_format">dd/MM/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-in/donottranslate-cldr.xml b/core/res/res/values-in/donottranslate-cldr.xml
index 6e9bba4..7371132 100644
--- a/core/res/res/values-in/donottranslate-cldr.xml
+++ b/core/res/res/values-in/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%d/%m/%Y</string>
     <string name="numeric_date_format">dd/MM/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 1c1af24..638528f 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Mengizinkan aplikasi membaca data penggunaan baterai tingkat rendah. Dapat mengizinkan aplikasi mencari informasi mendetail tentang aplikasi mana yang Anda gunakan."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"ubah statistik baterai"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Mengizinkan aplikasi mengubah statistik baterai yang dikumpulkan. Tidak untuk digunakan oleh aplikasi normal."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"memodifikasi statistik pengoperasian aplikasi"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Mengizinkan aplikasi memodifikasi statistik pengoperasian aplikasi yang dikumpulkan. Tidak untuk digunakan oleh aplikasi normal."</string>
     <string name="permlab_backup" msgid="470013022865453920">"mengontrol cadangan dan pemulihan sistem"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Mengizinkan apl mengontrol mekanisme pencadangan dan pemulihan sistem. Tidak untuk digunakan oleh apl normal."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"konfirmasi pencadangan penuh atau pulihkan operasi"</string>
diff --git a/core/res/res/values-it/donottranslate-cldr.xml b/core/res/res/values-it/donottranslate-cldr.xml
index 9ff27e8..b06e73d 100644
--- a/core/res/res/values-it/donottranslate-cldr.xml
+++ b/core/res/res/values-it/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%H:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">HH:mm</string>
     <string name="numeric_date">%d/%m/%Y</string>
     <string name="numeric_date_format">dd/MM/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 7c160d7..7d18dc9 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -315,6 +315,8 @@
     <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="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>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"conferma di un\'operazione completa di backup o di ripristino"</string>
diff --git a/core/res/res/values-iw/donottranslate-cldr.xml b/core/res/res/values-iw/donottranslate-cldr.xml
index 631c059..037251e 100644
--- a/core/res/res/values-iw/donottranslate-cldr.xml
+++ b/core/res/res/values-iw/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%d/%m/%Y</string>
     <string name="numeric_date_format">dd/MM/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 1b5c6610..784a893 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"מאפשר ליישום לקרוא את נתוני השימוש הנוכחיים של הסוללה ברמה נמוכה. עשוי לאפשר ליישום לגלות מידע מפורט לגבי היישומים שבהם אתה משתמש."</string>
     <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="permlab_backup" msgid="470013022865453920">"שלוט בגיבוי ובשחזור של המערכת"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"מאפשר ליישום לשלוט במנגנון הגיבוי והשחזור של המערכת. לא מיועד לשימוש על ידי יישומים רגילים."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"אשר פעולה של גיבוי או שחזור מלא"</string>
diff --git a/core/res/res/values-ja/donottranslate-cldr.xml b/core/res/res/values-ja/donottranslate-cldr.xml
index 1c1d55f..f2cdbba 100644
--- a/core/res/res/values-ja/donottranslate-cldr.xml
+++ b/core/res/res/values-ja/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M%p</string>
     <string name="hour_minute_cap_ampm">%-l:%M%^p</string>
-    <string name="twelve_hour_time_format">h:mma</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%Y/%m/%d</string>
     <string name="numeric_date_format">yyyy/MM/dd</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 6b7eb01..e53ff29 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"現在の電池消費量の低レベルデータを読み取ることをアプリに許可します。このアプリが、使用しているアプリの詳細情報を確認できるようになります。"</string>
     <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="permlab_backup" msgid="470013022865453920">"システムのバックアップと復元を制御する"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"システムのバックアップと復元メカニズムの制御をアプリに許可します。通常のアプリでは使用しません。"</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"フルバックアップや復元の操作の確認"</string>
diff --git a/core/res/res/values-ko/donottranslate-cldr.xml b/core/res/res/values-ko/donottranslate-cldr.xml
index 59b975f..29d7982 100644
--- a/core/res/res/values-ko/donottranslate-cldr.xml
+++ b/core/res/res/values-ko/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%p %-l:%M</string>
     <string name="hour_minute_cap_ampm">%p %-l:%M</string>
-    <string name="twelve_hour_time_format">a h:mm</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%Y. %-m. %-e.</string>
     <string name="numeric_date_format">yyyy. M. d.</string>
     <string name="numeric_date_template">"%s. %s. %s."</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 8346fee..17006fd 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"애플리케이션이 현재의 낮은 수준 배터리 사용 데이터를 읽을 수 있도록 합니다. 이 경우 애플리케이션이 내가 사용하는 앱에 대한 세부정보를 파악할 수도 있습니다."</string>
     <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="permlab_backup" msgid="470013022865453920">"시스템 백업 및 복원 관리"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"앱이 시스템의 백업 및 복원 메커니즘을 제어할 수 있도록 허용합니다. 일반 앱에서는 사용하지 않습니다."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"전체 백업 또는 복원 작업 확인"</string>
diff --git a/core/res/res/values-lt-rLT/donottranslate-cldr.xml b/core/res/res/values-lt-rLT/donottranslate-cldr.xml
index 6cf6098..19ae2ea 100644
--- a/core/res/res/values-lt-rLT/donottranslate-cldr.xml
+++ b/core/res/res/values-lt-rLT/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%Y-%m-%d</string>
     <string name="numeric_date_format">yyyy-MM-dd</string>
     <string name="numeric_date_template">"%s-%s-%s"</string>
diff --git a/core/res/res/values-lt/donottranslate-cldr.xml b/core/res/res/values-lt/donottranslate-cldr.xml
index b1a94f3..0683d46 100644
--- a/core/res/res/values-lt/donottranslate-cldr.xml
+++ b/core/res/res/values-lt/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%Y-%m-%d</string>
     <string name="numeric_date_format">yyyy-MM-dd</string>
     <string name="numeric_date_template">"%s-%s-%s"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 25fc107..7700f1c 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Leidžiama programai skaityti dabartinius išsikraunančio akumuliatoriaus naudojimo duomenis. Gali būti leidžiama programai sužinoti išsamią informaciją apie jūsų naudojamas programas."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"keisti akumuliatoriaus statistiką"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Leidžiama programai keisti surinktą akumuliatoriaus statistiką. Neskirta naudoti įprastoms programoms."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"keisti programos naudojimo statistiką"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Programai leidžiama keisti surinktą programos naudojimo statistiką. Neskirta naudoti įprastoms programoms."</string>
     <string name="permlab_backup" msgid="470013022865453920">"valdyti sistemos atsarginę kopiją ir atkūrimą"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Leidžiama programai valdyti sistemos atsarginės kopijos ir atkūrimo mechanizmą. Neskirta naudoti įprastose programose."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"patvirtinkite visos atsarginės kopijos kūrimą arba atkurkite operaciją"</string>
diff --git a/core/res/res/values-lv-rLV/donottranslate-cldr.xml b/core/res/res/values-lv-rLV/donottranslate-cldr.xml
index 3922f6b..eb74fa3 100644
--- a/core/res/res/values-lv-rLV/donottranslate-cldr.xml
+++ b/core/res/res/values-lv-rLV/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%H:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">HH:mm</string>
     <string name="numeric_date">%d.%m.%Y</string>
     <string name="numeric_date_format">dd.MM.yyyy</string>
     <string name="numeric_date_template">"%s.%s.%s"</string>
diff --git a/core/res/res/values-lv/donottranslate-cldr.xml b/core/res/res/values-lv/donottranslate-cldr.xml
index ec768bc..cd55478 100644
--- a/core/res/res/values-lv/donottranslate-cldr.xml
+++ b/core/res/res/values-lv/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%H:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">HH:mm</string>
     <string name="numeric_date">%d.%m.%Y</string>
     <string name="numeric_date_format">dd.MM.yyyy</string>
     <string name="numeric_date_template">"%s.%s.%s"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 3f85435..040beb1 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Ļauj lietojumprogrammai lasīt pašreizējos zema akumulatora enerģijas patēriņa datus. Var atļaut lietojumprogrammai iegūt detalizētu informāciju par to, kuras lietotnes izmantojat."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"akumulatora statistikas pārveidošana"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Ļauj lietotnei pārveidot apkopoto statistiku par akumulatoru. Atļauja neattiecas uz parastām lietotnēm."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"Pārveidot lietotnes darbības statistiku"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Ļauj lietotnei pārveidot apkopoto statistiku par lietojumprogrammas darbību. Atļauja neattiecas uz parastām lietotnēm."</string>
     <string name="permlab_backup" msgid="470013022865453920">"kontrolēt sistēmas dublējumu un atjaunošanu"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Ļauj lietotnei kontrolēt sistēmas dublēšanas un atjaunošanas mehānismu. Atļauja neattiecas uz parastām lietotnēm."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"Apstiprināt pilnu dublējumu vai atjaunot darbību"</string>
diff --git a/core/res/res/values-ms/donottranslate-cldr.xml b/core/res/res/values-ms/donottranslate-cldr.xml
index a5843ff..7c02f3f 100644
--- a/core/res/res/values-ms/donottranslate-cldr.xml
+++ b/core/res/res/values-ms/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%d/%m/%Y</string>
     <string name="numeric_date_format">dd/MM/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 9d29e3c..436a1db 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Membenarkan aplikasi membaca data penggunaan bateri tahap rendah semasa. Boleh membenarkan aplikasi untuk mencari maklumat terperinci tentang apl yang anda gunakan."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"ubah suai statistik bateri"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Membenarkan apl mengubah suai statistik bateri yang dikumpul. Bukan untuk penggunaan apl normal."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"ubah suai apl ops statistik"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Membenarkan apl mengubah suai statistik operasi aplikasi yang dikumpul. Bukan untuk kegunaan apl biasa."</string>
     <string name="permlab_backup" msgid="470013022865453920">"sandaran dan pemulihan sistem kawalan"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Membenarkan apl untuk mengawal sandaran sistem dan memulihkan mekanisme. Bukan untuk digunakan oleh apl biasa."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"sahkan penyandaran penuh atau pemulihan operasi"</string>
diff --git a/core/res/res/values-nb/donottranslate-cldr.xml b/core/res/res/values-nb/donottranslate-cldr.xml
index 8eb1ff6..6d343ca 100644
--- a/core/res/res/values-nb/donottranslate-cldr.xml
+++ b/core/res/res/values-nb/donottranslate-cldr.xml
@@ -1,17 +1,15 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="hour_minute_24">%H.%M</string>
-    <string name="hour_minute_ampm">%-l.%M %p</string>
-    <string name="hour_minute_cap_ampm">%-l.%M %^p</string>
-    <string name="twelve_hour_time_format">h.mm a</string>
-    <string name="twenty_four_hour_time_format">HH.mm</string>
+    <string name="hour_minute_24">%H:%M</string>
+    <string name="hour_minute_ampm">%-l:%M %p</string>
+    <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
     <string name="numeric_date">%d.%m.%Y</string>
     <string name="numeric_date_format">dd.MM.yyyy</string>
     <string name="numeric_date_template">"%s.%s.%s"</string>
     <string name="month_day_year">%-e. %B %Y</string>
-    <string name="time_of_day">%H.%M.%S</string>
-    <string name="date_and_time">%H.%M.%S %-e. %b %Y</string>
+    <string name="time_of_day">%H:%M:%S</string>
+    <string name="date_and_time">%H:%M:%S %-e. %b %Y</string>
     <string name="date_time">%2$s %1$s</string>
     <string name="time_date">%1$s %3$s</string>
     <string name="abbrev_month_day_year">%-e. %b %Y</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index b9291a0..3a7cd46 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Lar apper lese gjeldende data på lavt nivå om batteribruk. Kan også la appen finne ut detaljert informasjon om hvilke apper du bruker."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"endre batteristatistikk"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Lar appen endre innsamlet batteristatistikk. Ikke beregnet på vanlige apper."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"endre bruksstatistikk for appen"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Lar appen endre innsamlet bruksstatistikk. Ikke beregnet på vanlige apper."</string>
     <string name="permlab_backup" msgid="470013022865453920">"kontrollere sikkerhetskopiering og gjenoppretting"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Lar appen kontrollere systemets mekanisme for sikkerhetskopiering og gjenoppretting. Ikke beregnet på vanlige apper."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"bekrefte en fullstendig sikkerhetskopi, eller gjenopprette driften"</string>
diff --git a/core/res/res/values-nl/donottranslate-cldr.xml b/core/res/res/values-nl/donottranslate-cldr.xml
index 1495a48..54e7e39 100644
--- a/core/res/res/values-nl/donottranslate-cldr.xml
+++ b/core/res/res/values-nl/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%H:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">HH:mm</string>
     <string name="numeric_date">%d-%m-%Y</string>
     <string name="numeric_date_format">dd-MM-yyyy</string>
     <string name="numeric_date_template">"%s-%s-%s"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 4e38a7d..1dd9fe9 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Hiermee kan de app het huidige accugebruik voor gegevens op laag niveau lezen. Een app kan hierdoor mogelijk gedetailleerde informatie achterhalen over de door u gebruikte apps."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"accustatistieken aanpassen"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Hiermee kan de app verzamelde accustatistieken wijzigen. Niet voor gebruik door normale apps."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"bewerkingsstatistieken van apps wijzigen"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Hiermee kan de app verzamelde bewerkingsstatistieken van apps wijzigen. Niet voor gebruik door normale apps."</string>
     <string name="permlab_backup" msgid="470013022865453920">"systeemback-up en -herstel beheren"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Hiermee kan de app het beheer van het mechanisme voor systeemback-up en -herstel beheren. Niet voor gebruik door normale apps."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"een volledige back-up- of herstelbewerking bevestigen"</string>
diff --git a/core/res/res/values-pl/donottranslate-cldr.xml b/core/res/res/values-pl/donottranslate-cldr.xml
index 53f0c36..91c98eab 100644
--- a/core/res/res/values-pl/donottranslate-cldr.xml
+++ b/core/res/res/values-pl/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%d.%m.%Y</string>
     <string name="numeric_date_format">dd.MM.yyyy</string>
     <string name="numeric_date_template">"%s.%s.%s"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 560769d..8123bea 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Zezwala aplikacji na odczytywanie bieżących danych niskiego poziomu o wykorzystaniu baterii. Możliwe jest wtedy zbieranie przez aplikację szczegółowych danych o używanych aplikacjach."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"zmienianie statystyk dotyczących baterii"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Zezwala aplikacji na modyfikowanie zebranych statystyk dotyczących baterii. Nieprzeznaczone dla zwykłych aplikacji."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modyfikowanie statystyk działania aplikacji"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Zezwala aplikacji na modyfikowanie zebranych statystyk działania aplikacji. Nieprzeznaczone dla zwykłych aplikacji."</string>
     <string name="permlab_backup" msgid="470013022865453920">"kontrolowanie tworzenia i przywracania kopii zapasowych systemu"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Pozwala aplikacji na sterowanie mechanizmem tworzenia kopii zapasowych systemu i ich przywracania. Nieprzeznaczone dla zwykłych aplikacji."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"Potwierdzenie operacji utworzenia pełnej kopii zapasowej lub przywracania"</string>
diff --git a/core/res/res/values-pt-rPT/donottranslate-cldr.xml b/core/res/res/values-pt-rPT/donottranslate-cldr.xml
index 26d8371..60787a9 100644
--- a/core/res/res/values-pt-rPT/donottranslate-cldr.xml
+++ b/core/res/res/values-pt-rPT/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-kh%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H\'h\'mm</string>
     <string name="numeric_date">%d/%m/%Y</string>
     <string name="numeric_date_format">dd/MM/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 1239695..66879ee 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Permite que uma aplicação leia os atuais dados de utilização da bateria de baixo nível. Poderá permitir que a aplicação encontre informações detalhadas sobre as aplicações que utiliza."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"modificar estatísticas da bateria"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Permite que a aplicação modifique as estatísticas recolhidas sobre a bateria. Não se destina a utilização por aplicações normais."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modificar estatísticas de utilização da aplicação"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Permite que a aplicação modifique as estatísticas de utilização de aplicação recolhidas. Não se destina a utilização por aplicações normais."</string>
     <string name="permlab_backup" msgid="470013022865453920">"controlar a cópia de segurança e restauro do sistema"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Permite que a aplicação controle o mecanismo de cópia de segurança e de restauro do sistema. Não se destina a utilização por aplicações normais."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"confirmar uma operação de restauro ou de cópia de segurança completa"</string>
diff --git a/core/res/res/values-pt/donottranslate-cldr.xml b/core/res/res/values-pt/donottranslate-cldr.xml
index c3e6c29..d5fee19 100644
--- a/core/res/res/values-pt/donottranslate-cldr.xml
+++ b/core/res/res/values-pt/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-kh%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H\'h\'mm</string>
     <string name="numeric_date">%d/%m/%Y</string>
     <string name="numeric_date_format">dd/MM/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 6e61195..b9167ef 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Permite que o aplicativo leia os dados de uso da bateria de baixo nível atuais. Pode fornecer ao aplicativo informações detalhadas sobre os aplicativos usados por você."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"modificar estatísticas da bateria"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Permite que o aplicativo modifique as estatísticas coletadas da bateria. Não deve ser usado em aplicativos normais."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modificar estatísticas de operações de aplicativos"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Permite que o aplicativo modifique as estatísticas de operações de aplicativos. Não deve ser usado em aplicativos normais."</string>
     <string name="permlab_backup" msgid="470013022865453920">"controlar backup e restauração do sistema"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Permite que o aplicativo controle o backup do sistema e restaure mecanismos. Não deve ser usado em aplicativos normais."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"confirmar um backup completo ou uma operação de restauração"</string>
diff --git a/core/res/res/values-rm/donottranslate-cldr.xml b/core/res/res/values-rm/donottranslate-cldr.xml
index e6f0df2..32e1170 100644
--- a/core/res/res/values-rm/donottranslate-cldr.xml
+++ b/core/res/res/values-rm/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%d.%m.%Y</string>
     <string name="numeric_date_format">dd.MM.yyyy</string>
     <string name="numeric_date_template">"%s.%s.%s"</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index 8b8f32b..8d8d882 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -477,6 +477,10 @@
     <skip />
     <!-- no translation found for permdesc_updateBatteryStats (6862817857178025002) -->
     <skip />
+    <!-- no translation found for permlab_updateAppOpsStats (8829097373851521505) -->
+    <skip />
+    <!-- no translation found for permdesc_updateAppOpsStats (50784596594403483) -->
+    <skip />
     <string name="permlab_backup" msgid="470013022865453920">"controllar las copias da segirezza e la restauraziun dal sistem"</string>
     <!-- no translation found for permdesc_backup (6912230525140589891) -->
     <skip />
diff --git a/core/res/res/values-ro-rRO/donottranslate-cldr.xml b/core/res/res/values-ro-rRO/donottranslate-cldr.xml
index 7056803..1233f4e 100644
--- a/core/res/res/values-ro-rRO/donottranslate-cldr.xml
+++ b/core/res/res/values-ro-rRO/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%d.%m.%Y</string>
     <string name="numeric_date_format">dd.MM.yyyy</string>
     <string name="numeric_date_template">"%s.%s.%s"</string>
diff --git a/core/res/res/values-ro/donottranslate-cldr.xml b/core/res/res/values-ro/donottranslate-cldr.xml
index 935c6af..f80a944 100644
--- a/core/res/res/values-ro/donottranslate-cldr.xml
+++ b/core/res/res/values-ro/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%d.%m.%Y</string>
     <string name="numeric_date_format">dd.MM.yyyy</string>
     <string name="numeric_date_template">"%s.%s.%s"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 82d7532..30276cb 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Permite unei aplicaţii să citească datele actuale privind utilizarea la nivel redus a bateriei. Cu această permisiune, aplicaţia poate afla informaţii detaliate despre aplicaţiile pe care le utilizaţi."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"modifică statistici referitoare la baterie"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Permite aplicaţiei să modifice statisticile colectate despre baterie. Nu se utilizează de aplicaţiile obişnuite."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modificarea statisticilor privind utilizarea aplicației"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Permite aplicației să modifice statisticile colectate despre utilizarea aplicației. Nu se utilizează de aplicațiile obișnuite."</string>
     <string name="permlab_backup" msgid="470013022865453920">"controlare copiere de rezervă şi restabilire a sistemului"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Permite aplicaţiei să controleze mecanismul de copiere de rezervă şi de restabilire al sistemului. Nu se utilizează de aplicaţiile obişnuite."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"confirmă o operaţie completă de copiere de rezervă sau de restabilire"</string>
diff --git a/core/res/res/values-ru/donottranslate-cldr.xml b/core/res/res/values-ru/donottranslate-cldr.xml
index 03c64ab..4e315eb 100644
--- a/core/res/res/values-ru/donottranslate-cldr.xml
+++ b/core/res/res/values-ru/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%d.%m.%Y</string>
     <string name="numeric_date_format">dd.MM.yyyy</string>
     <string name="numeric_date_template">"%s.%s.%s"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 89b0e06..bbfd928 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Разрешает приложению получать данные об использовании батареи на низшем уровне. В результате оно может иметь доступ к информации об используемых вами программах."</string>
     <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="permlab_backup" msgid="470013022865453920">"управление резервным копированием и восстановлением системы"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Приложение сможет управлять механизмами резервного копирования и восстановления системы. Это разрешение не используется обычными приложениями."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"подтверждать полное резервное копирование или восстановление"</string>
diff --git a/core/res/res/values-sk-rSK/donottranslate-cldr.xml b/core/res/res/values-sk-rSK/donottranslate-cldr.xml
index 651c58c..94dca66 100644
--- a/core/res/res/values-sk-rSK/donottranslate-cldr.xml
+++ b/core/res/res/values-sk-rSK/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%H:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">HH:mm</string>
     <string name="numeric_date">%-e. %-m. %Y</string>
     <string name="numeric_date_format">d. M. yyyy</string>
     <string name="numeric_date_template">"%s. %s. %s"</string>
diff --git a/core/res/res/values-sk/donottranslate-cldr.xml b/core/res/res/values-sk/donottranslate-cldr.xml
index 48c644a..9eeb133 100644
--- a/core/res/res/values-sk/donottranslate-cldr.xml
+++ b/core/res/res/values-sk/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%H:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">HH:mm</string>
     <string name="numeric_date">%-e.%-m.%Y</string>
     <string name="numeric_date_format">d.M.yyyy</string>
     <string name="numeric_date_template">"%s.%s.%s"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 8d1250e..c0147eb 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Umožňuje aplikácii čítať aktuálne údaje nízkej úrovne o používaní batérie. Pomocou tejto funkcie môže aplikácia zistiť podrobnosti o tom, ktoré aplikácie používate."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"zmena štatistických údajov o batérii"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Umožňuje aplikácii zmeniť zhromaždené štatistické údaje o batérii. Bežné aplikácie toto nastavenie nepoužívajú."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"upraviť štatistické údaje o fungovaní aplikácií"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Umožňuje aplikácii zmeniť zhromaždené štatistické údaje o fungovaní aplikácií. Bežné aplikácie toto nastavenie nepoužívajú."</string>
     <string name="permlab_backup" msgid="470013022865453920">"Ovládať zálohovanie a obnovu systému"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Umožňuje aplikácii ovládať mechanizmus na zálohovanie a obnovu údajov systému. Bežné aplikácie toto nastavenie nepoužívajú."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"potvrdenie operácie úplnej zálohy alebo úplného obnovenia"</string>
diff --git a/core/res/res/values-sl-rSI/donottranslate-cldr.xml b/core/res/res/values-sl-rSI/donottranslate-cldr.xml
index 84fffbd..6cf9bd6 100644
--- a/core/res/res/values-sl-rSI/donottranslate-cldr.xml
+++ b/core/res/res/values-sl-rSI/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%-e. %m. %Y</string>
     <string name="numeric_date_format">d. MM. yyyy</string>
     <string name="numeric_date_template">"%s. %s. %s"</string>
diff --git a/core/res/res/values-sl/donottranslate-cldr.xml b/core/res/res/values-sl/donottranslate-cldr.xml
index 137ed3f..49d36fd 100644
--- a/core/res/res/values-sl/donottranslate-cldr.xml
+++ b/core/res/res/values-sl/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%-e. %m. %Y</string>
     <string name="numeric_date_format">d. MM. yyyy</string>
     <string name="numeric_date_template">"%s. %s. %s"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 508156b..5e90928 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Aplikaciji omogoča branje podatkov o trenutni nizki napolnjenosti akumulatorja. Aplikaciji lahko tudi dovoli dostop do podrobnosti o tem, katere aplikacije uporabljate."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"spreminjanje statističnih podatkov o akumulatorju"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Aplikaciji omogoča spreminjanje zbranih statističnih podatkov o akumulatorju. Ni primerno za uporabo z običajnimi aplikacijami."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"spreminjanje statističnih podatkov o delovanju aplikacije"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Aplikaciji dovoli spreminjanje zbranih statističnih podatkov o delovanju aplikacij. Ni za uporabo v navadnih aplikacijah."</string>
     <string name="permlab_backup" msgid="470013022865453920">"nadzor varnostnega kopiranja sistema in obnovitev"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Programu omogoča nadzor mehanizma za varnostno kopiranje in obnovitev sistema. Ni za uporabo z navadnimi programi."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"potrditev popolnega varnostnega kopiranja ali obnovitve"</string>
diff --git a/core/res/res/values-sr-rRS/donottranslate-cldr.xml b/core/res/res/values-sr-rRS/donottranslate-cldr.xml
index 996c75e..3f889600 100644
--- a/core/res/res/values-sr-rRS/donottranslate-cldr.xml
+++ b/core/res/res/values-sr-rRS/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%H.%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">HH.mm</string>
     <string name="numeric_date">%-e.%-m.%Y.</string>
     <string name="numeric_date_format">d.M.yyyy.</string>
     <string name="numeric_date_template">"%s.%s.%s."</string>
diff --git a/core/res/res/values-sr/donottranslate-cldr.xml b/core/res/res/values-sr/donottranslate-cldr.xml
index 7fca1a2..ca3cb08 100644
--- a/core/res/res/values-sr/donottranslate-cldr.xml
+++ b/core/res/res/values-sr/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%H.%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">HH.mm</string>
     <string name="numeric_date">%-e.%-m.%Y.</string>
     <string name="numeric_date_format">d.M.yyyy.</string>
     <string name="numeric_date_template">"%s.%s.%s."</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 8b624af..736a545 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Дозвољава апликацији да чита опште податке о тренутној употреби батерије на измаку. Можда ће апликацији дозволити да сазна детаљне информације о томе које апликације користите."</string>
     <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="permlab_backup" msgid="470013022865453920">"контрола резервне копије система и враћање почетних вредности"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Дозвољава апликацији да управља системским механизмом за прављење резервне копије и враћање. Не користе је уобичајене апликације."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"потврда прављења пуне резервне копије или операције враћања"</string>
diff --git a/core/res/res/values-sv/donottranslate-cldr.xml b/core/res/res/values-sv/donottranslate-cldr.xml
index 64c83f2..46b9f9d 100644
--- a/core/res/res/values-sv/donottranslate-cldr.xml
+++ b/core/res/res/values-sv/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%d-%m-%Y</string>
     <string name="numeric_date_format">dd-MM-yyyy</string>
     <string name="numeric_date_template">"%s-%s-%s"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index ddf2863..e975f68 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Tillåter att en app läser de aktuella uppgifterna om låg batterianvändningsnivå. Appen kan tillåtas få reda på detaljerade uppgifter om vilka appar du använder."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"ändra batteristatistik"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Tillåter att appen ändrar samlad batteristatistik. Används inte av vanliga appar."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"ändra appars åtgärdsstatistik"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Tillåter att appen ändrar samlad åtgärdsstatistik för appar. Används inte av vanliga appar."</string>
     <string name="permlab_backup" msgid="470013022865453920">"kontrollera säkerhetskopiering och återställning av systemet"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Tillåter att appen styr över systemets mekanism för säkerhetskopiering och återställning. Används inte av vanliga appar."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"Bekräfta fullständig säkerhetskopia eller återställning"</string>
diff --git a/core/res/res/values-sw/donottranslate-cldr.xml b/core/res/res/values-sw/donottranslate-cldr.xml
index a7a5150..e0625e9 100644
--- a/core/res/res/values-sw/donottranslate-cldr.xml
+++ b/core/res/res/values-sw/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%Y/%m/%d</string>
     <string name="numeric_date_format">yyyy/MM/dd</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index a949499c..fff8a16 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -315,7 +315,9 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Inaruhusu programu kusoma data ya sasa ya matumizi ya kiwango cha chini cha betri. Huenda ikaruhusu kupata maelezo ya kina kuhusu programu unazozitumia."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"rekebisha takwimu za betri"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Inaruhusu programu kurekebisha takwimu za betri zilizokusanywa. Si ya kutumiwa na programu za kawaida."</string>
-    <string name="permlab_backup" msgid="470013022865453920">"Dhibiti chelezo la mfumo na rejesha"</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"rekebisha takwimu za oparesheni ya programu"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Inaruhusu programu kurekebisha takwimu za matumizi ya programu zilizokusanywa. Si ya kutumiwa na programu za kawaida."</string>
+    <string name="permlab_backup" msgid="470013022865453920">"Dhibiti kuhifadhi nakala na kurejesha kwa mfumo"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Inaruhusu programu kudhibiti utaratibu wa kucheleza na kurejesha wa mfumo. Si kwa matumizi na programu za kawaida."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"thibitisha chelezo kamilifu au rejesha upya uendeshaji"</string>
     <string name="permdesc_confirm_full_backup" msgid="1748762171637699562">"Inaruhusu programu kuzindua UI ya kuthibitisha chelezo kamili. Si ya kutumiwa na programu yoyote."</string>
diff --git a/core/res/res/values-th-rTH/donottranslate-cldr.xml b/core/res/res/values-th-rTH/donottranslate-cldr.xml
index 2517143..94ec23d 100644
--- a/core/res/res/values-th-rTH/donottranslate-cldr.xml
+++ b/core/res/res/values-th-rTH/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%-e/%-m/%Y</string>
     <string name="numeric_date_format">d/M/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-th/donottranslate-cldr.xml b/core/res/res/values-th/donottranslate-cldr.xml
index 7ab4191..b42656a 100644
--- a/core/res/res/values-th/donottranslate-cldr.xml
+++ b/core/res/res/values-th/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%-e/%-m/%Y</string>
     <string name="numeric_date_format">d/M/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 5aa1cbe..96e03b1 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"อนุญาตให้แอปพลิเคชันอ่านข้อมูลการใช้แบตเตอรี่ที่มีพลังงานเหลือน้อยในปัจจุบัน โดยอาจอนุญาตให้แอปพลิเคชันค้นหาข้อมูลรายละเอียดว่าคุณใช้งานแอปพลิเคชันใดบ้าง"</string>
     <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="permlab_backup" msgid="470013022865453920">"ควบคุมการสำรองและคืนค่า"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"อนุญาตให้แอปพลิเคชันควบคุมการสำรองข้อมูลของระบบและกลไกการเรียกคืน ไม่ใช้สำหรับแอปพลิเคชันทั่วไป"</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"ยืนยันการสำรองข้อมูลหรือการคืนค่าทั้งหมด"</string>
diff --git a/core/res/res/values-tl/donottranslate-cldr.xml b/core/res/res/values-tl/donottranslate-cldr.xml
index 4545fb5..970a538 100644
--- a/core/res/res/values-tl/donottranslate-cldr.xml
+++ b/core/res/res/values-tl/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%Y-%m-%d</string>
     <string name="numeric_date_format">yyyy-MM-dd</string>
     <string name="numeric_date_template">"%s-%s-%s"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 092ce73..95d8cf2 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Binibigyang-daan ang application na basahin ang kasalukuyang data sa paggamit ng mababang antas ng baterya. Maaaring bigyang-daan ang application na malaman ang detalyadong impormasyon tungkol sa kung aling apps ang ginagamit mo."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"baguhin ang mga istatistika ng baterya"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Binibigyang-daan ang app na baguhin ang mga nakolektang istatistika ng baterya. Hindi para sa paggamit ng normal na apps."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"baguhin ang mga istatistika ng pagpapatakbo ng app"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Binibigyang-daan ang app na baguhin ang mga nakolektang istatistika ng pagpapatakbo ng application. Hindi para sa paggamit ng normal na apps."</string>
     <string name="permlab_backup" msgid="470013022865453920">"kontrolin ang system backup at pagbawi"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Pinapayagan ang app na kontrolin ang backup ng system at ipanumbalik ang mekanismo. Hindi para sa paggamit ng normal na apps."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"Kumpirmahin ang isang buong pagpapatakbo ng pag-backup o pagpapanumbalik"</string>
diff --git a/core/res/res/values-tr/donottranslate-cldr.xml b/core/res/res/values-tr/donottranslate-cldr.xml
index 1ab4ff7..a0ee370 100644
--- a/core/res/res/values-tr/donottranslate-cldr.xml
+++ b/core/res/res/values-tr/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%H:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">HH:mm</string>
     <string name="numeric_date">%d %m %Y</string>
     <string name="numeric_date_format">dd MM yyyy</string>
     <string name="numeric_date_template">"%s %s %s"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index d335479..7308264 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Uygulamaya, mevcut pil kullanım verilerini alt düzeyde okuma izni verir. Uygulamanın hangi uygulamaları kullandığınızla ilgili ayrıntılı bilgi edinmesine olanak sağlayabilir."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"pil istatistiklerini değiştir"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Uygulamaya, toplanan pil kullanım istatistiklerini değiştirme izni verir. Normal uygulamaların kullanımına yönelik değildir."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"uygulama çalışma istatistiklerini değiştir"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Uygulamaya, uygulama çalışma istatistiklerini değiştirme izni verir. Normal uygulamaların kullanımına yönelik değildir."</string>
     <string name="permlab_backup" msgid="470013022865453920">"sistem yedeğini kontrol et ve geri yükle"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Uygulamaya, sistem yedekleme ve geri yükleme mekanizmasını denetleme izni verir. Normal uygulamaların kullanımına yönelik değildir."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"tam yedekleme veya geri yükleme işlemini onaylayın"</string>
diff --git a/core/res/res/values-uk-rUA/donottranslate-cldr.xml b/core/res/res/values-uk-rUA/donottranslate-cldr.xml
index f7ca458..5cb41f4 100644
--- a/core/res/res/values-uk-rUA/donottranslate-cldr.xml
+++ b/core/res/res/values-uk-rUA/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%d.%m.%Y</string>
     <string name="numeric_date_format">dd.MM.yyyy</string>
     <string name="numeric_date_template">"%s.%s.%s"</string>
diff --git a/core/res/res/values-uk/donottranslate-cldr.xml b/core/res/res/values-uk/donottranslate-cldr.xml
index 51c2cb5..f2a61da 100644
--- a/core/res/res/values-uk/donottranslate-cldr.xml
+++ b/core/res/res/values-uk/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%d.%m.%Y</string>
     <string name="numeric_date_format">dd.MM.yyyy</string>
     <string name="numeric_date_template">"%s.%s.%s"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 904adae..17460f1 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Дозволяє програмі зчитувати дані про поточний низький рівень споживання заряду акумулятора. Програма може отримувати докладну інформацію про те, якими програмами ви користуєтеся."</string>
     <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="permlab_backup" msgid="470013022865453920">"контр. резерв. копіюв. і відн. сист."</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Дозволяє програмі контролювати механізми резервного копіювання та відновлення системи. Не для використання звичайними програмами."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"підтверджувати повну операцію резервного копіювання або відновлення"</string>
diff --git a/core/res/res/values-vi-rVN/donottranslate-cldr.xml b/core/res/res/values-vi-rVN/donottranslate-cldr.xml
index a385015..307dab2 100644
--- a/core/res/res/values-vi-rVN/donottranslate-cldr.xml
+++ b/core/res/res/values-vi-rVN/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%d/%m/%Y</string>
     <string name="numeric_date_format">dd/MM/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-vi/donottranslate-cldr.xml b/core/res/res/values-vi/donottranslate-cldr.xml
index 977e021..f4d5dc3 100644
--- a/core/res/res/values-vi/donottranslate-cldr.xml
+++ b/core/res/res/values-vi/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%d/%m/%Y</string>
     <string name="numeric_date_format">dd/MM/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 01b76c3..148f4a8 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Cho phép ứng dụng đọc dữ liệu sử dụng pin mức thấp hiện tại. Có thể cho phép ứng dụng biết thông tin chi tiết về ứng dụng bạn sử dụng."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"sửa đổi số liệu thống kê về pin"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Cho phép ứng dụng sửa đổi các số liệu thống kê về pin đã được thu thập. Không dành cho các ứng dụng thông thường."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"sửa đổi số liệu thống kê hoạt động của ứng dụng"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Cho phép ứng dụng sửa đổi số liệu thống kê hoạt động của ứng dụng đã thu thập. Không dành cho ứng dụng thông thường."</string>
     <string name="permlab_backup" msgid="470013022865453920">"kiểm soát sao lưu và khôi phục hệ thống"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Cho phép ứng dụng kiểm soát cơ chế sao lưu và khôi phục của hệ thống. Không dành cho các ứng dụng thông thường."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"xác nhận bản sao lưu đầy đủ hoặc khôi phục hoạt động"</string>
diff --git a/core/res/res/values-zh-rCN/donottranslate-cldr.xml b/core/res/res/values-zh-rCN/donottranslate-cldr.xml
index b34b8b3..6ac384a 100644
--- a/core/res/res/values-zh-rCN/donottranslate-cldr.xml
+++ b/core/res/res/values-zh-rCN/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%p %-l:%M</string>
     <string name="hour_minute_cap_ampm">%p %-l:%M</string>
-    <string name="twelve_hour_time_format">a h:mm</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%Y-%-m-%-e</string>
     <string name="numeric_date_format">yyyy-M-d</string>
     <string name="numeric_date_template">"%s-%s-%s"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index bc7df02..fa43c99 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"允许应用读取当前电量使用情况的基础数据,此权限可让应用了解关于您使用了哪些应用的详细信息。"</string>
     <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="permlab_backup" msgid="470013022865453920">"控制系统备份和还原"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"允许应用控制系统的备份和还原机制。普通应用不能使用此权限。"</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"确认完整备份或恢复操作"</string>
diff --git a/core/res/res/values-zh-rTW/donottranslate-cldr.xml b/core/res/res/values-zh-rTW/donottranslate-cldr.xml
index 869705e..42faa83 100644
--- a/core/res/res/values-zh-rTW/donottranslate-cldr.xml
+++ b/core/res/res/values-zh-rTW/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%p %-l:%M</string>
     <string name="hour_minute_cap_ampm">%p %-l:%M</string>
-    <string name="twelve_hour_time_format">a h:mm</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%Y/%-m/%-e</string>
     <string name="numeric_date_format">yyyy/M/d</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index a574468..3bfed3c 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"允許應用程式讀取目前的低電量使用資料。應用程式可能藉此找到一些詳細資訊,例如您所使用的應用程式為何。"</string>
     <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="permlab_backup" msgid="470013022865453920">"控制系統備份與還原"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"允許應用程式控制系統備份與還原機制 (不建議一般應用程式使用)。"</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"確認完整備份或還原作業"</string>
diff --git a/core/res/res/values-zu/donottranslate-cldr.xml b/core/res/res/values-zu/donottranslate-cldr.xml
index 97b179b1..703823c 100644
--- a/core/res/res/values-zu/donottranslate-cldr.xml
+++ b/core/res/res/values-zu/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%-k:%M</string>
     <string name="hour_minute_ampm">%-l:%M %p</string>
     <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">H:mm</string>
     <string name="numeric_date">%Y-%m-%d</string>
     <string name="numeric_date_format">yyyy-MM-dd</string>
     <string name="numeric_date_template">"%s-%s-%s"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 6dab9ffc..54be6ea 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -315,6 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Ivumela uhlelo lokusebenza ukufunda idatha yokusebenza yebhethri leleveli ephansi yamanje. Ingavumela uhlelo lokusebenza ukuthola ulwazi lemininingwane mayelana nokuthi iziphi izinhlelo zokusebenza ozisebenzisayo."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"guqula izibalo zebhetri"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Ivumela uhlelo lokusebenza ukuthi luguqule izibalo zebhethri eziqoqiwe. Akwenzelwe ukuthi kusetshenziswe izinhlelo zokusebenza ezijwayelekile."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"shintsha izinombolo zokusebenza zohlelo lokusebenza"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Ivumela uhlelo lokusebenza ukuthi lishintshe izinombolo zokusebenza kohlelo lokusebenza lokuqoqiwe. Akufanele kusetshenziswe izinhlelo zokusebenza ezijwayelekile."</string>
     <string name="permlab_backup" msgid="470013022865453920">"lawula ukusekela ngokulondoloza uhlelo bese ubuyisela esimweni"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Ivumela insiza ukuthi ilawule isipele sesistimu kanye nohlelo lokubuyiselwa kabusha. Ayenzelwe ukusetshenziswa izinsiza ezijwayelekile."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"qinisekisa isipele sonke noma buyisela futhi ukusebenza"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 69a723da8..fa15324 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3509,7 +3509,7 @@
         <attr name="calendarViewShown" format="boolean" />
         <!-- The minimal date shown by this calendar view in mm/dd/yyyy format. -->
         <attr name="minDate" format="string" />
-        <!-- The minimal date shown by this calendar view in mm/dd/yyyy format. -->
+        <!-- The maximal date shown by this calendar view in mm/dd/yyyy format. -->
         <attr name="maxDate" format="string" />
         <!-- @hide The layout of the date picker. -->
         <attr name="internalLayout" format="reference"  />
diff --git a/core/res/res/values/donottranslate-cldr.xml b/core/res/res/values/donottranslate-cldr.xml
index 0587c165..4d37d47 100644
--- a/core/res/res/values/donottranslate-cldr.xml
+++ b/core/res/res/values/donottranslate-cldr.xml
@@ -4,8 +4,6 @@
     <string name="hour_minute_24">%H:%M</string>
     <string name="hour_minute_ampm">%-l:%M%p</string>
     <string name="hour_minute_cap_ampm">%-l:%M%^p</string>
-    <string name="twelve_hour_time_format">h:mm a</string>
-    <string name="twenty_four_hour_time_format">HH:mm</string>
     <string name="numeric_date">%-m/%-e/%Y</string>
     <string name="numeric_date_format">M/d/yyyy</string>
     <string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values/donottranslate.xml b/core/res/res/values/donottranslate.xml
index bdcab39..b49e7bd 100644
--- a/core/res/res/values/donottranslate.xml
+++ b/core/res/res/values/donottranslate.xml
@@ -26,4 +26,8 @@
     <string name="lock_pattern_view_aspect">square</string>
     <!-- @hide DO NOT TRANSLATE. Separator between the hour and minute elements in a TimePicker widget -->
     <string name="time_picker_separator">:</string>
+    <!-- @hide DO NOT TRANSLATE. ICU pattern for "Mon, 14 January" -->
+    <string name="icu_abbrev_wday_month_day_no_year">eeeMMMMd</string>
+    <!-- @hide DO NOT TRANSLATE. date formatting pattern for system ui.-->
+    <string name="system_ui_date_pattern">@string/icu_abbrev_wday_month_day_no_year</string>
 </resources>
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/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7fc3a34..6db89d9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -813,8 +813,6 @@
   <java-symbol type="string" name="time_picker_separator" />
   <java-symbol type="string" name="time_wday" />
   <java-symbol type="string" name="time_wday_date" />
-  <java-symbol type="string" name="twelve_hour_time_format" />
-  <java-symbol type="string" name="twenty_four_hour_time_format" />
   <java-symbol type="string" name="upload_file" />
   <java-symbol type="string" name="user_switched" />
   <java-symbol type="string" name="volume_alarm" />
@@ -1376,6 +1374,7 @@
   <java-symbol type="layout" name="keyguard_transport_control_view" />
   <java-symbol type="layout" name="keyguard_status_view" />
   <java-symbol type="string" name="abbrev_wday_month_day_no_year" />
+  <java-symbol type="string" name="system_ui_date_pattern" />
   <java-symbol type="string" name="android_upgrading_title" />
   <java-symbol type="string" name="bugreport_title" />
   <java-symbol type="string" name="bugreport_message" />
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
index a6057de..649e39d 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
@@ -28,6 +28,7 @@
 import android.net.wifi.WifiConfiguration.IpAssignment;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiConfiguration.ProxySettings;
+import android.net.wifi.WifiEnterpriseConfig;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.RouteInfo;
@@ -67,7 +68,6 @@
  *      networkprefixlength.
  */
 public class AccessPointParserHelper {
-    private static final String KEYSTORE_SPACE = "keystore://";
     private static final String TAG = "AccessPointParserHelper";
     static final int NONE = 0;
     static final int WEP = 1;
@@ -212,14 +212,11 @@
                         config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
                         config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
                         // Initialize other fields.
-                        config.phase2.setValue("");
-                        config.ca_cert.setValue("");
-                        config.client_cert.setValue("");
-                        config.engine.setValue("");
-                        config.engine_id.setValue("");
-                        config.key_id.setValue("");
-                        config.identity.setValue("");
-                        config.anonymous_identity.setValue("");
+                        config.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.NONE);
+                        config.enterpriseConfig.setCaCertificate("");
+                        config.enterpriseConfig.setClientCertificate("");
+                        config.enterpriseConfig.setIdentity("");
+                        config.enterpriseConfig.setAnonymousIdentity("");
                         break;
                     default:
                         throw new SAXException();
@@ -246,7 +243,7 @@
                         config.preSharedKey = '"' + passwordStr + '"';
                     }
                 } else if (securityType == EAP) {
-                    config.password.setValue(passwordStr);
+                    config.enterpriseConfig.setPassword(passwordStr);
                 } else {
                     throw new SAXException();
                 }
@@ -257,33 +254,46 @@
                 if (!validateEapValue(eapValue)) {
                     throw new SAXException();
                 }
-                config.eap.setValue(eapValue);
+		if (eapValue.equals("TLS")) {
+		    config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+		} else if (eapValue.equals("TTLS")) {
+		    config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TTLS);
+		} else if (eapValue.equals("PEAP")) {
+		    config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.PEAP);
+		}
                 eap = false;
             }
             if (phase2) {
                 String phase2Value = new String(ch, start, length);
-                config.phase2.setValue("auth=" + phase2Value);
+		if (phase2Value.equals("PAP")) {
+                    config.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.PAP);
+		} else if (phase2Value.equals("MSCHAP")) {
+                    config.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.MSCHAP);
+		} else if (phase2Value.equals("MSCHAPV2")) {
+                    config.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.MSCHAPV2);
+		} else if (phase2Value.equals("GTC")) {
+                    config.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC);
+		}
                 phase2 = false;
             }
             if (identity) {
                 String identityValue = new String(ch, start, length);
-                config.identity.setValue(identityValue);
+                config.enterpriseConfig.setIdentity(identityValue);
                 identity = false;
             }
             if (anonymousidentity) {
                 String anonyId = new String(ch, start, length);
-                config.anonymous_identity.setValue(anonyId);
+                config.enterpriseConfig.setAnonymousIdentity(anonyId);
                 anonymousidentity = false;
             }
             if (cacert) {
                 String cacertValue = new String(ch, start, length);
-                // need to install the credentail to "keystore://"
-                config.ca_cert.setValue(KEYSTORE_SPACE);
+                config.enterpriseConfig.setCaCertificate(cacertValue);
                 cacert = false;
             }
             if (usercert) {
                 String usercertValue = new String(ch, start, length);
-                config.client_cert.setValue(KEYSTORE_SPACE);
+                config.enterpriseConfig.setClientCertificate(usercertValue);
                 usercert = false;
             }
             if (ip) {
diff --git a/docs/downloads/devbytes/BitmapAllocation.zip b/docs/downloads/devbytes/BitmapAllocation.zip
new file mode 100644
index 0000000..b7426d0
--- /dev/null
+++ b/docs/downloads/devbytes/BitmapAllocation.zip
Binary files differ
diff --git a/docs/downloads/devbytes/BitmapScaling.zip b/docs/downloads/devbytes/BitmapScaling.zip
new file mode 100644
index 0000000..ff9832b
--- /dev/null
+++ b/docs/downloads/devbytes/BitmapScaling.zip
Binary files differ
diff --git a/docs/downloads/devbytes/Bouncer.zip b/docs/downloads/devbytes/Bouncer.zip
new file mode 100644
index 0000000..b0b128e
--- /dev/null
+++ b/docs/downloads/devbytes/Bouncer.zip
Binary files differ
diff --git a/docs/downloads/devbytes/CrossFading.zip b/docs/downloads/devbytes/CrossFading.zip
new file mode 100644
index 0000000..d31f66d
--- /dev/null
+++ b/docs/downloads/devbytes/CrossFading.zip
Binary files differ
diff --git a/docs/downloads/devbytes/KeyframeAnimation.zip b/docs/downloads/devbytes/KeyframeAnimation.zip
new file mode 100644
index 0000000..79e1deb
--- /dev/null
+++ b/docs/downloads/devbytes/KeyframeAnimation.zip
Binary files differ
diff --git a/docs/downloads/devbytes/LayoutTransChanging.zip b/docs/downloads/devbytes/LayoutTransChanging.zip
new file mode 100644
index 0000000..e6d76b4
--- /dev/null
+++ b/docs/downloads/devbytes/LayoutTransChanging.zip
Binary files differ
diff --git a/docs/downloads/devbytes/ListViewAnimations.zip b/docs/downloads/devbytes/ListViewAnimations.zip
new file mode 100644
index 0000000..d70100e
--- /dev/null
+++ b/docs/downloads/devbytes/ListViewAnimations.zip
Binary files differ
diff --git a/docs/downloads/devbytes/ListViewDeletion.zip b/docs/downloads/devbytes/ListViewDeletion.zip
new file mode 100644
index 0000000..78c4ed3
--- /dev/null
+++ b/docs/downloads/devbytes/ListViewDeletion.zip
Binary files differ
diff --git a/docs/downloads/devbytes/PictureViewer.zip b/docs/downloads/devbytes/PictureViewer.zip
new file mode 100644
index 0000000..3999d8f
--- /dev/null
+++ b/docs/downloads/devbytes/PictureViewer.zip
Binary files differ
diff --git a/docs/downloads/devbytes/PropertyAnimations.zip b/docs/downloads/devbytes/PropertyAnimations.zip
new file mode 100644
index 0000000..97c013c
--- /dev/null
+++ b/docs/downloads/devbytes/PropertyAnimations.zip
Binary files differ
diff --git a/docs/downloads/devbytes/RequestDuringLayout.zip b/docs/downloads/devbytes/RequestDuringLayout.zip
new file mode 100644
index 0000000..ab7ad8d
--- /dev/null
+++ b/docs/downloads/devbytes/RequestDuringLayout.zip
Binary files differ
diff --git a/docs/downloads/devbytes/ViewAnimations.zip b/docs/downloads/devbytes/ViewAnimations.zip
new file mode 100644
index 0000000..9a0fc83
--- /dev/null
+++ b/docs/downloads/devbytes/ViewAnimations.zip
Binary files differ
diff --git a/docs/downloads/devbytes/WindowAnimations.zip b/docs/downloads/devbytes/WindowAnimations.zip
new file mode 100644
index 0000000..d34de0d
--- /dev/null
+++ b/docs/downloads/devbytes/WindowAnimations.zip
Binary files differ
diff --git a/docs/downloads/training/BitmapFun.zip b/docs/downloads/training/BitmapFun.zip
index c4ea7aa..882ce03 100644
--- a/docs/downloads/training/BitmapFun.zip
+++ b/docs/downloads/training/BitmapFun.zip
Binary files differ
diff --git a/docs/html/develop/index.jd b/docs/html/develop/index.jd
index b4faa58..c12761b 100644
--- a/docs/html/develop/index.jd
+++ b/docs/html/develop/index.jd
@@ -121,25 +121,25 @@
 			<div class="feed-frame">
                                 <!-- DEVELOPER NEWS -->
           <ul>
-            <li><a href="http://android-developers.blogspot.com/2012/12/localize-your-promotional-graphics-on.html">
-              <div class="feed-image" style="background:url('{@docRoot}images/home/play_logo.png') no-repeat 0 0"></div>
-              <h4>Localized Promo Graphics on Google Play</h4>
-              <p>Google Play now lets you provide different promotional graphics, screenshots, and videos for each language you support...</p>
+            <li><a href="//android-developers.blogspot.com/2013/01/verifying-back-end-calls-from-android.html">
+              <div class="feed-image" style="background:url('//lh4.ggpht.com/7z4NItEg-X21zvFGAarKonk-VaysBYthJ30u1JjaQ0-5fjyHNawnmoNeG--4FCACog=w124') no-repeat 0 0"></div>
+              <h4>Verifying Back-End Calls from Android Apps</h4>
+              <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/in-app-billing-version-3.html">
-              <div class="feed-image" style="background:url('{@docRoot}images/iab-thumb.png') no-repeat 0 0;background-position:center right;"></div>
-              <h4>In-app Billing Version 3 Now Available</h4>
-              <p>A new version of In-app Billing is available that lets you sell digital goods in your app with just a few lines of code...</p>
+            <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>
+              <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>
             <li><a href="//android-developers.blogspot.com/2012/11/designing-for-tablets-were-here-to-help.html">
-              <div class="feed-image" style="background:url('{@docRoot}design/media/multipane_expand.png') no-repeat 0 0; background-position:right top;"></div>
+              <div class="feed-image" style="background:url('//developer.android.com/design/media/multipane_expand.png') no-repeat 0 0; background-position:right top;"></div>
               <h4>Designing for Tablets?</h4>
               <p>Essential resources for everyone in the app development pipeline—from product managers, to designers, to developers, and QA engineers...</p>
               </a></li>
-            <li><a href="//developer.android.com/google/play-services/index.html">
-              <div class="feed-image" style="background:url('//lh4.ggpht.com/7z4NItEg-X21zvFGAarKonk-VaysBYthJ30u1JjaQ0-5fjyHNawnmoNeG--4FCACog=w124') no-repeat 0 0"></div>
-              <h4>Google Play services and OAuth Tools</h4>
-              <p>The rollout of Google Play services to all Android 2.2+ devices worldwide is now complete, and all of those devices now have new tools for working with OAuth 2.0 tokens...</p>
+            <li><a href="//android-developers.blogspot.com/2012/12/in-app-billing-version-3.html">
+              <div class="feed-image" style="background:url('//developer.android.com/images/iab-thumb.png') no-repeat 0 0;background-position:center right;"></div>
+              <h4>In-app Billing Version 3 Now Available</h4>
+              <p>A new version of In-app Billing is available that lets you sell digital goods in your app with just a few lines of code...</p>
               </a></li>
           </ul>
                                 <!-- FEATURED DOCS -->
@@ -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/distribute/googleplay/promote/badge-files.jd b/docs/html/distribute/googleplay/promote/badge-files.jd
index 92001ed..ede1e21 100644
--- a/docs/html/distribute/googleplay/promote/badge-files.jd
+++ b/docs/html/distribute/googleplay/promote/badge-files.jd
@@ -21,9 +21,9 @@
        <a href="{@docRoot}downloads/brand/en_generic_rgb_wo.ai">English (English)</a><br/>
 
        <a href="{@docRoot}downloads/brand/af_generic_rgb_wo.ai">Afrikaans (Afrikaans)</a><br/>
-
+<!--
        <a href="{@docRoot}downloads/brand/ar_generic_rgb_wo.ai">العربية (Arabic)</a><br/>
-
+-->
        <a href="{@docRoot}downloads/brand/be_generic_rgb_wo.ai">Беларуская (Belarusian)</a><br/>
        
        <a href="{@docRoot}downloads/brand/bg_generic_rgb_wo.ai">български (Bulgarian)</a><br/>
@@ -61,9 +61,9 @@
        <a href="{@docRoot}downloads/brand/de_generic_rgb_wo.ai">Deutsch (German)</a><br/>
 
        <a href="{@docRoot}downloads/brand/el_generic_rgb_wo.ai">Ελληνικά (Greek)</a><br/>
-
+<!--
        <a href="{@docRoot}downloads/brand/iw-he_generic_rgb_wo.ai">עברית (Hebrew)</a><br/>
-
+-->
        <a href="{@docRoot}downloads/brand/hu_generic_rgb_wo.ai">Magyar (Hungarian)</a><br/>
 
        <a href="{@docRoot}downloads/brand/id-in_generic_rgb_wo.ai">Bahasa Indonesia (Indonesian)</a><br/>
diff --git a/docs/html/distribute/googleplay/promote/badges.jd b/docs/html/distribute/googleplay/promote/badges.jd
index 0027f59..081c8f7 100644
--- a/docs/html/distribute/googleplay/promote/badges.jd
+++ b/docs/html/distribute/googleplay/promote/badges.jd
@@ -199,8 +199,10 @@
           onchange="changeBadgeLang()">
     <option title="Afrikaans"
             value="af">Afrikaans</option>
+<!--
     <option title="Arabic"
             value="ar">العربية</option>
+-->
     <option title="Belarusian"
             value="be">Беларуская</option>
     <option title="Bulgarian"
@@ -237,8 +239,10 @@
             value="el">Ελληνικά</option>
     <option title="English"
             value="en" selected="true">English</option>
+<!--
     <option title="Hebrew"
             value="iw-he">עברית</option>
+-->
     <option title="Hungarian"
             value="hu">Magyar</option>
     <option title="Indonesian"
diff --git a/docs/html/distribute/googleplay/spotlight/index.jd b/docs/html/distribute/googleplay/spotlight/index.jd
index 6acd914..a248dea 100644
--- a/docs/html/distribute/googleplay/spotlight/index.jd
+++ b/docs/html/distribute/googleplay/spotlight/index.jd
@@ -7,7 +7,34 @@
 
 <p>Android developers, their apps, and their successes with Android and Google Play. </p>
 
-
+<div style="background: #F0F0F0;
+            border-top: 1px solid #DDD;
+            padding: 0px 0 24px 0;
+            overflow: auto;
+            clear:both;
+            margin-bottom:40px;
+            margin-top:30px;"">
+   <div style="padding:0 0 0 29px;">
+        <h4>Developer Story: Smule</h4>
+          <img alt="" class="screenshot thumbnail" style="-webkit-border-radius: 5px;
+            -moz-border-radius: 5px;
+            border-radius: 5px height:78px;
+            width: 78px;
+            float: left;
+            margin: 17px 20px 9px 0;" src=
+            "//lh6.ggpht.com/z5wl9PuHl9JfO54uefjRTUX70SuLY-1DRpPxQ5mg7XEDfnYhBDssh1RrPZjN1tbwzhg=w124">
+          <div style="width:700px;">
+          <p style="margin-top:26px;
+                    margin-bottom:12px;">
+          The creators of <a href="//play.google.com/store/apps/details?id=com.smule.autorap">AutoRap</a>, <a href="//play.google.com/store/apps/details?id=com.smule.magicpiano">Magic Piano</a>, and <a href="//play.google.com/store/apps/details?id=com.smule.songify">Songify</a> talk about their experiences launching on Android, the explosive global growth they’ve seen on Google Play, and some of the techniques they use to market and monetize their products effectively across the world.</p>
+           </div>
+           <iframe style="float:left;
+             margin-right:24px;
+             margin-top:14px;" width="700" height="394" src=
+             "http://www.youtube.com/embed/RRelFvc6Czo?HD=1;rel=0;origin=developer.android.com;" frameborder="0" allowfullscreen>
+           </iframe>
+   </div> 
+</div>
 
 <div style="background: #F0F0F0;
             border-top: 1px solid #DDD;
diff --git a/docs/html/google/gcm/gcm.jd b/docs/html/google/gcm/gcm.jd
index 7fc192b..6e15ef7 100644
--- a/docs/html/google/gcm/gcm.jd
+++ b/docs/html/google/gcm/gcm.jd
@@ -208,9 +208,9 @@
 <p>The registration ID lasts until the Android application explicitly unregisters
 itself, or until Google refreshes the registration ID for your Android application.</p>
 
-<p class="note"><strong>Note:</strong> When users uninstall an application, it is not automatically unregistered on GCM. It is only  unregistered when the GCM server tries to send a message to the device and the device answers that the application is uninstalled or it does not have a broadcast receiver configured to receive <code>com.google.android.c2dm.intent.RECEIVE</code> intents. At that point, you server should mark the device as unregistered (the server will receive a <code><a href="#unreg_device">NotRegistered</a></code> error).
+<p class="note"><strong>Note:</strong> When users uninstall an application, it is not automatically unregistered on GCM. It is only  unregistered when the GCM server tries to send a message to the device and the device answers that the application is uninstalled or it does not have a broadcast receiver configured to receive <code>com.google.android.c2dm.intent.RECEIVE</code> intents. At that point, your server should mark the device as unregistered (the server will receive a <code><a href="#unreg_device">NotRegistered</a></code> error).
   <p>
-Note that it might take a few minutes for the registration ID to be completed removed from the GCM server. So if the 3rd party server sends a message during this time, it will get a valid message ID, even though the message will not be delivered to the device.</p>
+Note that it might take a few minutes for the registration ID to be completely removed from the GCM server. So if the 3rd party server sends a message during this time, it will get a valid message ID, even though the message will not be delivered to the device.</p>
 </p>
 
 <h4 id="push-process">Sending a Message</h4>
@@ -593,7 +593,7 @@
 <ul>
   <li>Able to communicate with your client.</li>
   <li>Able to  fire off HTTPS requests to the GCM server.</li>
-  <li>Able to handle requests and resend then as needed, using <a href="http://en.wikipedia.org/wiki/Exponential_backoff">exponential back-off.</a></li>
+  <li>Able to handle requests and resend them as needed, using <a href="http://en.wikipedia.org/wiki/Exponential_backoff">exponential back-off.</a></li>
   <li>Able to store the API key and client registration IDs. The
 API key is included in the header of POST requests that send
 messages.</li>
diff --git a/docs/html/google/play/billing/billing_integrate.jd b/docs/html/google/play/billing/billing_integrate.jd
index 3f6df927..315befa 100644
--- a/docs/html/google/play/billing/billing_integrate.jd
+++ b/docs/html/google/play/billing/billing_integrate.jd
@@ -149,7 +149,7 @@
 <p>To retrieve this information from Google Play, call the {@code getSkuDetails} method on the In-app Billing Version 3 API, and pass the method the In-app Billing API version (“3”), the package name of your calling app, the purchase type (“inapp”), and the {@link android.os.Bundle} that you created.</p>
 <pre>
 Bundle skuDetails = mService.getSkuDetails(3, 
-   getPackageName(), “inapp”, querySkus);
+   getPackageName(), "inapp", querySkus);
 </pre>
 <p>If the request is successful, the returned {@link android.os.Bundle}has a response code of {@code BILLING_RESPONSE_RESULT_OK} (0).</p>
 <p class="note"><strong>Warning:</strong> Do not call the {@code getSkuDetails} method on the main thread. Calling this method triggers a network request which could block your main thread.  Instead, create a separate thread and call the {@code getSkuDetails} method from inside that thread.</p>
@@ -169,8 +169,8 @@
       JSONObject object = new JSONObject(thisResponse);
       String sku = object.getString("productId");
       String price = object.getString("price");
-      if (sku.equals(“premiumUpgrade”)) mPremiumUpgradePrice = price;
-      else if (sku.equals(“gas”)) mGasPrice = price;
+      if (sku.equals("premiumUpgrade")) mPremiumUpgradePrice = price;
+      else if (sku.equals("gas")) mGasPrice = price;
    }
 }
 </pre>
@@ -186,7 +186,7 @@
 If the request is successful, the returned {@link android.os.Bundle} has a response code of {@code BILLING_RESPONSE_RESULT_OK} (0) and a {@link android.app.PendingIntent} that you can use to start the purchase flow. To see all the possible response codes from Google Play, see <a href="{@docRoot}google/play/billing/billing_reference.html#billing-codes">In-app Billing Reference</a>. Next, extract a {@link android.app.PendingIntent} from the response {@link android.os.Bundle} with key {@code BUY_INTENT}.
 </p>
 <pre>
-PendingIntent pendingIntent = buyIntentBundle.getParcelable(“BUY_INTENT”);
+PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT");
 </pre>
 <p>
 To complete the purchase transaction, call the {@link android.app.Activity#startIntentSenderForResult startIntentSenderForResult} method and use the {@link android.app.PendingIntent} that you created. In this example, you are using an arbitrary value of 1001 for the request code.</p>
@@ -206,7 +206,7 @@
    "purchaseTime":1345678900000,
    "purchaseState":0,
    "developerPayload":"bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ",
-   “purchaseToken”:“rojeslcdyyiapnqcynkjyyjh”
+   "purchaseToken":"rojeslcdyyiapnqcynkjyyjh"
  }'
 </pre>
 </p>
@@ -217,8 +217,8 @@
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {	
    if (requestCode == 1001) {    	
       int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
-      String purchaseData = data.getStringExtra(“INAPP_PURCHASE_DATA”);
-      String dataSignature = data.getStringExtra(“INAPP_DATA_SIGNATURE”);
+      String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
+      String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
         
       if (resultCode == RESULT_OK) {
          try {
@@ -240,7 +240,7 @@
 <h3 id="QueryPurchases">Querying for Purchased Items</h3>
 <p>To retrieve information about purchases made by a user from your app, call the {@code getPurchases} method on the In-app Billing Version 3 service. Pass in to the method the In-app Billing API version (“3”), the package name of your calling app, and the purchase type (“inapp”).</p>
 <pre>
-Bundle ownedItems = mService.getPurchases(3, getPackageName(), “inapp”, null);
+Bundle ownedItems = mService.getPurchases(3, getPackageName(), "inapp", null);
 </pre>
 <p>The Google Play service returns only the purchases made by the user account that is currently logged in to the device. If the request is successful, the returned {@link android.os.Bundle} has a response code of 0. The response {@link android.os.Bundle} also contains a list of the product IDs, a list of the order details for each purchase, and the signatures for each purchase.</p>
 <p>To improve performance, the In-app Billing service returns only up to 700 products that are owned by the user when {@code getPurchase} is first called. If the user owns a large number of products, Google Play includes a String token mapped to the key {@code INAPP_CONTINUATION_TOKEN} in the response {@link android.os.Bundle} to indicate that more products can be retrieved. Your application can then make a subsequent {@code getPurchases} call, and pass in this token as an argument. Google Play continues to return a continuation token in the response {@link android.os.Bundle} until all products that are owned by the user has been sent to your app.</p>
diff --git a/docs/html/google/play/expansion-files.jd b/docs/html/google/play/expansion-files.jd
index 271c192..d2ab509 100644
--- a/docs/html/google/play/expansion-files.jd
+++ b/docs/html/google/play/expansion-files.jd
@@ -684,7 +684,7 @@
     <p>The Downloader Library includes some APIs in the {@code Helper} class to
 help with this process:</p>
   <ul>
-    <li>{@code getExtendedAPKFileName(Context, c, boolean mainFile, int
+    <li>{@code getExpansionAPKFileName(Context, c, boolean mainFile, int
 versionCode)}</li>
     <li>{@code doesFileExist(Context c, String fileName, long fileSize)}</li>
   </ul>
diff --git a/docs/html/guide/components/activities.jd b/docs/html/guide/components/activities.jd
index 36f651f..2897804 100644
--- a/docs/html/guide/components/activities.jd
+++ b/docs/html/guide/components/activities.jd
@@ -691,7 +691,7 @@
 android.app.Activity#onSaveInstanceState onSaveInstanceState()} helps save the state of the UI, if
 you override the method in order to save additional state information, you should always call the
 superclass implementation of {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()}
-before doing any work. Likewise, you should also call the supercall implementation of {@link
+before doing any work. Likewise, you should also call the superclass implementation of {@link
 android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} if you override it, so the
 default implementation can restore view states.</p>
 
@@ -773,4 +773,4 @@
 enables user multitasking, continue with the <b><a
 href="{@docRoot}guide/components/tasks-and-back-stack.html">Tasks and Back
 Stack</a></b> document.</p>
--->
\ No newline at end of file
+-->
diff --git a/docs/html/guide/components/bound-services.jd b/docs/html/guide/components/bound-services.jd
index 8d2bba5..653c7a0 100644
--- a/docs/html/guide/components/bound-services.jd
+++ b/docs/html/guide/components/bound-services.jd
@@ -578,7 +578,7 @@
 </pre>
 
 <p>With this {@link android.content.ServiceConnection}, the client can bind to a service by passing
-this it to {@link android.content.Context#bindService bindService()}. For example:</p>
+it to {@link android.content.Context#bindService bindService()}. For example:</p>
 
 <pre>
 Intent intent = new Intent(this, LocalService.class);
@@ -667,7 +667,7 @@
 and also allows binding.</p>
 
 
-<p>For more information about the lifecycle of an started service, see the <a
+<p>For more information about the lifecycle of a started service, see the <a
 href="{@docRoot}guide/components/services.html#Lifecycle">Services</a> document.</p>
 
 
diff --git a/docs/html/guide/topics/location/strategies.jd b/docs/html/guide/topics/location/strategies.jd
index f790953..6cc8f1a 100644
--- a/docs/html/guide/topics/location/strategies.jd
+++ b/docs/html/guide/topics/location/strategies.jd
@@ -202,9 +202,9 @@
 android.location.LocationManager#requestLocationUpdates requestLocationUpdates()}:</p>
 
 <pre>
-LocationProvider locationProvider = LocationManager.NETWORK_PROVIDER;
+String locationProvider = LocationManager.NETWORK_PROVIDER;
 // Or, use GPS location data:
-// LocationProvider locationProvider = LocationManager.GPS_PROVIDER;
+// String locationProvider = LocationManager.GPS_PROVIDER;
 
 locationManager.requestLocationUpdates(locationProvider, 0, 0, locationListener);
 </pre>
@@ -217,7 +217,7 @@
 should utilize a cached location by calling {@link
 android.location.LocationManager#getLastKnownLocation}:</p>
 <pre>
-LocationProvider locationProvider = LocationManager.NETWORK_PROVIDER;
+String locationProvider = LocationManager.NETWORK_PROVIDER;
 // Or use LocationManager.GPS_PROVIDER
 
 Location lastKnownLocation = locationManager.getLastKnownLocation(locationProvider);
diff --git a/docs/html/guide/topics/renderscript/index.jd b/docs/html/guide/topics/renderscript/index.jd
index 4c22e72..b5c53ff 100644
--- a/docs/html/guide/topics/renderscript/index.jd
+++ b/docs/html/guide/topics/renderscript/index.jd
@@ -9,6 +9,15 @@
 
   <div>
     <h3>Blog Articles</h3>
+
+      <a
+href="http://android-developers.blogspot.com/2013/01/evolution-of-renderscript-performance.html">
+        <h4>Evolution of Renderscript Performance</h4>
+      <p>It’s been a year since the last blog post on Renderscript, and with the release
+      of Android 4.2, it’s a good time to talk about the performance work that we’ve done
+      since then. One of the major goals of this past year was to improve the performance
+      of common image-processing operations with Renderscript.</p> </a>
+
       <a
 href="http://android-developers.blogspot.com/2012/01/levels-in-renderscript.html">
         <h4>Levels in Renderscript</h4>
diff --git a/docs/html/guide/topics/resources/layout-resource.jd b/docs/html/guide/topics/resources/layout-resource.jd
index cd88ae9..380ab15 100644
--- a/docs/html/guide/topics/resources/layout-resource.jd
+++ b/docs/html/guide/topics/resources/layout-resource.jd
@@ -264,7 +264,7 @@
 <pre>
 public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
-    setContentView.(R.layout.main_activity);
+    setContentView(R.layout.main_activity);
 }
 </pre>
 </dd> <!-- end example -->
@@ -279,4 +279,4 @@
 </ul>
 </dd>
 
-</dl>
\ No newline at end of file
+</dl>
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 da410a4..5a96ba1 100644
--- a/docs/html/guide/topics/resources/string-resource.jd
+++ b/docs/html/guide/topics/resources/string-resource.jd
@@ -177,7 +177,7 @@
 
     </dd>
   <dt id="string-array-item-element"><code>&lt;item&gt;</code></dt>
-    <dd>A string, which can include styling tags. The value can be a referenced to another
+    <dd>A string, which can include styling tags. The value can be a reference to another
 string resource. Must be a child of a {@code &lt;string-array&gt;} element. Beware that you
 must escape apostrophes and
 quotation marks. See <a href="#FormattingAndStyling">Formatting and Styling</a>, below, for
@@ -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/guide/topics/ui/declaring-layout.jd b/docs/html/guide/topics/ui/declaring-layout.jd
index e229f23..28e1418 100644
--- a/docs/html/guide/topics/ui/declaring-layout.jd
+++ b/docs/html/guide/topics/ui/declaring-layout.jd
@@ -395,7 +395,7 @@
 subclass of the {@link android.widget.AdapterView} class uses an {@link android.widget.Adapter} to
 bind data to its layout. The {@link android.widget.Adapter} behaves as a middle-man between the data
 source and the {@link android.widget.AdapterView} layout&mdash;the {@link android.widget.Adapter}
-retreives the data (from a source such as an array or a database query) and converts each entry
+retrieves the data (from a source such as an array or a database query) and converts each entry
 into a view that can be added into the {@link android.widget.AdapterView} layout.</p>
 
 <p>Common layouts backed by an adapter include:</p>
diff --git a/docs/html/index.jd b/docs/html/index.jd
index e91b68c..cf06324 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -13,6 +13,50 @@
         <div class="frame">
             <ul>
                 <li class="item carousel-home">
+                    <div class="content-left col-11" style="padding-top:65px;">
+                      <script src="//ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
+                      <div style="box-shadow: 3px 10px 18px 1px #999;width:600px;height:338px">
+                        <div id="ytapiplayer">
+                          You need Flash player 8+ and JavaScript enabled to view this video.
+                        </div>
+                        <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",
+                              "ytapiplayer", "600", "338", "8", null, null, params, atts);
+
+                            // Callback used to pause/resume carousel based on video state
+                            function onytplayerStateChange(newState) {              
+                               var isPaused = $("#pauseButton").hasClass("paused");
+                               if ((newState == 1) || (newState == 3)) {
+                               // if playing or buffering, pause the carousel
+                                 if (!isPaused) {
+                                    $("#pauseButton").click();
+                                 }
+                               } else {
+                               // otherwise, make sure carousel is running
+                                 if (isPaused) {
+                                    $("#pauseButton").click();
+                                 }
+                               }
+                            }
+
+                            // Callback received when YouTube player loads to setup callback (above)
+                            function onYouTubePlayerReady(playerId) {
+                              var ytplayer = document.getElementById("ytapiplayer");
+                              ytplayer.addEventListener("onStateChange", "onytplayerStateChange");
+                            }
+
+                        </script>
+                      </div>
+                    </div>
+                    <div class="content-right col-4">
+                    <h1 style="white-space:nowrap;line-height:1em;">Developer Story: Smule</h1>
+                    <p>The creators of AutoRap, Magic Piano, and Songify talk about launching on
+                       Android and the explosive global growth they’ve seen on Google Play.</p>
+                    </div>
+                </li>
+                <li class="item carousel-home">
                     <div class="content-left col-9">
                         <a href="{@docRoot}about/versions/jelly-bean.html"><img src="{@docRoot}images/home/android-jellybean.png" ></a>
                     </div>
@@ -63,6 +107,8 @@
     </div>
     <!-- /End slideshow -->
     
+    <a href="" id="pauseButton" style="display:none">pause</a>
+    
     
 </div>
 <div class="wrap" style="padding-bottom:20px">
diff --git a/docs/html/tools/extras/oem-usb.jd b/docs/html/tools/extras/oem-usb.jd
index 005ba29..9e3c709 100644
--- a/docs/html/tools/extras/oem-usb.jd
+++ b/docs/html/tools/extras/oem-usb.jd
@@ -292,7 +292,7 @@
     </td>
   </tr>
   <tr>
-    <td>Lenevo</td>
+    <td>Lenovo</td>
     <td><a href="http://developer.lenovomm.com/developer/download.jsp"
         >http://developer.lenovomm.com/developer/download.jsp</a>
     </td>
diff --git a/docs/html/tools/testing/testing_ui.jd b/docs/html/tools/testing/testing_ui.jd
index 5289ffa..701415e 100644
--- a/docs/html/tools/testing/testing_ui.jd
+++ b/docs/html/tools/testing/testing_ui.jd
@@ -141,11 +141,11 @@
 </ul>
 </p>
 
-<p>For more information about implementing and testing accessibility, see <a href="{@docRoot}/guide/topics/ui/accessibility/apps.html">Making Applications Accessible</a>.</p>
+<p>For more information about implementing and testing accessibility, see <a href="{@docRoot}guide/topics/ui/accessibility/apps.html">Making Applications Accessible</a>.</p>
 
 <p class="note"><strong>Note: </strong>To identify the non-accessible components in the UI, click on the <strong>Toggle NAF Nodes</strong> option in the {@code uiautomatorviewer} tool.</p>
 
-<p>Generally, Android application developers get accessibility support for free, courtesy of the {@link android.view.View} and {@link android.view.ViewGroup} classes. However, some applications use custom view components to provide a richer user experience. Such custom components won't get the accessibility support that is provided by the standard Android UI components. If this applies to your application, ensure that the application developer exposes the custom drawn UI components to Android accessibility services, by implementing the {@link android.view.accessibility.AccessibilityNodeProvider} class. For more information about making custom view components accessible, see <a href="{@docRoot}/guide/topics/ui/accessibility/apps.html#custom-views">Making Applications Accessible</a>.</p>
+<p>Generally, Android application developers get accessibility support for free, courtesy of the {@link android.view.View} and {@link android.view.ViewGroup} classes. However, some applications use custom view components to provide a richer user experience. Such custom components won't get the accessibility support that is provided by the standard Android UI components. If this applies to your application, ensure that the application developer exposes the custom drawn UI components to Android accessibility services, by implementing the {@link android.view.accessibility.AccessibilityNodeProvider} class. For more information about making custom view components accessible, see <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#custom-views">Making Applications Accessible</a>.</p>
 
 <h3 id="configure">Configure your development environment</h3>
 <p>If you're developing in Eclipse, the Android SDK provides additional tools that help you write test cases using {@code uiautomator} and buiild your JAR file. In order to set up Eclipse to assist you, you need to create a project that includes the {@code uiautomator} client library, along with the Android SDK library. To configure Eclipse:</p>
diff --git a/docs/html/training/basics/location/geocoding.jd b/docs/html/training/basics/location/geocoding.jd
index 6364976..3192d14 100644
--- a/docs/html/training/basics/location/geocoding.jd
+++ b/docs/html/training/basics/location/geocoding.jd
@@ -82,7 +82,7 @@
             // Update UI field with the exception.
             Message.obtain(mHandler, UPDATE_ADDRESS, e.toString()).sendToTarget();
         }
-        if (addresses != null &amps;&amps; addresses.size() &gt; 0) {
+        if (addresses != null &amp;&amp; addresses.size() &gt; 0) {
             Address address = addresses.get(0);
             // Format the first line of address (if available), city, and country name.
             String addressText = String.format("&#037;s, &#037;s, &#037;s",
@@ -95,4 +95,4 @@
         return null;
     }
 }
-</pre>
\ No newline at end of file
+</pre>
diff --git a/docs/html/training/displaying-bitmaps/cache-bitmap.jd b/docs/html/training/displaying-bitmaps/cache-bitmap.jd
index 2a333cc..417ec5b 100644
--- a/docs/html/training/displaying-bitmaps/cache-bitmap.jd
+++ b/docs/html/training/displaying-bitmaps/cache-bitmap.jd
@@ -101,19 +101,20 @@
 &#64;Override
 protected void onCreate(Bundle savedInstanceState) {
     ...
-    // Get memory class of this device, exceeding this amount will throw an
-    // OutOfMemory exception.
-    final int memClass = ((ActivityManager) context.getSystemService(
-            Context.ACTIVITY_SERVICE)).getMemoryClass();
+    // Get max available VM memory, exceeding this amount will throw an
+    // OutOfMemory exception. Stored in kilobytes as LruCache takes an
+    // int in its constructor.
+    final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
 
     // Use 1/8th of the available memory for this memory cache.
-    final int cacheSize = 1024 * 1024 * memClass / 8;
+    final int cacheSize = maxMemory / 8;
 
     mMemoryCache = new LruCache&lt;String, Bitmap&gt;(cacheSize) {
         &#64;Override
         protected int sizeOf(String key, Bitmap bitmap) {
-            // The cache size will be measured in bytes rather than number of items.
-            return bitmap.getByteCount();
+            // The cache size will be measured in kilobytes rather than
+            // number of items.
+            return bitmap.getByteCount() / 1024;
         }
     };
     ...
diff --git a/docs/html/training/in-app-billing/purchase-iab-products.jd b/docs/html/training/in-app-billing/purchase-iab-products.jd
index d5f6634..7fa77d3 100644
--- a/docs/html/training/in-app-billing/purchase-iab-products.jd
+++ b/docs/html/training/in-app-billing/purchase-iab-products.jd
@@ -65,7 +65,7 @@
       else if (purchase.getSku().equals(SKU_GAS)) {
          // consume the gas and update the UI
       }
-      else (purchase.getSku().equals(SKU_PREMIUM)) {
+      else if (purchase.getSku().equals(SKU_PREMIUM)) {
          // give user access to premium content and update the UI
       }
    }
diff --git a/docs/html/training/multiple-threads/create-threadpool.jd b/docs/html/training/multiple-threads/create-threadpool.jd
index 4a4ddb1..e22afd3 100644
--- a/docs/html/training/multiple-threads/create-threadpool.jd
+++ b/docs/html/training/multiple-threads/create-threadpool.jd
@@ -46,7 +46,7 @@
     <code>synchronized</code> block. This approach will prevent one thread from reading the variable
     while another is writing to it. Typically, this situation arises with static variables, but it
     also occurs in any object that is only instantiated once. To learn more about this, read the
-    <a href="{@docRoot}http://developer.android.com/guide/components/processes-and-threads.html">
+    <a href="{@docRoot}guide/components/processes-and-threads.html">
     Processes and Threads</a> API guide.
     
 </p>
diff --git a/docs/html/training/notepad/notepad-ex2.jd b/docs/html/training/notepad/notepad-ex2.jd
index 1334d7a..895a071 100644
--- a/docs/html/training/notepad/notepad-ex2.jd
+++ b/docs/html/training/notepad/notepad-ex2.jd
@@ -100,7 +100,7 @@
 </ol>
 
 <h2>Step 3</h2>
-  <p>Now that the we've registered our ListView for a context menu and defined our context menu item, we need
+  <p>Now that we've registered our ListView for a context menu and defined our context menu item, we need
   to handle the callback when it is selected. For this, we need to identify the list ID of the
   selected item, then delete it. So fill in the
   <code>onContextItemSelected()</code> method like this:</p>
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 6795ac3..dfc4e25 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -35,6 +35,9 @@
 // Turn on to enable layers debugging when rendered as regions
 #define DEBUG_LAYERS_AS_REGIONS 0
 
+// Turn on to enable debugging when the clip is not a rect
+#define DEBUG_CLIP_REGIONS 0
+
 // Turn on to display debug info about vertex/fragment shaders
 #define DEBUG_PROGRAMS 0
 
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 06574cd..f0c9ce4 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -44,6 +44,8 @@
     "SetMatrix",
     "ConcatMatrix",
     "ClipRect",
+    "ClipPath",
+    "ClipRegion",
     "DrawDisplayList",
     "DrawLayer",
     "DrawBitmap",
@@ -166,6 +168,10 @@
         delete mPaints.itemAt(i);
     }
 
+    for (size_t i = 0; i < mRegions.size(); i++) {
+        delete mRegions.itemAt(i);
+    }
+
     for (size_t i = 0; i < mPaths.size(); i++) {
         SkPath* path = mPaths.itemAt(i);
         caches.pathCache.remove(path);
@@ -182,6 +188,7 @@
     mShaders.clear();
     mSourcePaths.clear();
     mPaints.clear();
+    mRegions.clear();
     mPaths.clear();
     mMatrices.clear();
     mLayers.clear();
@@ -259,20 +266,10 @@
 
     caches.resourceCache.unlock();
 
-    const Vector<SkPaint*>& paints = recorder.getPaints();
-    for (size_t i = 0; i < paints.size(); i++) {
-        mPaints.add(paints.itemAt(i));
-    }
-
-    const Vector<SkPath*>& paths = recorder.getPaths();
-    for (size_t i = 0; i < paths.size(); i++) {
-        mPaths.add(paths.itemAt(i));
-    }
-
-    const Vector<SkMatrix*>& matrices = recorder.getMatrices();
-    for (size_t i = 0; i < matrices.size(); i++) {
-        mMatrices.add(matrices.itemAt(i));
-    }
+    mPaints.appendVector(recorder.getPaints());
+    mRegions.appendVector(recorder.getRegions());
+    mPaths.appendVector(recorder.getPaths());
+    mMatrices.appendVector(recorder.getMatrices());
 }
 
 void DisplayList::init() {
@@ -429,6 +426,18 @@
                         f1, f2, f3, f4, regionOp);
             }
             break;
+            case ClipPath: {
+                SkPath* path = getPath();
+                int regionOp = getInt();
+                ALOGD("%s%s %d", (char*) indent, OP_NAMES[op], regionOp);
+            }
+            break;
+            case ClipRegion: {
+                SkRegion* region = getRegion();
+                int regionOp = getInt();
+                ALOGD("%s%s %d", (char*) indent, OP_NAMES[op], regionOp);
+            }
+            break;
             case DrawDisplayList: {
                 DisplayList* displayList = getDisplayList();
                 int32_t flags = getInt();
@@ -1031,6 +1040,20 @@
                 renderer.clipRect(f1, f2, f3, f4, (SkRegion::Op) regionOp);
             }
             break;
+            case ClipPath: {
+                SkPath* path = getPath();
+                int32_t regionOp = getInt();
+                DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, OP_NAMES[op], regionOp);
+                renderer.clipPath(path, (SkRegion::Op) regionOp);
+            }
+            break;
+            case ClipRegion: {
+                SkRegion* region = getRegion();
+                int32_t regionOp = getInt();
+                DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, OP_NAMES[op], regionOp);
+                renderer.clipRegion(region, (SkRegion::Op) regionOp);
+            }
+            break;
             case DrawDisplayList: {
                 DisplayList* displayList = getDisplayList();
                 int32_t flags = getInt();
@@ -1415,6 +1438,9 @@
     mPaints.clear();
     mPaintMap.clear();
 
+    mRegions.clear();
+    mRegionMap.clear();
+
     mPaths.clear();
     mPathMap.clear();
 
@@ -1571,6 +1597,20 @@
     return OpenGLRenderer::clipRect(left, top, right, bottom, op);
 }
 
+bool DisplayListRenderer::clipPath(SkPath* path, SkRegion::Op op) {
+    addOp(DisplayList::ClipPath);
+    addPath(path);
+    addInt(op);
+    return OpenGLRenderer::clipPath(path, op);
+}
+
+bool DisplayListRenderer::clipRegion(SkRegion* region, SkRegion::Op op) {
+    addOp(DisplayList::ClipRegion);
+    addRegion(region);
+    addInt(op);
+    return OpenGLRenderer::clipRegion(region, op);
+}
+
 status_t DisplayListRenderer::drawDisplayList(DisplayList* displayList,
         Rect& dirty, int32_t flags, uint32_t level) {
     // dirty is an out parameter and should not be recorded,
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index fb01753..f55f1f2 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -85,6 +85,8 @@
         SetMatrix,
         ConcatMatrix,
         ClipRect,
+        ClipPath,
+        ClipRegion,
         // Drawing operations
         DrawDisplayList,
         DrawLayer,
@@ -457,6 +459,10 @@
         return (SkPath*) getInt();
     }
 
+    SkRegion* getRegion() {
+        return (SkRegion*) getInt();
+    }
+
     SkPaint* getPaint(OpenGLRenderer& renderer) {
         return renderer.filterPaint((SkPaint*) getInt());
     }
@@ -496,6 +502,7 @@
     Vector<SkPaint*> mPaints;
     Vector<SkPath*> mPaths;
     SortedVector<SkPath*> mSourcePaths;
+    Vector<SkRegion*> mRegions;
     Vector<SkMatrix*> mMatrices;
     Vector<SkiaShader*> mShaders;
     Vector<Layer*> mLayers;
@@ -577,6 +584,8 @@
     virtual void concatMatrix(SkMatrix* matrix);
 
     virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
+    virtual bool clipPath(SkPath* path, SkRegion::Op op);
+    virtual bool clipRegion(SkRegion* region, SkRegion::Op op);
 
     virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t flags,
             uint32_t level = 0);
@@ -657,6 +666,10 @@
         return mSourcePaths;
     }
 
+    const Vector<SkRegion*>& getRegions() const {
+        return mRegions;
+    }
+
     const Vector<Layer*>& getLayers() const {
         return mLayers;
     }
@@ -802,6 +815,26 @@
         return paintCopy;
     }
 
+    inline SkRegion* addRegion(SkRegion* region) {
+        if (!region) {
+            addInt((int) NULL);
+            return region;
+        }
+
+        SkRegion* regionCopy = mRegionMap.valueFor(region);
+        // TODO: Add generation ID to SkRegion
+        if (regionCopy == NULL) {
+            regionCopy = new SkRegion(*region);
+            // replaceValueFor() performs an add if the entry doesn't exist
+            mRegionMap.replaceValueFor(region, regionCopy);
+            mRegions.add(regionCopy);
+        }
+
+        addInt((int) regionCopy);
+
+        return regionCopy;
+    }
+
     inline void addDisplayList(DisplayList* displayList) {
         // TODO: To be safe, the display list should be ref-counted in the
         //       resources cache, but we rely on the caller (UI toolkit) to
@@ -876,6 +909,9 @@
 
     SortedVector<SkPath*> mSourcePaths;
 
+    Vector<SkRegion*> mRegions;
+    DefaultKeyedVector<SkRegion*, SkRegion*> mRegionMap;
+
     Vector<SkiaShader*> mShaders;
     DefaultKeyedVector<SkiaShader*, SkiaShader*> mShaderMap;
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 8cda729..bb1edbb 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1288,10 +1288,38 @@
     bool clipped = mSnapshot->clip(left, top, right, bottom, op);
     if (clipped) {
         dirtyClip();
+#if DEBUG_CLIP_REGIONS
+        if (!isDeferred() && mSnapshot->clipRegion && !mSnapshot->clipRegion->isRect()) {
+            int count = 0;
+            Vector<float> rects;
+            SkRegion::Iterator it(*mSnapshot->clipRegion);
+            while (!it.done()) {
+                const SkIRect& r = it.rect();
+                rects.push(r.fLeft);
+                rects.push(r.fTop);
+                rects.push(r.fRight);
+                rects.push(r.fBottom);
+                count++;
+                it.next();
+            }
+
+            drawColorRects(rects.array(), count, 0x7f00ff00, SkXfermode::kSrcOver_Mode, true);
+        }
+#endif
     }
     return !mSnapshot->clipRect->isEmpty();
 }
 
+bool OpenGLRenderer::clipPath(SkPath* path, SkRegion::Op op) {
+    const SkRect& bounds = path->getBounds();
+    return clipRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, op);
+}
+
+bool OpenGLRenderer::clipRegion(SkRegion* region, SkRegion::Op op) {
+    const SkIRect& bounds = region->getBounds();
+    return clipRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, op);
+}
+
 Rect* OpenGLRenderer::getClipRect() {
     return mSnapshot->clipRect;
 }
@@ -3046,6 +3074,19 @@
         return DrawGlInfo::kStatusDone;
     }
 
+    int color = paint->getColor();
+    // If a shader is set, preserve only the alpha
+    if (mShader) {
+        color |= 0x00ffffff;
+    }
+    SkXfermode::Mode mode = getXfermode(paint->getXfermode());
+
+    return drawColorRects(rects, count, color, mode);
+}
+
+status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color,
+        SkXfermode::Mode mode, bool ignoreTransform) {
+
     float left = FLT_MAX;
     float top = FLT_MAX;
     float right = FLT_MIN;
@@ -3081,13 +3122,6 @@
 
     if (count == 0) return DrawGlInfo::kStatusDone;
 
-    int color = paint->getColor();
-    // If a shader is set, preserve only the alpha
-    if (mShader) {
-        color |= 0x00ffffff;
-    }
-    SkXfermode::Mode mode = getXfermode(paint->getXfermode());
-
     setupDraw();
     setupDrawNoTexture();
     setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
@@ -3096,7 +3130,7 @@
     setupDrawBlending(mode);
     setupDrawProgram();
     setupDrawDirtyRegionsDisabled();
-    setupDrawModelView(0.0f, 0.0f, 1.0f, 1.0f);
+    setupDrawModelView(0.0f, 0.0f, 1.0f, 1.0f, ignoreTransform, true);
     setupDrawColorUniforms();
     setupDrawShaderUniforms();
     setupDrawColorFilterUniforms();
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 5520edb..f07325f 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -164,6 +164,8 @@
     ANDROID_API bool quickReject(float left, float top, float right, float bottom);
     bool quickRejectNoScissor(float left, float top, float right, float bottom);
     virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
+    virtual bool clipPath(SkPath* path, SkRegion::Op op);
+    virtual bool clipRegion(SkRegion* region, SkRegion::Op op);
     virtual Rect* getClipRect();
 
     virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t flags,
@@ -498,7 +500,8 @@
 
     /**
      * Draws a colored rectangle with the specified color. The specified coordinates
-     * are transformed by the current snapshot's transform matrix.
+     * are transformed by the current snapshot's transform matrix unless specified
+     * otherwise.
      *
      * @param left The left coordinate of the rectangle
      * @param top The top coordinate of the rectangle
@@ -512,6 +515,20 @@
             int color, SkXfermode::Mode mode, bool ignoreTransform = false);
 
     /**
+     * Draws a series of colored rectangles with the specified color. The specified
+     * coordinates are transformed by the current snapshot's transform matrix unless
+     * specified otherwise.
+     *
+     * @param rects A list of rectangles, 4 floats (left, top, right, bottom)
+     *              per rectangle
+     * @param color The rectangles' ARGB color, defined as a packed 32 bits word
+     * @param mode The Skia xfermode to use
+     * @param ignoreTransform True if the current transform should be ignored
+     */
+    status_t drawColorRects(const float* rects, int count, int color,
+            SkXfermode::Mode mode, bool ignoreTransform = false);
+
+    /**
      * Draws the shape represented by the specified path texture.
      * This method invokes drawPathTexture() but takes into account
      * the extra left/top offset and the texture offset to correctly
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index fbc8455..d947299 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -130,6 +130,7 @@
     switch (op) {
         case SkRegion::kIntersect_Op: {
             if (CC_UNLIKELY(clipRegion)) {
+                ensureClipRegion();
                 clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kIntersect_Op);
             } else {
                 clipped = clipRect->intersect(r);
@@ -142,6 +143,7 @@
         }
         case SkRegion::kUnion_Op: {
             if (CC_UNLIKELY(clipRegion)) {
+                ensureClipRegion();
                 clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kUnion_Op);
             } else {
                 clipped = clipRect->unionWith(r);
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/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index cccce9d..fc0ff55 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -71,7 +71,7 @@
     // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
     // is properly propagated through your change.  Not doing so will result in a loss of user
     // settings.
-    private static final int DATABASE_VERSION = 95;
+    private static final int DATABASE_VERSION = 96;
 
     private Context mContext;
     private int mUserHandle;
@@ -1488,7 +1488,6 @@
             // Redo this step, since somehow it didn't work the first time for some users
             if (mUserHandle == UserHandle.USER_OWNER) {
                 db.beginTransaction();
-                SQLiteStatement stmt = null;
                 try {
                     // Migrate now-global settings
                     String[] settingsToMove = hashsetToStringArray(SettingsProvider.sSystemGlobalKeys);
@@ -1499,7 +1498,6 @@
                     db.setTransactionSuccessful();
                 } finally {
                     db.endTransaction();
-                    if (stmt != null) stmt.close();
                 }
             }
             upgradeVersion = 94;
@@ -1524,6 +1522,20 @@
             upgradeVersion = 95;
         }
 
+        if (upgradeVersion == 95) {
+            if (mUserHandle == UserHandle.USER_OWNER) {
+                db.beginTransaction();
+                try {
+                    String[] settingsToMove = { Settings.Global.BUGREPORT_IN_POWER_MENU };
+                    moveSettingsToNewTable(db, TABLE_SECURE, TABLE_GLOBAL, settingsToMove, true);
+                    db.setTransactionSuccessful();
+                } finally {
+                    db.endTransaction();
+                }
+            }
+            upgradeVersion = 96;
+        }
+
         // *** Remember to update DATABASE_VERSION above!
 
         if (upgradeVersion != currentVersion) {
diff --git a/packages/SystemUI/res/values/donottranslate.xml b/packages/SystemUI/res/values/donottranslate.xml
index 41ea6f3..351a1fd 100644
--- a/packages/SystemUI/res/values/donottranslate.xml
+++ b/packages/SystemUI/res/values/donottranslate.xml
@@ -18,6 +18,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Date format for display: should match the lockscreen in /policy.  -->
-    <string name="abbrev_wday_month_day_no_year">@*android:string/abbrev_wday_month_day_no_year</string>
+    <string name="system_ui_date_pattern">@*android:string/system_ui_date_pattern</string>
 
 </resources>
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/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 3227a34..01596dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -838,9 +838,14 @@
     }
 
     public void refreshAllStatusBarIcons() {
-        final int count = mStatusIcons.getChildCount();
+        refreshAllIconsForLayout(mStatusIcons);
+        refreshAllIconsForLayout(mNotificationIcons);
+    }
+
+    private void refreshAllIconsForLayout(LinearLayout ll) {
+        final int count = ll.getChildCount();
         for (int n = 0; n < count; n++) {
-            View child = mStatusIcons.getChildAt(n);
+            View child = ll.getChildAt(n);
             if (child instanceof StatusBarIconView) {
                 ((StatusBarIconView) child).updateDrawable();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
index 00991c1..1037137 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
@@ -139,7 +139,7 @@
         public void startObserving() {
             final ContentResolver cr = mContext.getContentResolver();
             cr.registerContentObserver(
-                    Settings.Secure.getUriFor(Settings.Secure.BUGREPORT_IN_POWER_MENU), false, this);
+                    Settings.Global.getUriFor(Settings.Global.BUGREPORT_IN_POWER_MENU), false, this);
         }
     }
 
@@ -544,7 +544,7 @@
         final ContentResolver cr = mContext.getContentResolver();
         boolean enabled = false;
         try {
-            enabled = (Settings.Secure.getInt(cr, Settings.Secure.BUGREPORT_IN_POWER_MENU) != 0);
+            enabled = (Settings.Global.getInt(cr, Settings.Global.BUGREPORT_IN_POWER_MENU) != 0);
         } catch (SettingNotFoundException e) {
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index e41de47..bff6cda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -43,6 +43,8 @@
 import java.util.Locale;
 import java.util.TimeZone;
 
+import libcore.icu.LocaleData;
+
 import com.android.internal.R;
 
 /**
@@ -137,20 +139,14 @@
 
     private final CharSequence getSmallTime() {
         Context context = getContext();
-        boolean b24 = DateFormat.is24HourFormat(context);
-        int res;
-
-        if (b24) {
-            res = R.string.twenty_four_hour_time_format;
-        } else {
-            res = R.string.twelve_hour_time_format;
-        }
+        boolean is24 = DateFormat.is24HourFormat(context);
+        LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
 
         final char MAGIC1 = '\uEF00';
         final char MAGIC2 = '\uEF01';
 
         SimpleDateFormat sdf;
-        String format = context.getString(res);
+        String format = is24 ? d.timeFormat24 : d.timeFormat12;
         if (!format.equals(mClockFormatString)) {
             /*
              * Search for an unquoted "a" in the format string, so we can
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
index 1d6b3d1..cbe296d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
@@ -20,7 +20,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.text.format.DateFormat;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewParent;
@@ -28,7 +27,11 @@
 
 import com.android.systemui.R;
 
+import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.Locale;
+
+import libcore.icu.ICU;
 
 public class DateView extends TextView {
     private static final String TAG = "DateView";
@@ -87,8 +90,11 @@
     }
 
     protected void updateClock() {
-        final String dateFormat = getContext().getString(R.string.abbrev_wday_month_day_no_year);
-        setText(DateFormat.format(dateFormat, new Date()));
+        final String dateFormat = getContext().getString(R.string.system_ui_date_pattern);
+        final Locale l = Locale.getDefault();
+        String fmt = ICU.getBestDateTimePattern(dateFormat, l.toString());
+        SimpleDateFormat sdf = new SimpleDateFormat(fmt, l);
+        setText(sdf.format(new Date()));
     }
 
     private boolean isVisible() {
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 3dc77d4..761eb2d 100755
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -260,8 +260,8 @@
         mItems.add(mAirplaneModeOn);
 
         // next: bug report, if enabled
-        if (Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.BUGREPORT_IN_POWER_MENU, 0) != 0) {
+        if (Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0) {
             mItems.add(
                 new SinglePressAction(com.android.internal.R.drawable.stat_sys_adb,
                         R.string.global_action_bug_report) {
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/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java
index 0edb7a13..d938cec 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java
@@ -20,7 +20,6 @@
 import android.content.res.Resources;
 import android.graphics.Typeface;
 import android.text.TextUtils;
-import android.text.format.DateFormat;
 import android.util.AttributeSet;
 import android.util.Slog;
 import android.view.View;
@@ -30,7 +29,11 @@
 import com.android.internal.R;
 import com.android.internal.widget.LockPatternUtils;
 
+import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.Locale;
+
+import libcore.icu.ICU;
 
 public class KeyguardStatusView extends GridLayout {
     private static final boolean DEBUG = KeyguardViewMediator.DEBUG;
@@ -41,7 +44,7 @@
     public static final int CHARGING_ICON = 0; //R.drawable.ic_lock_idle_charging;
     public static final int BATTERY_LOW_ICON = 0; //R.drawable.ic_lock_idle_low_battery;
 
-    private CharSequence mDateFormatString;
+    private SimpleDateFormat mDateFormat;
     private LockPatternUtils mLockPatternUtils;
 
     private TextView mDateView;
@@ -80,8 +83,11 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         Resources res = getContext().getResources();
-        mDateFormatString =
-                res.getText(com.android.internal.R.string.abbrev_wday_month_day_no_year);
+        final Locale locale = Locale.getDefault();
+        final String datePattern =
+                res.getString(com.android.internal.R.string.system_ui_date_pattern);
+        final String bestFormat = ICU.getBestDateTimePattern(datePattern, locale.toString());
+        mDateFormat = new SimpleDateFormat(bestFormat, locale);
         mDateView = (TextView) findViewById(R.id.date);
         mAlarmStatusView = (TextView) findViewById(R.id.alarm_status);
         mClockView = (ClockView) findViewById(R.id.clock_view);
@@ -121,7 +127,7 @@
     }
 
     void refreshDate() {
-        maybeSetUpperCaseText(mDateView, DateFormat.format(mDateFormatString, new Date()));
+        maybeSetUpperCaseText(mDateView, mDateFormat.format(new Date()));
     }
 
     @Override
diff --git a/services/java/com/android/server/AppOpsService.java b/services/java/com/android/server/AppOpsService.java
index 5ad4be8..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,49 +252,239 @@
         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.
-            // XXX for now we always allow null through until we can fix everything
-            // to provide the name.
-            if (packageName != null) {
-                final long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                int pkgUid = -1;
                 try {
-                    int pkgUid = -1;
-                    try {
-                        pkgUid = mContext.getPackageManager().getPackageUid(packageName,
-                                UserHandle.getUserId(uid));
-                    } catch (NameNotFoundException e) {
-                    }
-                    if (pkgUid != uid) {
-                        // Oops!  The package name is not valid for the uid they are calling
-                        // under.  Abort.
-                        Slog.w(TAG, "Bad call: specified package " + packageName
-                                + " under uid " + uid + " but it is really " + pkgUid);
-                        return null;
-                    }
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
+                    pkgUid = mContext.getPackageManager().getPackageUid(packageName,
+                            UserHandle.getUserId(uid));
+                } catch (NameNotFoundException e) {
                 }
+                if (pkgUid != uid) {
+                    // Oops!  The package name is not valid for the uid they are calling
+                    // under.  Abort.
+                    Slog.w(TAG, "Bad call: specified package " + packageName
+                            + " under uid " + uid + " but it is really " + pkgUid);
+                    return null;
+                }
+            } 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/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 91ac1de..2f12112 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -16,6 +16,8 @@
 package com.android.server;
 
 import com.android.internal.content.PackageMonitor;
+import com.android.internal.inputmethod.InputMethodUtils;
+import com.android.internal.inputmethod.InputMethodUtils.InputMethodSettings;
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.FastXmlSerializer;
@@ -53,7 +55,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.res.Configuration;
@@ -76,8 +77,6 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.provider.Settings.Secure;
-import android.provider.Settings.SettingNotFoundException;
 import android.text.TextUtils;
 import android.text.style.SuggestionSpan;
 import android.util.AtomicFile;
@@ -154,15 +153,9 @@
 
     static final int SECURE_SUGGESTION_SPANS_MAX_SIZE = 20;
 
-    private static final int NOT_A_SUBTYPE_ID = -1;
-    private static final String NOT_A_SUBTYPE_ID_STR = String.valueOf(NOT_A_SUBTYPE_ID);
-    private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
-    private static final String SUBTYPE_MODE_VOICE = "voice";
+    private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
     private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
-    private static final String TAG_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE =
-            "EnabledWhenDefaultIsNotAsciiCapable";
-    private static final String TAG_ASCII_CAPABLE = "AsciiCapable";
-    private static final Locale ENGLISH_LOCALE = new Locale("en");
+
 
     final Context mContext;
     final Resources mRes;
@@ -700,21 +693,24 @@
 
     private void resetDefaultImeLocked(Context context) {
         // Do not reset the default (current) IME when it is a 3rd-party IME
-        if (mCurMethodId != null && !isSystemIme(mMethodMap.get(mCurMethodId))) {
+        if (mCurMethodId != null
+                && !InputMethodUtils.isSystemIme(mMethodMap.get(mCurMethodId))) {
             return;
         }
 
         InputMethodInfo defIm = null;
         for (InputMethodInfo imi : mMethodList) {
             if (defIm == null) {
-                if (isValidSystemDefaultIme(imi, context)) {
+                if (InputMethodUtils.isValidSystemDefaultIme(
+                        mSystemReady, imi, context)) {
                     defIm = imi;
                     Slog.i(TAG, "Selected default: " + imi.getId());
                 }
             }
         }
         if (defIm == null && mMethodList.size() > 0) {
-            defIm = getMostApplicableDefaultIMELocked();
+            defIm = InputMethodUtils.getMostApplicableDefaultIME(
+                    mSettings.getEnabledInputMethodListLocked());
             Slog.i(TAG, "No default found, using " + defIm.getId());
         }
         if (defIm != null) {
@@ -775,49 +771,6 @@
         resetAllInternalStateLocked(false);
     }
 
-    private boolean isValidSystemDefaultIme(InputMethodInfo imi, Context context) {
-        if (!mSystemReady) {
-            return false;
-        }
-        if (!isSystemIme(imi)) {
-            return false;
-        }
-        if (imi.getIsDefaultResourceId() != 0) {
-            try {
-                Resources res = context.createPackageContext(
-                        imi.getPackageName(), 0).getResources();
-                if (res.getBoolean(imi.getIsDefaultResourceId())
-                        && containsSubtypeOf(imi, context.getResources().getConfiguration().
-                                locale.getLanguage())) {
-                    return true;
-                }
-            } catch (PackageManager.NameNotFoundException ex) {
-            } catch (Resources.NotFoundException ex) {
-            }
-        }
-        if (imi.getSubtypeCount() == 0) {
-            Slog.w(TAG, "Found no subtypes in a system IME: " + imi.getPackageName());
-        }
-        return false;
-    }
-
-    private static boolean isSystemImeThatHasEnglishSubtype(InputMethodInfo imi) {
-        if (!isSystemIme(imi)) {
-            return false;
-        }
-        return containsSubtypeOf(imi, ENGLISH_LOCALE.getLanguage());
-    }
-
-    private static boolean containsSubtypeOf(InputMethodInfo imi, String language) {
-        final int N = imi.getSubtypeCount();
-        for (int i = 0; i < N; ++i) {
-            if (imi.getSubtypeAt(i).getLocale().startsWith(language)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     @Override
     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
             throws RemoteException {
@@ -935,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
@@ -966,24 +920,15 @@
                 new HashMap<InputMethodInfo, List<InputMethodSubtype>>();
         for (InputMethodInfo imi: mSettings.getEnabledInputMethodListLocked()) {
             enabledInputMethodAndSubtypes.put(
-                    imi, getEnabledInputMethodSubtypeListLocked(imi, true));
+                    imi, mSettings.getEnabledInputMethodSubtypeListLocked(mContext, imi, true));
         }
         return enabledInputMethodAndSubtypes;
     }
 
-    public List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(InputMethodInfo imi,
-            boolean allowsImplicitlySelectedSubtypes) {
-        if (imi == null && mCurMethodId != null) {
-            imi = mMethodMap.get(mCurMethodId);
-        }
-        List<InputMethodSubtype> enabledSubtypes =
-                mSettings.getEnabledInputMethodSubtypeListLocked(imi);
-        if (allowsImplicitlySelectedSubtypes && enabledSubtypes.isEmpty()) {
-            enabledSubtypes = getImplicitlyApplicableSubtypesLocked(mRes, imi);
-        }
-        return InputMethodSubtype.sort(mContext, 0, imi, enabledSubtypes);
-    }
-
+    /**
+     * @param imi if null, returns enabled subtypes for the current imi
+     * @return enabled subtypes of the specified imi
+     */
     @Override
     public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi,
             boolean allowsImplicitlySelectedSubtypes) {
@@ -992,7 +937,11 @@
             return Collections.emptyList();
         }
         synchronized (mMethodMap) {
-            return getEnabledInputMethodSubtypeListLocked(imi, allowsImplicitlySelectedSubtypes);
+            if (imi == null && mCurMethodId != null) {
+                imi = mMethodMap.get(mCurMethodId);
+            }
+            return mSettings.getEnabledInputMethodSubtypeListLocked(
+                    mContext, imi, allowsImplicitlySelectedSubtypes);
         }
     }
 
@@ -1441,8 +1390,8 @@
             InputMethodSubtype auxSubtype = null;
             for(int i = 0; i < N; ++i) {
                 final InputMethodInfo imi = imis.get(i);
-                final List<InputMethodSubtype> subtypes = getEnabledInputMethodSubtypeListLocked(
-                        imi, true);
+                final List<InputMethodSubtype> subtypes =
+                        mSettings.getEnabledInputMethodSubtypeListLocked(mContext, imi, true);
                 final int subtypeCount = subtypes.size();
                 if (subtypeCount == 0) {
                     ++nonAuxCount;
@@ -1547,7 +1496,6 @@
                 SuggestionSpan ss = spans[i];
                 if (!TextUtils.isEmpty(ss.getNotificationTargetClassName())) {
                     mSecureSuggestionSpans.put(ss, currentImi);
-                    final InputMethodInfo targetImi = mSecureSuggestionSpans.get(ss);
                 }
             }
         }
@@ -1597,7 +1545,7 @@
         }
         if (!TextUtils.isEmpty(id)) {
             try {
-                setInputMethodLocked(id, getSelectedInputMethodSubtypeId(id));
+                setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id));
             } catch (IllegalArgumentException e) {
                 Slog.w(TAG, "Unknown input method from prefs: " + id, e);
                 mCurMethodId = null;
@@ -2008,7 +1956,7 @@
         }
         synchronized (mMethodMap) {
             if (subtype != null) {
-                setInputMethodWithSubtypeId(token, id, getSubtypeIdFromHashCode(
+                setInputMethodWithSubtypeId(token, id, InputMethodUtils.getSubtypeIdFromHashCode(
                         mMethodMap.get(id), subtype.hashCode()));
             } else {
                 setInputMethod(token, id);
@@ -2056,11 +2004,12 @@
                 // defined, there is no need to switch to the last IME.
                 if (!imiIdIsSame || lastSubtypeHash != currentSubtypeHash) {
                     targetLastImiId = lastIme.first;
-                    subtypeId = getSubtypeIdFromHashCode(lastImi, lastSubtypeHash);
+                    subtypeId = InputMethodUtils.getSubtypeIdFromHashCode(lastImi, lastSubtypeHash);
                 }
             }
 
-            if (TextUtils.isEmpty(targetLastImiId) && !canAddToLastInputMethod(mCurrentSubtype)) {
+            if (TextUtils.isEmpty(targetLastImiId)
+                    && !InputMethodUtils.canAddToLastInputMethod(mCurrentSubtype)) {
                 // This is a safety net. If the currentSubtype can't be added to the history
                 // and the framework couldn't find the last ime, we will make the last ime be
                 // the most applicable enabled keyboard subtype of the system imes.
@@ -2072,13 +2021,14 @@
                             : mCurrentSubtype.getLocale();
                     for (int i = 0; i < N; ++i) {
                         final InputMethodInfo imi = enabled.get(i);
-                        if (imi.getSubtypeCount() > 0 && isSystemIme(imi)) {
+                        if (imi.getSubtypeCount() > 0 && InputMethodUtils.isSystemIme(imi)) {
                             InputMethodSubtype keyboardSubtype =
-                                    findLastResortApplicableSubtypeLocked(mRes, getSubtypes(imi),
-                                            SUBTYPE_MODE_KEYBOARD, locale, true);
+                                    InputMethodUtils.findLastResortApplicableSubtypeLocked(mRes,
+                                            InputMethodUtils.getSubtypes(imi),
+                                            InputMethodUtils.SUBTYPE_MODE_KEYBOARD, locale, true);
                             if (keyboardSubtype != null) {
                                 targetLastImiId = imi.getId();
-                                subtypeId = getSubtypeIdFromHashCode(
+                                subtypeId = InputMethodUtils.getSubtypeIdFromHashCode(
                                         imi, keyboardSubtype.hashCode());
                                 if(keyboardSubtype.getLocale().equals(locale)) {
                                     break;
@@ -2132,7 +2082,8 @@
             if (lastImi == null) return null;
             try {
                 final int lastSubtypeHash = Integer.valueOf(lastIme.second);
-                final int lastSubtypeId = getSubtypeIdFromHashCode(lastImi, lastSubtypeHash);
+                final int lastSubtypeId =
+                        InputMethodUtils.getSubtypeIdFromHashCode(lastImi, lastSubtypeHash);
                 if (lastSubtypeId < 0 || lastSubtypeId >= lastImi.getSubtypeCount()) {
                     return null;
                 }
@@ -2403,56 +2354,9 @@
         return false;
     }
 
-    private static boolean isSystemIme(InputMethodInfo inputMethod) {
-        return (inputMethod.getServiceInfo().applicationInfo.flags
-                & ApplicationInfo.FLAG_SYSTEM) != 0;
-    }
-
-    private static ArrayList<InputMethodSubtype> getSubtypes(InputMethodInfo imi) {
-        ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
-        final int subtypeCount = imi.getSubtypeCount();
-        for (int i = 0; i < subtypeCount; ++i) {
-            subtypes.add(imi.getSubtypeAt(i));
-        }
-        return subtypes;
-    }
-
-    private static ArrayList<InputMethodSubtype> getOverridingImplicitlyEnabledSubtypes(
-            InputMethodInfo imi, String mode) {
-        ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
-        final int subtypeCount = imi.getSubtypeCount();
-        for (int i = 0; i < subtypeCount; ++i) {
-            final InputMethodSubtype subtype = imi.getSubtypeAt(i);
-            if (subtype.overridesImplicitlyEnabledSubtype() && subtype.getMode().equals(mode)) {
-                subtypes.add(subtype);
-            }
-        }
-        return subtypes;
-    }
-
-    private InputMethodInfo getMostApplicableDefaultIMELocked() {
-        List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked();
-        if (enabled != null && enabled.size() > 0) {
-            // We'd prefer to fall back on a system IME, since that is safer.
-            int i = enabled.size();
-            int firstFoundSystemIme = -1;
-            while (i > 0) {
-                i--;
-                final InputMethodInfo imi = enabled.get(i);
-                if (isSystemImeThatHasEnglishSubtype(imi) && !imi.isAuxiliaryIme()) {
-                    return imi;
-                }
-                if (firstFoundSystemIme < 0 && isSystemIme(imi) && !imi.isAuxiliaryIme()) {
-                    firstFoundSystemIme = i;
-                }
-            }
-            return enabled.get(Math.max(firstFoundSystemIme, 0));
-        }
-        return null;
-    }
-
     private boolean chooseNewDefaultIMELocked() {
-        final InputMethodInfo imi = getMostApplicableDefaultIMELocked();
+        final InputMethodInfo imi = InputMethodUtils.getMostApplicableDefaultIME(
+                mSettings.getEnabledInputMethodListLocked());
         if (imi != null) {
             if (DEBUG) {
                 Slog.d(TAG, "New default IME was selected: " + imi.getId());
@@ -2474,8 +2378,6 @@
 
         // Use for queryIntentServicesAsUser
         final PackageManager pm = mContext.getPackageManager();
-        final Configuration config = mRes.getConfiguration();
-        final boolean haveHardKeyboard = config.keyboard == Configuration.KEYBOARD_QWERTY;
         String disabledSysImes = mSettings.getDisabledSystemInputMethods();
         if (disabledSysImes == null) disabledSysImes = "";
 
@@ -2507,7 +2409,7 @@
 
                 // Valid system default IMEs and IMEs that have English subtypes are enabled
                 // by default
-                if ((isValidSystemDefaultIme(p, mContext) || isSystemImeThatHasEnglishSubtype(p))) {
+                if (InputMethodUtils.isDefaultEnabledIme(mSystemReady, p, mContext)) {
                     setInputMethodEnabledLocked(id, true);
                 }
 
@@ -2576,7 +2478,7 @@
         final boolean isScreenLocked = isScreenLocked();
 
         final String lastInputMethodId = mSettings.getSelectedInputMethod();
-        int lastInputMethodSubtypeId = getSelectedInputMethodSubtypeId(lastInputMethodId);
+        int lastInputMethodSubtypeId = mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId);
         if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
 
         synchronized (mMethodMap) {
@@ -2596,8 +2498,8 @@
                 final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtypeLocked();
                 if (currentSubtype != null) {
                     final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId);
-                    lastInputMethodSubtypeId =
-                            getSubtypeIdFromHashCode(currentImi, currentSubtype.hashCode());
+                    lastInputMethodSubtypeId = InputMethodUtils.getSubtypeIdFromHashCode(
+                            currentImi, currentSubtype.hashCode());
                 }
             }
 
@@ -2847,7 +2749,7 @@
             }
         }
     }
-    
+
     boolean setInputMethodEnabledLocked(String id, boolean enabled) {
         // Make sure this is a valid input method.
         InputMethodInfo imm = mMethodMap.get(id);
@@ -2889,25 +2791,10 @@
         }
     }
 
-    private boolean canAddToLastInputMethod(InputMethodSubtype subtype) {
-        if (subtype == null) return true;
-        return !subtype.isAuxiliary();
-    }
-
-    private void saveCurrentInputMethodAndSubtypeToHistory() {
-        String subtypeId = NOT_A_SUBTYPE_ID_STR;
-        if (mCurrentSubtype != null) {
-            subtypeId = String.valueOf(mCurrentSubtype.hashCode());
-        }
-        if (canAddToLastInputMethod(mCurrentSubtype)) {
-            mSettings.addSubtypeToHistory(mCurMethodId, subtypeId);
-        }
-    }
-
     private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId,
             boolean setSubtypeOnly) {
         // Update the history of InputMethod and Subtype
-        saveCurrentInputMethodAndSubtypeToHistory();
+        mSettings.saveCurrentInputMethodAndSubtypeToHistory(mCurMethodId, mCurrentSubtype);
 
         // Set Subtype here
         if (imi == null || subtypeId < 0) {
@@ -2948,7 +2835,7 @@
             String subtypeHashCode = mSettings.getLastSubtypeForInputMethodLocked(newDefaultIme);
             if (subtypeHashCode != null) {
                 try {
-                    lastSubtypeId = getSubtypeIdFromHashCode(
+                    lastSubtypeId = InputMethodUtils.getSubtypeIdFromHashCode(
                             imi, Integer.valueOf(subtypeHashCode));
                 } catch (NumberFormatException e) {
                     Slog.w(TAG, "HashCode for subtype looks broken: " + subtypeHashCode, e);
@@ -2958,159 +2845,6 @@
         setSelectedInputMethodAndSubtypeLocked(imi, lastSubtypeId, false);
     }
 
-    private int getSelectedInputMethodSubtypeId(String id) {
-        InputMethodInfo imi = mMethodMap.get(id);
-        if (imi == null) {
-            return NOT_A_SUBTYPE_ID;
-        }
-        final int subtypeHashCode = mSettings.getSelectedInputMethodSubtypeHashCode();
-        return getSubtypeIdFromHashCode(imi, subtypeHashCode);
-    }
-
-    private static boolean isValidSubtypeId(InputMethodInfo imi, int subtypeHashCode) {
-        return getSubtypeIdFromHashCode(imi, subtypeHashCode) != NOT_A_SUBTYPE_ID;
-    }
-
-    private static int getSubtypeIdFromHashCode(InputMethodInfo imi, int subtypeHashCode) {
-        if (imi != null) {
-            final int subtypeCount = imi.getSubtypeCount();
-            for (int i = 0; i < subtypeCount; ++i) {
-                InputMethodSubtype ims = imi.getSubtypeAt(i);
-                if (subtypeHashCode == ims.hashCode()) {
-                    return i;
-                }
-            }
-        }
-        return NOT_A_SUBTYPE_ID;
-    }
-
-    private static ArrayList<InputMethodSubtype> getImplicitlyApplicableSubtypesLocked(
-            Resources res, InputMethodInfo imi) {
-        final List<InputMethodSubtype> subtypes = getSubtypes(imi);
-        final String systemLocale = res.getConfiguration().locale.toString();
-        if (TextUtils.isEmpty(systemLocale)) return new ArrayList<InputMethodSubtype>();
-        final HashMap<String, InputMethodSubtype> applicableModeAndSubtypesMap =
-                new HashMap<String, InputMethodSubtype>();
-        final int N = subtypes.size();
-        for (int i = 0; i < N; ++i) {
-            // scan overriding implicitly enabled subtypes.
-            InputMethodSubtype subtype = subtypes.get(i);
-            if (subtype.overridesImplicitlyEnabledSubtype()) {
-                final String mode = subtype.getMode();
-                if (!applicableModeAndSubtypesMap.containsKey(mode)) {
-                    applicableModeAndSubtypesMap.put(mode, subtype);
-                }
-            }
-        }
-        if (applicableModeAndSubtypesMap.size() > 0) {
-            return new ArrayList<InputMethodSubtype>(applicableModeAndSubtypesMap.values());
-        }
-        for (int i = 0; i < N; ++i) {
-            final InputMethodSubtype subtype = subtypes.get(i);
-            final String locale = subtype.getLocale();
-            final String mode = subtype.getMode();
-            // When system locale starts with subtype's locale, that subtype will be applicable
-            // for system locale
-            // For instance, it's clearly applicable for cases like system locale = en_US and
-            // subtype = en, but it is not necessarily considered applicable for cases like system
-            // locale = en and subtype = en_US.
-            // We just call systemLocale.startsWith(locale) in this function because there is no
-            // need to find applicable subtypes aggressively unlike
-            // findLastResortApplicableSubtypeLocked.
-            if (systemLocale.startsWith(locale)) {
-                final InputMethodSubtype applicableSubtype = applicableModeAndSubtypesMap.get(mode);
-                // If more applicable subtypes are contained, skip.
-                if (applicableSubtype != null) {
-                    if (systemLocale.equals(applicableSubtype.getLocale())) continue;
-                    if (!systemLocale.equals(locale)) continue;
-                }
-                applicableModeAndSubtypesMap.put(mode, subtype);
-            }
-        }
-        final InputMethodSubtype keyboardSubtype
-                = applicableModeAndSubtypesMap.get(SUBTYPE_MODE_KEYBOARD);
-        final ArrayList<InputMethodSubtype> applicableSubtypes = new ArrayList<InputMethodSubtype>(
-                applicableModeAndSubtypesMap.values());
-        if (keyboardSubtype != null && !keyboardSubtype.containsExtraValueKey(TAG_ASCII_CAPABLE)) {
-            for (int i = 0; i < N; ++i) {
-                final InputMethodSubtype subtype = subtypes.get(i);
-                final String mode = subtype.getMode();
-                if (SUBTYPE_MODE_KEYBOARD.equals(mode) && subtype.containsExtraValueKey(
-                        TAG_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)) {
-                    applicableSubtypes.add(subtype);
-                }
-            }
-        }
-        if (keyboardSubtype == null) {
-            InputMethodSubtype lastResortKeyboardSubtype = findLastResortApplicableSubtypeLocked(
-                    res, subtypes, SUBTYPE_MODE_KEYBOARD, systemLocale, true);
-            if (lastResortKeyboardSubtype != null) {
-                applicableSubtypes.add(lastResortKeyboardSubtype);
-            }
-        }
-        return applicableSubtypes;
-    }
-
-    /**
-     * If there are no selected subtypes, tries finding the most applicable one according to the
-     * given locale.
-     * @param subtypes this function will search the most applicable subtype in subtypes
-     * @param mode subtypes will be filtered by mode
-     * @param locale subtypes will be filtered by locale
-     * @param canIgnoreLocaleAsLastResort if this function can't find the most applicable subtype,
-     * it will return the first subtype matched with mode
-     * @return the most applicable subtypeId
-     */
-    private static InputMethodSubtype findLastResortApplicableSubtypeLocked(
-            Resources res, List<InputMethodSubtype> subtypes, String mode, String locale,
-            boolean canIgnoreLocaleAsLastResort) {
-        if (subtypes == null || subtypes.size() == 0) {
-            return null;
-        }
-        if (TextUtils.isEmpty(locale)) {
-            locale = res.getConfiguration().locale.toString();
-        }
-        final String language = locale.substring(0, 2);
-        boolean partialMatchFound = false;
-        InputMethodSubtype applicableSubtype = null;
-        InputMethodSubtype firstMatchedModeSubtype = null;
-        final int N = subtypes.size();
-        for (int i = 0; i < N; ++i) {
-            InputMethodSubtype subtype = subtypes.get(i);
-            final String subtypeLocale = subtype.getLocale();
-            // An applicable subtype should match "mode". If mode is null, mode will be ignored,
-            // and all subtypes with all modes can be candidates.
-            if (mode == null || subtypes.get(i).getMode().equalsIgnoreCase(mode)) {
-                if (firstMatchedModeSubtype == null) {
-                    firstMatchedModeSubtype = subtype;
-                }
-                if (locale.equals(subtypeLocale)) {
-                    // Exact match (e.g. system locale is "en_US" and subtype locale is "en_US")
-                    applicableSubtype = subtype;
-                    break;
-                } else if (!partialMatchFound && subtypeLocale.startsWith(language)) {
-                    // Partial match (e.g. system locale is "en_US" and subtype locale is "en")
-                    applicableSubtype = subtype;
-                    partialMatchFound = true;
-                }
-            }
-        }
-
-        if (applicableSubtype == null && canIgnoreLocaleAsLastResort) {
-            return firstMatchedModeSubtype;
-        }
-
-        // The first subtype applicable to the system locale will be defined as the most applicable
-        // subtype.
-        if (DEBUG) {
-            if (applicableSubtype != null) {
-                Slog.d(TAG, "Applicable InputMethodSubtype was found: "
-                        + applicableSubtype.getMode() + "," + applicableSubtype.getLocale());
-            }
-        }
-        return applicableSubtype;
-    }
-
     // If there are no selected shortcuts, tries finding the most applicable ones.
     private Pair<InputMethodInfo, InputMethodSubtype>
             findLastResortApplicableShortcutInputMethodAndSubtypeLocked(String mode) {
@@ -3127,32 +2861,33 @@
             }
             InputMethodSubtype subtype = null;
             final List<InputMethodSubtype> enabledSubtypes =
-                    getEnabledInputMethodSubtypeListLocked(imi, true);
+                    mSettings.getEnabledInputMethodSubtypeListLocked(mContext, imi, true);
             // 1. Search by the current subtype's locale from enabledSubtypes.
             if (mCurrentSubtype != null) {
-                subtype = findLastResortApplicableSubtypeLocked(
+                subtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
                         mRes, enabledSubtypes, mode, mCurrentSubtype.getLocale(), false);
             }
             // 2. Search by the system locale from enabledSubtypes.
             // 3. Search the first enabled subtype matched with mode from enabledSubtypes.
             if (subtype == null) {
-                subtype = findLastResortApplicableSubtypeLocked(
+                subtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
                         mRes, enabledSubtypes, mode, null, true);
             }
             final ArrayList<InputMethodSubtype> overridingImplicitlyEnabledSubtypes =
-                    getOverridingImplicitlyEnabledSubtypes(imi, mode);
+                    InputMethodUtils.getOverridingImplicitlyEnabledSubtypes(imi, mode);
             final ArrayList<InputMethodSubtype> subtypesForSearch =
                     overridingImplicitlyEnabledSubtypes.isEmpty()
-                            ? getSubtypes(imi) : overridingImplicitlyEnabledSubtypes;
+                            ? InputMethodUtils.getSubtypes(imi)
+                            : overridingImplicitlyEnabledSubtypes;
             // 4. Search by the current subtype's locale from all subtypes.
             if (subtype == null && mCurrentSubtype != null) {
-                subtype = findLastResortApplicableSubtypeLocked(
+                subtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
                         mRes, subtypesForSearch, mode, mCurrentSubtype.getLocale(), false);
             }
             // 5. Search by the system locale from all subtypes.
             // 6. Search the first enabled subtype matched with mode from all subtypes.
             if (subtype == null) {
-                subtype = findLastResortApplicableSubtypeLocked(
+                subtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
                         mRes, subtypesForSearch, mode, null, true);
             }
             if (subtype != null) {
@@ -3209,37 +2944,36 @@
         if (mCurMethodId == null) {
             return null;
         }
-        final boolean subtypeIsSelected =
-                mSettings.getSelectedInputMethodSubtypeHashCode() != NOT_A_SUBTYPE_ID;
+        final boolean subtypeIsSelected = mSettings.isSubtypeSelected();
         final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
         if (imi == null || imi.getSubtypeCount() == 0) {
             return null;
         }
         if (!subtypeIsSelected || mCurrentSubtype == null
-                || !isValidSubtypeId(imi, mCurrentSubtype.hashCode())) {
-            int subtypeId = getSelectedInputMethodSubtypeId(mCurMethodId);
+                || !InputMethodUtils.isValidSubtypeId(imi, mCurrentSubtype.hashCode())) {
+            int subtypeId = mSettings.getSelectedInputMethodSubtypeId(mCurMethodId);
             if (subtypeId == NOT_A_SUBTYPE_ID) {
                 // If there are no selected subtypes, the framework will try to find
                 // the most applicable subtype from explicitly or implicitly enabled
                 // subtypes.
                 List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypes =
-                        getEnabledInputMethodSubtypeListLocked(imi, true);
+                        mSettings.getEnabledInputMethodSubtypeListLocked(mContext, imi, true);
                 // If there is only one explicitly or implicitly enabled subtype,
                 // just returns it.
                 if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) {
                     mCurrentSubtype = explicitlyOrImplicitlyEnabledSubtypes.get(0);
                 } else if (explicitlyOrImplicitlyEnabledSubtypes.size() > 1) {
-                    mCurrentSubtype = findLastResortApplicableSubtypeLocked(
+                    mCurrentSubtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
                             mRes, explicitlyOrImplicitlyEnabledSubtypes,
-                            SUBTYPE_MODE_KEYBOARD, null, true);
+                            InputMethodUtils.SUBTYPE_MODE_KEYBOARD, null, true);
                     if (mCurrentSubtype == null) {
-                        mCurrentSubtype = findLastResortApplicableSubtypeLocked(
+                        mCurrentSubtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
                                 mRes, explicitlyOrImplicitlyEnabledSubtypes, null, null,
                                 true);
                     }
                 }
             } else {
-                mCurrentSubtype = getSubtypes(imi).get(subtypeId);
+                mCurrentSubtype = InputMethodUtils.getSubtypes(imi).get(subtypeId);
             }
         }
         return mCurrentSubtype;
@@ -3268,7 +3002,7 @@
                 // SUBTYPE_MODE_VOICE. This is an exceptional case, so we will hardcode the mode.
                 Pair<InputMethodInfo, InputMethodSubtype> info =
                     findLastResortApplicableShortcutInputMethodAndSubtypeLocked(
-                            SUBTYPE_MODE_VOICE);
+                            InputMethodUtils.SUBTYPE_MODE_VOICE);
                 if (info != null) {
                     ret.add(info.first);
                     ret.add(info.second);
@@ -3294,7 +3028,7 @@
         synchronized (mMethodMap) {
             if (subtype != null && mCurMethodId != null) {
                 InputMethodInfo imi = mMethodMap.get(mCurMethodId);
-                int subtypeId = getSubtypeIdFromHashCode(imi, subtype.hashCode());
+                int subtypeId = InputMethodUtils.getSubtypeIdFromHashCode(imi, subtype.hashCode());
                 if (subtypeId != NOT_A_SUBTYPE_ID) {
                     setInputMethodLocked(mCurMethodId, subtypeId);
                     return true;
@@ -3345,7 +3079,7 @@
             }
             final int N = imList.size();
             final int currentSubtypeId = subtype != null
-                    ? getSubtypeIdFromHashCode(imi, subtype.hashCode())
+                    ? InputMethodUtils.getSubtypeIdFromHashCode(imi, subtype.hashCode())
                     : NOT_A_SUBTYPE_ID;
             for (int i = 0; i < N; ++i) {
                 final ImeSubtypeListItem isli = imList.get(i);
@@ -3386,7 +3120,6 @@
                 for (InputMethodSubtype subtype: explicitlyOrImplicitlyEnabledSubtypeList) {
                     enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
                 }
-                ArrayList<InputMethodSubtype> subtypes = getSubtypes(imi);
                 final CharSequence imeLabel = imi.loadLabel(mPm);
                 if (showSubtypes && enabledSubtypeSet.size() > 0) {
                     final int subtypeCount = imi.getSubtypeCount();
@@ -3422,451 +3155,6 @@
         }
     }
 
-    /**
-     * Utility class for putting and getting settings for InputMethod
-     * TODO: Move all putters and getters of settings to this class.
-     */
-    private static class InputMethodSettings {
-        // The string for enabled input method is saved as follows:
-        // example: ("ime0;subtype0;subtype1;subtype2:ime1:ime2;subtype0")
-        private static final char INPUT_METHOD_SEPARATER = ':';
-        private static final char INPUT_METHOD_SUBTYPE_SEPARATER = ';';
-        private final TextUtils.SimpleStringSplitter mInputMethodSplitter =
-                new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATER);
-
-        private final TextUtils.SimpleStringSplitter mSubtypeSplitter =
-                new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATER);
-
-        private final Resources mRes;
-        private final ContentResolver mResolver;
-        private final HashMap<String, InputMethodInfo> mMethodMap;
-        private final ArrayList<InputMethodInfo> mMethodList;
-
-        private String mEnabledInputMethodsStrCache;
-        private int mCurrentUserId;
-
-        private static void buildEnabledInputMethodsSettingString(
-                StringBuilder builder, Pair<String, ArrayList<String>> pair) {
-            String id = pair.first;
-            ArrayList<String> subtypes = pair.second;
-            builder.append(id);
-            // Inputmethod and subtypes are saved in the settings as follows:
-            // ime0;subtype0;subtype1:ime1;subtype0:ime2:ime3;subtype0;subtype1
-            for (String subtypeId: subtypes) {
-                builder.append(INPUT_METHOD_SUBTYPE_SEPARATER).append(subtypeId);
-            }
-        }
-
-        public InputMethodSettings(
-                Resources res, ContentResolver resolver,
-                HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
-                int userId) {
-            setCurrentUserId(userId);
-            mRes = res;
-            mResolver = resolver;
-            mMethodMap = methodMap;
-            mMethodList = methodList;
-        }
-
-        public void setCurrentUserId(int userId) {
-            if (DEBUG) {
-                Slog.d(TAG, "--- Swtich the current user from " + mCurrentUserId + " to "
-                        + userId + ", new ime = " + getSelectedInputMethod());
-            }
-            // IMMS settings are kept per user, so keep track of current user
-            mCurrentUserId = userId;
-        }
-
-        public List<InputMethodInfo> getEnabledInputMethodListLocked() {
-            return createEnabledInputMethodListLocked(
-                    getEnabledInputMethodsAndSubtypeListLocked());
-        }
-
-        public List<Pair<InputMethodInfo, ArrayList<String>>>
-                getEnabledInputMethodAndSubtypeHashCodeListLocked() {
-            return createEnabledInputMethodAndSubtypeHashCodeListLocked(
-                    getEnabledInputMethodsAndSubtypeListLocked());
-        }
-
-        public List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(
-                InputMethodInfo imi) {
-            List<Pair<String, ArrayList<String>>> imsList =
-                    getEnabledInputMethodsAndSubtypeListLocked();
-            ArrayList<InputMethodSubtype> enabledSubtypes =
-                    new ArrayList<InputMethodSubtype>();
-            if (imi != null) {
-                for (Pair<String, ArrayList<String>> imsPair : imsList) {
-                    InputMethodInfo info = mMethodMap.get(imsPair.first);
-                    if (info != null && info.getId().equals(imi.getId())) {
-                        final int subtypeCount = info.getSubtypeCount();
-                        for (int i = 0; i < subtypeCount; ++i) {
-                            InputMethodSubtype ims = info.getSubtypeAt(i);
-                            for (String s: imsPair.second) {
-                                if (String.valueOf(ims.hashCode()).equals(s)) {
-                                    enabledSubtypes.add(ims);
-                                }
-                            }
-                        }
-                        break;
-                    }
-                }
-            }
-            return enabledSubtypes;
-        }
-
-        // At the initial boot, the settings for input methods are not set,
-        // so we need to enable IME in that case.
-        public void enableAllIMEsIfThereIsNoEnabledIME() {
-            if (TextUtils.isEmpty(getEnabledInputMethodsStr())) {
-                StringBuilder sb = new StringBuilder();
-                final int N = mMethodList.size();
-                for (int i = 0; i < N; i++) {
-                    InputMethodInfo imi = mMethodList.get(i);
-                    Slog.i(TAG, "Adding: " + imi.getId());
-                    if (i > 0) sb.append(':');
-                    sb.append(imi.getId());
-                }
-                putEnabledInputMethodsStr(sb.toString());
-            }
-        }
-
-        private List<Pair<String, ArrayList<String>>> getEnabledInputMethodsAndSubtypeListLocked() {
-            ArrayList<Pair<String, ArrayList<String>>> imsList
-                    = new ArrayList<Pair<String, ArrayList<String>>>();
-            final String enabledInputMethodsStr = getEnabledInputMethodsStr();
-            if (TextUtils.isEmpty(enabledInputMethodsStr)) {
-                return imsList;
-            }
-            mInputMethodSplitter.setString(enabledInputMethodsStr);
-            while (mInputMethodSplitter.hasNext()) {
-                String nextImsStr = mInputMethodSplitter.next();
-                mSubtypeSplitter.setString(nextImsStr);
-                if (mSubtypeSplitter.hasNext()) {
-                    ArrayList<String> subtypeHashes = new ArrayList<String>();
-                    // The first element is ime id.
-                    String imeId = mSubtypeSplitter.next();
-                    while (mSubtypeSplitter.hasNext()) {
-                        subtypeHashes.add(mSubtypeSplitter.next());
-                    }
-                    imsList.add(new Pair<String, ArrayList<String>>(imeId, subtypeHashes));
-                }
-            }
-            return imsList;
-        }
-
-        public void appendAndPutEnabledInputMethodLocked(String id, boolean reloadInputMethodStr) {
-            if (reloadInputMethodStr) {
-                getEnabledInputMethodsStr();
-            }
-            if (TextUtils.isEmpty(mEnabledInputMethodsStrCache)) {
-                // Add in the newly enabled input method.
-                putEnabledInputMethodsStr(id);
-            } else {
-                putEnabledInputMethodsStr(
-                        mEnabledInputMethodsStrCache + INPUT_METHOD_SEPARATER + id);
-            }
-        }
-
-        /**
-         * Build and put a string of EnabledInputMethods with removing specified Id.
-         * @return the specified id was removed or not.
-         */
-        public boolean buildAndPutEnabledInputMethodsStrRemovingIdLocked(
-                StringBuilder builder, List<Pair<String, ArrayList<String>>> imsList, String id) {
-            boolean isRemoved = false;
-            boolean needsAppendSeparator = false;
-            for (Pair<String, ArrayList<String>> ims: imsList) {
-                String curId = ims.first;
-                if (curId.equals(id)) {
-                    // We are disabling this input method, and it is
-                    // currently enabled.  Skip it to remove from the
-                    // new list.
-                    isRemoved = true;
-                } else {
-                    if (needsAppendSeparator) {
-                        builder.append(INPUT_METHOD_SEPARATER);
-                    } else {
-                        needsAppendSeparator = true;
-                    }
-                    buildEnabledInputMethodsSettingString(builder, ims);
-                }
-            }
-            if (isRemoved) {
-                // Update the setting with the new list of input methods.
-                putEnabledInputMethodsStr(builder.toString());
-            }
-            return isRemoved;
-        }
-
-        private List<InputMethodInfo> createEnabledInputMethodListLocked(
-                List<Pair<String, ArrayList<String>>> imsList) {
-            final ArrayList<InputMethodInfo> res = new ArrayList<InputMethodInfo>();
-            for (Pair<String, ArrayList<String>> ims: imsList) {
-                InputMethodInfo info = mMethodMap.get(ims.first);
-                if (info != null) {
-                    res.add(info);
-                }
-            }
-            return res;
-        }
-
-        private List<Pair<InputMethodInfo, ArrayList<String>>>
-                createEnabledInputMethodAndSubtypeHashCodeListLocked(
-                        List<Pair<String, ArrayList<String>>> imsList) {
-            final ArrayList<Pair<InputMethodInfo, ArrayList<String>>> res
-                    = new ArrayList<Pair<InputMethodInfo, ArrayList<String>>>();
-            for (Pair<String, ArrayList<String>> ims : imsList) {
-                InputMethodInfo info = mMethodMap.get(ims.first);
-                if (info != null) {
-                    res.add(new Pair<InputMethodInfo, ArrayList<String>>(info, ims.second));
-                }
-            }
-            return res;
-        }
-
-        private void putEnabledInputMethodsStr(String str) {
-            Settings.Secure.putStringForUser(
-                    mResolver, Settings.Secure.ENABLED_INPUT_METHODS, str, mCurrentUserId);
-            mEnabledInputMethodsStrCache = str;
-            if (DEBUG) {
-                Slog.d(TAG, "putEnabledInputMethodStr: " + str);
-            }
-        }
-
-        private String getEnabledInputMethodsStr() {
-            mEnabledInputMethodsStrCache = Settings.Secure.getStringForUser(
-                    mResolver, Settings.Secure.ENABLED_INPUT_METHODS, mCurrentUserId);
-            if (DEBUG) {
-                Slog.d(TAG, "getEnabledInputMethodsStr: " + mEnabledInputMethodsStrCache
-                        + ", " + mCurrentUserId);
-            }
-            return mEnabledInputMethodsStrCache;
-        }
-
-        private void saveSubtypeHistory(
-                List<Pair<String, String>> savedImes, String newImeId, String newSubtypeId) {
-            StringBuilder builder = new StringBuilder();
-            boolean isImeAdded = false;
-            if (!TextUtils.isEmpty(newImeId) && !TextUtils.isEmpty(newSubtypeId)) {
-                builder.append(newImeId).append(INPUT_METHOD_SUBTYPE_SEPARATER).append(
-                        newSubtypeId);
-                isImeAdded = true;
-            }
-            for (Pair<String, String> ime: savedImes) {
-                String imeId = ime.first;
-                String subtypeId = ime.second;
-                if (TextUtils.isEmpty(subtypeId)) {
-                    subtypeId = NOT_A_SUBTYPE_ID_STR;
-                }
-                if (isImeAdded) {
-                    builder.append(INPUT_METHOD_SEPARATER);
-                } else {
-                    isImeAdded = true;
-                }
-                builder.append(imeId).append(INPUT_METHOD_SUBTYPE_SEPARATER).append(
-                        subtypeId);
-            }
-            // Remove the last INPUT_METHOD_SEPARATER
-            putSubtypeHistoryStr(builder.toString());
-        }
-
-        public void addSubtypeToHistory(String imeId, String subtypeId) {
-            List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistoryLocked();
-            for (Pair<String, String> ime: subtypeHistory) {
-                if (ime.first.equals(imeId)) {
-                    if (DEBUG) {
-                        Slog.v(TAG, "Subtype found in the history: " + imeId + ", "
-                                + ime.second);
-                    }
-                    // We should break here
-                    subtypeHistory.remove(ime);
-                    break;
-                }
-            }
-            if (DEBUG) {
-                Slog.v(TAG, "Add subtype to the history: " + imeId + ", " + subtypeId);
-            }
-            saveSubtypeHistory(subtypeHistory, imeId, subtypeId);
-        }
-
-        private void putSubtypeHistoryStr(String str) {
-            if (DEBUG) {
-                Slog.d(TAG, "putSubtypeHistoryStr: " + str);
-            }
-            Settings.Secure.putStringForUser(
-                    mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, str, mCurrentUserId);
-        }
-
-        public Pair<String, String> getLastInputMethodAndSubtypeLocked() {
-            // Gets the first one from the history
-            return getLastSubtypeForInputMethodLockedInternal(null);
-        }
-
-        public String getLastSubtypeForInputMethodLocked(String imeId) {
-            Pair<String, String> ime = getLastSubtypeForInputMethodLockedInternal(imeId);
-            if (ime != null) {
-                return ime.second;
-            } else {
-                return null;
-            }
-        }
-
-        private Pair<String, String> getLastSubtypeForInputMethodLockedInternal(String imeId) {
-            List<Pair<String, ArrayList<String>>> enabledImes =
-                    getEnabledInputMethodsAndSubtypeListLocked();
-            List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistoryLocked();
-            for (Pair<String, String> imeAndSubtype : subtypeHistory) {
-                final String imeInTheHistory = imeAndSubtype.first;
-                // If imeId is empty, returns the first IME and subtype in the history
-                if (TextUtils.isEmpty(imeId) || imeInTheHistory.equals(imeId)) {
-                    final String subtypeInTheHistory = imeAndSubtype.second;
-                    final String subtypeHashCode =
-                            getEnabledSubtypeHashCodeForInputMethodAndSubtypeLocked(
-                                    enabledImes, imeInTheHistory, subtypeInTheHistory);
-                    if (!TextUtils.isEmpty(subtypeHashCode)) {
-                        if (DEBUG) {
-                            Slog.d(TAG, "Enabled subtype found in the history: " + subtypeHashCode);
-                        }
-                        return new Pair<String, String>(imeInTheHistory, subtypeHashCode);
-                    }
-                }
-            }
-            if (DEBUG) {
-                Slog.d(TAG, "No enabled IME found in the history");
-            }
-            return null;
-        }
-
-        private String getEnabledSubtypeHashCodeForInputMethodAndSubtypeLocked(List<Pair<String,
-                ArrayList<String>>> enabledImes, String imeId, String subtypeHashCode) {
-            for (Pair<String, ArrayList<String>> enabledIme: enabledImes) {
-                if (enabledIme.first.equals(imeId)) {
-                    final ArrayList<String> explicitlyEnabledSubtypes = enabledIme.second;
-                    final InputMethodInfo imi = mMethodMap.get(imeId);
-                    if (explicitlyEnabledSubtypes.size() == 0) {
-                        // If there are no explicitly enabled subtypes, applicable subtypes are
-                        // enabled implicitly.
-                        // If IME is enabled and no subtypes are enabled, applicable subtypes
-                        // are enabled implicitly, so needs to treat them to be enabled.
-                        if (imi != null && imi.getSubtypeCount() > 0) {
-                            List<InputMethodSubtype> implicitlySelectedSubtypes =
-                                    getImplicitlyApplicableSubtypesLocked(mRes, imi);
-                            if (implicitlySelectedSubtypes != null) {
-                                final int N = implicitlySelectedSubtypes.size();
-                                for (int i = 0; i < N; ++i) {
-                                    final InputMethodSubtype st = implicitlySelectedSubtypes.get(i);
-                                    if (String.valueOf(st.hashCode()).equals(subtypeHashCode)) {
-                                        return subtypeHashCode;
-                                    }
-                                }
-                            }
-                        }
-                    } else {
-                        for (String s: explicitlyEnabledSubtypes) {
-                            if (s.equals(subtypeHashCode)) {
-                                // If both imeId and subtypeId are enabled, return subtypeId.
-                                try {
-                                    final int hashCode = Integer.valueOf(subtypeHashCode);
-                                    // Check whether the subtype id is valid or not
-                                    if (isValidSubtypeId(imi, hashCode)) {
-                                        return s;
-                                    } else {
-                                        return NOT_A_SUBTYPE_ID_STR;
-                                    }
-                                } catch (NumberFormatException e) {
-                                    return NOT_A_SUBTYPE_ID_STR;
-                                }
-                            }
-                        }
-                    }
-                    // If imeId was enabled but subtypeId was disabled.
-                    return NOT_A_SUBTYPE_ID_STR;
-                }
-            }
-            // If both imeId and subtypeId are disabled, return null
-            return null;
-        }
-
-        private List<Pair<String, String>> loadInputMethodAndSubtypeHistoryLocked() {
-            ArrayList<Pair<String, String>> imsList = new ArrayList<Pair<String, String>>();
-            final String subtypeHistoryStr = getSubtypeHistoryStr();
-            if (TextUtils.isEmpty(subtypeHistoryStr)) {
-                return imsList;
-            }
-            mInputMethodSplitter.setString(subtypeHistoryStr);
-            while (mInputMethodSplitter.hasNext()) {
-                String nextImsStr = mInputMethodSplitter.next();
-                mSubtypeSplitter.setString(nextImsStr);
-                if (mSubtypeSplitter.hasNext()) {
-                    String subtypeId = NOT_A_SUBTYPE_ID_STR;
-                    // The first element is ime id.
-                    String imeId = mSubtypeSplitter.next();
-                    while (mSubtypeSplitter.hasNext()) {
-                        subtypeId = mSubtypeSplitter.next();
-                        break;
-                    }
-                    imsList.add(new Pair<String, String>(imeId, subtypeId));
-                }
-            }
-            return imsList;
-        }
-
-        private String getSubtypeHistoryStr() {
-            if (DEBUG) {
-                Slog.d(TAG, "getSubtypeHistoryStr: " + Settings.Secure.getStringForUser(
-                        mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, mCurrentUserId));
-            }
-            return Settings.Secure.getStringForUser(
-                    mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, mCurrentUserId);
-        }
-
-        public void putSelectedInputMethod(String imeId) {
-            if (DEBUG) {
-                Slog.d(TAG, "putSelectedInputMethodStr: " + imeId + ", "
-                        + mCurrentUserId);
-            }
-            Settings.Secure.putStringForUser(
-                    mResolver, Settings.Secure.DEFAULT_INPUT_METHOD, imeId, mCurrentUserId);
-        }
-
-        public void putSelectedSubtype(int subtypeId) {
-            if (DEBUG) {
-                Slog.d(TAG, "putSelectedInputMethodSubtypeStr: " + subtypeId + ", "
-                        + mCurrentUserId);
-            }
-            Settings.Secure.putIntForUser(mResolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
-                    subtypeId, mCurrentUserId);
-        }
-
-        public String getDisabledSystemInputMethods() {
-            return Settings.Secure.getStringForUser(
-                    mResolver, Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS, mCurrentUserId);
-        }
-
-        public String getSelectedInputMethod() {
-            if (DEBUG) {
-                Slog.d(TAG, "getSelectedInputMethodStr: " + Settings.Secure.getStringForUser(
-                        mResolver, Settings.Secure.DEFAULT_INPUT_METHOD, mCurrentUserId)
-                        + ", " + mCurrentUserId);
-            }
-            return Settings.Secure.getStringForUser(
-                    mResolver, Settings.Secure.DEFAULT_INPUT_METHOD, mCurrentUserId);
-        }
-
-        public int getSelectedInputMethodSubtypeHashCode() {
-            try {
-                return Settings.Secure.getIntForUser(
-                        mResolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, mCurrentUserId);
-            } catch (SettingNotFoundException e) {
-                return NOT_A_SUBTYPE_ID;
-            }
-        }
-
-        public int getCurrentUserId() {
-            return mCurrentUserId;
-        }
-    }
-
     // TODO: Cache the state for each user and reset when the cached user is removed.
     private static class InputMethodFileManager {
         private static final String SYSTEM_PATH = "system";
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 9e40dc5..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
@@ -1010,7 +1039,7 @@
                     if (UserHandle.getUserId(record.mReceiver.mUid) == mCurrentUserId) {
                         LocationRequest locationRequest = record.mRequest;
                         if (locationRequest.getInterval() <= thresholdInterval) {
-                            worksource.add(record.mReceiver.mUid);
+                            worksource.add(record.mReceiver.mUid, record.mReceiver.mPackageName);
                         }
                     }
                 }
@@ -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 cc8165a..de8a44b 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -1345,49 +1345,6 @@
     }
 
     @Override
-    public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-        try {
-            mConnector.execute("interface", "setthrottle", iface, rxKbps, txKbps);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
-        }
-    }
-
-    private int getInterfaceThrottle(String iface, boolean rx) {
-        final NativeDaemonEvent event;
-        try {
-            event = mConnector.execute("interface", "getthrottle", iface, rx ? "rx" : "tx");
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
-        }
-
-        if (rx) {
-            event.checkCode(InterfaceRxThrottleResult);
-        } else {
-            event.checkCode(InterfaceTxThrottleResult);
-        }
-
-        try {
-            return Integer.parseInt(event.getMessage());
-        } catch (NumberFormatException e) {
-            throw new IllegalStateException("unexpected response:" + event);
-        }
-    }
-
-    @Override
-    public int getInterfaceRxThrottle(String iface) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-        return getInterfaceThrottle(iface, true);
-    }
-
-    @Override
-    public int getInterfaceTxThrottle(String iface) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-        return getInterfaceThrottle(iface, false);
-    }
-
-    @Override
     public void setDefaultInterfaceForDns(String iface) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
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/SystemServer.java b/services/java/com/android/server/SystemServer.java
index c33eb2b..39ee47e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -146,7 +146,6 @@
         TwilightService twilight = null;
         UiModeManagerService uiMode = null;
         RecognitionManagerService recognition = null;
-        ThrottleService throttle = null;
         NetworkTimeUpdateService networkTimeUpdater = null;
         CommonTimeManagementService commonTimeMgmtService = null;
         InputManagerService inputManager = null;
@@ -511,15 +510,6 @@
             }
 
             try {
-                Slog.i(TAG, "Throttle Service");
-                throttle = new ThrottleService(context);
-                ServiceManager.addService(
-                        Context.THROTTLE_SERVICE, throttle);
-            } catch (Throwable e) {
-                reportWtf("starting ThrottleService", e);
-            }
-
-            try {
                 Slog.i(TAG, "UpdateLock Service");
                 ServiceManager.addService(Context.UPDATE_LOCK_SERVICE,
                         new UpdateLockService(context));
@@ -839,7 +829,6 @@
         final ConnectivityService connectivityF = connectivity;
         final DockObserver dockF = dock;
         final UsbService usbF = usb;
-        final ThrottleService throttleF = throttle;
         final TwilightService twilightF = twilight;
         final UiModeManagerService uiModeF = uiMode;
         final AppWidgetService appWidgetF = appWidget;
@@ -952,11 +941,6 @@
                     reportWtf("making Country Detector Service ready", e);
                 }
                 try {
-                    if (throttleF != null) throttleF.systemReady();
-                } catch (Throwable e) {
-                    reportWtf("making Throttle Service ready", e);
-                }
-                try {
                     if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemReady();
                 } catch (Throwable e) {
                     reportWtf("making Network Time Service ready", e);
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/ThrottleService.java b/services/java/com/android/server/ThrottleService.java
deleted file mode 100644
index 75eb3c4..0000000
--- a/services/java/com/android/server/ThrottleService.java
+++ /dev/null
@@ -1,1160 +0,0 @@
-/*
- * Copyright (C) 2007 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 com.android.server;
-
-import android.app.AlarmManager;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.net.INetworkManagementEventObserver;
-import android.net.IThrottleManager;
-import android.net.NetworkStats;
-import android.net.ThrottleManager;
-import android.os.Binder;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.INetworkManagementService;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.util.NtpTrustedTime;
-import android.util.Slog;
-import android.util.TrustedTime;
-
-import com.android.internal.R;
-import com.android.internal.telephony.TelephonyProperties;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Calendar;
-import java.util.GregorianCalendar;
-import java.util.Random;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
-
-// TODO - add comments - reference the ThrottleManager for public API
-public class ThrottleService extends IThrottleManager.Stub {
-
-    private static final String TESTING_ENABLED_PROPERTY = "persist.throttle.testing";
-
-    private static final String TAG = "ThrottleService";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-    private Handler mHandler;
-    private HandlerThread mThread;
-
-    private Context mContext;
-
-    private static final int INITIAL_POLL_DELAY_SEC = 90;
-    private static final int TESTING_POLLING_PERIOD_SEC = 60 * 1;
-    private static final int TESTING_RESET_PERIOD_SEC = 60 * 10;
-    private static final long TESTING_THRESHOLD = 1 * 1024 * 1024;
-
-    private static final long MAX_NTP_CACHE_AGE = 24 * 60 * 60 * 1000;
-
-    private long mMaxNtpCacheAge = MAX_NTP_CACHE_AGE;
-
-    private int mPolicyPollPeriodSec;
-    private AtomicLong mPolicyThreshold;
-    private AtomicInteger mPolicyThrottleValue;
-    private int mPolicyResetDay; // 1-28
-    private int mPolicyNotificationsAllowedMask;
-
-    private long mLastRead; // read byte count from last poll
-    private long mLastWrite; // write byte count from last poll
-
-    private static final String ACTION_POLL = "com.android.server.ThrottleManager.action.POLL";
-    private static int POLL_REQUEST = 0;
-    private PendingIntent mPendingPollIntent;
-    private static final String ACTION_RESET = "com.android.server.ThorottleManager.action.RESET";
-    private static int RESET_REQUEST = 1;
-    private PendingIntent mPendingResetIntent;
-
-    private INetworkManagementService mNMService;
-    private AlarmManager mAlarmManager;
-    private NotificationManager mNotificationManager;
-
-    private DataRecorder mRecorder;
-
-    private String mIface;
-
-    private static final int NOTIFICATION_WARNING   = 2;
-
-    private Notification mThrottlingNotification;
-    private boolean mWarningNotificationSent = false;
-
-    private InterfaceObserver mInterfaceObserver;
-    private SettingsObserver mSettingsObserver;
-
-    private AtomicInteger mThrottleIndex; // 0 for none, 1 for first throttle val, 2 for next, etc
-    private static final int THROTTLE_INDEX_UNINITIALIZED = -1;
-    private static final int THROTTLE_INDEX_UNTHROTTLED   =  0;
-
-    private Intent mPollStickyBroadcast;
-
-    private TrustedTime mTime;
-
-    private static INetworkManagementService getNetworkManagementService() {
-        final IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-        return INetworkManagementService.Stub.asInterface(b);
-    }
-
-    public ThrottleService(Context context) {
-        this(context, getNetworkManagementService(), NtpTrustedTime.getInstance(context),
-                context.getResources().getString(R.string.config_datause_iface));
-    }
-
-    public ThrottleService(Context context, INetworkManagementService nmService, TrustedTime time,
-            String iface) {
-        if (VDBG) Slog.v(TAG, "Starting ThrottleService");
-        mContext = context;
-
-        mPolicyThreshold = new AtomicLong();
-        mPolicyThrottleValue = new AtomicInteger();
-        mThrottleIndex = new AtomicInteger();
-
-        mIface = iface;
-        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
-        Intent pollIntent = new Intent(ACTION_POLL, null);
-        mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
-        Intent resetIntent = new Intent(ACTION_RESET, null);
-        mPendingResetIntent = PendingIntent.getBroadcast(mContext, RESET_REQUEST, resetIntent, 0);
-
-        mNMService = nmService;
-        mTime = time;
-
-        mNotificationManager = (NotificationManager)mContext.getSystemService(
-                Context.NOTIFICATION_SERVICE);
-    }
-
-    private static class InterfaceObserver extends INetworkManagementEventObserver.Stub {
-        private int mMsg;
-        private Handler mHandler;
-        private String mIface;
-
-        InterfaceObserver(Handler handler, int msg, String iface) {
-            super();
-            mHandler = handler;
-            mMsg = msg;
-            mIface = iface;
-        }
-
-        public void interfaceStatusChanged(String iface, boolean up) {
-            if (up) {
-                if (TextUtils.equals(iface, mIface)) {
-                    mHandler.obtainMessage(mMsg).sendToTarget();
-                }
-            }
-        }
-
-        public void interfaceLinkStateChanged(String iface, boolean up) {
-        }
-
-        public void interfaceAdded(String iface) {
-            // TODO - an interface added in the UP state should also trigger a StatusChanged
-            // notification..
-            if (TextUtils.equals(iface, mIface)) {
-                mHandler.obtainMessage(mMsg).sendToTarget();
-            }
-        }
-
-        public void interfaceRemoved(String iface) {}
-        public void limitReached(String limitName, String iface) {}
-        public void interfaceClassDataActivityChanged(String label, boolean active) {}
-    }
-
-
-    private static class SettingsObserver extends ContentObserver {
-        private int mMsg;
-        private Handler mHandler;
-        SettingsObserver(Handler handler, int msg) {
-            super(handler);
-            mHandler = handler;
-            mMsg = msg;
-        }
-
-        void register(Context context) {
-            ContentResolver resolver = context.getContentResolver();
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.THROTTLE_POLLING_SEC), false, this);
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.THROTTLE_THRESHOLD_BYTES), false, this);
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.THROTTLE_VALUE_KBITSPS), false, this);
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.THROTTLE_RESET_DAY), false, this);
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.THROTTLE_NOTIFICATION_TYPE), false, this);
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.THROTTLE_HELP_URI), false, this);
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.THROTTLE_MAX_NTP_CACHE_AGE_SEC), false, this);
-        }
-
-        void unregister(Context context) {
-            final ContentResolver resolver = context.getContentResolver();
-            resolver.unregisterContentObserver(this);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            mHandler.obtainMessage(mMsg).sendToTarget();
-        }
-    }
-
-    private void enforceAccessPermission() {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.ACCESS_NETWORK_STATE,
-                "ThrottleService");
-    }
-
-    private long ntpToWallTime(long ntpTime) {
-        // get time quickly without worrying about trusted state
-        long bestNow = mTime.hasCache() ? mTime.currentTimeMillis()
-                : System.currentTimeMillis();
-        long localNow = System.currentTimeMillis();
-        return localNow + (ntpTime - bestNow);
-    }
-
-    // TODO - fetch for the iface
-    // return time in the local, system wall time, correcting for the use of ntp
-
-    public long getResetTime(String iface) {
-        enforceAccessPermission();
-        long resetTime = 0;
-        if (mRecorder != null) {
-            resetTime = mRecorder.getPeriodEnd();
-        }
-        resetTime = ntpToWallTime(resetTime);
-        return resetTime;
-    }
-
-    // TODO - fetch for the iface
-    // return time in the local, system wall time, correcting for the use of ntp
-    public long getPeriodStartTime(String iface) {
-        long startTime = 0;
-        enforceAccessPermission();
-        if (mRecorder != null) {
-            startTime = mRecorder.getPeriodStart();
-        }
-        startTime = ntpToWallTime(startTime);
-        return startTime;
-    }
-    //TODO - a better name?  getCliffByteCountThreshold?
-    // TODO - fetch for the iface
-    public long getCliffThreshold(String iface, int cliff) {
-        enforceAccessPermission();
-        if (cliff == 1) {
-           return mPolicyThreshold.get();
-        }
-        return 0;
-    }
-    // TODO - a better name? getThrottleRate?
-    // TODO - fetch for the iface
-    public int getCliffLevel(String iface, int cliff) {
-        enforceAccessPermission();
-        if (cliff == 1) {
-            return mPolicyThrottleValue.get();
-        }
-        return 0;
-    }
-
-    public String getHelpUri() {
-        enforceAccessPermission();
-        return Settings.Global.getString(mContext.getContentResolver(),
-                    Settings.Global.THROTTLE_HELP_URI);
-    }
-
-    // TODO - fetch for the iface
-    public long getByteCount(String iface, int dir, int period, int ago) {
-        enforceAccessPermission();
-        if ((period == ThrottleManager.PERIOD_CYCLE) && (mRecorder != null)) {
-            if (dir == ThrottleManager.DIRECTION_TX) return mRecorder.getPeriodTx(ago);
-            if (dir == ThrottleManager.DIRECTION_RX) return mRecorder.getPeriodRx(ago);
-        }
-        return 0;
-    }
-
-    // TODO - a better name - getCurrentThrottleRate?
-    // TODO - fetch for the iface
-    public int getThrottle(String iface) {
-        enforceAccessPermission();
-        if (mThrottleIndex.get() == 1) {
-            return mPolicyThrottleValue.get();
-        }
-        return 0;
-    }
-
-    void systemReady() {
-        if (VDBG) Slog.v(TAG, "systemReady");
-        mContext.registerReceiver(
-            new BroadcastReceiver() {
-                @Override
-                public void onReceive(Context context, Intent intent) {
-                    dispatchPoll();
-                }
-            }, new IntentFilter(ACTION_POLL));
-
-        mContext.registerReceiver(
-            new BroadcastReceiver() {
-                @Override
-                public void onReceive(Context context, Intent intent) {
-                    dispatchReset();
-                }
-            }, new IntentFilter(ACTION_RESET));
-
-        // use a new thread as we don't want to stall the system for file writes
-        mThread = new HandlerThread(TAG);
-        mThread.start();
-        mHandler = new MyHandler(mThread.getLooper());
-        mHandler.obtainMessage(EVENT_REBOOT_RECOVERY).sendToTarget();
-
-        mInterfaceObserver = new InterfaceObserver(mHandler, EVENT_IFACE_UP, mIface);
-        try {
-            mNMService.registerObserver(mInterfaceObserver);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Could not register InterfaceObserver " + e);
-        }
-
-        mSettingsObserver = new SettingsObserver(mHandler, EVENT_POLICY_CHANGED);
-        mSettingsObserver.register(mContext);
-    }
-
-    void shutdown() {
-        // TODO: eventually connect with ShutdownThread to persist stats during
-        // graceful shutdown.
-
-        if (mThread != null) {
-            mThread.quit();
-        }
-
-        if (mSettingsObserver != null) {
-            mSettingsObserver.unregister(mContext);
-        }
-
-        if (mPollStickyBroadcast != null) {
-            mContext.removeStickyBroadcastAsUser(mPollStickyBroadcast, UserHandle.ALL);
-        }
-    }
-
-    void dispatchPoll() {
-        mHandler.obtainMessage(EVENT_POLL_ALARM).sendToTarget();
-    }
-
-    void dispatchReset() {
-        mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget();
-    }
-
-    private static final int EVENT_REBOOT_RECOVERY = 0;
-    private static final int EVENT_POLICY_CHANGED  = 1;
-    private static final int EVENT_POLL_ALARM      = 2;
-    private static final int EVENT_RESET_ALARM     = 3;
-    private static final int EVENT_IFACE_UP        = 4;
-    private class MyHandler extends Handler {
-        public MyHandler(Looper l) {
-            super(l);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-            case EVENT_REBOOT_RECOVERY:
-                onRebootRecovery();
-                break;
-            case EVENT_POLICY_CHANGED:
-                onPolicyChanged();
-                break;
-            case EVENT_POLL_ALARM:
-                onPollAlarm();
-                break;
-            case EVENT_RESET_ALARM:
-                onResetAlarm();
-                break;
-            case EVENT_IFACE_UP:
-                onIfaceUp();
-            }
-        }
-
-        private void onRebootRecovery() {
-            if (VDBG) Slog.v(TAG, "onRebootRecovery");
-            // check for sim change TODO
-            // reregister for notification of policy change
-
-            mThrottleIndex.set(THROTTLE_INDEX_UNINITIALIZED);
-
-            mRecorder = new DataRecorder(mContext, ThrottleService.this);
-
-            // get policy
-            mHandler.obtainMessage(EVENT_POLICY_CHANGED).sendToTarget();
-
-            // if we poll now we won't have network connectivity or even imsi access
-            // queue up a poll to happen in a little while - after ntp and imsi are avail
-            // TODO - make this callback based (ie, listen for notificaitons)
-            mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_POLL_ALARM),
-                    INITIAL_POLL_DELAY_SEC * 1000);
-        }
-
-        // check for new policy info (threshold limit/value/etc)
-        private void onPolicyChanged() {
-            boolean testing = SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true");
-
-            int pollingPeriod = mContext.getResources().getInteger(
-                    R.integer.config_datause_polling_period_sec);
-            mPolicyPollPeriodSec = Settings.Global.getInt(mContext.getContentResolver(),
-                    Settings.Global.THROTTLE_POLLING_SEC, pollingPeriod);
-
-            // TODO - remove testing stuff?
-            long defaultThreshold = mContext.getResources().getInteger(
-                    R.integer.config_datause_threshold_bytes);
-            int defaultValue = mContext.getResources().getInteger(
-                    R.integer.config_datause_throttle_kbitsps);
-            long threshold = Settings.Global.getLong(mContext.getContentResolver(),
-                    Settings.Global.THROTTLE_THRESHOLD_BYTES, defaultThreshold);
-            int value = Settings.Global.getInt(mContext.getContentResolver(),
-                    Settings.Global.THROTTLE_VALUE_KBITSPS, defaultValue);
-
-            mPolicyThreshold.set(threshold);
-            mPolicyThrottleValue.set(value);
-            if (testing) {
-                mPolicyPollPeriodSec = TESTING_POLLING_PERIOD_SEC;
-                mPolicyThreshold.set(TESTING_THRESHOLD);
-            }
-
-            mPolicyResetDay = Settings.Global.getInt(mContext.getContentResolver(),
-                    Settings.Global.THROTTLE_RESET_DAY, -1);
-            if (mPolicyResetDay == -1 ||
-                    ((mPolicyResetDay < 1) || (mPolicyResetDay > 28))) {
-                Random g = new Random();
-                mPolicyResetDay = 1 + g.nextInt(28); // 1-28
-                Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.THROTTLE_RESET_DAY, mPolicyResetDay);
-            }
-            if (mIface == null) {
-                mPolicyThreshold.set(0);
-            }
-
-            int defaultNotificationType = mContext.getResources().getInteger(
-                    R.integer.config_datause_notification_type);
-            mPolicyNotificationsAllowedMask = Settings.Global.getInt(mContext.getContentResolver(),
-                    Settings.Global.THROTTLE_NOTIFICATION_TYPE, defaultNotificationType);
-
-            final int maxNtpCacheAgeSec = Settings.Global.getInt(mContext.getContentResolver(),
-                    Settings.Global.THROTTLE_MAX_NTP_CACHE_AGE_SEC,
-                    (int) (MAX_NTP_CACHE_AGE / 1000));
-            mMaxNtpCacheAge = maxNtpCacheAgeSec * 1000;
-
-            if (VDBG || (mPolicyThreshold.get() != 0)) {
-                Slog.d(TAG, "onPolicyChanged testing=" + testing +", period=" +
-                        mPolicyPollPeriodSec + ", threshold=" + mPolicyThreshold.get() +
-                        ", value=" + mPolicyThrottleValue.get() + ", resetDay=" + mPolicyResetDay +
-                        ", noteType=" + mPolicyNotificationsAllowedMask + ", mMaxNtpCacheAge=" +
-                        mMaxNtpCacheAge);
-            }
-
-            // force updates
-            mThrottleIndex.set(THROTTLE_INDEX_UNINITIALIZED);
-
-            onResetAlarm();
-
-            onPollAlarm();
-
-            Intent broadcast = new Intent(ThrottleManager.POLICY_CHANGED_ACTION);
-            mContext.sendBroadcastAsUser(broadcast, UserHandle.ALL);
-        }
-
-        private void onPollAlarm() {
-            long now = SystemClock.elapsedRealtime();
-            long next = now + mPolicyPollPeriodSec * 1000;
-
-            // when trusted cache outdated, try refreshing
-            if (mTime.getCacheAge() > mMaxNtpCacheAge) {
-                if (mTime.forceRefresh()) {
-                    if (VDBG) Slog.d(TAG, "updated trusted time, reseting alarm");
-                    dispatchReset();
-                }
-            }
-
-            long incRead = 0;
-            long incWrite = 0;
-            try {
-                final NetworkStats stats = mNMService.getNetworkStatsSummaryDev();
-                final int index = stats.findIndex(mIface, NetworkStats.UID_ALL,
-                        NetworkStats.SET_DEFAULT, NetworkStats.TAG_NONE);
-
-                if (index != -1) {
-                    final NetworkStats.Entry entry = stats.getValues(index, null);
-                    incRead = entry.rxBytes - mLastRead;
-                    incWrite = entry.txBytes - mLastWrite;
-                } else {
-                    // missing iface, assume stats are 0
-                    Slog.w(TAG, "unable to find stats for iface " + mIface);
-                }
-
-                // handle iface resets - on some device the 3g iface comes and goes and gets
-                // totals reset to 0.  Deal with it
-                if ((incRead < 0) || (incWrite < 0)) {
-                    incRead += mLastRead;
-                    incWrite += mLastWrite;
-                    mLastRead = 0;
-                    mLastWrite = 0;
-                }
-            } catch (IllegalStateException e) {
-                Slog.e(TAG, "problem during onPollAlarm: " + e);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "problem during onPollAlarm: " + e);
-            }
-
-            // don't count this data if we're roaming.
-            boolean roaming = "true".equals(
-                    SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING));
-            if (!roaming) {
-                mRecorder.addData(incRead, incWrite);
-            }
-
-            long periodRx = mRecorder.getPeriodRx(0);
-            long periodTx = mRecorder.getPeriodTx(0);
-            long total = periodRx + periodTx;
-            if (VDBG || (mPolicyThreshold.get() != 0)) {
-                Slog.d(TAG, "onPollAlarm - roaming =" + roaming +
-                        ", read =" + incRead + ", written =" + incWrite + ", new total =" + total);
-            }
-            mLastRead += incRead;
-            mLastWrite += incWrite;
-
-            checkThrottleAndPostNotification(total);
-
-            Intent broadcast = new Intent(ThrottleManager.THROTTLE_POLL_ACTION);
-            broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_READ, periodRx);
-            broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_WRITE, periodTx);
-            broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_START, getPeriodStartTime(mIface));
-            broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, getResetTime(mIface));
-            mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
-            mPollStickyBroadcast = broadcast;
-
-            mAlarmManager.cancel(mPendingPollIntent);
-            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
-        }
-
-        private void onIfaceUp() {
-            // if we were throttled before, be sure and set it again - the iface went down
-            // (and may have disappeared all together) and these settings were lost
-            if (mThrottleIndex.get() == 1) {
-                try {
-                    mNMService.setInterfaceThrottle(mIface, -1, -1);
-                    mNMService.setInterfaceThrottle(mIface,
-                            mPolicyThrottleValue.get(), mPolicyThrottleValue.get());
-                } catch (Exception e) {
-                    Slog.e(TAG, "error setting Throttle: " + e);
-                }
-            }
-        }
-
-        private void checkThrottleAndPostNotification(long currentTotal) {
-            // is throttling enabled?
-            long threshold = mPolicyThreshold.get();
-            if (threshold == 0) {
-                clearThrottleAndNotification();
-                return;
-            }
-
-            // have we spoken with an ntp server yet?
-            // this is controversial, but we'd rather err towards not throttling
-            if (!mTime.hasCache()) {
-                Slog.w(TAG, "missing trusted time, skipping throttle check");
-                return;
-            }
-
-            // check if we need to throttle
-            if (currentTotal > threshold) {
-                if (mThrottleIndex.get() != 1) {
-                    mThrottleIndex.set(1);
-                    if (DBG) Slog.d(TAG, "Threshold " + threshold + " exceeded!");
-                    try {
-                        mNMService.setInterfaceThrottle(mIface,
-                                mPolicyThrottleValue.get(), mPolicyThrottleValue.get());
-                    } catch (Exception e) {
-                        Slog.e(TAG, "error setting Throttle: " + e);
-                    }
-
-                    mNotificationManager.cancel(R.drawable.stat_sys_throttled);
-
-                    postNotification(R.string.throttled_notification_title,
-                            R.string.throttled_notification_message,
-                            R.drawable.stat_sys_throttled,
-                            Notification.FLAG_ONGOING_EVENT);
-
-                    Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
-                    broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL,
-                            mPolicyThrottleValue.get());
-                    mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
-
-                } // else already up!
-            } else {
-                clearThrottleAndNotification();
-                if ((mPolicyNotificationsAllowedMask & NOTIFICATION_WARNING) != 0) {
-                    // check if we should warn about throttle
-                    // pretend we only have 1/2 the time remaining that we actually do
-                    // if our burn rate in the period so far would have us exceed the limit
-                    // in that 1/2 window, warn the user.
-                    // this gets more generous in the early to middle period and converges back
-                    // to the limit as we move toward the period end.
-
-                    // adding another factor - it must be greater than the total cap/4
-                    // else we may get false alarms very early in the period..  in the first
-                    // tenth of a percent of the period if we used more than a tenth of a percent
-                    // of the cap we'd get a warning and that's not desired.
-                    long start = mRecorder.getPeriodStart();
-                    long end = mRecorder.getPeriodEnd();
-                    long periodLength = end - start;
-                    long now = System.currentTimeMillis();
-                    long timeUsed = now - start;
-                    long warningThreshold = 2*threshold*timeUsed/(timeUsed+periodLength);
-                    if ((currentTotal > warningThreshold) && (currentTotal > threshold/4)) {
-                        if (mWarningNotificationSent == false) {
-                            mWarningNotificationSent = true;
-                            mNotificationManager.cancel(R.drawable.stat_sys_throttled);
-                            postNotification(R.string.throttle_warning_notification_title,
-                                    R.string.throttle_warning_notification_message,
-                                    R.drawable.stat_sys_throttled,
-                                    0);
-                        }
-                    } else {
-                        if (mWarningNotificationSent == true) {
-                            mNotificationManager.cancel(R.drawable.stat_sys_throttled);
-                            mWarningNotificationSent =false;
-                        }
-                    }
-                }
-            }
-        }
-
-        private void postNotification(int titleInt, int messageInt, int icon, int flags) {
-            Intent intent = new Intent();
-            // TODO - fix up intent
-            intent.setClassName("com.android.phone", "com.android.phone.DataUsage");
-            intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
-
-            PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0, intent, 0,
-                    null, UserHandle.CURRENT);
-
-            Resources r = Resources.getSystem();
-            CharSequence title = r.getText(titleInt);
-            CharSequence message = r.getText(messageInt);
-            if (mThrottlingNotification == null) {
-                mThrottlingNotification = new Notification();
-                mThrottlingNotification.when = 0;
-                // TODO -  fixup icon
-                mThrottlingNotification.icon = icon;
-                mThrottlingNotification.defaults &= ~Notification.DEFAULT_SOUND;
-            }
-            mThrottlingNotification.flags = flags;
-            mThrottlingNotification.tickerText = title;
-            mThrottlingNotification.setLatestEventInfo(mContext, title, message, pi);
-
-            mNotificationManager.notifyAsUser(null, mThrottlingNotification.icon,
-                    mThrottlingNotification, UserHandle.ALL);
-        }
-
-
-        private void clearThrottleAndNotification() {
-            if (mThrottleIndex.get() != THROTTLE_INDEX_UNTHROTTLED) {
-                mThrottleIndex.set(THROTTLE_INDEX_UNTHROTTLED);
-                try {
-                    mNMService.setInterfaceThrottle(mIface, -1, -1);
-                } catch (Exception e) {
-                    Slog.e(TAG, "error clearing Throttle: " + e);
-                }
-                Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
-                broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, -1);
-                mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
-                mNotificationManager.cancelAsUser(null, R.drawable.stat_sys_throttled,
-                        UserHandle.ALL);
-                mWarningNotificationSent = false;
-            }
-        }
-
-        private Calendar calculatePeriodEnd(long now) {
-            Calendar end = GregorianCalendar.getInstance();
-            end.setTimeInMillis(now);
-            int day = end.get(Calendar.DAY_OF_MONTH);
-            end.set(Calendar.DAY_OF_MONTH, mPolicyResetDay);
-            end.set(Calendar.HOUR_OF_DAY, 0);
-            end.set(Calendar.MINUTE, 0);
-            end.set(Calendar.SECOND, 0);
-            end.set(Calendar.MILLISECOND, 0);
-            if (day >= mPolicyResetDay) {
-                int month = end.get(Calendar.MONTH);
-                if (month == Calendar.DECEMBER) {
-                    end.set(Calendar.YEAR, end.get(Calendar.YEAR) + 1);
-                    month = Calendar.JANUARY - 1;
-                }
-                end.set(Calendar.MONTH, month + 1);
-            }
-
-            // TODO - remove!
-            if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
-                end = GregorianCalendar.getInstance();
-                end.setTimeInMillis(now);
-                end.add(Calendar.SECOND, TESTING_RESET_PERIOD_SEC);
-            }
-            return end;
-        }
-        private Calendar calculatePeriodStart(Calendar end) {
-            Calendar start = (Calendar)end.clone();
-            int month = end.get(Calendar.MONTH);
-            if (end.get(Calendar.MONTH) == Calendar.JANUARY) {
-                month = Calendar.DECEMBER + 1;
-                start.set(Calendar.YEAR, start.get(Calendar.YEAR) - 1);
-            }
-            start.set(Calendar.MONTH, month - 1);
-
-            // TODO - remove!!
-            if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
-                start = (Calendar)end.clone();
-                start.add(Calendar.SECOND, -TESTING_RESET_PERIOD_SEC);
-            }
-            return start;
-        }
-
-        private void onResetAlarm() {
-            if (VDBG || (mPolicyThreshold.get() != 0)) {
-                Slog.d(TAG, "onResetAlarm - last period had " + mRecorder.getPeriodRx(0) +
-                        " bytes read and " + mRecorder.getPeriodTx(0) + " written");
-            }
-
-            // when trusted cache outdated, try refreshing
-            if (mTime.getCacheAge() > mMaxNtpCacheAge) {
-                mTime.forceRefresh();
-            }
-
-            // as long as we have a trusted time cache, we always reset alarms,
-            // even if the refresh above failed.
-            if (mTime.hasCache()) {
-                final long now = mTime.currentTimeMillis();
-                Calendar end = calculatePeriodEnd(now);
-                Calendar start = calculatePeriodStart(end);
-
-                if (mRecorder.setNextPeriod(start, end)) {
-                    onPollAlarm();
-                }
-
-                mAlarmManager.cancel(mPendingResetIntent);
-                long offset = end.getTimeInMillis() - now;
-                // use Elapsed realtime so clock changes don't fool us.
-                mAlarmManager.set(AlarmManager.ELAPSED_REALTIME,
-                        SystemClock.elapsedRealtime() + offset,
-                        mPendingResetIntent);
-            } else {
-                if (VDBG) Slog.d(TAG, "no trusted time, not resetting period");
-            }
-        }
-    }
-
-    // records bytecount data for a given time and accumulates it into larger time windows
-    // for logging and other purposes
-    //
-    // since time can be changed (user or network action) we will have to track the time of the
-    // last recording and deal with it.
-    private static class DataRecorder {
-        long[] mPeriodRxData;
-        long[] mPeriodTxData;
-        int mCurrentPeriod;
-        int mPeriodCount;
-
-        Calendar mPeriodStart;
-        Calendar mPeriodEnd;
-
-        ThrottleService mParent;
-        Context mContext;
-        String mImsi = null;
-
-        TelephonyManager mTelephonyManager;
-
-        DataRecorder(Context context, ThrottleService parent) {
-            mContext = context;
-            mParent = parent;
-
-            mTelephonyManager = (TelephonyManager)mContext.getSystemService(
-                    Context.TELEPHONY_SERVICE);
-
-            synchronized (mParent) {
-                mPeriodCount = 6;
-                mPeriodRxData = new long[mPeriodCount];
-                mPeriodTxData = new long[mPeriodCount];
-
-                mPeriodStart = Calendar.getInstance();
-                mPeriodEnd = Calendar.getInstance();
-
-                retrieve();
-            }
-        }
-
-        boolean setNextPeriod(Calendar start, Calendar end) {
-            // TODO - how would we deal with a dual-IMSI device?
-            checkForSubscriberId();
-            boolean startNewPeriod = true;
-
-            if (start.equals(mPeriodStart) && end.equals(mPeriodEnd)) {
-                // same endpoints - keep collecting
-                if (VDBG) {
-                    Slog.d(TAG, "same period (" + start.getTimeInMillis() + "," +
-                            end.getTimeInMillis() +") - ammending data");
-                }
-                startNewPeriod = false;
-            } else {
-                if (VDBG) {
-                    if(start.equals(mPeriodEnd) || start.after(mPeriodEnd)) {
-                        Slog.d(TAG, "next period (" + start.getTimeInMillis() + "," +
-                                end.getTimeInMillis() + ") - old end was " +
-                                mPeriodEnd.getTimeInMillis() + ", following");
-                    } else {
-                        Slog.d(TAG, "new period (" + start.getTimeInMillis() + "," +
-                                end.getTimeInMillis() + ") replacing old (" +
-                                mPeriodStart.getTimeInMillis() + "," +
-                                mPeriodEnd.getTimeInMillis() + ")");
-                    }
-                }
-                synchronized (mParent) {
-                    ++mCurrentPeriod;
-                    if (mCurrentPeriod >= mPeriodCount) mCurrentPeriod = 0;
-                    mPeriodRxData[mCurrentPeriod] = 0;
-                    mPeriodTxData[mCurrentPeriod] = 0;
-                }
-            }
-            setPeriodStart(start);
-            setPeriodEnd(end);
-            record();
-            return startNewPeriod;
-        }
-
-        public long getPeriodEnd() {
-            synchronized (mParent) {
-                return mPeriodEnd.getTimeInMillis();
-            }
-        }
-
-        private void setPeriodEnd(Calendar end) {
-            synchronized (mParent) {
-                mPeriodEnd = end;
-            }
-        }
-
-        public long getPeriodStart() {
-            synchronized (mParent) {
-                return mPeriodStart.getTimeInMillis();
-            }
-        }
-
-        private void setPeriodStart(Calendar start) {
-            synchronized (mParent) {
-                mPeriodStart = start;
-            }
-        }
-
-        public int getPeriodCount() {
-            synchronized (mParent) {
-                return mPeriodCount;
-            }
-        }
-
-        private void zeroData(int field) {
-            synchronized (mParent) {
-                for(int period = 0; period<mPeriodCount; period++) {
-                    mPeriodRxData[period] = 0;
-                    mPeriodTxData[period] = 0;
-                }
-                mCurrentPeriod = 0;
-            }
-
-        }
-
-        // if time moves backward accumulate all read/write that's lost into the now
-        // otherwise time moved forward.
-        void addData(long bytesRead, long bytesWritten) {
-            checkForSubscriberId();
-
-            synchronized (mParent) {
-                mPeriodRxData[mCurrentPeriod] += bytesRead;
-                mPeriodTxData[mCurrentPeriod] += bytesWritten;
-            }
-            record();
-        }
-
-        private File getDataFile() {
-            File dataDir = Environment.getDataDirectory();
-            File throttleDir = new File(dataDir, "system/throttle");
-            throttleDir.mkdirs();
-            String mImsi = mTelephonyManager.getSubscriberId();
-            File dataFile;
-            if (mImsi == null) {
-                dataFile = useMRUFile(throttleDir);
-                if (VDBG) Slog.v(TAG, "imsi not available yet, using " + dataFile);
-            } else {
-                String imsiHash = Integer.toString(mImsi.hashCode());
-                dataFile = new File(throttleDir, imsiHash);
-            }
-            // touch the file so it's not LRU
-            dataFile.setLastModified(System.currentTimeMillis());
-            checkAndDeleteLRUDataFile(throttleDir);
-            return dataFile;
-        }
-
-        // TODO - get broadcast (TelephonyIntents.ACTION_SIM_STATE_CHANGED) instead of polling
-        private void checkForSubscriberId() {
-            if (mImsi != null) return;
-
-            mImsi = mTelephonyManager.getSubscriberId();
-            if (mImsi == null) return;
-
-            if (VDBG) Slog.d(TAG, "finally have imsi - retreiving data");
-            retrieve();
-        }
-
-        private final static int MAX_SIMS_SUPPORTED = 3;
-
-        private void checkAndDeleteLRUDataFile(File dir) {
-            File[] files = dir.listFiles();
-
-            if (files == null || files.length <= MAX_SIMS_SUPPORTED) return;
-            if (DBG) Slog.d(TAG, "Too many data files");
-            do {
-                File oldest = null;
-                for (File f : files) {
-                    if ((oldest == null) || (oldest.lastModified() > f.lastModified())) {
-                        oldest = f;
-                    }
-                }
-                if (oldest == null) return;
-                if (DBG) Slog.d(TAG, " deleting " + oldest);
-                oldest.delete();
-                files = dir.listFiles();
-            } while (files.length > MAX_SIMS_SUPPORTED);
-        }
-
-        private File useMRUFile(File dir) {
-            File newest = null;
-            File[] files = dir.listFiles();
-
-            if (files != null) {
-                for (File f : files) {
-                    if ((newest == null) || (newest.lastModified() < f.lastModified())) {
-                        newest = f;
-                    }
-                }
-            }
-            if (newest == null) {
-                newest = new File(dir, "temp");
-            }
-            return newest;
-        }
-
-
-        private static final int DATA_FILE_VERSION = 1;
-
-        private void record() {
-            // 1 int version
-            // 1 int mPeriodCount
-            // 13*6 long[PERIOD_COUNT] mPeriodRxData
-            // 13*6 long[PERIOD_COUNT] mPeriodTxData
-            // 1  int mCurrentPeriod
-            // 13 long periodStartMS
-            // 13 long periodEndMS
-            // 200 chars max
-            StringBuilder builder = new StringBuilder();
-            builder.append(DATA_FILE_VERSION);
-            builder.append(":");
-            builder.append(mPeriodCount);
-            builder.append(":");
-            for(int i = 0; i < mPeriodCount; i++) {
-                builder.append(mPeriodRxData[i]);
-                builder.append(":");
-            }
-            for(int i = 0; i < mPeriodCount; i++) {
-                builder.append(mPeriodTxData[i]);
-                builder.append(":");
-            }
-            builder.append(mCurrentPeriod);
-            builder.append(":");
-            builder.append(mPeriodStart.getTimeInMillis());
-            builder.append(":");
-            builder.append(mPeriodEnd.getTimeInMillis());
-
-            BufferedWriter out = null;
-            try {
-                out = new BufferedWriter(new FileWriter(getDataFile()), 256);
-                out.write(builder.toString());
-            } catch (IOException e) {
-                Slog.e(TAG, "Error writing data file");
-                return;
-            } finally {
-                if (out != null) {
-                    try {
-                        out.close();
-                    } catch (Exception e) {}
-                }
-            }
-        }
-
-        private void retrieve() {
-            // clean out any old data first.  If we fail to read we don't want old stuff
-            zeroData(0);
-
-            File f = getDataFile();
-            byte[] buffer;
-            FileInputStream s = null;
-            try {
-                buffer = new byte[(int)f.length()];
-                s = new FileInputStream(f);
-                s.read(buffer);
-            } catch (IOException e) {
-                Slog.e(TAG, "Error reading data file");
-                return;
-            } finally {
-                if (s != null) {
-                    try {
-                        s.close();
-                    } catch (Exception e) {}
-                }
-            }
-            String data = new String(buffer);
-            if (data == null || data.length() == 0) {
-                if (DBG) Slog.d(TAG, "data file empty");
-                return;
-            }
-            String[] parsed = data.split(":");
-            int parsedUsed = 0;
-            if (parsed.length < 6) {
-                Slog.e(TAG, "reading data file with insufficient length - ignoring");
-                return;
-            }
-
-            int periodCount;
-            long[] periodRxData;
-            long[] periodTxData;
-            int currentPeriod;
-            Calendar periodStart;
-            Calendar periodEnd;
-            try {
-                if (Integer.parseInt(parsed[parsedUsed++]) != DATA_FILE_VERSION) {
-                    Slog.e(TAG, "reading data file with bad version - ignoring");
-                    return;
-                }
-
-                periodCount = Integer.parseInt(parsed[parsedUsed++]);
-                if (parsed.length != 5 + (2 * periodCount)) {
-                    Slog.e(TAG, "reading data file with bad length (" + parsed.length +
-                            " != " + (5 + (2 * periodCount)) + ") - ignoring");
-                    return;
-                }
-                periodRxData = new long[periodCount];
-                for (int i = 0; i < periodCount; i++) {
-                    periodRxData[i] = Long.parseLong(parsed[parsedUsed++]);
-                }
-                periodTxData = new long[periodCount];
-                for (int i = 0; i < periodCount; i++) {
-                    periodTxData[i] = Long.parseLong(parsed[parsedUsed++]);
-                }
-
-                currentPeriod = Integer.parseInt(parsed[parsedUsed++]);
-
-                periodStart = new GregorianCalendar();
-                periodStart.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
-                periodEnd = new GregorianCalendar();
-                periodEnd.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
-            } catch (Exception e) {
-                Slog.e(TAG, "Error parsing data file - ignoring");
-                return;
-            }
-            synchronized (mParent) {
-                mPeriodCount = periodCount;
-                mPeriodRxData = periodRxData;
-                mPeriodTxData = periodTxData;
-                mCurrentPeriod = currentPeriod;
-                mPeriodStart = periodStart;
-                mPeriodEnd = periodEnd;
-            }
-        }
-
-        long getPeriodRx(int which) {
-            synchronized (mParent) {
-                if (which > mPeriodCount) return 0;
-                which = mCurrentPeriod - which;
-                if (which < 0) which += mPeriodCount;
-                return mPeriodRxData[which];
-            }
-        }
-        long getPeriodTx(int which) {
-            synchronized (mParent) {
-                if (which > mPeriodCount) return 0;
-                which = mCurrentPeriod - which;
-                if (which < 0) which += mPeriodCount;
-                return mPeriodTxData[which];
-            }
-        }
-    }
-
-    @Override
-    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.DUMP)
-                != PackageManager.PERMISSION_GRANTED) {
-            pw.println("Permission Denial: can't dump ThrottleService " +
-                    "from from pid=" + Binder.getCallingPid() + ", uid=" +
-                    Binder.getCallingUid());
-            return;
-        }
-        pw.println();
-
-        pw.println("The threshold is " + mPolicyThreshold.get() +
-                ", after which you experince throttling to " +
-                mPolicyThrottleValue.get() + "kbps");
-        pw.println("Current period is " +
-                (mRecorder.getPeriodEnd() - mRecorder.getPeriodStart())/1000 + " seconds long " +
-                "and ends in " + (getResetTime(mIface) - System.currentTimeMillis()) / 1000 +
-                " seconds.");
-        pw.println("Polling every " + mPolicyPollPeriodSec + " seconds");
-        pw.println("Current Throttle Index is " + mThrottleIndex.get());
-        pw.println("mMaxNtpCacheAge=" + mMaxNtpCacheAge);
-
-        for (int i = 0; i < mRecorder.getPeriodCount(); i++) {
-            pw.println(" Period[" + i + "] - read:" + mRecorder.getPeriodRx(i) + ", written:" +
-                    mRecorder.getPeriodTx(i));
-        }
-    }
-}
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/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index f1739d5..12c14bf 100644
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -312,7 +312,7 @@
     private final IBatteryStats mBatteryStats;
 
     // only modified on handler thread
-    private int[] mClientUids = new int[0];
+    private WorkSource mClientSource = new WorkSource();
 
     private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() {
         @Override
@@ -805,11 +805,7 @@
 
         if (request.reportLocation) {
             // update client uids
-            int[] uids = new int[source.size()];
-            for (int i=0; i < source.size(); i++) {
-                uids[i] = source.get(i);
-            }
-            updateClientUids(uids);
+            updateClientUids(source);
 
             mFixInterval = (int) request.interval;
 
@@ -831,7 +827,7 @@
                 startNavigating();
             }
         } else {
-            updateClientUids(new int[0]);
+            updateClientUids(new WorkSource());
 
             stopNavigating();
             mAlarmManager.cancel(mWakeupIntent);
@@ -859,47 +855,48 @@
         }
     }
 
-    private void updateClientUids(int[] uids) {
-        // Find uid's that were not previously tracked
-        for (int uid1 : uids) {
-            boolean newUid = true;
-            for (int uid2 : mClientUids) {
-                if (uid1 == uid2) {
-                    newUid = false;
-                    break;
-                }
-            }
-            if (newUid) {
+    private void updateClientUids(WorkSource source) {
+        // Update work source.
+        WorkSource[] changes = mClientSource.setReturningDiffs(source);
+        if (changes == null) {
+            return;
+        }
+        WorkSource newWork = changes[0];
+        WorkSource goneWork = changes[1];
+
+        // Update sources that were not previously tracked.
+        if (newWork != null) {
+            int lastuid = -1;
+            for (int i=0; i<newWork.size(); i++) {
                 try {
-                    mAppOpsService.startOperation(AppOpsManager.OP_GPS, uid1, null);
-                    mBatteryStats.noteStartGps(uid1);
+                    int uid = newWork.get(i);
+                    mAppOpsService.startOperation(AppOpsManager.OP_GPS, uid, newWork.getName(i));
+                    if (uid != lastuid) {
+                        lastuid = uid;
+                        mBatteryStats.noteStartGps(uid);
+                    }
                 } catch (RemoteException e) {
                     Log.w(TAG, "RemoteException", e);
                 }
             }
         }
 
-        // Find uid'd that were tracked but have now disappeared
-        for (int uid1 : mClientUids) {
-            boolean oldUid = true;
-            for (int uid2 : uids) {
-                if (uid1 == uid2) {
-                    oldUid = false;
-                    break;
-                }
-            }
-            if (oldUid) {
+        // Update sources that are no longer tracked.
+        if (goneWork != null) {
+            int lastuid = -1;
+            for (int i=0; i<goneWork.size(); i++) {
                 try {
-                    mBatteryStats.noteStopGps(uid1);
-                    mAppOpsService.finishOperation(AppOpsManager.OP_GPS, uid1, null);
+                    int uid = goneWork.get(i);
+                    mAppOpsService.finishOperation(AppOpsManager.OP_GPS, uid, goneWork.getName(i));
+                    if (uid != lastuid) {
+                        lastuid = uid;
+                        mBatteryStats.noteStopGps(uid);
+                    }
                 } catch (RemoteException e) {
                     Log.w(TAG, "RemoteException", e);
                 }
             }
         }
-
-        // save current uids
-        mClientUids = uids;
     }
 
     @Override
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/services/java/com/android/server/wm/DimLayer.java b/services/java/com/android/server/wm/DimLayer.java
index 4c7add9..88efe2e 100644
--- a/services/java/com/android/server/wm/DimLayer.java
+++ b/services/java/com/android/server/wm/DimLayer.java
@@ -84,6 +84,17 @@
         return mTargetAlpha;
     }
 
+    void setLayer(int layer) {
+        if (mLayer != layer) {
+            mLayer = layer;
+            mDimSurface.setLayer(layer);
+        }
+    }
+
+    int getLayer() {
+        return mLayer;
+    }
+
     private void setAlpha(float alpha) {
         if (mAlpha != alpha) {
             if (DEBUG) Slog.v(TAG, "setAlpha alpha=" + alpha);
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index d5144fb..d42221e 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -573,10 +573,13 @@
 
                 final DimLayer dimAnimator = displayAnimator.mDimAnimator;
                 final WindowStateAnimator winAnimator = displayAnimator.mDimWinAnimator;
+                final int dimLayer;
                 final float dimAmount;
                 if (winAnimator == null) {
+                    dimLayer = dimAnimator.getLayer();
                     dimAmount = 0;
                 } else {
+                    dimLayer = winAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
                     dimAmount = winAnimator.mWin.mAttrs.dimAmount;
                 }
                 final float targetAlpha = dimAnimator.getTargetAlpha();
@@ -590,9 +593,10 @@
                         if (targetAlpha > dimAmount) {
                             duration = getDimBehindFadeDuration(duration);
                         }
-                        dimAnimator.show(winAnimator.mAnimLayer -
-                                WindowManagerService.LAYER_OFFSET_DIM, dimAmount, duration);
+                        dimAnimator.show(dimLayer, dimAmount, duration);
                     }
+                } else if (dimAnimator.getLayer() != dimLayer) {
+                    dimAnimator.setLayer(dimLayer);
                 }
                 if (dimAnimator.isAnimating()) {
                     if (!mService.okToDisplay()) {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index a488b84..ff2dc0f 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -4880,6 +4880,7 @@
         ShutdownThread.rebootSafeMode(mContext, confirm);
     }
 
+    @Override
     public void setInputFilter(IInputFilter filter) {
         if (!checkCallingPermission(android.Manifest.permission.FILTER_EVENTS, "setInputFilter()")) {
             throw new SecurityException("Requires FILTER_EVENTS permission");
@@ -5127,6 +5128,7 @@
         }
     }
 
+    @Override
     public void setInTouchMode(boolean mode) {
         synchronized(mWindowMap) {
             mInTouchMode = mode;
@@ -5181,6 +5183,7 @@
         }
     }
 
+    @Override
     public void setStrictModeVisualIndicatorPreference(String value) {
         SystemProperties.set(StrictMode.VISUAL_PROPERTY, value);
     }
@@ -5355,6 +5358,7 @@
      * @param rotation The desired rotation to freeze to, or -1 to use the
      * current rotation.
      */
+    @Override
     public void freezeRotation(int rotation) {
         if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
                 "freezeRotation()")) {
@@ -5376,6 +5380,7 @@
      * Thaw rotation changes.  (Disable "rotation lock".)
      * Persists across reboots.
      */
+    @Override
     public void thawRotation() {
         if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
                 "thawRotation()")) {
@@ -5395,6 +5400,7 @@
      * such that the current rotation might need to be updated, such as when the
      * device is docked or rotated into a new posture.
      */
+    @Override
     public void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) {
         updateRotationUnchecked(alwaysSendConfiguration, forceRelayout);
     }
@@ -5405,7 +5411,8 @@
      * This can be used to prevent rotation changes from occurring while the user is
      * performing certain operations, such as drag and drop.
      *
-     * This call nests and must be matched by an equal number of calls to {@link #resumeRotation}.
+     * This call nests and must be matched by an equal number of calls to
+     * {@link #resumeRotationLocked}.
      */
     void pauseRotationLocked() {
         mDeferredRotationPauseCount += 1;
@@ -5639,6 +5646,7 @@
      *
      * @return A {@link Gravity} value for placing the options menu window
      */
+    @Override
     public int getPreferredOptionsPanelGravity() {
         synchronized (mWindowMap) {
             final int rotation = getRotation();
@@ -5658,19 +5666,19 @@
                     case Surface.ROTATION_270:
                         return Gravity.START | Gravity.BOTTOM;
                 }
-            } else {
-                // On devices with a natural orientation of landscape
-                switch (rotation) {
-                    default:
-                    case Surface.ROTATION_0:
-                        return Gravity.RIGHT | Gravity.BOTTOM;
-                    case Surface.ROTATION_90:
-                        return Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
-                    case Surface.ROTATION_180:
-                        return Gravity.START | Gravity.BOTTOM;
-                    case Surface.ROTATION_270:
-                        return Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
-                }
+            }
+
+            // On devices with a natural orientation of landscape
+            switch (rotation) {
+                default:
+                case Surface.ROTATION_0:
+                    return Gravity.RIGHT | Gravity.BOTTOM;
+                case Surface.ROTATION_90:
+                    return Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+                case Surface.ROTATION_180:
+                    return Gravity.START | Gravity.BOTTOM;
+                case Surface.ROTATION_270:
+                    return Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
             }
         }
     }
@@ -5685,6 +5693,7 @@
      * @see com.android.server.wm.ViewServer
      * @see com.android.server.wm.ViewServer#VIEW_SERVER_DEFAULT_PORT
      */
+    @Override
     public boolean startViewServer(int port) {
         if (isSystemSecure()) {
             return false;
@@ -5731,6 +5740,7 @@
      *
      * @see com.android.server.wm.ViewServer
      */
+    @Override
     public boolean stopViewServer() {
         if (isSystemSecure()) {
             return false;
@@ -5753,6 +5763,7 @@
      *
      * @see com.android.server.wm.ViewServer
      */
+    @Override
     public boolean isViewServerRunning() {
         if (isSystemSecure()) {
             return false;
@@ -6401,10 +6412,11 @@
     // -------------------------------------------------------------
     // Input Events and Focus Management
     // -------------------------------------------------------------
-    
+
     final InputMonitor mInputMonitor = new InputMonitor(this);
     private boolean mEventDispatchingEnabled;
 
+    @Override
     public void pauseKeyDispatching(IBinder _token) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "pauseKeyDispatching()")) {
@@ -6419,6 +6431,7 @@
         }
     }
 
+    @Override
     public void resumeKeyDispatching(IBinder _token) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "resumeKeyDispatching()")) {
@@ -6433,6 +6446,7 @@
         }
     }
 
+    @Override
     public void setEventDispatching(boolean enabled) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "setEventDispatching()")) {
@@ -6448,6 +6462,7 @@
         }
     }
 
+    @Override
     public IBinder getFocusedWindowToken() {
         if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO,
                 "getFocusedWindowToken()")) {
@@ -7272,6 +7287,7 @@
         performLayoutAndPlaceSurfacesLocked();
     }
 
+    @Override
     public boolean hasSystemNavBar() {
         return mPolicy.hasSystemNavBar();
     }
@@ -7495,7 +7511,7 @@
             // applications.  Don't do any window layout until we have it.
             return;
         }
-        
+
         if (!mDisplayReady) {
             // Not yet initialized, nothing to do.
             return;
@@ -7504,7 +7520,7 @@
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmLayout");
         mInLayout = true;
         boolean recoveringMemory = false;
-        
+
         try {
             if (mForceRemoves != null) {
                 recoveringMemory = true;
@@ -7717,7 +7733,7 @@
                 attachedBehindDream = behindDream;
             }
         }
-        
+
         // Window frames may have changed.  Tell the input dispatcher about it.
         mInputMonitor.setUpdateInputWindowsNeededLw();
         if (updateInputWindows) {
@@ -7741,7 +7757,7 @@
                 // XXX should probably keep timeout from
                 // when we first froze the display.
                 mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
-                mH.sendEmptyMessageDelayed(H.WINDOW_FREEZE_TIMEOUT, 
+                mH.sendEmptyMessageDelayed(H.WINDOW_FREEZE_TIMEOUT,
                         WINDOW_FREEZE_TIMEOUT_DURATION);
             }
         }
@@ -9304,12 +9320,12 @@
         mH.sendEmptyMessageDelayed(H.FORCE_GC, 2000);
 
         mScreenFrozenLock.release();
-        
+
         if (updateRotation) {
             if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-rotate rotation");
             configChanged |= updateRotationUncheckedLocked(false);
         }
-        
+
         if (configChanged) {
             mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
         }
@@ -9414,7 +9430,7 @@
             }
         }
     }
- 
+
     @Override
     public void reevaluateStatusBarVisibility() {
         synchronized (mWindowMap) {
@@ -9475,7 +9491,7 @@
     public void lockNow(Bundle options) {
         mPolicy.lockNow(options);
     }
-    
+
     @Override
     public boolean isSafeModeEnabled() {
         return mSafeMode;
diff --git a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
deleted file mode 100644
index 569acee..0000000
--- a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * Copyright (C) 2011 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 com.android.server;
-
-import static android.net.NetworkStats.SET_DEFAULT;
-import static android.net.NetworkStats.TAG_NONE;
-import static android.net.NetworkStats.UID_ALL;
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.eq;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.expectLastCall;
-import static org.easymock.EasyMock.isA;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.reset;
-import static org.easymock.EasyMock.verify;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.net.INetworkManagementEventObserver;
-import android.net.NetworkStats;
-import android.net.ThrottleManager;
-import android.os.IBinder;
-import android.os.INetworkManagementService;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.Suppress;
-import android.text.format.DateUtils;
-import android.util.Log;
-import android.util.TrustedTime;
-
-import java.util.concurrent.Future;
-
-/**
- * Tests for {@link ThrottleService}.
- */
-@LargeTest
-public class ThrottleServiceTest extends AndroidTestCase {
-    private static final String TAG = "ThrottleServiceTest";
-
-    private static final long MB_IN_BYTES = 1024 * 1024;
-
-    private static final int TEST_KBITPS = 222;
-    private static final int TEST_RESET_DAY = 11;
-
-    private static final String TEST_IFACE = "test0";
-
-    private BroadcastInterceptingContext mWatchingContext;
-    private INetworkManagementService mMockNMService;
-    private TrustedTime mMockTime;
-
-    private ThrottleService mThrottleService;
-
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-
-        mWatchingContext = new BroadcastInterceptingContext(getContext());
-
-        mMockNMService = createMock(INetworkManagementService.class);
-        mMockTime = createMock(TrustedTime.class);
-
-        mThrottleService = new ThrottleService(
-                mWatchingContext, mMockNMService, mMockTime, TEST_IFACE);
-    }
-
-    @Override
-    public void tearDown() throws Exception {
-        mWatchingContext = null;
-        mMockNMService = null;
-
-        mThrottleService.shutdown();
-        mThrottleService = null;
-
-        clearThrottlePolicy();
-
-        super.tearDown();
-    }
-
-    public void testNoPolicyNotThrottled() throws Exception {
-        expectTimeCurrent();
-        expectSystemReady();
-
-        // provide stats without policy, verify not throttled
-        expectGetInterfaceCounter(1 * MB_IN_BYTES, 2 * MB_IN_BYTES);
-        expectSetInterfaceThrottle(-1, -1);
-
-        replay(mMockTime, mMockNMService);
-        systemReady();
-        verify(mMockTime, mMockNMService);
-    }
-
-    public void testUnderLimitNotThrottled() throws Exception {
-        setThrottlePolicy(200 * MB_IN_BYTES, TEST_KBITPS, TEST_RESET_DAY);
-
-        expectTimeCurrent();
-        expectSystemReady();
-
-        // provide stats under limits, and verify not throttled
-        expectGetInterfaceCounter(1 * MB_IN_BYTES, 2 * MB_IN_BYTES);
-        expectSetInterfaceThrottle(-1, -1);
-
-        replay(mMockTime, mMockNMService);
-        systemReady();
-        verify(mMockTime, mMockNMService);
-    }
-
-    public void testOverLimitThrottled() throws Exception {
-        setThrottlePolicy(200 * MB_IN_BYTES, TEST_KBITPS, TEST_RESET_DAY);
-
-        expectTimeCurrent();
-        expectSystemReady();
-
-        // provide stats over limits, and verify throttled
-        expectGetInterfaceCounter(500 * MB_IN_BYTES, 600 * MB_IN_BYTES);
-        expectSetInterfaceThrottle(TEST_KBITPS, TEST_KBITPS);
-
-        replay(mMockTime, mMockNMService);
-        systemReady();
-        verify(mMockTime, mMockNMService);
-    }
-
-    public void testUnderThenOverLimitThrottled() throws Exception {
-        setThrottlePolicy(201 * MB_IN_BYTES, TEST_KBITPS, TEST_RESET_DAY);
-
-        expectTimeCurrent();
-        expectSystemReady();
-
-        // provide stats right under 201MB limit, verify not throttled
-        expectGetInterfaceCounter(100 * MB_IN_BYTES, 100 * MB_IN_BYTES);
-        expectSetInterfaceThrottle(-1, -1);
-
-        replay(mMockTime, mMockNMService);
-        systemReady();
-        verify(mMockTime, mMockNMService);
-        reset(mMockTime, mMockNMService);
-
-        expectTimeCurrent();
-
-        // adjust usage to bump over limit, verify throttle kicks in
-        expectGetInterfaceCounter(105 * MB_IN_BYTES, 100 * MB_IN_BYTES);
-        expectSetInterfaceThrottle(TEST_KBITPS, TEST_KBITPS);
-
-        // and kick poll event which should throttle
-        replay(mMockTime, mMockNMService);
-        forceServicePoll();
-        verify(mMockTime, mMockNMService);
-    }
-
-    public void testUpdatedPolicyThrottled() throws Exception {
-        setThrottlePolicy(500 * MB_IN_BYTES, TEST_KBITPS, TEST_RESET_DAY);
-
-        expectTimeCurrent();
-        expectSystemReady();
-
-        // provide stats under limit, verify not throttled
-        expectGetInterfaceCounter(50 * MB_IN_BYTES, 50 * MB_IN_BYTES);
-        expectSetInterfaceThrottle(-1, -1);
-
-        replay(mMockTime, mMockNMService);
-        systemReady();
-        verify(mMockTime, mMockNMService);
-        reset(mMockTime, mMockNMService);
-
-        expectTimeCurrent();
-
-        // provide same stats, but verify that modified policy will throttle
-        expectGetInterfaceCounter(50 * MB_IN_BYTES, 50 * MB_IN_BYTES);
-        expectSetInterfaceThrottle(TEST_KBITPS, TEST_KBITPS);
-
-        replay(mMockTime, mMockNMService);
-
-        // now adjust policy to bump usage over limit
-        setThrottlePolicy(5 * MB_IN_BYTES, TEST_KBITPS, TEST_RESET_DAY);
-
-        // and wait for policy updated broadcast
-        mWatchingContext.nextBroadcastIntent(ThrottleManager.POLICY_CHANGED_ACTION).get();
-
-        verify(mMockTime, mMockNMService);
-    }
-
-    public void testWithPolicyOverLimitThrottledAndRemovedAfterCycle() throws Exception {
-        setThrottlePolicy(90 * MB_IN_BYTES, TEST_KBITPS, TEST_RESET_DAY);
-
-        final long baseTime = System.currentTimeMillis();
-
-        expectTime(baseTime);
-        expectSystemReady();
-
-        // provide stats over limit, verify throttle kicks in
-        expectGetInterfaceCounter(50 * MB_IN_BYTES, 50 * MB_IN_BYTES);
-        expectSetInterfaceThrottle(TEST_KBITPS, TEST_KBITPS);
-
-        replay(mMockTime, mMockNMService);
-        systemReady();
-        verify(mMockTime, mMockNMService);
-        reset(mMockTime, mMockNMService);
-
-        // pretend that time has jumped forward two months
-        expectTime(baseTime + DateUtils.WEEK_IN_MILLIS * 8);
-
-        // provide slightly updated stats, but verify throttle is removed
-        expectGetInterfaceCounter(60 * MB_IN_BYTES, 60 * MB_IN_BYTES);
-        expectSetInterfaceThrottle(-1, -1);
-
-        // and kick poll event which should throttle
-        replay(mMockTime, mMockNMService);
-        forceServiceReset();
-        verify(mMockTime, mMockNMService);
-    }
-
-    @Suppress
-    public void testReturnStats() throws Exception {
-        final IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-        final INetworkManagementService nmService = INetworkManagementService.Stub.asInterface(b);
-
-        // test is currently no-op, just exercises stats apis
-        Log.d(TAG, nmService.getNetworkStatsSummaryDev().toString());
-        Log.d(TAG, nmService.getNetworkStatsSummaryXt().toString());
-        Log.d(TAG, nmService.getNetworkStatsDetail().toString());
-    }
-
-    /**
-     * Persist the given {@link ThrottleService} policy into {@link Settings}.
-     */
-    public void setThrottlePolicy(long thresholdBytes, int valueKbitps, int resetDay) {
-        final ContentResolver resolver = getContext().getContentResolver();
-        Settings.Global.putLong(resolver, Settings.Global.THROTTLE_THRESHOLD_BYTES, thresholdBytes);
-        Settings.Global.putInt(resolver, Settings.Global.THROTTLE_VALUE_KBITSPS, valueKbitps);
-        Settings.Global.putInt(resolver, Settings.Global.THROTTLE_RESET_DAY, resetDay);
-    }
-
-    /**
-     * Clear any {@link ThrottleService} policy from {@link Settings}.
-     */
-    public void clearThrottlePolicy() {
-        final ContentResolver resolver = getContext().getContentResolver();
-        Settings.Global.putString(resolver, Settings.Global.THROTTLE_THRESHOLD_BYTES, null);
-        Settings.Global.putString(resolver, Settings.Global.THROTTLE_VALUE_KBITSPS, null);
-        Settings.Global.putString(resolver, Settings.Global.THROTTLE_RESET_DAY, null);
-    }
-
-    /**
-     * Expect any {@link TrustedTime} mock calls, and respond with
-     * {@link System#currentTimeMillis()}.
-     */
-    public void expectTimeCurrent() throws Exception {
-        expectTime(System.currentTimeMillis());
-    }
-
-    /**
-     * Expect any {@link TrustedTime} mock calls, and respond with the given
-     * time in response to {@link TrustedTime#currentTimeMillis()}.
-     */
-    public void expectTime(long currentTime) throws Exception {
-        expect(mMockTime.forceRefresh()).andReturn(false).anyTimes();
-        expect(mMockTime.hasCache()).andReturn(true).anyTimes();
-        expect(mMockTime.currentTimeMillis()).andReturn(currentTime).anyTimes();
-        expect(mMockTime.getCacheAge()).andReturn(0L).anyTimes();
-        expect(mMockTime.getCacheCertainty()).andReturn(0L).anyTimes();
-    }
-
-    /**
-     * Expect {@link ThrottleService#systemReady()} generated calls, such as
-     * connecting with {@link NetworkManagementService} mock.
-     */
-    public void expectSystemReady() throws Exception {
-        mMockNMService.registerObserver(isA(INetworkManagementEventObserver.class));
-        expectLastCall().atLeastOnce();
-    }
-
-    /**
-     * Expect {@link NetworkManagementService#getNetworkStatsSummaryDev()} mock
-     * calls, responding with the given counter values.
-     */
-    public void expectGetInterfaceCounter(long rx, long tx) throws Exception {
-        // TODO: provide elapsedRealtime mock to match TimeAuthority
-        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
-        stats.addValues(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, rx, 0L, tx, 0L, 0);
-
-        expect(mMockNMService.getNetworkStatsSummaryDev()).andReturn(stats).atLeastOnce();
-    }
-
-    /**
-     * Expect {@link NetworkManagementService#setInterfaceThrottle} mock call
-     * with the specified parameters.
-     */
-    public void expectSetInterfaceThrottle(int rx, int tx) throws Exception {
-        mMockNMService.setInterfaceThrottle(isA(String.class), eq(rx), eq(tx));
-        expectLastCall().atLeastOnce();
-    }
-
-    /**
-     * Dispatch {@link ThrottleService#systemReady()} and block until finished.
-     */
-    public void systemReady() throws Exception {
-        final Future<Intent> policyChanged = mWatchingContext.nextBroadcastIntent(
-                ThrottleManager.POLICY_CHANGED_ACTION);
-        final Future<Intent> pollAction = mWatchingContext.nextBroadcastIntent(
-                ThrottleManager.THROTTLE_POLL_ACTION);
-
-        mThrottleService.systemReady();
-
-        // wait for everything to settle; for policy to update and for first poll
-        policyChanged.get();
-        pollAction.get();
-    }
-
-    /**
-     * Dispatch {@link ThrottleService#dispatchPoll()} and block until finished.
-     */
-    public void forceServicePoll() throws Exception {
-        // during systemReady() service already pushed a sticky broadcast, so we
-        // need to skip the immediate and wait for the updated sticky.
-        final Future<Intent> pollAction = mWatchingContext.nextBroadcastIntent(
-                ThrottleManager.THROTTLE_POLL_ACTION);
-
-        mThrottleService.dispatchPoll();
-
-        pollAction.get();
-    }
-
-    /**
-     * Dispatch {@link ThrottleService#dispatchReset()} and block until finished.
-     */
-    public void forceServiceReset() throws Exception {
-        // during systemReady() service already pushed a sticky broadcast, so we
-        // need to skip the immediate and wait for the updated sticky.
-        final Future<Intent> pollAction = mWatchingContext.nextBroadcastIntent(
-                ThrottleManager.THROTTLE_POLL_ACTION);
-
-        mThrottleService.dispatchReset();
-
-        pollAction.get();
-    }
-}
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/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java
index b2a508b..d5daa5f 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java
@@ -18,13 +18,7 @@
 
 import android.app.Activity;
 import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
 import android.graphics.Region;
 import android.os.Bundle;
 import android.view.View;
@@ -50,8 +44,10 @@
             canvas.save();
             canvas.clipRect(100.0f, 100.0f, getWidth() - 100.0f, getHeight() - 100.0f,
                     Region.Op.DIFFERENCE);
-            canvas.drawARGB(255, 255, 0, 0);
+            canvas.drawARGB(128, 255, 0, 0);
             canvas.restore();
+
+            invalidate();
         }
     }
 }
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
index a8c1399..32b2771 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -23,14 +23,7 @@
 import android.graphics.BitmapFactory;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.renderscript.ScriptC;
-import android.renderscript.RenderScript;
-import android.renderscript.Type;
-import android.renderscript.Allocation;
-import android.renderscript.Element;
-import android.renderscript.Script;
 import android.view.SurfaceView;
-import android.view.SurfaceHolder;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
 import android.widget.ImageView;
@@ -39,13 +32,8 @@
 import android.widget.TextView;
 import android.view.View;
 import android.util.Log;
-import java.lang.Math;
 
 import android.os.Environment;
-import android.app.Instrumentation;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileWriter;
@@ -54,12 +42,68 @@
 public class ImageProcessingActivity extends Activity
                                        implements SeekBar.OnSeekBarChangeListener {
     private final String TAG = "Img";
-    private final String RESULT_FILE = "image_processing_result.csv";
+    public final String RESULT_FILE = "image_processing_result.csv";
+
+    /**
+     * Define enum type for test names
+     */
+    public enum TestName {
+        // totally there are 38 test cases
+        LEVELS_VEC3_RELAXED ("Levels Vec3 Relaxed"),
+        LEVELS_VEC4_RELAXED ("Levels Vec4 Relaxed"),
+        LEVELS_VEC3_FULL ("Levels Vec3 Full"),
+        LEVELS_VEC4_FULL ("Levels Vec4 Full"),
+        BLUR_RADIUS_25 ("Blur radius 25"),
+        INTRINSIC_BLUE_RADIUS_25 ("Intrinsic Blur radius 25"),
+        GREYSCALE ("Greyscale"),
+        GRAIN ("Grain"),
+        FISHEYE_FULL ("Fisheye Full"),
+        FISHEYE_RELAXED ("Fisheye Relaxed"),
+        FISHEYE_APPROXIMATE_FULL ("Fisheye Approximate Full"),
+        FISHEYE_APPROXIMATE_RELAXED ("Fisheye Approximate Relaxed"),
+        VIGNETTE_FULL ("Vignette Full"),
+        VIGNETTE_RELAXED ("Vignette Relaxed"),
+        VIGNETTE_APPROXIMATE_FULL ("Vignette Approximate Full"),
+        VIGNETTE_APPROXIMATE_RELAXED ("Vignette Approximate Relaxed"),
+        GROUP_TEST_EMULATED ("Group Test (emulated)"),
+        GROUP_TEST_NATIVE ("Group Test (native)"),
+        CONVOLVE_3X3 ("Convolve 3x3"),
+        INTRINSICS_CONVOLVE_3X3 ("Intrinsics Convolve 3x3"),
+        COLOR_MATRIX ("ColorMatrix"),
+        INTRINSICS_COLOR_MATRIX ("Intrinsics ColorMatrix"),
+        INTRINSICS_COLOR_MATRIX_GREY ("Intrinsics ColorMatrix Grey"),
+        COPY ("Copy"),
+        CROSS_PROCESS_USING_LUT ("CrossProcess (using LUT)"),
+        CONVOLVE_5X5 ("Convolve 5x5"),
+        INTRINSICS_CONVOLVE_5X5 ("Intrinsics Convolve 5x5"),
+        MANDELBROT ("Mandelbrot"),
+        INTRINSICS_BLEND ("Intrinsics Blend"),
+        INTRINSICS_BLUR_25G ("Intrinsics Blur 25 uchar"),
+        VIBRANCE ("Vibrance"),
+        BW_FILTER ("BW Filter"),
+        SHADOWS ("Shadows"),
+        CONTRAST ("Contrast"),
+        EXPOSURE ("Exposure"),
+        WHITE_BALANCE ("White Balance"),
+        COLOR_CUBE ("Color Cube"),
+        COLOR_CUBE_3D_INTRINSIC ("Color Cube (3D LUT intrinsic)");
+
+
+        private final String name;
+
+        private TestName(String s) {
+            name = s;
+        }
+
+        // return quoted string as displayed test name
+        public String toString() {
+            return name;
+        }
+    }
 
     Bitmap mBitmapIn;
     Bitmap mBitmapIn2;
     Bitmap mBitmapOut;
-    String mTestNames[];
 
     private Spinner mSpinner;
     private SeekBar mBar1;
@@ -178,123 +222,123 @@
     }
 
 
-    void changeTest(int testID) {
+    void changeTest(TestName testName) {
         if (mTest != null) {
             mTest.destroy();
         }
-        switch(testID) {
-        case 0:
+        switch(testName) {
+        case LEVELS_VEC3_RELAXED:
             mTest = new LevelsV4(false, false);
             break;
-        case 1:
+        case LEVELS_VEC4_RELAXED:
             mTest = new LevelsV4(false, true);
             break;
-        case 2:
+        case LEVELS_VEC3_FULL:
             mTest = new LevelsV4(true, false);
             break;
-        case 3:
+        case LEVELS_VEC4_FULL:
             mTest = new LevelsV4(true, true);
             break;
-        case 4:
+        case BLUR_RADIUS_25:
             mTest = new Blur25(false);
             break;
-        case 5:
+        case INTRINSIC_BLUE_RADIUS_25:
             mTest = new Blur25(true);
             break;
-        case 6:
+        case GREYSCALE:
             mTest = new Greyscale();
             break;
-        case 7:
+        case GRAIN:
             mTest = new Grain();
             break;
-        case 8:
+        case FISHEYE_FULL:
             mTest = new Fisheye(false, false);
             break;
-        case 9:
+        case FISHEYE_RELAXED:
             mTest = new Fisheye(false, true);
             break;
-        case 10:
+        case FISHEYE_APPROXIMATE_FULL:
             mTest = new Fisheye(true, false);
             break;
-        case 11:
+        case FISHEYE_APPROXIMATE_RELAXED:
             mTest = new Fisheye(true, true);
             break;
-        case 12:
+        case VIGNETTE_FULL:
             mTest = new Vignette(false, false);
             break;
-        case 13:
+        case VIGNETTE_RELAXED:
             mTest = new Vignette(false, true);
             break;
-        case 14:
+        case VIGNETTE_APPROXIMATE_FULL:
             mTest = new Vignette(true, false);
             break;
-        case 15:
+        case VIGNETTE_APPROXIMATE_RELAXED:
             mTest = new Vignette(true, true);
             break;
-        case 16:
+        case GROUP_TEST_EMULATED:
             mTest = new GroupTest(false);
             break;
-        case 17:
+        case GROUP_TEST_NATIVE:
             mTest = new GroupTest(true);
             break;
-        case 18:
+        case CONVOLVE_3X3:
             mTest = new Convolve3x3(false);
             break;
-        case 19:
+        case INTRINSICS_CONVOLVE_3X3:
             mTest = new Convolve3x3(true);
             break;
-        case 20:
+        case COLOR_MATRIX:
             mTest = new ColorMatrix(false, false);
             break;
-        case 21:
+        case INTRINSICS_COLOR_MATRIX:
             mTest = new ColorMatrix(true, false);
             break;
-        case 22:
+        case INTRINSICS_COLOR_MATRIX_GREY:
             mTest = new ColorMatrix(true, true);
             break;
-        case 23:
+        case COPY:
             mTest = new Copy();
             break;
-        case 24:
+        case CROSS_PROCESS_USING_LUT:
             mTest = new CrossProcess();
             break;
-        case 25:
+        case CONVOLVE_5X5:
             mTest = new Convolve5x5(false);
             break;
-        case 26:
+        case INTRINSICS_CONVOLVE_5X5:
             mTest = new Convolve5x5(true);
             break;
-        case 27:
+        case MANDELBROT:
             mTest = new Mandelbrot();
             break;
-        case 28:
+        case INTRINSICS_BLEND:
             mTest = new Blend();
             break;
-        case 29:
+        case INTRINSICS_BLUR_25G:
             mTest = new Blur25G();
             break;
-        case 30:
+        case VIBRANCE:
             mTest = new Vibrance();
             break;
-        case 31:
+        case BW_FILTER:
             mTest = new BWFilter();
             break;
-        case 32:
+        case SHADOWS:
             mTest = new Shadows();
             break;
-        case 33:
+        case CONTRAST:
             mTest = new Contrast();
             break;
-        case 34:
+        case EXPOSURE:
             mTest = new Exposure();
             break;
-        case 35:
+        case WHITE_BALANCE:
             mTest = new WhiteBalance();
             break;
-        case 36:
+        case COLOR_CUBE:
             mTest = new ColorCube(false);
             break;
-        case 37:
+        case COLOR_CUBE_3D_INTRINSIC:
             mTest = new ColorCube(true);
             break;
         }
@@ -308,54 +352,14 @@
     }
 
     void setupTests() {
-        mTestNames = new String[38];
-        mTestNames[0] = "Levels Vec3 Relaxed";
-        mTestNames[1] = "Levels Vec4 Relaxed";
-        mTestNames[2] = "Levels Vec3 Full";
-        mTestNames[3] = "Levels Vec4 Full";
-        mTestNames[4] = "Blur radius 25";
-        mTestNames[5] = "Intrinsic Blur radius 25";
-        mTestNames[6] = "Greyscale";
-        mTestNames[7] = "Grain";
-        mTestNames[8] = "Fisheye Full";
-        mTestNames[9] = "Fisheye Relaxed";
-        mTestNames[10] = "Fisheye Approximate Full";
-        mTestNames[11] = "Fisheye Approximate Relaxed";
-        mTestNames[12] = "Vignette Full";
-        mTestNames[13] = "Vignette Relaxed";
-        mTestNames[14] = "Vignette Approximate Full";
-        mTestNames[15] = "Vignette Approximate Relaxed";
-        mTestNames[16] = "Group Test (emulated)";
-        mTestNames[17] = "Group Test (native)";
-        mTestNames[18] = "Convolve 3x3";
-        mTestNames[19] = "Intrinsics Convolve 3x3";
-        mTestNames[20] = "ColorMatrix";
-        mTestNames[21] = "Intrinsics ColorMatrix";
-        mTestNames[22] = "Intrinsics ColorMatrix Grey";
-        mTestNames[23] = "Copy";
-        mTestNames[24] = "CrossProcess (using LUT)";
-        mTestNames[25] = "Convolve 5x5";
-        mTestNames[26] = "Intrinsics Convolve 5x5";
-        mTestNames[27] = "Mandelbrot";
-        mTestNames[28] = "Intrinsics Blend";
-        mTestNames[29] = "Intrinsics Blur 25 uchar";
-        mTestNames[30] = "Vibrance";
-        mTestNames[31] = "BW Filter";
-        mTestNames[32] = "Shadows";
-        mTestNames[33] = "Contrast";
-        mTestNames[34] = "Exposure";
-        mTestNames[35] = "White Balance";
-        mTestNames[36] = "Color Cube";
-        mTestNames[37] = "Color Cube (3D LUT intrinsic)";
-
-        mTestSpinner.setAdapter(new ArrayAdapter<String>(
-            this, R.layout.spinner_layout, mTestNames));
+        mTestSpinner.setAdapter(new ArrayAdapter<TestName>(
+            this, R.layout.spinner_layout, TestName.values()));
     }
 
     private AdapterView.OnItemSelectedListener mTestSpinnerListener =
             new AdapterView.OnItemSelectedListener() {
                 public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
-                    changeTest(pos);
+                    changeTest(TestName.values()[pos]);
                 }
 
                 public void onNothingSelected(AdapterView parent) {
@@ -405,7 +409,7 @@
         mBenchmarkResult.setText("Result: not run");
 
         setupTests();
-        changeTest(0);
+        changeTest(TestName.LEVELS_VEC3_RELAXED);
     }
 
 
@@ -436,10 +440,10 @@
         try {
             BufferedWriter rsWriter = new BufferedWriter(new FileWriter(resultFile));
             Log.v(TAG, "Saved results in: " + resultFile.getAbsolutePath());
-            for (int i = 0; i < mTestNames.length; i++ ) {
-                changeTest(i);
+            for (TestName tn: TestName.values()) {
+                changeTest(tn);
                 float t = getBenchmark();
-                String s = new String("" + mTestNames[i] + ", " + t);
+                String s = new String("" + tn.toString() + ", " + t);
                 rsWriter.write(s + "\n");
                 Log.v(TAG, "Test " + s + "ms\n");
             }
@@ -447,7 +451,7 @@
         } catch (IOException e) {
             Log.v(TAG, "Unable to write result file " + e.getMessage());
         }
-        changeTest(0);
+        changeTest(TestName.LEVELS_VEC3_RELAXED);
     }
 
     // For benchmark test
@@ -464,7 +468,6 @@
             mTest.finish();
         } while (t > java.lang.System.currentTimeMillis());
 
-
         //Log.v(TAG, "Benchmarking");
         int ct = 0;
         t = java.lang.System.currentTimeMillis();
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingTest.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingTest.java
index f995437..5263c61 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingTest.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingTest.java
@@ -16,33 +16,30 @@
 
 package com.android.rs.image;
 
+import com.android.rs.image.ImageProcessingTestRunner;
+
 import android.os.Bundle;
-import android.os.Environment;
-import android.app.Instrumentation;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
+import com.android.rs.image.ImageProcessingActivity.TestName;
+
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
 
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-
 /**
  * ImageProcessing benchmark test.
  * To run the test, please use command
  *
- * adb shell am instrument -w com.android.rs.image/.ImageProcessingTestRunner
+ * adb shell am instrument -e iteration <n> -w com.android.rs.image/.ImageProcessingTestRunner
  *
  */
 public class ImageProcessingTest extends ActivityInstrumentationTestCase2<ImageProcessingActivity> {
     private final String TAG = "ImageProcessingTest";
-    private final String RESULT_FILE = "image_processing_result.txt";
-    private int ITERATION = 5;
-    private ImageProcessingActivity mAct;
+    private final String TEST_NAME = "Testname";
+    private final String ITERATIONS = "Iterations";
+    private final String BENCHMARK = "Benchmark";
+    private static int INSTRUMENTATION_IN_PROGRESS = 2;
+    private int mIteration;
+    private ImageProcessingActivity mActivity;
 
     public ImageProcessingTest() {
         super(ImageProcessingActivity.class);
@@ -51,7 +48,11 @@
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        mAct = getActivity();
+        setActivityInitialTouchMode(false);
+        mActivity = getActivity();
+        ImageProcessingTestRunner mRunner = (ImageProcessingTestRunner) getInstrumentation();
+        mIteration = mRunner.mIteration;
+        assertTrue("please enter a valid iteration value", mIteration > 0);
    }
 
     @Override
@@ -59,36 +60,320 @@
         super.tearDown();
     }
 
-    /**
-     * ImageProcessing benchmark test
-     */
-    @LargeTest
-    public void testImageProcessingBench() {
-        long t = 0;
-        long sum = 0;
-        // write result into a file
-        File externalStorage = Environment.getExternalStorageDirectory();
-        if (!externalStorage.canWrite()) {
-            Log.v(TAG, "sdcard is not writable");
-            return;
+    class TestAction implements Runnable {
+        TestName mTestName;
+        float mResult;
+        public TestAction(TestName testName) {
+            mTestName = testName;
         }
-        File resultFile = new File(externalStorage, RESULT_FILE);
-        resultFile.setWritable(true, false);
-        try {
-            BufferedWriter rsWriter = new BufferedWriter(new FileWriter(resultFile));
-            Log.v(TAG, "Saved results in: " + resultFile.getAbsolutePath());
-            for (int i = 0; i < ITERATION; i++ ) {
-                t = (long)mAct.getBenchmark();
-                sum += t;
-                rsWriter.write("Renderscript frame time core: " + t + " ms\n");
-                Log.v(TAG, "RenderScript framew time core: " + t + " ms");
+        public void run() {
+            mActivity.changeTest(mTestName);
+            mResult = mActivity.getBenchmark();
+            Log.v(TAG, "Benchmark for test \"" + mTestName.toString() + "\" is: " + mResult);
+            synchronized(this) {
+                this.notify();
             }
-            long avgValue = sum/ITERATION;
-            rsWriter.write("Average frame time: " + avgValue + " ms\n");
-            Log.v(TAG, "Average frame time: " + avgValue + " ms");
-            rsWriter.close();
-        } catch (IOException e) {
-            Log.v(TAG, "Unable to write result file " + e.getMessage());
         }
+        public float getBenchmark() {
+            return mResult;
+        }
+    }
+
+    // Set the benchmark thread to run on ui thread
+    // Synchronized the thread such that the test will wait for the benchmark thread to finish
+    public void runOnUiThread(Runnable action) {
+        synchronized(action) {
+            mActivity.runOnUiThread(action);
+            try {
+                action.wait();
+            } catch (InterruptedException e) {
+                Log.v(TAG, "waiting for action running on UI thread is interrupted: " +
+                        e.toString());
+            }
+        }
+    }
+
+    public void runTest(TestAction ta, String testName) {
+        float sum = 0;
+        for (int i = 0; i < mIteration; i++) {
+            runOnUiThread(ta);
+            float bmValue = ta.getBenchmark();
+            Log.v(TAG, "results for iteration " + i + " is " + bmValue);
+            sum += bmValue;
+        }
+        float avgResult = sum/mIteration;
+
+        // post result to INSTRUMENTATION_STATUS
+        Bundle results = new Bundle();
+        results.putString(TEST_NAME, testName);
+        results.putInt(ITERATIONS, mIteration);
+        results.putFloat(BENCHMARK, avgResult);
+        getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, results);
+    }
+
+    // Test case 0: Levels Vec3 Relaxed
+    @LargeTest
+    public void testLevelsVec3Relaxed() {
+        TestAction ta = new TestAction(TestName.LEVELS_VEC3_RELAXED);
+        runTest(ta, TestName.LEVELS_VEC3_RELAXED.name());
+    }
+
+    // Test case 1: Levels Vec4 Relaxed
+    @LargeTest
+    public void testLevelsVec4Relaxed() {
+        TestAction ta = new TestAction(TestName.LEVELS_VEC4_RELAXED);
+        runTest(ta, TestName.LEVELS_VEC4_RELAXED.name());
+    }
+
+    // Test case 2: Levels Vec3 Full
+    @LargeTest
+    public void testLevelsVec3Full() {
+        TestAction ta = new TestAction(TestName.LEVELS_VEC3_FULL);
+        runTest(ta, TestName.LEVELS_VEC3_FULL.name());
+    }
+
+    // Test case 3: Levels Vec4 Full
+    @LargeTest
+    public void testLevelsVec4Full() {
+        TestAction ta = new TestAction(TestName.LEVELS_VEC4_FULL);
+        runTest(ta, TestName.LEVELS_VEC4_FULL.name());
+    }
+
+    // Test case 4: Blur Radius 25
+    @LargeTest
+    public void testBlurRadius25() {
+        TestAction ta = new TestAction(TestName.BLUR_RADIUS_25);
+        runTest(ta, TestName.BLUR_RADIUS_25.name());
+    }
+
+    // Test case 5: Intrinsic Blur Radius 25
+    @LargeTest
+    public void testIntrinsicBlurRadius25() {
+        TestAction ta = new TestAction(TestName.INTRINSIC_BLUE_RADIUS_25);
+        runTest(ta, TestName.INTRINSIC_BLUE_RADIUS_25.name());
+    }
+
+    // Test case 6: Greyscale
+    @LargeTest
+    public void testGreyscale() {
+        TestAction ta = new TestAction(TestName.GREYSCALE);
+        runTest(ta, TestName.GREYSCALE.name());
+    }
+
+    // Test case 7: Grain
+    @LargeTest
+    public void testGrain() {
+        TestAction ta = new TestAction(TestName.GRAIN);
+        runTest(ta, TestName.GRAIN.name());
+    }
+
+    // Test case 8: Fisheye Full
+    @LargeTest
+    public void testFisheyeFull() {
+        TestAction ta = new TestAction(TestName.FISHEYE_FULL);
+        runTest(ta, TestName.FISHEYE_FULL.name());
+    }
+
+    // Test case 9: Fisheye Relaxed
+    @LargeTest
+    public void testFishEyeRelaxed() {
+        TestAction ta = new TestAction(TestName.FISHEYE_RELAXED);
+        runTest(ta, TestName.FISHEYE_RELAXED.name());
+    }
+
+    // Test case 10: Fisheye Approximate Full
+    @LargeTest
+    public void testFisheyeApproximateFull() {
+        TestAction ta = new TestAction(TestName.FISHEYE_APPROXIMATE_FULL);
+        runTest(ta, TestName.FISHEYE_APPROXIMATE_FULL.name());
+    }
+
+    // Test case 11: Fisheye Approximate Relaxed
+    @LargeTest
+    public void testFisheyeApproximateRelaxed() {
+        TestAction ta = new TestAction(TestName.FISHEYE_APPROXIMATE_RELAXED);
+        runTest(ta, TestName.FISHEYE_APPROXIMATE_RELAXED.name());
+    }
+
+    // Test case 12: Vignette Full
+    @LargeTest
+    public void testVignetteFull() {
+        TestAction ta = new TestAction(TestName.VIGNETTE_FULL);
+        runTest(ta, TestName.VIGNETTE_FULL.name());
+    }
+
+    // Test case 13: Vignette Relaxed
+    @LargeTest
+    public void testVignetteRelaxed() {
+        TestAction ta = new TestAction(TestName.VIGNETTE_RELAXED);
+        runTest(ta, TestName.VIGNETTE_RELAXED.name());
+    }
+
+    // Test case 14: Vignette Approximate Full
+    @LargeTest
+    public void testVignetteApproximateFull() {
+        TestAction ta = new TestAction(TestName.VIGNETTE_APPROXIMATE_FULL);
+        runTest(ta, TestName.VIGNETTE_APPROXIMATE_FULL.name());
+    }
+
+    // Test case 15: Vignette Approximate Relaxed
+    @LargeTest
+    public void testVignetteApproximateRelaxed() {
+        TestAction ta = new TestAction(TestName.VIGNETTE_APPROXIMATE_RELAXED);
+        runTest(ta, TestName.VIGNETTE_APPROXIMATE_RELAXED.name());
+    }
+
+    // Test case 16: Group Test (emulated)
+    @LargeTest
+    public void testGroupTestEmulated() {
+        TestAction ta = new TestAction(TestName.GROUP_TEST_EMULATED);
+        runTest(ta, TestName.GROUP_TEST_EMULATED.name());
+    }
+
+    // Test case 17: Group Test (native)
+    @LargeTest
+    public void testGroupTestNative() {
+        TestAction ta = new TestAction(TestName.GROUP_TEST_NATIVE);
+        runTest(ta, TestName.GROUP_TEST_NATIVE.name());
+    }
+
+    // Test case 18: Convolve 3x3
+    @LargeTest
+    public void testConvolve3x3() {
+        TestAction ta = new TestAction(TestName.CONVOLVE_3X3);
+        runTest(ta, TestName.CONVOLVE_3X3.name());
+    }
+
+    // Test case 19: Intrinsics Convolve 3x3
+    @LargeTest
+    public void testIntrinsicsConvolve3x3() {
+        TestAction ta = new TestAction(TestName.INTRINSICS_CONVOLVE_3X3);
+        runTest(ta, TestName.INTRINSICS_CONVOLVE_3X3.name());
+    }
+
+    // Test case 20: ColorMatrix
+    @LargeTest
+    public void testColorMatrix() {
+        TestAction ta = new TestAction(TestName.COLOR_MATRIX);
+        runTest(ta, TestName.COLOR_MATRIX.name());
+    }
+
+    // Test case 21: Intrinsics ColorMatrix
+    @LargeTest
+    public void testIntrinsicsColorMatrix() {
+        TestAction ta = new TestAction(TestName.INTRINSICS_COLOR_MATRIX);
+        runTest(ta, TestName.INTRINSICS_COLOR_MATRIX.name());
+    }
+
+    // Test case 22: Intrinsics ColorMatrix Grey
+    @LargeTest
+    public void testIntrinsicsColorMatrixGrey() {
+        TestAction ta = new TestAction(TestName.INTRINSICS_COLOR_MATRIX_GREY);
+        runTest(ta, TestName.INTRINSICS_COLOR_MATRIX_GREY.name());
+    }
+
+    // Test case 23: Copy
+    @LargeTest
+    public void testCopy() {
+        TestAction ta = new TestAction(TestName.COPY);
+        runTest(ta, TestName.COPY.name());
+    }
+
+    // Test case 24: CrossProcess (using LUT)
+    @LargeTest
+    public void testCrossProcessUsingLUT() {
+        TestAction ta = new TestAction(TestName.CROSS_PROCESS_USING_LUT);
+        runTest(ta, TestName.CROSS_PROCESS_USING_LUT.name());
+    }
+
+    // Test case 25: Convolve 5x5
+    @LargeTest
+    public void testConvolve5x5() {
+        TestAction ta = new TestAction(TestName.CONVOLVE_5X5);
+        runTest(ta, TestName.CONVOLVE_5X5.name());
+    }
+
+    // Test case 26: Intrinsics Convolve 5x5
+    @LargeTest
+    public void testIntrinsicsConvolve5x5() {
+        TestAction ta = new TestAction(TestName.INTRINSICS_CONVOLVE_5X5);
+        runTest(ta, TestName.INTRINSICS_CONVOLVE_5X5.name());
+    }
+
+    // Test case 27: Mandelbrot
+    @LargeTest
+    public void testMandelbrot() {
+        TestAction ta = new TestAction(TestName.MANDELBROT);
+        runTest(ta, TestName.MANDELBROT.name());
+    }
+
+    // Test case 28: Intrinsics Blend
+    @LargeTest
+    public void testIntrinsicsBlend() {
+        TestAction ta = new TestAction(TestName.INTRINSICS_BLEND);
+        runTest(ta, TestName.INTRINSICS_BLEND.name());
+    }
+
+    // Test case 29: Intrinsics Blur 25 uchar
+    @LargeTest
+    public void testIntrinsicsBlur25G() {
+        TestAction ta = new TestAction(TestName.INTRINSICS_BLUR_25G);
+        runTest(ta, TestName.INTRINSICS_BLUR_25G.name());
+    }
+
+    // Test case 30: Vibrance
+    @LargeTest
+    public void testVibrance() {
+        TestAction ta = new TestAction(TestName.VIBRANCE);
+        runTest(ta, TestName.VIBRANCE.name());
+    }
+
+    // Test case 31: BWFilter
+    @LargeTest
+    public void testBWFilter() {
+        TestAction ta = new TestAction(TestName.BW_FILTER);
+        runTest(ta, TestName.BW_FILTER.name());
+    }
+
+    // Test case 32: Shadows
+    @LargeTest
+    public void testShadows() {
+        TestAction ta = new TestAction(TestName.SHADOWS);
+        runTest(ta, TestName.SHADOWS.name());
+    }
+
+    // Test case 33: Contrast
+    @LargeTest
+    public void testContrast() {
+        TestAction ta = new TestAction(TestName.CONTRAST);
+        runTest(ta, TestName.CONTRAST.name());
+    }
+
+    // Test case 34: Exposure
+    @LargeTest
+    public void testExposure(){
+        TestAction ta = new TestAction(TestName.EXPOSURE);
+        runTest(ta, TestName.EXPOSURE.name());
+    }
+
+    // Test case 35: White Balance
+    @LargeTest
+    public void testWhiteBalance() {
+        TestAction ta = new TestAction(TestName.WHITE_BALANCE);
+        runTest(ta, TestName.WHITE_BALANCE.name());
+    }
+
+    // Test case 36: Color Cube
+    @LargeTest
+    public void testColorCube() {
+        TestAction ta = new TestAction(TestName.COLOR_CUBE);
+        runTest(ta, TestName.COLOR_CUBE.name());
+    }
+
+    // Test case 37: Color Cube (3D Intrinsic)
+    @LargeTest
+    public void testColorCube3DIntrinsic() {
+        TestAction ta = new TestAction(TestName.COLOR_CUBE_3D_INTRINSIC);
+        runTest(ta, TestName.COLOR_CUBE_3D_INTRINSIC.name());
     }
 }
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingTestRunner.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingTestRunner.java
index 4e27b7f..36fbb3e 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingTestRunner.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingTestRunner.java
@@ -24,14 +24,25 @@
 
 /**
  * Run the ImageProcessing benchmark test
- * adb shell am instrument -w com.android.rs.image/.ImageProcessingTestRunner
+ * adb shell am instrument -e iteration <n> -w com.android.rs.image/.ImageProcessingTestRunner
  *
  */
 public class ImageProcessingTestRunner extends InstrumentationTestRunner {
+    public int mIteration = 5;
+
     @Override
     public TestSuite getAllTests() {
         TestSuite suite = new InstrumentationTestSuite(this);
         suite.addTestSuite(ImageProcessingTest.class);
         return suite;
     }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        String strIteration = (String) icicle.get("iteration");
+        if (strIteration != null) {
+            mIteration = Integer.parseInt(strIteration);
+        }
+    }
 }
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index b98925b..5cec06f 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -1852,7 +1852,13 @@
 
     String8 input(bundle->getSingleCrunchInputFile());
     String8 output(bundle->getSingleCrunchOutputFile());
-    return preProcessImageToCache(bundle, input, output);
+
+    if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
+        // we can't return the status_t as it gets truncate to the lower 8 bits.
+        return 42;
+    }
+
+    return NO_ERROR;
 }
 
 char CONSOLE_DATA[2925] = {
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;
     }
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index 135446f..ac53eb2 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -24,7 +24,6 @@
 import android.net.NetworkInfo.DetailedState;
 import android.net.ProxyProperties;
 import android.net.RouteInfo;
-import android.net.wifi.WifiConfiguration.EnterpriseField;
 import android.net.wifi.WifiConfiguration.IpAssignment;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiConfiguration.ProxySettings;
@@ -1090,31 +1089,17 @@
                 break setVariables;
             }
 
-            for (WifiConfiguration.EnterpriseField field
-                    : config.enterpriseFields) {
-                String varName = field.varName();
-                String value = field.value();
-                if (value != null) {
-                    if (field == config.engine) {
-                        /*
-                         * If the field is declared as an integer, it must not
-                         * be null
-                         */
-                        if (value.length() == 0) {
-                            value = "0";
-                        }
-                    } else if (field != config.eap) {
-                        value = (value.length() == 0) ? "NULL" : convertToQuotedString(value);
-                    }
+            HashMap<String, String> enterpriseFields = config.enterpriseConfig.getFields();
+            for (String key : enterpriseFields.keySet()) {
+                    String value = enterpriseFields.get(key);
                     if (!mWifiNative.setNetworkVariable(
                                 netId,
-                                varName,
+                                key,
                                 value)) {
-                        loge(config.SSID + ": failed to set " + varName +
+                        loge(config.SSID + ": failed to set " + key +
                                 ": " + value);
                         break setVariables;
                     }
-                }
             }
             updateFailed = false;
         }
@@ -1413,78 +1398,26 @@
             }
         }
 
-        for (WifiConfiguration.EnterpriseField field :
-                config.enterpriseFields) {
-            value = mWifiNative.getNetworkVariable(netId,
-                    field.varName());
+        HashMap<String, String> entepriseFields = config.enterpriseConfig.getFields();
+        for (String key : entepriseFields.keySet()) {
+            value = mWifiNative.getNetworkVariable(netId, key);
             if (!TextUtils.isEmpty(value)) {
-                if (field != config.eap && field != config.engine) {
-                    value = removeDoubleQuotes(value);
-                }
-                field.setValue(value);
+                entepriseFields.put(key, removeDoubleQuotes(value));
             }
         }
 
-        migrateOldEapTlsIfNecessary(config, netId);
-    }
-
-    /**
-     * Migration code for old EAP-TLS configurations. This should only be used
-     * when restoring an old wpa_supplicant.conf or upgrading from a previous
-     * platform version.
-     *
-     * @param config the configuration to be migrated
-     * @param netId the wpa_supplicant's net ID
-     * @param value the old private_key value
-     */
-    private void migrateOldEapTlsIfNecessary(WifiConfiguration config, int netId) {
-        String value = mWifiNative.getNetworkVariable(netId,
-                WifiConfiguration.OLD_PRIVATE_KEY_NAME);
-        /*
-         * If the old configuration value is not present, then there is nothing
-         * to do.
-         */
-        if (TextUtils.isEmpty(value)) {
-            return;
-        } else {
-            // Also ignore it if it's empty quotes.
-            value = removeDoubleQuotes(value);
-            if (TextUtils.isEmpty(value)) {
-                return;
-            }
+        if (config.enterpriseConfig.migrateOldEapTlsNative(mWifiNative, netId)) {
+            saveConfig();
         }
-
-        config.engine.setValue(WifiConfiguration.ENGINE_ENABLE);
-        config.engine_id.setValue(convertToQuotedString(WifiConfiguration.KEYSTORE_ENGINE_ID));
-
-        /*
-         * The old key started with the keystore:// URI prefix, but we don't
-         * need that anymore. Trim it off if it exists.
-         */
-        final String keyName;
-        if (value.startsWith(WifiConfiguration.KEYSTORE_URI)) {
-            keyName = new String(value.substring(WifiConfiguration.KEYSTORE_URI.length()));
-        } else {
-            keyName = value;
-        }
-        config.key_id.setValue(convertToQuotedString(keyName));
-
-        // Now tell the wpa_supplicant the new configuration values.
-        final EnterpriseField needsUpdate[] = { config.engine, config.engine_id, config.key_id };
-        for (EnterpriseField field : needsUpdate) {
-            mWifiNative.setNetworkVariable(netId, field.varName(), field.value());
-        }
-
-        // Remove old private_key string so we don't run this again.
-        mWifiNative.setNetworkVariable(netId, WifiConfiguration.OLD_PRIVATE_KEY_NAME,
-                convertToQuotedString(""));
-
-        saveConfig();
     }
 
     private String removeDoubleQuotes(String string) {
-        if (string.length() <= 2) return "";
-        return string.substring(1, string.length() - 1);
+        int length = string.length();
+        if ((length > 1) && (string.charAt(0) == '"')
+                && (string.charAt(length - 1) == '"')) {
+            return string.substring(1, length - 1);
+        }
+        return string;
     }
 
     private String convertToQuotedString(String string) {
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index c4fe1b4..552356c 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -24,44 +24,10 @@
 
 /**
  * A class representing a configured Wi-Fi network, including the
- * security configuration. Android will not necessarily support
- * all of these security schemes initially.
+ * security configuration.
  */
 public class WifiConfiguration implements Parcelable {
-
-    /**
-     * In old configurations, the "private_key" field was used. However, newer
-     * configurations use the key_id field with the engine_id set to "keystore".
-     * If this field is found in the configuration, the migration code is
-     * triggered.
-     * @hide
-     */
-    public static final String OLD_PRIVATE_KEY_NAME = "private_key";
-
-    /**
-     * String representing the keystore OpenSSL ENGINE's ID.
-     * @hide
-     */
-    public static final String KEYSTORE_ENGINE_ID = "keystore";
-
-    /**
-     * String representing the keystore URI used for wpa_supplicant.
-     * @hide
-     */
-    public static final String KEYSTORE_URI = "keystore://";
-
-    /**
-     * String to set the engine value to when it should be enabled.
-     * @hide
-     */
-    public static final String ENGINE_ENABLE = "1";
-
-    /**
-     * String to set the engine value to when it should be disabled.
-     * @hide
-     */
-    public static final String ENGINE_DISABLE = "0";
-
+    private static final String TAG = "WifiConfiguration";
     /** {@hide} */
     public static final String ssidVarName = "ssid";
     /** {@hide} */
@@ -78,56 +44,6 @@
     public static final String hiddenSSIDVarName = "scan_ssid";
     /** {@hide} */
     public static final int INVALID_NETWORK_ID = -1;
-
-    /** {@hide} */
-    public class EnterpriseField {
-        private String varName;
-        private String value;
-
-        private EnterpriseField(String varName) {
-            this.varName = varName;
-            this.value = null;
-        }
-
-        public void setValue(String value) {
-            this.value = value;
-        }
-
-        public String varName() {
-            return varName;
-        }
-
-        public String value() {
-            return value;
-        }
-    }
-
-    /** {@hide} */
-    public EnterpriseField eap = new EnterpriseField("eap");
-    /** {@hide} */
-    public EnterpriseField phase2 = new EnterpriseField("phase2");
-    /** {@hide} */
-    public EnterpriseField identity = new EnterpriseField("identity");
-    /** {@hide} */
-    public EnterpriseField anonymous_identity = new EnterpriseField("anonymous_identity");
-    /** {@hide} */
-    public EnterpriseField password = new EnterpriseField("password");
-    /** {@hide} */
-    public EnterpriseField client_cert = new EnterpriseField("client_cert");
-    /** {@hide} */
-    public EnterpriseField engine = new EnterpriseField("engine");
-    /** {@hide} */
-    public EnterpriseField engine_id = new EnterpriseField("engine_id");
-    /** {@hide} */
-    public EnterpriseField key_id = new EnterpriseField("key_id");
-    /** {@hide} */
-    public EnterpriseField ca_cert = new EnterpriseField("ca_cert");
-
-    /** {@hide} */
-    public EnterpriseField[] enterpriseFields = {
-            eap, phase2, identity, anonymous_identity, password, client_cert,
-            engine, engine_id, key_id, ca_cert };
-
     /**
      * Recognized key management schemes.
      */
@@ -357,6 +273,11 @@
      * Defaults to CCMP TKIP WEP104 WEP40.
      */
     public BitSet allowedGroupCiphers;
+    /**
+     * The enterprise configuration details
+     * @hide
+     */
+    public WifiEnterpriseConfig enterpriseConfig;
 
     /**
      * @hide
@@ -412,11 +333,10 @@
         allowedPairwiseCiphers = new BitSet();
         allowedGroupCiphers = new BitSet();
         wepKeys = new String[4];
-        for (int i = 0; i < wepKeys.length; i++)
+        for (int i = 0; i < wepKeys.length; i++) {
             wepKeys[i] = null;
-        for (EnterpriseField field : enterpriseFields) {
-            field.setValue(null);
         }
+        enterpriseConfig = new WifiEnterpriseConfig();
         ipAssignment = IpAssignment.UNASSIGNED;
         proxySettings = ProxySettings.UNASSIGNED;
         linkProperties = new LinkProperties();
@@ -496,12 +416,9 @@
             sbuf.append('*');
         }
 
-        for (EnterpriseField field : enterpriseFields) {
-            sbuf.append('\n').append(" " + field.varName() + ": ");
-            String value = field.value();
-            if (value != null) sbuf.append(value);
-        }
+        sbuf.append(enterpriseConfig);
         sbuf.append('\n');
+
         sbuf.append("IP assignment: " + ipAssignment.toString());
         sbuf.append("\n");
         sbuf.append("Proxy settings: " + proxySettings.toString());
@@ -549,8 +466,9 @@
         int cardinality = src.readInt();
 
         BitSet set = new BitSet();
-        for (int i = 0; i < cardinality; i++)
+        for (int i = 0; i < cardinality; i++) {
             set.set(src.readInt());
+        }
 
         return set;
     }
@@ -560,8 +478,9 @@
 
         dest.writeInt(set.cardinality());
 
-        while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1)
+        while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
             dest.writeInt(nextSetBit);
+        }
     }
 
     /** @hide */
@@ -594,8 +513,9 @@
             preSharedKey = source.preSharedKey;
 
             wepKeys = new String[4];
-            for (int i = 0; i < wepKeys.length; i++)
+            for (int i = 0; i < wepKeys.length; i++) {
                 wepKeys[i] = source.wepKeys[i];
+            }
 
             wepTxKeyIndex = source.wepTxKeyIndex;
             priority = source.priority;
@@ -606,9 +526,8 @@
             allowedPairwiseCiphers = (BitSet) source.allowedPairwiseCiphers.clone();
             allowedGroupCiphers    = (BitSet) source.allowedGroupCiphers.clone();
 
-            for (int i = 0; i < source.enterpriseFields.length; i++) {
-                enterpriseFields[i].setValue(source.enterpriseFields[i].value());
-            }
+            enterpriseConfig = new WifiEnterpriseConfig(source.enterpriseConfig);
+
             ipAssignment = source.ipAssignment;
             proxySettings = source.proxySettings;
             linkProperties = new LinkProperties(source.linkProperties);
@@ -623,8 +542,9 @@
         dest.writeString(SSID);
         dest.writeString(BSSID);
         dest.writeString(preSharedKey);
-        for (String wepKey : wepKeys)
+        for (String wepKey : wepKeys) {
             dest.writeString(wepKey);
+        }
         dest.writeInt(wepTxKeyIndex);
         dest.writeInt(priority);
         dest.writeInt(hiddenSSID ? 1 : 0);
@@ -635,9 +555,8 @@
         writeBitSet(dest, allowedPairwiseCiphers);
         writeBitSet(dest, allowedGroupCiphers);
 
-        for (EnterpriseField field : enterpriseFields) {
-            dest.writeString(field.value());
-        }
+        dest.writeParcelable(enterpriseConfig, flags);
+
         dest.writeString(ipAssignment.name());
         dest.writeString(proxySettings.name());
         dest.writeParcelable(linkProperties, flags);
@@ -654,8 +573,9 @@
                 config.SSID = in.readString();
                 config.BSSID = in.readString();
                 config.preSharedKey = in.readString();
-                for (int i = 0; i < config.wepKeys.length; i++)
+                for (int i = 0; i < config.wepKeys.length; i++) {
                     config.wepKeys[i] = in.readString();
+                }
                 config.wepTxKeyIndex = in.readInt();
                 config.priority = in.readInt();
                 config.hiddenSSID = in.readInt() != 0;
@@ -665,13 +585,12 @@
                 config.allowedPairwiseCiphers = readBitSet(in);
                 config.allowedGroupCiphers    = readBitSet(in);
 
-                for (EnterpriseField field : config.enterpriseFields) {
-                    field.setValue(in.readString());
-                }
+                config.enterpriseConfig = in.readParcelable(null);
 
                 config.ipAssignment = IpAssignment.valueOf(in.readString());
                 config.proxySettings = ProxySettings.valueOf(in.readString());
                 config.linkProperties = in.readParcelable(null);
+
                 return config;
             }
 
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.aidl b/wifi/java/android/net/wifi/WifiEnterpriseConfig.aidl
new file mode 100644
index 0000000..b0f5f84
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.net.wifi;
+
+parcelable WifiEnterpriseConfig;
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
new file mode 100644
index 0000000..46e446e
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -0,0 +1,429 @@
+/*
+ * 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.net.wifi;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.security.Credentials;
+import android.text.TextUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/** Enterprise configuration details for Wi-Fi @hide */
+public class WifiEnterpriseConfig implements Parcelable {
+    private static final String TAG = "WifiEnterpriseConfig";
+    /**
+     * In old configurations, the "private_key" field was used. However, newer
+     * configurations use the key_id field with the engine_id set to "keystore".
+     * If this field is found in the configuration, the migration code is
+     * triggered.
+     */
+    private static final String OLD_PRIVATE_KEY_NAME = "private_key";
+
+    /**
+     * String representing the keystore OpenSSL ENGINE's ID.
+     */
+    private static final String ENGINE_ID_KEYSTORE = "keystore";
+
+    /**
+     * String representing the keystore URI used for wpa_supplicant.
+     */
+    private static final String KEYSTORE_URI = "keystore://";
+
+    /**
+     * String to set the engine value to when it should be enabled.
+     */
+    private static final String ENGINE_ENABLE = "1";
+
+    /**
+     * String to set the engine value to when it should be disabled.
+     */
+    private static final String ENGINE_DISABLE = "0";
+
+    private static final String CA_CERT_PREFIX = KEYSTORE_URI + Credentials.CA_CERTIFICATE;
+    private static final String CLIENT_CERT_PREFIX = KEYSTORE_URI + Credentials.USER_CERTIFICATE;
+
+    private static final String EAP_KEY             = "eap";
+    private static final String PHASE2_KEY          = "phase2";
+    private static final String IDENTITY_KEY        = "identity";
+    private static final String ANON_IDENTITY_KEY   = "anonymous_identity";
+    private static final String PASSWORD_KEY        = "password";
+    private static final String CLIENT_CERT_KEY     = "client_cert";
+    private static final String CA_CERT_KEY         = "ca_cert";
+    private static final String SUBJECT_MATCH_KEY   = "subject_match";
+    private static final String ENGINE_KEY          = "engine";
+    private static final String ENGINE_ID_KEY       = "engine_id";
+    private static final String PRIVATE_KEY_ID_KEY  = "key_id";
+
+    private HashMap<String, String> mFields = new HashMap<String, String>();
+
+    /** This represents an empty value of an enterprise field.
+     * NULL is used at wpa_supplicant to indicate an empty value
+     */
+    private static final String EMPTY_VALUE = "NULL";
+
+    public WifiEnterpriseConfig() {
+        // Set the required defaults
+        mFields.put(EAP_KEY, Eap.strings[Eap.PEAP]);
+        mFields.put(ENGINE_KEY, ENGINE_DISABLE);
+
+        for (String key : new String[] {PHASE2_KEY, IDENTITY_KEY, ANON_IDENTITY_KEY,
+                PASSWORD_KEY, CLIENT_CERT_KEY, ENGINE_ID_KEY, PRIVATE_KEY_ID_KEY,
+                CA_CERT_KEY, SUBJECT_MATCH_KEY}) {
+            mFields.put(key, EMPTY_VALUE);
+        }
+    }
+
+    /** Copy constructor */
+    public WifiEnterpriseConfig(WifiEnterpriseConfig source) {
+        for (String key : source.mFields.keySet()) {
+            mFields.put(key, source.mFields.get(key));
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mFields.size());
+        for (Map.Entry<String, String> entry : mFields.entrySet()) {
+            dest.writeString(entry.getKey());
+            dest.writeString(entry.getValue());
+        }
+    }
+
+    public static final Creator<WifiEnterpriseConfig> CREATOR =
+            new Creator<WifiEnterpriseConfig>() {
+                public WifiEnterpriseConfig createFromParcel(Parcel in) {
+                    WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+                    int count = in.readInt();
+                    for (int i = 0; i < count; i++) {
+                        String key = in.readString();
+                        String value = in.readString();
+                        enterpriseConfig.mFields.put(key, value);
+                    }
+                    return enterpriseConfig;
+                }
+
+                public WifiEnterpriseConfig[] newArray(int size) {
+                    return new WifiEnterpriseConfig[size];
+                }
+            };
+
+    public static final class Eap {
+        public static final int PEAP    = 0;
+        public static final int TLS     = 1;
+        public static final int TTLS    = 2;
+        public static final int PWD     = 3;
+        /** @hide */
+        public static final String[] strings = { "PEAP", "TLS", "TTLS", "PWD" };
+    }
+
+    public static final class Phase2 {
+        public static final int NONE        = 0;
+        public static final int PAP         = 1;
+        public static final int MSCHAP      = 2;
+        public static final int MSCHAPV2    = 3;
+        public static final int GTC         = 4;
+        private static final String PREFIX = "auth=";
+        /** @hide */
+        public static final String[] strings = {EMPTY_VALUE, "PAP", "MSCHAP", "MSCHAPV2", "GTC" };
+    }
+
+    /** Internal use only @hide */
+    public HashMap<String, String> getFields() {
+        return mFields;
+    }
+
+    /**
+     * Set the EAP authentication method.
+     * @param  eapMethod is one {@link Eap#PEAP}, {@link Eap#TLS}, {@link Eap#TTLS} or
+     *                   {@link Eap#PWD}
+     */
+    public void setEapMethod(int eapMethod) {
+        switch (eapMethod) {
+            /** Valid methods */
+            case Eap.PEAP:
+            case Eap.PWD:
+            case Eap.TLS:
+            case Eap.TTLS:
+                mFields.put(EAP_KEY, Eap.strings[eapMethod]);
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown EAP method");
+        }
+    }
+
+    /**
+     * Get the eap method.
+     * @return eap method configured
+     */
+    public int getEapMethod() {
+        String eapMethod  = mFields.get(EAP_KEY);
+        return getStringIndex(Eap.strings, eapMethod, Eap.PEAP);
+    }
+
+    /**
+     * Set Phase 2 authentication method. Sets the inner authentication method to be used in
+     * phase 2 after setting up a secure channel
+     * @param phase2Method is the inner authentication method and can be one of {@link Phase2#NONE},
+     *                     {@link Phase2#PAP}, {@link Phase2#MSCHAP}, {@link Phase2#MSCHAPV2},
+     *                     {@link Phase2#GTC}
+     *
+     */
+    public void setPhase2Method(int phase2Method) {
+        switch (phase2Method) {
+            case Phase2.NONE:
+                mFields.put(PHASE2_KEY, EMPTY_VALUE);
+                break;
+            /** Valid methods */
+            case Phase2.PAP:
+            case Phase2.MSCHAP:
+            case Phase2.MSCHAPV2:
+            case Phase2.GTC:
+                mFields.put(PHASE2_KEY, convertToQuotedString(
+                        Phase2.PREFIX + Phase2.strings[phase2Method]));
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown Phase 2 method");
+        }
+    }
+
+    /**
+     * Get the phase 2 authentication method.
+     * @return a phase 2 method defined at {@link Phase2}
+     * */
+    public int getPhase2Method() {
+        String phase2Method = mFields.get(PHASE2_KEY);
+        return getStringIndex(Phase2.strings, phase2Method, Phase2.NONE);
+    }
+
+    /**
+     * Set the identity
+     * @param identity
+     */
+    public void setIdentity(String identity) {
+        setFieldValue(IDENTITY_KEY, identity, "");
+    }
+
+    /**
+     * Get the identity
+     * @return the identity
+     */
+    public String getIdentity() {
+        return getFieldValue(IDENTITY_KEY, "");
+    }
+
+    /**
+     * Set anonymous identity. This is used as the unencrypted identity with
+     * certain EAP types
+     * @param anonymousIdentity the anonymous identity
+     */
+    public void setAnonymousIdentity(String anonymousIdentity) {
+        setFieldValue(ANON_IDENTITY_KEY, anonymousIdentity, "");
+    }
+
+    /** Get the anonymous identity
+     * @return anonymous identity
+     */
+    public String getAnonymousIdentity() {
+        return getFieldValue(ANON_IDENTITY_KEY, "");
+    }
+
+    /**
+     * Set the password.
+     * @param password the password
+     */
+    public void setPassword(String password) {
+        setFieldValue(PASSWORD_KEY, password, "");
+    }
+
+    /**
+     * Set CA certificate alias.
+     *
+     * <p> See the {@link android.security.KeyChain} for details on installing or choosing
+     * a certificate
+     * </p>
+     * @param alias identifies the certificate
+     */
+    public void setCaCertificate(String alias) {
+        setFieldValue(CA_CERT_KEY, alias, CA_CERT_PREFIX);
+    }
+
+    /**
+     * Get CA certificate alias
+     * @return alias to the CA certificate
+     */
+    public String getCaCertificate() {
+        return getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX);
+    }
+
+    /**
+     * Set Client certificate alias.
+     *
+     * <p> See the {@link android.security.KeyChain} for details on installing or choosing
+     * a certificate
+     * </p>
+     * @param alias identifies the certificate
+     */
+    public void setClientCertificate(String alias) {
+        setFieldValue(CLIENT_CERT_KEY, alias, CLIENT_CERT_PREFIX);
+        setFieldValue(PRIVATE_KEY_ID_KEY, alias, Credentials.USER_PRIVATE_KEY);
+        // Also, set engine parameters
+        if (TextUtils.isEmpty(alias)) {
+            mFields.put(ENGINE_KEY, ENGINE_DISABLE);
+            mFields.put(ENGINE_ID_KEY, EMPTY_VALUE);
+        } else {
+            mFields.put(ENGINE_KEY, ENGINE_ENABLE);
+            mFields.put(ENGINE_ID_KEY, convertToQuotedString(ENGINE_ID_KEYSTORE));
+        }
+    }
+
+    /**
+     * Get client certificate alias
+     * @return alias to the client certificate
+     */
+    public String getClientCertificate() {
+        return getFieldValue(CLIENT_CERT_KEY, CLIENT_CERT_PREFIX);
+    }
+
+    /**
+     * Set subject match. This is the substring to be matched against the subject of the
+     * authentication server certificate.
+     * @param subjectMatch substring to be matched
+     */
+    public void setSubjectMatch(String subjectMatch) {
+        setFieldValue(SUBJECT_MATCH_KEY, subjectMatch, "");
+    }
+
+    /**
+     * Get subject match
+     * @return the subject match string
+     */
+    public String getSubjectMatch() {
+        return getFieldValue(SUBJECT_MATCH_KEY, "");
+    }
+
+    /** Migrates the old style TLS config to the new config style. This should only be used
+     * when restoring an old wpa_supplicant.conf or upgrading from a previous
+     * platform version.
+     * @return true if the config was updated
+     * @hide
+     */
+    public boolean migrateOldEapTlsNative(WifiNative wifiNative, int netId) {
+        String oldPrivateKey = wifiNative.getNetworkVariable(netId, OLD_PRIVATE_KEY_NAME);
+        /*
+         * If the old configuration value is not present, then there is nothing
+         * to do.
+         */
+        if (TextUtils.isEmpty(oldPrivateKey)) {
+            return false;
+        } else {
+            // Also ignore it if it's empty quotes.
+            oldPrivateKey = removeDoubleQuotes(oldPrivateKey);
+            if (TextUtils.isEmpty(oldPrivateKey)) {
+                return false;
+            }
+        }
+
+        mFields.put(ENGINE_KEY, ENGINE_ENABLE);
+        mFields.put(ENGINE_ID_KEY, convertToQuotedString(ENGINE_ID_KEYSTORE));
+
+        /*
+        * The old key started with the keystore:// URI prefix, but we don't
+        * need that anymore. Trim it off if it exists.
+        */
+        final String keyName;
+        if (oldPrivateKey.startsWith(KEYSTORE_URI)) {
+            keyName = new String(oldPrivateKey.substring(KEYSTORE_URI.length()));
+        } else {
+            keyName = oldPrivateKey;
+        }
+        mFields.put(PRIVATE_KEY_ID_KEY, convertToQuotedString(keyName));
+
+        wifiNative.setNetworkVariable(netId, ENGINE_KEY, mFields.get(ENGINE_KEY));
+        wifiNative.setNetworkVariable(netId, ENGINE_ID_KEY, mFields.get(ENGINE_ID_KEY));
+        wifiNative.setNetworkVariable(netId, PRIVATE_KEY_ID_KEY, mFields.get(PRIVATE_KEY_ID_KEY));
+        // Remove old private_key string so we don't run this again.
+        wifiNative.setNetworkVariable(netId, OLD_PRIVATE_KEY_NAME, EMPTY_VALUE);
+        return true;
+    }
+
+    private String removeDoubleQuotes(String string) {
+        int length = string.length();
+        if ((length > 1) && (string.charAt(0) == '"')
+                && (string.charAt(length - 1) == '"')) {
+            return string.substring(1, length - 1);
+        }
+        return string;
+    }
+
+    private String convertToQuotedString(String string) {
+        return "\"" + string + "\"";
+    }
+
+    /** Returns the index at which the toBeFound string is found in the array.
+     * @param arr array of strings
+     * @param toBeFound string to be found
+     * @param defaultIndex default index to be returned when string is not found
+     * @return the index into array
+     */
+    private int getStringIndex(String arr[], String toBeFound, int defaultIndex) {
+        for (int i = 0; i < arr.length; i++) {
+            // toBeFound can be formatted with a prefix. For example, phase2
+            // string has "auth=" as the prefix.
+            if (toBeFound.contains(arr[i])) return i;
+        }
+        return defaultIndex;
+    }
+
+    /** Returns the field value for the key.
+     * @param key into the hash
+     * @param prefix is the prefix that the value may have
+     * @return value
+     */
+    private String getFieldValue(String key, String prefix) {
+        String value = mFields.get(key);
+        if (EMPTY_VALUE.equals(value)) return "";
+        return removeDoubleQuotes(value).substring(prefix.length());
+    }
+
+    /** Set a value with an optional prefix at key
+     * @param key into the hash
+     * @param value to be set
+     * @param prefix an optional value to be prefixed to actual value
+     */
+    private void setFieldValue(String key, String value, String prefix) {
+        if (TextUtils.isEmpty(value)) {
+            mFields.put(key, EMPTY_VALUE);
+        } else {
+            mFields.put(key, convertToQuotedString(prefix + value));
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        for (String key : mFields.keySet()) {
+            sb.append(key).append(" ").append(mFields.get(key)).append("\n");
+        }
+        return sb.toString();
+    }
+}