Merge "Import translations. DO NOT MERGE" into klp-dev
diff --git a/api/current.txt b/api/current.txt
index a603b773..c2dfa92 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -71,6 +71,7 @@
     field public static final java.lang.String INJECT_EVENTS = "android.permission.INJECT_EVENTS";
     field public static final java.lang.String INSTALL_LOCATION_PROVIDER = "android.permission.INSTALL_LOCATION_PROVIDER";
     field public static final java.lang.String INSTALL_PACKAGES = "android.permission.INSTALL_PACKAGES";
+    field public static final java.lang.String INSTALL_SHORTCUT = "com.android.launcher.permission.INSTALL_SHORTCUT";
     field public static final java.lang.String INTERNAL_SYSTEM_WINDOW = "android.permission.INTERNAL_SYSTEM_WINDOW";
     field public static final java.lang.String INTERNET = "android.permission.INTERNET";
     field public static final java.lang.String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES";
@@ -132,6 +133,7 @@
     field public static final java.lang.String SUBSCRIBED_FEEDS_WRITE = "android.permission.SUBSCRIBED_FEEDS_WRITE";
     field public static final java.lang.String SYSTEM_ALERT_WINDOW = "android.permission.SYSTEM_ALERT_WINDOW";
     field public static final java.lang.String TRANSMIT_IR = "android.permission.TRANSMIT_IR";
+    field public static final java.lang.String UNINSTALL_SHORTCUT = "com.android.launcher.permission.UNINSTALL_SHORTCUT";
     field public static final java.lang.String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS";
     field public static final java.lang.String USE_CREDENTIALS = "android.permission.USE_CREDENTIALS";
     field public static final java.lang.String USE_SIP = "android.permission.USE_SIP";
@@ -5744,11 +5746,10 @@
     method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
     method public static deprecated android.content.SyncInfo getCurrentSync();
     method public static java.util.List<android.content.SyncInfo> getCurrentSyncs();
-    method public android.net.Uri[] getIncomingUriPermissionGrants(int, int);
     method public static int getIsSyncable(android.accounts.Account, java.lang.String);
     method public static boolean getMasterSyncAutomatically();
-    method public android.net.Uri[] getOutgoingUriPermissionGrants(int, int);
     method public static java.util.List<android.content.PeriodicSync> getPeriodicSyncs(android.accounts.Account, java.lang.String);
+    method public java.util.List<android.content.UriPermission> getPersistedUriPermissions();
     method public java.lang.String[] getStreamTypes(android.net.Uri, java.lang.String);
     method public static android.content.SyncAdapterType[] getSyncAdapterTypes();
     method public static boolean getSyncAutomatically(android.accounts.Account, java.lang.String);
@@ -5770,6 +5771,7 @@
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
     method public final void registerContentObserver(android.net.Uri, boolean, android.database.ContentObserver);
+    method public void releasePersistableUriPermission(android.net.Uri, int);
     method public static void removePeriodicSync(android.accounts.Account, java.lang.String, android.os.Bundle);
     method public static void removeStatusChangeListener(java.lang.Object);
     method public static void requestSync(android.accounts.Account, java.lang.String, android.os.Bundle);
@@ -5778,6 +5780,7 @@
     method public static void setMasterSyncAutomatically(boolean);
     method public static void setSyncAutomatically(android.accounts.Account, java.lang.String, boolean);
     method public deprecated void startSync(android.net.Uri, android.os.Bundle);
+    method public void takePersistableUriPermission(android.net.Uri, int);
     method public final android.net.Uri uncanonicalize(android.net.Uri);
     method public final void unregisterContentObserver(android.database.ContentObserver);
     method public final int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
@@ -6515,10 +6518,10 @@
     field public static final int FLAG_DEBUG_LOG_RESOLUTION = 8; // 0x8
     field public static final int FLAG_EXCLUDE_STOPPED_PACKAGES = 16; // 0x10
     field public static final int FLAG_FROM_BACKGROUND = 4; // 0x4
+    field public static final int FLAG_GRANT_PERSISTABLE_URI_PERMISSION = 64; // 0x40
     field public static final int FLAG_GRANT_READ_URI_PERMISSION = 1; // 0x1
     field public static final int FLAG_GRANT_WRITE_URI_PERMISSION = 2; // 0x2
     field public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 32; // 0x20
-    field public static final int FLAG_PERSIST_GRANT_URI_PERMISSION = 64; // 0x40
     field public static final int FLAG_RECEIVER_FOREGROUND = 268435456; // 0x10000000
     field public static final int FLAG_RECEIVER_NO_ABORT = 134217728; // 0x8000000
     field public static final int FLAG_RECEIVER_REGISTERED_ONLY = 1073741824; // 0x40000000
@@ -6911,6 +6914,17 @@
     field public static final int NO_MATCH = -1; // 0xffffffff
   }
 
+  public final class UriPermission implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getPersistedTime();
+    method public android.net.Uri getUri();
+    method public boolean isReadPermission();
+    method public boolean isWritePermission();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final long INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
+  }
+
 }
 
 package android.content.pm {
@@ -20954,7 +20968,6 @@
     method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri);
     method public static java.lang.String getDocumentId(android.net.Uri);
     method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal);
-    method public static android.net.Uri[] getOpenDocuments(android.content.Context);
     method public static java.lang.String getRootId(android.net.Uri);
     method public static java.lang.String getSearchDocumentsQuery(android.net.Uri);
     field public static final java.lang.String EXTRA_ERROR = "error";
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 7d4d57c..3b88973 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -23,9 +23,11 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
+import android.content.UriPermission;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ConfigurationInfo;
 import android.content.pm.IPackageDataObserver;
+import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
@@ -1130,6 +1132,32 @@
             return true;
         }
 
+        case TAKE_PERSISTABLE_URI_PERMISSION_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            Uri uri = Uri.CREATOR.createFromParcel(data);
+            int mode = data.readInt();
+            takePersistableUriPermission(uri, mode);
+            reply.writeNoException();
+            return true;
+        }
+
+        case RELEASE_PERSISTABLE_URI_PERMISSION_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            Uri uri = Uri.CREATOR.createFromParcel(data);
+            int mode = data.readInt();
+            releasePersistableUriPermission(uri, mode);
+            reply.writeNoException();
+            return true;
+        }
+
+        case GET_PERSISTED_URI_PERMISSIONS_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            final ParceledListSlice<UriPermission> perms = getPersistedUriPermissions();
+            reply.writeNoException();
+            perms.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+            return true;
+        }
+
         case SHOW_WAITING_FOR_DEBUGGER_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder b = data.readStrongBinder();
@@ -1983,19 +2011,6 @@
             return true;
         }
 
-        case GET_GRANTED_URI_PERMISSIONS_TRANSACTION: {
-            data.enforceInterface(IActivityManager.descriptor);
-            final String sourcePackage = data.readString();
-            final String targetPackage = data.readString();
-            final int modeFlags = data.readInt();
-            final int modeMask = data.readInt();
-            final Uri[] uris = getGrantedUriPermissions(
-                    sourcePackage, targetPackage, modeFlags, modeMask);
-            reply.writeNoException();
-            reply.writeParcelableArray(uris, 0);
-            return true;
-        }
-
         case PERFORM_IDLE_MAINTENANCE_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             performIdleMaintenance();
@@ -3436,6 +3451,47 @@
         data.recycle();
         reply.recycle();
     }
+
+    @Override
+    public void takePersistableUriPermission(Uri uri, int mode) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        uri.writeToParcel(data, 0);
+        data.writeInt(mode);
+        mRemote.transact(TAKE_PERSISTABLE_URI_PERMISSION_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
+    @Override
+    public void releasePersistableUriPermission(Uri uri, int mode) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        uri.writeToParcel(data, 0);
+        data.writeInt(mode);
+        mRemote.transact(RELEASE_PERSISTABLE_URI_PERMISSION_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
+    @Override
+    public ParceledListSlice<UriPermission> getPersistedUriPermissions() throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        mRemote.transact(GET_PERSISTED_URI_PERMISSIONS_TRANSACTION, data, reply, 0);
+        reply.readException();
+        final ParceledListSlice<UriPermission> perms = ParceledListSlice.CREATOR.createFromParcel(
+                reply);
+        data.recycle();
+        reply.recycle();
+        return perms;
+    }
+
     public void showWaitingForDebugger(IApplicationThread who, boolean waiting)
             throws RemoteException {
         Parcel data = Parcel.obtain();
@@ -4567,24 +4623,6 @@
         reply.recycle();
     }
 
-    public Uri[] getGrantedUriPermissions(
-            String sourcePackage, String targetPackage, int modeFlags, int modeMask)
-            throws RemoteException {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        data.writeInterfaceToken(IActivityManager.descriptor);
-        data.writeString(sourcePackage);
-        data.writeString(targetPackage);
-        data.writeInt(modeFlags);
-        data.writeInt(modeMask);
-        mRemote.transact(GET_GRANTED_URI_PERMISSIONS_TRANSACTION, data, reply, 0);
-        reply.readException();
-        final Uri[] uris = (Uri[]) reply.readParcelableArray(null);
-        data.recycle();
-        reply.recycle();
-        return uris;
-    }
-
     public void performIdleMaintenance() throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3e20f1f..6605b5b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -533,7 +533,8 @@
     private native void dumpGraphicsInfo(FileDescriptor fd);
 
     private class ApplicationThread extends ApplicationThreadNative {
-        private static final String HEAP_COLUMN = "%13s %8s %8s %8s %8s %8s %8s %8s %8s %8s";
+        private static final String HEAP_FULL_COLUMN = "%13s %8s %8s %8s %8s %8s %8s %8s %8s %8s";
+        private static final String HEAP_COLUMN = "%13s %8s %8s %8s %8s %8s %8s %8s";
         private static final String ONE_COUNT_COLUMN = "%21s %8d";
         private static final String TWO_COUNT_COLUMNS = "%21s %8d %21s %8d";
         private static final String DB_INFO_FORMAT = "  %8s %8s %14s %14s  %s";
@@ -892,18 +893,18 @@
 
         @Override
         public void dumpMemInfo(FileDescriptor fd, Debug.MemoryInfo mem, boolean checkin,
-                boolean dumpInfo, boolean dumpDalvik, String[] args) {
+                boolean dumpFullInfo, boolean dumpDalvik, String[] args) {
             FileOutputStream fout = new FileOutputStream(fd);
             PrintWriter pw = new FastPrintWriter(fout);
             try {
-                dumpMemInfo(pw, mem, checkin, dumpInfo, dumpDalvik);
+                dumpMemInfo(pw, mem, checkin, dumpFullInfo, dumpDalvik);
             } finally {
                 pw.flush();
             }
         }
 
         private void dumpMemInfo(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin,
-                boolean dumpInfo, boolean dumpDalvik) {
+                boolean dumpFullInfo, boolean dumpDalvik) {
             long nativeMax = Debug.getNativeHeapSize() / 1024;
             long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
             long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
@@ -1036,20 +1037,37 @@
             }
 
             // otherwise, show human-readable format
-            printRow(pw, HEAP_COLUMN, "", "Pss", "Pss","Shared", "Private", "Shared", "Private",
-                    "Heap", "Heap", "Heap");
-            printRow(pw, HEAP_COLUMN, "", "Total", "Clean", "Dirty", "Dirty", "Clean", "Clean",
-                    "Size", "Alloc", "Free");
-            printRow(pw, HEAP_COLUMN, "", "------", "------", "------", "------", "------",
-                    "------", "------", "------", "------");
-            printRow(pw, HEAP_COLUMN, "Native Heap", memInfo.nativePss, memInfo.nativeSwappablePss,
-                    memInfo.nativeSharedDirty,
-                    memInfo.nativePrivateDirty, memInfo.nativeSharedClean,
-                    memInfo.nativePrivateClean, nativeMax, nativeAllocated, nativeFree);
-            printRow(pw, HEAP_COLUMN, "Dalvik Heap", memInfo.dalvikPss, memInfo.dalvikSwappablePss,
-                    memInfo.dalvikSharedDirty,
-                    memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean,
-                    memInfo.dalvikPrivateClean, dalvikMax, dalvikAllocated, dalvikFree);
+            if (dumpFullInfo) {
+                printRow(pw, HEAP_FULL_COLUMN, "", "Pss", "Pss", "Shared", "Private",
+                        "Shared", "Private", "Heap", "Heap", "Heap");
+                printRow(pw, HEAP_FULL_COLUMN, "", "Total", "Clean", "Dirty", "Dirty",
+                        "Clean", "Clean", "Size", "Alloc", "Free");
+                printRow(pw, HEAP_FULL_COLUMN, "", "------", "------", "------", "------",
+                        "------", "------", "------", "------", "------");
+                printRow(pw, HEAP_FULL_COLUMN, "Native Heap", memInfo.nativePss,
+                        memInfo.nativeSwappablePss, memInfo.nativeSharedDirty,
+                        memInfo.nativePrivateDirty, memInfo.nativeSharedClean,
+                        memInfo.nativePrivateClean, nativeMax, nativeAllocated, nativeFree);
+                printRow(pw, HEAP_FULL_COLUMN, "Dalvik Heap", memInfo.dalvikPss,
+                        memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty,
+                        memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean,
+                        memInfo.dalvikPrivateClean, dalvikMax, dalvikAllocated, dalvikFree);
+            } else {
+                printRow(pw, HEAP_COLUMN, "", "Pss", "Pss", "Private",
+                        "Private", "Heap", "Heap", "Heap");
+                printRow(pw, HEAP_COLUMN, "", "Total", "Clean", "Dirty",
+                        "Clean", "Size", "Alloc", "Free");
+                printRow(pw, HEAP_COLUMN, "", "------", "------", "------",
+                        "------", "------", "------", "------");
+                printRow(pw, HEAP_COLUMN, "Native Heap", memInfo.nativePss,
+                        memInfo.nativeSwappablePss,
+                        memInfo.nativePrivateDirty,
+                        memInfo.nativePrivateClean, nativeMax, nativeAllocated, nativeFree);
+                printRow(pw, HEAP_COLUMN, "Dalvik Heap", memInfo.dalvikPss,
+                        memInfo.dalvikSwappablePss,
+                        memInfo.dalvikPrivateDirty,
+                        memInfo.dalvikPrivateClean, dalvikMax, dalvikAllocated, dalvikFree);
+            }
 
             int otherPss = memInfo.otherPss;
             int otherSwappablePss = memInfo.otherSwappablePss;
@@ -1067,9 +1085,15 @@
                 final int myPrivateClean = memInfo.getOtherPrivateClean(i);
                 if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
                         || mySharedClean != 0 || myPrivateClean != 0) {
-                    printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
-                            myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
-                            mySharedClean, myPrivateClean, "", "", "");
+                    if (dumpFullInfo) {
+                        printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
+                                myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
+                                mySharedClean, myPrivateClean, "", "", "");
+                    } else {
+                        printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
+                                myPss, mySwappablePss, myPrivateDirty,
+                                myPrivateClean, "", "", "");
+                    }
                     otherPss -= myPss;
                     otherSwappablePss -= mySwappablePss;
                     otherSharedDirty -= mySharedDirty;
@@ -1079,16 +1103,27 @@
                 }
             }
 
