Merge "Remove icon from base alert dialog, use light theme"
diff --git a/Android.mk b/Android.mk
index a7eb03d..5d957d0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -210,6 +210,7 @@
 	core/java/android/service/dreams/IDozeHardware.aidl \
 	core/java/android/service/dreams/IDreamManager.aidl \
 	core/java/android/service/dreams/IDreamService.aidl \
+	core/java/android/service/persistentdata/IPersistentDataBlockService.aidl \
 	core/java/android/service/fingerprint/IFingerprintService.aidl \
 	core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl \
 	core/java/android/service/trust/ITrustAgentService.aidl \
diff --git a/api/current.txt b/api/current.txt
index 453f33d..29ec56b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -35802,6 +35802,7 @@
     method public deprecated void onGlobalFocusChanged(android.view.View, android.view.View);
     method public void onPause();
     method public void onResume();
+    method public static void optOutDataReductionProxy();
     method public boolean overlayHorizontalScrollbar();
     method public boolean overlayVerticalScrollbar();
     method public boolean pageDown(boolean);
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index a435fba..92e9290 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -19,15 +19,18 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.IActivityManager;
+import android.app.PackageInstallObserver;
 import android.content.ComponentName;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageDeleteObserver;
-import android.content.pm.IPackageInstallObserver2;
+import android.content.pm.IPackageInstaller;
 import android.content.pm.IPackageManager;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstallerParams;
 import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
@@ -46,12 +49,21 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.Log;
 
 import com.android.internal.content.PackageHelper;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.SizedInputStream;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
 
 import java.io.File;
 import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