-
-
-            printRow(pw, HEAP_COLUMN, "Unknown", otherPss, otherSwappablePss, otherSharedDirty,
-                    otherPrivateDirty, otherSharedClean, otherPrivateClean,"", "", "");
-            printRow(pw, HEAP_COLUMN, "TOTAL", memInfo.getTotalPss(),
-                    memInfo.getTotalSwappablePss(),
-                    memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(),
-                    memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(),
-                    nativeMax+dalvikMax,
-                    nativeAllocated+dalvikAllocated, nativeFree+dalvikFree);
+            if (dumpFullInfo) {
+                printRow(pw, HEAP_FULL_COLUMN, "Unknown", otherPss, otherSwappablePss,
+                        otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean,
+                        "", "", "");
+                printRow(pw, HEAP_FULL_COLUMN, "TOTAL", memInfo.getTotalPss(),
+                        memInfo.getTotalSwappablePss(),
+                        memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(),
+                        memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(),
+                        nativeMax+dalvikMax,
+                        nativeAllocated+dalvikAllocated, nativeFree+dalvikFree);
+            } else {
+                printRow(pw, HEAP_COLUMN, "Unknown", otherPss, otherSwappablePss,
+                        otherPrivateDirty, otherPrivateClean,
+                        "", "", "");
+                printRow(pw, HEAP_COLUMN, "TOTAL", memInfo.getTotalPss(),
+                        memInfo.getTotalSwappablePss(),
+                        memInfo.getTotalPrivateDirty(),
+                        memInfo.getTotalPrivateClean(),
+                        nativeMax+dalvikMax,
+                        nativeAllocated+dalvikAllocated, nativeFree+dalvikFree);
+            }
 
             if (dumpDalvik) {
                 pw.println(" ");
@@ -1104,9 +1139,15 @@
                     final int myPrivateClean = memInfo.getOtherPrivateClean(i);
                     if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
                             || mySharedClean != 0 || myPrivateClean != 0) {
-                        printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
-                                myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
-                                mySharedClean, myPrivateClean, "", "", "");
+                        if (dumpFullInfo) {
+                            printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
+                                    myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
+                                    mySharedClean, myPrivateClean, "", "", "");
+                        } else {
+                            printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
+                                    myPss, mySwappablePss, myPrivateDirty,
+                                    myPrivateClean, "", "", "");
+                        }
                     }
                 }
             }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 25c02df..9a77377 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -27,9 +27,11 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
+import android.content.UriPermission;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ConfigurationInfo;
 import android.content.pm.IPackageDataObserver;
+import android.content.pm.ParceledListSlice;
 import android.content.pm.ProviderInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
@@ -210,7 +212,10 @@
             Uri uri, int mode) throws RemoteException;
     public void revokeUriPermission(IApplicationThread caller, Uri uri,
             int mode) throws RemoteException;
-    
+    public void takePersistableUriPermission(Uri uri, int modeFlags) throws RemoteException;
+    public void releasePersistableUriPermission(Uri uri, int modeFlags) throws RemoteException;
+    public ParceledListSlice<UriPermission> getPersistedUriPermissions() throws RemoteException;
+
     public void showWaitingForDebugger(IApplicationThread who, boolean waiting)
             throws RemoteException;
     
@@ -399,10 +404,6 @@
 
     public void restart() throws RemoteException;
 
-    public Uri[] getGrantedUriPermissions(
-            String sourcePackage, String targetPackage, int modeFlags, int modeMask)
-            throws RemoteException;
-
     public void performIdleMaintenance() throws RemoteException;
 
     /*
@@ -686,6 +687,8 @@
     int NOTIFY_ACTIVITY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+175;
     int REPORT_ACTIVITY_FULLY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+176;
     int RESTART_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+177;
-    int GET_GRANTED_URI_PERMISSIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+178;
-    int PERFORM_IDLE_MAINTENANCE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+179;
+    int PERFORM_IDLE_MAINTENANCE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+178;
+    int TAKE_PERSISTABLE_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+179;
+    int RELEASE_PERSISTABLE_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+180;
+    int GET_PERSISTED_URI_PERMISSIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+181;
 }
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 2750d68..95fb6858 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -266,12 +266,18 @@
 
     /** @hide */
     protected abstract IContentProvider acquireProvider(Context c, String name);
-    /** Providing a default implementation of this, to avoid having to change
-     * a lot of other things, but implementations of ContentResolver should
-     * implement it. @hide */
+
+    /**
+     * Providing a default implementation of this, to avoid having to change a
+     * lot of other things, but implementations of ContentResolver should
+     * implement it.
+     *
+     * @hide
+     */
     protected IContentProvider acquireExistingProvider(Context c, String name) {
         return acquireProvider(c, name);
     }
+
     /** @hide */
     public abstract boolean releaseProvider(IContentProvider icp);
     /** @hide */
@@ -1616,54 +1622,50 @@
     }
 
     /**
-     * Return list of all Uri permissions that have been granted <em>to</em> the
-     * calling package, and which exactly match the requested flags. For
-     * example, to return all Uris that the calling application has
-     * <em>non-persistent</em> read access to:
+     * Take a persistable Uri permission grant that has been offered. Once
+     * taken, the permission grant will be remembered across device reboots.
+     * Only Uri permissions granted with
+     * {@link Intent#FLAG_GRANT_PERSISTABLE_URI_PERMISSION} can be persisted. If
+     * the grant has already been persisted, taking it again will touch
+     * {@link UriPermission#getPersistedTime()}.
      *
-     * <pre class="prettyprint">
-     * getIncomingUriPermissionGrants(Intent.FLAG_GRANT_READ_URI_PERMISSION,
-     *         Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
-     * </pre>
-     *
-     * @param modeFlags any combination of
-     *            {@link Intent#FLAG_GRANT_READ_URI_PERMISSION},
-     *            {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, or
-     *            {@link Intent#FLAG_PERSIST_GRANT_URI_PERMISSION}.
-     * @param modeMask mask indicating which flags must match.
+     * @see #getPersistedUriPermissions()
      */
-    public Uri[] getIncomingUriPermissionGrants(int modeFlags, int modeMask) {
+    public void takePersistableUriPermission(Uri uri, int modeFlags) {
         try {
-            return ActivityManagerNative.getDefault()
-                    .getGrantedUriPermissions(null, getPackageName(), modeFlags, modeMask);
+            ActivityManagerNative.getDefault().takePersistableUriPermission(uri, modeFlags);
         } catch (RemoteException e) {
-            return new Uri[0];
         }
     }
 
     /**
-     * Return list of all Uri permissions that have been granted <em>from</em> the
-     * calling package, and which exactly match the requested flags. For
-     * example, to return all Uris that the calling application has granted
-     * <em>non-persistent</em> read access to:
+     * Relinquish a persisted Uri permission grant. The Uri must have been
+     * previously made persistent with
+     * {@link #takePersistableUriPermission(Uri, int)}. Any non-persistent
+     * grants to the calling package will remain intact.
      *
-     * <pre class="prettyprint">
-     * getOutgoingUriPermissionGrants(Intent.FLAG_GRANT_READ_URI_PERMISSION,
-     *         Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
-     * </pre>
-     *
-     * @param modeFlags any combination of
-     *            {@link Intent#FLAG_GRANT_READ_URI_PERMISSION},
-     *            {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, or
-     *            {@link Intent#FLAG_PERSIST_GRANT_URI_PERMISSION}.
-     * @param modeMask mask indicating which flags must match.
+     * @see #getPersistedUriPermissions()
      */
-    public Uri[] getOutgoingUriPermissionGrants(int modeFlags, int modeMask) {
+    public void releasePersistableUriPermission(Uri uri, int modeFlags) {
         try {
-            return ActivityManagerNative.getDefault()
-                    .getGrantedUriPermissions(getPackageName(), null, modeFlags, modeMask);
+            ActivityManagerNative.getDefault().releasePersistableUriPermission(uri, modeFlags);
         } catch (RemoteException e) {
-            return new Uri[0];
+        }
+    }
+
+    /**
+     * Return list of all Uri permission grants that have been persisted for the
+     * calling app. Only persistable grants taken with
+     * {@link #takePersistableUriPermission(Uri, int)} are returned.
+     *
+     * @see #takePersistableUriPermission(Uri, int)
+     * @see #releasePersistableUriPermission(Uri, int)
+     */
+    public List<UriPermission> getPersistedUriPermissions() {
+        try {
+            return ActivityManagerNative.getDefault().getPersistedUriPermissions().getList();
+        } catch (RemoteException e) {
+            throw new RuntimeException("Activity manager has died", e);
         }
     }
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index dcc6328..047f175 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3339,12 +3339,18 @@
 
     /**
      * When combined with {@link #FLAG_GRANT_READ_URI_PERMISSION} and/or
-     * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, the grant will be remembered
-     * until explicitly revoked with
-     * {@link Context#revokeUriPermission(Uri, int)}. These grants persist
-     * across device reboots.
+     * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, the Uri permission grant can be
+     * persisted across device reboots until explicitly revoked with
+     * {@link Context#revokeUriPermission(Uri, int)}. This flag only offers the
+     * grant for possible persisting; the receiving application must call
+     * {@link ContentResolver#takePersistableUriPermission(Uri, int)} to
+     * actually persist.
+     *
+     * @see ContentResolver#takePersistableUriPermission(Uri, int)
+     * @see ContentResolver#releasePersistableUriPermission(Uri, int)
+     * @see ContentResolver#getPersistedUriPermissions()
      */
-    public static final int FLAG_PERSIST_GRANT_URI_PERMISSION = 0x00000040;
+    public static final int FLAG_GRANT_PERSISTABLE_URI_PERMISSION = 0x00000040;
 
     /**
      * If set, the new activity is not kept in the history stack.  As soon as
@@ -7173,7 +7179,7 @@
                     setClipData(target.getClipData());
                     addFlags(target.getFlags()
                             & (FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION
-                                    | FLAG_PERSIST_GRANT_URI_PERMISSION));
+                                    | FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
                     return true;
                 } else {
                     return false;
diff --git a/core/java/android/content/UriPermission.java b/core/java/android/content/UriPermission.java
new file mode 100644
index 0000000..df9200d
--- /dev/null
+++ b/core/java/android/content/UriPermission.java
@@ -0,0 +1,117 @@
+/*
+ * 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.content;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Description of a single Uri permission grant. This grants may have been
+ * created via {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}, etc when sending
+ * an {@link Intent}, or explicitly through
+ * {@link Context#grantUriPermission(String, android.net.Uri, int)}.
+ *
+ * @see ContentResolver#getPersistedUriPermissions()
+ */
+public final class UriPermission implements Parcelable {
+    private final Uri mUri;
+    private final int mModeFlags;
+    private final long mPersistedTime;
+
+    /**
+     * Value returned when a permission has not been persisted.
+     */
+    public static final long INVALID_TIME = Long.MIN_VALUE;
+
+    /** {@hide} */
+    public UriPermission(Uri uri, int modeFlags, long persistedTime) {
+        mUri = uri;
+        mModeFlags = modeFlags;
+        mPersistedTime = persistedTime;
+    }
+
+    /** {@hide} */
+    public UriPermission(Parcel in) {
+        mUri = in.readParcelable(null);
+        mModeFlags = in.readInt();
+        mPersistedTime = in.readLong();
+    }
+
+    /**
+     * Return the Uri this permission pertains to.
+     */
+    public Uri getUri() {
+        return mUri;
+    }
+
+    /**
+     * Returns if this permission offers read access.
+     */
+    public boolean isReadPermission() {
+        return (mModeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0;
+    }
+
+    /**
+     * Returns if this permission offers write access.
+     */
+    public boolean isWritePermission() {
+        return (mModeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0;
+    }
+
+    /**
+     * Return the time when this permission was first persisted, in milliseconds
+     * since January 1, 1970 00:00:00.0 UTC. Returns {@link #INVALID_TIME} if
+     * not persisted.
+     *
+     * @see ContentResolver#takePersistableUriPermission(Uri, int)
+     * @see System#currentTimeMillis()
+     */
+    public long getPersistedTime() {
+        return mPersistedTime;
+    }
+
+    @Override
+    public String toString() {
+        return "UriPermission {uri=" + mUri + ", modeFlags=" + mModeFlags + ", persistedTime="
+                + mPersistedTime + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mUri, flags);
+        dest.writeInt(mModeFlags);
+        dest.writeLong(mPersistedTime);
+    }
+
+    public static final Creator<UriPermission> CREATOR = new Creator<UriPermission>() {
+        @Override
+        public UriPermission createFromParcel(Parcel source) {
+            return new UriPermission(source);
+        }
+
+        @Override
+        public UriPermission[] newArray(int size) {
+            return new UriPermission[size];
+        }
+    };
+}
diff --git a/core/java/android/hardware/ICameraService.aidl b/core/java/android/hardware/ICameraService.aidl
index fc54828..542af6a 100644
--- a/core/java/android/hardware/ICameraService.aidl
+++ b/core/java/android/hardware/ICameraService.aidl
@@ -22,6 +22,7 @@
 import android.hardware.IProCameraCallbacks;
 import android.hardware.camera2.ICameraDeviceUser;
 import android.hardware.camera2.ICameraDeviceCallbacks;
+import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.utils.BinderHolder;
 import android.hardware.ICameraServiceListener;
 import android.hardware.CameraInfo;
@@ -58,4 +59,6 @@
 
     int addListener(ICameraServiceListener listener);
     int removeListener(ICameraServiceListener listener);
+
+    int getCameraCharacteristics(int cameraId, out CameraMetadataNative info);
 }
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index af0512e..798ad7b 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -176,10 +176,17 @@
             }
         }
 
-        // TODO: implement and call a service function to get the capabilities on C++ side
+        CameraMetadataNative info = new CameraMetadataNative();
+        try {
+            mCameraService.getCameraCharacteristics(Integer.valueOf(cameraId), info);
+        } catch(CameraRuntimeException e) {
+            throw e.asChecked();
+        } catch(RemoteException e) {
+            // impossible
+            return null;
+        }
 
-        // TODO: get properties from service
-        return new CameraCharacteristics(new CameraMetadataNative());
+        return new CameraCharacteristics(info);
     }
 
     /**
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 0c718f4..0a1ffc9 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -1031,7 +1031,13 @@
     /** @hide */
     public static final int MEMINFO_SLAB = 5;
     /** @hide */
-    public static final int MEMINFO_COUNT = 6;
+    public static final int MEMINFO_SWAP_TOTAL = 6;
+    /** @hide */
+    public static final int MEMINFO_SWAP_FREE = 7;
+    /** @hide */
+    public static final int MEMINFO_ZRAM_TOTAL = 8;
+    /** @hide */
+    public static final int MEMINFO_COUNT = 9;
 
     /**
      * Retrieves /proc/meminfo.  outSizes is filled with fields
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 3f33e80..8f22312 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -21,10 +21,7 @@
 
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
-import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
 import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
 import android.graphics.Bitmap;
@@ -38,8 +35,6 @@
 import android.os.RemoteException;
 import android.util.Log;
 
-import com.google.android.collect.Lists;
-
 import libcore.io.ErrnoException;
 import libcore.io.IoUtils;
 import libcore.io.Libcore;
@@ -624,37 +619,6 @@
     }
 
     /**
-     * Return list of all documents that the calling package has "open." These
-     * are Uris matching {@link DocumentsContract} to which persistent
-     * read/write access has been granted, usually through
-     * {@link Intent#ACTION_OPEN_DOCUMENT} or
-     * {@link Intent#ACTION_CREATE_DOCUMENT}.
-     *
-     * @see Context#grantUriPermission(String, Uri, int)
-     * @see Context#revokeUriPermission(Uri, int)
-     * @see ContentResolver#getIncomingUriPermissionGrants(int, int)
-     */
-    public static Uri[] getOpenDocuments(Context context) {
-        final int openedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION
-                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION;
-        final Uri[] uris = context.getContentResolver()
-                .getIncomingUriPermissionGrants(openedFlags, openedFlags);
-
-        // Filter to only include document providers
-        final PackageManager pm = context.getPackageManager();
-        final List<Uri> result = Lists.newArrayList();
-        for (Uri uri : uris) {
-            final ProviderInfo info = pm.resolveContentProvider(
-                    uri.getAuthority(), PackageManager.GET_META_DATA);
-            if (info.metaData.containsKey(META_DATA_DOCUMENT_PROVIDER)) {
-                result.add(uri);
-            }
-        }
-
-        return result.toArray(new Uri[result.size()]);
-    }
-
-    /**
      * Return thumbnail representing the document at the given Uri. Callers are
      * responsible for their own in-memory caching.
      *
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index bc4e28b..337b735 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -387,7 +387,7 @@
                     context.grantUriPermission(getCallingPackage(), newDocumentUri,
                             Intent.FLAG_GRANT_READ_URI_PERMISSION
                             | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-                            | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
+                            | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
                 }
 
             } else if (METHOD_DELETE_DOCUMENT.equals(method)) {
@@ -396,7 +396,7 @@
                 // Document no longer exists, clean up any grants
                 context.revokeUriPermission(documentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION
                         | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-                        | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
+                        | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
 
             } else {
                 throw new UnsupportedOperationException("Method not supported " + method);
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
index dd2e006..45a38be 100644
--- a/core/java/com/android/internal/policy/IKeyguardService.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -42,4 +42,5 @@
     oneway void setCurrentUser(int userId);
     oneway void showAssistant();
     oneway void dispatch(in MotionEvent event);
+    oneway void launchCamera();
 }
diff --git a/core/java/com/android/internal/util/MemInfoReader.java b/core/java/com/android/internal/util/MemInfoReader.java
index ad65433..5f240f7 100644
--- a/core/java/com/android/internal/util/MemInfoReader.java
+++ b/core/java/com/android/internal/util/MemInfoReader.java
@@ -16,47 +16,11 @@
 
 package com.android.internal.util;
 
-import java.io.FileInputStream;
-
 import android.os.Debug;
 import android.os.StrictMode;
 
-public class MemInfoReader {
-    byte[] mBuffer = new byte[1024];
-
-    private long mTotalSize;
-    private long mFreeSize;
-    private long mCachedSize;
-
-    private boolean matchText(byte[] buffer, int index, String text) {
-        int N = text.length();
-        if ((index+N) >= buffer.length) {
-            return false;
-        }
-        for (int i=0; i<N; i++) {
-            if (buffer[index+i] != text.charAt(i)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private long extractMemValue(byte[] buffer, int index) {
-        while (index < buffer.length && buffer[index] != '\n') {
-            if (buffer[index] >= '0' && buffer[index] <= '9') {
-                int start = index;
-                index++;
-                while (index < buffer.length && buffer[index] >= '0'
-                    && buffer[index] <= '9') {
-                    index++;
-                }
-                String str = new String(buffer, 0, start, index-start);
-                return ((long)Integer.parseInt(str)) * 1024;
-            }
-            index++;
-        }
-        return 0;
-    }
+public final class MemInfoReader {
+    final long[] mInfos = new long[Debug.MEMINFO_COUNT];
 
     public void readMemInfo() {
         // Permit disk reads here, as /proc/meminfo isn't really "on
@@ -64,25 +28,57 @@
         // /proc/ and /sys/ files perhaps?
         StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
         try {
-            long[] infos = new long[Debug.MEMINFO_COUNT];
-            Debug.getMemInfo(infos);
-            mTotalSize = infos[Debug.MEMINFO_TOTAL] * 1024;
-            mFreeSize = infos[Debug.MEMINFO_FREE] * 1024;
-            mCachedSize = infos[Debug.MEMINFO_CACHED] * 1024;
+            Debug.getMemInfo(mInfos);
         } finally {
             StrictMode.setThreadPolicy(savedPolicy);
         }
     }
 
     public long getTotalSize() {
-        return mTotalSize;
+        return mInfos[Debug.MEMINFO_TOTAL] * 1024;
     }
 
     public long getFreeSize() {
-        return mFreeSize;
+        return mInfos[Debug.MEMINFO_FREE] * 1024;
     }
 
     public long getCachedSize() {
-        return mCachedSize;
+        return mInfos[Debug.MEMINFO_CACHED] * 1024;
+    }
+
+    public long getTotalSizeKb() {
+        return mInfos[Debug.MEMINFO_TOTAL];
+    }
+
+    public long getFreeSizeKb() {
+        return mInfos[Debug.MEMINFO_FREE];
+    }
+
+    public long getCachedSizeKb() {
+        return mInfos[Debug.MEMINFO_CACHED];
+    }
+
+    public long getBuffersSizeKb() {
+        return mInfos[Debug.MEMINFO_BUFFERS];
+    }
+
+    public long getShmemSizeKb() {
+        return mInfos[Debug.MEMINFO_SHMEM];
+    }
+
+    public long getSlabSizeKb() {
+        return mInfos[Debug.MEMINFO_SLAB];
+    }
+
+    public long getSwapTotalSizeKb() {
+        return mInfos[Debug.MEMINFO_SWAP_TOTAL];
+    }
+
+    public long getSwapFreeSizeKb() {
+        return mInfos[Debug.MEMINFO_SWAP_FREE];
+    }
+
+    public long getZramTotalSizeKb() {
+        return mInfos[Debug.MEMINFO_ZRAM_TOTAL];
     }
 }
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index f40f48c..0b74cf3 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -931,6 +931,15 @@
         out.attribute(null, name, Integer.toString(value));
     }
 
+    public static long readLongAttribute(XmlPullParser in, String name, long defaultValue) {
+        final String value = in.getAttributeValue(null, name);
+        try {
+            return Long.parseLong(value);
+        } catch (NumberFormatException e) {
+            return defaultValue;
+        }
+    }
+
     public static long readLongAttribute(XmlPullParser in, String name) throws IOException {
         final String value = in.getAttributeValue(null, name);
         try {
diff --git a/core/java/com/android/internal/widget/SubtitleView.java b/core/java/com/android/internal/widget/SubtitleView.java
index 356401c..e30c1ff 100644
--- a/core/java/com/android/internal/widget/SubtitleView.java
+++ b/core/java/com/android/internal/widget/SubtitleView.java
@@ -31,7 +31,6 @@
 import android.text.Layout.Alignment;
 import android.text.StaticLayout;
 import android.text.TextPaint;
-import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
@@ -52,14 +51,12 @@
     /** Temporary rectangle used for computing line bounds. */
     private final RectF mLineBounds = new RectF();
 
-    /** Temporary array used for computing line wrapping. */
-    private float[] mTextWidths;
-
     /** Reusable string builder used for holding text. */
     private final StringBuilder mText = new StringBuilder();
-    private final StringBuilder mBreakText = new StringBuilder();
 
-    private TextPaint mPaint;
+    private Alignment mAlignment;
+    private TextPaint mTextPaint;
+    private Paint mPaint;
 
     private int mForegroundColor;
     private int mBackgroundColor;
@@ -122,11 +119,12 @@
         mShadowOffsetX = res.getDimension(com.android.internal.R.dimen.subtitle_shadow_offset);
         mShadowOffsetY = mShadowOffsetX;
 
-        final TextPaint paint = new TextPaint();
-        paint.setAntiAlias(true);
-        paint.setSubpixelText(true);
+        mTextPaint = new TextPaint();
+        mTextPaint.setAntiAlias(true);
+        mTextPaint.setSubpixelText(true);
 
-        mPaint = paint;
+        mPaint = new Paint();
+        mPaint.setAntiAlias(true);
 
         setText(text);
         setTextSize(textSize);
@@ -174,21 +172,30 @@
     public void setTextSize(float size) {
         final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
         final float pixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, size, metrics);
-        if (mPaint.getTextSize() != size) {
-            mHasMeasurements = false;
+        if (mTextPaint.getTextSize() != size) {
+            mTextPaint.setTextSize(size);
             mInnerPaddingX = (int) (size * INNER_PADDING_RATIO + 0.5f);
-            mPaint.setTextSize(size);
 
-            requestLayout();
+            mHasMeasurements = false;
+            forceLayout();
         }
     }
 
     public void setTypeface(Typeface typeface) {
-        if (mPaint.getTypeface() != typeface) {
-            mHasMeasurements = false;
-            mPaint.setTypeface(typeface);
+        if (mTextPaint.getTypeface() != typeface) {
+            mTextPaint.setTypeface(typeface);
 
-            requestLayout();
+            mHasMeasurements = false;
+            forceLayout();
+        }
+    }
+
+    public void setAlignment(Alignment textAlignment) {
+        if (mAlignment != textAlignment) {
+            mAlignment = textAlignment;
+
+            mHasMeasurements = false;
+            forceLayout();
         }
     }
 
@@ -222,63 +229,19 @@
         }
 
         // Account for padding.
-        final int paddingX = mPaddingLeft + mPaddingRight + mInnerPaddingX;
+        final int paddingX = mPaddingLeft + mPaddingRight + mInnerPaddingX * 2;
         maxWidth -= paddingX;
-
         if (maxWidth <= 0) {
             return false;
         }
 
-        final TextPaint paint = mPaint;
-        final CharSequence text = mText;
-        final int textLength = text.length();
-        if (mTextWidths == null || mTextWidths.length < textLength) {
-            mTextWidths = new float[textLength];
-        }
-
-        final float[] textWidths = mTextWidths;
-        paint.getTextWidths(text, 0, textLength, textWidths);
-
-        // Compute total length.
-        float runLength = 0;
-        for (int i = 0; i < textLength; i++) {
-            runLength += textWidths[i];
-        }
-
-        final int lineCount = (int) (runLength / maxWidth) + 1;
-        final int lineLength = (int) (runLength / lineCount);
-
-        // Build line break buffer.
-        final StringBuilder breakText = mBreakText;
-        breakText.setLength(0);
-
-        int line = 0;
-        int lastBreak = 0;
-        int maxRunLength = 0;
-        runLength = 0;
-        for (int i = 0; i < textLength; i++) {
-            if (runLength > lineLength) {
-                final CharSequence sequence = text.subSequence(lastBreak, i);
-                final int trimmedLength = TextUtils.getTrimmedLength(sequence);
-                breakText.append(sequence, 0, trimmedLength);
-                breakText.append('\n');
-                lastBreak = i;
-                runLength = 0;
-            }
-
-            runLength += textWidths[i];
-
-            if (runLength > maxRunLength) {
-                maxRunLength = (int) Math.ceil(runLength);
-            }
-        }
-        breakText.append(text.subSequence(lastBreak, textLength));
-
+        // TODO: Implement minimum-difference line wrapping. Adding the results
+        // of Paint.getTextWidths() seems to return different values than
+        // StaticLayout.getWidth(), so this is non-trivial.
         mHasMeasurements = true;
         mLastMeasuredWidth = maxWidth;
-
-        mLayout = new StaticLayout(breakText, paint, maxRunLength, Alignment.ALIGN_LEFT,
-                mSpacingMult, mSpacingAdd, true);
+        mLayout = new StaticLayout(
+                mText, mTextPaint, maxWidth, mAlignment, mSpacingMult, mSpacingAdd, true);
 
         return true;
     }
@@ -316,54 +279,50 @@
         final int innerPaddingX = mInnerPaddingX;
         c.translate(mPaddingLeft + innerPaddingX, mPaddingTop);
 
-        final RectF bounds = mLineBounds;
         final int lineCount = layout.getLineCount();
-        final Paint paint = layout.getPaint();
-        paint.setShadowLayer(0, 0, 0, 0);
+        final Paint textPaint = mTextPaint;
+        final Paint paint = mPaint;
+        final RectF bounds = mLineBounds;
 
-        final int backgroundColor = mBackgroundColor;
-        if (Color.alpha(backgroundColor) > 0) {
-            paint.setColor(backgroundColor);
-            paint.setStyle(Style.FILL);
-
+        if (Color.alpha(mBackgroundColor) > 0) {
             final float cornerRadius = mCornerRadius;
             float previousBottom = layout.getLineTop(0);
 
+            paint.setColor(mBackgroundColor);
+            paint.setStyle(Style.FILL);
+
             for (int i = 0; i < lineCount; i++) {
-                bounds.left = layout.getLineLeft(i) - innerPaddingX;
+                bounds.left = layout.getLineLeft(i) -innerPaddingX;
                 bounds.right = layout.getLineRight(i) + innerPaddingX;
                 bounds.top = previousBottom;
                 bounds.bottom = layout.getLineBottom(i);
-
                 previousBottom = bounds.bottom;
 
                 c.drawRoundRect(bounds, cornerRadius, cornerRadius, paint);
             }
         }
 
-        final int edgeType = mEdgeType;
-        if (edgeType == CaptionStyle.EDGE_TYPE_OUTLINE) {
-            paint.setColor(mEdgeColor);
-            paint.setStyle(Style.FILL_AND_STROKE);
-            paint.setStrokeJoin(Join.ROUND);
-            paint.setStrokeWidth(mOutlineWidth);
+        if (mEdgeType == CaptionStyle.EDGE_TYPE_OUTLINE) {
+            textPaint.setStrokeJoin(Join.ROUND);
+            textPaint.setStrokeWidth(mOutlineWidth);
+            textPaint.setColor(mEdgeColor);
+            textPaint.setStyle(Style.FILL_AND_STROKE);
 
             for (int i = 0; i < lineCount; i++) {
                 layout.drawText(c, i, i);
             }
+        } else if (mEdgeType == CaptionStyle.EDGE_TYPE_DROP_SHADOW) {
+            textPaint.setShadowLayer(mShadowRadius, mShadowOffsetX, mShadowOffsetY, mEdgeColor);
         }
 
-        if (edgeType == CaptionStyle.EDGE_TYPE_DROP_SHADOW) {
-            paint.setShadowLayer(mShadowRadius, mShadowOffsetX, mShadowOffsetY, mEdgeColor);
-        }
-
-        paint.setColor(mForegroundColor);
-        paint.setStyle(Style.FILL);
+        textPaint.setColor(mForegroundColor);
+        textPaint.setStyle(Style.FILL);
 
         for (int i = 0; i < lineCount; i++) {
             layout.drawText(c, i, i);
         }
 
+        textPaint.setShadowLayer(0, 0, 0, 0);
         c.restoreToCount(saveCount);
     }
 }
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 60540f4..aa5b254 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -516,6 +516,19 @@
     return android_os_Debug_getPssPid(env, clazz, getpid(), NULL);
 }
 
+enum {
+    MEMINFO_TOTAL,
+    MEMINFO_FREE,
+    MEMINFO_BUFFERS,
+    MEMINFO_CACHED,
+    MEMINFO_SHMEM,
+    MEMINFO_SLAB,
+    MEMINFO_SWAP_TOTAL,
+    MEMINFO_SWAP_FREE,
+    MEMINFO_ZRAM_TOTAL,
+    MEMINFO_COUNT
+};
+
 static void android_os_Debug_getMemInfo(JNIEnv *env, jobject clazz, jlongArray out)
 {
     char buffer[1024];
@@ -529,15 +542,15 @@
     int fd = open("/proc/meminfo", O_RDONLY);
 
     if (fd < 0) {
-        printf("Unable to open /proc/meminfo: %s\n", strerror(errno));
+        ALOGW("Unable to open /proc/meminfo: %s\n", strerror(errno));
         return;
     }
 
-    const int len = read(fd, buffer, sizeof(buffer)-1);
+    int len = read(fd, buffer, sizeof(buffer)-1);
     close(fd);
 
     if (len < 0) {
-        printf("Empty /proc/meminfo");
+        ALOGW("Empty /proc/meminfo");
         return;
     }
     buffer[len] = 0;
@@ -549,6 +562,8 @@
             "Cached:",
             "Shmem:",
             "Slab:",
+            "SwapTotal:",
+            "SwapFree:",
             NULL
     };
     static const int tagsLen[] = {
@@ -558,12 +573,14 @@
             7,
             6,
             5,
+            10,
+            9,
             0
     };