@@ -61,7 +73,10 @@
 import java.util.WeakHashMap;
 
 public final class Pm {
+    private static final String TAG = "Pm";
+
     IPackageManager mPm;
+    IPackageInstaller mInstaller;
     IUserManager mUm;
 
     private WeakHashMap<String, Resources> mResourceCache
@@ -75,10 +90,18 @@
         "Error: Could not access the Package Manager.  Is the system running?";
 
     public static void main(String[] args) {
-        new Pm().run(args);
+        try {
+            new Pm().run(args);
+        } catch (Exception e) {
+            Log.e(TAG, "Error", e);
+            System.err.println("Error: " + e);
+            if (e instanceof RemoteException) {
+                System.err.println(PM_NOT_RUNNING_ERR);
+            }
+        }
     }
 
-    public void run(String[] args) {
+    public void run(String[] args) throws IOException, RemoteException {
         boolean validCommand = false;
         if (args.length < 1) {
             showUsage();
@@ -91,6 +114,7 @@
             System.err.println(PM_NOT_RUNNING_ERR);
             return;
         }
+        mInstaller = mPm.getPackageInstaller();
 
         mArgs = args;
         String op = args[0];
@@ -116,6 +140,31 @@
             return;
         }
 
+        if ("install-create".equals(op)) {
+            runInstallCreate();
+            return;
+        }
+
+        if ("install-write".equals(op)) {
+            runInstallWrite();
+            return;
+        }
+
+        if ("install-commit".equals(op)) {
+            runInstallCommit();
+            return;
+        }
+
+        if ("install-destroy".equals(op)) {
+            runInstallDestroy();
+            return;
+        }
+
+        if ("set-installer".equals(op)) {
+            runSetInstaller();
+            return;
+        }
+
         if ("uninstall".equals(op)) {
             runUninstall();
             return;
@@ -697,7 +746,7 @@
         ActivityManager.dumpPackageStateStatic(FileDescriptor.out, pkg);
     }
 
-    class PackageInstallObserver extends IPackageInstallObserver2.Stub {
+    class LocalPackageInstallObserver extends PackageInstallObserver {
         boolean finished;
         int result;
         String extraPermission;
@@ -705,7 +754,7 @@
 
         @Override
         public void packageInstalled(String name, Bundle extras, int status) {
-            synchronized( this) {
+            synchronized (this) {
                 finished = true;
                 result = status;
                 if (status == PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION) {
@@ -723,7 +772,7 @@
      * Converts a failure code into a string by using reflection to find a matching constant
      * in PackageManager.
      */
-    private String installFailureToString(PackageInstallObserver obs) {
+    private String installFailureToString(LocalPackageInstallObserver obs) {
         final int result = obs.result;
         Field[] fields = PackageManager.class.getFields();
         for (Field f: fields) {
@@ -908,12 +957,12 @@
             verificationURI = null;
         }
 
-        PackageInstallObserver obs = new PackageInstallObserver();
+        LocalPackageInstallObserver obs = new LocalPackageInstallObserver();
         try {
             VerificationParams verificationParams = new VerificationParams(verificationURI,
                     originatingURI, referrerURI, VerificationParams.NO_UID, null);
 
-            mPm.installPackage(apkFilePath, obs, installFlags, installerPackageName,
+            mPm.installPackage(apkFilePath, obs.getBinder(), installFlags, installerPackageName,
                     verificationParams, abi);
 
             synchronized (obs) {
@@ -937,35 +986,149 @@
         }
     }
 
-    /**
-     * Convert a string containing hex-encoded bytes to a byte array.
-     *
-     * @param input String containing hex-encoded bytes
-     * @return input as an array of bytes
-     */
-    private byte[] hexToBytes(String input) {
-        if (input == null) {
-            return null;
+    private void runInstallCreate() throws RemoteException {
+        String installerPackageName = null;
+
+        final PackageInstallerParams params = new PackageInstallerParams();
+        params.installFlags = PackageManager.INSTALL_ALL_USERS;
+        params.fullInstall = true;
+
+        String opt;
+        while ((opt = nextOption()) != null) {
+            if (opt.equals("-l")) {
+                params.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
+            } else if (opt.equals("-r")) {
+                params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
+            } else if (opt.equals("-i")) {
+                installerPackageName = nextArg();
+                if (installerPackageName == null) {
+                    throw new IllegalArgumentException("Missing installer package");
+                }
+            } else if (opt.equals("-t")) {
+                params.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
+            } else if (opt.equals("-s")) {
+                params.installFlags |= PackageManager.INSTALL_EXTERNAL;
+            } else if (opt.equals("-f")) {
+                params.installFlags |= PackageManager.INSTALL_INTERNAL;
+            } else if (opt.equals("-d")) {
+                params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
+            } else if (opt.equals("-p")) {
+                params.fullInstall = false;
+            } else if (opt.equals("-S")) {
+                params.deltaSize = Long.parseLong(nextOptionData());
+            } else {
+                throw new IllegalArgumentException("Unknown option " + opt);
+            }
         }
 
-        final int inputLength = input.length();
-        if ((inputLength % 2) != 0) {
-            System.err.print("Invalid length; must be multiple of 2");
-            return null;
+        final int sessionId = mInstaller.createSession(installerPackageName, params,
+                UserHandle.USER_OWNER);
+
+        // NOTE: adb depends on parsing this string
+        System.out.println("Success: created install session [" + sessionId + "]");
+    }
+
+    private void runInstallWrite() throws IOException, RemoteException {
+        long sizeBytes = -1;
+
+        String opt;
+        while ((opt = nextOption()) != null) {
+            if (opt.equals("-S")) {
+                sizeBytes = Long.parseLong(nextOptionData());
+            } else {
+                throw new IllegalArgumentException("Unknown option: " + opt);
+            }
         }
 
-        final int byteLength = inputLength / 2;
-        final byte[] output = new byte[byteLength];
+        final int sessionId = Integer.parseInt(nextArg());
+        final String splitName = nextArg();
 
-        int inputIndex = 0;
-        int byteIndex = 0;
-        while (inputIndex < inputLength) {
-            output[byteIndex++] = (byte) Integer.parseInt(
-                    input.substring(inputIndex, inputIndex + 2), 16);
-            inputIndex += 2;
+        String path = nextArg();
+        if ("-".equals(path)) {
+            path = null;
+        } else if (path != null) {
+            final File file = new File(path);
+            if (file.isFile()) {
+                sizeBytes = file.length();
+            }
         }
 
-        return output;
+        PackageInstaller.Session session = null;
+        InputStream in = null;
+        OutputStream out = null;
+        try {
+            session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
+
+            if (path != null) {
+                in = new FileInputStream(path);
+            } else {
+                in = new SizedInputStream(System.in, sizeBytes);
+            }
+            out = session.openWrite(splitName, 0, sizeBytes);
+
+            final int n = Streams.copy(in, out);
+            out.flush();
+
+            System.out.println("Success: streamed " + n + " bytes");
+        } finally {
+            IoUtils.closeQuietly(out);
+            IoUtils.closeQuietly(in);
+            IoUtils.closeQuietly(session);
+        }
+    }
+
+    private void runInstallCommit() throws RemoteException {
+        final int sessionId = Integer.parseInt(nextArg());
+
+        PackageInstaller.Session session = null;
+        try {
+            session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
+
+            final LocalPackageInstallObserver observer = new LocalPackageInstallObserver();
+            session.install(observer);
+
+            synchronized (observer) {
+                while (!observer.finished) {
+                    try {
+                        observer.wait();
+                    } catch (InterruptedException e) {
+                    }
+                }
+                if (observer.result != PackageManager.INSTALL_SUCCEEDED) {
+                    throw new IllegalStateException(
+                            "Failure [" + installFailureToString(observer) + "]");
+                }
+            }
+            System.out.println("Success");
+        } finally {
+            IoUtils.closeQuietly(session);
+        }
+    }
+
+    private void runInstallDestroy() throws RemoteException {
+        final int sessionId = Integer.parseInt(nextArg());
+
+        PackageInstaller.Session session = null;
+        try {
+            session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
+            session.destroy();
+            System.out.println("Success");
+        } finally {
+            IoUtils.closeQuietly(session);
+        }
+    }
+
+    private void runSetInstaller() throws RemoteException {
+        final String targetPackage = nextArg();
+        final String installerPackageName = nextArg();
+
+        if (targetPackage == null || installerPackageName == null) {
+            throw new IllegalArgumentException(
+                    "must provide both target and installer package names");
+        }
+
+        mPm.setInstallerPackageName(targetPackage, installerPackageName);
+        System.out.println("Success");
     }
 
     public void runCreateUser() {
@@ -1146,10 +1309,10 @@
         }
     }
 
-    private boolean deletePackage(String pkg, int unInstallFlags, int userId) {
+    private boolean deletePackage(String packageName, int flags, int userId) {
         PackageDeleteObserver obs = new PackageDeleteObserver();
         try {
-            mPm.deletePackageAsUser(pkg, obs, userId, unInstallFlags);
+            mInstaller.uninstall(packageName, flags, obs, userId);
 
             synchronized (obs) {
                 while (!obs.finished) {
@@ -1548,10 +1711,13 @@
         System.err.println("       pm list users");
         System.err.println("       pm path PACKAGE");
         System.err.println("       pm dump PACKAGE");
-        System.err.println("       pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f]");
-        System.err.println("                  [--algo <algorithm name> --key <key-in-hex> --iv <IV-in-hex>]");
-        System.err.println("                  [--originating-uri <URI>] [--referrer <URI>] PATH");
+        System.err.println("       pm install [-lrtsfd] [-i PACKAGE] [PATH]");
+        System.err.println("       pm install-create [-lrtsfdp] [-i PACKAGE] [-S BYTES]");
+        System.err.println("       pm install-write [-S BYTES] SESSION_ID SPLIT_NAME [PATH]");
+        System.err.println("       pm install-commit SESSION_ID");
+        System.err.println("       pm install-destroy SESSION_ID");
         System.err.println("       pm uninstall [-k] [--user USER_ID] PACKAGE");
+        System.err.println("       pm set-installer PACKAGE INSTALLER");
         System.err.println("       pm clear [--user USER_ID] PACKAGE");
         System.err.println("       pm enable [--user USER_ID] PACKAGE_OR_COMPONENT");
         System.err.println("       pm disable [--user USER_ID] PACKAGE_OR_COMPONENT");
@@ -1600,16 +1766,28 @@
         System.err.println("");
         System.err.println("pm path: print the path to the .apk of the given PACKAGE.");
         System.err.println("");
-        System.err.println("pm dump: print system state associated w ith the given PACKAGE.");
+        System.err.println("pm dump: print system state associated with the given PACKAGE.");
         System.err.println("");
-        System.err.println("pm install: installs a package to the system.  Options:");
-        System.err.println("    -l: install the package with FORWARD_LOCK.");
-        System.err.println("    -r: reinstall an exisiting app, keeping its data.");
-        System.err.println("    -t: allow test .apks to be installed.");
-        System.err.println("    -i: specify the installer package name.");
-        System.err.println("    -s: install package on sdcard.");
-        System.err.println("    -f: install package on internal flash.");
-        System.err.println("    -d: allow version code downgrade.");
+        System.err.println("pm install: install a single legacy package");
+        System.err.println("pm install-create: create an install session");
+        System.err.println("    -l: forward lock application");
+        System.err.println("    -r: replace existing application");
+        System.err.println("    -t: allow test packages");
+        System.err.println("    -i: specify the installer package name");
+        System.err.println("    -s: install application on sdcard");
+        System.err.println("    -f: install application on internal flash");
+        System.err.println("    -d: allow version code downgrade");
+        System.err.println("    -p: partial application install");
+        System.err.println("    -S: size in bytes of entire session");
+        System.err.println("");
+        System.err.println("pm install-write: write a package into existing session; path may");
+        System.err.println("  be '-' to read from stdin");
+        System.err.println("    -S: size in bytes of package, required for stdin");
+        System.err.println("");
+        System.err.println("pm install-commit: perform install of fully staged session");
+        System.err.println("pm install-destroy: destroy session");
+        System.err.println("");
+        System.err.println("pm set-installer: set installer package name");
         System.err.println("");
         System.err.println("pm uninstall: removes a package from the system. Options:");
         System.err.println("    -k: keep the data and cache directories around after package removal.");
@@ -1643,5 +1821,6 @@
         System.err.println("");
         System.err.println("pm remove-user: remove the user with the given USER_IDENTIFIER,");
         System.err.println("  deleting all data associated with that user");
+        System.err.println("");
     }
 }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 97f1e50..2935b8e 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1463,8 +1463,8 @@
     @Override
     public PackageInstaller getPackageInstaller() {
         try {
-            return new PackageInstaller(this, mPM.getPackageInstaller(), mContext.getUserId(),
-                    mContext.getPackageName());
+            return new PackageInstaller(this, mPM.getPackageInstaller(), mContext.getPackageName(),
+                    mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index fa2266b..cacb2df 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -20,6 +20,8 @@
 import android.net.wifi.WifiScanner;
 import android.os.Build;
 
+import android.service.persistentdata.IPersistentDataBlockService;
+import android.service.persistentdata.PersistentDataBlockManager;
 import com.android.internal.policy.PolicyManager;
 import com.android.internal.util.Preconditions;
 
@@ -733,7 +735,14 @@
             public Object createService(ContextImpl ctx) {
                 IBinder b = ServiceManager.getService(JOB_SCHEDULER_SERVICE);
                 return new JobSchedulerImpl(IJobScheduler.Stub.asInterface(b));
-            }});
+        }});
+
+        registerService(PERSISTENT_DATA_BLOCK_SERVICE, new ServiceFetcher() {
+            public Object createService(ContextImpl ctx) {
+                IBinder b = ServiceManager.getService(PERSISTENT_DATA_BLOCK_SERVICE);
+                return new PersistentDataBlockManager(
+                        IPersistentDataBlockService.Stub.asInterface(b));
+        }});
     }
 
     static ContextImpl getImpl(Context context) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 80b6b58..04275d8 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2801,6 +2801,16 @@
     public static final String JOB_SCHEDULER_SERVICE = "jobscheduler";
 
     /**
+     * Use with {@link #getSystemService} to retrieve a {@link
+     * android.service.persistentdata.PersistentDataBlockManager} instance retrieving
+     * a file descriptor for a persistent data block.
+     * @see #getSystemService
+     * @see android.service.persistentdata.PersistentDataBlockManager
+     * @hide
+     */
+    public static final String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index 68c019b..4d6ee64 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -23,11 +23,11 @@
 
 /** {@hide} */
 interface IPackageInstaller {
-    int createSession(int userId, String installerPackageName, in PackageInstallerParams params);
+    int createSession(String installerPackageName, in PackageInstallerParams params, int userId);
     IPackageInstallerSession openSession(int sessionId);
 
-    int[] getSessions(int userId, String installerPackageName);
+    int[] getSessions(String installerPackageName, int userId);
 
-    void uninstall(int userId, String basePackageName, in IPackageDeleteObserver observer);
-    void uninstallSplit(int userId, String basePackageName, String splitName, in IPackageDeleteObserver observer);
+    void uninstall(String basePackageName, int flags, in IPackageDeleteObserver observer, int userId);
+    void uninstallSplit(String basePackageName, String splitName, int flags, in IPackageDeleteObserver observer, int userId);
 }
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 4672015..a60a2fe 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -23,6 +23,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 
+import java.io.Closeable;
 import java.io.OutputStream;
 
 /** {@hide} */
@@ -33,12 +34,12 @@
     private final String mInstallerPackageName;
 
     /** {@hide} */
-    public PackageInstaller(PackageManager pm, IPackageInstaller installer, int userId,
-            String installerPackageName) {
+    public PackageInstaller(PackageManager pm, IPackageInstaller installer,
+            String installerPackageName, int userId) {
         mPm = pm;
         mInstaller = installer;
-        mUserId = userId;
         mInstallerPackageName = installerPackageName;
+        mUserId = userId;
     }
 
     public boolean isPackageAvailable(String basePackageName) {
@@ -63,7 +64,7 @@
 
     public int createSession(PackageInstallerParams params) {
         try {
-            return mInstaller.createSession(mUserId, mInstallerPackageName, params);
+            return mInstaller.createSession(mInstallerPackageName, params, mUserId);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -79,7 +80,7 @@
 
     public int[] getSessions() {
         try {
-            return mInstaller.getSessions(mUserId, mInstallerPackageName);
+            return mInstaller.getSessions(mInstallerPackageName, mUserId);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -87,7 +88,7 @@
 
     public void uninstall(String basePackageName, PackageUninstallObserver observer) {
         try {
-            mInstaller.uninstall(mUserId, basePackageName, observer.getBinder());
+            mInstaller.uninstall(basePackageName, 0, observer.getBinder(), mUserId);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -96,7 +97,7 @@
     public void uninstall(String basePackageName, String splitName,
             PackageUninstallObserver observer) {
         try {
-            mInstaller.uninstallSplit(mUserId, basePackageName, splitName, observer.getBinder());
+            mInstaller.uninstallSplit(basePackageName, splitName, 0, observer.getBinder(), mUserId);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -114,7 +115,7 @@
      * installation (for example, the same split name), the package in this
      * session will replace the existing package.
      */
-    public class Session {
+    public static class Session implements Closeable {
         private IPackageInstallerSession mSession;
 
         /** {@hide} */
@@ -154,6 +155,7 @@
             }
         }
 
+        @Override
         public void close() {
             // No resources to release at the moment
         }
diff --git a/core/java/android/content/pm/PackageInstallerParams.java b/core/java/android/content/pm/PackageInstallerParams.java
index 67cf276..527edee 100644
--- a/core/java/android/content/pm/PackageInstallerParams.java
+++ b/core/java/android/content/pm/PackageInstallerParams.java
@@ -41,6 +41,8 @@
     /** {@hide} */
     public long deltaSize = -1;
     /** {@hide} */
+    public String basePackageName;
+    /** {@hide} */
     public Bitmap icon;
     /** {@hide} */
     public String title;
@@ -48,23 +50,27 @@
     public Uri originatingUri;
     /** {@hide} */
     public Uri referrerUri;
+    /** {@hide} */
+    public String abiOverride;
 
     public PackageInstallerParams() {
     }
 
     /** {@hide} */
     public PackageInstallerParams(Parcel source) {
-        this.fullInstall = source.readInt() != 0;
-        this.installFlags = source.readInt();
-        this.installLocation = source.readInt();
-        this.signatures = (Signature[]) source.readParcelableArray(null);
+        fullInstall = source.readInt() != 0;
+        installFlags = source.readInt();
+        installLocation = source.readInt();
+        signatures = (Signature[]) source.readParcelableArray(null);
         deltaSize = source.readLong();
+        basePackageName = source.readString();
         if (source.readInt() != 0) {
             icon = Bitmap.CREATOR.createFromParcel(source);
         }
         title = source.readString();
-        originatingUri = Uri.CREATOR.createFromParcel(source);
-        referrerUri = Uri.CREATOR.createFromParcel(source);
+        originatingUri = source.readParcelable(null);
+        referrerUri = source.readParcelable(null);
+        abiOverride = source.readString();
     }
 
     public void setFullInstall(boolean fullInstall) {
@@ -87,6 +93,10 @@
         this.deltaSize = deltaSize;
     }
 
+    public void setBasePackageName(String basePackageName) {
+        this.basePackageName = basePackageName;
+    }
+
     public void setIcon(Bitmap icon) {
         this.icon = icon;
     }
@@ -115,6 +125,7 @@
         dest.writeInt(installLocation);
         dest.writeParcelableArray(signatures, flags);
         dest.writeLong(deltaSize);
+        dest.writeString(basePackageName);
         if (icon != null) {
             dest.writeInt(1);
             icon.writeToParcel(dest, flags);
@@ -124,6 +135,7 @@
         dest.writeString(title);
         dest.writeParcelable(originatingUri, flags);
         dest.writeParcelable(referrerUri, flags);
+        dest.writeString(abiOverride);
     }
 
     public static final Parcelable.Creator<PackageInstallerParams>
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 3b118c0..ab0bf25 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -71,7 +71,6 @@
 import java.security.spec.X509EncodedKeySpec;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -923,19 +922,17 @@
                             "Package " + apkPath + " has no certificates at entry "
                             + entry.getName());
                 }
+                final Signature[] entrySignatures = convertToSignatures(entryCerts);
 
                 if (pkg.mCertificates == null) {
                     pkg.mCertificates = entryCerts;
-                    pkg.mSignatures = convertToSignatures(entryCerts);
+                    pkg.mSignatures = entrySignatures;
                     pkg.mSigningKeys = new ArraySet<PublicKey>();
                     for (int i=0; i < entryCerts.length; i++) {
                         pkg.mSigningKeys.add(entryCerts[i][0].getPublicKey());
                     }
                 } else {
-                    final boolean certsMatch = (pkg.mCertificates.length == entryCerts.length)
-                            && ArrayUtils.containsAll(pkg.mCertificates, entryCerts)
-                            && ArrayUtils.containsAll(entryCerts, pkg.mCertificates);
-                    if (!certsMatch) {
+                    if (!Signature.areExactMatch(pkg.mSignatures, entrySignatures)) {
                         throw new PackageParserException(
                                 INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, "Package " + apkPath
                                         + " has mismatched certificates at entry "
@@ -1097,7 +1094,7 @@
             if (splitName.length() == 0) {
                 splitName = null;
             } else {
-                final String error = validateName(splitName, true);
+                final String error = validateName(splitName, false);
                 if (error != null) {
                     throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
                             "Invalid manifest split: " + error);
diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java
index 96aa083..7edf4b9 100644
--- a/core/java/android/content/pm/Signature.java
+++ b/core/java/android/content/pm/Signature.java
@@ -249,6 +249,7 @@
      * @hide
      */
     public static boolean areExactMatch(Signature[] a, Signature[] b) {
-        return ArrayUtils.containsAll(a, b) && ArrayUtils.containsAll(b, a);
+        return (a.length == b.length) && ArrayUtils.containsAll(a, b)
+                && ArrayUtils.containsAll(b, a);
     }
 }
diff --git a/core/java/android/os/FileBridge.java b/core/java/android/os/FileBridge.java
index 7f8bc9f..691afdd 100644
--- a/core/java/android/os/FileBridge.java
+++ b/core/java/android/os/FileBridge.java
@@ -54,6 +54,8 @@
     private static final int CMD_WRITE = 1;
     /** CMD_FSYNC */
     private static final int CMD_FSYNC = 2;
+    /** CMD_CLOSE */
+    private static final int CMD_CLOSE = 3;
 
     private FileDescriptor mTarget;
 
@@ -102,12 +104,17 @@
                     // Sync and echo back to confirm
                     Os.fsync(mTarget);
                     IoBridge.write(mServer, temp, 0, MSG_LENGTH);
+
+                } else if (cmd == CMD_CLOSE) {
+                    // Close and echo back to confirm
+                    Os.fsync(mTarget);
+                    Os.close(mTarget);
+                    mClosed = true;
+                    IoBridge.write(mServer, temp, 0, MSG_LENGTH);
+                    break;
                 }
             }
 
-            // Client was closed; one last fsync
-            Os.fsync(mTarget);
-
         } catch (ErrnoException e) {
             Log.e(TAG, "Failed during bridge: ", e);
         } catch (IOException e) {
@@ -130,22 +137,30 @@
 
         @Override
         public void close() throws IOException {
-            IoBridge.closeAndSignalBlockedThreads(mClient);
+            try {
+                writeCommandAndBlock(CMD_CLOSE, "close()");
+            } finally {
+                IoBridge.closeAndSignalBlockedThreads(mClient);
+            }
         }
 
         @Override
         public void flush() throws IOException {
-            Memory.pokeInt(mTemp, 0, CMD_FSYNC, ByteOrder.BIG_ENDIAN);
+            writeCommandAndBlock(CMD_FSYNC, "fsync()");
+        }
+
+        private void writeCommandAndBlock(int cmd, String cmdString) throws IOException {
+            Memory.pokeInt(mTemp, 0, cmd, ByteOrder.BIG_ENDIAN);
             IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
 
             // Wait for server to ack
             if (IoBridge.read(mClient, mTemp, 0, MSG_LENGTH) == MSG_LENGTH) {
-                if (Memory.peekInt(mTemp, 0, ByteOrder.BIG_ENDIAN) == CMD_FSYNC) {
+                if (Memory.peekInt(mTemp, 0, ByteOrder.BIG_ENDIAN) == cmd) {
                     return;
                 }
             }
 
-            throw new SyncFailedException("Failed to fsync() across bridge");
+            throw new IOException("Failed to execute " + cmdString + " across bridge");
         }
 
         @Override
diff --git a/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl b/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl
new file mode 100644
index 0000000..06e776d
--- /dev/null
+++ b/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl
@@ -0,0 +1,20 @@
+package android.service.persistentdata;
+
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Internal interface through which to communicate to the
+ * PersistentDataBlockService. The persistent data block allows writing
+ * raw data and setting the OEM unlock enabled/disabled bit contained
+ * in the partition.
+ *
+ * @hide
+ */
+interface IPersistentDataBlockService {
+    int write(in byte[] data);
+    int read(out byte[] data);
+    int getDataBlockSize();
+
+    void setOemUnlockEnabled(boolean enabled);
+    boolean getOemUnlockEnabled();
+}
diff --git a/core/java/android/service/persistentdata/PersistentDataBlockManager.java b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
new file mode 100644
index 0000000..afcf717
--- /dev/null
+++ b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
@@ -0,0 +1,97 @@
+package android.service.persistentdata;
+
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * Interface for reading and writing data blocks to a persistent partition.
+ *
+ * Allows writing one block at a time. Namely, each time
+ * {@link android.service.persistentdata.PersistentDataBlockManager}.write(byte[] data)
+ * is called, it will overwite the data that was previously written on the block.
+ *
+ * Clients can query the size of the currently written block via
+ * {@link android.service.persistentdata.PersistentDataBlockManager}.getTotalDataSize().
+ *
+ * Clients can any number of bytes from the currently written block up to its total size by invoking
+ * {@link android.service.persistentdata.PersistentDataBlockManager}.read(byte[] data).
+ *
+ * @hide
+ */
+public class PersistentDataBlockManager {
+    private static final String TAG = PersistentDataBlockManager.class.getSimpleName();
+    private IPersistentDataBlockService sService;
+
+    public PersistentDataBlockManager(IPersistentDataBlockService service) {
+        sService = service;
+    }
+
+    /**
+     * Writes {@code data} to the persistent partition. Previously written data
+     * will be overwritten. This data will persist across factory resets.
+     *
+     * @param data the data to write
+     */
+    public void write(byte[] data) {
+        try {
+            sService.write(data);
+        } catch (RemoteException e) {
+            onError("writing data");
+        }
+    }
+
+    /**
+     * Tries to read {@code data.length} bytes into {@code data}. Call {@code getDataBlockSize()}
+     * to determine the total size of the block currently residing in the persistent partition.
+     *
+     * @param data the buffer in which to read the data
+     * @return the actual number of bytes read
+     */
+    public int read(byte[] data) {
+        try {
+            return sService.read(data);
+        } catch (RemoteException e) {
+            onError("reading data");
+            return -1;
+        }
+    }
+
+    /**
+     * Retrieves the size of the block currently written to the persistent partition.
+     */
+    public int getDataBlockSize() {
+        try {
+            return sService.getDataBlockSize();
+        } catch (RemoteException e) {
+            onError("getting data block size");
+            return 0;
+        }
+    }
+
+    /**
+     * Writes a byte enabling or disabling the ability to "OEM unlock" the device.
+     */
+    public void setOemUnlockEnabled(boolean enabled) {
+        try {
+            sService.setOemUnlockEnabled(enabled);
+        } catch (RemoteException e) {
+            onError("setting OEM unlock enabled to " + enabled);
+        }
+    }
+
+    /**
+     * Returns whether or not "OEM unlock" is enabled or disabled on this device.
+     */
+    public boolean getOemUnlockEnabled() {
+        try {
+            return sService.getOemUnlockEnabled();
+        } catch (RemoteException e) {
+            onError("getting OEM unlock enabled bit");
+            return false;
+        }
+    }
+
+    private void onError(String msg) {
+        Slog.v(TAG, "Remote exception while " + msg);
+    }
+}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 482c7e8..290a574 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1805,6 +1805,21 @@
     }
 
     /**
+     * Sets whether the application wants to opt out from using the Data Reduction Proxy
+     * service.
+     * Data reduction proxy can only be enabled by the user and will almost always be
+     * transparent to the application. In rare cases where using the proxy interferes
+     * with the app, the application developer can use this API to opt out from using the
+     * proxy. Note that this may increase network bandwidth usage.
+     *
+     * See <a href=http://developer.chrome.com/multidevice/data-compression>
+     * Data Compression Proxy</a>
+     */
+    public static void optOutDataReductionProxy() {
+        getFactory().getStatics().optOutDataReductionProxy();
+    }
+
+    /**
      * Gets the list of currently loaded plugins.
      *
      * @return the list of currently loaded plugins
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index 6e6a987..5ff2655 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -59,6 +59,11 @@
          */
         void clearClientCertPreferences(Runnable onCleared);
 
+        /**
+         * Implements the API method:
+         * {@link android.webkit.WebView#optOutDataReductionProxy() }
+         */
+        void optOutDataReductionProxy();
     }
 
     Statics getStatics();
diff --git a/core/java/com/android/internal/util/SizedInputStream.java b/core/java/com/android/internal/util/SizedInputStream.java
new file mode 100644
index 0000000..00a729d
--- /dev/null
+++ b/core/java/com/android/internal/util/SizedInputStream.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import libcore.io.Streams;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Reads exact number of bytes from wrapped stream, returning EOF once those
+ * bytes have been read.
+ */
+public class SizedInputStream extends InputStream {
+    private final InputStream mWrapped;
+    private long mLength;
+
+    public SizedInputStream(InputStream wrapped, long length) {
+        mWrapped = wrapped;
+        mLength = length;
+    }
+
+    @Override
+    public void close() throws IOException {
+        super.close();
+        mWrapped.close();
+    }
+
+    @Override
+    public int read() throws IOException {
+        return Streams.readSingleByte(this);
+    }
+
+    @Override
+    public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
+        if (mLength <= 0) {
+            return -1;
+        } else if (byteCount > mLength) {
+            byteCount = (int) mLength;
+        }
+
+        final int n = mWrapped.read(buffer, byteOffset, byteCount);
+        if (n == -1) {
+            if (mLength > 0) {
+                throw new IOException("Unexpected EOF; expected " + mLength + " more bytes");
+            }
+        } else {
+            mLength -= n;
+        }
+        return n;
+    }
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 59399d2..e38fce9 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1075,6 +1075,13 @@
     <permission android:name="android.permission.TV_INPUT_HARDWARE"
         android:protectionLevel="signatureOrSystem" />
 
+    <!-- @hide Allows enabling/disabling OEM unlock
+   <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.OEM_UNLOCK_STATE"
+                android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+                android:protectionLevel="signature" />
+
+
     <!-- =========================================== -->
     <!-- Permissions associated with audio capture -->
     <!-- =========================================== -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f34fbc13..4585b78 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1610,12 +1610,15 @@
         -->
     </string-array>
 
-<!-- Flag indicating apps will skip sending hold request before merge. In this case
-         IMS service implementation will do both.i.e.hold followed by merge. -->
+    <!-- Flag indicating which package name can access the persistent data partition -->
+    <string name="config_persistentDataPackageName" translatable="false"></string>
+
+    <!-- Flag indicating apps will skip sending hold request before merge. In this case
+        IMS service implementation will do both.i.e.hold followed by merge. -->
     <bool name="skipHoldBeforeMerge">true</bool>
 
     <!-- Flag indicating emergency calls will always use IMS irrespective of the state of
-         the IMS connection -->
+    the IMS connection -->
     <bool name="useImsAlwaysForEmergencyCall">true</bool>
 
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9422212..2065088 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1668,6 +1668,7 @@
   <java-symbol type="string" name="config_customAdbPublicKeyConfirmationComponent" />
   <java-symbol type="string" name="config_customVpnConfirmDialogComponent" />
   <java-symbol type="string" name="config_defaultNetworkScorerPackageName" />
+  <java-symbol type="string" name="config_persistentDataPackageName" />
 
   <java-symbol type="layout" name="resolver_list" />
   <java-symbol type="id" name="resolver_list" />
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index 30c6f5d..bcfda99 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -187,7 +187,6 @@
  */
 bool ShadowTessellator::isClockwise(const Vector2* polygon, int len) {
     if (len < 2 || polygon == NULL) {
-        ALOGW("Invalid polygon %p, length is %d @ isClockwise()", polygon, len);
         return true;
     }
     double sum = 0;
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
new file mode 100644
index 0000000..1eded6f
--- /dev/null
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -0,0 +1,261 @@
+package com.android.server;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.service.persistentdata.IPersistentDataBlockService;
+import android.util.Log;
+import com.android.internal.R;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+
+/**
+ * Service for reading and writing blocks to a persistent partition.
+ * This data will live across factory resets.
+ *
+ * Allows writing one block at a time. Namely, each time
+ * {@link android.service.persistentdata.IPersistentDataBlockService}.write(byte[] data)
+ * is called, it will overwite the data that was previously written on the block.
+ *
+ * Clients can query the size of the currently written block via
+ * {@link android.service.persistentdata.IPersistentDataBlockService}.getTotalDataSize().
+ *
+ * Clients can any number of bytes from the currently written block up to its total size by invoking
+ * {@link android.service.persistentdata.IPersistentDataBlockService}.read(byte[] data)
+ */
+public class PersistentDataBlockService extends SystemService {
+    private static final String TAG = PersistentDataBlockService.class.getSimpleName();
+
+    private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
+    private static final int HEADER_SIZE = 8;
+    private static final int BLOCK_ID = 0x1990;
+
+    private final Context mContext;
+    private final String mDataBlockFile;
+    private long mBlockDeviceSize;
+
+    private final int mAllowedUid;
+
+    public PersistentDataBlockService(Context context) {
+        super(context);
+        mContext = context;
+        mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
+        mBlockDeviceSize = 0; // Load lazily
+        String allowedPackage = context.getResources()
+                .getString(R.string.config_persistentDataPackageName);
+        PackageManager pm = mContext.getPackageManager();
+        int allowedUid = -1;
+        try {
+            allowedUid = pm.getPackageUid(allowedPackage,
+                    Binder.getCallingUserHandle().getIdentifier());
+        } catch (PackageManager.NameNotFoundException e) {
+            // not expected
+            Log.e(TAG, "not able to find package " + allowedPackage, e);
+        }
+
+        mAllowedUid = allowedUid;
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService);
+    }
+
+    private void enforceOemUnlockPermission() {
+        mContext.enforceCallingOrSelfPermission(
+                Manifest.permission.OEM_UNLOCK_STATE,
+                "Can't access OEM unlock state");
+    }
+
+    private void enforceUid(int callingUid) {
+        if (callingUid != mAllowedUid) {
+            throw new SecurityException("uid " + callingUid + " not allowed to access PST");
+        }
+    }
+
+    private int getTotalDataSize(DataInputStream inputStream) throws IOException {
+        int totalDataSize;
+        int blockId = inputStream.readInt();
+        if (blockId == BLOCK_ID) {
+            totalDataSize = inputStream.readInt();
+        } else {
+            totalDataSize = 0;
+        }
+        return totalDataSize;
+    }
+
+    private long maybeReadBlockDeviceSize() {
+        synchronized (this) {
+            if (mBlockDeviceSize == 0) {
+                mBlockDeviceSize = getBlockDeviceSize(mDataBlockFile);
+            }
+        }
+
+        return mBlockDeviceSize;
+    }
+
+    private native long getBlockDeviceSize(String path);
+
+    private final IBinder mService = new IPersistentDataBlockService.Stub() {
+        @Override
+        public int write(byte[] data) throws RemoteException {
+            enforceUid(Binder.getCallingUid());
+
+            // Need to ensure we don't write over the last byte
+            if (data.length > maybeReadBlockDeviceSize() - HEADER_SIZE - 1) {
+                return -1;
+            }
+
+            DataOutputStream outputStream;
+            try {
+                outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
+            } catch (FileNotFoundException e) {
+                Log.e(TAG, "partition not available?", e);
+                return -1;
+            }
+
+            ByteBuffer headerAndData = ByteBuffer.allocate(data.length + HEADER_SIZE);
+            headerAndData.putInt(BLOCK_ID);
+            headerAndData.putInt(data.length);
+            headerAndData.put(data);
+
+            try {
+                outputStream.write(headerAndData.array());
+                return data.length;
+            } catch (IOException e) {
+                Log.e(TAG, "failed writing to the persistent data block", e);
+                return -1;
+            } finally {
+                try {
+                    outputStream.close();
+                } catch (IOException e) {
+                    Log.e(TAG, "failed closing output stream", e);
+                }
+            }
+        }
+
+        @Override
+        public int read(byte[] data) {
+            enforceUid(Binder.getCallingUid());
+
+            DataInputStream inputStream;
+            try {
+                inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
+            } catch (FileNotFoundException e) {
+                Log.e(TAG, "partition not available?", e);
+                return -1;
+            }
+
+            try {
+                int totalDataSize = getTotalDataSize(inputStream);
+                return inputStream.read(data, 0,
+                        (data.length > totalDataSize) ? totalDataSize : data.length);
+            } catch (IOException e) {
+                Log.e(TAG, "failed to read data", e);
+                return -1;
+            } finally {
+                try {
+                    inputStream.close();
+                } catch (IOException e) {
+                    Log.e(TAG, "failed to close OutputStream");
+                }
+            }
+        }
+
+        @Override
+        public void setOemUnlockEnabled(boolean enabled) {
+            enforceOemUnlockPermission();
+            FileOutputStream outputStream;
+            try {
+                outputStream = new FileOutputStream(new File(mDataBlockFile));
+            } catch (FileNotFoundException e) {
+                Log.e(TAG, "parition not available", e);
+                return;
+            }
+
+            try {
+                FileChannel channel = outputStream.getChannel();
+
+                channel.position(maybeReadBlockDeviceSize() - 1);
+
+                ByteBuffer data = ByteBuffer.allocate(1);
+                data.put(enabled ? (byte) 1 : (byte) 0);
+                data.flip();
+
+                channel.write(data);
+            } catch (IOException e) {
+                Log.e(TAG, "unable to access persistent partition", e);
+            } finally {
+                try {
+                    outputStream.close();
+                } catch (IOException e) {
+                    Log.e(TAG, "failed to close OutputStream");
+                }
+            }
+        }
+
+        @Override
+        public boolean getOemUnlockEnabled() {
+            enforceOemUnlockPermission();
+            DataInputStream inputStream;
+            try {
+                inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
+            } catch (FileNotFoundException e) {
+                Log.e(TAG, "partition not available");
+                return false;
+            }
+
+            try {
+                inputStream.skip(maybeReadBlockDeviceSize() - 1);
+                return inputStream.readByte() != 0;
+            } catch (IOException e) {
+                Log.e(TAG, "unable to access persistent partition", e);
+                return false;
+            } finally {
+                try {
+                    inputStream.close();
+                } catch (IOException e) {
+                    Log.e(TAG, "failed to close OutputStream");
+                }
+            }
+        }
+
+        @Override
+        public int getDataBlockSize() {
+            enforceUid(Binder.getCallingUid());
+
+            DataInputStream inputStream;
+            try {
+                inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
+            } catch (FileNotFoundException e) {
+                Log.e(TAG, "partition not available");
+                return 0;
+            }
+
+            try {
+                return getTotalDataSize(inputStream);
+            } catch (IOException e) {
+                Log.e(TAG, "error reading data block size");
+                return 0;
+            } finally {
+                try {
+                    inputStream.close();
+                } catch (IOException e) {
+                    Log.e(TAG, "failed to close OutputStream");
+                }
+            }
+        }
+    };
+}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 5fdfce4..682a38d 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -30,8 +30,11 @@
 import android.os.FileUtils;
 import android.os.HandlerThread;
 import android.os.Process;
+import android.os.SELinux;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.system.ErrnoException;
+import android.system.Os;
 import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -42,6 +45,8 @@
 import com.google.android.collect.Sets;
 
 import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
 
 public class PackageInstallerService extends IPackageInstaller.Stub {
     private static final String TAG = "PackageInstaller";
@@ -54,8 +59,8 @@
     private final AppOpsManager mAppOps;
 
     private final File mStagingDir;
+    private final HandlerThread mInstallThread;
 
-    private final HandlerThread mInstallThread = new HandlerThread(TAG);
     private final Callback mCallback = new Callback();
 
     @GuardedBy("mSessions")
@@ -63,27 +68,50 @@
     @GuardedBy("mSessions")
     private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
 
+    private static final FilenameFilter sStageFilter = new FilenameFilter() {
+        @Override
+        public boolean accept(File dir, String name) {
+            return name.startsWith("vmdl") && name.endsWith(".tmp");
+        }
+    };
+
     public PackageInstallerService(Context context, PackageManagerService pm, File stagingDir) {
         mContext = context;
         mPm = pm;
         mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
 
         mStagingDir = stagingDir;
-        mStagingDir.mkdirs();
+
+        mInstallThread = new HandlerThread(TAG);
+        mInstallThread.start();
 
         synchronized (mSessions) {
             readSessionsLocked();
 
             // Clean up orphaned staging directories
-            final ArraySet<String> dirs = Sets.newArraySet(mStagingDir.list());
+            final ArraySet<File> stages = Sets.newArraySet(mStagingDir.listFiles(sStageFilter));
             for (int i = 0; i < mSessions.size(); i++) {
-                dirs.remove(Integer.toString(mSessions.keyAt(i)));
+                final PackageInstallerSession session = mSessions.valueAt(i);
+                stages.remove(session.sessionStageDir);
             }
-            for (String dirName : dirs) {
-                Slog.w(TAG, "Deleting orphan session " + dirName);
-                final File dir = new File(mStagingDir, dirName);
-                FileUtils.deleteContents(dir);
-                dir.delete();
+            for (File stage : stages) {
+                Slog.w(TAG, "Deleting orphan stage " + stage);
+                if (stage.isDirectory()) {
+                    FileUtils.deleteContents(stage);
+                }
+                stage.delete();
+            }
+        }
+    }
+
+    @Deprecated
+    public File allocateSessionDir() throws IOException {
+        synchronized (mSessions) {
+            try {
+                final int sessionId = allocateSessionIdLocked();
+                return prepareSessionStageDir(sessionId);
+            } catch (IllegalStateException e) {
+                throw new IOException(e);
             }
         }
     }
@@ -110,11 +138,10 @@
     }
 
     @Override
-    public int createSession(int userId, String installerPackageName,
-            PackageInstallerParams params) {
+    public int createSession(String installerPackageName, PackageInstallerParams params,
+            int userId) {
         final int callingUid = Binder.getCallingUid();
         mPm.enforceCrossUserPermission(callingUid, userId, false, TAG);
-        mAppOps.checkPackage(callingUid, installerPackageName);
 
         if (mPm.isUserRestricted(UserHandle.getUserId(callingUid),
                 UserManager.DISALLOW_INSTALL_APPS)) {
@@ -124,20 +151,21 @@
         if ((callingUid == Process.SHELL_UID) || (callingUid == 0)) {
             params.installFlags |= INSTALL_FROM_ADB;
         } else {
+            mAppOps.checkPackage(callingUid, installerPackageName);
+
             params.installFlags &= ~INSTALL_FROM_ADB;
             params.installFlags &= ~INSTALL_ALL_USERS;
             params.installFlags |= INSTALL_REPLACE_EXISTING;
         }
 
         synchronized (mSessions) {
-            final int sessionId = allocateSessionIdLocked();
             final long createdMillis = System.currentTimeMillis();
-            final File sessionDir = new File(mStagingDir, Integer.toString(sessionId));
-            sessionDir.mkdirs();
+            final int sessionId = allocateSessionIdLocked();
+            final File sessionStageDir = prepareSessionStageDir(sessionId);
 
             final PackageInstallerSession session = new PackageInstallerSession(mCallback, mPm,
                     sessionId, userId, installerPackageName, callingUid, params, createdMillis,
-                    sessionDir, mInstallThread.getLooper());
+                    sessionStageDir, mInstallThread.getLooper());
             mSessions.put(sessionId, session);
 
             writeSessionsAsync();
@@ -166,8 +194,30 @@
         return mNextSessionId++;
     }
 
+    private File prepareSessionStageDir(int sessionId) {
+        final File file = new File(mStagingDir, "vmdl" + sessionId + ".tmp");
+
+        if (file.exists()) {
+            throw new IllegalStateException();
+        }
+
+        try {
+            Os.mkdir(file.getAbsolutePath(), 0755);
+            Os.chmod(file.getAbsolutePath(), 0755);
+        } catch (ErrnoException e) {
+            // This purposefully throws if directory already exists
+            throw new IllegalStateException("Failed to prepare session dir", e);
+        }
+
+        if (!SELinux.restorecon(file)) {
+            throw new IllegalStateException("Failed to prepare session dir");
+        }
+
+        return file;
+    }
+
     @Override
-    public int[] getSessions(int userId, String installerPackageName) {
+    public int[] getSessions(String installerPackageName, int userId) {
         final int callingUid = Binder.getCallingUid();
         mPm.enforceCrossUserPermission(callingUid, userId, false, TAG);
         mAppOps.checkPackage(callingUid, installerPackageName);
@@ -187,13 +237,14 @@
     }
 
     @Override
-    public void uninstall(int userId, String basePackageName, IPackageDeleteObserver observer) {
-        mPm.deletePackageAsUser(basePackageName, observer, userId, 0);
+    public void uninstall(String basePackageName, int flags, IPackageDeleteObserver observer,
+            int userId) {
+        mPm.deletePackageAsUser(basePackageName, observer, userId, flags);
     }
 
     @Override
-    public void uninstallSplit(int userId, String basePackageName, String overlayName,
-            IPackageDeleteObserver observer) {
+    public void uninstallSplit(String basePackageName, String overlayName, int flags,
+            IPackageDeleteObserver observer, int userId) {
         // TODO: flesh out once PM has split support
         throw new UnsupportedOperationException();
     }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 8067bd9..3448803 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -20,6 +20,8 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
 import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
+import static android.system.OsConstants.O_CREAT;
+import static android.system.OsConstants.O_WRONLY;
 
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageInstallObserver2;
@@ -37,9 +39,11 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.system.ErrnoException;
+import android.system.Os;
 import android.system.OsConstants;
 import android.system.StructStat;
 import android.util.ArraySet;
@@ -58,6 +62,10 @@
 public class PackageInstallerSession extends IPackageInstallerSession.Stub {
     private static final String TAG = "PackageInstaller";
 
+    // TODO: enforce INSTALL_ALLOW_TEST
+    // TODO: enforce INSTALL_ALLOW_DOWNGRADE
+    // TODO: handle INSTALL_EXTERNAL, INSTALL_INTERNAL
+
     private final PackageInstallerService.Callback mCallback;
     private final PackageManagerService mPm;
     private final Handler mHandler;
@@ -69,7 +77,7 @@
     public final int installerUid;
     public final PackageInstallerParams params;
     public final long createdMillis;
-    public final File sessionDir;
+    public final File sessionStageDir;
 
     private static final int MSG_INSTALL = 0;
 
@@ -85,6 +93,7 @@
                     installLocked();
                 } catch (InstallFailedException e) {
                     Slog.e(TAG, "Install failed: " + e);
+                    destroy();
                     try {
                         mRemoteObserver.packageInstalled(mPackageName, null, e.error);
                     } catch (RemoteException ignored) {
@@ -114,7 +123,7 @@
 
     public PackageInstallerSession(PackageInstallerService.Callback callback,
             PackageManagerService pm, int sessionId, int userId, String installerPackageName,
-            int installerUid, PackageInstallerParams params, long createdMillis, File sessionDir,
+            int installerUid, PackageInstallerParams params, long createdMillis, File sessionStageDir,
             Looper looper) {
         mCallback = callback;
         mPm = pm;
@@ -126,7 +135,7 @@
         this.installerUid = installerUid;
         this.params = params;
         this.createdMillis = createdMillis;
-        this.sessionDir = sessionDir;
+        this.sessionStageDir = sessionStageDir;
 
         // Check against any explicitly provided signatures
         mSignatures = params.signatures;
@@ -138,6 +147,9 @@
                 == PackageManager.PERMISSION_GRANTED) {
             mPermissionsConfirmed = true;
         }
+        if (installerUid == Process.SHELL_UID || installerUid == 0) {
+            mPermissionsConfirmed = true;
+        }
     }
 
     @Override
@@ -168,10 +180,11 @@
             if (!FileUtils.isValidExtFilename(name)) {
                 throw new IllegalArgumentException("Invalid name: " + name);
             }
-            final File target = new File(sessionDir, name);
+            final File target = new File(sessionStageDir, name);
 
             final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(),
-                    OsConstants.O_CREAT | OsConstants.O_WRONLY, 00700);
+                    O_CREAT | O_WRONLY, 0644);
+            Os.chmod(target.getAbsolutePath(), 0644);
 
             // If caller specified a total length, allocate it for them. Free up
             // cache space to grow, if needed.
@@ -235,7 +248,7 @@
         if (!mPermissionsConfirmed) {
             // TODO: async confirm permissions with user
             // when they confirm, we'll kick off another install() pass
-            mPermissionsConfirmed = true;
+            throw new SecurityException("Caller must hold INSTALL permission");
         }
 
         // Inherit any packages and native libraries from existing install that
@@ -258,7 +271,7 @@
             }
         };
 
-        mPm.installStage(mPackageName, this.sessionDir, localObserver, params, installerPackageName,
+        mPm.installStage(mPackageName, this.sessionStageDir, localObserver, params, installerPackageName,
                 installerUid, new UserHandle(userId));
     }
 
@@ -271,9 +284,8 @@
     private void validateInstallLocked() throws InstallFailedException {
         mPackageName = null;
         mVersionCode = -1;
-        mSignatures = null;
 
-        final File[] files = sessionDir.listFiles();
+        final File[] files = sessionStageDir.listFiles();
         if (ArrayUtils.isEmpty(files)) {
             throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, "No packages staged");
         }
@@ -296,11 +308,11 @@
             }
 
             // Use first package to define unknown values
-            if (mPackageName != null) {
+            if (mPackageName == null) {
                 mPackageName = info.packageName;
                 mVersionCode = info.versionCode;
             }
-            if (mSignatures != null) {
+            if (mSignatures == null) {
                 mSignatures = info.signatures;
             }
 
@@ -310,9 +322,9 @@
             // Take this opportunity to enforce uniform naming
             final String name;
             if (info.splitName == null) {
-                name = info.packageName + ".apk";
+                name = "base.apk";
             } else {
-                name = info.packageName + "-" + info.splitName + ".apk";
+                name = "split_" + info.splitName + ".apk";
             }
             if (!FileUtils.isValidExtFilename(name)) {
                 throw new InstallFailedException(INSTALL_FAILED_INVALID_APK,
@@ -382,7 +394,7 @@
         final File existingDir = new File(app.getBaseCodePath());
 
         try {
-            linkTreeIgnoringExisting(existingDir, sessionDir);
+            linkTreeIgnoringExisting(existingDir, sessionStageDir);
         } catch (ErrnoException e) {
             throw new InstallFailedException(INSTALL_FAILED_INTERNAL_ERROR,
                     "Failed to splice into stage");
@@ -416,8 +428,8 @@
             synchronized (mLock) {
                 mInvalid = true;
             }
-            FileUtils.deleteContents(sessionDir);
-            sessionDir.delete();
+            FileUtils.deleteContents(sessionStageDir);
+            sessionStageDir.delete();
         } finally {
             mCallback.onSessionInvalid(this);
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0a201f9..fe9ce8f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -377,11 +377,6 @@
     // apps.
     final File mDrmAppPrivateInstallDir;
 
-    /** Directory where third-party apps are staged before install */
-    final File mAppStagingDir;
-
-    private final Random mTempFileRandom = new Random();
-
     // ----------------------------------------------------------------
 
     // Lock for state used when installing and doing other long running
@@ -1363,7 +1358,6 @@
             mAsecInternalPath = new File(dataDir, "app-asec").getPath();
             mUserAppDataDir = new File(dataDir, "user");
             mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
-            mAppStagingDir = new File(dataDir, "app-staging");
 
             sUserManager = new UserManagerService(context, this,
                     mInstallLock, mPackages);
@@ -1767,7 +1761,7 @@
         } // synchronized (mPackages)
         } // synchronized (mInstallLock)
 
-        mInstallerService = new PackageInstallerService(context, this, mAppStagingDir);
+        mInstallerService = new PackageInstallerService(context, this, mAppInstallDir);
 
         // Now after opening every single application zip, make sure they
         // are all flushed.  Not really needed, but keeps things nice and
@@ -4172,6 +4166,7 @@
             pp.collectCertificates(pkg, parseFlags);
             pp.collectManifestDigest(pkg);
         } catch (PackageParserException e) {
+            Slog.e(TAG, "Failed during collect: " + e);
             mLastScanError = e.error;
             return false;
         }
@@ -4200,6 +4195,7 @@
         try {
             pkg = pp.parsePackage(scanFile, parseFlags);
         } catch (PackageParserException e) {
+            Slog.e(TAG, "Failed during scan: " + e);
             mLastScanError = e.error;
             return null;
         }
@@ -7652,8 +7648,20 @@
         verificationParams.setInstallerUid(uid);
 
         final Message msg = mHandler.obtainMessage(INIT_COPY);
-        msg.obj = new InstallParams(originFile, observer, filteredFlags, installerPackageName,
-                verificationParams, user, packageAbiOverride);
+        msg.obj = new InstallParams(originFile, false, observer, filteredFlags,
+                installerPackageName, verificationParams, user, packageAbiOverride);
+        mHandler.sendMessage(msg);
+    }
+
+    void installStage(String packageName, File stageDir, IPackageInstallObserver2 observer,
+            PackageInstallerParams params, String installerPackageName, int installerUid,
+            UserHandle user) {
+        final VerificationParams verifParams = new VerificationParams(null, params.originatingUri,
+                params.referrerUri, installerUid, null);
+
+        final Message msg = mHandler.obtainMessage(INIT_COPY);
+        msg.obj = new InstallParams(stageDir, true, observer, params.installFlags,
+                installerPackageName, verifParams, user, params.abiOverride);
         mHandler.sendMessage(msg);
     }
 
@@ -7769,17 +7777,6 @@
         }
     }
 
-    void installStage(String packageName, File stageDir, IPackageInstallObserver2 observer2,
-            PackageInstallerParams params, String installerPackageName, int installerUid,
-            UserHandle user) {
-        Slog.e(TAG, "TODO: install stage!");
-        try {
-            observer2.packageInstalled(packageName, null,
-                    PackageManager.INSTALL_FAILED_INTERNAL_ERROR);
-        } catch (RemoteException ignored) {
-        }
-    }
-
     /**
      * @hide
      */
@@ -8365,10 +8362,10 @@
         final File originFile;
 
         /**
-         * Flag indicating that {@link #originFile} lives in a trusted location,
+         * Flag indicating that {@link #originFile} has already been staged,
          * meaning downstream users don't need to defensively copy the contents.
          */
-        boolean originTrusted;
+        boolean originStaged;
 
         final IPackageInstallObserver2 observer;
         int flags;
@@ -8379,12 +8376,12 @@
         final String packageAbiOverride;
         final String packageInstructionSetOverride;
 
-        InstallParams(File originFile, IPackageInstallObserver2 observer, int flags,
-                String installerPackageName, VerificationParams verificationParams, UserHandle user,
-                String packageAbiOverride) {
+        InstallParams(File originFile, boolean originStaged, IPackageInstallObserver2 observer,
+                int flags, String installerPackageName, VerificationParams verificationParams,
+                UserHandle user, String packageAbiOverride) {
             super(user);
             this.originFile = Preconditions.checkNotNull(originFile);
-            this.originTrusted = false;
+            this.originStaged = originStaged;
             this.observer = observer;
             this.flags = flags;
             this.installerPackageName = installerPackageName;
@@ -8912,8 +8909,8 @@
     static abstract class InstallArgs {
         /** @see InstallParams#originFile */
         final File originFile;
-        /** @see InstallParams#originTrusted */
-        final boolean originTrusted;
+        /** @see InstallParams#originStaged */
+        final boolean originStaged;
 
         // TODO: define inherit location
 
@@ -8926,11 +8923,11 @@
         final String instructionSet;
         final String abiOverride;
 
-        InstallArgs(File originFile, boolean originTrusted, IPackageInstallObserver2 observer,
+        InstallArgs(File originFile, boolean originStaged, IPackageInstallObserver2 observer,
                 int flags, String installerPackageName, ManifestDigest manifestDigest,
                 UserHandle user, String instructionSet, String abiOverride) {
             this.originFile = originFile;
-            this.originTrusted = originTrusted;
+            this.originStaged = originStaged;
             this.flags = flags;
             this.observer = observer;
             this.installerPackageName = installerPackageName;
@@ -9009,7 +9006,7 @@
 
         /** New install */
         FileInstallArgs(InstallParams params) {
-            super(params.originFile, params.originTrusted, params.observer, params.flags,
+            super(params.originFile, params.originStaged, params.observer, params.flags,
                     params.installerPackageName, params.getManifestDigest(), params.getUser(),
                     params.packageInstructionSetOverride, params.packageAbiOverride);
             if (isFwdLocked()) {
@@ -9028,7 +9025,7 @@
 
         /** New install from existing */
         FileInstallArgs(File originFile, String instructionSet) {
-            super(originFile, true, null, 0, null, null, null, instructionSet, null);
+            super(originFile, false, null, 0, null, null, null, instructionSet, null);
         }
 
         boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
@@ -9053,37 +9050,45 @@
         }
 
         int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
-            try {
-                final File tempDir = createTempPackageDir(mAppInstallDir);
-                codeFile = tempDir;
-                resourceFile = tempDir;
-            } catch (IOException e) {
-                Slog.w(TAG, "Failed to create copy file: " + e);
-                return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
-            }
+            int ret = PackageManager.INSTALL_SUCCEEDED;
 
-            final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
-                @Override
-                public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
-                    if (!FileUtils.isValidExtFilename(name)) {
-                        throw new IllegalArgumentException("Invalid filename: " + name);
-                    }
-                    try {
-                        final File file = new File(codeFile, name);
-                        final FileDescriptor fd = Os.open(file.getAbsolutePath(),
-                                O_RDWR | O_CREAT, 0644);
-                        Os.chmod(file.getAbsolutePath(), 0644);
-                        return new ParcelFileDescriptor(fd);
-                    } catch (ErrnoException e) {
-                        throw new RemoteException("Failed to open: " + e.getMessage());
-                    }
+            if (originStaged) {
+                Slog.d(TAG, originFile + " already staged; skipping copy");
+                codeFile = originFile;
+                resourceFile = originFile;
+            } else {
+                try {
+                    final File tempDir = mInstallerService.allocateSessionDir();
+                    codeFile = tempDir;
+                    resourceFile = tempDir;
+                } catch (IOException e) {
+                    Slog.w(TAG, "Failed to create copy file: " + e);
+                    return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
                 }
-            };
 
-            int ret = imcs.copyPackage(originFile.getAbsolutePath(), target);
-            if (ret != PackageManager.INSTALL_SUCCEEDED) {
-                Slog.e(TAG, "Failed to copy package");
-                return ret;
+                final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
+                    @Override
+                    public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
+                        if (!FileUtils.isValidExtFilename(name)) {
+                            throw new IllegalArgumentException("Invalid filename: " + name);
+                        }
+                        try {
+                            final File file = new File(codeFile, name);
+                            final FileDescriptor fd = Os.open(file.getAbsolutePath(),
+                                    O_RDWR | O_CREAT, 0644);
+                            Os.chmod(file.getAbsolutePath(), 0644);
+                            return new ParcelFileDescriptor(fd);
+                        } catch (ErrnoException e) {
+                            throw new RemoteException("Failed to open: " + e.getMessage());
+                        }
+                    }
+                };
+
+                ret = imcs.copyPackage(originFile.getAbsolutePath(), target);
+                if (ret != PackageManager.INSTALL_SUCCEEDED) {
+                    Slog.e(TAG, "Failed to copy package");
+                    return ret;
+                }
             }
 
             String[] abiList = (abiOverride != null) ?
@@ -9288,7 +9293,7 @@
 
         /** New install */
         AsecInstallArgs(InstallParams params) {
-            super(params.originFile, params.originTrusted, params.observer, params.flags,
+            super(params.originFile, params.originStaged, params.observer, params.flags,
                     params.installerPackageName, params.getManifestDigest(), params.getUser(),
                     params.packageInstructionSetOverride, params.packageAbiOverride);
         }
@@ -9318,7 +9323,7 @@
         /** New install from existing */
         AsecInstallArgs(File originPackageFile, String cid, String instructionSet,
                 boolean isExternal, boolean isForwardLocked) {
-            super(originPackageFile, true, null, (isExternal ? INSTALL_EXTERNAL : 0)
+            super(originPackageFile, false, null, (isExternal ? INSTALL_EXTERNAL : 0)
                     | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
                     instructionSet, null);
             this.cid = cid;
@@ -10082,6 +10087,7 @@
         try {
             pkg = pp.parsePackage(tmpPackageFile, parseFlags);
         } catch (PackageParserException e) {
+            Slog.e(TAG, "Failed during install: " + e);
             res.returnCode = e.error;
             return;
         }
@@ -10098,6 +10104,7 @@
             pp.collectCertificates(pkg, parseFlags);
             pp.collectManifestDigest(pkg);
         } catch (PackageParserException e) {
+            Slog.e(TAG, "Failed during install: " + e);
             res.returnCode = e.error;
             return;
         }
@@ -10276,65 +10283,11 @@
                 return name.startsWith("vmdl") && name.endsWith(".tmp");
             }
         };
-        deleteTempPackageFilesInDirectory(mAppInstallDir, filter);
-        deleteTempPackageFilesInDirectory(mDrmAppPrivateInstallDir, filter);
-    }
-
-    private static final void deleteTempPackageFilesInDirectory(File directory,
-            FilenameFilter filter) {
-        final File[] files = directory.listFiles(filter);
-        if (!ArrayUtils.isEmpty(files)) {
-            for (File file : files) {
-                if (file.isDirectory()) {
-                    FileUtils.deleteContents(file);
-                    file.delete();
-                } else if (file.isFile()) {
-                    file.delete();
-                }
-            }
+        for (File file : mDrmAppPrivateInstallDir.listFiles(filter)) {
+            file.delete();
         }
     }
 
-    private File createTempPackageDir(File installDir) throws IOException {
-        int n = 0;
-        while (n++ < 32) {
-            final File file = new File(installDir, "vmdl" + mTempFileRandom.nextInt() + ".tmp");
-            try {
-                Os.mkdir(file.getAbsolutePath(), 0755);
-                Os.chmod(file.getAbsolutePath(), 0755);
-                if (!SELinux.restorecon(file)) {
-                    throw new IOException("Failed to restorecon");
-                }
-                return file;
-            } catch (ErrnoException e) {
-                if (e.errno == EEXIST) continue;
-                throw e.rethrowAsIOException();
-            }
-        }
-        throw new IOException("Failed to create temp directory");
-    }
-
-    private File createTempPackageFile(File installDir) throws IOException {
-        int n = 0;
-        while (n++ < 32) {
-            final File file = new File(installDir, "vmdl" + mTempFileRandom.nextInt() + ".tmp");
-            try {
-                final FileDescriptor fd = Os.open(file.getAbsolutePath(),
-                        O_RDWR | O_CREAT | O_EXCL, 0644);
-                IoUtils.closeQuietly(fd);
-                Os.chmod(file.getAbsolutePath(), 0644);
-                if (!SELinux.restorecon(file)) {
-                    throw new IOException("Failed to restorecon");
-                }
-                return file;
-            } catch (ErrnoException e) {
-                if (e.errno == EEXIST) continue;
-                throw e.rethrowAsIOException();
-            }
-        }
-        throw new IOException("Failed to create temp file");
-    }
-
     @Override
     public void deletePackageAsUser(final String packageName,
                                     final IPackageDeleteObserver observer,
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index db44d3a..5599760 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -26,6 +26,7 @@
     $(LOCAL_REL_DIR)/com_android_server_UsbDeviceManager.cpp \
     $(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \
     $(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \
+    $(LOCAL_REL_DIR)/com_android_server_PersistentDataBlockService.cpp \
     $(LOCAL_REL_DIR)/onload.cpp
 
 include external/stlport/libstlport.mk
diff --git a/services/core/jni/com_android_server_PersistentDataBlockService.cpp b/services/core/jni/com_android_server_PersistentDataBlockService.cpp
new file mode 100644
index 0000000..673a6e2
--- /dev/null
+++ b/services/core/jni/com_android_server_PersistentDataBlockService.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android_runtime/AndroidRuntime.h>
+#include <JNIHelp.h>
+#include <jni.h>
+
+#include <utils/misc.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+
+#include <inttypes.h>
+#include <fcntl.h>
+
+namespace android {
+
+    uint64_t get_block_device_size(int fd)
+    {
+        uint64_t size = 0;
+        int ret;
+
+        ret = ioctl(fd, BLKGETSIZE64, &size);
+
+        if (ret)
+            return 0;
+
+        return size;
+    }
+
+    static jlong com_android_server_PeristentDataBlockService_getBlockDeviceSize(JNIEnv *env, jclass, jstring jpath) {
+        const char *path = env->GetStringUTFChars(jpath, 0);
+        int fd = open(path, O_RDONLY);
+
+        if (fd < 0)
+            return 0;
+
+        return get_block_device_size(fd);
+    }
+
+    static JNINativeMethod sMethods[] = {
+         /* name, signature, funcPtr */
+        {"getBlockDeviceSize", "(Ljava/lang/String;)J", (void*)com_android_server_PeristentDataBlockService_getBlockDeviceSize},
+    };
+
+    int register_android_server_PersistentDataBlockService(JNIEnv* env)
+    {
+        return jniRegisterNativeMethods(env, "com/android/server/PersistentDataBlockService",
+                                        sMethods, NELEM(sMethods));
+    }
+
+} /* namespace android */
\ No newline at end of file
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index a302104..bf7501e 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -41,6 +41,7 @@
 int register_android_server_hdmi_HdmiCecController(JNIEnv* env);
 int register_android_server_hdmi_HdmiMhlController(JNIEnv* env);
 int register_android_server_tv_TvInputHal(JNIEnv* env);
+int register_android_server_PersistentDataBlockService(JNIEnv* env);
 };
 
 using namespace android;
@@ -77,6 +78,7 @@
     register_android_server_hdmi_HdmiCecController(env);
     register_android_server_hdmi_HdmiMhlController(env);
     register_android_server_tv_TvInputHal(env);
+    register_android_server_PersistentDataBlockService(env);
 
     return JNI_VERSION_1_4;
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b63bbe7..e0ebd54 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -135,6 +135,7 @@
             "com.android.server.ethernet.EthernetService";
     private static final String JOB_SCHEDULER_SERVICE_CLASS =
             "com.android.server.job.JobSchedulerService";
+    private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
 
     private final int mFactoryTestMode;
     private Timer mProfilerSnapshotTimer;
@@ -574,6 +575,10 @@
                     reportWtf("starting LockSettingsService service", e);
                 }
 
+                if (!SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP).equals("")) {
+                    mSystemServiceManager.startService(PersistentDataBlockService.class);
+                }
+
                 // Always start the Device Policy Manager, so that the API is compatible with
                 // API8.
                 mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 3d93bbe..963c796 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -900,8 +900,8 @@
     return 0;
 }
 
-status_t generateAndroidManifestForSplit(const String16& package, const sp<ApkSplit>& split,
-        sp<AaptFile>& outFile) {
+status_t generateAndroidManifestForSplit(Bundle* bundle, const sp<AaptAssets>& assets,
+        const sp<ApkSplit>& split, sp<AaptFile>& outFile, ResourceTable* table) {
     const String8 filename("AndroidManifest.xml");
     const String16 androidPrefix("android");
     const String16 androidNSUri("http://schemas.android.com/apk/res/android");
@@ -910,8 +910,19 @@
     // Build the <manifest> tag
     sp<XMLNode> manifest = XMLNode::newElement(filename, String16(), String16("manifest"));
 
-    // Add the 'package' attribute which is set to the original package name.
-    manifest->addAttribute(String16(), String16("package"), package);
+    // Add the 'package' attribute which is set to the package name.
+    const char* packageName = assets->getPackage();
+    const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride();
+    if (manifestPackageNameOverride != NULL) {
+        packageName = manifestPackageNameOverride;
+    }
+    manifest->addAttribute(String16(), String16("package"), String16(packageName));
+
+    // Add the 'versionCode' attribute which is set to the original version code.
+    if (!addTagAttribute(manifest, RESOURCES_ANDROID_NAMESPACE, "versionCode",
+            bundle->getVersionCode(), true, true)) {
+        return UNKNOWN_ERROR;
+    }
 
     // Add the 'split' attribute which describes the configurations included.
     String8 splitName("config_");
@@ -923,8 +934,8 @@
     manifest->addChild(app);
     root->addChild(manifest);
 
-    status_t err = root->flatten(outFile, true, true);
-    if (err != NO_ERROR) {
+    int err = compileXmlFile(assets, root, outFile, table);
+    if (err < NO_ERROR) {
         return err;
     }
     outFile->setCompressionMethod(ZipEntry::kCompressDeflated);
@@ -1371,8 +1382,8 @@
             } else {
                 sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"),
                         AaptGroupEntry(), String8());
-                err = generateAndroidManifestForSplit(String16(assets->getPackage()), split,
-                        generatedManifest);
+                err = generateAndroidManifestForSplit(bundle, assets, split,
+                        generatedManifest, &table);
                 if (err != NO_ERROR) {
                     fprintf(stderr, "Failed to generate AndroidManifest.xml for split '%s'\n",
                             split->getPrintableName().string());