-    long mem[] = { 0, 0, 0, 0, 0, 0 };
+    long mem[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
 
     char* p = buffer;
-    while (*p && numFound < 6) {
+    while (*p && numFound < 8) {
         int i = 0;
         while (tags[i]) {
             if (strncmp(p, tags[i], tagsLen[i]) == 0) {
@@ -587,7 +604,20 @@
         if (*p) p++;
     }
 
+    fd = open("/sys/block/zram0/mem_used_total", O_RDONLY);
+    if (fd >= 0) {
+        len = read(fd, buffer, sizeof(buffer)-1);
+        close(fd);
+        if (len > 0) {
+            buffer[len] = 0;
+            mem[MEMINFO_ZRAM_TOTAL] = atoll(buffer);
+        }
+    }
+
     int maxNum = env->GetArrayLength(out);
+    if (maxNum > MEMINFO_COUNT) {
+        maxNum = MEMINFO_COUNT;
+    }
     jlong* outArray = env->GetLongArrayElements(out, 0);
     if (outArray != NULL) {
         for (int i=0; i<maxNum && tags[i]; i++) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4c15e18..b41b5b5 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1403,6 +1403,27 @@
         android:label="@string/permlab_expandStatusBar"
         android:description="@string/permdesc_expandStatusBar" />
 
+    <!-- ============================================================== -->
+    <!-- Permissions related to adding/removing shortcuts from Launcher -->
+    <!-- ============================================================== -->
+    <eat-comment />
+
+    <!-- Allows an application to install a shortcut in Launcher -->
+    <permission
+        android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="dangerous"
+        android:label="@string/permlab_install_shortcut"
+        android:description="@string/permdesc_install_shortcut" />
+
+        <!-- Allows an application to uninstall a shortcut in Launcher -->
+    <permission
+        android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="dangerous"
+        android:label="@string/permlab_uninstall_shortcut"
+        android:description="@string/permdesc_uninstall_shortcut"/>
+
     <!-- ==================================================== -->
     <!-- Permissions related to accessing sync settings   -->
     <!-- ==================================================== -->
diff --git a/core/res/res/drawable-hdpi/toast_frame_holo.9.png b/core/res/res/drawable-hdpi/toast_frame_holo.9.png
index ca65994..f0d9b21 100644
--- a/core/res/res/drawable-hdpi/toast_frame_holo.9.png
+++ b/core/res/res/drawable-hdpi/toast_frame_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/toast_frame_holo.9.png b/core/res/res/drawable-mdpi/toast_frame_holo.9.png
index 9e93fe7..458137c 100644
--- a/core/res/res/drawable-mdpi/toast_frame_holo.9.png
+++ b/core/res/res/drawable-mdpi/toast_frame_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/toast_frame_holo.9.png b/core/res/res/drawable-xhdpi/toast_frame_holo.9.png
index 1f63420..f1209a2 100644
--- a/core/res/res/drawable-xhdpi/toast_frame_holo.9.png
+++ b/core/res/res/drawable-xhdpi/toast_frame_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/kg_add_widget.png b/core/res/res/drawable-xxhdpi/kg_add_widget.png
deleted file mode 100644
index 33d839e..0000000
--- a/core/res/res/drawable-xxhdpi/kg_add_widget.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/kg_add_widget_disabled.png b/core/res/res/drawable-xxhdpi/kg_add_widget_disabled.png
deleted file mode 100644
index 405ab30..0000000
--- a/core/res/res/drawable-xxhdpi/kg_add_widget_disabled.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/kg_add_widget_pressed.png b/core/res/res/drawable-xxhdpi/kg_add_widget_pressed.png
deleted file mode 100644
index f23fad5..0000000
--- a/core/res/res/drawable-xxhdpi/kg_add_widget_pressed.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/kg_bouncer_bg_focused.9.png b/core/res/res/drawable-xxhdpi/kg_bouncer_bg_focused.9.png
deleted file mode 100644
index 67e5900..0000000
--- a/core/res/res/drawable-xxhdpi/kg_bouncer_bg_focused.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/kg_bouncer_bg_normal.9.png b/core/res/res/drawable-xxhdpi/kg_bouncer_bg_normal.9.png
deleted file mode 100644
index b474e7d..0000000
--- a/core/res/res/drawable-xxhdpi/kg_bouncer_bg_normal.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/kg_bouncer_bg_pressed.9.png b/core/res/res/drawable-xxhdpi/kg_bouncer_bg_pressed.9.png
deleted file mode 100644
index 5edf225..0000000
--- a/core/res/res/drawable-xxhdpi/kg_bouncer_bg_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/kg_dot_pattern.png b/core/res/res/drawable-xxhdpi/kg_dot_pattern.png
deleted file mode 100644
index 2ce3ba2..0000000
--- a/core/res/res/drawable-xxhdpi/kg_dot_pattern.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/kg_security_grip.9.png b/core/res/res/drawable-xxhdpi/kg_security_grip.9.png
deleted file mode 100644
index 47fd407..0000000
--- a/core/res/res/drawable-xxhdpi/kg_security_grip.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/kg_security_lock_focused.png b/core/res/res/drawable-xxhdpi/kg_security_lock_focused.png
deleted file mode 100644
index e0a2e2a..0000000
--- a/core/res/res/drawable-xxhdpi/kg_security_lock_focused.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/kg_security_lock_normal.png b/core/res/res/drawable-xxhdpi/kg_security_lock_normal.png
deleted file mode 100644
index 2d62c45..0000000
--- a/core/res/res/drawable-xxhdpi/kg_security_lock_normal.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/kg_security_lock_pressed.png b/core/res/res/drawable-xxhdpi/kg_security_lock_pressed.png
deleted file mode 100644
index 97900d5..0000000
--- a/core/res/res/drawable-xxhdpi/kg_security_lock_pressed.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/kg_widget_bg_padded.9.png b/core/res/res/drawable-xxhdpi/kg_widget_bg_padded.9.png
deleted file mode 100644
index a22bc8e..0000000
--- a/core/res/res/drawable-xxhdpi/kg_widget_bg_padded.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/toast_frame_holo.9.png b/core/res/res/drawable-xxhdpi/toast_frame_holo.9.png
index 882b9c6..6c86258 100644
--- a/core/res/res/drawable-xxhdpi/toast_frame_holo.9.png
+++ b/core/res/res/drawable-xxhdpi/toast_frame_holo.9.png
Binary files differ
diff --git a/core/res/res/layout/transient_notification.xml b/core/res/res/layout/transient_notification.xml
index 5523807..daa9faf 100644
--- a/core/res/res/layout/transient_notification.xml
+++ b/core/res/res/layout/transient_notification.xml
@@ -30,7 +30,7 @@
         android:layout_height="wrap_content"
         android:layout_weight="1"
         android:layout_gravity="center_horizontal"
-        android:textAppearance="@style/TextAppearance.Small"
+        android:textAppearance="@style/TextAppearance.Toast"
         android:textColor="@color/bright_foreground_dark"
         android:shadowColor="#BB000000"
         android:shadowRadius="2.75"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 84a0d37..b3cb2a1 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1255,4 +1255,8 @@
     <!-- Threshold (in ms) under which a screen off / screen on will be considered a reset of the
          transient navigation confirmation prompt.-->
     <integer name="config_transient_navigation_confirmation_panic">5000</integer>
+
+    <!-- For some operators, PDU has garbages. To fix it, need to use valid index -->
+    <integer name="config_valid_wappush_index">-1</integer>
+
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index c57873e..c20c427 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -599,6 +599,22 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_expandStatusBar">Allows the app to expand or collapse the status bar.</string>
 
+    <!-- Title of an application permission, listed so the user can install application shortcuts
+    in their Launcher -->
+    <string name="permlab_install_shortcut">install shortcuts</string>
+    <!-- Description of an application permission, listed so the user can install application shortcuts
+    in their Launcher -->
+    <string name="permdesc_install_shortcut">Allows an application to add
+        Homescreen shortcuts without user intervention.</string>
+
+    <!-- Title of an application permission, listed so the user can uninstall application shortcuts
+    in their Launcher -->
+    <string name="permlab_uninstall_shortcut">uninstall shortcuts</string>
+    <!-- Description of an application permission, listed so the user can install application shortcuts
+    in their Launcher -->
+    <string name="permdesc_uninstall_shortcut">Allows the application to remove
+        Homescreen shortcuts without user intervention.</string>
+
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_processOutgoingCalls">reroute outgoing calls</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index ba72a2b..c5dab3b 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -918,6 +918,10 @@
         <item name="android:textSize">30sp</item>
     </style>
 
+    <style name="TextAppearance.Toast">
+        <item name="android:fontFamily">sans-serif-condensed</item>
+    </style>
+
     <style name="Widget.ActivityChooserView">
         <item name="android:gravity">center</item>
         <item name="android:background">@android:drawable/ab_share_pack_holo_dark</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 14b319f..57a4bb7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -316,6 +316,7 @@
   <java-symbol type="integer" name="config_safe_media_volume_index" />
   <java-symbol type="integer" name="config_mobile_mtu" />
   <java-symbol type="integer" name="config_volte_replacement_rat"/>
+  <java-symbol type="integer" name="config_valid_wappush_index" />
 
   <java-symbol type="color" name="tab_indicator_text_v4" />
 
diff --git a/docs/html/sdk/installing/studio.jd b/docs/html/sdk/installing/studio.jd
index 5a47f85..3399ee8 100644
--- a/docs/html/sdk/installing/studio.jd
+++ b/docs/html/sdk/installing/studio.jd
@@ -187,7 +187,7 @@
 
 <div id="main">
 
-<div class="figure" style="width:400px;margin-top:-50px">
+<div class="figure" style="width:400px;margin-top:-20px">
 <img src="{@docRoot}images/tools/android-studio.png" height="330" width="400" style="margin-bottom:20px" />
 
 <a class="big button subtitle" id="download-ide-button"
@@ -221,6 +221,10 @@
   <li>Template-based wizards to create common Android designs and components.</li>
   <li>A rich layout editor that allows you to drag-and-drop UI components, preview layouts on
   multiple screen configurations, and much more.</li>
+  <li>Built-in support for <a
+  href="http://android-developers.blogspot.com/2013/06/adding-backend-to-your-app-in-android.html"
+  class="external-link">Google Cloud Platform</a>, making it easy to integrate Google Cloud
+  Messaging and App Engine as server-side components.
 </ul>
 
 <p class="caution"><strong>Caution:</strong> Android Studio is currently available as
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index c8ace44..a7c5b20 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -553,6 +553,11 @@
      * @return The decoded bitmap, or null if the image data could not be
      *         decoded, or, if opts is non-null, if opts requested only the
      *         size be returned (in opts.outWidth and opts.outHeight)
+     *
+     * <p class="note">Prior to {@link android.os.Build.VERSION_CODES#KITKAT},
+     * if {@link InputStream#markSupported is.markSupported()} returns true,
+     * <code>is.mark(1024)</code> would be called. As of
+     * {@link android.os.Build.VERSION_CODES#KITKAT}, this is no longer the case.</p>
      */
     public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
         // we don't throw in this case, thus allowing the caller to only check
diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java
index 3524b25..3a99977 100644
--- a/graphics/java/android/graphics/BitmapRegionDecoder.java
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -104,6 +104,11 @@
      *                    allowing sharing may degrade the decoding speed.
      * @return BitmapRegionDecoder, or null if the image data could not be decoded.
      * @throws IOException if the image format is not supported or can not be decoded.
+     *
+     * <p class="note">Prior to {@link android.os.Build.VERSION_CODES#KITKAT},
+     * if {@link InputStream#markSupported is.markSupported()} returns true,
+     * <code>is.mark(1024)</code> would be called. As of
+     * {@link android.os.Build.VERSION_CODES#KITKAT}, this is no longer the case.</p>
      */
     public static BitmapRegionDecoder newInstance(InputStream is,
             boolean isShareable) throws IOException {
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index 5e574a6..25d4c5e 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -41,7 +41,7 @@
     inline bool has1BitStencil() const { return mHas1BitStencil; }
     inline bool has4BitStencil() const { return mHas4BitStencil; }
     inline bool hasNvSystemTime() const { return mHasNvSystemTime; }
-
+    inline bool hasUnpackRowLength() const { return mVersionMajor >= 3; }
     inline bool hasPixelBufferObjects() const { return mVersionMajor >= 3; }
     inline bool hasOcclusionQueries() const { return mVersionMajor >= 3; }
     inline bool hasFloatTextures() const { return mVersionMajor >= 3; }
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index a63cac6..ed0a79a 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -240,20 +240,20 @@
     switch (bitmap->getConfig()) {
     case SkBitmap::kA8_Config:
         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-        uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), texture->height,
-                GL_UNSIGNED_BYTE, bitmap->getPixels());
+        uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(),
+                texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
         texture->blend = true;
         break;
     case SkBitmap::kRGB_565_Config:
         glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
-        uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), texture->height,
-                GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
+        uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(),
+                texture->width, texture->height, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
         texture->blend = false;
         break;
     case SkBitmap::kARGB_8888_Config:
         glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
-        uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height,
-                GL_UNSIGNED_BYTE, bitmap->getPixels());
+        uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(),
+                texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
         // Do this after calling getPixels() to make sure Skia's deferred
         // decoding happened
         texture->blend = !bitmap->isOpaque();
@@ -293,17 +293,28 @@
     SkCanvas canvas(rgbaBitmap);
     canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL);
 
-    uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), height,
+    uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), width, height,
             GL_UNSIGNED_BYTE, rgbaBitmap.getPixels());
 }
 
-void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height,
-        GLenum type, const GLvoid * data) {
+void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride,
+        GLsizei width, GLsizei height, GLenum type, const GLvoid * data) {
+    // TODO: With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
+    //       if the stride doesn't match the width
+    const bool useStride = stride != width && Extensions::getInstance().hasUnpackRowLength();
+    if (useStride) {
+        glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
+    }
+
     if (resize) {
         glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
     } else {
         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
     }
+
+    if (useStride) {
+        glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+    }
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index 80bb22e..57fc19a 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -125,8 +125,8 @@
     void generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate = false);
 
     void uploadLoFiTexture(bool resize, SkBitmap* bitmap, uint32_t width, uint32_t height);
-    void uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height,
-            GLenum type, const GLvoid * data);
+    void uploadToTexture(bool resize, GLenum format, GLsizei stride,
+            GLsizei width, GLsizei height, GLenum type, const GLvoid * data);
 
     void init();
 
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index cbed3e4..d5f38b5 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -119,7 +119,7 @@
     // OpenGL ES 3.0+ lets us specify the row length for unpack operations such
     // as glTexSubImage2D(). This allows us to upload a sub-rectangle of a texture.
     // With OpenGL ES 2.0 we have to upload entire stripes instead.
-    mHasES3 = Extensions::getInstance().getMajorGlVersion() >= 3;
+    mHasUnpackRowLength = Extensions::getInstance().hasUnpackRowLength();
 }
 
 CacheTexture::~CacheTexture() {
@@ -206,21 +206,21 @@
 bool CacheTexture::upload() {
     const Rect& dirtyRect = mDirtyRect;
 
-    uint32_t x = mHasES3 ? dirtyRect.left : 0;
+    uint32_t x = mHasUnpackRowLength ? dirtyRect.left : 0;
     uint32_t y = dirtyRect.top;
-    uint32_t width = mHasES3 ? dirtyRect.getWidth() : mWidth;
+    uint32_t width = mHasUnpackRowLength ? dirtyRect.getWidth() : mWidth;
     uint32_t height = dirtyRect.getHeight();
 
     // The unpack row length only needs to be specified when a new
     // texture is bound
-    if (mHasES3) {
+    if (mHasUnpackRowLength) {
         glPixelStorei(GL_UNPACK_ROW_LENGTH, mWidth);
     }
 
     mTexture->upload(x, y, width, height);
     setDirty(false);
 
-    return mHasES3;
+    return mHasUnpackRowLength;
 }
 
 void CacheTexture::setDirty(bool dirty) {
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
index 028b611..61b38f8 100644
--- a/libs/hwui/font/CacheTexture.h
+++ b/libs/hwui/font/CacheTexture.h
@@ -190,7 +190,7 @@
     uint32_t mMaxQuadCount;
     Caches& mCaches;
     CacheBlock* mCacheBlocks;
-    bool mHasES3;
+    bool mHasUnpackRowLength;
     Rect mDirtyRect;
 };
 
diff --git a/media/java/android/media/WebVttRenderer.java b/media/java/android/media/WebVttRenderer.java
index edde68d..4dec081 100644
--- a/media/java/android/media/WebVttRenderer.java
+++ b/media/java/android/media/WebVttRenderer.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.content.Context;
+import android.text.Layout.Alignment;
 import android.text.SpannableStringBuilder;
 import android.util.ArrayMap;
 import android.util.AttributeSet;
@@ -1583,6 +1584,7 @@
             }
 
             final CueLayout cueBox = new CueLayout(getContext(), cue, mCaptionStyle, mFontSize);
+            mRegionCueBoxes.add(cueBox);
             addView(cueBox, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
 
             if (getChildCount() > mRegion.mLines) {
@@ -1696,12 +1698,27 @@
 
             removeAllViews();
 
+            final int cueAlignment = resolveCueAlignment(getLayoutDirection(), mCue.mAlignment);
+            final Alignment alignment;
+            switch (cueAlignment) {
+                case TextTrackCue.ALIGNMENT_LEFT:
+                    alignment = Alignment.ALIGN_LEFT;
+                    break;
+                case TextTrackCue.ALIGNMENT_RIGHT:
+                    alignment = Alignment.ALIGN_RIGHT;
+                    break;
+                case TextTrackCue.ALIGNMENT_MIDDLE:
+                default:
+                    alignment = Alignment.ALIGN_CENTER;
+            }
+
             final CaptionStyle captionStyle = mCaptionStyle;
             final float fontSize = mFontSize;
             final TextTrackCueSpan[][] lines = mCue.mLines;
             final int lineCount = lines.length;
             for (int i = 0; i < lineCount; i++) {
                 final SpanLayout lineBox = new SpanLayout(getContext(), lines[i]);
+                lineBox.setAlignment(alignment);
                 lineBox.setCaptionStyle(captionStyle, fontSize);
 
                 addView(lineBox, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 9d8489c..aef61c7 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -68,6 +68,7 @@
     }
 
     class IsMetadataNotEmpty extends ArgumentMatcher<CameraMetadataNative> {
+        @Override
         public boolean matches(Object obj) {
             return !((CameraMetadataNative) obj).isEmpty();
         }
@@ -273,6 +274,17 @@
     }
 
     @SmallTest
+    public void testCameraCharacteristics() throws RemoteException {
+        CameraMetadataNative info = new CameraMetadataNative();
+
+        int status = mUtils.getCameraService().getCameraCharacteristics(mCameraId, /*out*/info);
+        assertEquals(CameraBinderTestUtils.NO_ERROR, status);
+
+        assertFalse(info.isEmpty());
+        assertNotNull(info.get(CameraCharacteristics.SCALER_AVAILABLE_FORMATS));
+    }
+
+    @SmallTest
     public void testWaitUntilIdle() throws Exception {
         CaptureRequest.Builder builder = createDefaultBuilder(/* needStream */true);
         int requestIdStreaming = submitCameraRequest(builder.build(), /* streaming */true);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index ca9bea4..72fdc57 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -1001,7 +1001,7 @@
         } else {
             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
                     | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-                    | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
+                    | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
         }
 
         setResult(Activity.RESULT_OK, intent);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java b/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
index 1cc35a7..9861399 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
@@ -209,6 +209,8 @@
         if (requestCode == CODE_READ) {
             final Uri uri = data != null ? data.getData() : null;
             if (uri != null) {
+                getContentResolver()
+                        .takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
                 InputStream is = null;
                 try {
                     is = getContentResolver().openInputStream(uri);
@@ -226,6 +228,8 @@
         } else if (requestCode == CODE_WRITE) {
             final Uri uri = data != null ? data.getData() : null;
             if (uri != null) {
+                getContentResolver()
+                        .takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                 OutputStream os = null;
                 try {
                     os = getContentResolver().openOutputStream(uri);
diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml
index 5cf05f8..89e7240 100644
--- a/packages/Keyguard/res/values/strings.xml
+++ b/packages/Keyguard/res/values/strings.xml
@@ -149,6 +149,20 @@
     <!-- Shown on transport control of lockscreen. Pressing button pauses playback -->
     <string name="keyguard_accessibility_transport_stop_description">Stop button</string>
 
+    <!-- Accessibility description for when the device prompts the user to dismiss keyguard
+         in order to complete an action. This will be followed by a message about the current
+         security option (e.g. "Pattern unlock."). [CHAR LIMIT=NONE] -->
+    <string name="keyguard_accessibility_show_bouncer">Unlock to continue</string>
+
+    <!-- Accessibility description for when the bouncer prompt is dismissed. [CHAR LIMIT=NONE] -->
+    <string name="keyguard_accessibility_hide_bouncer">Launch canceled</string>
+
+    <!-- Accessibility description announced when user drags widget over the delete drop target [CHAR LIMIT=NONE] -->
+    <string name="keyguard_accessibility_delete_widget_start">Drop <xliff:g id="widget_index">%1$s</xliff:g> to delete.</string>
+
+    <!-- Accessibility description announced when user drags widget away from delete drop target [CHAR LIMIT=NONE] -->
+    <string name="keyguard_accessibility_delete_widget_end"><xliff:g id="widget_index">%1$s</xliff:g> will not be deleted.</string>
+
     <!-- Password keyboard strings. Used by LockScreen and Settings --><skip />
     <!-- Label for "switch to symbols" key.  Must be short to fit on key! -->
     <string name="password_keyboard_label_symbol_key">\?123</string>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index aa43711..a9e9d3a 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -24,7 +24,6 @@
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.AlertDialog;
-import android.app.PendingIntent;
 import android.app.SearchManager;
 import android.app.admin.DevicePolicyManager;
 import android.appwidget.AppWidgetHost;
@@ -41,7 +40,6 @@
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.media.RemoteControlClient;
-import android.os.Bundle;
 import android.os.Looper;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -49,11 +47,9 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.telephony.TelephonyManager;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Slog;
-import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
@@ -206,6 +202,13 @@
         }
     }
 
+    public void announceCurrentSecurityMethod() {
+        View v = (View) getSecurityView(mCurrentSecuritySelection);
+        if (v != null) {
+            v.announceForAccessibility(v.getContentDescription());
+        }
+    }
+
     private void getInitialTransportState() {
         DisplayClientState dcs = KeyguardUpdateMonitor.getInstance(mContext)
                 .getCachedDisplayClientState();
@@ -1663,4 +1666,8 @@
         mAppWidgetContainer.handleExternalCameraEvent(event);
     }
 
+    public void launchCamera() {
+        mActivityLauncher.launchCamera(getHandler(), null);
+    }
+
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardService.java b/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
index 77006c5..d7c5fe2 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
@@ -137,6 +137,10 @@
             checkPermission();
             mKeyguardViewMediator.dispatch(event);
         }
+        public void launchCamera() {
+            checkPermission();
+            mKeyguardViewMediator.launchCamera();
+        }
     };
 
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
index 177e0f8..a0e44d7 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
@@ -506,4 +506,10 @@
             mKeyguardView.dispatch(event);
         }
     }
+
+    public void launchCamera() {
+        if (mKeyguardView != null) {
+            mKeyguardView.launchCamera();
+        }
+    }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
index 478096c..0606d83 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
@@ -122,6 +122,7 @@
     private static final int KEYGUARD_TIMEOUT = 13;
     private static final int SHOW_ASSISTANT = 14;
     private static final int DISPATCH_EVENT = 15;
+    private static final int LAUNCH_CAMERA = 16;
 
     /**
      * The default amount of time we stay awake (used for all key input)
@@ -1071,6 +1072,9 @@
                 case DISPATCH_EVENT:
                     handleDispatchEvent((MotionEvent) msg.obj);
                     break;
+                case LAUNCH_CAMERA:
+                    handleLaunchCamera();
+                    break;
             }
         }
     };
@@ -1107,6 +1111,10 @@
         sendUserPresentBroadcast();
     }
 
+    protected void handleLaunchCamera() {
+        mKeyguardViewManager.launchCamera();
+    }
+
     protected void handleDispatchEvent(MotionEvent event) {
         mKeyguardViewManager.dispatch(event);
     }
@@ -1341,4 +1349,9 @@
         Message msg = mHandler.obtainMessage(DISPATCH_EVENT, event);
         mHandler.sendMessage(msg);
     }
+
+    public void launchCamera() {
+        Message msg = mHandler.obtainMessage(LAUNCH_CAMERA);
+        mHandler.sendMessage(msg);
+    }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java
index d9f9471..d1862cd 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java
@@ -87,6 +87,11 @@
     }
 
     public void showBouncer(boolean show) {
+        CharSequence what = mKeyguardHostView.getContext().getResources().getText(
+                show ? R.string.keyguard_accessibility_show_bouncer
+                        : R.string.keyguard_accessibility_hide_bouncer);
+        mKeyguardHostView.announceForAccessibility(what);
+        mKeyguardHostView.announceCurrentSecurityMethod();
         mChallengeLayout.showBouncer();
     }
 
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetFrame.java b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetFrame.java
index 81f6221..c0586d5 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetFrame.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetFrame.java
@@ -147,6 +147,10 @@
         if (ENABLE_HOVER_OVER_DELETE_DROP_TARGET_OVERLAY) {
             if (mIsHoveringOverDeleteDropTarget != isHovering) {
                 mIsHoveringOverDeleteDropTarget = isHovering;
+                int resId = isHovering ? R.string.keyguard_accessibility_delete_widget_start
+                        : R.string.keyguard_accessibility_delete_widget_end;
+                String text = getContext().getResources().getString(resId, getContentDescription());
+                announceForAccessibility(text);
                 invalidate();
             }
         }
@@ -326,7 +330,7 @@
 
     /**
      * Depending on whether the security is up, the widget size needs to change
-     * 
+     *
      * @param height The height of the widget, -1 for full height
      */
     private void setWidgetHeight(int height) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
index f7dc058..f8857ab 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
@@ -90,6 +90,7 @@
     // Background worker thread: used here for persistence, also made available to widget frames
     private final HandlerThread mBackgroundWorkerThread;
     private final Handler mBackgroundWorkerHandler;
+    private boolean mCameraEventInProgress;
 
     public KeyguardWidgetPager(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -941,14 +942,18 @@
         beginCameraEvent();
         int cameraPage = getPageCount() - 1;
         boolean endWarp = false;
-        if (isCameraPage(cameraPage)) {
+        if (isCameraPage(cameraPage) || mCameraEventInProgress) {
             switch (event.getAction()) {
                 case MotionEvent.ACTION_DOWN:
+                    // Once we start dispatching camera events, we must continue to do so
+                    // to keep event dispatch happy.
+                    mCameraEventInProgress = true;
                     userActivity();
                     startWarp(cameraPage);
                     break;
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL:
+                    mCameraEventInProgress = false;
                     endWarp = true;
                     break;
             }
diff --git a/packages/Keyguard/src/com/android/keyguard/PagedView.java b/packages/Keyguard/src/com/android/keyguard/PagedView.java
index b6279d0..666227c 100644
--- a/packages/Keyguard/src/com/android/keyguard/PagedView.java
+++ b/packages/Keyguard/src/com/android/keyguard/PagedView.java
@@ -125,6 +125,7 @@
     private int[] mChildOffsets;
     private int[] mChildRelativeOffsets;
     private int[] mChildOffsetsWithLayoutScale;
+    private String mDeleteString; // Accessibility announcement when widget is deleted
 
     protected final static int TOUCH_STATE_REST = 0;
     protected final static int TOUCH_STATE_SCROLLING = 1;
@@ -1118,6 +1119,8 @@
                 // i.e. fall through to the next case (don't break)
                 // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events
                 // while it's small- this was causing a crash before we checked for INVALID_POINTER)
+
+                break;
             }
 
             case MotionEvent.ACTION_DOWN: {
@@ -2194,8 +2197,13 @@
 
     protected void onEndReordering() {
         if (AccessibilityManager.getInstance(mContext).isEnabled()) {
-            announceForAccessibility(mContext.getString(
-                    R.string.keyguard_accessibility_widget_reorder_end));
+            if (mDeleteString != null) {
+                announceForAccessibility(mDeleteString);
+                mDeleteString = null;
+            } else {
+                announceForAccessibility(mContext.getString(
+                        R.string.keyguard_accessibility_widget_reorder_end));
+            }
         }
         mIsReordering = false;
 
@@ -2507,6 +2515,9 @@
         AnimatorUpdateListener updateCb = new FlingAlongVectorAnimatorUpdateListener(dragView, vel,
                 from, startTime, FLING_TO_DELETE_FRICTION);
 
+        mDeleteString = getContext().getResources()
+                .getString(R.string.keyguard_accessibility_widget_deleted,
+                        mDragView.getContentDescription());
         final Runnable onAnimationEndRunnable = createPostDeleteAnimationRunnable(dragView);
 
         // Create and start the animation
@@ -2562,6 +2573,9 @@
                 ObjectAnimator.ofFloat(dragView, "alpha", toAlpha));
         animations.add(alphaAnim);
 
+        mDeleteString = getContext().getResources()
+                .getString(R.string.keyguard_accessibility_widget_deleted,
+                        mDragView.getContentDescription());
         final Runnable onAnimationEndRunnable = createPostDeleteAnimationRunnable(dragView);
 
         AnimatorSet anim = new AnimatorSet();
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg_press.9.png b/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg_press.9.png
index e591a7b..cbcb3e3 100644
--- a/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg_press.9.png
+++ b/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg_press.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg_press.9.png b/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg_press.9.png
index 7dfea4c..3ac7c40 100644
--- a/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg_press.9.png
+++ b/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg_press.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/recents_thumbnail_bg_press.9.png b/packages/SystemUI/res/drawable-xhdpi/recents_thumbnail_bg_press.9.png
index f01a79e..78a69f5 100644
--- a/packages/SystemUI/res/drawable-xhdpi/recents_thumbnail_bg_press.9.png
+++ b/packages/SystemUI/res/drawable-xhdpi/recents_thumbnail_bg_press.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/recents_thumbnail_bg_press.9.png b/packages/SystemUI/res/drawable-xxhdpi/recents_thumbnail_bg_press.9.png
index b7bbd82..1fa1e62 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/recents_thumbnail_bg_press.9.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/recents_thumbnail_bg_press.9.png
Binary files differ
diff --git a/packages/SystemUI/res/layout-ldrtl/navigation_bar.xml b/packages/SystemUI/res/layout-ldrtl/navigation_bar.xml
index 765b274..aa7256b 100644
--- a/packages/SystemUI/res/layout-ldrtl/navigation_bar.xml
+++ b/packages/SystemUI/res/layout-ldrtl/navigation_bar.xml
@@ -153,6 +153,7 @@
             android:src="@drawable/search_light"
             android:scaleType="center"
             android:visibility="gone"
+            android:contentDescription="@string/accessibility_search_light"
             />
 
         <com.android.systemui.statusbar.policy.DeadZone
@@ -297,6 +298,7 @@
             android:src="@drawable/search_light"
             android:scaleType="center"
             android:visibility="gone"
+            android:contentDescription="@string/accessibility_search_light"
             />
 
         <com.android.systemui.statusbar.policy.DeadZone
diff --git a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
index 9592b18..b9ad799 100644
--- a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
+++ b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
@@ -149,6 +149,7 @@
             android:src="@drawable/search_light"
             android:scaleType="center"
             android:visibility="gone"
+            android:contentDescription="@string/accessibility_search_light"
             />
 
         <com.android.systemui.statusbar.policy.DeadZone
@@ -290,6 +291,7 @@
             android:src="@drawable/search_light"
             android:scaleType="center"
             android:visibility="gone"
+            android:contentDescription="@string/accessibility_search_light"
             />
 
         <com.android.systemui.statusbar.policy.DeadZone
diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml
index 11cbbc7..aa365ae 100644
--- a/packages/SystemUI/res/layout/navigation_bar.xml
+++ b/packages/SystemUI/res/layout/navigation_bar.xml
@@ -157,6 +157,7 @@
                 android:src="@drawable/search_light"
                 android:scaleType="center"
                 android:visibility="gone"
+                android:contentDescription="@string/accessibility_search_light"
                 />
 
             <com.android.systemui.statusbar.policy.KeyButtonView
@@ -167,6 +168,7 @@
                 android:src="@drawable/ic_sysbar_camera"
                 android:scaleType="center"
                 android:visibility="gone"
+                android:contentDescription="@string/accessibility_camera_button"
                 />
         </FrameLayout>
 
@@ -312,6 +314,7 @@
             android:src="@drawable/search_light"
             android:scaleType="center"
             android:visibility="gone"
+            android:contentDescription="@string/accessibility_search_light"
             />
 
         <!-- No camera button in landscape mode -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index eb425e6..58865ab 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -192,6 +192,10 @@
     <string name="accessibility_menu">Menu</string>
     <!-- Content description of the recents button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_recent">Recent apps</string>
+    <!-- Content description of the search button for accessibility. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_search_light">Search</string>
+    <!-- Content description of the camera button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_camera_button">Camera</string>
 
     <!-- Content description of the switch input method button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_ime_switch_button">Switch input method button.</string>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
index 3a5524d..3a82753 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
@@ -50,8 +50,7 @@
     }
 
     public boolean onInterceptTouchEvent(MotionEvent event) {
-        if (mSourceView == null || mDelegateView == null || mDisabled
-                || mBar.shouldDisableNavbarGestures()) {
+        if (mSourceView == null || mDelegateView == null || mBar.shouldDisableNavbarGestures()) {
             return false;
         }
 
@@ -73,7 +72,7 @@
             return false;
         }
 
-        if (!mPanelShowing && action == MotionEvent.ACTION_MOVE) {
+        if (!mDisabled && !mPanelShowing && action == MotionEvent.ACTION_MOVE) {
             final int historySize = event.getHistorySize();
             for (int k = 0; k < historySize + 1; k++) {
                 float x = k < historySize ? event.getHistoricalX(k) : event.getX();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java
index a6e2347..1221a55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java
@@ -103,4 +103,30 @@
         return false;
     }
 
+    public void showAssistant() {
+        if (mService != null) {
+            try {
+                mService.showAssistant();
+            } catch (RemoteException e) {
+                // What to do?
+                Log.e(TAG, "RemoteException launching assistant!", e);
+            }
+        } else {
+            Log.w(TAG, "dispatch(event): NO SERVICE!");
+        }
+    }
+
+    public void launchCamera() {
+        if (mService != null) {
+            try {
+                mService.launchCamera();
+            } catch (RemoteException e) {
+                // What to do?
+                Log.e(TAG, "RemoteException launching camera!", e);
+            }
+        } else {
+            Log.w(TAG, "dispatch(event): NO SERVICE!");
+        }
+    }
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 24e27b1..596fac6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -37,8 +37,10 @@
 import android.view.MotionEvent;
 import android.view.Surface;
 import android.view.View;
+import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 
@@ -86,7 +88,7 @@
 
     // used to disable the camera icon in navbar when disabled by DPM
     private boolean mCameraDisabledByDpm;
-    KeyguardTouchDelegate mTouchDelegate;
+    KeyguardTouchDelegate mKeyguardTouchDelegate;
 
     private final OnTouchListener mCameraTouchListener = new OnTouchListener() {
         @Override
@@ -110,7 +112,7 @@
                     }
                     break;
             }
-            return mTouchDelegate.dispatch(event);
+            return mKeyguardTouchDelegate.dispatch(event);
         }
     };
 
@@ -153,7 +155,7 @@
 
         mBarTransitions = new NavigationBarTransitions(this);
 
-        mTouchDelegate = new KeyguardTouchDelegate(mContext);
+        mKeyguardTouchDelegate = new KeyguardTouchDelegate(mContext);
 
         mCameraDisabledByDpm = isCameraDisabledByDpm();
         watchForDevicePolicyChanges();
@@ -339,7 +341,7 @@
                 final int disabledFlags = dpm.getKeyguardDisabledFeatures(null, userId);
                 final  boolean disabledBecauseKeyguardSecure =
                         (disabledFlags & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0
-                        && mTouchDelegate.isSecure();
+                        && mKeyguardTouchDelegate.isSecure();
                 return dpm.getCameraDisabled(null) || disabledBecauseKeyguardSecure;
             } catch (RemoteException e) {
                 Log.e(TAG, "Can't get userId", e);
@@ -389,12 +391,44 @@
 
         mCurrentView = mRotatedViews[Surface.ROTATION_0];
 
-        // Add a touch handler for camera icon for all view orientations.
-        for (int i = 0; i < mRotatedViews.length; i++) {
-            View cameraButton = mRotatedViews[i].findViewById(R.id.camera_button);
-            if (cameraButton != null) {
-                cameraButton.setOnTouchListener(mCameraTouchListener);
+
+        final AccessibilityManager accessibilityManager =
+                (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+        if (accessibilityManager.isEnabled()) {
+            // In accessibility mode, we add a simple click handler since swipe is tough to
+            // trigger near screen edges.
+            View camera = getCameraButton();
+            View searchLight = getSearchLight();
+            if (camera != null || searchLight != null) {
+                OnClickListener listener = new OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        launchForAccessibilityClick(v);
+                    }
+                };
+                if (camera != null) {
+                    camera.setOnClickListener(listener);
+                }
+                if (searchLight != null) {
+                    searchLight.setOnClickListener(listener);
+                }
             }
+        } else {
+            // Add a touch handler for camera icon for all view orientations.
+            for (int i = 0; i < mRotatedViews.length; i++) {
+                View cameraButton = mRotatedViews[i].findViewById(R.id.camera_button);
+                if (cameraButton != null) {
+                    cameraButton.setOnTouchListener(mCameraTouchListener);
+                }
+            }
+        }
+    }
+
+    protected void launchForAccessibilityClick(View v) {
+        if (v == getCameraButton()) {
+            mKeyguardTouchDelegate.launchCamera();
+        } else if (v == getSearchLight()) {
+            mKeyguardTouchDelegate.showAssistant();
         }
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
index b27584d..5e299ee 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
@@ -192,6 +192,10 @@
         // Not used by PhoneWindowManager.  See code in {@link NavigationBarView}
     }
 
+    public void launchCamera() {
+        // Not used by PhoneWindowManager.  See code in {@link NavigationBarView}
+    }
+
     @Override
     public IBinder asBinder() {
         return mService.asBinder();
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 4f73588..3c11933 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -18,7 +18,9 @@
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static com.android.internal.util.XmlUtils.readIntAttribute;
+import static com.android.internal.util.XmlUtils.readLongAttribute;
 import static com.android.internal.util.XmlUtils.writeIntAttribute;
+import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
@@ -36,7 +38,6 @@
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.ProcessCpuTracker;
 import com.android.internal.os.TransferPipe;
-import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.MemInfoReader;
@@ -113,6 +114,7 @@
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PathPermission;
@@ -153,6 +155,7 @@
 import android.os.UpdateLock;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.text.format.DateUtils;
 import android.text.format.Time;
 import android.util.AtomicFile;
 import android.util.EventLog;
@@ -310,6 +313,9 @@
     // to respond with the result.
     static final int PENDING_ASSIST_EXTRAS_TIMEOUT = 500;
 
+    // Maximum number of persisted Uri grants a package is allowed
+    static final int MAX_PERSISTED_URI_GRANTS = 128;
+
     static final int MY_PID = Process.myPid();
 
     static final String[] EMPTY_STRING_ARRAY = new String[0];
@@ -504,6 +510,12 @@
     int mLruProcessActivityStart = 0;
 
     /**
+     * Where in mLruProcesses that the processes hosting services start.
+     * This is after (lower index) than mLruProcessesActivityStart.
+     */
+    int mLruProcessServiceStart = 0;
+
+    /**
      * List of processes that should gc as soon as things are idle.
      */
     final ArrayList<ProcessRecord> mProcessesToGc = new ArrayList<ProcessRecord>();
@@ -685,6 +697,7 @@
     private static final String ATTR_TARGET_PKG = "targetPkg";
     private static final String ATTR_URI = "uri";
     private static final String ATTR_MODE_FLAGS = "modeFlags";
+    private static final String ATTR_CREATED_TIME = "createdTime";
 
     /**
      * Global set of specific {@link Uri} permissions that have been granted.
@@ -853,6 +866,7 @@
      * determine on the next iteration which should be B services.
      */
     int mNumServiceProcs = 0;
+    int mNewNumAServiceProcs = 0;
     int mNewNumServiceProcs = 0;
 
     /**
@@ -1540,7 +1554,15 @@
                         logBuilder.append(infos[Debug.MEMINFO_BUFFERS]).append(" kB buffers, ");
                         logBuilder.append(infos[Debug.MEMINFO_CACHED]).append(" kB cached, ");
                         logBuilder.append(infos[Debug.MEMINFO_FREE]).append(" kB free\n");
-
+                        if (infos[Debug.MEMINFO_ZRAM_TOTAL] != 0) {
+                            logBuilder.append("  ZRAM: ");
+                            logBuilder.append(infos[Debug.MEMINFO_ZRAM_TOTAL]);
+                            logBuilder.append(" kB RAM, ");
+                            logBuilder.append(infos[Debug.MEMINFO_SWAP_TOTAL]);
+                            logBuilder.append(" kB swap total, ");
+                            logBuilder.append(infos[Debug.MEMINFO_SWAP_FREE]);
+                            logBuilder.append(" kB swap free\n");
+                        }
                         Slog.i(TAG, logBuilder.toString());
 
                         StringBuilder dropBuilder = new StringBuilder(1024);
@@ -2250,6 +2272,12 @@
             return index;
         }
 
+        if (lrui >= index) {
+            // Don't want to cause this to move dependent processes *back* in the
+            // list as if they were less frequently used.
+            return index;
+        }
+
         if (lrui >= mLruProcessActivityStart) {
             // Don't want to touch dependent processes that are hosting activities.
             return index;
@@ -2269,12 +2297,16 @@
             if (lrui <= mLruProcessActivityStart) {
                 mLruProcessActivityStart--;
             }
+            if (lrui <= mLruProcessServiceStart) {
+                mLruProcessServiceStart--;
+            }
             mLruProcesses.remove(lrui);
         }
     }
 
     final void updateLruProcessLocked(ProcessRecord app, boolean oomAdj, boolean activityChange) {
         final boolean hasActivity = app.activities.size() > 0;
+        final boolean hasService = false; // not impl yet. app.services.size() > 0;
         if (!activityChange && hasActivity) {
             // The process has activties, so we are only going to allow activity-based
             // adjustments move it.  It should be kept in the front of the list with other
@@ -2293,19 +2325,28 @@
             if (lrui < mLruProcessActivityStart) {
                 mLruProcessActivityStart--;
             }
+            if (lrui < mLruProcessServiceStart) {
+                mLruProcessServiceStart--;
+            }
             mLruProcesses.remove(lrui);
         }
 
         int nextIndex;
-        if (!hasActivity) {
-            // Process doesn't have activities, it goes to the top of the non-activity area.
-            mLruProcesses.add(mLruProcessActivityStart, app);
-            nextIndex = mLruProcessActivityStart-1;
-            mLruProcessActivityStart++;
-        } else {
-            // Process does have activities, put it at the very tipsy-top.
+        if (hasActivity) {
+            // Process has activities, put it at the very tipsy-top.
             mLruProcesses.add(app);
             nextIndex = mLruProcessActivityStart;
+        } else if (hasService) {
+            // Process has services, put it at the top of the service list.
+            mLruProcesses.add(mLruProcessActivityStart, app);
+            nextIndex = mLruProcessServiceStart;
+            mLruProcessActivityStart++;
+        } else  {
+            // Process not otherwise of interest, it goes to the top of the non-service area.
+            mLruProcesses.add(mLruProcessServiceStart, app);
+            nextIndex = mLruProcessServiceStart-1;
+            mLruProcessActivityStart++;
+            mLruProcessServiceStart++;
         }
 
         // If the app is currently using a content provider or service,
@@ -2359,7 +2400,7 @@
                 && mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
                 && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
             if (DEBUG_PSS) Slog.d(TAG, "May not keep " + proc + ": pss=" + proc.lastCachedPss);
-            if (proc.lastCachedPss >= mProcessList.getCachedRestoreThreshold()) {
+            if (proc.lastCachedPss >= mProcessList.getCachedRestoreThresholdKb()) {
                 if (proc.baseProcessTracker != null) {
                     proc.baseProcessTracker.reportCachedKill(proc.pkgList, proc.lastCachedPss);
                 }
@@ -5669,6 +5710,15 @@
         return pi;
     }
 
+    private UriPermission findUriPermissionLocked(int targetUid, Uri uri) {
+        ArrayMap<Uri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
+        if (targetUris != null) {
+            return targetUris.get(uri);
+        } else {
+            return null;
+        }
+    }
+
     private UriPermission findOrCreateUriPermissionLocked(
             String sourcePkg, String targetPkg, int targetUid, Uri uri) {
         ArrayMap<Uri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
@@ -5686,8 +5736,8 @@
         return perm;
     }
 
-    private final boolean checkUriPermissionLocked(Uri uri, int uid,
-            int modeFlags) {
+    private final boolean checkUriPermissionLocked(
+            Uri uri, int uid, int modeFlags, int minStrength) {
         // Root gets to do everything.
         if (uid == 0) {
             return true;
@@ -5696,7 +5746,7 @@
         if (perms == null) return false;
         UriPermission perm = perms.get(uri);
         if (perm == null) return false;
-        return (modeFlags&perm.modeFlags) == modeFlags;
+        return perm.getStrength(modeFlags) >= minStrength;
     }
 
     @Override
@@ -5716,7 +5766,7 @@
             return PackageManager.PERMISSION_GRANTED;
         }
         synchronized(this) {
-            return checkUriPermissionLocked(uri, uid, modeFlags)
+            return checkUriPermissionLocked(uri, uid, modeFlags, UriPermission.STRENGTH_OWNED)
                     ? PackageManager.PERMISSION_GRANTED
                     : PackageManager.PERMISSION_DENIED;
         }
@@ -5733,7 +5783,7 @@
      */
     int checkGrantUriPermissionLocked(int callingUid, String targetPkg,
             Uri uri, int modeFlags, int lastTargetUid) {
-        final boolean persist = (modeFlags & Intent.FLAG_PERSIST_GRANT_URI_PERMISSION) != 0;
+        final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
         modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
                 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
         if (modeFlags == 0) {
@@ -5777,7 +5827,7 @@
 
         if (targetUid >= 0) {
             // First...  does the target actually need this permission?
-            if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags) && !persist) {
+            if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags)) {
                 // No need to grant the target this permission.
                 if (DEBUG_URI_PERMISSION) Slog.v(TAG,
                         "Target " + targetPkg + " already has full permission to " + uri);
@@ -5796,7 +5846,7 @@
                     allowed = false;
                 }
             }
-            if (allowed && !persist) {
+            if (allowed) {
                 return -1;
             }
         }
@@ -5830,7 +5880,10 @@
         // this uri?
         if (callingUid != Process.myUid()) {
             if (!checkHoldingPermissionsLocked(pm, pi, uri, callingUid, modeFlags)) {
-                if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) {
+                // Require they hold a strong enough Uri permission
+                final int minStrength = persistable ? UriPermission.STRENGTH_PERSISTABLE
+                        : UriPermission.STRENGTH_OWNED;
+                if (!checkUriPermissionLocked(uri, callingUid, modeFlags, minStrength)) {
                     throw new SecurityException("Uid " + callingUid
                             + " does not have permission to uri " + uri);
                 }
@@ -5851,7 +5904,7 @@
 
     void grantUriPermissionUncheckedLocked(
             int targetUid, String targetPkg, Uri uri, int modeFlags, UriPermissionOwner owner) {
-        final boolean persist = (modeFlags & Intent.FLAG_PERSIST_GRANT_URI_PERMISSION) != 0;
+        final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
         modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
                 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
         if (modeFlags == 0) {
@@ -5874,11 +5927,7 @@
 
         final UriPermission perm = findOrCreateUriPermissionLocked(
                 pi.packageName, targetPkg, targetUid, uri);
-        final boolean persistChanged = perm.grantModes(modeFlags, persist, owner);
-        if (persistChanged) {
-            mHandler.removeMessages(PERSIST_URI_GRANTS_MSG);
-            mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG).sendToTarget();
-        }
+        perm.grantModes(modeFlags, persistable, owner);
     }
 
     void grantUriPermissionLocked(int callingUid, String targetPkg, Uri uri,
@@ -5930,6 +5979,7 @@
         if (data == null && clip == null) {
             return null;
         }
+
         if (data != null) {
             int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, data,
                 mode, needed != null ? needed.targetUid : -1);
@@ -6011,6 +6061,14 @@
                 throw new IllegalArgumentException("null uri");
             }
 
+            // Persistable only supported through Intents
+            modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+            if (modeFlags == 0) {
+                throw new IllegalArgumentException("Mode flags must be "
+                        + "FLAG_GRANT_READ_URI_PERMISSION and/or FLAG_GRANT_WRITE_URI_PERMISSION");
+            }
+
             grantUriPermissionLocked(r.uid, targetPkg, uri, modeFlags,
                     null);
         }
@@ -6032,8 +6090,7 @@
         }
     }
 
-    private void revokeUriPermissionLocked(
-            int callingUid, Uri uri, int modeFlags, boolean persist) {
+    private void revokeUriPermissionLocked(int callingUid, Uri uri, int modeFlags) {
         if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Revoking all granted permissions to " + uri);
 
         final IPackageManager pm = AppGlobals.getPackageManager();
@@ -6086,7 +6143,7 @@
                     }
                     if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
                             "Revoking " + perm.targetUid + " permission to " + perm.uri);
-                    persistChanged |= perm.clearModes(modeFlags, persist);
+                    persistChanged |= perm.clearModes(modeFlags, true);
                     if (perm.modeFlags == 0) {
                         it.remove();
                     }
@@ -6101,8 +6158,7 @@
         }
 
         if (persistChanged) {
-            mHandler.removeMessages(PERSIST_URI_GRANTS_MSG);
-            mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG).sendToTarget();
+            schedulePersistUriGrants();
         }
     }
 
@@ -6122,7 +6178,6 @@
                 return;
             }
 
-            final boolean persist = (modeFlags & Intent.FLAG_PERSIST_GRANT_URI_PERMISSION) != 0;
             modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
                     | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
             if (modeFlags == 0) {
@@ -6138,7 +6193,7 @@
                 return;
             }
 
-            revokeUriPermissionLocked(r.uid, uri, modeFlags, persist);
+            revokeUriPermissionLocked(r.uid, uri, modeFlags);
         }
     }
 
@@ -6150,10 +6205,10 @@
      *            packages.
      * @param userHandle User to match, or {@link UserHandle#USER_ALL} to apply
      *            to all users.
-     * @param persist If persistent grants should be removed.
+     * @param persistable If persistable grants should be removed.
      */
     private void removeUriPermissionsForPackageLocked(
-            String packageName, int userHandle, boolean persist) {
+            String packageName, int userHandle, boolean persistable) {
         if (userHandle == UserHandle.USER_ALL && packageName == null) {
             throw new IllegalArgumentException("Must narrow by either package or user");
         }
@@ -6173,7 +6228,7 @@
                     // Only inspect grants matching package
                     if (packageName == null || perm.sourcePkg.equals(packageName)
                             || perm.targetPkg.equals(packageName)) {
-                        persistChanged |= perm.clearModes(~0, persist);
+                        persistChanged |= perm.clearModes(~0, persistable);
 
                         // Only remove when no modes remain; any persisted grants
                         // will keep this alive.
@@ -6186,8 +6241,7 @@
         }
 
         if (persistChanged) {
-            mHandler.removeMessages(PERSIST_URI_GRANTS_MSG);
-            mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG).sendToTarget();
+            schedulePersistUriGrants();
         }
     }
 
@@ -6242,6 +6296,13 @@
         }
     }
 
+    private void schedulePersistUriGrants() {
+        if (!mHandler.hasMessages(PERSIST_URI_GRANTS_MSG)) {
+            mHandler.sendMessageDelayed(mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG),
+                    10 * DateUtils.SECOND_IN_MILLIS);
+        }
+    }
+
     private void writeGrantedUriPermissions() {
         if (DEBUG_URI_PERMISSION) Slog.v(TAG, "writeGrantedUriPermissions()");
 
@@ -6273,6 +6334,7 @@
                 out.attribute(null, ATTR_TARGET_PKG, perm.targetPkg);
                 out.attribute(null, ATTR_URI, String.valueOf(perm.uri));
                 writeIntAttribute(out, ATTR_MODE_FLAGS, perm.persistedModeFlags);
+                writeLongAttribute(out, ATTR_CREATED_TIME, perm.persistedCreateTime);
                 out.endTag(null, TAG_URI_GRANT);
             }
             out.endTag(null, TAG_URI_GRANTS);
@@ -6289,6 +6351,8 @@
     private void readGrantedUriPermissionsLocked() {
         if (DEBUG_URI_PERMISSION) Slog.v(TAG, "readGrantedUriPermissions()");
 
+        final long now = System.currentTimeMillis();
+
         FileInputStream fis = null;
         try {
             fis = mGrantFile.openRead();
@@ -6305,6 +6369,7 @@
                         final String targetPkg = in.getAttributeValue(null, ATTR_TARGET_PKG);
                         final Uri uri = Uri.parse(in.getAttributeValue(null, ATTR_URI));
                         final int modeFlags = readIntAttribute(in, ATTR_MODE_FLAGS);
+                        final long createdTime = readLongAttribute(in, ATTR_CREATED_TIME, now);
 
                         // Sanity check that provider still belongs to source package
                         final ProviderInfo pi = getProviderInfoLocked(
@@ -6319,7 +6384,7 @@
                             if (targetUid != -1) {
                                 final UriPermission perm = findOrCreateUriPermissionLocked(
                                         sourcePkg, targetPkg, targetUid, uri);
-                                perm.grantModes(modeFlags, true, null);
+                                perm.initPersistedModes(modeFlags, createdTime);
                             }
                         } else {
                             Slog.w(TAG, "Persisted grant for " + uri + " had source " + sourcePkg
@@ -6340,47 +6405,117 @@
     }
 
     @Override
-    public Uri[] getGrantedUriPermissions(
-            String sourcePackage, String targetPackage, int modeFlags, int modeMask) {
-        enforceNotIsolatedCaller("getGrantedUriPermissions");
+    public void takePersistableUriPermission(Uri uri, int modeFlags) {
+        enforceNotIsolatedCaller("takePersistableUriPermission");
+
+        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        if (modeFlags == 0) {
+            return;
+        }
+
         synchronized (this) {
-            // Verify that caller owns at least one of the requested packages
-            final int uid = Binder.getCallingUid();
-            final IPackageManager pm = AppGlobals.getPackageManager();
-            final String[] callerPackages;
-            try {
-                callerPackages = pm.getPackagesForUid(uid);
-            } catch (RemoteException e) {
-                throw new SecurityException("Failed to find packages for UID " + uid);
-            }
-            final boolean callerOwnsSource = sourcePackage != null
-                    && ArrayUtils.contains(callerPackages, sourcePackage);
-            final boolean callerOwnsTarget = targetPackage != null
-                    && ArrayUtils.contains(callerPackages, targetPackage);
-            if (!(callerOwnsSource || callerOwnsTarget)) {
-                throw new SecurityException("Caller " + Arrays.toString(callerPackages)
-                        + " doesn't own " + sourcePackage + " or " + targetPackage);
+            final int callingUid = Binder.getCallingUid();
+            final UriPermission perm = findUriPermissionLocked(callingUid, uri);
+            if (perm == null) {
+                Slog.w(TAG, "No permission grant found for UID " + callingUid + " and Uri "
+                        + uri.toSafeString());
+                return;
             }
 
-            final ArrayList<Uri> result = Lists.newArrayList();
-            final int size = mGrantedUriPermissions.size();
-            for (int i = 0; i < size; i++) {
-                final ArrayMap<Uri, UriPermission> map = mGrantedUriPermissions.valueAt(i);
-                final int mapSize = map.size();
-                for (int j = 0; j < mapSize; j++) {
-                    final UriPermission perm = map.valueAt(j);
-                    final boolean sourceMatch = sourcePackage == null
-                            || sourcePackage.equals(perm.sourcePkg);
-                    final boolean targetMatch = targetPackage == null
-                            || targetPackage.equals(perm.targetPkg);
-                    final boolean modeMatch = (perm.modeFlags & modeMask) == modeFlags;
-                    if (sourceMatch && targetMatch && modeMatch) {
-                        result.add(perm.uri);
+            boolean persistChanged = perm.takePersistableModes(modeFlags);
+            persistChanged |= maybePrunePersistedUriGrantsLocked(callingUid);
+
+            if (persistChanged) {
+                schedulePersistUriGrants();
+            }
+        }
+    }
+
+    @Override
+    public void releasePersistableUriPermission(Uri uri, int modeFlags) {
+        enforceNotIsolatedCaller("releasePersistableUriPermission");
+
+        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        if (modeFlags == 0) {
+            return;
+        }
+
+        synchronized (this) {
+            final int callingUid = Binder.getCallingUid();
+
+            final UriPermission perm = findUriPermissionLocked(callingUid, uri);
+            if (perm == null) {
+                Slog.w(TAG, "No permission grant found for UID " + callingUid + " and Uri "
+                        + uri.toSafeString());
+                return;
+            }
+
+            final boolean persistChanged = perm.releasePersistableModes(modeFlags);
+            removeUriPermissionIfNeededLocked(perm);
+            if (persistChanged) {
+                schedulePersistUriGrants();
+            }
+        }
+    }
+
+    /**
+     * Prune any older {@link UriPermission} for the given UID until outstanding
+     * persisted grants are below {@link #MAX_PERSISTED_URI_GRANTS}.
+     *
+     * @return if any mutations occured that require persisting.
+     */
+    private boolean maybePrunePersistedUriGrantsLocked(int uid) {
+        final ArrayMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(uid);
+        if (perms == null) return false;
+        if (perms.size() < MAX_PERSISTED_URI_GRANTS) return false;
+
+        final ArrayList<UriPermission> persisted = Lists.newArrayList();
+        for (UriPermission perm : perms.values()) {
+            if (perm.persistedModeFlags != 0) {
+                persisted.add(perm);
+            }
+        }
+
+        final int trimCount = persisted.size() - MAX_PERSISTED_URI_GRANTS;
+        if (trimCount <= 0) return false;
+
+        Collections.sort(persisted, new UriPermission.PersistedTimeComparator());
+        for (int i = 0; i < trimCount; i++) {
+            final UriPermission perm = persisted.get(i);
+
+            if (DEBUG_URI_PERMISSION) {
+                Slog.v(TAG, "Trimming grant created at " + perm.persistedCreateTime);
+            }
+
+            perm.releasePersistableModes(~0);
+            removeUriPermissionIfNeededLocked(perm);
+        }
+
+        return true;
+    }
+
+    @Override
+    public ParceledListSlice<android.content.UriPermission> getPersistedUriPermissions() {
+        enforceNotIsolatedCaller("getPersistedUriPermissions");
+
+        synchronized (this) {
+            final int callingUid = Binder.getCallingUid();
+            final ArrayList<android.content.UriPermission> result = Lists.newArrayList();
+            final ArrayMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
+            if (perms == null) {
+                Slog.w(TAG, "No permission grants found for UID " + callingUid);
+            } else {
+                final int size = perms.size();
+                for (int i = 0; i < size; i++) {
+                    final UriPermission perm = perms.valueAt(i);
+                    if (perm.persistedModeFlags != 0) {
+                        result.add(perm.buildPersistedPublicApiObject());
                     }
                 }
             }
-
-            return result.toArray(new Uri[result.size()]);
+            return new ParceledListSlice<android.content.UriPermission>(result);
         }
     }
 
@@ -10327,8 +10462,10 @@
                 pw.println();
             }
             pw.print("  Process LRU list (sorted by oom_adj, "); pw.print(mLruProcesses.size());
-                    pw.print(" total, non-activities at ");
+                    pw.print(" total, non-act at ");
                     pw.print(mLruProcesses.size()-mLruProcessActivityStart);
+                    pw.print(", non-svc at ");
+                    pw.print(mLruProcesses.size()-mLruProcessServiceStart);
                     pw.println("):");
             dumpProcessOomList(pw, this, mLruProcesses, "    ", "Proc", "PERS", false, dumpPackage);
             needSep = true;
@@ -10704,8 +10841,10 @@
 
             if (needSep) pw.println();
             pw.print("  Process OOM control ("); pw.print(mLruProcesses.size());
-                    pw.print(" total, non-activities at ");
+                    pw.print(" total, non-act at ");
                     pw.print(mLruProcesses.size()-mLruProcessActivityStart);
+                    pw.print(", non-svc at ");
+                    pw.print(mLruProcesses.size()-mLruProcessServiceStart);
                     pw.println("):");
             dumpProcessOomList(pw, this, mLruProcesses, "    ", "Proc", "PERS", true, null);
             needSep = true;
@@ -11513,6 +11652,7 @@
     final void dumpApplicationMemoryUsage(FileDescriptor fd,
             PrintWriter pw, String prefix, String[] args, boolean brief, PrintWriter categoryPw) {
         boolean dumpDetails = false;
+        boolean dumpFullDetails = false;
         boolean dumpDalvik = false;
         boolean oomOnly = false;
         boolean isCompact = false;
@@ -11526,6 +11666,7 @@
             opti++;
             if ("-a".equals(opt)) {
                 dumpDetails = true;
+                dumpFullDetails = true;
                 dumpDalvik = true;
             } else if ("-d".equals(opt)) {
                 dumpDalvik = true;
@@ -11613,7 +11754,8 @@
                 if (dumpDetails) {
                     try {
                         pw.flush();
-                        thread.dumpMemInfo(fd, mi, isCheckinRequest, true, dumpDalvik, innerArgs);
+                        thread.dumpMemInfo(fd, mi, isCheckinRequest, dumpFullDetails,
+                                dumpDalvik, innerArgs);
                     } catch (RemoteException e) {
                         if (!isCheckinRequest) {
                             pw.println("Got RemoteException!");
@@ -11754,25 +11896,53 @@
             if (!isCompact) {
                 pw.println();
             }
+            MemInfoReader memInfo = new MemInfoReader();
+            memInfo.readMemInfo();
             if (!brief) {
-                MemInfoReader memInfo = new MemInfoReader();
-                memInfo.readMemInfo();
                 if (!isCompact) {
-                    pw.print("Total RAM: "); pw.print(memInfo.getTotalSize()/1024);
+                    pw.print("Total RAM: "); pw.print(memInfo.getTotalSizeKb());
                     pw.println(" kB");
-                    pw.print(" Free RAM: "); pw.print(cachedPss + (memInfo.getCachedSize()/1024)
-                            + (memInfo.getFreeSize()/1024)); pw.println(" kB");
+                    pw.print(" Free RAM: "); pw.print(cachedPss + memInfo.getCachedSizeKb()
+                            + memInfo.getFreeSizeKb()); pw.print(" kB (");
+                            pw.print(cachedPss); pw.print(" cached pss + ");
+                            pw.print(memInfo.getCachedSizeKb()); pw.print(" cached + ");
+                            pw.print(memInfo.getFreeSizeKb()); pw.println(" free)");
                 } else {
-                    pw.print("ram,"); pw.print(memInfo.getTotalSize()/1024); pw.print(",");
-                    pw.print(cachedPss + (memInfo.getCachedSize()/1024)
-                            + (memInfo.getFreeSize()/1024)); pw.print(",");
+                    pw.print("ram,"); pw.print(memInfo.getTotalSizeKb()); pw.print(",");
+                    pw.print(cachedPss + memInfo.getCachedSizeKb()
+                            + memInfo.getFreeSizeKb()); pw.print(",");
                     pw.println(totalPss - cachedPss);
                 }
             }
             if (!isCompact) {
-                pw.print(" Used PSS: "); pw.print(totalPss - cachedPss); pw.println(" kB");
+                pw.print(" Used RAM: "); pw.print(totalPss - cachedPss
+                        + memInfo.getBuffersSizeKb() + memInfo.getShmemSizeKb()
+                        + memInfo.getSlabSizeKb()); pw.print(" kB (");
+                        pw.print(totalPss - cachedPss); pw.print(" used pss + ");
+                        pw.print(memInfo.getBuffersSizeKb()); pw.print(" buffers + ");
+                        pw.print(memInfo.getShmemSizeKb()); pw.print(" shmem + ");
+                        pw.print(memInfo.getSlabSizeKb()); pw.println(" slab)");
+                pw.print(" Lost RAM: "); pw.print(memInfo.getTotalSizeKb()
+                        - totalPss - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
+                        - memInfo.getBuffersSizeKb() - memInfo.getShmemSizeKb()
+                        - memInfo.getSlabSizeKb()); pw.println(" kB");
             }
             if (!brief) {
+                if (memInfo.getZramTotalSizeKb() != 0) {
+                    if (!isCompact) {
+                        pw.print("     ZRAM: "); pw.print(memInfo.getZramTotalSizeKb());
+                                pw.print(" kB used for ");
+                                pw.print(memInfo.getSwapTotalSizeKb()
+                                        - memInfo.getSwapFreeSizeKb());
+                                pw.print(" kB in swap (");
+                                pw.print(memInfo.getSwapTotalSizeKb());
+                                pw.println(" kB total swap)");
+                    } else {
+                        pw.print("zram,"); pw.print(memInfo.getZramTotalSizeKb()); pw.print(",");
+                                pw.print(memInfo.getSwapTotalSizeKb()); pw.print(",");
+                                pw.println(memInfo.getSwapFreeSizeKb());
+                    }
+                }
                 final int[] SINGLE_LONG_FORMAT = new int[] {
                     Process.PROC_SPACE_TERM|Process.PROC_OUT_LONG
                 };
@@ -11807,6 +11977,9 @@
                     pw.print("), oom ");
                     pw.print(mProcessList.getMemLevel(ProcessList.CACHED_APP_MAX_ADJ)/1024);
                     pw.print(" kB");
+                    pw.print(", restore limit ");
+                    pw.print(mProcessList.getCachedRestoreThresholdKb());
+                    pw.print(" kB");
                     if (ActivityManager.isLowRamDeviceStatic()) {
                         pw.print(" (low-ram)");
                     }
@@ -14342,14 +14515,30 @@
 
         if (adj == ProcessList.SERVICE_ADJ) {
             if (doingAll) {
-                app.serviceb = mNewNumServiceProcs > (mNumServiceProcs/3);
+                app.serviceb = mNewNumAServiceProcs > (mNumServiceProcs/3);
                 mNewNumServiceProcs++;
+                //Slog.i(TAG, "ADJ " + app + " serviceb=" + app.serviceb);
+                if (!app.serviceb) {
+                    // This service isn't far enough down on the LRU list to
+                    // normally be a B service, but if we are low on RAM and it
+                    // is large we want to force it down since we would prefer to
+                    // keep launcher over it.
+                    if (mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
+                            && app.lastPss >= mProcessList.getCachedRestoreThresholdKb()) {
+                        app.serviceHighRam = true;
+                        app.serviceb = true;
+                        //Slog.i(TAG, "ADJ " + app + " high ram!");
+                    } else {
+                        mNewNumAServiceProcs++;
+                        //Slog.i(TAG, "ADJ " + app + " not high ram!");
+                    }
+                } else {
+                    app.serviceHighRam = false;
+                }
             }
             if (app.serviceb) {
                 adj = ProcessList.SERVICE_B_ADJ;
             }
-        } else {
-            app.serviceb = false;
         }
 
         app.curRawAdj = adj;
@@ -14905,6 +15094,7 @@
 
         mAdjSeq++;
         mNewNumServiceProcs = 0;
+        mNewNumAServiceProcs = 0;
 
         final int emptyProcessLimit;
         final int cachedProcessLimit;
diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java
index f24e7fe..d3777c7 100644
--- a/services/java/com/android/server/am/ProcessList.java
+++ b/services/java/com/android/server/am/ProcessList.java
@@ -502,7 +502,7 @@
      * Return the maximum pss size in kb that we consider a process acceptable to
      * restore from its cached state for running in the background when RAM is low.
      */
-    long getCachedRestoreThreshold() {
+    long getCachedRestoreThresholdKb() {
         return mCachedRestoreLevel;
     }
 
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index c5491ef..4b62e7d 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -82,6 +82,7 @@
     int setProcState = -1;      // Last set process state in process tracker
     int pssProcState = -1;      // The proc state we are currently requesting pss for
     boolean serviceb;           // Process currently is on the service B list
+    boolean serviceHighRam;     // We are forcing to service B list due to its RAM use
     boolean keeping;            // Actively running code so don't kill due to that?
     boolean setIsForeground;    // Running foreground UI when last set?
     boolean notCachedSinceIdle; // Has this process not been in a cached state since last idle?
@@ -223,10 +224,13 @@
                 pw.print(" lruSeq="); pw.print(lruSeq);
                 pw.print(" lastPss="); pw.print(lastPss);
                 pw.print(" lastCachedPss="); pw.println(lastCachedPss);
-        pw.print(prefix); pw.print("serviceb="); pw.print(serviceb);
-                pw.print(" keeping="); pw.print(keeping);
+        pw.print(prefix); pw.print("keeping="); pw.print(keeping);
                 pw.print(" cached="); pw.print(cached);
                 pw.print(" empty="); pw.println(empty);
+        if (serviceb) {
+            pw.print(prefix); pw.print("serviceb="); pw.print(serviceb);
+                    pw.print(" serviceHighRam="); pw.println(serviceHighRam);
+        }
         if (notCachedSinceIdle) {
             pw.print(prefix); pw.print("notCachedSinceIdle="); pw.print(notCachedSinceIdle);
                     pw.print(" initialIdlePss="); pw.println(initialIdlePss);
diff --git a/services/java/com/android/server/am/UriPermission.java b/services/java/com/android/server/am/UriPermission.java
index 5e2ad009..7057c24 100644
--- a/services/java/com/android/server/am/UriPermission.java
+++ b/services/java/com/android/server/am/UriPermission.java
@@ -20,11 +20,12 @@
 import android.net.Uri;
 import android.os.UserHandle;
 import android.util.Log;
+import android.util.Slog;
 
-import com.android.internal.util.IndentingPrintWriter;
 import com.google.android.collect.Sets;
 
 import java.io.PrintWriter;
+import java.util.Comparator;
 import java.util.HashSet;
 
 /**
@@ -38,6 +39,11 @@
 final class UriPermission {
     private static final String TAG = "UriPermission";
 
+    public static final int STRENGTH_NONE = 0;
+    public static final int STRENGTH_OWNED = 1;
+    public static final int STRENGTH_GLOBAL = 2;
+    public static final int STRENGTH_PERSISTABLE = 3;
+
     final int userHandle;
     final String sourcePkg;
     final String targetPkg;
@@ -49,26 +55,29 @@
 
     /**
      * Allowed modes. All permission enforcement should use this field. Must
-     * always be a superset of {@link #globalModeFlags},
-     * {@link #persistedModeFlags}, {@link #mReadOwners}, and
-     * {@link #mWriteOwners}. Mutations should only be performed by the owning
-     * class.
+     * always be a combination of {@link #ownedModeFlags},
+     * {@link #globalModeFlags}, {@link #persistableModeFlags}, and
+     * {@link #persistedModeFlags}. Mutations <em>must</em> only be performed by
+     * the owning class.
      */
     int modeFlags = 0;
 
-    /**
-     * Allowed modes without explicit owner. Must always be a superset of
-     * {@link #persistedModeFlags}. Mutations should only be performed by the
-     * owning class.
-     */
+    /** Allowed modes with explicit owner. */
+    int ownedModeFlags = 0;
+    /** Allowed modes without explicit owner. */
     int globalModeFlags = 0;
+    /** Allowed modes that have been offered for possible persisting. */
+    int persistableModeFlags = 0;
+    /** Allowed modes that should be persisted across device boots. */
+    int persistedModeFlags = 0;
 
     /**
-     * Allowed modes that should be persisted across device boots. These modes
-     * have no explicit owners. Mutations should only be performed by the owning
-     * class.
+     * Timestamp when {@link #persistedModeFlags} was first defined in
+     * {@link System#currentTimeMillis()} time base.
      */
-    int persistedModeFlags = 0;
+    long persistedCreateTime = INVALID_TIME;
+
+    private static final long INVALID_TIME = Long.MIN_VALUE;
 
     private HashSet<UriPermissionOwner> mReadOwners;
     private HashSet<UriPermissionOwner> mWriteOwners;
@@ -83,21 +92,25 @@
         this.uri = uri;
     }
 
+    private void updateModeFlags() {
+        modeFlags = ownedModeFlags | globalModeFlags | persistableModeFlags | persistedModeFlags;
+    }
+
     /**
-     * @return If mode changes should trigger persisting.
+     * Initialize persisted modes as read from file. This doesn't issue any
+     * global or owner grants.
      */
-    boolean grantModes(int modeFlagsToGrant, boolean persist, UriPermissionOwner owner) {
-        boolean persistChanged = false;
+    void initPersistedModes(int modeFlags, long createdTime) {
+        persistableModeFlags = modeFlags;
+        persistedModeFlags = modeFlags;
+        persistedCreateTime = createdTime;
 
-        modeFlags |= modeFlagsToGrant;
+        updateModeFlags();
+    }
 
-        if (persist) {
-            final int before = persistedModeFlags;
-            persistedModeFlags |= modeFlagsToGrant;
-            persistChanged = persistedModeFlags != before;
-
-            // Treat persisted grants as global (ownerless)
-            owner = null;
+    void grantModes(int modeFlags, boolean persistable, UriPermissionOwner owner) {
+        if (persistable) {
+            persistableModeFlags |= modeFlags;
         }
 
         if (owner == null) {
@@ -105,43 +118,77 @@
         } else {
             if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
                 addReadOwner(owner);
-                owner.addReadPermission(this);
             }
             if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
                 addWriteOwner(owner);
-                owner.addWritePermission(this);
             }
         }
 
-        return persistChanged;
+        updateModeFlags();
     }
-    
+
     /**
-     * @return If mode changes should trigger persisting.
+     * @return if mode changes should trigger persisting.
      */
-    boolean clearModes(int modeFlagsToClear, boolean persist) {
+    boolean takePersistableModes(int modeFlags) {
+        if ((~persistableModeFlags & modeFlags) != 0) {
+            Slog.w(TAG, "Trying to take 0x" + Integer.toHexString(modeFlags) + " but only 0x"
+                    + Integer.toHexString(persistableModeFlags) + " are available");
+        }
+
+        final int before = persistedModeFlags;
+        persistedModeFlags |= (persistableModeFlags & modeFlags);
+
+        if (persistedModeFlags != 0) {
+            persistedCreateTime = System.currentTimeMillis();
+        }
+
+        updateModeFlags();
+        return persistedModeFlags != before;
+    }
+
+    boolean releasePersistableModes(int modeFlags) {
         final int before = persistedModeFlags;
 
-        if ((modeFlagsToClear & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
-            if (persist) {
+        persistableModeFlags &= ~modeFlags;
+        persistedModeFlags &= ~modeFlags;
+
+        if (persistedModeFlags == 0) {
+            persistedCreateTime = INVALID_TIME;
+        }
+
+        updateModeFlags();
+        return persistedModeFlags != before;
+    }
+
+    /**
+     * @return if mode changes should trigger persisting.
+     */
+    boolean clearModes(int modeFlags, boolean persistable) {
+        final int before = persistedModeFlags;
+
+        if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+            if (persistable) {
+                persistableModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
                 persistedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
             }
             globalModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
-            modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
             if (mReadOwners != null) {
+                ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
                 for (UriPermissionOwner r : mReadOwners) {
                     r.removeReadPermission(this);
                 }
                 mReadOwners = null;
             }
         }
-        if ((modeFlagsToClear & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
-            if (persist) {
+        if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+            if (persistable) {
+                persistableModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
                 persistedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
             }
             globalModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
-            modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
             if (mWriteOwners != null) {
+                ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
                 for (UriPermissionOwner r : mWriteOwners) {
                     r.removeWritePermission(this);
                 }
@@ -149,18 +196,38 @@
             }
         }
 
-        // Mode flags always bubble up
-        globalModeFlags |= persistedModeFlags;
-        modeFlags |= globalModeFlags;
+        if (persistedModeFlags == 0) {
+            persistedCreateTime = INVALID_TIME;
+        }
 
+        updateModeFlags();
         return persistedModeFlags != before;
     }
 
+    /**
+     * Return strength of this permission grant for the given flags.
+     */
+    public int getStrength(int modeFlags) {
+        if ((persistableModeFlags & modeFlags) == modeFlags) {
+            return STRENGTH_PERSISTABLE;
+        } else if ((globalModeFlags & modeFlags) == modeFlags) {
+            return STRENGTH_GLOBAL;
+        } else if ((ownedModeFlags & modeFlags) == modeFlags) {
+            return STRENGTH_OWNED;
+        } else {
+            return STRENGTH_NONE;
+        }
+    }
+
     private void addReadOwner(UriPermissionOwner owner) {
         if (mReadOwners == null) {
             mReadOwners = Sets.newHashSet();
+            ownedModeFlags |= Intent.FLAG_GRANT_READ_URI_PERMISSION;
+            updateModeFlags();
         }
-        mReadOwners.add(owner);
+        if (mReadOwners.add(owner)) {
+            owner.addReadPermission(this);
+        }
     }
 
     /**
@@ -172,17 +239,20 @@
         }
         if (mReadOwners.size() == 0) {
             mReadOwners = null;
-            if ((globalModeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
-                modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
-            }
+            ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+            updateModeFlags();
         }
     }
 
     private void addWriteOwner(UriPermissionOwner owner) {
         if (mWriteOwners == null) {
             mWriteOwners = Sets.newHashSet();
+            ownedModeFlags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+            updateModeFlags();
         }
-        mWriteOwners.add(owner);
+        if (mWriteOwners.add(owner)) {
+            owner.addWritePermission(this);
+        }
     }
 
     /**
@@ -194,9 +264,8 @@
         }
         if (mWriteOwners.size() == 0) {
             mWriteOwners = null;
-            if ((globalModeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
-                modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
-            }
+            ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+            updateModeFlags();
         }
     }
 
@@ -221,9 +290,15 @@
         pw.println(" targetPkg=" + targetPkg);
 
         pw.print(prefix);
-        pw.print("modeFlags=0x" + Integer.toHexString(modeFlags));
-        pw.print(" globalModeFlags=0x" + Integer.toHexString(globalModeFlags));
-        pw.println(" persistedModeFlags=0x" + Integer.toHexString(persistedModeFlags));
+        pw.print("mode=0x" + Integer.toHexString(modeFlags));
+        pw.print(" owned=0x" + Integer.toHexString(ownedModeFlags));
+        pw.print(" global=0x" + Integer.toHexString(globalModeFlags));
+        pw.print(" persistable=0x" + Integer.toHexString(persistableModeFlags));
+        pw.print(" persisted=0x" + Integer.toHexString(persistedModeFlags));
+        if (persistedCreateTime != INVALID_TIME) {
+            pw.print(" persistedCreate=" + persistedCreateTime);
+        }
+        pw.println();
 
         if (mReadOwners != null) {
             pw.print(prefix);
@@ -243,6 +318,13 @@
         }
     }
 
+    public static class PersistedTimeComparator implements Comparator<UriPermission> {
+        @Override
+        public int compare(UriPermission lhs, UriPermission rhs) {
+            return Long.compare(lhs.persistedCreateTime, rhs.persistedCreateTime);
+        }
+    }
+
     /**
      * Snapshot of {@link UriPermission} with frozen
      * {@link UriPermission#persistedModeFlags} state.
@@ -253,6 +335,7 @@
         final String targetPkg;
         final Uri uri;
         final int persistedModeFlags;
+        final long persistedCreateTime;
 
         private Snapshot(UriPermission perm) {
             this.userHandle = perm.userHandle;
@@ -260,10 +343,15 @@
             this.targetPkg = perm.targetPkg;
             this.uri = perm.uri;
             this.persistedModeFlags = perm.persistedModeFlags;
+            this.persistedCreateTime = perm.persistedCreateTime;
         }
     }
 
     public Snapshot snapshot() {
         return new Snapshot(this);
     }
+
+    public android.content.UriPermission buildPersistedPublicApiObject() {
+        return new android.content.UriPermission(uri, persistedModeFlags, persistedCreateTime);
+    }
 }