Merge "Fix minor blur doc bug"
diff --git a/Android.mk b/Android.mk
index 2a94f3a..d9e4455 100644
--- a/Android.mk
+++ b/Android.mk
@@ -199,6 +199,7 @@
 	core/java/android/os/INetworkActivityListener.aidl \
 	core/java/android/os/INetworkManagementService.aidl \
 	core/java/android/os/IPermissionController.aidl \
+	core/java/android/os/IProcessInfoService.aidl \
 	core/java/android/os/IPowerManager.aidl \
 	core/java/android/os/IRemoteCallback.aidl \
 	core/java/android/os/ISchedulingPolicyService.aidl \
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index c86fd53..c5af992 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -7,6 +7,12 @@
 
 #define LOG_TAG "appproc"
 
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
 #include <utils/Log.h>
@@ -17,11 +23,6 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <private/android_filesystem_config.h>  // for AID_SYSTEM
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/prctl.h>
-
 namespace android {
 
 static void app_usage()
diff --git a/cmds/idmap/scan.cpp b/cmds/idmap/scan.cpp
index 197e36b..84158d3 100644
--- a/cmds/idmap/scan.cpp
+++ b/cmds/idmap/scan.cpp
@@ -1,3 +1,6 @@
+#include <dirent.h>
+#include <sys/stat.h>
+
 #include "idmap.h"
 
 #include <UniquePtr.h>
@@ -9,8 +12,6 @@
 #include <utils/String16.h>
 #include <utils/String8.h>
 
-#include <dirent.h>
-
 #define NO_OVERLAY_TAG (-1000)
 
 using namespace android;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 7a636db..c6ffef6 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -256,6 +256,9 @@
     /** @hide User operation call: given user id is the current user, can't be stopped. */
     public static final int USER_OP_IS_CURRENT = -2;
 
+    /** @hide Process does not exist. */
+    public static final int PROCESS_STATE_NONEXISTENT = -1;
+
     /** @hide Process is a persistent system process. */
     public static final int PROCESS_STATE_PERSISTENT = 0;
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 6ea9f7e..beb244b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -75,6 +75,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.security.NetworkSecurityPolicy;
 import android.util.AndroidRuntimeException;
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
@@ -256,18 +257,21 @@
         }
     }
 
+    static final class AcquiringProviderRecord {
+        IActivityManager.ContentProviderHolder holder;
+        boolean acquiring = true;
+        int requests = 1;
+    }
+
     // The lock of mProviderMap protects the following variables.
-    final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
-        = new ArrayMap<ProviderKey, ProviderClientRecord>();
-    final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap
-        = new ArrayMap<IBinder, ProviderRefCount>();
-    final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders
-        = new ArrayMap<IBinder, ProviderClientRecord>();
-    final ArrayMap<ComponentName, ProviderClientRecord> mLocalProvidersByName
-            = new ArrayMap<ComponentName, ProviderClientRecord>();
+    final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap = new ArrayMap<>();
+    final ArrayMap<ProviderKey, AcquiringProviderRecord> mAcquiringProviderMap = new ArrayMap<>();
+    final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap = new ArrayMap<>();
+    final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders = new ArrayMap<>();
+    final ArrayMap<ComponentName, ProviderClientRecord> mLocalProvidersByName = new ArrayMap<>();
 
     final ArrayMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
-        = new ArrayMap<Activity, ArrayList<OnActivityPausedListener>>();
+            = new ArrayMap<>();
 
     final GcIdler mGcIdler = new GcIdler();
     boolean mGcIdlerScheduled = false;
@@ -344,7 +348,7 @@
         }
     }
 
-    final class ProviderClientRecord {
+    static final class ProviderClientRecord {
         final String[] mNames;
         final IContentProvider mProvider;
         final ContentProvider mLocalProvider;
@@ -4435,6 +4439,9 @@
             StrictMode.enableDeathOnNetwork();
         }
 
+        NetworkSecurityPolicy.getInstance().setCleartextTrafficPermitted(
+                (data.appInfo.flags & ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC) != 0);
+
         if (data.debugMode != IApplicationThread.DEBUG_OFF) {
             // XXX should have option to change the port.
             Debug.changeDebugPort(8100);
@@ -4644,22 +4651,57 @@
 
     public final IContentProvider acquireProvider(
             Context c, String auth, int userId, boolean stable) {
-        final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
+        final ProviderKey key = new ProviderKey(auth, userId);
+        final IContentProvider provider = acquireExistingProvider(c, key, stable);
         if (provider != null) {
             return provider;
         }
+        AcquiringProviderRecord r;
+        boolean first = false;
+        synchronized (mAcquiringProviderMap) {
+            r = mAcquiringProviderMap.get(key);
+            if (r == null) {
+                r = new AcquiringProviderRecord();
+                mAcquiringProviderMap.put(key, r);
+                first = true;
+            } else {
+                r.requests++;
+            }
+        }
 
-        // There is a possible race here.  Another thread may try to acquire
-        // the same provider at the same time.  When this happens, we want to ensure
-        // that the first one wins.
-        // Note that we cannot hold the lock while acquiring and installing the
-        // provider since it might take a long time to run and it could also potentially
-        // be re-entrant in the case where the provider is in the same process.
         IActivityManager.ContentProviderHolder holder = null;
-        try {
-            holder = ActivityManagerNative.getDefault().getContentProvider(
-                    getApplicationThread(), auth, userId, stable);
-        } catch (RemoteException ex) {
+        if (first) {
+            // Multiple threads may try to acquire the same provider at the same time.
+            // When this happens, we only let the first one really gets provider.
+            // Other threads just wait for its result.
+            // Note that we cannot hold the lock while acquiring and installing the
+            // provider since it might take a long time to run and it could also potentially
+            // be re-entrant in the case where the provider is in the same process.
+            try {
+                holder = ActivityManagerNative.getDefault().getContentProvider(
+                        getApplicationThread(), auth, userId, stable);
+            } catch (RemoteException ex) {
+            }
+            synchronized (r) {
+                r.holder = holder;
+                r.acquiring = false;
+                r.notifyAll();
+            }
+        } else {
+            synchronized (r) {
+                while (r.acquiring) {
+                    try {
+                        r.wait();
+                    } catch (InterruptedException e) {
+                    }
+                }
+                holder = r.holder;
+            }
+        }
+        synchronized (mAcquiringProviderMap) {
+            if (--r.requests == 0) {
+                mAcquiringProviderMap.remove(key);
+            }
         }
         if (holder == null) {
             Slog.e(TAG, "Failed to find provider info for " + auth);
@@ -4743,8 +4785,12 @@
 
     public final IContentProvider acquireExistingProvider(
             Context c, String auth, int userId, boolean stable) {
+        return acquireExistingProvider(c, new ProviderKey(auth, userId), stable);
+    }
+
+    final IContentProvider acquireExistingProvider(
+            Context c, ProviderKey key, boolean stable) {
         synchronized (mProviderMap) {
-            final ProviderKey key = new ProviderKey(auth, userId);
             final ProviderClientRecord pr = mProviderMap.get(key);
             if (pr == null) {
                 return null;
@@ -4755,7 +4801,7 @@
             if (!jBinder.isBinderAlive()) {
                 // The hosting process of the provider has died; we can't
                 // use this one.
-                Log.i(TAG, "Acquiring provider " + auth + " for user " + userId
+                Log.i(TAG, "Acquiring provider " + key.authority + " for user " +  key.userId
                         + ": existing object's process dead");
                 handleUnstableProviderDiedLocked(jBinder, true);
                 return null;
@@ -5077,18 +5123,12 @@
                     if (DEBUG_PROVIDER) {
                         Slog.v(TAG, "installProvider: lost the race, updating ref count");
                     }
-                    // We need to transfer our new reference to the existing
-                    // ref count, releasing the old one...  but only if
-                    // release is needed (that is, it is not running in the
-                    // system process).
+                    // The provider has already been installed, so we need
+                    // to increase reference count to the existing one, but
+                    // only if release is needed (that is, it is not running
+                    // in the system process or local to the process).
                     if (!noReleaseNeeded) {
                         incProviderRefLocked(prc, stable);
-                        try {
-                            ActivityManagerNative.getDefault().removeContentProvider(
-                                    holder.connection, stable);
-                        } catch (RemoteException e) {
-                            //do nothing content provider object is dead any way
-                        }
                     }
                 } else {
                     ProviderClientRecord client = installProviderAuthoritiesLocked(
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index e1a2aa9..b3c558b 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -334,6 +334,18 @@
     public static final int FLAG_FULL_BACKUP_ONLY = 1<<26;
 
     /**
+     * Value for {@link #flags}: {@code true} if the application may use cleartext network traffic
+     * (e.g., HTTP rather than HTTPS; WebSockets rather than WebSockets Secure; XMPP, IMAP, STMP
+     * without STARTTLS or TLS). If {@code false}, the app declares that it does not intend to use
+     * cleartext network traffic, in which case platform components (e.g., HTTP stacks,
+     * {@code WebView}, {@code MediaPlayer}) will refuse app's requests to use cleartext traffic.
+     * Third-party libraries are encouraged to honor this flag as well.
+     *
+     * @hide
+     */
+    public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 1<<27;
+
+    /**
      * Value for {@link #flags}: true if code from this application will need to be
      * loaded into other applications' processes. On devices that support multiple
      * instruction sets, this implies the code might be loaded into a process that's
@@ -888,6 +900,20 @@
     /**
      * @hide
      */
+    public boolean isSystemApp() {
+        return (flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+    }
+
+    /**
+     * @hide
+     */
+    public boolean isUpdatedSystemApp() {
+        return (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
+    }
+
+    /**
+     * @hide
+     */
     @Override protected ApplicationInfo getApplicationInfo() {
         return this;
     }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4d9445d..5320929 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2550,6 +2550,12 @@
         }
 
         if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_usesCleartextTraffic,
+                true)) {
+            ai.flags |= ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC;
+        }
+
+        if (sa.getBoolean(
                 com.android.internal.R.styleable.AndroidManifestApplication_supportsRtl,
                 false /* default is no RTL support*/)) {
             ai.flags |= ApplicationInfo.FLAG_SUPPORTS_RTL;
@@ -4438,6 +4444,20 @@
             return applicationInfo.isForwardLocked();
         }
 
+        /**
+         * @hide
+         */
+        public boolean isSystemApp() {
+            return applicationInfo.isSystemApp();
+        }
+
+        /**
+         * @hide
+         */
+        public boolean isUpdatedSystemApp() {
+            return applicationInfo.isUpdatedSystemApp();
+        }
+
         public String toString() {
             return "Package{"
                 + Integer.toHexString(System.identityHashCode(this))
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 2099c3f..fb2f445 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -384,6 +384,11 @@
                     }
                 }
                 return builder.toString();
+            } else if (scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https")
+                    || scheme.equalsIgnoreCase("ftp")) {
+                ssp = "//" + ((getHost() != null) ? getHost() : "")
+                        + ((getPort() != -1) ? (":" + getPort()) : "")
+                        + "/...";
             }
         }
         // Not a sensitive scheme, but let's still be conservative about
diff --git a/core/java/android/os/IProcessInfoService.aidl b/core/java/android/os/IProcessInfoService.aidl
new file mode 100644
index 0000000..c98daa2
--- /dev/null
+++ b/core/java/android/os/IProcessInfoService.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2015 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.os;
+
+/** {@hide} */
+interface IProcessInfoService
+{
+    /**
+     * For each PID in the given input array, write the current process state
+     * for that process into the output array, or ActivityManager.PROCESS_STATE_NONEXISTENT
+     * to indicate that no process with the given PID exists.
+     */
+    void getProcessStatesFromPids(in int[] pids, out int[] states);
+}
+
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 0224c73..d989cd1 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -1426,7 +1426,7 @@
     protected boolean persistString(String value) {
         if (shouldPersist()) {
             // Shouldn't store null
-            if (value == getPersistedString(null)) {
+            if (TextUtils.equals(value, getPersistedString(null))) {
                 // It's already there, so the same as persisting
                 return true;
             }
diff --git a/core/java/android/security/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl
index ac6bbb7..579cdbe 100644
--- a/core/java/android/security/IKeystoreService.aidl
+++ b/core/java/android/security/IKeystoreService.aidl
@@ -19,6 +19,7 @@
 import android.security.keymaster.ExportResult;
 import android.security.keymaster.KeyCharacteristics;
 import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterBlob;
 import android.security.keymaster.OperationResult;
 import android.security.KeystoreArguments;
 
@@ -59,16 +60,19 @@
 
     // Keymaster 0.4 methods
     int addRngEntropy(in byte[] data);
-    int generateKey(String alias, in KeymasterArguments arguments, int uid, int flags,
+    int generateKey(String alias, in KeymasterArguments arguments, in byte[] entropy, int uid,
+        int flags, out KeyCharacteristics characteristics);
+    int getKeyCharacteristics(String alias, in KeymasterBlob clientId, in KeymasterBlob appId,
         out KeyCharacteristics characteristics);
-    int getKeyCharacteristics(String alias, in byte[] clientId,
-        in byte[] appId, out KeyCharacteristics characteristics);
     int importKey(String alias, in KeymasterArguments arguments, int format,
         in byte[] keyData, int uid, int flags, out KeyCharacteristics characteristics);
-    ExportResult exportKey(String alias, int format, in byte[] clientId, in byte[] appId);
+    ExportResult exportKey(String alias, int format, in KeymasterBlob clientId,
+        in KeymasterBlob appId);
     OperationResult begin(IBinder appToken, String alias, int purpose, boolean pruneable,
-        in KeymasterArguments params, out KeymasterArguments operationParams);
+        in KeymasterArguments params, in byte[] entropy, out KeymasterArguments operationParams);
     OperationResult update(IBinder token, in KeymasterArguments params, in byte[] input);
     OperationResult finish(IBinder token, in KeymasterArguments params, in byte[] signature);
     int abort(IBinder handle);
+    boolean isOperationAuthorized(IBinder token);
+    int addAuthToken(in byte[] authToken);
 }
diff --git a/core/java/android/security/NetworkSecurityPolicy.java b/core/java/android/security/NetworkSecurityPolicy.java
new file mode 100644
index 0000000..0626bbc
--- /dev/null
+++ b/core/java/android/security/NetworkSecurityPolicy.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright (c) 2015, 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.security;
+
+/**
+ * Network security policy.
+ *
+ * <p>Network stacks/components should honor this policy to make it possible to centrally control
+ * the relevant aspects of network security behavior.
+ *
+ * <p>The policy currently consists of a single flag: whether cleartext network traffic is
+ * permitted. See {@link #isCleartextTrafficPermitted()}.
+ *
+ * @hide
+ */
+public class NetworkSecurityPolicy {
+
+    private static final NetworkSecurityPolicy INSTANCE = new NetworkSecurityPolicy();
+
+    private NetworkSecurityPolicy() {}
+
+    /**
+     * Gets the policy for this process.
+     *
+     * <p>It's fine to cache this reference. Any changes to the policy will be immediately visible
+     * through the reference.
+     */
+    public static NetworkSecurityPolicy getInstance() {
+        return INSTANCE;
+    }
+
+    /**
+     * Returns whether cleartext network traffic (e.g. HTTP, FTP, WebSockets, XMPP, IMAP, SMTP --
+     * without TLS or STARTTLS) is permitted for this process.
+     *
+     * <p>When cleartext network traffic is not permitted, the platform's components (e.g. HTTP and
+     * FTP stacks, {@code WebView}, {@code MediaPlayer}) will refuse this process's requests to use
+     * cleartext traffic. Third-party libraries are strongly encouraged to honor this setting as
+     * well.
+     *
+     * <p>This flag is honored on a best effort basis because it's impossible to prevent all
+     * cleartext traffic from Android applications given the level of access provided to them. For
+     * example, there's no expectation that the {@link java.net.Socket} API will honor this flag
+     * because it cannot determine whether its traffic is in cleartext. However, most network
+     * traffic from applications is handled by higher-level network stacks/components which can
+     * honor this aspect of the policy.
+     */
+    public boolean isCleartextTrafficPermitted() {
+        return libcore.net.NetworkSecurityPolicy.isCleartextTrafficPermitted();
+    }
+
+    /**
+     * Sets whether cleartext network traffic is permitted for this process.
+     *
+     * <p>This method is used by the platform early on in the application's initialization to set
+     * the policy.
+     *
+     * @hide
+     */
+    public void setCleartextTrafficPermitted(boolean permitted) {
+        libcore.net.NetworkSecurityPolicy.setCleartextTrafficPermitted(permitted);
+    }
+}
diff --git a/core/java/android/security/keymaster/KeymasterBlob.aidl b/core/java/android/security/keymaster/KeymasterBlob.aidl
new file mode 100644
index 0000000..8f70f7c
--- /dev/null
+++ b/core/java/android/security/keymaster/KeymasterBlob.aidl
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2015, 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.security.keymaster;
+
+/* @hide */
+parcelable KeymasterBlob;
diff --git a/core/java/android/security/keymaster/KeymasterBlob.java b/core/java/android/security/keymaster/KeymasterBlob.java
new file mode 100644
index 0000000..cb95604
--- /dev/null
+++ b/core/java/android/security/keymaster/KeymasterBlob.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2015, 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.security.keymaster;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+public class KeymasterBlob implements Parcelable {
+    public byte[] blob;
+
+    public KeymasterBlob(byte[] blob) {
+        this.blob = blob;
+    }
+    public static final Parcelable.Creator<KeymasterBlob> CREATOR = new
+            Parcelable.Creator<KeymasterBlob>() {
+                public KeymasterBlob createFromParcel(Parcel in) {
+                    return new KeymasterBlob(in);
+                }
+
+                public KeymasterBlob[] newArray(int length) {
+                    return new KeymasterBlob[length];
+                }
+            };
+
+    protected KeymasterBlob(Parcel in) {
+        blob = in.createByteArray();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeByteArray(blob);
+    }
+}
diff --git a/core/java/android/security/keymaster/KeymasterBlobArgument.java b/core/java/android/security/keymaster/KeymasterBlobArgument.java
index 27f1153..a9085c4 100644
--- a/core/java/android/security/keymaster/KeymasterBlobArgument.java
+++ b/core/java/android/security/keymaster/KeymasterBlobArgument.java
@@ -27,6 +27,13 @@
 
     public KeymasterBlobArgument(int tag, byte[] blob) {
         super(tag);
+        switch (KeymasterDefs.getTagType(tag)) {
+            case KeymasterDefs.KM_BIGNUM:
+            case KeymasterDefs.KM_BYTES:
+                break; // OK.
+            default:
+                throw new IllegalArgumentException("Bad blob tag " + tag);
+        }
         this.blob = blob;
     }
 
diff --git a/core/java/android/security/keymaster/KeymasterBooleanArgument.java b/core/java/android/security/keymaster/KeymasterBooleanArgument.java
index 8e17db4..cc04bb6 100644
--- a/core/java/android/security/keymaster/KeymasterBooleanArgument.java
+++ b/core/java/android/security/keymaster/KeymasterBooleanArgument.java
@@ -29,6 +29,12 @@
 
     public KeymasterBooleanArgument(int tag) {
         super(tag);
+        switch (KeymasterDefs.getTagType(tag)) {
+            case KeymasterDefs.KM_BOOL:
+                break; // OK.
+            default:
+                throw new IllegalArgumentException("Bad bool tag " + tag);
+        }
     }
 
     public KeymasterBooleanArgument(int tag, Parcel in) {
diff --git a/core/java/android/security/keymaster/KeymasterDateArgument.java b/core/java/android/security/keymaster/KeymasterDateArgument.java
index e8f4055..47db6ea 100644
--- a/core/java/android/security/keymaster/KeymasterDateArgument.java
+++ b/core/java/android/security/keymaster/KeymasterDateArgument.java
@@ -29,6 +29,12 @@
 
     public KeymasterDateArgument(int tag, Date date) {
         super(tag);
+        switch (KeymasterDefs.getTagType(tag)) {
+            case KeymasterDefs.KM_DATE:
+                break; // OK.
+            default:
+                throw new IllegalArgumentException("Bad date tag " + tag);
+        }
         this.date = date;
     }
 
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 88cad79..e94a312 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -16,6 +16,9 @@
 
 package android.security.keymaster;
 
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * Class tracking all the keymaster enum values needed for the binder API to keystore.
  * This must be kept in sync with hardware/libhardware/include/hardware/keymaster_defs.h
@@ -37,6 +40,7 @@
     public static final int KM_BOOL = 7 << 28;
     public static final int KM_BIGNUM = 8 << 28;
     public static final int KM_BYTES = 9 << 28;
+    public static final int KM_LONG_REP = 10 << 28;
 
     // Tag values.
     public static final int KM_TAG_INVALID = KM_INVALID | 0;
@@ -66,9 +70,10 @@
 
     public static final int KM_TAG_ALL_USERS = KM_BOOL | 500;
     public static final int KM_TAG_USER_ID = KM_INT | 501;
-    public static final int KM_TAG_NO_AUTH_REQUIRED = KM_BOOL | 502;
-    public static final int KM_TAG_USER_AUTH_ID = KM_INT_REP | 503;
-    public static final int KM_TAG_AUTH_TIMEOUT = KM_INT | 504;
+    public static final int KM_TAG_USER_SECURE_ID = KM_LONG_REP | 502;
+    public static final int KM_TAG_NO_AUTH_REQUIRED = KM_BOOL | 503;
+    public static final int KM_TAG_USER_AUTH_TYPE = KM_ENUM | 504;
+    public static final int KM_TAG_AUTH_TIMEOUT = KM_INT | 505;
 
     public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600;
     public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
@@ -82,6 +87,7 @@
     public static final int KM_TAG_ASSOCIATED_DATA = KM_BYTES | 1000;
     public static final int KM_TAG_NONCE = KM_BYTES | 1001;
     public static final int KM_TAG_CHUNK_LENGTH = KM_INT | 1002;
+    public static final int KM_TAG_AUTH_TOKEN = KM_BYTES | 1003;
 
     // Algorithm values.
     public static final int KM_ALGORITHM_RSA = 1;
@@ -175,7 +181,7 @@
     public static final int KM_ERROR_UNSUPPORTED_KEY_SIZE = -6;
     public static final int KM_ERROR_UNSUPPORTED_BLOCK_MODE = -7;
     public static final int KM_ERROR_INCOMPATIBLE_BLOCK_MODE = -8;
-    public static final int KM_ERROR_UNSUPPORTED_TAG_LENGTH = -9;
+    public static final int KM_ERROR_UNSUPPORTED_MAC_LENGTH = -9;
     public static final int KM_ERROR_UNSUPPORTED_PADDING_MODE = -10;
     public static final int KM_ERROR_INCOMPATIBLE_PADDING_MODE = -11;
     public static final int KM_ERROR_UNSUPPORTED_DIGEST = -12;
@@ -221,7 +227,54 @@
     public static final int KM_ERROR_VERSION_MISMATCH = -101;
     public static final int KM_ERROR_UNKNOWN_ERROR = -1000;
 
+    public static final Map<Integer, String> sErrorCodeToString = new HashMap<Integer, String>();
+    static {
+        sErrorCodeToString.put(KM_ERROR_OK, "OK");
+        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_PURPOSE, "Unsupported purpose");
+        sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_PURPOSE, "Incompatible purpose");
+        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_ALGORITHM, "Unsupported algorithm");
+        sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_ALGORITHM, "Incompatible algorithm");
+        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_KEY_SIZE, "Unsupported key size");
+        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_BLOCK_MODE, "Unsupported block mode");
+        sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_BLOCK_MODE, "Incompatible block mode");
+        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_MAC_LENGTH,
+                "Unsupported MAC or authentication tag length");
+        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_PADDING_MODE, "Unsupported padding mode");
+        sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_PADDING_MODE, "Incompatible padding mode");
+        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_DIGEST, "Unsupported digest");
+        sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_DIGEST, "Incompatible digest");
+        sErrorCodeToString.put(KM_ERROR_INVALID_EXPIRATION_TIME, "Invalid expiration time");
+        sErrorCodeToString.put(KM_ERROR_INVALID_USER_ID, "Invalid user ID");
+        sErrorCodeToString.put(KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT,
+                "Invalid user authorization timeout");
+        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_KEY_FORMAT, "Unsupported key format");
+        sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_KEY_FORMAT, "Incompatible key format");
+        sErrorCodeToString.put(KM_ERROR_INVALID_INPUT_LENGTH, "Invalid input length");
+        sErrorCodeToString.put(KM_ERROR_KEY_NOT_YET_VALID, "Key not yet valid");
+        sErrorCodeToString.put(KM_ERROR_KEY_EXPIRED, "Key expired");
+        sErrorCodeToString.put(KM_ERROR_KEY_USER_NOT_AUTHENTICATED, "Key user not authenticated");
+        sErrorCodeToString.put(KM_ERROR_INVALID_OPERATION_HANDLE, "Invalid operation handle");
+        sErrorCodeToString.put(KM_ERROR_VERIFICATION_FAILED, "Signature/MAC verification failed");
+        sErrorCodeToString.put(KM_ERROR_TOO_MANY_OPERATIONS, "Too many operations");
+        sErrorCodeToString.put(KM_ERROR_INVALID_KEY_BLOB, "Invalid key blob");
+        sErrorCodeToString.put(KM_ERROR_INVALID_ARGUMENT, "Invalid argument");
+        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_TAG, "Unsupported tag");
+        sErrorCodeToString.put(KM_ERROR_INVALID_TAG, "Invalid tag");
+        sErrorCodeToString.put(KM_ERROR_MEMORY_ALLOCATION_FAILED, "Memory allocation failed");
+        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_EC_FIELD, "Unsupported EC field");
+        sErrorCodeToString.put(KM_ERROR_UNIMPLEMENTED, "Not implemented");
+        sErrorCodeToString.put(KM_ERROR_UNKNOWN_ERROR, "Unknown error");
+    }
+
     public static int getTagType(int tag) {
         return tag & (0xF << 28);
     }
+
+    public static String getErrorMessage(int errorCode) {
+        String result = sErrorCodeToString.get(errorCode);
+        if (result != null) {
+            return result;
+        }
+        return String.valueOf(errorCode);
+    }
 }
diff --git a/core/java/android/security/keymaster/KeymasterIntArgument.java b/core/java/android/security/keymaster/KeymasterIntArgument.java
index 71797ae..94ff87e 100644
--- a/core/java/android/security/keymaster/KeymasterIntArgument.java
+++ b/core/java/android/security/keymaster/KeymasterIntArgument.java
@@ -27,6 +27,15 @@
 
     public KeymasterIntArgument(int tag, int value) {
         super(tag);
+        switch (KeymasterDefs.getTagType(tag)) {
+            case KeymasterDefs.KM_INT:
+            case KeymasterDefs.KM_INT_REP:
+            case KeymasterDefs.KM_ENUM:
+            case KeymasterDefs.KM_ENUM_REP:
+                break; // OK.
+            default:
+                throw new IllegalArgumentException("Bad int tag " + tag);
+        }
         this.value = value;
     }
 
diff --git a/core/java/android/security/keymaster/KeymasterLongArgument.java b/core/java/android/security/keymaster/KeymasterLongArgument.java
index 781b1ab..d51d7e6 100644
--- a/core/java/android/security/keymaster/KeymasterLongArgument.java
+++ b/core/java/android/security/keymaster/KeymasterLongArgument.java
@@ -27,6 +27,12 @@
 
     public KeymasterLongArgument(int tag, long value) {
         super(tag);
+        switch (KeymasterDefs.getTagType(tag)) {
+            case KeymasterDefs.KM_LONG:
+                break; // OK.
+            default:
+                throw new IllegalArgumentException("Bad long tag " + tag);
+        }
         this.value = value;
     }
 
diff --git a/core/java/android/security/keymaster/OperationResult.java b/core/java/android/security/keymaster/OperationResult.java
index ad54c96..7cc43d3 100644
--- a/core/java/android/security/keymaster/OperationResult.java
+++ b/core/java/android/security/keymaster/OperationResult.java
@@ -30,6 +30,7 @@
 public class OperationResult implements Parcelable {
     public final int resultCode;
     public final IBinder token;
+    public final long operationHandle;
     public final int inputConsumed;
     public final byte[] output;
 
@@ -47,6 +48,7 @@
     protected OperationResult(Parcel in) {
         resultCode = in.readInt();
         token = in.readStrongBinder();
+        operationHandle = in.readLong();
         inputConsumed = in.readInt();
         output = in.createByteArray();
     }
@@ -60,6 +62,7 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(resultCode);
         out.writeStrongBinder(token);
+        out.writeLong(operationHandle);
         out.writeInt(inputConsumed);
         out.writeByteArray(output);
     }
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 749f813..d751266 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -436,11 +436,7 @@
 
     Request removeRequest(IBinder reqInterface) {
         synchronized (this) {
-            Request req = mActiveRequests.get(reqInterface);
-            if (req != null) {
-                mActiveRequests.remove(req);
-            }
-            return req;
+            return mActiveRequests.remove(reqInterface);
         }
     }
 
diff --git a/core/java/android/text/style/URLSpan.java b/core/java/android/text/style/URLSpan.java
index d29bfb6..0669b6f 100644
--- a/core/java/android/text/style/URLSpan.java
+++ b/core/java/android/text/style/URLSpan.java
@@ -16,6 +16,7 @@
 
 package android.text.style;
 
+import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
@@ -23,6 +24,7 @@
 import android.provider.Browser;
 import android.text.ParcelableSpan;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.View;
 
 public class URLSpan extends ClickableSpan implements ParcelableSpan {
@@ -59,6 +61,10 @@
         Context context = widget.getContext();
         Intent intent = new Intent(Intent.ACTION_VIEW, uri);
         intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
-        context.startActivity(intent);
+        try {
+            context.startActivity(intent);
+        } catch (ActivityNotFoundException e) {
+            Log.w("URLSpan", "Actvity was not found for intent, " + intent.toString());
+        }
     }
 }
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 50e64c6..a237afd 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -1005,31 +1005,23 @@
             return fields;
         }
 
-        final ArrayList<Field> declaredFields = new ArrayList();
-        klass.getDeclaredFieldsUnchecked(false, declaredFields);
-
-        final ArrayList<Field> foundFields = new ArrayList<Field>();
-        final int count = declaredFields.size();
-        for (int i = 0; i < count; i++) {
-            final Field field = declaredFields.get(i);
-
-            // Ensure the field type can be resolved.
-            try {
-                field.getType();
-            } catch (NoClassDefFoundError e) {
-                continue;
+        try {
+            final Field[] declaredFields = klass.getDeclaredFieldsUnchecked(false);
+            final ArrayList<Field> foundFields = new ArrayList<Field>();
+            for (final Field field : declaredFields) {
+              // Fields which can't be resolved have a null type.
+              if (field.getType() != null && field.isAnnotationPresent(ExportedProperty.class)) {
+                  field.setAccessible(true);
+                  foundFields.add(field);
+                  sAnnotations.put(field, field.getAnnotation(ExportedProperty.class));
+              }
             }
-
-            if (field.isAnnotationPresent(ExportedProperty.class)) {
-                field.setAccessible(true);
-                foundFields.add(field);
-                sAnnotations.put(field, field.getAnnotation(ExportedProperty.class));
-            }
+            fields = foundFields.toArray(new Field[foundFields.size()]);
+            map.put(klass, fields);
+        } catch (NoClassDefFoundError e) {
+            throw new AssertionError(e);
         }
 
-        fields = foundFields.toArray(new Field[foundFields.size()]);
-        map.put(klass, fields);
-
         return fields;
     }
 
@@ -1651,4 +1643,4 @@
             }
         });
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index f7c839f..b4a003a 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -1210,13 +1210,13 @@
         switch (keyCode) {
             
         case KeyEvent.KEYCODE_DPAD_LEFT:
-            if (movePrevious()) {
+            if (moveDirection(-1)) {
                 playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
                 return true;
             }
             break;
         case KeyEvent.KEYCODE_DPAD_RIGHT:
-            if (moveNext()) {
+            if (moveDirection(1)) {
                 playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
                 return true;
             }
@@ -1256,18 +1256,12 @@
         return super.onKeyUp(keyCode, event);
     }
     
-    boolean movePrevious() {
-        if (mItemCount > 0 && mSelectedPosition > 0) {
-            scrollToChild(mSelectedPosition - mFirstPosition - 1);
-            return true;
-        } else {
-            return false;
-        }
-    }
+    boolean moveDirection(int direction) {
+        direction = isLayoutRtl() ? -direction : direction;
+        int targetPosition = mSelectedPosition + direction;
 
-    boolean moveNext() {
-        if (mItemCount > 0 && mSelectedPosition < mItemCount - 1) {
-            scrollToChild(mSelectedPosition - mFirstPosition + 1);
+        if (mItemCount > 0 && targetPosition >= 0 && targetPosition < mItemCount) {
+            scrollToChild(targetPosition - mFirstPosition);
             return true;
         } else {
             return false;
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 56bdb9b..5eaf20c 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -817,12 +817,12 @@
         mContext = context;
         mIntent = intent;
 
-        mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1);
-
-        mLayoutInflater = LayoutInflater.from(context);
         if (mIntent == null) {
             throw new IllegalArgumentException("Non-null Intent must be specified.");
         }
+
+        mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1);
+        mLayoutInflater = LayoutInflater.from(context);
         mRequestedViews = new RemoteViewsFrameLayoutRefSet();
 
         // Strip the previously injected app widget id from service intent
diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java
index 433a54b..a4cdf19 100644
--- a/core/java/com/android/internal/os/InstallerConnection.java
+++ b/core/java/com/android/internal/os/InstallerConnection.java
@@ -91,11 +91,11 @@
     }
 
     public int dexopt(String apkPath, int uid, boolean isPublic, String instructionSet) {
-        return dexopt(apkPath, uid, isPublic, "*", instructionSet, false, false);
+        return dexopt(apkPath, uid, isPublic, "*", instructionSet, false, false, null);
     }
 
     public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName,
-            String instructionSet, boolean vmSafeMode, boolean debuggable) {
+            String instructionSet, boolean vmSafeMode, boolean debuggable, String outputPath) {
         StringBuilder builder = new StringBuilder("dexopt");
         builder.append(' ');
         builder.append(apkPath);
@@ -108,6 +108,8 @@
         builder.append(instructionSet);
         builder.append(vmSafeMode ? " 1" : " 0");
         builder.append(debuggable ? " 1" : " 0");
+        builder.append(' ');
+        builder.append(outputPath != null ? outputPath : "!");
         return execute(builder.toString());
     }
 
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 516bb65..d826303 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -536,7 +536,6 @@
  */
 int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
 {
-    int result = -1;
     JavaVMInitArgs initArgs;
     char propBuf[PROPERTY_VALUE_MAX];
     char stackTraceFileBuf[sizeof("-Xstacktracefile:")-1 + PROPERTY_VALUE_MAX];
@@ -552,12 +551,19 @@
     char gctypeOptsBuf[sizeof("-Xgc:")-1 + PROPERTY_VALUE_MAX];
     char backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1 + PROPERTY_VALUE_MAX];
     char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX];
+    char cachePruneBuf[sizeof("-Xzygote-max-boot-retry=")-1 + PROPERTY_VALUE_MAX];
     char dex2oatXmsImageFlagsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];
     char dex2oatXmxImageFlagsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
     char dex2oatXmsFlagsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];
     char dex2oatXmxFlagsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
     char dex2oatCompilerFilterBuf[sizeof("--compiler-filter=")-1 + PROPERTY_VALUE_MAX];
     char dex2oatImageCompilerFilterBuf[sizeof("--compiler-filter=")-1 + PROPERTY_VALUE_MAX];
+    char dex2oatThreadsBuf[sizeof("-j")-1 + PROPERTY_VALUE_MAX];
+    char dex2oatThreadsImageBuf[sizeof("-j")-1 + PROPERTY_VALUE_MAX];
+    char dex2oat_isa_variant_key[PROPERTY_KEY_MAX];
+    char dex2oat_isa_variant[sizeof("--instruction-set-variant=") -1 + PROPERTY_VALUE_MAX];
+    char dex2oat_isa_features_key[PROPERTY_KEY_MAX];
+    char dex2oat_isa_features[sizeof("--instruction-set-features=") -1 + PROPERTY_VALUE_MAX];
     char dex2oatFlagsBuf[PROPERTY_VALUE_MAX];
     char dex2oatImageFlagsBuf[PROPERTY_VALUE_MAX];
     char extraOptsBuf[PROPERTY_VALUE_MAX];
@@ -648,9 +654,9 @@
     /*
      * JIT related options.
      */
-    parseRuntimeOption("debug.dalvik.vm.usejit", usejitOptsBuf, "-Xusejit:");
-    parseRuntimeOption("debug.dalvik.vm.jitcodecachesize", jitcodecachesizeOptsBuf, "-Xjitcodecachesize:");
-    parseRuntimeOption("debug.dalvik.vm.jitthreshold", jitthresholdOptsBuf, "-Xjitthreshold:");
+    parseRuntimeOption("dalvik.vm.usejit", usejitOptsBuf, "-Xusejit:");
+    parseRuntimeOption("dalvik.vm.jitcodecachesize", jitcodecachesizeOptsBuf, "-Xjitcodecachesize:");
+    parseRuntimeOption("dalvik.vm.jitthreshold", jitthresholdOptsBuf, "-Xjitthreshold:");
 
     property_get("ro.config.low_ram", propBuf, "");
     if (strcmp(propBuf, "true") == 0) {
@@ -698,7 +704,7 @@
     if (!hasFile("/system/etc/preloaded-classes")) {
         ALOGE("Missing preloaded-classes file, /system/etc/preloaded-classes not found: %s\n",
               strerror(errno));
-        goto bail;
+        return -1;
     }
     addOption("-Ximage-compiler-option");
     addOption("--image-classes=/system/etc/preloaded-classes");
@@ -732,6 +738,46 @@
         parseCompilerOption("dalvik.vm.dex2oat-filter", dex2oatCompilerFilterBuf,
                             "--compiler-filter=", "-Xcompiler-option");
     }
+    parseCompilerOption("dalvik.vm.dex2oat-threads", dex2oatThreadsBuf, "-j", "-Xcompiler-option");
+    parseCompilerOption("dalvik.vm.image-dex2oat-threads", dex2oatThreadsImageBuf, "-j",
+                        "-Ximage-compiler-option");
+
+    // The runtime will compile a boot image, when necessary, not using installd. Thus, we need to
+    // pass the instruction-set-features/variant as an image-compiler-option.
+    // TODO: Find a better way for the instruction-set.
+#if defined(__arm__)
+    constexpr const char* instruction_set = "arm";
+#elif defined(__aarch64__)
+    constexpr const char* instruction_set = "arm64";
+#elif defined(__mips__) && !defined(__LP64__)
+    constexpr const char* instruction_set = "mips";
+#elif defined(__mips__) && defined(__LP64__)
+    constexpr const char* instruction_set = "mips64";
+#elif defined(__i386__)
+    constexpr const char* instruction_set = "x86";
+#elif defined(__x86_64__)
+    constexpr const char* instruction_set = "x86_64";
+#else
+    constexpr const char* instruction_set = "unknown";
+#endif
+    // Note: it is OK to reuse the buffer, as the values are exactly the same between
+    //       * compiler-option, used for runtime compilation (DexClassLoader)
+    //       * image-compiler-option, used for boot-image compilation on device
+
+    // Copy the variant.
+    sprintf(dex2oat_isa_variant_key, "dalvik.vm.isa.%s.variant", instruction_set);
+    parseCompilerOption(dex2oat_isa_variant_key, dex2oat_isa_variant,
+                        "--instruction-set-variant=", "-Ximage-compiler-option");
+    parseCompilerOption(dex2oat_isa_variant_key, dex2oat_isa_variant,
+                        "--instruction-set-variant=", "-Xcompiler-option");
+    // Copy the features.
+    sprintf(dex2oat_isa_features_key, "dalvik.vm.isa.%s.features", instruction_set);
+    parseCompilerOption(dex2oat_isa_features_key, dex2oat_isa_features,
+                        "--instruction-set-features=", "-Ximage-compiler-option");
+    parseCompilerOption(dex2oat_isa_features_key, dex2oat_isa_features,
+                        "--instruction-set-features=", "-Xcompiler-option");
+
+
     property_get("dalvik.vm.dex2oat-flags", dex2oatFlagsBuf, "");
     parseExtraOpts(dex2oatFlagsBuf, "-Xcompiler-option");
 
@@ -810,6 +856,10 @@
         addOption(nativeBridgeLibrary);
     }
 
+    // Dalvik-cache pruning counter.
+    parseRuntimeOption("dalvik.vm.zygote.max-boot-retry", cachePruneBuf,
+                       "-Xzygote-max-boot-retry=");
+
     initArgs.version = JNI_VERSION_1_4;
     initArgs.options = mOptions.editArray();
     initArgs.nOptions = mOptions.size();
@@ -824,13 +874,10 @@
      */
     if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
         ALOGE("JNI_CreateJavaVM failed\n");
-        goto bail;
+        return -1;
     }
 
-    result = 0;
-
-bail:
-    return result;
+    return 0;
 }
 
 char* AndroidRuntime::toSlashClassName(const char* className)
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ccdb5db..0ded6d32 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3094,9 +3094,9 @@
             </intent-filter>
         </receiver>
 
-        <receiver android:name="com.android.server.updates.TZInfoInstallReceiver" >
+        <receiver android:name="com.android.server.updates.TzDataInstallReceiver" >
             <intent-filter>
-                <action android:name="android.intent.action.UPDATE_TZINFO" />
+                <action android:name="android.intent.action.UPDATE_TZDATA" />
                 <data android:scheme="content" android:host="*" android:mimeType="*/*" />
             </intent-filter>
         </receiver>
diff --git a/core/res/res/drawable/pointer_arrow_icon.xml b/core/res/res/drawable/pointer_arrow_icon.xml
index 8f7d658..72af0c1 100644
--- a/core/res/res/drawable/pointer_arrow_icon.xml
+++ b/core/res/res/drawable/pointer_arrow_icon.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
     android:bitmap="@drawable/pointer_arrow"
-    android:hotSpotX="6dp"
-    android:hotSpotY="6dp" />
+    android:hotSpotX="5dp"
+    android:hotSpotY="5dp" />
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 0c3fb9a..ea592cf 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -389,6 +389,15 @@
          with the same {@link android.R.attr#taskAffinity} as it has. -->
     <attr name="allowTaskReparenting" format="boolean" />
 
+    <!-- Declare that this application may use cleartext traffic (e.g., HTTP rather than HTTPS;
+         WebSockets rather than WebSockets Secure; XMPP, IMAP, STMP without STARTTLS or TLS).
+         Defaults to true. If set to false {@code false}, the app declares that it does not
+         intend to use cleartext network traffic, in which case platform components (e.g.,
+         HTTP stacks, {@code WebView}, {@code MediaPlayer}) will refuse app's requests to use
+         cleartext traffic. Third-party libraries are encouraged to honor this flag as well.
+         @hide -->
+    <attr name="usesCleartextTraffic" format="boolean" />
+
     <!-- Declare that code from this application will need to be loaded into other
          applications' processes. On devices that support multiple instruction sets,
          this implies the code might be loaded into a process that's using any of the devices
@@ -1133,6 +1142,14 @@
              "com.google". -->
         <attr name="requiredAccountType" format="string"/>
         <attr name="isGame" />
+        <!-- Declare that this application may use cleartext traffic (e.g., HTTP rather than HTTPS;
+             WebSockets rather than WebSockets Secure; XMPP, IMAP, STMP without STARTTLS or TLS).
+             Defaults to true. If set to false {@code false}, the app declares that it does not
+             intend to use cleartext network traffic, in which case platform components (e.g.,
+             HTTP stacks, {@code WebView}, {@code MediaPlayer}) will refuse app's requests to use
+             cleartext traffic. Third-party libraries are encouraged to honor this flag as well.
+             @hide -->
+        <attr name="usesCleartextTraffic" />
         <attr name="multiArch" />
     </declare-styleable>
     
diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java
index cd45017..6fa28b1 100644
--- a/core/tests/coretests/src/android/net/UriTest.java
+++ b/core/tests/coretests/src/android/net/UriTest.java
@@ -804,4 +804,56 @@
         assertFalse(Uri.parse("content://com.example/path/path").isPathPrefixMatch(
                 Uri.parse("content://com.example/path%2Fpath")));
     }
+
+    public void testToSafeString() {
+        checkToSafeString("tel:xxxxxx", "tel:Google");
+        checkToSafeString("tel:xxxxxxxxxx", "tel:1234567890");
+        checkToSafeString("tEl:xxx.xxx-xxxx", "tEl:123.456-7890");
+
+        checkToSafeString("sms:xxxxxx", "sms:123abc");
+        checkToSafeString("smS:xxx.xxx-xxxx", "smS:123.456-7890");
+
+        checkToSafeString("smsto:xxxxxx", "smsto:123abc");
+        checkToSafeString("SMSTo:xxx.xxx-xxxx", "SMSTo:123.456-7890");
+
+        checkToSafeString("mailto:xxxxxxx@xxxxxxx.xxx", "mailto:android@android.com");
+        checkToSafeString("Mailto:xxxxxxx@xxxxxxx.xxxxxxxxxx",
+                "Mailto:android@android.com/secret");
+
+        checkToSafeString("sip:xxxxxxx@xxxxxxx.xxxxxxxx", "sip:android@android.com:1234");
+        checkToSafeString("sIp:xxxxxxx@xxxxxxx.xxx", "sIp:android@android.com");
+
+        checkToSafeString("http://www.android.com/...", "http://www.android.com");
+        checkToSafeString("HTTP://www.android.com/...", "HTTP://www.android.com");
+        checkToSafeString("http://www.android.com/...", "http://www.android.com/");
+        checkToSafeString("http://www.android.com/...", "http://www.android.com/secretUrl?param");
+        checkToSafeString("http://www.android.com/...",
+                "http://user:pwd@www.android.com/secretUrl?param");
+        checkToSafeString("http://www.android.com/...",
+                "http://user@www.android.com/secretUrl?param");
+        checkToSafeString("http://www.android.com/...", "http://www.android.com/secretUrl?param");
+        checkToSafeString("http:///...", "http:///path?param");
+        checkToSafeString("http:///...", "http://");
+        checkToSafeString("http://:12345/...", "http://:12345/");
+
+        checkToSafeString("https://www.android.com/...", "https://www.android.com/secretUrl?param");
+        checkToSafeString("https://www.android.com:8443/...",
+                "https://user:pwd@www.android.com:8443/secretUrl?param");
+        checkToSafeString("https://www.android.com/...", "https://user:pwd@www.android.com");
+        checkToSafeString("Https://www.android.com/...", "Https://user:pwd@www.android.com");
+
+        checkToSafeString("ftp://ftp.android.com/...", "ftp://ftp.android.com/");
+        checkToSafeString("ftP://ftp.android.com/...", "ftP://anonymous@ftp.android.com/");
+        checkToSafeString("ftp://ftp.android.com:2121/...",
+                "ftp://root:love@ftp.android.com:2121/");
+
+        checkToSafeString("unsupported://ajkakjah/askdha/secret?secret",
+                "unsupported://ajkakjah/askdha/secret?secret");
+        checkToSafeString("unsupported:ajkakjah/askdha/secret?secret",
+                "unsupported:ajkakjah/askdha/secret?secret");
+    }
+
+    private void checkToSafeString(String expectedSafeString, String original) {
+        assertEquals(expectedSafeString, Uri.parse(original).toSafeString());
+    }
 }
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index dd26019..a64f0ce 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -791,7 +791,6 @@
         // is no need for deep copying.
         private final Path mPath;
         private final Path mRenderPath;
-        private static final Matrix IDENTITY_MATRIX = new Matrix();
         private final Matrix mFinalPathMatrix = new Matrix();
 
         private Paint mStrokePaint;
@@ -932,7 +931,7 @@
 
         public void draw(Canvas canvas, int w, int h, ColorFilter filter) {
             // Travese the tree in pre-order to draw.
-            drawGroupTree(mRootGroup, IDENTITY_MATRIX, canvas, w, h, filter);
+            drawGroupTree(mRootGroup, Matrix.IDENTITY_MATRIX, canvas, w, h, filter);
         }
 
         private void drawPath(VGroup vGroup, VPath vPath, Canvas canvas, int w, int h,
diff --git a/keystore/java/android/security/AndroidKeyPairGenerator.java b/keystore/java/android/security/AndroidKeyPairGenerator.java
index 9d9a173..5fae831 100644
--- a/keystore/java/android/security/AndroidKeyPairGenerator.java
+++ b/keystore/java/android/security/AndroidKeyPairGenerator.java
@@ -136,6 +136,8 @@
             throw new IllegalStateException("could not generate key in keystore");
         }
 
+        Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
+
         final PrivateKey privKey;
         final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
         try {
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index acbae8f..dcc79be 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -19,6 +19,9 @@
 import com.android.org.conscrypt.OpenSSLEngine;
 import com.android.org.conscrypt.OpenSSLKeyHolder;
 
+import android.security.keymaster.KeyCharacteristics;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
 import android.util.Log;
 
 import java.io.ByteArrayInputStream;
@@ -31,6 +34,7 @@
 import java.security.KeyStore.PrivateKeyEntry;
 import java.security.KeyStore.ProtectionParameter;
 import java.security.KeyStore;
+import java.security.KeyStore.SecretKeyEntry;
 import java.security.KeyStoreException;
 import java.security.KeyStoreSpi;
 import java.security.NoSuchAlgorithmException;
@@ -50,6 +54,8 @@
 import java.util.Iterator;
 import java.util.Set;
 
+import javax.crypto.SecretKey;
+
 /**
  * A java.security.KeyStore interface for the Android KeyStore. An instance of
  * it can be created via the {@link java.security.KeyStore#getInstance(String)
@@ -77,18 +83,72 @@
     @Override
     public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
             UnrecoverableKeyException {
-        if (!isKeyEntry(alias)) {
-            return null;
+        if (isPrivateKeyEntry(alias)) {
+            final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
+            try {
+                return engine.getPrivateKeyById(Credentials.USER_PRIVATE_KEY + alias);
+            } catch (InvalidKeyException e) {
+                UnrecoverableKeyException t = new UnrecoverableKeyException("Can't get key");
+                t.initCause(e);
+                throw t;
+            }
+        } else if (isSecretKeyEntry(alias)) {
+            KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
+            String keyAliasInKeystore = Credentials.USER_SECRET_KEY + alias;
+            int errorCode = mKeyStore.getKeyCharacteristics(
+                    keyAliasInKeystore, null, null, keyCharacteristics);
+            if ((errorCode != KeymasterDefs.KM_ERROR_OK)
+                    && (errorCode != android.security.KeyStore.NO_ERROR)) {
+                throw new UnrecoverableKeyException("Failed to load information about key."
+                        + " Error code: " + errorCode);
+            }
+
+            int keymasterAlgorithm =
+                    keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_ALGORITHM, -1);
+            if (keymasterAlgorithm == -1) {
+                keymasterAlgorithm =
+                        keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_ALGORITHM, -1);
+            }
+            if (keymasterAlgorithm == -1) {
+                throw new UnrecoverableKeyException("Key algorithm unknown");
+            }
+            @KeyStoreKeyConstraints.AlgorithmEnum int keyAlgorithm;
+            try {
+                keyAlgorithm = KeyStoreKeyConstraints.Algorithm.fromKeymaster(keymasterAlgorithm);
+            } catch (IllegalArgumentException e) {
+                throw (UnrecoverableKeyException)
+                        new UnrecoverableKeyException("Unsupported key algorithm").initCause(e);
+            }
+
+            int keymasterDigest =
+                    keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_DIGEST, -1);
+            if (keymasterDigest == -1) {
+                keymasterDigest =
+                        keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_DIGEST, -1);
+            }
+            @KeyStoreKeyConstraints.DigestEnum Integer digest = null;
+            if (keymasterDigest != -1) {
+                try {
+                    digest = KeyStoreKeyConstraints.Digest.fromKeymaster(keymasterDigest);
+                } catch (IllegalArgumentException e) {
+                    throw (UnrecoverableKeyException)
+                            new UnrecoverableKeyException("Unsupported digest").initCause(e);
+                }
+            }
+
+            String keyAlgorithmString;
+            try {
+                keyAlgorithmString = KeyStoreKeyConstraints.Algorithm.toJCASecretKeyAlgorithm(
+                    keyAlgorithm, digest);
+            } catch (IllegalArgumentException e) {
+                throw (UnrecoverableKeyException)
+                        new UnrecoverableKeyException("Unsupported secret key type").initCause(e);
+            }
+
+            return new KeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmString);
         }
 
-        final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
-        try {
-            return engine.getPrivateKeyById(Credentials.USER_PRIVATE_KEY + alias);
-        } catch (InvalidKeyException e) {
-            UnrecoverableKeyException t = new UnrecoverableKeyException("Can't get key");
-            t.initCause(e);
-            throw t;
-        }
+        return null;
     }
 
     @Override
@@ -186,6 +246,11 @@
             return d;
         }
 
+        d = getModificationDate(Credentials.USER_SECRET_KEY + alias);
+        if (d != null) {
+            return d;
+        }
+
         d = getModificationDate(Credentials.USER_CERTIFICATE + alias);
         if (d != null) {
             return d;
@@ -203,8 +268,10 @@
 
         if (key instanceof PrivateKey) {
             setPrivateKeyEntry(alias, (PrivateKey) key, chain, null);
+        } else if (key instanceof SecretKey) {
+            setSecretKeyEntry(alias, (SecretKey) key, null);
         } else {
-            throw new KeyStoreException("Only PrivateKeys are supported");
+            throw new KeyStoreException("Only PrivateKey and SecretKey are supported");
         }
     }
 
@@ -319,6 +386,7 @@
             Credentials.deleteAllTypesForAlias(mKeyStore, alias);
         } else {
             Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
+            Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
         }
 
         final int flags = (params == null) ? 0 : params.getFlags();
@@ -340,6 +408,176 @@
         }
     }
 
+    private void setSecretKeyEntry(String entryAlias, SecretKey key, KeyStoreParameter params)
+            throws KeyStoreException {
+        if (key instanceof KeyStoreSecretKey) {
+            // KeyStore-backed secret key. It cannot be duplicated into another entry and cannot
+            // overwrite its own entry.
+            String keyAliasInKeystore = ((KeyStoreSecretKey) key).getAlias();
+            if (keyAliasInKeystore == null) {
+                throw new KeyStoreException("KeyStore-backed secret key does not have an alias");
+            }
+            if (!keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
+                throw new KeyStoreException("KeyStore-backed secret key has invalid alias: "
+                        + keyAliasInKeystore);
+            }
+            String keyEntryAlias =
+                    keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
+            if (!entryAlias.equals(keyEntryAlias)) {
+                throw new KeyStoreException("Can only replace KeyStore-backed keys with same"
+                        + " alias: " + entryAlias + " != " + keyEntryAlias);
+            }
+            // This is the entry where this key is already stored. No need to do anything.
+            if (params != null) {
+                throw new KeyStoreException("Modifying KeyStore-backed key using protection"
+                        + " parameters not supported");
+            }
+            return;
+        }
+
+        if (params == null) {
+            throw new KeyStoreException(
+                    "Protection parameters must be specified when importing a symmetric key");
+        }
+
+        // Not a KeyStore-backed secret key -- import its key material into keystore.
+        String keyExportFormat = key.getFormat();
+        if (keyExportFormat == null) {
+            throw new KeyStoreException(
+                    "Only secret keys that export their key material are supported");
+        } else if (!"RAW".equals(keyExportFormat)) {
+            throw new KeyStoreException(
+                    "Unsupported secret key material export format: " + keyExportFormat);
+        }
+        byte[] keyMaterial = key.getEncoded();
+        if (keyMaterial == null) {
+            throw new KeyStoreException("Key did not export its key material despite supporting"
+                    + " RAW format export");
+        }
+
+        String keyAlgorithmString = key.getAlgorithm();
+        @KeyStoreKeyConstraints.AlgorithmEnum int keyAlgorithm;
+        @KeyStoreKeyConstraints.DigestEnum Integer digest;
+        try {
+            keyAlgorithm =
+                    KeyStoreKeyConstraints.Algorithm.fromJCASecretKeyAlgorithm(keyAlgorithmString);
+            digest = KeyStoreKeyConstraints.Digest.fromJCASecretKeyAlgorithm(keyAlgorithmString);
+        } catch (IllegalArgumentException e) {
+            throw new KeyStoreException("Unsupported secret key algorithm: " + keyAlgorithmString);
+        }
+
+        if ((params.getAlgorithm() != null) && (params.getAlgorithm() != keyAlgorithm)) {
+            throw new KeyStoreException("Key algorithm mismatch. Key: " + keyAlgorithmString
+                    + ", parameter spec: "
+                    + KeyStoreKeyConstraints.Algorithm.toString(params.getAlgorithm()));
+        }
+
+        KeymasterArguments args = new KeymasterArguments();
+        args.addInt(KeymasterDefs.KM_TAG_ALGORITHM,
+                KeyStoreKeyConstraints.Algorithm.toKeymaster(keyAlgorithm));
+
+        if (digest != null) {
+            // Digest available from JCA key algorithm
+            if (params.getDigest() != null) {
+                // Digest also specified in parameters -- check that these two match
+                if (digest != params.getDigest()) {
+                    throw new KeyStoreException("Key digest mismatch. Key: " + keyAlgorithmString
+                            + ", parameter spec: "
+                            + KeyStoreKeyConstraints.Digest.toString(params.getDigest()));
+                }
+            }
+        } else {
+            // Digest not available from JCA key algorithm
+            digest = params.getDigest();
+        }
+        if (digest != null) {
+            args.addInt(KeymasterDefs.KM_TAG_DIGEST,
+                    KeyStoreKeyConstraints.Digest.toKeymaster(digest));
+            Integer digestOutputSizeBytes =
+                    KeyStoreKeyConstraints.Digest.getOutputSizeBytes(digest);
+            if (digestOutputSizeBytes != null) {
+                // TODO: Remove MAC length constraint once Keymaster API no longer requires it.
+                // TODO: Switch to bits instead of bytes, once this is fixed in Keymaster
+                args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, digestOutputSizeBytes);
+            }
+        }
+        if (keyAlgorithm == KeyStoreKeyConstraints.Algorithm.HMAC) {
+            if (digest == null) {
+                throw new IllegalStateException("Digest algorithm must be specified for key"
+                        + " algorithm " + keyAlgorithmString);
+            }
+        }
+
+        @KeyStoreKeyConstraints.PurposeEnum int purposes = (params.getPurposes() != null)
+                ? params.getPurposes()
+                : (KeyStoreKeyConstraints.Purpose.ENCRYPT
+                        | KeyStoreKeyConstraints.Purpose.DECRYPT
+                        | KeyStoreKeyConstraints.Purpose.SIGN
+                        | KeyStoreKeyConstraints.Purpose.VERIFY);
+        for (int keymasterPurpose :
+            KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
+            args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
+        }
+        if (params.getBlockMode() != null) {
+            args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE,
+                    KeyStoreKeyConstraints.BlockMode.toKeymaster(params.getBlockMode()));
+        }
+        if (params.getPadding() != null) {
+            args.addInt(KeymasterDefs.KM_TAG_PADDING,
+                    KeyStoreKeyConstraints.Padding.toKeymaster(params.getPadding()));
+        }
+        if (params.getMaxUsesPerBoot() != null) {
+            args.addInt(KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT, params.getMaxUsesPerBoot());
+        }
+        if (params.getMinSecondsBetweenOperations() != null) {
+            args.addInt(KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS,
+                    params.getMinSecondsBetweenOperations());
+        }
+        if (params.getUserAuthenticators().isEmpty()) {
+            args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
+        } else {
+            args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
+                    KeyStoreKeyConstraints.UserAuthenticator.allToKeymaster(
+                            params.getUserAuthenticators()));
+        }
+        if (params.getUserAuthenticationValidityDurationSeconds() != null) {
+            args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
+                    params.getUserAuthenticationValidityDurationSeconds());
+        }
+        args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
+                (params.getKeyValidityStart() != null)
+                        ? params.getKeyValidityStart() : new Date(0));
+        args.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
+                (params.getKeyValidityForOriginationEnd() != null)
+                        ? params.getKeyValidityForOriginationEnd() : new Date(Long.MAX_VALUE));
+        args.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
+                (params.getKeyValidityForConsumptionEnd() != null)
+                        ? params.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE));
+
+        // TODO: Remove this once keymaster does not require us to specify the size of imported key.
+        args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keyMaterial.length * 8);
+
+        if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
+                || ((purposes & KeyStoreKeyConstraints.Purpose.DECRYPT) != 0)) {
+            // Permit caller-specified IV. This is needed for the Cipher abstraction.
+            args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
+        }
+
+        Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias);
+        String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias;
+        int errorCode = mKeyStore.importKey(
+                keyAliasInKeystore,
+                args,
+                KeymasterDefs.KM_KEY_FORMAT_RAW,
+                keyMaterial,
+                params.getFlags(),
+                new KeyCharacteristics());
+        if (errorCode != android.security.KeyStore.NO_ERROR) {
+            throw new KeyStoreException("Failed to import secret key. Keystore error code: "
+                + errorCode);
+        }
+    }
+
     @Override
     public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)
             throws KeyStoreException {
@@ -413,6 +651,7 @@
         }
 
         return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias)
+                || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias)
                 || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias)
                 || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
     }
@@ -428,6 +667,10 @@
     }
 
     private boolean isKeyEntry(String alias) {
+        return isPrivateKeyEntry(alias) || isSecretKeyEntry(alias);
+    }
+
+    private boolean isPrivateKeyEntry(String alias) {
         if (alias == null) {
             throw new NullPointerException("alias == null");
         }
@@ -435,6 +678,14 @@
         return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias);
     }
 
+    private boolean isSecretKeyEntry(String alias) {
+        if (alias == null) {
+            throw new NullPointerException("alias == null");
+        }
+
+        return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias);
+    }
+
     private boolean isCertificateEntry(String alias) {
         if (alias == null) {
             throw new NullPointerException("alias == null");
@@ -554,11 +805,14 @@
             PrivateKeyEntry prE = (PrivateKeyEntry) entry;
             setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(),
                     (KeyStoreParameter) param);
-            return;
+        } else if (entry instanceof SecretKeyEntry) {
+            SecretKeyEntry secE = (SecretKeyEntry) entry;
+            setSecretKeyEntry(alias, secE.getSecretKey(), (KeyStoreParameter) param);
+        } else {
+            throw new KeyStoreException(
+                    "Entry must be a PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry"
+                    + "; was " + entry);
         }
-
-        throw new KeyStoreException(
-                "Entry must be a PrivateKeyEntry or TrustedCertificateEntry; was " + entry);
     }
 
 }
diff --git a/keystore/java/android/security/AndroidKeyStoreProvider.java b/keystore/java/android/security/AndroidKeyStoreProvider.java
index 9081e92..a59927d 100644
--- a/keystore/java/android/security/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/AndroidKeyStoreProvider.java
@@ -18,6 +18,9 @@
 
 import java.security.Provider;
 
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+
 /**
  * A provider focused on providing JCA interfaces for the Android KeyStore.
  *
@@ -26,14 +29,89 @@
 public class AndroidKeyStoreProvider extends Provider {
     public static final String PROVIDER_NAME = "AndroidKeyStore";
 
+    // IMPLEMENTATION NOTE: Class names are hard-coded in this provider to avoid loading these
+    // classes when this provider is instantiated and installed early on during each app's
+    // initialization process.
+
+    private static final String PACKAGE_NAME = "android.security";
+    private static final String KEYSTORE_SECRET_KEY_CLASS_NAME =
+            PACKAGE_NAME + ".KeyStoreSecretKey";
+
     public AndroidKeyStoreProvider() {
         super(PROVIDER_NAME, 1.0, "Android KeyStore security provider");
 
         // java.security.KeyStore
-        put("KeyStore." + AndroidKeyStore.NAME, AndroidKeyStore.class.getName());
+        put("KeyStore.AndroidKeyStore", PACKAGE_NAME + ".AndroidKeyStore");
 
         // java.security.KeyPairGenerator
-        put("KeyPairGenerator.EC", AndroidKeyPairGenerator.EC.class.getName());
-        put("KeyPairGenerator.RSA", AndroidKeyPairGenerator.RSA.class.getName());
+        put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyPairGenerator$EC");
+        put("KeyPairGenerator.RSA", PACKAGE_NAME +  ".AndroidKeyPairGenerator$RSA");
+
+        // javax.crypto.KeyGenerator
+        put("KeyGenerator.AES", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$AES");
+        put("KeyGenerator.HmacSHA256", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$HmacSHA256");
+
+        // java.security.SecretKeyFactory
+        put("SecretKeyFactory.AES", PACKAGE_NAME + ".KeyStoreSecretKeyFactorySpi");
+        put("SecretKeyFactory.HmacSHA256", PACKAGE_NAME + ".KeyStoreSecretKeyFactorySpi");
+
+        // javax.crypto.Mac
+        putMacImpl("HmacSHA256", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA256");
+
+        // javax.crypto.Cipher
+        putSymmetricCipherImpl("AES/ECB/NoPadding",
+                PACKAGE_NAME + ".KeyStoreCipherSpi$AES$ECB$NoPadding");
+        putSymmetricCipherImpl("AES/ECB/PKCS7Padding",
+                PACKAGE_NAME + ".KeyStoreCipherSpi$AES$ECB$PKCS7Padding");
+
+        putSymmetricCipherImpl("AES/CBC/NoPadding",
+                PACKAGE_NAME + ".KeyStoreCipherSpi$AES$CBC$NoPadding");
+        putSymmetricCipherImpl("AES/CBC/PKCS7Padding",
+                PACKAGE_NAME + ".KeyStoreCipherSpi$AES$CBC$PKCS7Padding");
+
+        putSymmetricCipherImpl("AES/CTR/NoPadding",
+                PACKAGE_NAME + ".KeyStoreCipherSpi$AES$CTR$NoPadding");
+    }
+
+    private void putMacImpl(String algorithm, String implClass) {
+        put("Mac." + algorithm, implClass);
+        put("Mac." + algorithm + " SupportedKeyClasses", KEYSTORE_SECRET_KEY_CLASS_NAME);
+    }
+
+    private void putSymmetricCipherImpl(String transformation, String implClass) {
+        put("Cipher." + transformation, implClass);
+        put("Cipher." + transformation + " SupportedKeyClasses", KEYSTORE_SECRET_KEY_CLASS_NAME);
+    }
+
+    /**
+     * Gets the {@link KeyStore} operation handle corresponding to the provided JCA crypto
+     * primitive.
+     *
+     * <p>The following primitives are supported: {@link Cipher} and {@link Mac}.
+     *
+     * @return KeyStore operation handle or {@code null} if the provided primitive's KeyStore
+     *         operation is not in progress.
+     *
+     * @throws IllegalArgumentException if the provided primitive is not supported or is not backed
+     *         by AndroidKeyStore provider.
+     */
+    public static Long getKeyStoreOperationHandle(Object cryptoPrimitive) {
+        if (cryptoPrimitive == null) {
+            throw new NullPointerException();
+        }
+        Object spi;
+        if (cryptoPrimitive instanceof Mac) {
+            spi = ((Mac) cryptoPrimitive).getSpi();
+        } else if (cryptoPrimitive instanceof Cipher) {
+            spi = ((Cipher) cryptoPrimitive).getSpi();
+        } else {
+            throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive);
+        }
+        if (!(spi instanceof KeyStoreCryptoOperation)) {
+            throw new IllegalArgumentException(
+                    "Crypto primitive not backed by AndroidKeyStore: " + cryptoPrimitive
+                    + ", spi: " + spi);
+        }
+        return ((KeyStoreCryptoOperation) spi).getOperationHandle();
     }
 }
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index af76d9d..6283e02 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -61,6 +61,9 @@
     /** Key prefix for user private keys. */
     public static final String USER_PRIVATE_KEY = "USRPKEY_";
 
+    /** Key prefix for user secret keys. */
+    public static final String USER_SECRET_KEY = "USRSKEY_";
+
     /** Key prefix for VPN. */
     public static final String VPN = "VPN_";
 
@@ -218,7 +221,8 @@
          * Make sure every type is deleted. There can be all three types, so
          * don't use a conditional here.
          */
-        return keystore.delKey(Credentials.USER_PRIVATE_KEY + alias)
+        return keystore.delete(Credentials.USER_PRIVATE_KEY + alias)
+                | keystore.delete(Credentials.USER_SECRET_KEY + alias)
                 | deleteCertificateTypesForAlias(keystore, alias);
     }
 
@@ -235,4 +239,20 @@
         return keystore.delete(Credentials.USER_CERTIFICATE + alias)
                 | keystore.delete(Credentials.CA_CERTIFICATE + alias);
     }
+
+    /**
+     * Delete private key for a particular {@code alias}.
+     * Returns {@code true} if an entry was was deleted.
+     */
+    static boolean deletePrivateKeyTypeForAlias(KeyStore keystore, String alias) {
+        return keystore.delete(Credentials.USER_PRIVATE_KEY + alias);
+    }
+
+    /**
+     * Delete secret key for a particular {@code alias}.
+     * Returns {@code true} if an entry was was deleted.
+     */
+    static boolean deleteSecretKeyTypeForAlias(KeyStore keystore, String alias) {
+        return keystore.delete(Credentials.USER_SECRET_KEY + alias);
+    }
 }
diff --git a/keystore/java/android/security/CryptoOperationException.java b/keystore/java/android/security/CryptoOperationException.java
new file mode 100644
index 0000000..00c142f
--- /dev/null
+++ b/keystore/java/android/security/CryptoOperationException.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 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.security;
+
+/**
+ * Base class for exceptions during cryptographic operations which cannot throw a suitable checked
+ * exception.
+ *
+ * <p>The contract of the majority of crypto primitives/operations (e.g. {@code Cipher} or
+ * {@code Signature}) is that they can throw a checked exception during initialization, but are not
+ * permitted to throw a checked exception during operation. Because crypto operations can fail
+ * for a variety of reasons after initialization, this base class provides type-safety for unchecked
+ * exceptions that may be thrown in those cases.
+ *
+ * @hide
+ */
+public class CryptoOperationException extends RuntimeException {
+
+    /**
+     * Constructs a new {@code CryptoOperationException} without detail message and cause.
+     */
+    public CryptoOperationException() {
+        super();
+    }
+
+    /**
+     * Constructs a new {@code CryptoOperationException} with the provided detail message and no
+     * cause.
+     */
+    public CryptoOperationException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new {@code CryptoOperationException} with the provided detail message and cause.
+     */
+    public CryptoOperationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Constructs a new {@code CryptoOperationException} with the provided cause.
+     */
+    public CryptoOperationException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/keystore/java/android/security/KeyExpiredException.java b/keystore/java/android/security/KeyExpiredException.java
new file mode 100644
index 0000000..35a5acc
--- /dev/null
+++ b/keystore/java/android/security/KeyExpiredException.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 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.security;
+
+/**
+ * Indicates that a cryptographic operation failed because the employed key's validity end date
+ * is in the past.
+ *
+ * @hide
+ */
+public class KeyExpiredException extends CryptoOperationException {
+
+    /**
+     * Constructs a new {@code KeyExpiredException} without detail message and cause.
+     */
+    public KeyExpiredException() {
+        super("Key expired");
+    }
+
+    /**
+     * Constructs a new {@code KeyExpiredException} with the provided detail message and no cause.
+     */
+    public KeyExpiredException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new {@code KeyExpiredException} with the provided detail message and cause.
+     */
+    public KeyExpiredException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/keystore/java/android/security/KeyGeneratorSpec.java b/keystore/java/android/security/KeyGeneratorSpec.java
new file mode 100644
index 0000000..1311368
--- /dev/null
+++ b/keystore/java/android/security/KeyGeneratorSpec.java
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2015 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.security;
+
+import android.content.Context;
+import android.text.TextUtils;
+
+import java.security.cert.Certificate;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+
+/**
+ * {@link AlgorithmParameterSpec} for initializing a {@code KeyGenerator} that works with
+ * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore facility</a>.
+ *
+ * <p>The Android KeyStore facility is accessed through a {@link KeyGenerator} API
+ * using the {@code AndroidKeyStore} provider. The {@code context} passed in may be used to pop up
+ * some UI to ask the user to unlock or initialize the Android KeyStore facility.
+ *
+ * <p>After generation, the {@code keyStoreAlias} is used with the
+ * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter)}
+ * interface to retrieve the {@link SecretKey} and its associated {@link Certificate} chain.
+ *
+ * @hide
+ */
+public class KeyGeneratorSpec implements AlgorithmParameterSpec {
+
+    private final Context mContext;
+    private final String mKeystoreAlias;
+    private final int mFlags;
+    private final Integer mKeySize;
+    private final Date mKeyValidityStart;
+    private final Date mKeyValidityForOriginationEnd;
+    private final Date mKeyValidityForConsumptionEnd;
+    private final @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
+    private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+    private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+    private final Integer mMinSecondsBetweenOperations;
+    private final Integer mMaxUsesPerBoot;
+    private final Set<Integer> mUserAuthenticators;
+    private final Integer mUserAuthenticationValidityDurationSeconds;
+
+    private KeyGeneratorSpec(
+            Context context,
+            String keyStoreAlias,
+            int flags,
+            Integer keySize,
+            Date keyValidityStart,
+            Date keyValidityForOriginationEnd,
+            Date keyValidityForConsumptionEnd,
+            @KeyStoreKeyConstraints.PurposeEnum Integer purposes,
+            @KeyStoreKeyConstraints.PaddingEnum Integer padding,
+            @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode,
+            Integer minSecondsBetweenOperations,
+            Integer maxUsesPerBoot,
+            Set<Integer> userAuthenticators,
+            Integer userAuthenticationValidityDurationSeconds) {
+        if (context == null) {
+            throw new IllegalArgumentException("context == null");
+        } else if (TextUtils.isEmpty(keyStoreAlias)) {
+            throw new IllegalArgumentException("keyStoreAlias must not be empty");
+        } else if ((userAuthenticationValidityDurationSeconds != null)
+                && (userAuthenticationValidityDurationSeconds < 0)) {
+            throw new IllegalArgumentException(
+                    "userAuthenticationValidityDurationSeconds must not be negative");
+        }
+
+        mContext = context;
+        mKeystoreAlias = keyStoreAlias;
+        mFlags = flags;
+        mKeySize = keySize;
+        mKeyValidityStart = keyValidityStart;
+        mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
+        mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
+        mPurposes = purposes;
+        mPadding = padding;
+        mBlockMode = blockMode;
+        mMinSecondsBetweenOperations = minSecondsBetweenOperations;
+        mMaxUsesPerBoot = maxUsesPerBoot;
+        mUserAuthenticators = (userAuthenticators != null)
+                ? new HashSet<Integer>(userAuthenticators)
+                : Collections.<Integer>emptySet();
+        mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
+    }
+
+    /**
+     * Gets the Android context used for operations with this instance.
+     */
+    public Context getContext() {
+        return mContext;
+    }
+
+    /**
+     * Returns the alias that will be used in the {@code java.security.KeyStore} in conjunction with
+     * the {@code AndroidKeyStore}.
+     */
+    public String getKeystoreAlias() {
+        return mKeystoreAlias;
+    }
+
+    /**
+     * @hide
+     */
+    public int getFlags() {
+        return mFlags;
+    }
+
+    /**
+     * Gets the requested key size or {@code null} if the default size should be used.
+     */
+    public Integer getKeySize() {
+        return mKeySize;
+    }
+
+    /**
+     * Gets the time instant before which the key is not yet valid.
+     *
+     * @return instant or {@code null} if not restricted.
+     */
+    public Date getKeyValidityStart() {
+        return mKeyValidityStart;
+    }
+
+    /**
+     * Gets the time instant after which the key is no longer valid for decryption and verification.
+     *
+     * @return instant or {@code null} if not restricted.
+     *
+     * @hide
+     */
+    public Date getKeyValidityForConsumptionEnd() {
+        return mKeyValidityForConsumptionEnd;
+    }
+
+    /**
+     * Gets the time instant after which the key is no longer valid for encryption and signing.
+     *
+     * @return instant or {@code null} if not restricted.
+     */
+    public Date getKeyValidityForOriginationEnd() {
+        return mKeyValidityForOriginationEnd;
+    }
+
+    /**
+     * Gets the set of purposes for which the key can be used.
+     *
+     * @return set of purposes or {@code null} if the key can be used for any purpose.
+     */
+    public @KeyStoreKeyConstraints.PurposeEnum Integer getPurposes() {
+        return mPurposes;
+    }
+
+    /**
+     * Gets the padding scheme to which the key is restricted.
+     *
+     * @return padding scheme or {@code null} if the padding scheme is not restricted.
+     */
+    public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() {
+        return mPadding;
+    }
+
+    /**
+     * Gets the block mode to which the key is restricted when used for encryption or decryption.
+     *
+     * @return block more or {@code null} if block mode is not restricted.
+     *
+     * @hide
+     */
+    public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() {
+        return mBlockMode;
+    }
+
+    /**
+     * Gets the minimum number of seconds that must expire since the most recent use of the key
+     * before it can be used again.
+     *
+     * @return number of seconds or {@code null} if there is no restriction on how frequently a key
+     *         can be used.
+     *
+     * @hide
+     */
+    public Integer getMinSecondsBetweenOperations() {
+        return mMinSecondsBetweenOperations;
+    }
+
+    /**
+     * Gets the number of times the key can be used without rebooting the device.
+     *
+     * @return maximum number of times or {@code null} if there is no restriction.
+     * @hide
+     */
+    public Integer getMaxUsesPerBoot() {
+        return mMaxUsesPerBoot;
+    }
+
+    /**
+     * Gets the user authenticators which protect access to this key. The key can only be used iff
+     * the user has authenticated to at least one of these user authenticators.
+     *
+     * @return user authenticators or empty set if the key can be used without user authentication.
+     *
+     * @hide
+     */
+    public Set<Integer> getUserAuthenticators() {
+        return new HashSet<Integer>(mUserAuthenticators);
+    }
+
+    /**
+     * Gets the duration of time (seconds) for which this key can be used after the user
+     * successfully authenticates to one of the associated user authenticators.
+     *
+     * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication
+     *         is required for every use of the key.
+     *
+     * @hide
+     */
+    public Integer getUserAuthenticationValidityDurationSeconds() {
+        return mUserAuthenticationValidityDurationSeconds;
+    }
+
+    /**
+     * Returns {@code true} if the key must be encrypted in the {@link java.security.KeyStore}.
+     */
+    public boolean isEncryptionRequired() {
+        return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0;
+    }
+
+    public static class Builder {
+        private final Context mContext;
+        private String mKeystoreAlias;
+        private int mFlags;
+        private Integer mKeySize;
+        private Date mKeyValidityStart;
+        private Date mKeyValidityForOriginationEnd;
+        private Date mKeyValidityForConsumptionEnd;
+        private @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
+        private @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+        private @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+        private Integer mMinSecondsBetweenOperations;
+        private Integer mMaxUsesPerBoot;
+        private Set<Integer> mUserAuthenticators;
+        private Integer mUserAuthenticationValidityDurationSeconds;
+
+        /**
+         * Creates a new instance of the {@code Builder} with the given {@code context}. The
+         * {@code context} passed in may be used to pop up some UI to ask the user to unlock or
+         * initialize the Android KeyStore facility.
+         */
+        public Builder(Context context) {
+            if (context == null) {
+                throw new NullPointerException("context == null");
+            }
+            mContext = context;
+        }
+
+        /**
+         * Sets the alias to be used to retrieve the key later from a {@link java.security.KeyStore}
+         * instance using the {@code AndroidKeyStore} provider.
+         *
+         * <p>The alias must be provided. There is no default.
+         */
+        public Builder setAlias(String alias) {
+            if (alias == null) {
+                throw new NullPointerException("alias == null");
+            }
+            mKeystoreAlias = alias;
+            return this;
+        }
+
+        /**
+         * Sets the size (in bits) of the key to be generated.
+         *
+         * <p>By default, the key size will be determines based on the key algorithm. For example,
+         * for {@code HmacSHA256}, the key size will default to {@code 256}.
+         */
+        public Builder setKeySize(int keySize) {
+            mKeySize = keySize;
+            return this;
+        }
+
+        /**
+         * Indicates that this key must be encrypted at rest on storage. Note that enabling this
+         * will require that the user enable a strong lock screen (e.g., PIN, password) before
+         * creating or using the generated key is successful.
+         */
+        public Builder setEncryptionRequired(boolean required) {
+            if (required) {
+                mFlags |= KeyStore.FLAG_ENCRYPTED;
+            } else {
+                mFlags &= ~KeyStore.FLAG_ENCRYPTED;
+            }
+            return this;
+        }
+
+        /**
+         * Sets the time instant before which the key is not yet valid.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityStart(Date startDate) {
+            mKeyValidityStart = startDate;
+            return this;
+        }
+
+        /**
+         * Sets the time instant after which the key is no longer valid.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityStart(Date)
+         * @see #setKeyValidityForConsumptionEnd(Date)
+         * @see #setKeyValidityForOriginationEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityEnd(Date endDate) {
+            setKeyValidityForOriginationEnd(endDate);
+            setKeyValidityForConsumptionEnd(endDate);
+            return this;
+        }
+
+        /**
+         * Sets the time instant after which the key is no longer valid for encryption and signing.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityForConsumptionEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityForOriginationEnd(Date endDate) {
+            mKeyValidityForOriginationEnd = endDate;
+            return this;
+        }
+
+        /**
+         * Sets the time instant after which the key is no longer valid for decryption and
+         * verification.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityForOriginationEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityForConsumptionEnd(Date endDate) {
+            mKeyValidityForConsumptionEnd = endDate;
+            return this;
+        }
+
+        /**
+         * Restricts the purposes for which the key can be used to the provided set of purposes.
+         *
+         * <p>By default, the key can be used for encryption, decryption, signing, and verification.
+         *
+         * @hide
+         */
+        public Builder setPurposes(@KeyStoreKeyConstraints.PurposeEnum int purposes) {
+            mPurposes = purposes;
+            return this;
+        }
+
+        /**
+         * Restricts the key to being used only with the provided padding scheme. Attempts to use
+         * the key with any other padding will be rejected.
+         *
+         * <p>This restriction must be specified for keys which are used for encryption/decryption.
+         *
+         * @hide
+         */
+        public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) {
+            mPadding = padding;
+            return this;
+        }
+
+        /**
+         * Restricts the key to being used only with the provided block mode when encrypting or
+         * decrypting. Attempts to use the key with any other block modes will be rejected.
+         *
+         * <p>This restriction must be specified for keys which are used for encryption/decryption.
+         *
+         * @hide
+         */
+        public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) {
+            mBlockMode = blockMode;
+            return this;
+        }
+
+        /**
+         * Sets the minimum number of seconds that must expire since the most recent use of the key
+         * before it can be used again.
+         *
+         * <p>By default, there is no restriction on how frequently a key can be used.
+         *
+         * @hide
+         */
+        public Builder setMinSecondsBetweenOperations(int seconds) {
+            mMinSecondsBetweenOperations = seconds;
+            return this;
+        }
+
+        /**
+         * Sets the maximum number of times a key can be used without rebooting the device.
+         *
+         * <p>By default, the key can be used for an unlimited number of times.
+         *
+         * @hide
+         */
+        public Builder setMaxUsesPerBoot(int count) {
+            mMaxUsesPerBoot = count;
+            return this;
+        }
+
+        /**
+         * Sets the user authenticators which protect access to this key. The key can only be used
+         * iff the user has authenticated to at least one of these user authenticators.
+         *
+         * <p>By default, the key can be used without user authentication.
+         *
+         * @param userAuthenticators user authenticators or empty list if this key can be accessed
+         *        without user authentication.
+         *
+         * @see #setUserAuthenticationValidityDurationSeconds(int)
+         *
+         * @hide
+         */
+        public Builder setUserAuthenticators(Set<Integer> userAuthenticators) {
+            mUserAuthenticators =
+                    (userAuthenticators != null) ? new HashSet<Integer>(userAuthenticators) : null;
+            return this;
+        }
+
+        /**
+         * Sets the duration of time (seconds) for which this key can be used after the user
+         * successfully authenticates to one of the associated user authenticators.
+         *
+         * <p>By default, the user needs to authenticate for every use of the key.
+         *
+         * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
+         *        every use of the key.
+         *
+         * @see #setUserAuthenticators(Set)
+         *
+         * @hide
+         */
+        public Builder setUserAuthenticationValidityDurationSeconds(int seconds) {
+            mUserAuthenticationValidityDurationSeconds = seconds;
+            return this;
+        }
+
+        /**
+         * Builds a new instance instance of {@code KeyGeneratorSpec}.
+         *
+         * @throws IllegalArgumentException if a required field is missing or violates a constraint.
+         */
+        public KeyGeneratorSpec build() {
+            return new KeyGeneratorSpec(mContext, mKeystoreAlias, mFlags, mKeySize,
+                    mKeyValidityStart, mKeyValidityForOriginationEnd, mKeyValidityForConsumptionEnd,
+                    mPurposes, mPadding, mBlockMode, mMinSecondsBetweenOperations, mMaxUsesPerBoot,
+                    mUserAuthenticators, mUserAuthenticationValidityDurationSeconds);
+        }
+    }
+}
diff --git a/keystore/java/android/security/KeyNotYetValidException.java b/keystore/java/android/security/KeyNotYetValidException.java
new file mode 100644
index 0000000..f1c2cac
--- /dev/null
+++ b/keystore/java/android/security/KeyNotYetValidException.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 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.security;
+
+/**
+ * Indicates that a cryptographic operation failed because the employed key's validity start date
+ * is in the future.
+ *
+ * @hide
+ */
+public class KeyNotYetValidException extends CryptoOperationException {
+
+    /**
+     * Constructs a new {@code KeyNotYetValidException} without detail message and cause.
+     */
+    public KeyNotYetValidException() {
+        super("Key not yet valid");
+    }
+
+    /**
+     * Constructs a new {@code KeyNotYetValidException} with the provided detail message and no
+     * cause.
+     */
+    public KeyNotYetValidException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new {@code KeyNotYetValidException} with the provided detail message and cause.
+     */
+    public KeyNotYetValidException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java
index cc097aa..0001604 100644
--- a/keystore/java/android/security/KeyPairGeneratorSpec.java
+++ b/keystore/java/android/security/KeyPairGeneratorSpec.java
@@ -24,7 +24,10 @@
 import java.security.PrivateKey;
 import java.security.cert.Certificate;
 import java.security.spec.AlgorithmParameterSpec;
+import java.util.Collections;
 import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
 
 import javax.security.auth.x500.X500Principal;
 
@@ -72,6 +75,28 @@
 
     private final int mFlags;
 
+    private final Date mKeyValidityStart;
+
+    private final Date mKeyValidityForOriginationEnd;
+
+    private final Date mKeyValidityForConsumptionEnd;
+
+    private final @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
+
+    private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
+
+    private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+
+    private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+
+    private final Integer mMinSecondsBetweenOperations;
+
+    private final Integer mMaxUsesPerBoot;
+
+    private final Set<Integer> mUserAuthenticators;
+
+    private final Integer mUserAuthenticationValidityDurationSeconds;
+
     /**
      * Parameter specification for the "{@code AndroidKeyPairGenerator}"
      * instance of the {@link java.security.KeyPairGenerator} API. The
@@ -106,7 +131,18 @@
      */
     public KeyPairGeneratorSpec(Context context, String keyStoreAlias, String keyType, int keySize,
             AlgorithmParameterSpec spec, X500Principal subjectDN, BigInteger serialNumber,
-            Date startDate, Date endDate, int flags) {
+            Date startDate, Date endDate, int flags,
+            Date keyValidityStart,
+            Date keyValidityForOriginationEnd,
+            Date keyValidityForConsumptionEnd,
+            @KeyStoreKeyConstraints.PurposeEnum Integer purposes,
+            @KeyStoreKeyConstraints.DigestEnum Integer digest,
+            @KeyStoreKeyConstraints.PaddingEnum Integer padding,
+            @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode,
+            Integer minSecondsBetweenOperations,
+            Integer maxUsesPerBoot,
+            Set<Integer> userAuthenticators,
+            Integer userAuthenticationValidityDurationSeconds) {
         if (context == null) {
             throw new IllegalArgumentException("context == null");
         } else if (TextUtils.isEmpty(keyStoreAlias)) {
@@ -121,6 +157,10 @@
             throw new IllegalArgumentException("endDate == null");
         } else if (endDate.before(startDate)) {
             throw new IllegalArgumentException("endDate < startDate");
+        } else if ((userAuthenticationValidityDurationSeconds != null)
+                && (userAuthenticationValidityDurationSeconds < 0)) {
+            throw new IllegalArgumentException(
+                    "userAuthenticationValidityDurationSeconds must not be negative");
         }
 
         mContext = context;
@@ -133,6 +173,31 @@
         mStartDate = startDate;
         mEndDate = endDate;
         mFlags = flags;
+        mKeyValidityStart = keyValidityStart;
+        mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
+        mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
+        mPurposes = purposes;
+        mDigest = digest;
+        mPadding = padding;
+        mBlockMode = blockMode;
+        mMinSecondsBetweenOperations = minSecondsBetweenOperations;
+        mMaxUsesPerBoot = maxUsesPerBoot;
+        mUserAuthenticators = (userAuthenticators != null)
+                ? new HashSet<Integer>(userAuthenticators)
+                : Collections.<Integer>emptySet();
+        mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
+    }
+
+    /**
+     * TODO: Remove this constructor once tests are switched over to the new one above.
+     * @hide
+     */
+    public KeyPairGeneratorSpec(Context context, String keyStoreAlias, String keyType, int keySize,
+            AlgorithmParameterSpec spec, X500Principal subjectDN, BigInteger serialNumber,
+            Date startDate, Date endDate, int flags) {
+        this(context, keyStoreAlias, keyType, keySize, spec, subjectDN, serialNumber, startDate,
+                endDate, flags, startDate, endDate, endDate, null, null, null, null, null, null,
+                null, null);
     }
 
     /**
@@ -222,6 +287,145 @@
     }
 
     /**
+     * Gets the time instant before which the key pair is not yet valid.
+     *
+     * @return instant or {@code null} if not restricted.
+     *
+     * @hide
+     */
+    public Date getKeyValidityStart() {
+        return mKeyValidityStart;
+    }
+
+    /**
+     * Gets the time instant after which the key pair is no longer valid for decryption and
+     * verification.
+     *
+     * @return instant or {@code null} if not restricted.
+     *
+     * @hide
+     */
+    public Date getKeyValidityForConsumptionEnd() {
+        return mKeyValidityForConsumptionEnd;
+    }
+
+    /**
+     * Gets the time instant after which the key pair is no longer valid for encryption and signing.
+     *
+     * @return instant or {@code null} if not restricted.
+     *
+     * @hide
+     */
+    public Date getKeyValidityForOriginationEnd() {
+        return mKeyValidityForOriginationEnd;
+    }
+
+    /**
+     * Gets the set of purposes for which the key can be used.
+     *
+     * @return set of purposes or {@code null} if the key can be used for any purpose.
+     *
+     * @hide
+     */
+    public @KeyStoreKeyConstraints.PurposeEnum Integer getPurposes() {
+        return mPurposes;
+    }
+
+    /**
+     * Gets the digest to which the key is restricted.
+     *
+     * @return digest or {@code null} if the digest is not restricted.
+     *
+     * @hide
+     */
+    public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() {
+        return mDigest;
+    }
+
+    /**
+     * Gets the padding scheme to which the key is restricted.
+     *
+     * @return padding scheme or {@code null} if the padding scheme is not restricted.
+     *
+     * @hide
+     */
+    public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() {
+        return mPadding;
+    }
+
+    /**
+     * Gets the block mode to which the key is restricted when used for encryption or decryption.
+     *
+     * @return block more or {@code null} if block mode is not restricted.
+     *
+     * @hide
+     */
+    public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() {
+        return mBlockMode;
+    }
+
+    /**
+     * Gets the minimum number of seconds that must expire since the most recent use of the private
+     * key before it can be used again.
+     *
+     * <p>This restriction applies only to private key operations. Public key operations are not
+     * restricted.
+     *
+     * @return number of seconds or {@code null} if there is no restriction on how frequently a key
+     *         can be used.
+     *
+     * @hide
+     */
+    public Integer getMinSecondsBetweenOperations() {
+        return mMinSecondsBetweenOperations;
+    }
+
+    /**
+     * Gets the number of times the private key can be used without rebooting the device.
+     *
+     * <p>This restriction applies only to private key operations. Public key operations are not
+     * restricted.
+     *
+     * @return maximum number of times or {@code null} if there is no restriction.
+     *
+     * @hide
+     */
+    public Integer getMaxUsesPerBoot() {
+        return mMaxUsesPerBoot;
+    }
+
+    /**
+     * Gets the user authenticators which protect access to the private key. The key can only be
+     * used iff the user has authenticated to at least one of these user authenticators.
+     *
+     * <p>This restriction applies only to private key operations. Public key operations are not
+     * restricted.
+     *
+     * @return user authenticators or empty set if the key can be used without user authentication.
+     *
+     * @hide
+     */
+    public Set<Integer> getUserAuthenticators() {
+        return new HashSet<Integer>(mUserAuthenticators);
+    }
+
+    /**
+     * Gets the duration of time (seconds) for which the private key can be used after the user
+     * successfully authenticates to one of the associated user authenticators.
+     *
+     * <p>This restriction applies only to private key operations. Public key operations are not
+     * restricted.
+     *
+     * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication
+     *         is required for every use of the key.
+     *
+     * @hide
+     */
+    public Integer getUserAuthenticationValidityDurationSeconds() {
+        return mUserAuthenticationValidityDurationSeconds;
+    }
+
+    /**
      * Builder class for {@link KeyPairGeneratorSpec} objects.
      * <p>
      * This will build a parameter spec for use with the <a href="{@docRoot}
@@ -263,6 +467,28 @@
 
         private int mFlags;
 
+        private Date mKeyValidityStart;
+
+        private Date mKeyValidityForOriginationEnd;
+
+        private Date mKeyValidityForConsumptionEnd;
+
+        private @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
+
+        private @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
+
+        private @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+
+        private @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+
+        private Integer mMinSecondsBetweenOperations;
+
+        private Integer mMaxUsesPerBoot;
+
+        private Set<Integer> mUserAuthenticators;
+
+        private Integer mUserAuthenticationValidityDurationSeconds;
+
         /**
          * Creates a new instance of the {@code Builder} with the given
          * {@code context}. The {@code context} passed in may be used to pop up
@@ -389,14 +615,218 @@
         }
 
         /**
+         * Sets the time instant before which the key is not yet valid.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityStart(Date startDate) {
+            mKeyValidityStart = startDate;
+            return this;
+        }
+
+        /**
+         * Sets the time instant after which the key is no longer valid.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityStart(Date)
+         * @see #setKeyValidityForConsumptionEnd(Date)
+         * @see #setKeyValidityForOriginationEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityEnd(Date endDate) {
+            setKeyValidityForOriginationEnd(endDate);
+            setKeyValidityForConsumptionEnd(endDate);
+            return this;
+        }
+
+        /**
+         * Sets the time instant after which the key is no longer valid for encryption and signing.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityForConsumptionEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityForOriginationEnd(Date endDate) {
+            mKeyValidityForOriginationEnd = endDate;
+            return this;
+        }
+
+        /**
+         * Sets the time instant after which the key is no longer valid for decryption and
+         * verification.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityForOriginationEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityForConsumptionEnd(Date endDate) {
+            mKeyValidityForConsumptionEnd = endDate;
+            return this;
+        }
+
+        /**
+         * Restricts the purposes for which the key can be used to the provided set of purposes.
+         *
+         * <p>By default, the key can be used for encryption, decryption, signing, and verification.
+         *
+         * @hide
+         */
+        public Builder setPurposes(@KeyStoreKeyConstraints.PurposeEnum int purposes) {
+            mPurposes = purposes;
+            return this;
+        }
+
+        /**
+         * Restricts the key to being used only with the provided digest. Attempts to use the key
+         * with any other digests be rejected.
+         *
+         * <p>This restriction must be specified for keys which are used for signing/verification.
+         *
+         * @hide
+         */
+        public Builder setDigest(@KeyStoreKeyConstraints.DigestEnum int digest) {
+            mDigest = digest;
+            return this;
+        }
+
+        /**
+         * Restricts the key to being used only with the provided padding scheme. Attempts to use
+         * the key with any other padding will be rejected.
+         *
+         * <p>This restriction must be specified for keys which are used for encryption/decryption.
+         *
+         * @hide
+         */
+        public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) {
+            mPadding = padding;
+            return this;
+        }
+
+        /**
+         * Restricts the key to being used only with the provided block mode when encrypting or
+         * decrypting. Attempts to use the key with any other block modes will be rejected.
+         *
+         * <p>This restriction must be specified for keys which are used for encryption/decryption.
+         *
+         * @hide
+         */
+        public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) {
+            mBlockMode = blockMode;
+            return this;
+        }
+
+        /**
+         * Sets the minimum number of seconds that must expire since the most recent use of the key
+         * before it can be used again.
+         *
+         * <p>By default, there is no restriction on how frequently a key can be used.
+         *
+         * <p>This restriction applies only to private key operations. Public key operations are not
+         * restricted.
+         *
+         * @hide
+         */
+        public Builder setMinSecondsBetweenOperations(int seconds) {
+            mMinSecondsBetweenOperations = seconds;
+            return this;
+        }
+
+        /**
+         * Sets the maximum number of times a key can be used without rebooting the device.
+         *
+         * <p>By default, the key can be used for an unlimited number of times.
+         *
+         * <p>This restriction applies only to private key operations. Public key operations are not
+         * restricted.
+         *
+         * @hide
+         */
+        public Builder setMaxUsesPerBoot(int count) {
+            mMaxUsesPerBoot = count;
+            return this;
+        }
+
+        /**
+         * Sets the user authenticators which protect access to this key. The key can only be used
+         * iff the user has authenticated to at least one of these user authenticators.
+         *
+         * <p>By default, the key can be used without user authentication.
+         *
+         * <p>This restriction applies only to private key operations. Public key operations are not
+         * restricted.
+         *
+         * @param userAuthenticators user authenticators or empty list if this key can be accessed
+         *        without user authentication.
+         *
+         * @see #setUserAuthenticationValidityDurationSeconds(int)
+         *
+         * @hide
+         */
+        public Builder setUserAuthenticators(Set<Integer> userAuthenticators) {
+            mUserAuthenticators =
+                    (userAuthenticators != null) ? new HashSet<Integer>(userAuthenticators) : null;
+            return this;
+        }
+
+        /**
+         * Sets the duration of time (seconds) for which this key can be used after the user
+         * successfully authenticates to one of the associated user authenticators.
+         *
+         * <p>By default, the user needs to authenticate for every use of the key.
+         *
+         * <p>This restriction applies only to private key operations. Public key operations are not
+         * restricted.
+         *
+         * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
+         *        every use of the key.
+         *
+         * @see #setUserAuthenticators(Set)
+         *
+         * @hide
+         */
+        public Builder setUserAuthenticationValidityDurationSeconds(int seconds) {
+            mUserAuthenticationValidityDurationSeconds = seconds;
+            return this;
+        }
+
+        /**
          * Builds the instance of the {@code KeyPairGeneratorSpec}.
          *
          * @throws IllegalArgumentException if a required field is missing
          * @return built instance of {@code KeyPairGeneratorSpec}
          */
         public KeyPairGeneratorSpec build() {
-            return new KeyPairGeneratorSpec(mContext, mKeystoreAlias, mKeyType, mKeySize, mSpec,
-                    mSubjectDN, mSerialNumber, mStartDate, mEndDate, mFlags);
+            return new KeyPairGeneratorSpec(mContext,
+                    mKeystoreAlias,
+                    mKeyType,
+                    mKeySize,
+                    mSpec,
+                    mSubjectDN,
+                    mSerialNumber,
+                    mStartDate,
+                    mEndDate,
+                    mFlags,
+                    mKeyValidityStart,
+                    mKeyValidityForOriginationEnd,
+                    mKeyValidityForConsumptionEnd,
+                    mPurposes,
+                    mDigest,
+                    mPadding,
+                    mBlockMode,
+                    mMinSecondsBetweenOperations,
+                    mMaxUsesPerBoot,
+                    mUserAuthenticators,
+                    mUserAuthenticationValidityDurationSeconds);
         }
     }
 }
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index bfbf028..84a664e 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -25,6 +25,8 @@
 import android.security.keymaster.ExportResult;
 import android.security.keymaster.KeyCharacteristics;
 import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterBlob;
+import android.security.keymaster.KeymasterDefs;
 import android.security.keymaster.OperationResult;
 import android.util.Log;
 
@@ -388,22 +390,22 @@
         }
     }
 
-    public int generateKey(String alias, KeymasterArguments args, int uid, int flags,
-            KeyCharacteristics outCharacteristics) {
+    public int generateKey(String alias, KeymasterArguments args, byte[] entropy, int uid,
+            int flags, KeyCharacteristics outCharacteristics) {
         try {
-            return mBinder.generateKey(alias, args, uid, flags, outCharacteristics);
+            return mBinder.generateKey(alias, args, entropy, uid, flags, outCharacteristics);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return SYSTEM_ERROR;
         }
     }
 
-    public int generateKey(String alias, KeymasterArguments args, int flags,
+    public int generateKey(String alias, KeymasterArguments args, byte[] entropy, int flags,
             KeyCharacteristics outCharacteristics) {
-        return generateKey(alias, args, UID_SELF, flags, outCharacteristics);
+        return generateKey(alias, args, entropy, UID_SELF, flags, outCharacteristics);
     }
 
-    public int getKeyCharacteristics(String alias, byte[] clientId, byte[] appId,
+    public int getKeyCharacteristics(String alias, KeymasterBlob clientId, KeymasterBlob appId,
             KeyCharacteristics outCharacteristics) {
         try {
             return mBinder.getKeyCharacteristics(alias, clientId, appId, outCharacteristics);
@@ -429,7 +431,8 @@
         return importKey(alias, args, format, keyData, UID_SELF, flags, outCharacteristics);
     }
 
-    public ExportResult exportKey(String alias, int format, byte[] clientId, byte[] appId) {
+    public ExportResult exportKey(String alias, int format, KeymasterBlob clientId,
+            KeymasterBlob appId) {
         try {
             return mBinder.exportKey(alias, format, clientId, appId);
         } catch (RemoteException e) {
@@ -439,9 +442,9 @@
     }
 
     public OperationResult begin(String alias, int purpose, boolean pruneable,
-            KeymasterArguments args, KeymasterArguments outArgs) {
+            KeymasterArguments args, byte[] entropy, KeymasterArguments outArgs) {
         try {
-            return mBinder.begin(getToken(), alias, purpose, pruneable, args, outArgs);
+            return mBinder.begin(getToken(), alias, purpose, pruneable, args, entropy, outArgs);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return null;
@@ -474,4 +477,87 @@
             return SYSTEM_ERROR;
         }
     }
+
+    /**
+     * Check if the operation referenced by {@code token} is currently authorized.
+     *
+     * @param token An operation token returned by a call to {@link KeyStore.begin}.
+     */
+    public boolean isOperationAuthorized(IBinder token) {
+        try {
+            return mBinder.isOperationAuthorized(token);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return false;
+        }
+    }
+
+    /**
+     * Add an authentication record to the keystore authorization table.
+     *
+     * @param authToken The packed bytes of a hw_auth_token_t to be provided to keymaster.
+     * @return {@code KeyStore.NO_ERROR} on success, otherwise an error value corresponding to
+     * a {@code KeymasterDefs.KM_ERROR_} value or {@code KeyStore} ResponseCode.
+     */
+    public int addAuthToken(byte[] authToken) {
+        try {
+            return mBinder.addAuthToken(authToken);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return SYSTEM_ERROR;
+        }
+    }
+
+    public static KeyStoreException getKeyStoreException(int errorCode) {
+        if (errorCode > 0) {
+            // KeyStore layer error
+            switch (errorCode) {
+                case NO_ERROR:
+                    return new KeyStoreException(errorCode, "OK");
+                case LOCKED:
+                    return new KeyStoreException(errorCode, "Keystore locked");
+                case UNINITIALIZED:
+                    return new KeyStoreException(errorCode, "Keystore not initialized");
+                case SYSTEM_ERROR:
+                    return new KeyStoreException(errorCode, "System error");
+                case PERMISSION_DENIED:
+                    return new KeyStoreException(errorCode, "Permission denied");
+                case KEY_NOT_FOUND:
+                    return new KeyStoreException(errorCode, "Key not found");
+                case VALUE_CORRUPTED:
+                    return new KeyStoreException(errorCode, "Key blob corrupted");
+                default:
+                    return new KeyStoreException(errorCode, String.valueOf(errorCode));
+            }
+        } else {
+            // Keymaster layer error
+            switch (errorCode) {
+                case KeymasterDefs.KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT:
+                    // The name of this parameter significantly differs between Keymaster and
+                    // framework APIs. Use the framework wording to make life easier for developers.
+                    return new KeyStoreException(errorCode,
+                            "Invalid user authentication validity duration");
+                default:
+                    return new KeyStoreException(errorCode,
+                            KeymasterDefs.getErrorMessage(errorCode));
+            }
+        }
+    }
+
+    public static CryptoOperationException getCryptoOperationException(KeyStoreException e) {
+        switch (e.getErrorCode()) {
+            case KeymasterDefs.KM_ERROR_KEY_EXPIRED:
+                return new KeyExpiredException();
+            case KeymasterDefs.KM_ERROR_KEY_NOT_YET_VALID:
+                return new KeyNotYetValidException();
+            case KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED:
+                return new UserNotAuthenticatedException();
+            default:
+                return new CryptoOperationException("Crypto operation failed", e);
+        }
+    }
+
+    public static CryptoOperationException getCryptoOperationException(int errorCode) {
+        return getCryptoOperationException(getKeyStoreException(errorCode));
+    }
 }
diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java
new file mode 100644
index 0000000..ec358d6
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreCipherSpi.java
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 2015 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.security;
+
+import android.os.IBinder;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keymaster.OperationResult;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.util.Arrays;
+
+import javax.crypto.AEADBadTagException;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.CipherSpi;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.IvParameterSpec;
+
+/**
+ * Base class for {@link CipherSpi} providing Android KeyStore backed ciphers.
+ *
+ * @hide
+ */
+public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCryptoOperation {
+
+    public abstract static class AES extends KeyStoreCipherSpi {
+        protected AES(@KeyStoreKeyConstraints.BlockModeEnum int blockMode,
+                @KeyStoreKeyConstraints.PaddingEnum int padding, boolean ivUsed) {
+            super(KeyStoreKeyConstraints.Algorithm.AES,
+                    blockMode,
+                    padding,
+                    16,
+                    ivUsed);
+        }
+
+        public abstract static class ECB extends AES {
+            protected ECB(@KeyStoreKeyConstraints.PaddingEnum int padding) {
+                super(KeyStoreKeyConstraints.BlockMode.ECB, padding, false);
+            }
+
+            public static class NoPadding extends ECB {
+                public NoPadding() {
+                    super(KeyStoreKeyConstraints.Padding.NONE);
+                }
+            }
+
+            public static class PKCS7Padding extends ECB {
+                public PKCS7Padding() {
+                    super(KeyStoreKeyConstraints.Padding.PKCS7);
+                }
+            }
+        }
+
+        public abstract static class CBC extends AES {
+            protected CBC(@KeyStoreKeyConstraints.BlockModeEnum int padding) {
+                super(KeyStoreKeyConstraints.BlockMode.CBC, padding, true);
+            }
+
+            public static class NoPadding extends CBC {
+                public NoPadding() {
+                    super(KeyStoreKeyConstraints.Padding.NONE);
+                }
+            }
+
+            public static class PKCS7Padding extends CBC {
+                public PKCS7Padding() {
+                    super(KeyStoreKeyConstraints.Padding.PKCS7);
+                }
+            }
+        }
+
+        public abstract static class CTR extends AES {
+            protected CTR(@KeyStoreKeyConstraints.BlockModeEnum int padding) {
+                super(KeyStoreKeyConstraints.BlockMode.CTR, padding, true);
+            }
+
+            public static class NoPadding extends CTR {
+                public NoPadding() {
+                    super(KeyStoreKeyConstraints.Padding.NONE);
+                }
+            }
+        }
+    }
+
+    private final KeyStore mKeyStore;
+    private final @KeyStoreKeyConstraints.AlgorithmEnum int mAlgorithm;
+    private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockMode;
+    private final @KeyStoreKeyConstraints.PaddingEnum int mPadding;
+    private final int mBlockSizeBytes;
+    private final boolean mIvUsed;
+
+    // Fields below are populated by Cipher.init and KeyStore.begin and should be preserved after
+    // doFinal finishes.
+    protected boolean mEncrypting;
+    private KeyStoreSecretKey mKey;
+    private SecureRandom mRng;
+    private boolean mFirstOperationInitiated;
+    byte[] mIv;
+
+    // Fields below must be reset
+    private byte[] mAdditionalEntropyForBegin;
+    /**
+     * Token referencing this operation inside keystore service. It is initialized by
+     * {@code engineInit} and is invalidated when {@code engineDoFinal} succeeds and one some
+     * error conditions in between.
+     */
+    private IBinder mOperationToken;
+    private Long mOperationHandle;
+    private KeyStoreCryptoOperationChunkedStreamer mMainDataStreamer;
+
+    protected KeyStoreCipherSpi(
+            @KeyStoreKeyConstraints.AlgorithmEnum int algorithm,
+            @KeyStoreKeyConstraints.BlockModeEnum int blockMode,
+            @KeyStoreKeyConstraints.PaddingEnum int padding,
+            int blockSizeBytes,
+            boolean ivUsed) {
+        mKeyStore = KeyStore.getInstance();
+        mAlgorithm = algorithm;
+        mBlockMode = blockMode;
+        mPadding = padding;
+        mBlockSizeBytes = blockSizeBytes;
+        mIvUsed = ivUsed;
+    }
+
+    @Override
+    protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
+        init(opmode, key, random);
+        initAlgorithmSpecificParameters();
+        ensureKeystoreOperationInitialized();
+    }
+
+    @Override
+    protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
+        init(opmode, key, random);
+        initAlgorithmSpecificParameters(params);
+        ensureKeystoreOperationInitialized();
+    }
+
+    @Override
+    protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
+            SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
+        init(opmode, key, random);
+        initAlgorithmSpecificParameters(params);
+        ensureKeystoreOperationInitialized();
+    }
+
+    private void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
+        reset();
+        if (!(key instanceof KeyStoreSecretKey)) {
+            throw new InvalidKeyException(
+                    "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null"));
+        }
+        mKey = (KeyStoreSecretKey) key;
+        mRng = random;
+        mIv = null;
+        mFirstOperationInitiated = false;
+
+        if ((opmode != Cipher.ENCRYPT_MODE) && (opmode != Cipher.DECRYPT_MODE)) {
+            throw new UnsupportedOperationException(
+                    "Only ENCRYPT and DECRYPT modes supported. Mode: " + opmode);
+        }
+        mEncrypting = opmode == Cipher.ENCRYPT_MODE;
+    }
+
+    private void reset() {
+        IBinder operationToken = mOperationToken;
+        if (operationToken != null) {
+            mOperationToken = null;
+            mKeyStore.abort(operationToken);
+        }
+        mOperationHandle = null;
+        mMainDataStreamer = null;
+        mAdditionalEntropyForBegin = null;
+    }
+
+    private void ensureKeystoreOperationInitialized() {
+        if (mMainDataStreamer != null) {
+            return;
+        }
+        if (mKey == null) {
+            throw new IllegalStateException("Not initialized");
+        }
+
+        KeymasterArguments keymasterInputArgs = new KeymasterArguments();
+        keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mAlgorithm);
+        keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, mBlockMode);
+        keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_PADDING, mPadding);
+        addAlgorithmSpecificParametersToBegin(keymasterInputArgs);
+
+        KeymasterArguments keymasterOutputArgs = new KeymasterArguments();
+        OperationResult opResult = mKeyStore.begin(
+                mKey.getAlias(),
+                mEncrypting ? KeymasterDefs.KM_PURPOSE_ENCRYPT : KeymasterDefs.KM_PURPOSE_DECRYPT,
+                true, // permit aborting this operation if keystore runs out of resources
+                keymasterInputArgs,
+                mAdditionalEntropyForBegin,
+                keymasterOutputArgs);
+        mAdditionalEntropyForBegin = null;
+        if (opResult == null) {
+            throw new KeyStoreConnectException();
+        } else if (opResult.resultCode != KeyStore.NO_ERROR) {
+            throw KeyStore.getCryptoOperationException(opResult.resultCode);
+        }
+
+        if (opResult.token == null) {
+            throw new CryptoOperationException("Keystore returned null operation token");
+        }
+        mOperationToken = opResult.token;
+        mOperationHandle = opResult.operationHandle;
+        loadAlgorithmSpecificParametersFromBeginResult(keymasterOutputArgs);
+        mFirstOperationInitiated = true;
+        mMainDataStreamer = new KeyStoreCryptoOperationChunkedStreamer(
+                new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
+                        mKeyStore, opResult.token));
+    }
+
+    @Override
+    protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
+        ensureKeystoreOperationInitialized();
+
+        if (inputLen == 0) {
+            return null;
+        }
+
+        byte[] output;
+        try {
+            output = mMainDataStreamer.update(input, inputOffset, inputLen);
+        } catch (KeyStoreException e) {
+            throw KeyStore.getCryptoOperationException(e);
+        }
+
+        if (output.length == 0) {
+            return null;
+        }
+
+        return output;
+    }
+
+    @Override
+    protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output,
+            int outputOffset) throws ShortBufferException {
+        byte[] outputCopy = engineUpdate(input, inputOffset, inputLen);
+        if (outputCopy == null) {
+            return 0;
+        }
+        int outputAvailable = output.length - outputOffset;
+        if (outputCopy.length > outputAvailable) {
+            throw new ShortBufferException("Output buffer too short. Produced: "
+                    + outputCopy.length + ", available: " + outputAvailable);
+        }
+        System.arraycopy(outputCopy, 0, output, outputOffset, outputCopy.length);
+        return outputCopy.length;
+    }
+
+    @Override
+    protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
+            throws IllegalBlockSizeException, BadPaddingException {
+        ensureKeystoreOperationInitialized();
+
+        byte[] output;
+        try {
+            output = mMainDataStreamer.doFinal(input, inputOffset, inputLen);
+        } catch (KeyStoreException e) {
+            switch (e.getErrorCode()) {
+                case KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH:
+                    throw new IllegalBlockSizeException();
+                case KeymasterDefs.KM_ERROR_INVALID_ARGUMENT:
+                    throw new BadPaddingException();
+                case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
+                    throw new AEADBadTagException();
+                default:
+                    throw KeyStore.getCryptoOperationException(e);
+            }
+        }
+
+        reset();
+        return output;
+    }
+
+    @Override
+    protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output,
+            int outputOffset) throws ShortBufferException, IllegalBlockSizeException,
+            BadPaddingException {
+        byte[] outputCopy = engineDoFinal(input, inputOffset, inputLen);
+        if (outputCopy == null) {
+            return 0;
+        }
+        int outputAvailable = output.length - outputOffset;
+        if (outputCopy.length > outputAvailable) {
+            throw new ShortBufferException("Output buffer too short. Produced: "
+                    + outputCopy.length + ", available: " + outputAvailable);
+        }
+        System.arraycopy(outputCopy, 0, output, outputOffset, outputCopy.length);
+        return outputCopy.length;
+    }
+
+    @Override
+    protected int engineGetBlockSize() {
+        return mBlockSizeBytes;
+    }
+
+    @Override
+    protected byte[] engineGetIV() {
+        return (mIv != null) ? mIv.clone() : null;
+    }
+
+    @Override
+    protected int engineGetOutputSize(int inputLen) {
+        return inputLen + 3 * engineGetBlockSize();
+    }
+
+    @Override
+    protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
+        // This should never be invoked because all algorithms registered with the AndroidKeyStore
+        // provide explicitly specify block mode.
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected void engineSetPadding(String arg0) throws NoSuchPaddingException {
+        // This should never be invoked because all algorithms registered with the AndroidKeyStore
+        // provide explicitly specify padding mode.
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void finalize() throws Throwable {
+        try {
+            IBinder operationToken = mOperationToken;
+            if (operationToken != null) {
+                mKeyStore.abort(operationToken);
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+
+    @Override
+    public Long getOperationHandle() {
+        return mOperationHandle;
+    }
+
+    // The methods below may need to be overridden by subclasses that use algorithm-specific
+    // parameters.
+
+    /**
+     * Returns algorithm-specific parameters used by this {@code CipherSpi} instance or {@code null}
+     * if no algorithm-specific parameters are used.
+     *
+     * <p>This implementation only handles the IV parameter.
+     */
+    @Override
+    protected AlgorithmParameters engineGetParameters() {
+        if (!mIvUsed) {
+            return null;
+        }
+        if ((mIv != null) && (mIv.length > 0)) {
+            try {
+                AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
+                params.init(new IvParameterSpec(mIv));
+                return params;
+            } catch (NoSuchAlgorithmException e) {
+                throw new RuntimeException("Failed to obtain AES AlgorithmParameters", e);
+            } catch (InvalidParameterSpecException e) {
+                throw new RuntimeException(
+                        "Failed to initialize AES AlgorithmParameters with an IV", e);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Invoked by {@code engineInit} to initialize algorithm-specific parameters. These parameters
+     * may need to be stored to be reused after {@code doFinal}.
+     *
+     * <p>The default implementation only handles the IV parameters.
+     *
+     * @param params algorithm parameters.
+     *
+     * @throws InvalidAlgorithmParameterException if some/all of the parameters cannot be
+     *         automatically configured and thus {@code Cipher.init} needs to be invoked with
+     *         explicitly provided parameters.
+     */
+    protected void initAlgorithmSpecificParameters(AlgorithmParameterSpec params)
+            throws InvalidAlgorithmParameterException {
+        if (!mIvUsed) {
+            if (params != null) {
+                throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
+            }
+            return;
+        }
+
+        // IV is used
+        if (params == null) {
+            if (!mEncrypting) {
+                // IV must be provided by the caller
+                throw new InvalidAlgorithmParameterException(
+                        "IvParameterSpec must be provided when decrypting");
+            }
+            return;
+        }
+        if (!(params instanceof IvParameterSpec)) {
+            throw new InvalidAlgorithmParameterException("Only IvParameterSpec supported");
+        }
+        mIv = ((IvParameterSpec) params).getIV();
+        if (mIv == null) {
+            throw new InvalidAlgorithmParameterException("Null IV in IvParameterSpec");
+        }
+    }
+
+    /**
+     * Invoked by {@code engineInit} to initialize algorithm-specific parameters. These parameters
+     * may need to be stored to be reused after {@code doFinal}.
+     *
+     * <p>The default implementation only handles the IV parameters.
+     *
+     * @param params algorithm parameters.
+     *
+     * @throws InvalidAlgorithmParameterException if some/all of the parameters cannot be
+     *         automatically configured and thus {@code Cipher.init} needs to be invoked with
+     *         explicitly provided parameters.
+     */
+    protected void initAlgorithmSpecificParameters(AlgorithmParameters params)
+            throws InvalidAlgorithmParameterException {
+        if (!mIvUsed) {
+            if (params != null) {
+                throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
+            }
+            return;
+        }
+
+        // IV is used
+        if (params == null) {
+            if (!mEncrypting) {
+                // IV must be provided by the caller
+                throw new InvalidAlgorithmParameterException("IV required when decrypting"
+                        + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
+            }
+            return;
+        }
+
+        IvParameterSpec ivSpec;
+        try {
+            ivSpec = params.getParameterSpec(IvParameterSpec.class);
+        } catch (InvalidParameterSpecException e) {
+            if (!mEncrypting) {
+                // IV must be provided by the caller
+                throw new InvalidAlgorithmParameterException("IV required when decrypting"
+                        + ", but not found in parameters: " + params, e);
+            }
+            mIv = null;
+            return;
+        }
+        mIv = ivSpec.getIV();
+        if (mIv == null) {
+            throw new InvalidAlgorithmParameterException("Null IV in AlgorithmParameters");
+        }
+    }
+
+    /**
+     * Invoked by {@code engineInit} to initialize algorithm-specific parameters. These parameters
+     * may need to be stored to be reused after {@code doFinal}.
+     *
+     * <p>The default implementation only handles the IV parameter.
+     *
+     * @throws InvalidKeyException if some/all of the parameters cannot be automatically configured
+     *         and thus {@code Cipher.init} needs to be invoked with explicitly provided parameters.
+     */
+    protected void initAlgorithmSpecificParameters() throws InvalidKeyException {
+        if (!mIvUsed) {
+            return;
+        }
+
+        // IV is used
+        if (!mEncrypting) {
+            throw new InvalidKeyException("IV required when decrypting"
+                    + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
+        }
+    }
+
+    /**
+     * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation.
+     *
+     * <p>The default implementation takes care of the IV.
+     *
+     * @param keymasterArgs keystore/keymaster arguments to be populated with algorithm-specific
+     *        parameters.
+     */
+    protected void addAlgorithmSpecificParametersToBegin(KeymasterArguments keymasterArgs) {
+        if (!mFirstOperationInitiated) {
+            // First begin operation -- see if we need to provide additional entropy for IV
+            // generation.
+            if (mIvUsed) {
+                // IV is needed
+                if ((mIv == null) && (mEncrypting)) {
+                    // TODO: Switch to keymaster-generated IV code below once keymaster supports
+                    // that.
+                    // IV is needed but was not provided by the caller -- generate an IV.
+                    mIv = new byte[mBlockSizeBytes];
+                    SecureRandom rng = (mRng != null) ? mRng : new SecureRandom();
+                    rng.nextBytes(mIv);
+//                    // IV was not provided by the caller and thus will be generated by keymaster.
+//                    // Mix in some additional entropy from the provided SecureRandom.
+//                    if (mRng != null) {
+//                        mAdditionalEntropyForBegin = new byte[mBlockSizeBytes];
+//                        mRng.nextBytes(mAdditionalEntropyForBegin);
+//                    }
+                }
+            }
+        }
+
+        if ((mIvUsed) && (mIv != null)) {
+            keymasterArgs.addBlob(KeymasterDefs.KM_TAG_NONCE, mIv);
+        }
+    }
+
+    /**
+     * Invoked by {@code engineInit} to obtain algorithm-specific parameters from the result of the
+     * Keymaster's {@code begin} operation. Some of these parameters may need to be reused after
+     * {@code doFinal} by {@link #addAlgorithmSpecificParametersToBegin(KeymasterArguments)}.
+     *
+     * <p>The default implementation only takes care of the IV.
+     *
+     * @param keymasterArgs keystore/keymaster arguments returned by KeyStore {@code begin}
+     *        operation.
+     */
+    protected void loadAlgorithmSpecificParametersFromBeginResult(
+            KeymasterArguments keymasterArgs) {
+        // NOTE: Keymaster doesn't always return an IV, even if it's used.
+        byte[] returnedIv = keymasterArgs.getBlob(KeymasterDefs.KM_TAG_NONCE, null);
+        if ((returnedIv != null) && (returnedIv.length == 0)) {
+            returnedIv = null;
+        }
+
+        if (mIvUsed) {
+            if (mIv == null) {
+                mIv = returnedIv;
+            } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
+                throw new CryptoOperationException("IV in use differs from provided IV");
+            }
+        } else {
+            if (returnedIv != null) {
+                throw new CryptoOperationException(
+                        "IV in use despite IV not being used by this transformation");
+            }
+        }
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreConnectException.java b/keystore/java/android/security/KeyStoreConnectException.java
new file mode 100644
index 0000000..8ed6e04
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreConnectException.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 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.security;
+
+/**
+ * Indicates a communications error with keystore service.
+ *
+ * @hide
+ */
+public class KeyStoreConnectException extends CryptoOperationException {
+    public KeyStoreConnectException() {
+        super("Failed to communicate with keystore service");
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreCryptoOperation.java b/keystore/java/android/security/KeyStoreCryptoOperation.java
new file mode 100644
index 0000000..19abd05
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreCryptoOperation.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 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.security;
+
+/**
+ * Cryptographic operation backed by {@link KeyStore}.
+ *
+ * @hide
+ */
+public interface KeyStoreCryptoOperation {
+    /**
+     * Gets the KeyStore operation handle of this crypto operation.
+     *
+     * @return handle or {@code null} if the KeyStore operation is not in progress.
+     */
+    Long getOperationHandle();
+}
diff --git a/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
new file mode 100644
index 0000000..1f8b7e4
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2015 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.security;
+
+import android.os.IBinder;
+import android.security.keymaster.OperationResult;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * Helper for streaming a crypto operation's input and output via {@link KeyStore} service's
+ * {@code update} and {@code finish} operations.
+ *
+ * <p>The helper abstracts away to issues that need to be solved in most code that uses KeyStore's
+ * update and finish operations. Firstly, KeyStore's update operation can consume only a limited
+ * amount of data in one go because the operations are marshalled via Binder. Secondly, the update
+ * operation may consume less data than provided, in which case the caller has to buffer the
+ * remainder for next time. The helper exposes {@link #update(byte[], int, int) update} and
+ * {@link #doFinal(byte[], int, int) doFinal} operations which can be used to conveniently implement
+ * various JCA crypto primitives.
+ *
+ * <p>Bidirectional chunked streaming of data via a KeyStore crypto operation is abstracted away as
+ * a {@link Stream} to avoid having this class deal with operation tokens and occasional additional
+ * parameters to {@code update} and {@code final} operations.
+ *
+ * @hide
+ */
+public class KeyStoreCryptoOperationChunkedStreamer {
+
+    /**
+     * Bidirectional chunked data stream over a KeyStore crypto operation.
+     */
+    public interface Stream {
+        /**
+         * Returns the result of the KeyStore {@code update} operation or null if keystore couldn't
+         * be reached.
+         */
+        OperationResult update(byte[] input);
+
+        /**
+         * Returns the result of the KeyStore {@code finish} operation or null if keystore couldn't
+         * be reached.
+         */
+        OperationResult finish();
+    }
+
+    // Binder buffer is about 1MB, but it's shared between all active transactions of the process.
+    // Thus, it's safer to use a much smaller upper bound.
+    private static final int DEFAULT_MAX_CHUNK_SIZE = 64 * 1024;
+    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
+
+    private final Stream mKeyStoreStream;
+    private final int mMaxChunkSize;
+
+    private byte[] mBuffered = EMPTY_BYTE_ARRAY;
+    private int mBufferedOffset;
+    private int mBufferedLength;
+
+    public KeyStoreCryptoOperationChunkedStreamer(Stream operation) {
+        this(operation, DEFAULT_MAX_CHUNK_SIZE);
+    }
+
+    public KeyStoreCryptoOperationChunkedStreamer(Stream operation, int maxChunkSize) {
+        mKeyStoreStream = operation;
+        mMaxChunkSize = maxChunkSize;
+    }
+
+    public byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyStoreException {
+        if (inputLength == 0) {
+            // No input provided
+            return EMPTY_BYTE_ARRAY;
+        }
+
+        ByteArrayOutputStream bufferedOutput = null;
+
+        while (inputLength > 0) {
+            byte[] chunk;
+            int inputBytesInChunk;
+            if ((mBufferedLength + inputLength) > mMaxChunkSize) {
+                // Too much input for one chunk -- extract one max-sized chunk and feed it into the
+                // update operation.
+                inputBytesInChunk = mMaxChunkSize - mBufferedLength;
+                chunk = concat(mBuffered, mBufferedOffset, mBufferedLength,
+                        input, inputOffset, inputBytesInChunk);
+            } else {
+                // All of available input fits into one chunk.
+                if ((mBufferedLength == 0) && (inputOffset == 0)
+                        && (inputLength == input.length)) {
+                    // Nothing buffered and all of input array needs to be fed into the update
+                    // operation.
+                    chunk = input;
+                    inputBytesInChunk = input.length;
+                } else {
+                    // Need to combine buffered data with input data into one array.
+                    inputBytesInChunk = inputLength;
+                    chunk = concat(mBuffered, mBufferedOffset, mBufferedLength,
+                            input, inputOffset, inputBytesInChunk);
+                }
+            }
+            // Update input array references to reflect that some of its bytes are now in mBuffered.
+            inputOffset += inputBytesInChunk;
+            inputLength -= inputBytesInChunk;
+
+            OperationResult opResult = mKeyStoreStream.update(chunk);
+            if (opResult == null) {
+                throw new KeyStoreConnectException();
+            } else if (opResult.resultCode != KeyStore.NO_ERROR) {
+                throw KeyStore.getKeyStoreException(opResult.resultCode);
+            }
+
+            if (opResult.inputConsumed == chunk.length) {
+                // The whole chunk was consumed
+                mBuffered = EMPTY_BYTE_ARRAY;
+                mBufferedOffset = 0;
+                mBufferedLength = 0;
+            } else if (opResult.inputConsumed == 0) {
+                // Nothing was consumed. More input needed.
+                if (inputLength > 0) {
+                    // More input is available, but it wasn't included into the previous chunk
+                    // because the chunk reached its maximum permitted size.
+                    // Shouldn't have happened.
+                    throw new CryptoOperationException("Nothing consumed from max-sized chunk: "
+                            + chunk.length + " bytes");
+                }
+                mBuffered = chunk;
+                mBufferedOffset = 0;
+                mBufferedLength = chunk.length;
+            } else if (opResult.inputConsumed < chunk.length) {
+                // The chunk was consumed only partially -- buffer the rest of the chunk
+                mBuffered = chunk;
+                mBufferedOffset = opResult.inputConsumed;
+                mBufferedLength = chunk.length - opResult.inputConsumed;
+            } else {
+                throw new CryptoOperationException("Consumed more than provided: "
+                        + opResult.inputConsumed + ", provided: " + chunk.length);
+            }
+
+            if ((opResult.output != null) && (opResult.output.length > 0)) {
+                if (inputLength > 0) {
+                    // More output might be produced in this loop -- buffer the current output
+                    if (bufferedOutput == null) {
+                        bufferedOutput = new ByteArrayOutputStream();
+                        try {
+                            bufferedOutput.write(opResult.output);
+                        } catch (IOException e) {
+                            throw new CryptoOperationException("Failed to buffer output", e);
+                        }
+                    }
+                } else {
+                    // No more output will be produced in this loop
+                    if (bufferedOutput == null) {
+                        // No previously buffered output
+                        return opResult.output;
+                    } else {
+                        // There was some previously buffered output
+                        try {
+                            bufferedOutput.write(opResult.output);
+                        } catch (IOException e) {
+                            throw new CryptoOperationException("Failed to buffer output", e);
+                        }
+                        return bufferedOutput.toByteArray();
+                    }
+                }
+            }
+        }
+
+        if (bufferedOutput == null) {
+            // No output produced
+            return EMPTY_BYTE_ARRAY;
+        } else {
+            return bufferedOutput.toByteArray();
+        }
+    }
+
+    public byte[] doFinal(byte[] input, int inputOffset, int inputLength)
+            throws KeyStoreException {
+        if (inputLength == 0) {
+            // No input provided -- simplify the rest of the code
+            input = EMPTY_BYTE_ARRAY;
+            inputOffset = 0;
+        }
+
+        // Flush all buffered input and provided input into keystore/keymaster.
+        byte[] output = update(input, inputOffset, inputLength);
+        output = concat(output, flush());
+
+        OperationResult opResult = mKeyStoreStream.finish();
+        if (opResult == null) {
+            throw new KeyStoreConnectException();
+        } else if (opResult.resultCode != KeyStore.NO_ERROR) {
+            throw KeyStore.getKeyStoreException(opResult.resultCode);
+        }
+
+        return concat(output, opResult.output);
+    }
+
+    /**
+     * Passes all of buffered input into the the KeyStore operation (via the {@code update}
+     * operation) and returns output.
+     */
+    public byte[] flush() throws KeyStoreException {
+        if (mBufferedLength <= 0) {
+            return EMPTY_BYTE_ARRAY;
+        }
+
+        byte[] chunk = subarray(mBuffered, mBufferedOffset, mBufferedLength);
+        mBuffered = EMPTY_BYTE_ARRAY;
+        mBufferedLength = 0;
+        mBufferedOffset = 0;
+
+        OperationResult opResult = mKeyStoreStream.update(chunk);
+        if (opResult == null) {
+            throw new KeyStoreConnectException();
+        } else if (opResult.resultCode != KeyStore.NO_ERROR) {
+            throw KeyStore.getKeyStoreException(opResult.resultCode);
+        }
+
+        if (opResult.inputConsumed < chunk.length) {
+            throw new CryptoOperationException("Keystore failed to consume all input. Provided: "
+                    + chunk.length + ", consumed: " + opResult.inputConsumed);
+        } else if (opResult.inputConsumed > chunk.length) {
+            throw new CryptoOperationException("Keystore consumed more input than provided"
+                    + " . Provided: " + chunk.length + ", consumed: " + opResult.inputConsumed);
+        }
+
+        return (opResult.output != null) ? opResult.output : EMPTY_BYTE_ARRAY;
+    }
+
+    private static byte[] concat(byte[] arr1, byte[] arr2) {
+        if ((arr1 == null) || (arr1.length == 0)) {
+            return arr2;
+        } else if ((arr2 == null) || (arr2.length == 0)) {
+            return arr1;
+        } else {
+            byte[] result = new byte[arr1.length + arr2.length];
+            System.arraycopy(arr1, 0, result, 0, arr1.length);
+            System.arraycopy(arr2, 0, result, arr1.length, arr2.length);
+            return result;
+        }
+    }
+
+    private static byte[] concat(byte[] arr1, int offset1, int len1, byte[] arr2, int offset2,
+            int len2) {
+        if (len1 == 0) {
+            return subarray(arr2, offset2, len2);
+        } else if (len2 == 0) {
+            return subarray(arr1, offset1, len1);
+        } else {
+            byte[] result = new byte[len1 + len2];
+            System.arraycopy(arr1, offset1, result, 0, len1);
+            System.arraycopy(arr2, offset2, result, len1, len2);
+            return result;
+        }
+    }
+
+    private static byte[] subarray(byte[] arr, int offset, int len) {
+        if (len == 0) {
+            return EMPTY_BYTE_ARRAY;
+        }
+        if ((offset == 0) && (len == arr.length)) {
+            return arr;
+        }
+        byte[] result = new byte[len];
+        System.arraycopy(arr, offset, result, 0, len);
+        return result;
+    }
+
+    /**
+     * Main data stream via a KeyStore streaming operation.
+     *
+     * <p>For example, for an encryption operation, this is the stream through which plaintext is
+     * provided and ciphertext is obtained.
+     */
+    public static class MainDataStream implements Stream {
+
+        private final KeyStore mKeyStore;
+        private final IBinder mOperationToken;
+
+        public MainDataStream(KeyStore keyStore, IBinder operationToken) {
+            mKeyStore = keyStore;
+            mOperationToken = operationToken;
+        }
+
+        @Override
+        public OperationResult update(byte[] input) {
+            return mKeyStore.update(mOperationToken, null, input);
+        }
+
+        @Override
+        public OperationResult finish() {
+            return mKeyStore.finish(mOperationToken, null, null);
+        }
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreException.java b/keystore/java/android/security/KeyStoreException.java
new file mode 100644
index 0000000..88e768c
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 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.security;
+
+/**
+ * KeyStore/keymaster exception with positive error codes coming from the KeyStore and negative
+ * ones from keymaster.
+ *
+ * @hide
+ */
+public class KeyStoreException extends Exception {
+
+    private final int mErrorCode;
+
+    public KeyStoreException(int errorCode, String message) {
+        super(message);
+        mErrorCode = errorCode;
+    }
+
+    public int getErrorCode() {
+        return mErrorCode;
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreHmacSpi.java b/keystore/java/android/security/KeyStoreHmacSpi.java
new file mode 100644
index 0000000..a5864a4
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreHmacSpi.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2015 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.security;
+
+import android.os.IBinder;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keymaster.OperationResult;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.MacSpi;
+
+/**
+ * {@link MacSpi} which provides HMAC implementations backed by Android KeyStore.
+ *
+ * @hide
+ */
+public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOperation {
+
+    public static class HmacSHA256 extends KeyStoreHmacSpi {
+        public HmacSHA256() {
+            super(KeyStoreKeyConstraints.Digest.SHA256, 256 / 8);
+        }
+    }
+
+    private final KeyStore mKeyStore = KeyStore.getInstance();
+    private final @KeyStoreKeyConstraints.DigestEnum int mDigest;
+    private final int mMacSizeBytes;
+
+    private String mKeyAliasInKeyStore;
+
+    // The fields below are reset by the engineReset operation.
+    private KeyStoreCryptoOperationChunkedStreamer mChunkedStreamer;
+    private IBinder mOperationToken;
+    private Long mOperationHandle;
+
+    protected KeyStoreHmacSpi(@KeyStoreKeyConstraints.DigestEnum int digest, int macSizeBytes) {
+        mDigest = digest;
+        mMacSizeBytes = macSizeBytes;
+    }
+
+    @Override
+    protected int engineGetMacLength() {
+        return mMacSizeBytes;
+    }
+
+    @Override
+    protected void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException,
+            InvalidAlgorithmParameterException {
+        if (key == null) {
+            throw new InvalidKeyException("key == null");
+        } else if (!(key instanceof KeyStoreSecretKey)) {
+            throw new InvalidKeyException(
+                    "Only Android KeyStore secret keys supported. Key: " + key);
+        }
+
+        if (params != null) {
+            throw new InvalidAlgorithmParameterException(
+                    "Unsupported algorithm parameters: " + params);
+        }
+
+        mKeyAliasInKeyStore = ((KeyStoreSecretKey) key).getAlias();
+        if (mKeyAliasInKeyStore == null) {
+            throw new InvalidKeyException("Key's KeyStore alias not known");
+        }
+        engineReset();
+        ensureKeystoreOperationInitialized();
+    }
+
+    @Override
+    protected void engineReset() {
+        IBinder operationToken = mOperationToken;
+        if (operationToken != null) {
+            mOperationToken = null;
+            mKeyStore.abort(operationToken);
+        }
+        mOperationHandle = null;
+        mChunkedStreamer = null;
+    }
+
+    private void ensureKeystoreOperationInitialized() {
+        if (mChunkedStreamer != null) {
+            return;
+        }
+        if (mKeyAliasInKeyStore == null) {
+            throw new IllegalStateException("Not initialized");
+        }
+
+        KeymasterArguments keymasterArgs = new KeymasterArguments();
+        keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeyStoreKeyConstraints.Algorithm.HMAC);
+        keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mDigest);
+
+        OperationResult opResult = mKeyStore.begin(mKeyAliasInKeyStore,
+                KeymasterDefs.KM_PURPOSE_SIGN,
+                true,
+                keymasterArgs,
+                null,
+                new KeymasterArguments());
+        if (opResult == null) {
+            throw new KeyStoreConnectException();
+        } else if (opResult.resultCode != KeyStore.NO_ERROR) {
+            throw KeyStore.getCryptoOperationException(opResult.resultCode);
+        }
+        if (opResult.token == null) {
+            throw new CryptoOperationException("Keystore returned null operation token");
+        }
+        mOperationToken = opResult.token;
+        mOperationHandle = opResult.operationHandle;
+        mChunkedStreamer = new KeyStoreCryptoOperationChunkedStreamer(
+                new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
+                        mKeyStore, mOperationToken));
+    }
+
+    @Override
+    protected void engineUpdate(byte input) {
+        engineUpdate(new byte[] {input}, 0, 1);
+    }
+
+    @Override
+    protected void engineUpdate(byte[] input, int offset, int len) {
+        ensureKeystoreOperationInitialized();
+
+        byte[] output;
+        try {
+            output = mChunkedStreamer.update(input, offset, len);
+        } catch (KeyStoreException e) {
+            throw KeyStore.getCryptoOperationException(e);
+        }
+        if ((output != null) && (output.length != 0)) {
+            throw new CryptoOperationException("Update operation unexpectedly produced output");
+        }
+    }
+
+    @Override
+    protected byte[] engineDoFinal() {
+        ensureKeystoreOperationInitialized();
+
+        byte[] result;
+        try {
+            result = mChunkedStreamer.doFinal(null, 0, 0);
+        } catch (KeyStoreException e) {
+            throw KeyStore.getCryptoOperationException(e);
+        }
+
+        engineReset();
+        return result;
+    }
+
+    @Override
+    public void finalize() throws Throwable {
+        try {
+            IBinder operationToken = mOperationToken;
+            if (operationToken != null) {
+                mKeyStore.abort(operationToken);
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+
+    @Override
+    public Long getOperationHandle() {
+        return mOperationHandle;
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreKeyCharacteristics.java b/keystore/java/android/security/KeyStoreKeyCharacteristics.java
new file mode 100644
index 0000000..543b5d8
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreKeyCharacteristics.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 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.security;
+
+import android.annotation.IntDef;
+import android.security.keymaster.KeymasterDefs;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Characteristics of {@code AndroidKeyStore} keys.
+ *
+ * @hide
+ */
+public abstract class KeyStoreKeyCharacteristics {
+    private KeyStoreKeyCharacteristics() {}
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({Origin.GENERATED_INSIDE_TEE, Origin.GENERATED_OUTSIDE_OF_TEE, Origin.IMPORTED})
+    public @interface OriginEnum {}
+
+    /**
+     * Origin of the key.
+     */
+    public static abstract class Origin {
+        private Origin() {}
+
+        /** Key was generated inside a TEE. */
+        public static final int GENERATED_INSIDE_TEE = 1;
+
+        /** Key was generated outside of a TEE. */
+        public static final int GENERATED_OUTSIDE_OF_TEE = 2;
+
+        /** Key was imported. */
+        public static final int IMPORTED = 0;
+
+        /**
+         * @hide
+         */
+        public static @OriginEnum int fromKeymaster(int origin) {
+            switch (origin) {
+                case KeymasterDefs.KM_ORIGIN_HARDWARE:
+                    return GENERATED_INSIDE_TEE;
+                case KeymasterDefs.KM_ORIGIN_SOFTWARE:
+                    return GENERATED_OUTSIDE_OF_TEE;
+                case KeymasterDefs.KM_ORIGIN_IMPORTED:
+                    return IMPORTED;
+                default:
+                    throw new IllegalArgumentException("Unknown origin: " + origin);
+            }
+        }
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreKeyConstraints.java b/keystore/java/android/security/KeyStoreKeyConstraints.java
new file mode 100644
index 0000000..c27ccb1
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreKeyConstraints.java
@@ -0,0 +1,609 @@
+/*
+ * Copyright (C) 2015 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.security;
+
+import android.annotation.IntDef;
+import android.security.keymaster.KeymasterDefs;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * Constraints for {@code AndroidKeyStore} keys.
+ *
+ * @hide
+ */
+public abstract class KeyStoreKeyConstraints {
+    private KeyStoreKeyConstraints() {}
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag=true, value={Purpose.ENCRYPT, Purpose.DECRYPT, Purpose.SIGN, Purpose.VERIFY})
+    public @interface PurposeEnum {}
+
+    /**
+     * Purpose of key.
+     */
+    public static abstract class Purpose {
+        private Purpose() {}
+
+        /**
+         * Purpose: encryption.
+         */
+        public static final int ENCRYPT = 1 << 0;
+
+        /**
+         * Purpose: decryption.
+         */
+        public static final int DECRYPT = 1 << 1;
+
+        /**
+         * Purpose: signing.
+         */
+        public static final int SIGN = 1 << 2;
+
+        /**
+         * Purpose: signature verification.
+         */
+        public static final int VERIFY = 1 << 3;
+
+        /**
+         * Number of flags defined above. Needs to be kept in sync with the flags above.
+         */
+        private static final int VALUE_COUNT = 4;
+
+        /**
+         * @hide
+         */
+        public static int toKeymaster(@PurposeEnum int purpose) {
+            switch (purpose) {
+                case ENCRYPT:
+                    return KeymasterDefs.KM_PURPOSE_ENCRYPT;
+                case DECRYPT:
+                    return KeymasterDefs.KM_PURPOSE_DECRYPT;
+                case SIGN:
+                    return KeymasterDefs.KM_PURPOSE_SIGN;
+                case VERIFY:
+                    return KeymasterDefs.KM_PURPOSE_VERIFY;
+                default:
+                    throw new IllegalArgumentException("Unknown purpose: " + purpose);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static @PurposeEnum int fromKeymaster(int purpose) {
+            switch (purpose) {
+                case KeymasterDefs.KM_PURPOSE_ENCRYPT:
+                    return ENCRYPT;
+                case KeymasterDefs.KM_PURPOSE_DECRYPT:
+                    return DECRYPT;
+                case KeymasterDefs.KM_PURPOSE_SIGN:
+                    return SIGN;
+                case KeymasterDefs.KM_PURPOSE_VERIFY:
+                    return VERIFY;
+                default:
+                    throw new IllegalArgumentException("Unknown purpose: " + purpose);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static int[] allToKeymaster(int purposes) {
+            int[] result = new int[VALUE_COUNT];
+            int resultCount = 0;
+            int purpose = 1;
+            for (int i = 0; i < 32; i++) {
+                if ((purposes & 1) != 0) {
+                    result[resultCount] = toKeymaster(purpose);
+                    resultCount++;
+                }
+                purposes >>>= 1;
+                purpose <<= 1;
+                if (purposes == 0) {
+                    break;
+                }
+            }
+            return Arrays.copyOf(result, resultCount);
+        }
+
+        /**
+         * @hide
+         */
+        public static @PurposeEnum int allFromKeymaster(Collection<Integer> purposes) {
+            @PurposeEnum int result = 0;
+            for (int keymasterPurpose : purposes) {
+                result |= fromKeymaster(keymasterPurpose);
+            }
+            return result;
+        }
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({Algorithm.AES, Algorithm.HMAC})
+    public @interface AlgorithmEnum {}
+
+    /**
+     * Key algorithm.
+     */
+    public static abstract class Algorithm {
+        private Algorithm() {}
+
+        /**
+         * Key algorithm: AES.
+         */
+        public static final int AES = 0;
+
+        /**
+         * Key algorithm: HMAC.
+         */
+        public static final int HMAC = 1;
+
+        /**
+         * @hide
+         */
+        public static int toKeymaster(@AlgorithmEnum int algorithm) {
+            switch (algorithm) {
+                case AES:
+                    return KeymasterDefs.KM_ALGORITHM_AES;
+                case HMAC:
+                    return KeymasterDefs.KM_ALGORITHM_HMAC;
+                default:
+                    throw new IllegalArgumentException("Unknown algorithm: " + algorithm);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static @AlgorithmEnum int fromKeymaster(int algorithm) {
+            switch (algorithm) {
+                case KeymasterDefs.KM_ALGORITHM_AES:
+                    return AES;
+                case KeymasterDefs.KM_ALGORITHM_HMAC:
+                    return HMAC;
+                default:
+                    throw new IllegalArgumentException("Unknown algorithm: " + algorithm);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static String toString(@AlgorithmEnum int algorithm) {
+            switch (algorithm) {
+                case AES:
+                    return "AES";
+                case HMAC:
+                    return "HMAC";
+                default:
+                    throw new IllegalArgumentException("Unknown algorithm: " + algorithm);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static @AlgorithmEnum int fromJCASecretKeyAlgorithm(String algorithm) {
+            if (algorithm == null) {
+                throw new NullPointerException("algorithm == null");
+            } else  if ("AES".equalsIgnoreCase(algorithm)) {
+                return AES;
+            } else if (algorithm.toLowerCase(Locale.US).startsWith("hmac")) {
+                return HMAC;
+            } else {
+                throw new IllegalArgumentException(
+                        "Unsupported secret key algorithm: " + algorithm);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static String toJCASecretKeyAlgorithm(@AlgorithmEnum int algorithm,
+                @DigestEnum Integer digest) {
+            switch (algorithm) {
+                case AES:
+                    return "AES";
+                case HMAC:
+                    if (digest == null) {
+                        throw new IllegalArgumentException("HMAC digest not specified");
+                    }
+                    switch (digest) {
+                        case Digest.SHA256:
+                            return "HmacSHA256";
+                        default:
+                            throw new IllegalArgumentException(
+                                    "Unsupported HMAC digest: " + digest);
+                    }
+                default:
+                    throw new IllegalArgumentException("Unsupported key algorithm: " + algorithm);
+            }
+        }
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({Padding.NONE, Padding.ZERO, Padding.PKCS7})
+    public @interface PaddingEnum {}
+
+    /**
+     * Padding for signing and encryption.
+     */
+    public static abstract class Padding {
+        private Padding() {}
+
+        /**
+         * No padding.
+         */
+        public static final int NONE = 0;
+
+        /**
+         * Pad with zeros.
+         */
+        public static final int ZERO = 1;
+
+        /**
+         * PKCS#7 padding.
+         */
+        public static final int PKCS7 = 2;
+
+        /**
+         * @hide
+         */
+        public static int toKeymaster(int padding) {
+            switch (padding) {
+                case NONE:
+                    return KeymasterDefs.KM_PAD_NONE;
+                case ZERO:
+                    return KeymasterDefs.KM_PAD_ZERO;
+                case PKCS7:
+                    return KeymasterDefs.KM_PAD_PKCS7;
+                default:
+                    throw new IllegalArgumentException("Unknown padding: " + padding);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static @PaddingEnum int fromKeymaster(int padding) {
+            switch (padding) {
+                case KeymasterDefs.KM_PAD_NONE:
+                    return NONE;
+                case KeymasterDefs.KM_PAD_ZERO:
+                    return ZERO;
+                case KeymasterDefs.KM_PAD_PKCS7:
+                    return PKCS7;
+                default:
+                    throw new IllegalArgumentException("Unknown padding: " + padding);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static String toString(@PaddingEnum int padding) {
+            switch (padding) {
+                case NONE:
+                    return "NONE";
+                case ZERO:
+                    return "ZERO";
+                case PKCS7:
+                    return "PKCS#7";
+                default:
+                    throw new IllegalArgumentException("Unknown padding: " + padding);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static @PaddingEnum int fromJCAPadding(String padding) {
+            String paddingLower = padding.toLowerCase(Locale.US);
+            if ("nopadding".equals(paddingLower)) {
+                return NONE;
+            } else if ("pkcs7padding".equals(paddingLower)) {
+                return PKCS7;
+            } else {
+                throw new IllegalArgumentException("Unknown padding: " + padding);
+            }
+        }
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({Digest.NONE, Digest.SHA256})
+    public @interface DigestEnum {}
+
+    /**
+     * Digests that can be used with a key when signing or generating Message Authentication
+     * Codes (MACs).
+     */
+    public static abstract class Digest {
+        private Digest() {}
+
+        /**
+         * No digest: sign/authenticate the raw message.
+         */
+        public static final int NONE = 0;
+
+        /**
+         * SHA-256 digest.
+         */
+        public static final int SHA256 = 1;
+
+        /**
+         * @hide
+         */
+        public static String toString(@DigestEnum int digest) {
+            switch (digest) {
+                case NONE:
+                    return "NONE";
+                case SHA256:
+                    return "SHA256";
+                default:
+                    throw new IllegalArgumentException("Unknown digest: " + digest);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static int toKeymaster(@DigestEnum int digest) {
+            switch (digest) {
+                case NONE:
+                    return KeymasterDefs.KM_DIGEST_NONE;
+                case SHA256:
+                    return KeymasterDefs.KM_DIGEST_SHA_2_256;
+                default:
+                    throw new IllegalArgumentException("Unknown digest: " + digest);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static @DigestEnum int fromKeymaster(int digest) {
+            switch (digest) {
+                case KeymasterDefs.KM_DIGEST_NONE:
+                    return NONE;
+                case KeymasterDefs.KM_DIGEST_SHA_2_256:
+                    return SHA256;
+                default:
+                    throw new IllegalArgumentException("Unknown digest: " + digest);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static @DigestEnum Integer fromJCASecretKeyAlgorithm(String algorithm) {
+            String algorithmLower = algorithm.toLowerCase(Locale.US);
+            if (algorithmLower.startsWith("hmac")) {
+                if ("hmacsha256".equals(algorithmLower)) {
+                    return SHA256;
+                } else {
+                    throw new IllegalArgumentException("Unsupported digest: "
+                            + algorithmLower.substring("hmac".length()));
+                }
+            } else {
+                return null;
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static String toJCASignatureAlgorithmDigest(@DigestEnum int digest) {
+            switch (digest) {
+                case NONE:
+                    return "NONE";
+                case SHA256:
+                    return "SHA256";
+                default:
+                    throw new IllegalArgumentException("Unknown digest: " + digest);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static Integer getOutputSizeBytes(@DigestEnum int digest) {
+            switch (digest) {
+                case NONE:
+                    return null;
+                case SHA256:
+                    return 256 / 8;
+                default:
+                    throw new IllegalArgumentException("Unknown digest: " + digest);
+            }
+        }
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({BlockMode.ECB, BlockMode.CBC, BlockMode.CTR})
+    public @interface BlockModeEnum {}
+
+    /**
+     * Block modes that can be used when encrypting/decrypting using a key.
+     */
+    public static abstract class BlockMode {
+        private BlockMode() {}
+
+        /** Electronic Codebook (ECB) block mode. */
+        public static final int ECB = 0;
+
+        /** Cipher Block Chaining (CBC) block mode. */
+        public static final int CBC = 1;
+
+        /** Counter (CTR) block mode. */
+        public static final int CTR = 2;
+
+        /**
+         * @hide
+         */
+        public static int toKeymaster(@BlockModeEnum int mode) {
+            switch (mode) {
+                case ECB:
+                    return KeymasterDefs.KM_MODE_ECB;
+                case CBC:
+                    return KeymasterDefs.KM_MODE_CBC;
+                case CTR:
+                    return KeymasterDefs.KM_MODE_CTR;
+                default:
+                    throw new IllegalArgumentException("Unknown block mode: " + mode);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static @BlockModeEnum int fromKeymaster(int mode) {
+            switch (mode) {
+                case KeymasterDefs.KM_MODE_ECB:
+                    return ECB;
+                case KeymasterDefs.KM_MODE_CBC:
+                    return CBC;
+                case KeymasterDefs.KM_MODE_CTR:
+                    return CTR;
+                default:
+                    throw new IllegalArgumentException("Unknown block mode: " + mode);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static String toString(@BlockModeEnum int mode) {
+            switch (mode) {
+                case ECB:
+                    return "ECB";
+                case CBC:
+                    return "CBC";
+                case CTR:
+                    return "CTR";
+                default:
+                    throw new IllegalArgumentException("Unknown block mode: " + mode);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static @BlockModeEnum int fromJCAMode(String mode) {
+            String modeLower = mode.toLowerCase(Locale.US);
+            if ("ecb".equals(modeLower)) {
+                return ECB;
+            } else if ("cbc".equals(modeLower)) {
+                return CBC;
+            } else if ("ctr".equals(modeLower)) {
+                return CTR;
+            } else {
+                throw new IllegalArgumentException("Unknown block mode: " + mode);
+            }
+        }
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({UserAuthenticator.LOCK_SCREEN})
+    public @interface UserAuthenticatorEnum {}
+
+    /**
+     * User authenticators which can be used to restrict/protect access to keys.
+     */
+    public static abstract class UserAuthenticator {
+        private UserAuthenticator() {}
+
+        /** Lock screen. */
+        public static final int LOCK_SCREEN = 1;
+
+        /**
+         * @hide
+         */
+        public static int toKeymaster(@UserAuthenticatorEnum int userAuthenticator) {
+            switch (userAuthenticator) {
+                case LOCK_SCREEN:
+                    return LOCK_SCREEN;
+                default:
+                    throw new IllegalArgumentException(
+                            "Unknown user authenticator: " + userAuthenticator);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static @UserAuthenticatorEnum int fromKeymaster(int userAuthenticator) {
+            switch (userAuthenticator) {
+                case LOCK_SCREEN:
+                    return LOCK_SCREEN;
+                default:
+                    throw new IllegalArgumentException(
+                            "Unknown user authenticator: " + userAuthenticator);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static int allToKeymaster(Set<Integer> userAuthenticators) {
+            int result = 0;
+            for (@UserAuthenticatorEnum int userAuthenticator : userAuthenticators) {
+                result |= toKeymaster(userAuthenticator);
+            }
+            return result;
+        }
+
+        /**
+         * @hide
+         */
+        public static Set<Integer> allFromKeymaster(int userAuthenticators) {
+            int userAuthenticator = 1;
+            Set<Integer> result = null;
+            while (userAuthenticators != 0) {
+                if ((userAuthenticators & 1) != 0) {
+                    if (result == null) {
+                        result = new HashSet<Integer>();
+                    }
+                    result.add(fromKeymaster(userAuthenticator));
+                }
+                userAuthenticators >>>= 1;
+                userAuthenticator <<= 1;
+            }
+            return (result != null) ? result : Collections.<Integer>emptySet();
+        }
+
+        /**
+         * @hide
+         */
+        public static String toString(@UserAuthenticatorEnum int userAuthenticator) {
+            switch (userAuthenticator) {
+                case LOCK_SCREEN:
+                    return "LOCK_SCREEN";
+                default:
+                    throw new IllegalArgumentException(
+                            "Unknown user authenticator: " + userAuthenticator);
+            }
+        }
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
new file mode 100644
index 0000000..c9c9bd8
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2015 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.security;
+
+import android.security.keymaster.KeyCharacteristics;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Date;
+
+import javax.crypto.KeyGeneratorSpi;
+import javax.crypto.SecretKey;
+
+/**
+ * {@link KeyGeneratorSpi} backed by Android KeyStore.
+ *
+ * @hide
+ */
+public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
+
+    public static class AES extends KeyStoreKeyGeneratorSpi {
+        public AES() {
+            super(KeyStoreKeyConstraints.Algorithm.AES, 128);
+        }
+    }
+
+    public static class HmacSHA256 extends KeyStoreKeyGeneratorSpi {
+        public HmacSHA256() {
+            super(KeyStoreKeyConstraints.Algorithm.HMAC,
+                    KeyStoreKeyConstraints.Digest.SHA256,
+                    KeyStoreKeyConstraints.Digest.getOutputSizeBytes(
+                            KeyStoreKeyConstraints.Digest.SHA256) * 8);
+        }
+    }
+
+    private final KeyStore mKeyStore = KeyStore.getInstance();
+    private final @KeyStoreKeyConstraints.AlgorithmEnum int mAlgorithm;
+    private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
+    private final int mDefaultKeySizeBits;
+
+    private KeyGeneratorSpec mSpec;
+    private SecureRandom mRng;
+
+    protected KeyStoreKeyGeneratorSpi(
+            @KeyStoreKeyConstraints.AlgorithmEnum int algorithm,
+            int defaultKeySizeBits) {
+        this(algorithm, null, defaultKeySizeBits);
+    }
+
+    protected KeyStoreKeyGeneratorSpi(
+            @KeyStoreKeyConstraints.AlgorithmEnum int algorithm,
+            @KeyStoreKeyConstraints.DigestEnum Integer digest,
+            int defaultKeySizeBits) {
+        mAlgorithm = algorithm;
+        mDigest = digest;
+        mDefaultKeySizeBits = defaultKeySizeBits;
+    }
+
+    @Override
+    protected SecretKey engineGenerateKey() {
+        KeyGeneratorSpec spec = mSpec;
+        if (spec == null) {
+            throw new IllegalStateException("Not initialized");
+        }
+
+        if ((spec.isEncryptionRequired())
+                && (mKeyStore.state() != KeyStore.State.UNLOCKED)) {
+            throw new IllegalStateException(
+                    "Android KeyStore must be in initialized and unlocked state if encryption is"
+                    + " required");
+        }
+
+        KeymasterArguments args = new KeymasterArguments();
+        args.addInt(KeymasterDefs.KM_TAG_ALGORITHM,
+                KeyStoreKeyConstraints.Algorithm.toKeymaster(mAlgorithm));
+        if (mDigest != null) {
+            args.addInt(KeymasterDefs.KM_TAG_DIGEST,
+                    KeyStoreKeyConstraints.Digest.toKeymaster(mDigest));
+            Integer digestOutputSizeBytes =
+                    KeyStoreKeyConstraints.Digest.getOutputSizeBytes(mDigest);
+            if (digestOutputSizeBytes != null) {
+                // TODO: Remove MAC length constraint once Keymaster API no longer requires it.
+                // TODO: Switch to bits instead of bytes, once this is fixed in Keymaster
+                args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, digestOutputSizeBytes);
+            }
+        }
+        if (mAlgorithm == KeyStoreKeyConstraints.Algorithm.HMAC) {
+            if (mDigest == null) {
+                throw new IllegalStateException("Digest algorithm must be specified for key"
+                        + " algorithm " + KeyStoreKeyConstraints.Algorithm.toString(mAlgorithm));
+            }
+        }
+        int keySizeBits = (spec.getKeySize() != null) ? spec.getKeySize() : mDefaultKeySizeBits;
+        args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keySizeBits);
+        @KeyStoreKeyConstraints.PurposeEnum int purposes = (spec.getPurposes() != null)
+                ? spec.getPurposes()
+                : (KeyStoreKeyConstraints.Purpose.ENCRYPT
+                        | KeyStoreKeyConstraints.Purpose.DECRYPT
+                        | KeyStoreKeyConstraints.Purpose.SIGN
+                        | KeyStoreKeyConstraints.Purpose.VERIFY);
+        for (int keymasterPurpose :
+            KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
+            args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
+        }
+        if (spec.getBlockMode() != null) {
+            args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE,
+                    KeyStoreKeyConstraints.BlockMode.toKeymaster(spec.getBlockMode()));
+        }
+        if (spec.getPadding() != null) {
+            args.addInt(KeymasterDefs.KM_TAG_PADDING,
+                    KeyStoreKeyConstraints.Padding.toKeymaster(spec.getPadding()));
+        }
+        if (spec.getMaxUsesPerBoot() != null) {
+            args.addInt(KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT, spec.getMaxUsesPerBoot());
+        }
+        if (spec.getMinSecondsBetweenOperations() != null) {
+            args.addInt(KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS,
+                    spec.getMinSecondsBetweenOperations());
+        }
+        if (spec.getUserAuthenticators().isEmpty()) {
+            args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
+        } else {
+            args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
+                    KeyStoreKeyConstraints.UserAuthenticator.allToKeymaster(
+                            spec.getUserAuthenticators()));
+        }
+        if (spec.getUserAuthenticationValidityDurationSeconds() != null) {
+            args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
+                    spec.getUserAuthenticationValidityDurationSeconds());
+        }
+        args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
+                (spec.getKeyValidityStart() != null)
+                ? spec.getKeyValidityStart() : new Date(0));
+        args.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
+                (spec.getKeyValidityForOriginationEnd() != null)
+                ? spec.getKeyValidityForOriginationEnd() : new Date(Long.MAX_VALUE));
+        args.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
+                (spec.getKeyValidityForConsumptionEnd() != null)
+                ? spec.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE));
+
+        if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
+            || ((purposes & KeyStoreKeyConstraints.Purpose.DECRYPT) != 0)) {
+            // Permit caller-specified IV. This is needed due to the Cipher abstraction.
+            args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
+        }
+
+        byte[] additionalEntropy = null;
+        SecureRandom rng = mRng;
+        if (rng != null) {
+            additionalEntropy = new byte[(keySizeBits + 7) / 8];
+            rng.nextBytes(additionalEntropy);
+        }
+
+        int flags = spec.getFlags();
+        String keyAliasInKeystore = Credentials.USER_SECRET_KEY + spec.getKeystoreAlias();
+        int errorCode = mKeyStore.generateKey(
+                keyAliasInKeystore, args, additionalEntropy, flags, new KeyCharacteristics());
+        if (errorCode != KeyStore.NO_ERROR) {
+            throw KeyStore.getCryptoOperationException(errorCode);
+        }
+        String keyAlgorithmJCA =
+                KeyStoreKeyConstraints.Algorithm.toJCASecretKeyAlgorithm(mAlgorithm, mDigest);
+        return new KeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmJCA);
+    }
+
+    @Override
+    protected void engineInit(SecureRandom random) {
+        throw new UnsupportedOperationException("Cannot initialize without an "
+                + KeyGeneratorSpec.class.getName() + " parameter");
+    }
+
+    @Override
+    protected void engineInit(AlgorithmParameterSpec params, SecureRandom random)
+            throws InvalidAlgorithmParameterException {
+        if ((params == null) || (!(params instanceof KeyGeneratorSpec))) {
+            throw new InvalidAlgorithmParameterException("Cannot initialize without an "
+                    + KeyGeneratorSpec.class.getName() + " parameter");
+        }
+        KeyGeneratorSpec spec = (KeyGeneratorSpec) params;
+        if (spec.getKeystoreAlias() == null) {
+            throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
+        }
+
+        mSpec = spec;
+        mRng = random;
+    }
+
+    @Override
+    protected void engineInit(int keySize, SecureRandom random) {
+        throw new UnsupportedOperationException("Cannot initialize without a "
+                + KeyGeneratorSpec.class.getName() + " parameter");
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreKeySpec.java b/keystore/java/android/security/KeyStoreKeySpec.java
new file mode 100644
index 0000000..ddeefbd
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreKeySpec.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2015 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.security;
+
+import java.security.spec.KeySpec;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Information about a key from the <a href="{@docRoot}training/articles/keystore.html">Android
+ * KeyStore</a>.
+ *
+ * @hide
+ */
+public class KeyStoreKeySpec implements KeySpec {
+    private final String mKeystoreAlias;
+    private final int mKeySize;
+    private final @KeyStoreKeyCharacteristics.OriginEnum int mOrigin;
+    private final Date mKeyValidityStart;
+    private final Date mKeyValidityForOriginationEnd;
+    private final Date mKeyValidityForConsumptionEnd;
+    private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
+    private final @KeyStoreKeyConstraints.AlgorithmEnum int mAlgorithm;
+    private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+    private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
+    private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+    private final Integer mMinSecondsBetweenOperations;
+    private final Integer mMaxUsesPerBoot;
+    private final Set<Integer> mUserAuthenticators;
+    private final Set<Integer> mTeeBackedUserAuthenticators;
+    private final Integer mUserAuthenticationValidityDurationSeconds;
+
+
+    /**
+     * @hide
+     */
+    KeyStoreKeySpec(String keystoreKeyAlias,
+            @KeyStoreKeyCharacteristics.OriginEnum int origin,
+            int keySize, Date keyValidityStart, Date keyValidityForOriginationEnd,
+            Date keyValidityForConsumptionEnd,
+            @KeyStoreKeyConstraints.PurposeEnum int purposes,
+            @KeyStoreKeyConstraints.AlgorithmEnum int algorithm,
+            @KeyStoreKeyConstraints.PaddingEnum Integer padding,
+            @KeyStoreKeyConstraints.DigestEnum Integer digest,
+            @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode,
+            Integer minSecondsBetweenOperations,
+            Integer maxUsesPerBoot,
+            Set<Integer> userAuthenticators,
+            Set<Integer> teeBackedUserAuthenticators,
+            Integer userAuthenticationValidityDurationSeconds) {
+        mKeystoreAlias = keystoreKeyAlias;
+        mOrigin = origin;
+        mKeySize = keySize;
+        mKeyValidityStart = keyValidityStart;
+        mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
+        mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
+        mPurposes = purposes;
+        mAlgorithm = algorithm;
+        mPadding = padding;
+        mDigest = digest;
+        mBlockMode = blockMode;
+        mMinSecondsBetweenOperations = minSecondsBetweenOperations;
+        mMaxUsesPerBoot = maxUsesPerBoot;
+        mUserAuthenticators = (userAuthenticators != null)
+                ? new HashSet<Integer>(userAuthenticators)
+                : Collections.<Integer>emptySet();
+        mTeeBackedUserAuthenticators = (teeBackedUserAuthenticators != null)
+                ? new HashSet<Integer>(teeBackedUserAuthenticators)
+                : Collections.<Integer>emptySet();
+        mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
+    }
+
+    /**
+     * Gets the entry alias under which the key is stored in the {@code AndroidKeyStore}.
+     */
+    public String getKeystoreAlias() {
+        return mKeystoreAlias;
+    }
+
+    /**
+     * Gets the origin of the key.
+     */
+    public @KeyStoreKeyCharacteristics.OriginEnum int getOrigin() {
+        return mOrigin;
+    }
+
+    /**
+     * Gets the key's size in bits.
+     */
+    public int getKeySize() {
+        return mKeySize;
+    }
+
+    /**
+     * Gets the time instant before which the key is not yet valid.
+     *
+     * @return instant or {@code null} if not restricted.
+     */
+    public Date getKeyValidityStart() {
+        return mKeyValidityStart;
+    }
+
+    /**
+     * Gets the time instant after which the key is no long valid for decryption and verification.
+     *
+     * @return instant or {@code null} if not restricted.
+     */
+    public Date getKeyValidityForConsumptionEnd() {
+        return mKeyValidityForConsumptionEnd;
+    }
+
+    /**
+     * Gets the time instant after which the key is no long valid for encryption and signing.
+     *
+     * @return instant or {@code null} if not restricted.
+     */
+    public Date getKeyValidityForOriginationEnd() {
+        return mKeyValidityForOriginationEnd;
+    }
+
+    /**
+     * Gets the set of purposes for which the key can be used.
+     */
+    public @KeyStoreKeyConstraints.PurposeEnum int getPurposes() {
+        return mPurposes;
+    }
+
+    /**
+     * Gets the algorithm of the key.
+     */
+    public @KeyStoreKeyConstraints.AlgorithmEnum int getAlgorithm() {
+        return mAlgorithm;
+    }
+
+    /**
+     * Gets the only block mode with which the key can be used.
+     *
+     * @return block mode or {@code null} if the block mode is not restricted.
+     */
+    public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() {
+        return mBlockMode;
+    }
+
+    /**
+     * Gets the only padding mode with which the key can be used.
+     *
+     * @return padding mode or {@code null} if the padding mode is not restricted.
+     */
+    public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() {
+        return mPadding;
+    }
+
+    /**
+     * Gets the only digest algorithm with which the key can be used.
+     *
+     * @return digest algorithm or {@code null} if the digest algorithm is not restricted.
+     */
+    public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() {
+        return mDigest;
+    }
+
+    /**
+     * Gets the minimum number of seconds that must expire since the most recent use of the key
+     * before it can be used again.
+     *
+     * @return number of seconds or {@code null} if there is no restriction on how frequently a key
+     *         can be used.
+     */
+    public Integer getMinSecondsBetweenOperations() {
+        return mMinSecondsBetweenOperations;
+    }
+
+    /**
+     * Gets the number of times the key can be used without rebooting the device.
+     *
+     * @return maximum number of times or {@code null} if there is no restriction.
+     */
+    public Integer getMaxUsesPerBoot() {
+        return mMaxUsesPerBoot;
+    }
+
+    /**
+     * Gets the user authenticators which protect access to the key. The key can only be used iff
+     * the user has authenticated to at least one of these user authenticators.
+     *
+     * @return user authenticators or empty set if the key can be used without user authentication.
+     */
+    public Set<Integer> getUserAuthenticators() {
+        return new HashSet<Integer>(mUserAuthenticators);
+    }
+
+    /**
+     * Gets the TEE-backed user authenticators which protect access to the key. This is a subset of
+     * the user authentications returned by {@link #getUserAuthenticators()}.
+     */
+    public Set<Integer> getTeeBackedUserAuthenticators() {
+        return new HashSet<Integer>(mTeeBackedUserAuthenticators);
+    }
+
+    /**
+     * Gets the duration of time (seconds) for which the key can be used after the user
+     * successfully authenticates to one of the associated user authenticators.
+     *
+     * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication
+     *         is required for every use of the key.
+     */
+    public Integer getUserAuthenticationValidityDurationSeconds() {
+        return mUserAuthenticationValidityDurationSeconds;
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java
index 2eeb6ad..998e1d9 100644
--- a/keystore/java/android/security/KeyStoreParameter.java
+++ b/keystore/java/android/security/KeyStoreParameter.java
@@ -20,6 +20,10 @@
 
 import java.security.KeyPairGenerator;
 import java.security.KeyStore.ProtectionParameter;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * This provides the optional parameters that can be specified for
@@ -43,9 +47,53 @@
  */
 public final class KeyStoreParameter implements ProtectionParameter {
     private int mFlags;
+    private final Date mKeyValidityStart;
+    private final Date mKeyValidityForOriginationEnd;
+    private final Date mKeyValidityForConsumptionEnd;
+    private final @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
+    private final @KeyStoreKeyConstraints.AlgorithmEnum Integer mAlgorithm;
+    private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+    private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
+    private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+    private final Integer mMinSecondsBetweenOperations;
+    private final Integer mMaxUsesPerBoot;
+    private final Set<Integer> mUserAuthenticators;
+    private final Integer mUserAuthenticationValidityDurationSeconds;
 
-    private KeyStoreParameter(int flags) {
+    private KeyStoreParameter(int flags,
+            Date keyValidityStart,
+            Date keyValidityForOriginationEnd,
+            Date keyValidityForConsumptionEnd,
+            @KeyStoreKeyConstraints.PurposeEnum Integer purposes,
+            @KeyStoreKeyConstraints.AlgorithmEnum Integer algorithm,
+            @KeyStoreKeyConstraints.PaddingEnum Integer padding,
+            @KeyStoreKeyConstraints.DigestEnum Integer digest,
+            @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode,
+            Integer minSecondsBetweenOperations,
+            Integer maxUsesPerBoot,
+            Set<Integer> userAuthenticators,
+            Integer userAuthenticationValidityDurationSeconds) {
+        if ((userAuthenticationValidityDurationSeconds != null)
+                && (userAuthenticationValidityDurationSeconds < 0)) {
+            throw new IllegalArgumentException(
+                    "userAuthenticationValidityDurationSeconds must not be negative");
+        }
+
         mFlags = flags;
+        mKeyValidityStart = keyValidityStart;
+        mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
+        mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
+        mPurposes = purposes;
+        mAlgorithm = algorithm;
+        mPadding = padding;
+        mDigest = digest;
+        mBlockMode = blockMode;
+        mMinSecondsBetweenOperations = minSecondsBetweenOperations;
+        mMaxUsesPerBoot = maxUsesPerBoot;
+        mUserAuthenticators = (userAuthenticators != null)
+                ? new HashSet<Integer>(userAuthenticators)
+                : Collections.<Integer>emptySet();
+        mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
     }
 
     /**
@@ -64,6 +112,141 @@
     }
 
     /**
+     * Gets the time instant before which the key is not yet valid.
+     *
+     * @return instant or {@code null} if not restricted.
+     * @hide
+     */
+    public Date getKeyValidityStart() {
+        return mKeyValidityStart;
+    }
+
+    /**
+     * Gets the time instant after which the key is no long valid for decryption and verification.
+     *
+     * @return instant or {@code null} if not restricted.
+     *
+     * @hide
+     */
+    public Date getKeyValidityForConsumptionEnd() {
+        return mKeyValidityForConsumptionEnd;
+    }
+
+    /**
+     * Gets the time instant after which the key is no long valid for encryption and signing.
+     *
+     * @return instant or {@code null} if not restricted.
+     *
+     * @hide
+     */
+    public Date getKeyValidityForOriginationEnd() {
+        return mKeyValidityForOriginationEnd;
+    }
+
+    /**
+     * Gets the set of purposes for which the key can be used to the provided set of purposes.
+     *
+     * @return set of purposes or {@code null} if the key can be used for any purpose.
+     *
+     * @hide
+     */
+    public @KeyStoreKeyConstraints.PurposeEnum Integer getPurposes() {
+        return mPurposes;
+    }
+
+    /**
+     * Gets the algorithm to which the key is restricted.
+     *
+     * @return algorithm or {@code null} if it's not restricted.
+     * @hide
+     */
+    public @KeyStoreKeyConstraints.AlgorithmEnum Integer getAlgorithm() {
+        return mAlgorithm;
+    }
+
+    /**
+     * Gets the padding scheme to which the key is restricted.
+     *
+     * @return padding scheme or {@code null} if the padding scheme is not restricted.
+     *
+     * @hide
+     */
+    public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() {
+        return mPadding;
+    }
+
+    /**
+     * Gets the digest to which the key is restricted when generating signatures or Message
+     * Authentication Codes (MACs).
+     *
+     * @return digest or {@code null} if the digest is not restricted.
+     *
+     * @hide
+     */
+    public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() {
+        return mDigest;
+    }
+
+    /**
+     * Gets the block mode to which the key is restricted when used for encryption or decryption.
+     *
+     * @return block more or {@code null} if block mode is not restricted.
+     *
+     * @hide
+     */
+    public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() {
+        return mBlockMode;
+    }
+
+    /**
+     * Gets the minimum number of seconds that must expire since the most recent use of the key
+     * before it can be used again.
+     *
+     * @return number of seconds or {@code null} if there is no restriction on how frequently a key
+     *         can be used.
+     *
+     * @hide
+     */
+    public Integer getMinSecondsBetweenOperations() {
+        return mMinSecondsBetweenOperations;
+    }
+
+    /**
+     * Gets the number of times the key can be used without rebooting the device.
+     *
+     * @return maximum number of times or {@code null} if there is no restriction.
+     * @hide
+     */
+    public Integer getMaxUsesPerBoot() {
+        return mMaxUsesPerBoot;
+    }
+
+    /**
+     * Gets the user authenticators which protect access to this key. The key can only be used iff
+     * the user has authenticated to at least one of these user authenticators.
+     *
+     * @return user authenticators or empty set if the key can be used without user authentication.
+     *
+     * @hide
+     */
+    public Set<Integer> getUserAuthenticators() {
+        return new HashSet<Integer>(mUserAuthenticators);
+    }
+
+    /**
+     * Gets the duration of time (seconds) for which this key can be used after the user
+     * successfully authenticates to one of the associated user authenticators.
+     *
+     * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication
+     *         is required for every use of the key.
+     *
+     * @hide
+     */
+    public Integer getUserAuthenticationValidityDurationSeconds() {
+        return mUserAuthenticationValidityDurationSeconds;
+    }
+
+    /**
      * Builder class for {@link KeyStoreParameter} objects.
      * <p>
      * This will build protection parameters for use with the
@@ -82,6 +265,18 @@
      */
     public final static class Builder {
         private int mFlags;
+        private Date mKeyValidityStart;
+        private Date mKeyValidityForOriginationEnd;
+        private Date mKeyValidityForConsumptionEnd;
+        private @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
+        private @KeyStoreKeyConstraints.AlgorithmEnum Integer mAlgorithm;
+        private @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+        private @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
+        private @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+        private Integer mMinSecondsBetweenOperations;
+        private Integer mMaxUsesPerBoot;
+        private Set<Integer> mUserAuthenticators;
+        private Integer mUserAuthenticationValidityDurationSeconds;
 
         /**
          * Creates a new instance of the {@code Builder} with the given
@@ -113,13 +308,216 @@
         }
 
         /**
-         * Builds the instance of the {@code KeyPairGeneratorSpec}.
+         * Sets the time instant before which the key is not yet valid.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityStart(Date startDate) {
+            mKeyValidityStart = startDate;
+            return this;
+        }
+
+        /**
+         * Sets the time instant after which the key is no longer valid.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityStart(Date)
+         * @see #setKeyValidityForConsumptionEnd(Date)
+         * @see #setKeyValidityForOriginationEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityEnd(Date endDate) {
+            setKeyValidityForOriginationEnd(endDate);
+            setKeyValidityForConsumptionEnd(endDate);
+            return this;
+        }
+
+        /**
+         * Sets the time instant after which the key is no longer valid for encryption and signing.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityForConsumptionEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityForOriginationEnd(Date endDate) {
+            mKeyValidityForOriginationEnd = endDate;
+            return this;
+        }
+
+        /**
+         * Sets the time instant after which the key is no longer valid for decryption and
+         * verification.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityForOriginationEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityForConsumptionEnd(Date endDate) {
+            mKeyValidityForConsumptionEnd = endDate;
+            return this;
+        }
+
+        /**
+         * Restricts the purposes for which the key can be used to the provided set of purposes.
+         *
+         * <p>By default, the key can be used for encryption, decryption, signing, and verification.
+         *
+         * @hide
+         */
+        public Builder setPurposes(@KeyStoreKeyConstraints.PurposeEnum int purposes) {
+            mPurposes = purposes;
+            return this;
+        }
+
+        /**
+         * Sets the algorithm of the key.
+         *
+         * <p>The algorithm of symmetric keys can be deduced from the key itself. Thus, explicitly
+         * specifying the algorithm of symmetric keys using this method is not necessary.
+         *
+         * @hide
+         */
+        public Builder setAlgorithm(@KeyStoreKeyConstraints.AlgorithmEnum int algorithm) {
+            mAlgorithm = algorithm;
+            return this;
+        }
+
+        /**
+         * Restricts the key to being used only with the provided padding scheme. Attempts to use
+         * the key with any other padding will be rejected.
+         *
+         * <p>This restriction must be specified for keys which are used for encryption/decryption.
+         *
+         * @hide
+         */
+        public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) {
+            mPadding = padding;
+            return this;
+        }
+
+        /**
+         * Restricts the key to being used only with the provided digest when generating signatures
+         * or Message Authentication Codes (MACs). Attempts to use the key with any other digest
+         * will be rejected.
+         *
+         * <p>For MAC keys, the default is to restrict to the digest specified in the key algorithm
+         * name. For asymmetric signing keys this constraint must be specified because there is no
+         * default.
+         *
+         * @see java.security.Key#getAlgorithm()
+         *
+         * @hide
+         */
+        public Builder setDigest(@KeyStoreKeyConstraints.DigestEnum int digest) {
+            mDigest = digest;
+            return this;
+        }
+
+        /**
+         * Restricts the key to being used only with the provided block mode when encrypting or
+         * decrypting. Attempts to use the key with any other block modes will be rejected.
+         *
+         * <p>This restriction must be specified for keys which are used for encryption/decryption.
+         *
+         * @hide
+         */
+        public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) {
+            mBlockMode = blockMode;
+            return this;
+        }
+
+        /**
+         * Sets the minimum number of seconds that must expire since the most recent use of the key
+         * before it can be used again.
+         *
+         * <p>By default, there is no restriction on how frequently a key can be used.
+         *
+         * @hide
+         */
+        public Builder setMinSecondsBetweenOperations(int seconds) {
+            mMinSecondsBetweenOperations = seconds;
+            return this;
+        }
+
+        /**
+         * Sets the maximum number of times a key can be used without rebooting the device.
+         *
+         * <p>By default, the key can be used for an unlimited number of times.
+         *
+         * @hide
+         */
+        public Builder setMaxUsesPerBoot(int count) {
+            mMaxUsesPerBoot = count;
+            return this;
+        }
+
+        /**
+         * Sets the user authenticators which protect access to this key. The key can only be used
+         * iff the user has authenticated to at least one of these user authenticators.
+         *
+         * <p>By default, the key can be used without user authentication.
+         *
+         * @param userAuthenticators user authenticators or empty list if this key can be accessed
+         *        without user authentication.
+         *
+         * @see #setUserAuthenticationValidityDurationSeconds(int)
+         *
+         * @hide
+         */
+        public Builder setUserAuthenticators(Set<Integer> userAuthenticators) {
+            mUserAuthenticators =
+                    (userAuthenticators != null) ? new HashSet<Integer>(userAuthenticators) : null;
+            return this;
+        }
+
+        /**
+         * Sets the duration of time (seconds) for which this key can be used after the user
+         * successfully authenticates to one of the associated user authenticators.
+         *
+         * <p>By default, the user needs to authenticate for every use of the key.
+         *
+         * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
+         *        every use of the key.
+         *
+         * @see #setUserAuthenticators(Set)
+         *
+         * @hide
+         */
+        public Builder setUserAuthenticationValidityDurationSeconds(int seconds) {
+            mUserAuthenticationValidityDurationSeconds = seconds;
+            return this;
+        }
+
+        /**
+         * Builds the instance of the {@code KeyStoreParameter}.
          *
          * @throws IllegalArgumentException if a required field is missing
-         * @return built instance of {@code KeyPairGeneratorSpec}
+         * @return built instance of {@code KeyStoreParameter}
          */
         public KeyStoreParameter build() {
-            return new KeyStoreParameter(mFlags);
+            return new KeyStoreParameter(mFlags,
+                    mKeyValidityStart,
+                    mKeyValidityForOriginationEnd,
+                    mKeyValidityForConsumptionEnd,
+                    mPurposes,
+                    mAlgorithm,
+                    mPadding,
+                    mDigest,
+                    mBlockMode,
+                    mMinSecondsBetweenOperations,
+                    mMaxUsesPerBoot,
+                    mUserAuthenticators,
+                    mUserAuthenticationValidityDurationSeconds);
         }
     }
 }
diff --git a/keystore/java/android/security/KeyStoreSecretKey.java b/keystore/java/android/security/KeyStoreSecretKey.java
new file mode 100644
index 0000000..7f0e3d3
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreSecretKey.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 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.security;
+
+import javax.crypto.SecretKey;
+
+/**
+ * {@link SecretKey} backed by keystore.
+ *
+ * @hide
+ */
+public class KeyStoreSecretKey implements SecretKey {
+    private final String mAlias;
+    private final String mAlgorithm;
+
+    public KeyStoreSecretKey(String alias, String algorithm) {
+        mAlias = alias;
+        mAlgorithm = algorithm;
+    }
+
+    String getAlias() {
+        return mAlias;
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return mAlgorithm;
+    }
+
+    @Override
+    public String getFormat() {
+        // This key does not export its key material
+        return null;
+    }
+
+    @Override
+    public byte[] getEncoded() {
+        // This key does not export its key material
+        return null;
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
new file mode 100644
index 0000000..f552759
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2015 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.security;
+
+import android.security.keymaster.KeyCharacteristics;
+import android.security.keymaster.KeymasterDefs;
+
+import java.security.InvalidKeyException;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.util.Date;
+import java.util.Set;
+
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactorySpi;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * {@link SecretKeyFactorySpi} backed by Android KeyStore.
+ *
+ * @hide
+ */
+public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
+
+    private final KeyStore mKeyStore = KeyStore.getInstance();
+
+    @Override
+    protected KeySpec engineGetKeySpec(SecretKey key,
+            @SuppressWarnings("rawtypes") Class keySpecClass) throws InvalidKeySpecException {
+        if (keySpecClass == null) {
+            throw new InvalidKeySpecException("keySpecClass == null");
+        }
+        if (!(key instanceof KeyStoreSecretKey)) {
+            throw new InvalidKeySpecException("Only Android KeyStore secret keys supported: " +
+                    ((key != null) ? key.getClass().getName() : "null"));
+        }
+        if (SecretKeySpec.class.isAssignableFrom(keySpecClass)) {
+            throw new InvalidKeySpecException(
+                    "Key material export of Android KeyStore keys is not supported");
+        }
+        if (!KeyStoreKeySpec.class.equals(keySpecClass)) {
+            throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName());
+        }
+        String keyAliasInKeystore = ((KeyStoreSecretKey) key).getAlias();
+        String entryAlias;
+        if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
+            entryAlias = keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
+        } else {
+            throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
+        }
+
+        KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
+        int errorCode =
+                mKeyStore.getKeyCharacteristics(keyAliasInKeystore, null, null, keyCharacteristics);
+        if (errorCode != KeyStore.NO_ERROR) {
+            throw new InvalidKeySpecException("Failed to obtain information about key."
+                    + " Keystore error: " + errorCode);
+        }
+
+        @KeyStoreKeyCharacteristics.OriginEnum Integer origin;
+        int keySize;
+        @KeyStoreKeyConstraints.PurposeEnum int purposes;
+        @KeyStoreKeyConstraints.AlgorithmEnum int algorithm;
+        @KeyStoreKeyConstraints.PaddingEnum Integer padding;
+        @KeyStoreKeyConstraints.DigestEnum Integer digest;
+        @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode;
+        try {
+            origin = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_ORIGIN);
+            if (origin == null) {
+                throw new InvalidKeySpecException("Key origin not available");
+            }
+            origin = KeyStoreKeyCharacteristics.Origin.fromKeymaster(origin);
+            Integer keySizeInteger =
+                    KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_KEY_SIZE);
+            if (keySizeInteger == null) {
+                throw new InvalidKeySpecException("Key size not available");
+            }
+            keySize = keySizeInteger;
+            purposes = KeyStoreKeyConstraints.Purpose.allFromKeymaster(
+                    KeymasterUtils.getInts(keyCharacteristics, KeymasterDefs.KM_TAG_PURPOSE));
+            Integer alg = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_ALGORITHM);
+            if (alg == null) {
+                throw new InvalidKeySpecException("Key algorithm not available");
+            }
+            algorithm = KeyStoreKeyConstraints.Algorithm.fromKeymaster(alg);
+            padding = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_PADDING);
+            if (padding != null) {
+                padding = KeyStoreKeyConstraints.Padding.fromKeymaster(padding);
+            }
+            digest = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_DIGEST);
+            if (digest != null) {
+                digest = KeyStoreKeyConstraints.Digest.fromKeymaster(digest);
+            }
+            blockMode = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_BLOCK_MODE);
+            if (blockMode != null) {
+                blockMode = KeyStoreKeyConstraints.BlockMode.fromKeymaster(blockMode);
+            }
+        } catch (IllegalArgumentException e) {
+            throw new InvalidKeySpecException("Unsupported key characteristic", e);
+        }
+
+        Date keyValidityStart =
+                KeymasterUtils.getDate(keyCharacteristics, KeymasterDefs.KM_TAG_ACTIVE_DATETIME);
+        if ((keyValidityStart != null) && (keyValidityStart.getTime() <= 0)) {
+            keyValidityStart = null;
+        }
+        Date keyValidityForOriginationEnd = KeymasterUtils.getDate(keyCharacteristics,
+                KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME);
+        if ((keyValidityForOriginationEnd != null)
+                && (keyValidityForOriginationEnd.getTime() == Long.MAX_VALUE)) {
+            keyValidityForOriginationEnd = null;
+        }
+        Date keyValidityForConsumptionEnd = KeymasterUtils.getDate(keyCharacteristics,
+                KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME);
+        if ((keyValidityForConsumptionEnd != null)
+                && (keyValidityForConsumptionEnd.getTime() == Long.MAX_VALUE)) {
+            keyValidityForConsumptionEnd = null;
+        }
+
+        int swEnforcedUserAuthenticatorIds =
+                keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
+        int hwEnforcedUserAuthenticatorIds =
+                keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
+        int userAuthenticatorIds = swEnforcedUserAuthenticatorIds | hwEnforcedUserAuthenticatorIds;
+        Set<Integer> userAuthenticators =
+                KeyStoreKeyConstraints.UserAuthenticator.allFromKeymaster(userAuthenticatorIds);
+        Set<Integer> teeBackedUserAuthenticators =
+                KeyStoreKeyConstraints.UserAuthenticator.allFromKeymaster(
+                        hwEnforcedUserAuthenticatorIds);
+
+        return new KeyStoreKeySpec(entryAlias,
+                origin,
+                keySize,
+                keyValidityStart,
+                keyValidityForOriginationEnd,
+                keyValidityForConsumptionEnd,
+                purposes,
+                algorithm,
+                padding,
+                digest,
+                blockMode,
+                KeymasterUtils.getInt(keyCharacteristics,
+                        KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS),
+                KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT),
+                userAuthenticators,
+                teeBackedUserAuthenticators,
+                KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_AUTH_TIMEOUT));
+    }
+
+    @Override
+    protected SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException {
+        throw new UnsupportedOperationException(
+                "Key import into Android KeyStore is not supported");
+    }
+
+    @Override
+    protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException {
+        throw new UnsupportedOperationException(
+                "Key import into Android KeyStore is not supported");
+    }
+}
diff --git a/keystore/java/android/security/KeymasterUtils.java b/keystore/java/android/security/KeymasterUtils.java
new file mode 100644
index 0000000..3143d4d
--- /dev/null
+++ b/keystore/java/android/security/KeymasterUtils.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 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.security;
+
+import android.security.keymaster.KeyCharacteristics;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public abstract class KeymasterUtils {
+    private KeymasterUtils() {}
+
+    public static Integer getInt(KeyCharacteristics keyCharacteristics, int tag) {
+        if (keyCharacteristics.hwEnforced.containsTag(tag)) {
+            return keyCharacteristics.hwEnforced.getInt(tag, -1);
+        } else if (keyCharacteristics.swEnforced.containsTag(tag)) {
+            return keyCharacteristics.swEnforced.getInt(tag, -1);
+        } else {
+            return null;
+        }
+    }
+
+    public static List<Integer> getInts(KeyCharacteristics keyCharacteristics, int tag) {
+        List<Integer> result = new ArrayList<Integer>();
+        result.addAll(keyCharacteristics.hwEnforced.getInts(tag));
+        result.addAll(keyCharacteristics.swEnforced.getInts(tag));
+        return result;
+    }
+
+    public static Date getDate(KeyCharacteristics keyCharacteristics, int tag) {
+        Date result = keyCharacteristics.hwEnforced.getDate(tag, null);
+        if (result == null) {
+            result = keyCharacteristics.swEnforced.getDate(tag, null);
+        }
+        return result;
+    }
+
+    public static boolean getBoolean(KeyCharacteristics keyCharacteristics, int tag) {
+        if (keyCharacteristics.hwEnforced.containsTag(tag)) {
+            return keyCharacteristics.hwEnforced.getBoolean(tag, false);
+        } else {
+            return keyCharacteristics.swEnforced.getBoolean(tag, false);
+        }
+    }
+}
diff --git a/keystore/java/android/security/UserNotAuthenticatedException.java b/keystore/java/android/security/UserNotAuthenticatedException.java
new file mode 100644
index 0000000..e6342ef
--- /dev/null
+++ b/keystore/java/android/security/UserNotAuthenticatedException.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 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.security;
+
+/**
+ * Indicates that a cryptographic operation could not be performed because the user has not been
+ * authenticated recently enough.
+ *
+ * @hide
+ */
+public class UserNotAuthenticatedException extends CryptoOperationException {
+
+    /**
+     * Constructs a new {@code UserNotAuthenticatedException} without detail message and cause.
+     */
+    public UserNotAuthenticatedException() {
+        super("User not authenticated");
+    }
+
+    /**
+     * Constructs a new {@code UserNotAuthenticatedException} with the provided detail message and
+     * no cause.
+     */
+    public UserNotAuthenticatedException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new {@code UserNotAuthenticatedException} with the provided detail message and
+     * cause.
+     */
+    public UserNotAuthenticatedException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/keystore/tests/src/android/security/AndroidKeyStoreTest.java b/keystore/tests/src/android/security/AndroidKeyStoreTest.java
index 9775e64..7a88dee 100644
--- a/keystore/tests/src/android/security/AndroidKeyStoreTest.java
+++ b/keystore/tests/src/android/security/AndroidKeyStoreTest.java
@@ -2127,7 +2127,7 @@
         assertEquals("The keystore size should match expected", 2, mKeyStore.size());
         assertAliases(new String[] { TEST_ALIAS_2, TEST_ALIAS_3 });
 
-        assertTrue(mAndroidKeyStore.delKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_3));
+        assertTrue(mAndroidKeyStore.delete(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_3));
 
         assertEquals("The keystore size should match expected", 1, mKeyStore.size());
         assertAliases(new String[] { TEST_ALIAS_2 });
diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
index f935bb1..c9a140c 100644
--- a/keystore/tests/src/android/security/KeyStoreTest.java
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -25,6 +25,7 @@
 import android.security.keymaster.ExportResult;
 import android.security.keymaster.KeyCharacteristics;
 import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterBlob;
 import android.security.keymaster.KeymasterDefs;
 import android.security.keymaster.OperationResult;
 import android.test.ActivityUnitTestCase;
@@ -36,6 +37,7 @@
 import java.util.Arrays;
 import java.util.Date;
 import java.util.HashSet;
+import java.security.spec.RSAKeyGenParameterSpec;
 
 import android.util.Log;
 import android.util.Base64;
@@ -710,12 +712,13 @@
         args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
         args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
         args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+        args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
         args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 2048);
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, null);
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_DATA, null);
+        args.addLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT,
+                RSAKeyGenParameterSpec.F4.longValue());
 
         KeyCharacteristics outCharacteristics = new KeyCharacteristics();
-        int result = mKeyStore.generateKey(name, args, 0, outCharacteristics);
+        int result = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
         assertEquals("generateRsaKey should succeed", KeyStore.NO_ERROR, result);
         return outCharacteristics;
     }
@@ -724,6 +727,25 @@
         generateRsaKey("test");
         mKeyStore.delete("test");
     }
+
+    public void testGenerateRsaWithEntropy() throws Exception {
+        byte[] entropy = new byte[] {1,2,3,4,5};
+        String name = "test";
+        KeymasterArguments args = new KeymasterArguments();
+        args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
+        args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
+        args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
+        args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+        args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
+        args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 2048);
+        args.addLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT,
+                RSAKeyGenParameterSpec.F4.longValue());
+
+        KeyCharacteristics outCharacteristics = new KeyCharacteristics();
+        int result = mKeyStore.generateKey(name, args, entropy, 0, outCharacteristics);
+        assertEquals("generateKey should succeed", KeyStore.NO_ERROR, result);
+    }
+
     public void testGenerateAndDelete() throws Exception {
         generateRsaKey("test");
         assertTrue("delete should succeed", mKeyStore.delete("test"));
@@ -741,6 +763,7 @@
 
     public void testAppId() throws Exception {
         String name = "test";
+        byte[] id = new byte[] {0x01, 0x02, 0x03};
         KeymasterArguments args = new KeymasterArguments();
         args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
         args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
@@ -748,18 +771,20 @@
         args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
         args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 2048);
         args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_ECB);
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, new byte[] {0x01, 0x02, 0x03});
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_DATA, null);
+        args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
+        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, id);
+        args.addLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT,
+                RSAKeyGenParameterSpec.F4.longValue());
 
         KeyCharacteristics outCharacteristics = new KeyCharacteristics();
-        int result = mKeyStore.generateKey(name, args, 0, outCharacteristics);
+        int result = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
         assertEquals("generateRsaKey should succeed", KeyStore.NO_ERROR, result);
         assertEquals("getKeyCharacteristics should fail without application ID",
                 KeymasterDefs.KM_ERROR_INVALID_KEY_BLOB,
                 mKeyStore.getKeyCharacteristics(name, null, null, outCharacteristics));
         assertEquals("getKeyCharacteristics should succeed with application ID",
                 KeyStore.NO_ERROR,
-                mKeyStore.getKeyCharacteristics(name, new byte[] {0x01, 0x02, 0x03}, null,
+                mKeyStore.getKeyCharacteristics(name, new KeymasterBlob(id), null,
                     outCharacteristics));
     }
 
@@ -784,19 +809,16 @@
         args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_OCB);
         args.addInt(KeymasterDefs.KM_TAG_CHUNK_LENGTH, 4096);
         args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 16);
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, null);
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_DATA, null);
+        args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
 
         KeyCharacteristics outCharacteristics = new KeyCharacteristics();
-        int rc = mKeyStore.generateKey(name, args, 0, outCharacteristics);
+        int rc = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
         assertEquals("Generate should succeed", KeyStore.NO_ERROR, rc);
 
         KeymasterArguments out = new KeymasterArguments();
         args = new KeymasterArguments();
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, null);
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_DATA, null);
         OperationResult result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT,
-                true, args, out);
+                true, args, null, out);
         IBinder token = result.token;
         assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode);
         result = mKeyStore.update(token, null, new byte[] {0x01, 0x02, 0x03, 0x04});
@@ -820,13 +842,14 @@
         args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
         args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, mode);
         args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, size);
+        args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
         return mKeyStore.importKey(name, args, KeymasterDefs.KM_KEY_FORMAT_RAW, key, 0,
                 new KeyCharacteristics());
     }
     private byte[] doOperation(String name, int purpose, byte[] in, KeymasterArguments beginArgs) {
         KeymasterArguments out = new KeymasterArguments();
         OperationResult result = mKeyStore.begin(name, purpose,
-                true, beginArgs, out);
+                true, beginArgs, null, out);
         assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode);
         IBinder token = result.token;
         result = mKeyStore.update(token, null, in);
@@ -883,28 +906,52 @@
         args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_OCB);
         args.addInt(KeymasterDefs.KM_TAG_CHUNK_LENGTH, 4096);
         args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 16);
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, null);
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_DATA, null);
+        args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
 
         KeyCharacteristics outCharacteristics = new KeyCharacteristics();
-        int rc = mKeyStore.generateKey(name, args, 0, outCharacteristics);
+        int rc = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
         assertEquals("Generate should succeed", KeyStore.NO_ERROR, rc);
 
         KeymasterArguments out = new KeymasterArguments();
         args = new KeymasterArguments();
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, null);
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_DATA, null);
         OperationResult result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT,
-                true, args, out);
+                true, args, null, out);
         assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode);
         IBinder first = result.token;
         // Implementation detail: softkeymaster supports 16 concurrent operations
         for (int i = 0; i < 16; i++) {
-            result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT, true, args, out);
+            result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT, true, args, null,
+                    out);
             assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode);
         }
         // At this point the first operation should be pruned.
         assertEquals("Operation should be pruned", KeymasterDefs.KM_ERROR_INVALID_OPERATION_HANDLE,
                 mKeyStore.update(first, null, new byte[] {0x01}).resultCode);
     }
+
+    public void testAuthNeeded() throws Exception {
+        String name = "test";
+        KeymasterArguments args = new KeymasterArguments();
+        args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
+        args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
+        args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+        args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+        args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256);
+        args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_OCB);
+        args.addInt(KeymasterDefs.KM_TAG_CHUNK_LENGTH, 4096);
+        args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 16);
+        args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 1);
+
+        KeyCharacteristics outCharacteristics = new KeyCharacteristics();
+        int rc = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
+        KeymasterArguments out = new KeymasterArguments();
+        assertEquals("Generate should succeed", KeyStore.NO_ERROR, rc);
+        OperationResult result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT,
+                true, args, null, out);
+        assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode);
+        IBinder token = result.token;
+        result = mKeyStore.update(token, null, new byte[] {0x01, 0x02, 0x03, 0x04});
+        assertEquals("Update should require authorization",
+                KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED, result.resultCode);
+    }
 }
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
index bc956be..1d6b7cc 100644
--- a/libs/hwui/TessellationCache.cpp
+++ b/libs/hwui/TessellationCache.cpp
@@ -380,6 +380,7 @@
         const Vector3& lightCenter, float lightRadius) {
     ShadowDescription key(casterPerimeter, drawTransform);
 
+    if (mShadowCache.get(key)) return;
     sp<ShadowTask> task = new ShadowTask(drawTransform, localClip, opaque,
             casterPerimeter, transformXY, transformZ, lightCenter, lightRadius);
     if (mShadowProcessor == NULL) {
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 36ba3a9..1f61b23 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -395,6 +395,7 @@
 }
 
 void RenderProxy::outputLogBuffer(int fd) {
+    if (!RenderThread::hasInstance()) return;
     SETUP_TASK(outputLogBuffer);
     args->fd = fd;
     staticPostAndWait(task);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b606a6f..226a8ca 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -104,6 +104,9 @@
     <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
     <uses-permission android:name="android.permission.TRUST_LISTENER" />
 
+    <!-- Needed for WallpaperManager.clear in ImageWallpaper.updateWallpaperLocked -->
+    <uses-permission android:name="android.permission.SET_WALLPAPER"/>
+
     <!-- Recents -->
     <uses-permission android:name="android.permission.BIND_APPWIDGET" />
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
index cbdd138..49693f5fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
@@ -37,7 +37,7 @@
         void onBluetoothPairedDevicesChanged();
     }
 
-    public static final class PairedDevice {
+    public static final class PairedDevice implements Comparable<PairedDevice> {
         public static int STATE_DISCONNECTED = 0;
         public static int STATE_CONNECTING = 1;
         public static int STATE_CONNECTED = 2;
@@ -55,5 +55,9 @@
             if (state == STATE_DISCONNECTING) return "STATE_DISCONNECTING";
             return "UNKNOWN";
         }
+
+        public int compareTo(PairedDevice another) {
+            return name.compareTo(another.name);
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 81e1e45..894f82a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -45,7 +45,6 @@
 import android.os.Message;
 import android.os.ParcelUuid;
 import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -55,6 +54,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Set;
+import java.util.TreeSet;
 
 public class BluetoothControllerImpl implements BluetoothController {
     private static final String TAG = "BluetoothController";
@@ -194,8 +194,8 @@
     }
 
     @Override
-    public ArraySet<PairedDevice> getPairedDevices() {
-        final ArraySet<PairedDevice> rt = new ArraySet<>();
+    public Set<PairedDevice> getPairedDevices() {
+        final Set<PairedDevice> rt = new TreeSet<>();
         for (int i = 0; i < mDeviceInfo.size(); i++) {
             final BluetoothDevice device = mDeviceInfo.keyAt(i);
             final DeviceInfo info = mDeviceInfo.valueAt(i);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 2fbb812..f0dd943 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -190,7 +190,8 @@
             NetworkCapabilities networkCapabilities =
                     mConnectivityManager.getNetworkCapabilities(network);
             if (DEBUG) Log.d(TAG, "onAvailable " + network.netId + " : " + networkCapabilities);
-            if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
+            if (networkCapabilities != null &&
+                    networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
                 setCurrentNetid(network.netId);
             }
         };
diff --git a/preloaded-classes b/preloaded-classes
index c8d8c5d..151766f 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -1146,8 +1146,6 @@
 android.provider.Settings$System
 android.provider.Telephony$Mms
 android.renderscript.RenderScript
-android.security.AndroidKeyPairGenerator
-android.security.AndroidKeyStore
 android.security.AndroidKeyStoreProvider
 android.speech.tts.TextToSpeechService
 android.speech.tts.TextToSpeechService$SpeechItemV1
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index 523c8fb..28547cc 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -1340,15 +1340,22 @@
 
     private void copyTo(Object array, Element.DataType dt, int arrayLen) {
         Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
-        if (dt.mSize * arrayLen < mSize) {
-            throw new RSIllegalArgumentException(
-                "Size of output array cannot be smaller than size of allocation.");
-        }
         mRS.validate();
         boolean usePadding = false;
         if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
             usePadding = true;
         }
+        if (usePadding) {
+            if (dt.mSize * arrayLen < mSize / 4 * 3) {
+                throw new RSIllegalArgumentException(
+                    "Size of output array cannot be smaller than size of allocation.");
+            }
+        } else {
+            if (dt.mSize * arrayLen < mSize) {
+                throw new RSIllegalArgumentException(
+                    "Size of output array cannot be smaller than size of allocation.");
+            }
+        }
         mRS.nAllocationRead(getID(mRS), array, dt, mType.mElement.mType.mSize, usePadding);
         Trace.traceEnd(RenderScript.TRACE_TAG);
     }
diff --git a/rs/java/android/renderscript/Element.java b/rs/java/android/renderscript/Element.java
index 287b3f1..60ff996 100644
--- a/rs/java/android/renderscript/Element.java
+++ b/rs/java/android/renderscript/Element.java
@@ -114,7 +114,8 @@
      * MATRIX the three matrix types contain FLOAT_32 elements and are treated
      * as 32 bits for alignment purposes.
      *
-     * RS_* objects.  32 bit opaque handles.
+     * RS_* objects:  opaque handles with implementation dependent
+     * sizes.
      */
     public enum DataType {
         NONE (0, 0),
diff --git a/rs/java/android/renderscript/FieldPacker.java b/rs/java/android/renderscript/FieldPacker.java
index 0f967fc..de1c497 100644
--- a/rs/java/android/renderscript/FieldPacker.java
+++ b/rs/java/android/renderscript/FieldPacker.java
@@ -47,6 +47,15 @@
         // subAlign() can never work correctly for copied FieldPacker objects.
     }
 
+    static FieldPacker createFromArray(Object[] args) {
+        FieldPacker fp = new FieldPacker(RenderScript.sPointerSize * 8);
+        for (Object arg : args) {
+            fp.addSafely(arg);
+        }
+        fp.resize(fp.mPos);
+        return fp;
+    }
+
     public void align(int v) {
         if ((v <= 0) || ((v & (v - 1)) != 0)) {
             throw new RSIllegalArgumentException("argument must be a non-negative non-zero power of 2: " + v);
@@ -618,294 +627,182 @@
         return mPos;
     }
 
-    private static void addToPack(FieldPacker fp, Object obj) {
+    private void add(Object obj) {
         if (obj instanceof Boolean) {
-            fp.addBoolean(((Boolean)obj).booleanValue());
+            addBoolean((Boolean)obj);
             return;
         }
 
         if (obj instanceof Byte) {
-            fp.addI8(((Byte)obj).byteValue());
+            addI8((Byte)obj);
             return;
         }
 
         if (obj instanceof Short) {
-            fp.addI16(((Short)obj).shortValue());
+            addI16((Short)obj);
             return;
         }
 
         if (obj instanceof Integer) {
-            fp.addI32(((Integer)obj).intValue());
+            addI32((Integer)obj);
             return;
         }
 
         if (obj instanceof Long) {
-            fp.addI64(((Long)obj).longValue());
+            addI64((Long)obj);
             return;
         }
 
         if (obj instanceof Float) {
-            fp.addF32(((Float)obj).floatValue());
+            addF32((Float)obj);
             return;
         }
 
         if (obj instanceof Double) {
-            fp.addF64(((Double)obj).doubleValue());
+            addF64((Double)obj);
             return;
         }
 
         if (obj instanceof Byte2) {
-            fp.addI8((Byte2)obj);
+            addI8((Byte2)obj);
             return;
         }
 
         if (obj instanceof Byte3) {
-            fp.addI8((Byte3)obj);
+            addI8((Byte3)obj);
             return;
         }
 
         if (obj instanceof Byte4) {
-            fp.addI8((Byte4)obj);
+            addI8((Byte4)obj);
             return;
         }
 
         if (obj instanceof Short2) {
-            fp.addI16((Short2)obj);
+            addI16((Short2)obj);
             return;
         }
 
         if (obj instanceof Short3) {
-            fp.addI16((Short3)obj);
+            addI16((Short3)obj);
             return;
         }
 
         if (obj instanceof Short4) {
-            fp.addI16((Short4)obj);
+            addI16((Short4)obj);
             return;
         }
 
         if (obj instanceof Int2) {
-            fp.addI32((Int2)obj);
+            addI32((Int2)obj);
             return;
         }
 
         if (obj instanceof Int3) {
-            fp.addI32((Int3)obj);
+            addI32((Int3)obj);
             return;
         }
 
         if (obj instanceof Int4) {
-            fp.addI32((Int4)obj);
+            addI32((Int4)obj);
             return;
         }
 
         if (obj instanceof Long2) {
-            fp.addI64((Long2)obj);
+            addI64((Long2)obj);
             return;
         }
 
         if (obj instanceof Long3) {
-            fp.addI64((Long3)obj);
+            addI64((Long3)obj);
             return;
         }
 
         if (obj instanceof Long4) {
-            fp.addI64((Long4)obj);
+            addI64((Long4)obj);
             return;
         }
 
         if (obj instanceof Float2) {
-            fp.addF32((Float2)obj);
+            addF32((Float2)obj);
             return;
         }
 
         if (obj instanceof Float3) {
-            fp.addF32((Float3)obj);
+            addF32((Float3)obj);
             return;
         }
 
         if (obj instanceof Float4) {
-            fp.addF32((Float4)obj);
+            addF32((Float4)obj);
             return;
         }
 
         if (obj instanceof Double2) {
-            fp.addF64((Double2)obj);
+            addF64((Double2)obj);
             return;
         }
 
         if (obj instanceof Double3) {
-            fp.addF64((Double3)obj);
+            addF64((Double3)obj);
             return;
         }
 
         if (obj instanceof Double4) {
-            fp.addF64((Double4)obj);
+            addF64((Double4)obj);
             return;
         }
 
         if (obj instanceof Matrix2f) {
-            fp.addMatrix((Matrix2f)obj);
+            addMatrix((Matrix2f)obj);
             return;
         }
 
         if (obj instanceof Matrix3f) {
-            fp.addMatrix((Matrix3f)obj);
+            addMatrix((Matrix3f)obj);
             return;
         }
 
         if (obj instanceof Matrix4f) {
-            fp.addMatrix((Matrix4f)obj);
+            addMatrix((Matrix4f)obj);
             return;
         }
 
         if (obj instanceof BaseObj) {
-            fp.addObj((BaseObj)obj);
+            addObj((BaseObj)obj);
             return;
         }
     }
 
-    private static int getPackedSize(Object obj) {
-        if (obj instanceof Boolean) {
-            return 1;
+    private boolean resize(int newSize) {
+        if (newSize == mLen) {
+            return false;
         }
 
-        if (obj instanceof Byte) {
-            return 1;
-        }
+        byte[] newData = new byte[newSize];
+        System.arraycopy(mData, 0, newData, 0, mPos);
+        mData = newData;
+        mLen = newSize;
+        return true;
+    }
 
-        if (obj instanceof Short) {
-            return 2;
-        }
-
-        if (obj instanceof Integer) {
-            return 4;
-        }
-
-        if (obj instanceof Long) {
-            return 8;
-        }
-
-        if (obj instanceof Float) {
-            return 4;
-        }
-
-        if (obj instanceof Double) {
-            return 8;
-        }
-
-        if (obj instanceof Byte2) {
-            return 2;
-        }
-
-        if (obj instanceof Byte3) {
-            return 3;
-        }
-
-        if (obj instanceof Byte4) {
-            return 4;
-        }
-
-        if (obj instanceof Short2) {
-            return 4;
-        }
-
-        if (obj instanceof Short3) {
-            return 6;
-        }
-
-        if (obj instanceof Short4) {
-            return 8;
-        }
-
-        if (obj instanceof Int2) {
-            return 8;
-        }
-
-        if (obj instanceof Int3) {
-            return 12;
-        }
-
-        if (obj instanceof Int4) {
-            return 16;
-        }
-
-        if (obj instanceof Long2) {
-            return 16;
-        }
-
-        if (obj instanceof Long3) {
-            return 24;
-        }
-
-        if (obj instanceof Long4) {
-            return 32;
-        }
-
-        if (obj instanceof Float2) {
-            return 8;
-        }
-
-        if (obj instanceof Float3) {
-            return 12;
-        }
-
-        if (obj instanceof Float4) {
-            return 16;
-        }
-
-        if (obj instanceof Double2) {
-            return 16;
-        }
-
-        if (obj instanceof Double3) {
-            return 24;
-        }
-
-        if (obj instanceof Double4) {
-            return 32;
-        }
-
-        if (obj instanceof Matrix2f) {
-            return 16;
-        }
-
-        if (obj instanceof Matrix3f) {
-            return 36;
-        }
-
-        if (obj instanceof Matrix4f) {
-            return 64;
-        }
-
-        if (obj instanceof BaseObj) {
-            if (RenderScript.sPointerSize == 8) {
-                return 32;
-            } else {
-                return 4;
+    private void addSafely(Object obj) {
+        boolean retry;
+        final int oldPos = mPos;
+        do {
+            retry = false;
+            try {
+                add(obj);
+            } catch (ArrayIndexOutOfBoundsException e) {
+                mPos = oldPos;
+                resize(mLen * 2);
+                retry = true;
             }
-        }
-
-        return 0;
+        } while (retry);
     }
 
-    static FieldPacker createFieldPack(Object[] args) {
-        int len = 0;
-        for (Object arg : args) {
-            len += getPackedSize(arg);
-        }
-        FieldPacker fp = new FieldPacker(len);
-        for (Object arg : args) {
-            addToPack(fp, arg);
-        }
-        return fp;
-    }
-
-    private final byte mData[];
+    private byte mData[];
     private int mPos;
     private int mLen;
     private BitSet mAlignment;
-
 }
-
-
diff --git a/rs/java/android/renderscript/FileA3D.java b/rs/java/android/renderscript/FileA3D.java
index 4164810..9d8f162 100644
--- a/rs/java/android/renderscript/FileA3D.java
+++ b/rs/java/android/renderscript/FileA3D.java
@@ -145,6 +145,9 @@
             case MESH:
                 entry.mLoadedObj = new Mesh(objectID, rs);
                 break;
+
+            default:
+                throw new RSRuntimeException("Unrecognized object type in file.");
             }
 
             entry.mLoadedObj.updateFromNative();
diff --git a/rs/java/android/renderscript/Mesh.java b/rs/java/android/renderscript/Mesh.java
index 5b4cadb..13c8e1c 100644
--- a/rs/java/android/renderscript/Mesh.java
+++ b/rs/java/android/renderscript/Mesh.java
@@ -363,6 +363,9 @@
                     alloc = Allocation.createTyped(mRS, entry.t, mUsage);
                 } else if(entry.e != null) {
                     alloc = Allocation.createSized(mRS, entry.e, entry.size, mUsage);
+                } else {
+                    // Should never happen because the builder will always set one
+                    throw new IllegalStateException("Builder corrupt, no valid element in entry.");
                 }
                 vertexBuffers[ct] = alloc;
                 vtx[ct] = alloc.getID(mRS);
@@ -375,6 +378,9 @@
                     alloc = Allocation.createTyped(mRS, entry.t, mUsage);
                 } else if(entry.e != null) {
                     alloc = Allocation.createSized(mRS, entry.e, entry.size, mUsage);
+                } else {
+                    // Should never happen because the builder will always set one
+                    throw new IllegalStateException("Builder corrupt, no valid element in entry.");
                 }
                 long allocID = (alloc == null) ? 0 : alloc.getID(mRS);
                 indexBuffers[ct] = alloc;
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index f08c985..45f0ca6 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -29,6 +29,7 @@
 import android.view.Surface;
 import android.os.SystemProperties;
 import android.os.Trace;
+import java.util.ArrayList;
 
 /**
  * This class provides access to a RenderScript context, which controls RenderScript
@@ -49,6 +50,12 @@
     @SuppressWarnings({"UnusedDeclaration", "deprecation"})
     static final boolean LOG_ENABLED = false;
 
+    static private ArrayList<RenderScript> mProcessContextList = new ArrayList<RenderScript>();
+    private boolean mIsProcessContext = false;
+    private int mContextFlags = 0;
+    private int mContextSdkVersion = 0;
+
+
     private Context mApplicationContext;
 
     /*
@@ -1313,20 +1320,13 @@
     }
 
     /**
-     * @hide
-     */
-    public static RenderScript create(Context ctx, int sdkVersion) {
-        return create(ctx, sdkVersion, ContextType.NORMAL, CREATE_FLAG_NONE);
-    }
-
-    /**
      * Create a RenderScript context.
      *
      * @hide
      * @param ctx The context.
      * @return RenderScript
      */
-    public static RenderScript create(Context ctx, int sdkVersion, ContextType ct, int flags) {
+    private static RenderScript internalCreate(Context ctx, int sdkVersion, ContextType ct, int flags) {
         if (!sInitialized) {
             Log.e(LOG_TAG, "RenderScript.create() called when disabled; someone is likely to crash");
             return null;
@@ -1341,6 +1341,8 @@
         rs.mDev = rs.nDeviceCreate();
         rs.mContext = rs.nContextCreate(rs.mDev, flags, sdkVersion, ct.mID);
         rs.mContextType = ct;
+        rs.mContextFlags = flags;
+        rs.mContextSdkVersion = sdkVersion;
         if (rs.mContext == 0) {
             throw new RSDriverException("Failed to create RS context.");
         }
@@ -1350,7 +1352,9 @@
     }
 
     /**
-     * Create a RenderScript context.
+     * calls create(ctx, ContextType.NORMAL, CREATE_FLAG_NONE)
+     *
+     * See documentation for @create for details
      *
      * @param ctx The context.
      * @return RenderScript
@@ -1360,21 +1364,33 @@
     }
 
     /**
-     * Create a RenderScript context.
+     * calls create(ctx, ct, CREATE_FLAG_NONE)
      *
+     * See documentation for @create for details
      *
      * @param ctx The context.
      * @param ct The type of context to be created.
      * @return RenderScript
      */
     public static RenderScript create(Context ctx, ContextType ct) {
-        int v = ctx.getApplicationInfo().targetSdkVersion;
-        return create(ctx, v, ct, CREATE_FLAG_NONE);
+        return create(ctx, ct, CREATE_FLAG_NONE);
     }
 
-     /**
-     * Create a RenderScript context.
+
+    /**
+     * Gets or creates a RenderScript context of the specified type.
      *
+     * The returned context will be cached for future reuse within
+     * the process. When an application is finished using
+     * RenderScript it should call releaseAllContexts()
+     *
+     * A process context is a context designed for easy creation and
+     * lifecycle management.  Multiple calls to this function will
+     * return the same object provided they are called with the same
+     * options.  This allows it to be used any time a RenderScript
+     * context is needed.
+     *
+     * Prior to API 23 this always created a new context.
      *
      * @param ctx The context.
      * @param ct The type of context to be created.
@@ -1387,6 +1403,100 @@
     }
 
     /**
+     * calls create(ctx, sdkVersion, ContextType.NORMAL, CREATE_FLAG_NONE)
+     *
+     * Used by the RenderScriptThunker to maintain backward compatibility.
+     *
+     * @hide
+     * @param ctx The context.
+     * @param sdkVersion The target SDK Version.
+     * @return RenderScript
+     */
+    public static RenderScript create(Context ctx, int sdkVersion) {
+        return create(ctx, sdkVersion, ContextType.NORMAL, CREATE_FLAG_NONE);
+    }
+
+     /**
+     * Gets or creates a RenderScript context of the specified type.
+     *
+     * @hide
+     * @param ctx The context.
+     * @param ct The type of context to be created.
+     * @param sdkVersion The target SDK Version.
+     * @param flags The OR of the CREATE_FLAG_* options desired
+     * @return RenderScript
+     */
+    public static RenderScript create(Context ctx, int sdkVersion, ContextType ct, int flags) {
+        if (sdkVersion < 23) {
+            return internalCreate(ctx, sdkVersion, ct, flags);
+        }
+
+        synchronized (mProcessContextList) {
+            for (RenderScript prs : mProcessContextList) {
+                if ((prs.mContextType == ct) &&
+                    (prs.mContextFlags == flags) &&
+                    (prs.mContextSdkVersion == sdkVersion)) {
+
+                    return prs;
+                }
+            }
+
+            RenderScript prs = internalCreate(ctx, sdkVersion, ct, flags);
+            prs.mIsProcessContext = true;
+            mProcessContextList.add(prs);
+            return prs;
+        }
+    }
+
+    /**
+     * @hide
+     *
+     * Releases all the process contexts.  This is the same as
+     * calling .destroy() on each unique context retreived with
+     * create(...). If no contexts have been created this
+     * function does nothing.
+     *
+     * Typically you call this when your application is losing focus
+     * and will not be using a context for some time.
+     *
+     * This has no effect on a context created with
+     * createMultiContext()
+     */
+    public static void releaseAllContexts() {
+        ArrayList<RenderScript> oldList;
+        synchronized (mProcessContextList) {
+            oldList = mProcessContextList;
+            mProcessContextList = new ArrayList<RenderScript>();
+        }
+
+        for (RenderScript prs : oldList) {
+            prs.mIsProcessContext = false;
+            prs.destroy();
+        }
+        oldList.clear();
+    }
+
+
+
+    /**
+     * Create a RenderScript context.
+     *
+     * This is an advanced function intended for applications which
+     * need to create more than one RenderScript context to be used
+     * at the same time.
+     *
+     * If you need a single context please use create()
+     *
+     * @hide
+     * @param ctx The context.
+     * @return RenderScript
+     */
+    public static RenderScript createMultiContext(Context ctx, ContextType ct, int flags, int API_number) {
+        return internalCreate(ctx, API_number, ct, flags);
+    }
+
+
+    /**
      * Print the currently available debugging information about the state of
      * the RS context to the log.
      *
@@ -1442,8 +1552,16 @@
      * using this context or any objects belonging to this context is
      * illegal.
      *
+     * API 23+, this function is a NOP if the context was created
+     * with create().  Please use releaseAllContexts() to clean up
+     * contexts created with the create function.
+     *
      */
     public void destroy() {
+        if (mIsProcessContext) {
+            // users cannot destroy a process context
+            return;
+        }
         validate();
         helpDestroy();
     }
diff --git a/rs/java/android/renderscript/ScriptGroup2.java b/rs/java/android/renderscript/ScriptGroup2.java
index 113b896..857e9fb 100644
--- a/rs/java/android/renderscript/ScriptGroup2.java
+++ b/rs/java/android/renderscript/ScriptGroup2.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2015 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.renderscript;
 
 import android.util.Log;
@@ -13,356 +29,415 @@
 You have tried to change the API from what has been previously approved.
 
 To make these errors go away, you have two choices:
-   1) You can add "@hide" javadoc comments to the methods, etc. listed in the
-      errors above.
+1) You can add "@hide" javadoc comments to the methods, etc. listed in the
+errors above.
 
-   2) You can update current.txt by executing the following command:
-         make update-api
+2) You can update current.txt by executing the following command:
+make update-api
 
 To submit the revised current.txt to the main Android repository,
 you will need approval.
 ******************************
 
-   @hide Pending Android public API approval.
- */
+@hide Pending Android public API approval.
+*/
 public class ScriptGroup2 extends BaseObj {
 
-  public static class Closure extends BaseObj {
-    private Allocation mReturnValue;
-    private Map<Script.FieldID, Object> mBindings;
+    public static class Closure extends BaseObj {
+        private Object[] mArgs;
+        private Allocation mReturnValue;
+        private Map<Script.FieldID, Object> mBindings;
 
-    private Future mReturnFuture;
-    private Map<Script.FieldID, Future> mGlobalFuture;
+        private Future mReturnFuture;
+        private Map<Script.FieldID, Future> mGlobalFuture;
 
-    private FieldPacker mFP;
+        private FieldPacker mFP;
 
-    private static final String TAG = "Closure";
+        private static final String TAG = "Closure";
 
-    public Closure(long id, RenderScript rs) {
-      super(id, rs);
-    }
-
-    public Closure(RenderScript rs, Script.KernelID kernelID, Type returnType,
-        Object[] args, Map<Script.FieldID, Object> globals) {
-      super(0, rs);
-
-      mReturnValue = Allocation.createTyped(rs, returnType);
-      mBindings = new HashMap<Script.FieldID, Object>();
-      mGlobalFuture = new HashMap<Script.FieldID, Future>();
-
-      int numValues = args.length + globals.size();
-
-      long[] fieldIDs = new long[numValues];
-      long[] values = new long[numValues];
-      int[] sizes = new int[numValues];
-      long[] depClosures = new long[numValues];
-      long[] depFieldIDs = new long[numValues];
-
-      int i;
-      for (i = 0; i < args.length; i++) {
-        Object obj = args[i];
-        fieldIDs[i] = 0;
-        if (obj instanceof UnboundValue) {
-          UnboundValue unbound = (UnboundValue)obj;
-          unbound.addReference(this, i);
-        } else {
-          retrieveValueAndDependenceInfo(rs, i, args[i], values, sizes,
-              depClosures, depFieldIDs);
+        public Closure(long id, RenderScript rs) {
+            super(id, rs);
         }
-      }
 
-      for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) {
-        Object obj = entry.getValue();
-        Script.FieldID fieldID = entry.getKey();
-        fieldIDs[i] = fieldID.getID(rs);
-        if (obj instanceof UnboundValue) {
-          UnboundValue unbound = (UnboundValue)obj;
-          unbound.addReference(this, fieldID);
-        } else {
-          retrieveValueAndDependenceInfo(rs, i, obj, values,
-              sizes, depClosures, depFieldIDs);
+        public Closure(RenderScript rs, Script.KernelID kernelID, Type returnType,
+                       Object[] args, Map<Script.FieldID, Object> globals) {
+            super(0, rs);
+
+            mArgs = args;
+            mReturnValue = Allocation.createTyped(rs, returnType);
+            mBindings = globals;
+            mGlobalFuture = new HashMap<Script.FieldID, Future>();
+
+            int numValues = args.length + globals.size();
+
+            long[] fieldIDs = new long[numValues];
+            long[] values = new long[numValues];
+            int[] sizes = new int[numValues];
+            long[] depClosures = new long[numValues];
+            long[] depFieldIDs = new long[numValues];
+
+            int i;
+            for (i = 0; i < args.length; i++) {
+                Object obj = args[i];
+                fieldIDs[i] = 0;
+                if (obj instanceof UnboundValue) {
+                    UnboundValue unbound = (UnboundValue)obj;
+                    unbound.addReference(this, i);
+                } else {
+                    retrieveValueAndDependenceInfo(rs, i, args[i], values, sizes,
+                                                   depClosures, depFieldIDs);
+                }
+            }
+
+            for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) {
+                Object obj = entry.getValue();
+                Script.FieldID fieldID = entry.getKey();
+                fieldIDs[i] = fieldID.getID(rs);
+                if (obj instanceof UnboundValue) {
+                    UnboundValue unbound = (UnboundValue)obj;
+                    unbound.addReference(this, fieldID);
+                } else {
+                    retrieveValueAndDependenceInfo(rs, i, obj, values,
+                                                   sizes, depClosures, depFieldIDs);
+                }
+                i++;
+            }
+
+            long id = rs.nClosureCreate(kernelID.getID(rs), mReturnValue.getID(rs),
+                                        fieldIDs, values, sizes, depClosures, depFieldIDs);
+
+            setID(id);
         }
-        i++;
-      }
 
-      long id = rs.nClosureCreate(kernelID.getID(rs), mReturnValue.getID(rs),
-          fieldIDs, values, sizes, depClosures, depFieldIDs);
+        public Closure(RenderScript rs, Script.InvokeID invokeID,
+                       Object[] args, Map<Script.FieldID, Object> globals) {
+            super(0, rs);
+            mFP = FieldPacker.createFromArray(args);
 
-      setID(id);
-    }
+            mArgs = args;
+            mBindings = globals;
+            mGlobalFuture = new HashMap<Script.FieldID, Future>();
 
-    public Closure(RenderScript rs, Script.InvokeID invokeID,
-        Object[] args, Map<Script.FieldID, Object> globals) {
-      super(0, rs);
-      mFP = FieldPacker.createFieldPack(args);
+            int numValues = globals.size();
 
-      mBindings = new HashMap<Script.FieldID, Object>();
-      mGlobalFuture = new HashMap<Script.FieldID, Future>();
+            long[] fieldIDs = new long[numValues];
+            long[] values = new long[numValues];
+            int[] sizes = new int[numValues];
+            long[] depClosures = new long[numValues];
+            long[] depFieldIDs = new long[numValues];
 
-      int numValues = globals.size();
+            int i = 0;
+            for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) {
+                Object obj = entry.getValue();
+                Script.FieldID fieldID = entry.getKey();
+                fieldIDs[i] = fieldID.getID(rs);
+                if (obj instanceof UnboundValue) {
+                    UnboundValue unbound = (UnboundValue)obj;
+                    unbound.addReference(this, fieldID);
+                } else {
+                    // TODO(yangni): Verify obj not a future.
+                    retrieveValueAndDependenceInfo(rs, i, obj, values,
+                                                   sizes, depClosures, depFieldIDs);
+                }
+                i++;
+            }
 
-      long[] fieldIDs = new long[numValues];
-      long[] values = new long[numValues];
-      int[] sizes = new int[numValues];
-      long[] depClosures = new long[numValues];
-      long[] depFieldIDs = new long[numValues];
+            long id = rs.nInvokeClosureCreate(invokeID.getID(rs), mFP.getData(), fieldIDs,
+                                              values, sizes);
 
-      int i = 0;
-      for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) {
-        Object obj = entry.getValue();
-        Script.FieldID fieldID = entry.getKey();
-        fieldIDs[i] = fieldID.getID(rs);
-        if (obj instanceof UnboundValue) {
-          UnboundValue unbound = (UnboundValue)obj;
-          unbound.addReference(this, fieldID);
-        } else {
-          // TODO(yangni): Verify obj not a future.
-          retrieveValueAndDependenceInfo(rs, i, obj, values,
-              sizes, depClosures, depFieldIDs);
+            setID(id);
         }
-        i++;
-      }
 
-      long id = rs.nInvokeClosureCreate(invokeID.getID(rs), mFP.getData(), fieldIDs,
-          values, sizes);
+        private static
+                void retrieveValueAndDependenceInfo(RenderScript rs,
+                                                    int index, Object obj,
+                                                    long[] values, int[] sizes,
+                                                    long[] depClosures,
+                                                    long[] depFieldIDs) {
 
-      setID(id);
-    }
+            if (obj instanceof Future) {
+                Future f = (Future)obj;
+                obj = f.getValue();
+                depClosures[index] = f.getClosure().getID(rs);
+                Script.FieldID fieldID = f.getFieldID();
+                depFieldIDs[index] = fieldID != null ? fieldID.getID(rs) : 0;
+                if (obj == null) {
+                    // Value is originally created by the owner closure
+                    values[index] = 0;
+                    sizes[index] = 0;
+                    return;
+                }
+            } else {
+                depClosures[index] = 0;
+                depFieldIDs[index] = 0;
+            }
 
-    private static void retrieveValueAndDependenceInfo(RenderScript rs,
-        int index, Object obj, long[] values, int[] sizes, long[] depClosures,
-        long[] depFieldIDs) {
-
-      if (obj instanceof Future) {
-        Future f = (Future)obj;
-        obj = f.getValue();
-        depClosures[index] = f.getClosure().getID(rs);
-        Script.FieldID fieldID = f.getFieldID();
-        depFieldIDs[index] = fieldID != null ? fieldID.getID(rs) : 0;
-        if (obj == null) {
-          // Value is originally created by the owner closure
-          values[index] = 0;
-          sizes[index] = 0;
-          return;
+            ValueAndSize vs = new ValueAndSize(rs, obj);
+            values[index] = vs.value;
+            sizes[index] = vs.size;
         }
-      } else {
-        depClosures[index] = 0;
-        depFieldIDs[index] = 0;
-      }
 
-      ValueAndSize vs = new ValueAndSize(rs, obj);
-      values[index] = vs.value;
-      sizes[index] = vs.size;
-    }
+        public Future getReturn() {
+            if (mReturnFuture == null) {
+                mReturnFuture = new Future(this, null, mReturnValue);
+            }
 
-    public Future getReturn() {
-      if (mReturnFuture == null) {
-        mReturnFuture = new Future(this, null, mReturnValue);
-      }
-
-      return mReturnFuture;
-    }
-
-    public Future getGlobal(Script.FieldID field) {
-      Future f = mGlobalFuture.get(field);
-
-      if (f == null) {
-        // If the field is not bound to this closure, this will return a future
-        // without an associated value (reference). So this is not working for
-        // cross-module (cross-script) linking in this case where a field not
-        // explicitly bound.
-        f = new Future(this, field, mBindings.get(field));
-        mGlobalFuture.put(field, f);
-      }
-
-      return f;
-    }
-
-    void setArg(int index, Object obj) {
-      ValueAndSize vs = new ValueAndSize(mRS, obj);
-      mRS.nClosureSetArg(getID(mRS), index, vs.value, vs.size);
-    }
-
-    void setGlobal(Script.FieldID fieldID, Object obj) {
-      ValueAndSize vs = new ValueAndSize(mRS, obj);
-      mRS.nClosureSetGlobal(getID(mRS), fieldID.getID(mRS), vs.value, vs.size);
-    }
-
-    private static final class ValueAndSize {
-      public ValueAndSize(RenderScript rs, Object obj) {
-        if (obj instanceof Allocation) {
-          value = ((Allocation)obj).getID(rs);
-          size = -1;
-        } else if (obj instanceof Boolean) {
-          value = ((Boolean)obj).booleanValue() ? 1 : 0;
-          size = 4;
-        } else if (obj instanceof Integer) {
-          value = ((Integer)obj).longValue();
-          size = 4;
-        } else if (obj instanceof Long) {
-          value = ((Long)obj).longValue();
-          size = 8;
-        } else if (obj instanceof Float) {
-          value = ((Float)obj).longValue();
-          size = 4;
-        } else if (obj instanceof Double) {
-          value = ((Double)obj).longValue();
-          size = 8;
+            return mReturnFuture;
         }
-      }
-      public long value;
-      public int size;
-    }
-  }
 
-  public static class Future {
-    Closure mClosure;
-    Script.FieldID mFieldID;
-    Object mValue;
+        public Future getGlobal(Script.FieldID field) {
+            Future f = mGlobalFuture.get(field);
 
-    Future(Closure closure, Script.FieldID fieldID, Object value) {
-      mClosure = closure;
-      mFieldID = fieldID;
-      mValue = value;
+            if (f == null) {
+                // If the field is not bound to this closure, this will return a future
+                // without an associated value (reference). So this is not working for
+                // cross-module (cross-script) linking in this case where a field not
+                // explicitly bound.
+                f = new Future(this, field, mBindings.get(field));
+                mGlobalFuture.put(field, f);
+            }
+
+            return f;
+        }
+
+        void setArg(int index, Object obj) {
+            mArgs[index] = obj;
+            ValueAndSize vs = new ValueAndSize(mRS, obj);
+            mRS.nClosureSetArg(getID(mRS), index, vs.value, vs.size);
+        }
+
+        void setGlobal(Script.FieldID fieldID, Object obj) {
+            mBindings.put(fieldID, obj);
+            ValueAndSize vs = new ValueAndSize(mRS, obj);
+            mRS.nClosureSetGlobal(getID(mRS), fieldID.getID(mRS), vs.value, vs.size);
+        }
+
+        private static final class ValueAndSize {
+            public ValueAndSize(RenderScript rs, Object obj) {
+                if (obj instanceof Allocation) {
+                    value = ((Allocation)obj).getID(rs);
+                    size = -1;
+                } else if (obj instanceof Boolean) {
+                    value = ((Boolean)obj).booleanValue() ? 1 : 0;
+                    size = 4;
+                } else if (obj instanceof Integer) {
+                    value = ((Integer)obj).longValue();
+                    size = 4;
+                } else if (obj instanceof Long) {
+                    value = ((Long)obj).longValue();
+                    size = 8;
+                } else if (obj instanceof Float) {
+                    value = ((Float)obj).longValue();
+                    size = 4;
+                } else if (obj instanceof Double) {
+                    value = ((Double)obj).longValue();
+                    size = 8;
+                }
+            }
+            public long value;
+            public int size;
+        }
     }
 
-    Closure getClosure() { return mClosure; }
-    Script.FieldID getFieldID() { return mFieldID; }
-    Object getValue() { return mValue; }
-  }
+    public static class Future {
+        Closure mClosure;
+        Script.FieldID mFieldID;
+        Object mValue;
 
-  public static class UnboundValue {
-    // Either mFieldID or mArgIndex should be set but not both.
-    List<Pair<Closure, Script.FieldID>> mFieldID;
-    // -1 means unset. Legal values are 0 .. n-1, where n is the number of
-    // arguments for the referencing closure.
-    List<Pair<Closure, Integer>> mArgIndex;
+        Future(Closure closure, Script.FieldID fieldID, Object value) {
+            mClosure = closure;
+            mFieldID = fieldID;
+            mValue = value;
+        }
 
-    UnboundValue() {
-      mFieldID = new ArrayList<Pair<Closure, Script.FieldID>>();
-      mArgIndex = new ArrayList<Pair<Closure, Integer>>();
+        Closure getClosure() { return mClosure; }
+        Script.FieldID getFieldID() { return mFieldID; }
+        Object getValue() { return mValue; }
     }
 
-    void addReference(Closure closure, int index) {
-      mArgIndex.add(Pair.create(closure, Integer.valueOf(index)));
+    public static class UnboundValue {
+        // Either mFieldID or mArgIndex should be set but not both.
+        List<Pair<Closure, Script.FieldID>> mFieldID;
+        // -1 means unset. Legal values are 0 .. n-1, where n is the number of
+        // arguments for the referencing closure.
+        List<Pair<Closure, Integer>> mArgIndex;
+
+        UnboundValue() {
+            mFieldID = new ArrayList<Pair<Closure, Script.FieldID>>();
+            mArgIndex = new ArrayList<Pair<Closure, Integer>>();
+        }
+
+        void addReference(Closure closure, int index) {
+            mArgIndex.add(Pair.create(closure, Integer.valueOf(index)));
+        }
+
+        void addReference(Closure closure, Script.FieldID fieldID) {
+            mFieldID.add(Pair.create(closure, fieldID));
+        }
+
+        void set(Object value) {
+            for (Pair<Closure, Integer> p : mArgIndex) {
+                Closure closure = p.first;
+                int index = p.second.intValue();
+                closure.setArg(index, value);
+            }
+            for (Pair<Closure, Script.FieldID> p : mFieldID) {
+                Closure closure = p.first;
+                Script.FieldID fieldID = p.second;
+                closure.setGlobal(fieldID, value);
+            }
+        }
     }
 
-    void addReference(Closure closure, Script.FieldID fieldID) {
-      mFieldID.add(Pair.create(closure, fieldID));
-    }
-
-    void set(Object value) {
-      for (Pair<Closure, Integer> p : mArgIndex) {
-        Closure closure = p.first;
-        int index = p.second.intValue();
-        closure.setArg(index, value);
-      }
-      for (Pair<Closure, Script.FieldID> p : mFieldID) {
-        Closure closure = p.first;
-        Script.FieldID fieldID = p.second;
-        closure.setGlobal(fieldID, value);
-      }
-    }
-  }
-
-  List<Closure> mClosures;
-  List<UnboundValue> mInputs;
-  Future[] mOutputs;
-
-  private static final String TAG = "ScriptGroup2";
-
-  public ScriptGroup2(long id, RenderScript rs) {
-    super(id, rs);
-  }
-
-  ScriptGroup2(RenderScript rs, List<Closure> closures,
-      List<UnboundValue> inputs, Future[] outputs) {
-    super(0, rs);
-    mClosures = closures;
-    mInputs = inputs;
-    mOutputs = outputs;
-
-    long[] closureIDs = new long[closures.size()];
-    for (int i = 0; i < closureIDs.length; i++) {
-      closureIDs[i] = closures.get(i).getID(rs);
-    }
-    long id = rs.nScriptGroup2Create(ScriptC.mCachePath, closureIDs);
-    setID(id);
-  }
-
-  public Object[] execute(Object... inputs) {
-    if (inputs.length < mInputs.size()) {
-      Log.e(TAG, this.toString() + " receives " + inputs.length + " inputs, " +
-          "less than expected " + mInputs.size());
-      return null;
-    }
-
-    if (inputs.length > mInputs.size()) {
-      Log.i(TAG, this.toString() + " receives " + inputs.length + " inputs, " +
-          "more than expected " + mInputs.size());
-    }
-
-    for (int i = 0; i < mInputs.size(); i++) {
-      Object obj = inputs[i];
-      if (obj instanceof Future || obj instanceof UnboundValue) {
-        Log.e(TAG, this.toString() + ": input " + i +
-            " is a future or unbound value");
-        return null;
-      }
-      UnboundValue unbound = mInputs.get(i);
-      unbound.set(obj);
-    }
-
-    mRS.nScriptGroup2Execute(getID(mRS));
-
-    Object[] outputObjs = new Object[mOutputs.length];
-    int i = 0;
-    for (Future f : mOutputs) {
-      outputObjs[i++] = f.getValue();
-    }
-    return outputObjs;
-  }
-
-  /**
-     @hide Pending Android public API approval.
-   */
-  public static final class Builder {
-    RenderScript mRS;
     List<Closure> mClosures;
     List<UnboundValue> mInputs;
+    Future[] mOutputs;
 
-    private static final String TAG = "ScriptGroup2.Builder";
+    private static final String TAG = "ScriptGroup2";
 
-    public Builder(RenderScript rs) {
-      mRS = rs;
-      mClosures = new ArrayList<Closure>();
-      mInputs = new ArrayList<UnboundValue>();
+    public ScriptGroup2(long id, RenderScript rs) {
+        super(id, rs);
     }
 
-    public Closure addKernel(Script.KernelID k, Type returnType, Object[] args,
-        Map<Script.FieldID, Object> globalBindings) {
-      Closure c = new Closure(mRS, k, returnType, args, globalBindings);
-      mClosures.add(c);
-      return c;
+    ScriptGroup2(RenderScript rs, List<Closure> closures,
+                 List<UnboundValue> inputs, Future[] outputs) {
+        super(0, rs);
+        mClosures = closures;
+        mInputs = inputs;
+        mOutputs = outputs;
+
+        long[] closureIDs = new long[closures.size()];
+        for (int i = 0; i < closureIDs.length; i++) {
+            closureIDs[i] = closures.get(i).getID(rs);
+        }
+        long id = rs.nScriptGroup2Create(ScriptC.mCachePath, closureIDs);
+        setID(id);
     }
 
-    public Closure addInvoke(Script.InvokeID invoke, Object[] args,
-        Map<Script.FieldID, Object> globalBindings) {
-      Closure c = new Closure(mRS, invoke, args, globalBindings);
-      mClosures.add(c);
-      return c;
+    public Object[] execute(Object... inputs) {
+        if (inputs.length < mInputs.size()) {
+            Log.e(TAG, this.toString() + " receives " + inputs.length + " inputs, " +
+                  "less than expected " + mInputs.size());
+            return null;
+        }
+
+        if (inputs.length > mInputs.size()) {
+            Log.i(TAG, this.toString() + " receives " + inputs.length + " inputs, " +
+                  "more than expected " + mInputs.size());
+        }
+
+        for (int i = 0; i < mInputs.size(); i++) {
+            Object obj = inputs[i];
+            if (obj instanceof Future || obj instanceof UnboundValue) {
+                Log.e(TAG, this.toString() + ": input " + i +
+                      " is a future or unbound value");
+                return null;
+            }
+            UnboundValue unbound = mInputs.get(i);
+            unbound.set(obj);
+        }
+
+        mRS.nScriptGroup2Execute(getID(mRS));
+
+        Object[] outputObjs = new Object[mOutputs.length];
+        int i = 0;
+        for (Future f : mOutputs) {
+            outputObjs[i++] = f.getValue();
+        }
+        return outputObjs;
     }
 
-    public UnboundValue addInput() {
-      UnboundValue unbound = new UnboundValue();
-      mInputs.add(unbound);
-      return unbound;
+    /**
+       @hide Pending Android public API approval.
+    */
+    public static final class Binding {
+        public Script.FieldID mField;
+        public Object mValue;
+        public Binding(Script.FieldID field, Object value) {
+            mField = field;
+            mValue = value;
+        }
     }
 
-    public ScriptGroup2 create(Future... outputs) {
-      ScriptGroup2 ret = new ScriptGroup2(mRS, mClosures, mInputs, outputs);
-      return ret;
-    }
+    /**
+       @hide Pending Android public API approval.
+    */
+    public static final class Builder {
+        RenderScript mRS;
+        List<Closure> mClosures;
+        List<UnboundValue> mInputs;
+        private static final String TAG = "ScriptGroup2.Builder";
 
-  }
+        public Builder(RenderScript rs) {
+            mRS = rs;
+            mClosures = new ArrayList<Closure>();
+            mInputs = new ArrayList<UnboundValue>();
+        }
+
+        public Closure addKernel(Script.KernelID k, Type returnType, Object[] args,
+                                 Map<Script.FieldID, Object> globalBindings) {
+            Closure c = new Closure(mRS, k, returnType, args, globalBindings);
+            mClosures.add(c);
+            return c;
+        }
+
+        public Closure addInvoke(Script.InvokeID invoke, Object[] args,
+                                 Map<Script.FieldID, Object> globalBindings) {
+            Closure c = new Closure(mRS, invoke, args, globalBindings);
+            mClosures.add(c);
+            return c;
+        }
+
+        public UnboundValue addInput() {
+            UnboundValue unbound = new UnboundValue();
+            mInputs.add(unbound);
+            return unbound;
+        }
+
+        public Closure addKernel(Script.KernelID k, Type returnType, Object... argsAndBindings) {
+            ArrayList<Object> args = new ArrayList<Object>();
+            Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>();
+            if (!seperateArgsAndBindings(argsAndBindings, args, bindingMap)) {
+                return null;
+            }
+            return addKernel(k, returnType, args.toArray(), bindingMap);
+        }
+
+        public Closure addInvoke(Script.InvokeID invoke, Object... argsAndBindings) {
+            ArrayList<Object> args = new ArrayList<Object>();
+            Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>();
+            if (!seperateArgsAndBindings(argsAndBindings, args, bindingMap)) {
+                return null;
+            }
+            return addInvoke(invoke, args.toArray(), bindingMap);
+        }
+
+        public ScriptGroup2 create(Future... outputs) {
+            ScriptGroup2 ret = new ScriptGroup2(mRS, mClosures, mInputs, outputs);
+            return ret;
+        }
+
+        private boolean seperateArgsAndBindings(Object[] argsAndBindings,
+                                                ArrayList<Object> args,
+                                                Map<Script.FieldID, Object> bindingMap) {
+            int i;
+            for (i = 0; i < argsAndBindings.length; i++) {
+                if (argsAndBindings[i] instanceof Binding) {
+                    break;
+                }
+                args.add(argsAndBindings[i]);
+            }
+
+            for (; i < argsAndBindings.length; i++) {
+                if (!(argsAndBindings[i] instanceof Binding)) {
+                    return false;
+                }
+                Binding b = (Binding)argsAndBindings[i];
+                bindingMap.put(b.mField, b.mValue);
+            }
+
+            return true;
+        }
+
+    }
 }
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 6d6757d..3591199 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -40,7 +40,6 @@
 #include <rsEnv.h>
 #include <gui/Surface.h>
 #include <gui/GLConsumer.h>
-#include <gui/Surface.h>
 #include <android_runtime/android_graphics_SurfaceTexture.h>
 
 //#define LOG_API ALOGE
@@ -373,7 +372,7 @@
   return (jlong)(uintptr_t)rsClosureCreate(
       (RsContext)con, (RsScriptKernelID)kernelID, (RsAllocation)returnValue,
       fieldIDs, (size_t)fieldIDs_length, values, (size_t)values_length,
-      (size_t*)sizes, (size_t)sizes_length,
+      (int*)sizes, (size_t)sizes_length,
       depClosures, (size_t)depClosures_length,
       depFieldIDs, (size_t)depFieldIDs_length);
 }
@@ -406,7 +405,7 @@
   return (jlong)(uintptr_t)rsInvokeClosureCreate(
       (RsContext)con, (RsScriptInvokeID)invokeID, jParams, jParamLength,
       fieldIDs, (size_t)fieldIDs_length, values, (size_t)values_length,
-      (size_t*)sizes, (size_t)sizes_length);
+      (int*)sizes, (size_t)sizes_length);
 }
 
 static void
diff --git a/services/core/Android.mk b/services/core/Android.mk
index 5c45201..43249e7 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -10,5 +10,6 @@
     java/com/android/server/am/EventLogTags.logtags
 
 LOCAL_JAVA_LIBRARIES := android.policy telephony-common
+LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d9ef766..758b0fc 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2022,6 +2022,8 @@
                         ReapUnvalidatedNetworks.DONT_REAP);
             }
         }
+        NetworkFactoryInfo nfi = mNetworkFactoryInfos.remove(msg.replyTo);
+        if (DBG && nfi != null) log("unregisterNetworkFactory for " + nfi.name);
     }
 
     // If this method proves to be too slow then we can maintain a separate
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 059dde1..a9d6a69 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2127,8 +2127,16 @@
             }
         }
 
-        // First clear app state from services.
-        for (int i=app.services.size()-1; i>=0; i--) {
+        // Clean up any connections this application has to other services.
+        for (int i = app.connections.size() - 1; i >= 0; i--) {
+            ConnectionRecord r = app.connections.valueAt(i);
+            removeConnectionLocked(r, app, null);
+        }
+        updateServiceConnectionActivitiesLocked(app);
+        app.connections.clear();
+
+        // Clear app state from services.
+        for (int i = app.services.size() - 1; i >= 0; i--) {
             ServiceRecord sr = app.services.valueAt(i);
             synchronized (sr.stats.getBatteryStats()) {
                 sr.stats.stopLaunchedLocked();
@@ -2188,14 +2196,6 @@
             }
         }
 
-        // Clean up any connections this application has to other services.
-        for (int i=app.connections.size()-1; i>=0; i--) {
-            ConnectionRecord r = app.connections.valueAt(i);
-            removeConnectionLocked(r, app, null);
-        }
-        updateServiceConnectionActivitiesLocked(app);
-        app.connections.clear();
-
         ServiceMap smap = getServiceMap(app.userId);
 
         // Now do remaining service cleanup.
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4ef171e..09ebe60 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -169,6 +169,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IPermissionController;
+import android.os.IProcessInfoService;
 import android.os.IRemoteCallback;
 import android.os.IUserManager;
 import android.os.Looper;
@@ -1215,7 +1216,7 @@
                 TAG, "Death received in " + this
                 + " for thread " + mAppThread.asBinder());
             synchronized(ActivityManagerService.this) {
-                appDiedLocked(mApp, mPid, mAppThread);
+                appDiedLocked(mApp, mPid, mAppThread, true);
             }
         }
     }
@@ -1918,6 +1919,7 @@
                 ServiceManager.addService("cpuinfo", new CpuBinder(this));
             }
             ServiceManager.addService("permission", new PermissionController(this));
+            ServiceManager.addService("processinfo", new ProcessInfoService(this));
 
             ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
                     "android", STOCK_PM_FLAGS);
@@ -2787,10 +2789,38 @@
         if (!isolated) {
             app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
             checkTime(startTime, "startProcess: after getProcessRecord");
+
+            if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) {
+                // If we are in the background, then check to see if this process
+                // is bad.  If so, we will just silently fail.
+                if (mBadProcesses.get(info.processName, info.uid) != null) {
+                    if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
+                            + "/" + info.processName);
+                    return null;
+                }
+            } else {
+                // When the user is explicitly starting a process, then clear its
+                // crash count so that we won't make it bad until they see at
+                // least one crash dialog again, and make the process good again
+                // if it had been bad.
+                if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
+                        + "/" + info.processName);
+                mProcessCrashTimes.remove(info.processName, info.uid);
+                if (mBadProcesses.get(info.processName, info.uid) != null) {
+                    EventLog.writeEvent(EventLogTags.AM_PROC_GOOD,
+                            UserHandle.getUserId(info.uid), info.uid,
+                            info.processName);
+                    mBadProcesses.remove(info.processName, info.uid);
+                    if (app != null) {
+                        app.bad = false;
+                    }
+                }
+            }
         } else {
             // If this is an isolated process, it can't re-use an existing process.
             app = null;
         }
+
         // We don't have to do anything more if:
         // (1) There is an existing application record; and
         // (2) The caller doesn't think it is dead, OR there is no thread
@@ -2824,35 +2854,6 @@
         String hostingNameStr = hostingName != null
                 ? hostingName.flattenToShortString() : null;
 
-        if (!isolated) {
-            if ((intentFlags&Intent.FLAG_FROM_BACKGROUND) != 0) {
-                // If we are in the background, then check to see if this process
-                // is bad.  If so, we will just silently fail.
-                if (mBadProcesses.get(info.processName, info.uid) != null) {
-                    if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
-                            + "/" + info.processName);
-                    return null;
-                }
-            } else {
-                // When the user is explicitly starting a process, then clear its
-                // crash count so that we won't make it bad until they see at
-                // least one crash dialog again, and make the process good again
-                // if it had been bad.
-                if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
-                        + "/" + info.processName);
-                mProcessCrashTimes.remove(info.processName, info.uid);
-                if (mBadProcesses.get(info.processName, info.uid) != null) {
-                    EventLog.writeEvent(EventLogTags.AM_PROC_GOOD,
-                            UserHandle.getUserId(info.uid), info.uid,
-                            info.processName);
-                    mBadProcesses.remove(info.processName, info.uid);
-                    if (app != null) {
-                        app.bad = false;
-                    }
-                }
-            }
-        }
-
         if (app == null) {
             checkTime(startTime, "startProcess: creating new process record");
             app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
@@ -4590,17 +4591,13 @@
             finishInstrumentationLocked(app, Activity.RESULT_CANCELED, info);
         }
 
-        if (!restarting) {
-            if (!mStackSupervisor.resumeTopActivitiesLocked()) {
-                // If there was nothing to resume, and we are not already
-                // restarting this process, but there is a visible activity that
-                // is hosted by the process...  then make sure all visible
-                // activities are running, taking care of restarting this
-                // process.
-                if (hasVisibleActivities) {
-                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
-                }
-            }
+        if (!restarting && hasVisibleActivities && !mStackSupervisor.resumeTopActivitiesLocked()) {
+            // If there was nothing to resume, and we are not already
+            // restarting this process, but there is a visible activity that
+            // is hosted by the process...  then make sure all visible
+            // activities are running, taking care of restarting this
+            // process.
+            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
         }
     }
 
@@ -4687,10 +4684,11 @@
     }
 
     final void appDiedLocked(ProcessRecord app) {
-       appDiedLocked(app, app.pid, app.thread);
+       appDiedLocked(app, app.pid, app.thread, false);
     }
 
-    final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread) {
+    final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread,
+            boolean fromBinderDied) {
         // First check if this ProcessRecord is actually active for the pid.
         synchronized (mPidsSelfLocked) {
             ProcessRecord curProc = mPidsSelfLocked.get(pid);
@@ -4706,7 +4704,9 @@
         }
 
         if (!app.killed) {
-            Process.killProcessQuiet(pid);
+            if (!fromBinderDied) {
+                Process.killProcessQuiet(pid);
+            }
             Process.killProcessGroup(app.info.uid, pid);
             app.killed = true;
         }
@@ -6802,7 +6802,46 @@
             }
         }
     }
-    
+
+    // =========================================================
+    // PROCESS INFO
+    // =========================================================
+
+    static class ProcessInfoService extends IProcessInfoService.Stub {
+        final ActivityManagerService mActivityManagerService;
+        ProcessInfoService(ActivityManagerService activityManagerService) {
+            mActivityManagerService = activityManagerService;
+        }
+
+        @Override
+        public void getProcessStatesFromPids(/*in*/ int[] pids, /*out*/ int[] states) {
+            mActivityManagerService.getProcessStatesForPIDs(/*in*/ pids, /*out*/ states);
+        }
+    }
+
+    /**
+     * For each PID in the given input array, write the current process state
+     * for that process into the output array, or -1 to indicate that no
+     * process with the given PID exists.
+     */
+    public void getProcessStatesForPIDs(/*in*/ int[] pids, /*out*/ int[] states) {
+        if (pids == null) {
+            throw new NullPointerException("pids");
+        } else if (states == null) {
+            throw new NullPointerException("states");
+        } else if (pids.length != states.length) {
+            throw new IllegalArgumentException("input and output arrays have different lengths!");
+        }
+
+        synchronized (mPidsSelfLocked) {
+            for (int i = 0; i < pids.length; i++) {
+                ProcessRecord pr = mPidsSelfLocked.get(pids[i]);
+                states[i] = (pr == null) ? ActivityManager.PROCESS_STATE_NONEXISTENT :
+                        pr.curProcState;
+            }
+        }
+    }
+
     // =========================================================
     // PERMISSIONS
     // =========================================================
@@ -17623,8 +17662,12 @@
         mFullPssPending = true;
         mPendingPssProcesses.ensureCapacity(mLruProcesses.size());
         mPendingPssProcesses.clear();
-        for (int i=mLruProcesses.size()-1; i>=0; i--) {
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
             ProcessRecord app = mLruProcesses.get(i);
+            if (app.thread == null
+                    || app.curProcState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
+                continue;
+            }
             if (memLowered || now > (app.lastStateTime+ProcessList.PSS_ALL_INTERVAL)) {
                 app.pssProcState = app.setProcState;
                 app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState, true,
@@ -17940,8 +17983,8 @@
                 }
             }
         }
-        if (app.setProcState < 0 || ProcessList.procStatesDifferForMem(app.curProcState,
-                app.setProcState)) {
+        if (app.setProcState == ActivityManager.PROCESS_STATE_NONEXISTENT
+                || ProcessList.procStatesDifferForMem(app.curProcState, app.setProcState)) {
             if (false && mTestPssMode && app.setProcState >= 0 && app.lastStateTime <= (now-200)) {
                 // Experimental code to more aggressively collect pss while
                 // running test...  the problem is that this tends to collect
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index b1b2a5c..1497c1d 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -982,24 +982,21 @@
     }
 
     private ActivityRecord getWaitingHistoryRecordLocked() {
-        // First find the real culprit...  if we are waiting
-        // for another app to start, then we have paused dispatching
-        // for this activity.
-        ActivityRecord r = this;
-        if (r.waitingVisible) {
+        // First find the real culprit...  if this activity is waiting for
+        // another activity to start or has stopped, then the key dispatching
+        // timeout should not be caused by this.
+        if (waitingVisible || stopped) {
             final ActivityStack stack = mStackSupervisor.getFocusedStack();
-            // Hmmm, who might we be waiting for?
-            r = stack.mResumedActivity;
+            // Try to use the one which is closest to top.
+            ActivityRecord r = stack.mResumedActivity;
             if (r == null) {
                 r = stack.mPausingActivity;
             }
-            // Both of those null?  Fall back to 'this' again
-            if (r == null) {
-                r = this;
+            if (r != null) {
+                return r;
             }
         }
-
-        return r;
+        return this;
     }
 
     public boolean keyDispatchingTimedOut(String reason) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 9f22aa9..82c71e3 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -3390,6 +3390,9 @@
                 if (DEBUG_CLEANUP) Slog.v(
                     TAG, "Record #" + i + " " + r + ": app=" + r.app);
                 if (r.app == app) {
+                    if (r.visible) {
+                        hasVisibleActivities = true;
+                    }
                     boolean remove;
                     if ((!r.haveState && !r.stateNotNeeded) || r.finishing) {
                         // Don't currently have state for the activity, or
@@ -3431,9 +3434,6 @@
                         // it can be restarted later when needed.
                         if (localLOGV) Slog.v(
                             TAG, "Keeping entry, setting app to null");
-                        if (r.visible) {
-                            hasVisibleActivities = true;
-                        }
                         if (DEBUG_APP) Slog.v(TAG, "Clearing app during removeHistory for activity "
                                 + r);
                         r.app = null;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 27c5404..8ab2368 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -607,17 +607,21 @@
     }
 
     boolean allResumedActivitiesVisible() {
+        boolean foundResumed = false;
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
             for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                 final ActivityStack stack = stacks.get(stackNdx);
                 final ActivityRecord r = stack.mResumedActivity;
-                if (r != null && (!r.nowVisible || r.waitingVisible)) {
-                    return false;
+                if (r != null) {
+                    if (!r.nowVisible || r.waitingVisible) {
+                        return false;
+                    }
+                    foundResumed = true;
                 }
             }
         }
-        return true;
+        return foundResumed;
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index a6c616a..466831a 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -16,6 +16,8 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+
 import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.Slog;
@@ -83,10 +85,10 @@
     int curSchedGroup;          // Currently desired scheduling class
     int setSchedGroup;          // Last set to background scheduling class
     int trimMemoryLevel;        // Last selected memory trimming level
-    int curProcState = -1;      // Currently computed process state: ActivityManager.PROCESS_STATE_*
-    int repProcState = -1;      // Last reported process state
-    int setProcState = -1;      // Last set process state in process tracker
-    int pssProcState = -1;      // The proc state we are currently requesting pss for
+    int curProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state
+    int repProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
+    int setProcState = PROCESS_STATE_NONEXISTENT; // Last set process state in process tracker
+    int pssProcState = PROCESS_STATE_NONEXISTENT; // 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 setIsForeground;    // Running foreground UI when last set?
@@ -418,7 +420,7 @@
                         tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList);
                 origBase.makeInactive();
             }
-            baseProcessTracker = tracker.getProcessStateLocked(info.packageName, info.uid,
+            baseProcessTracker = tracker.getProcessStateLocked(info.packageName, uid,
                     info.versionCode, processName);
             baseProcessTracker.makeActive();
             for (int i=0; i<pkgList.size(); i++) {
@@ -426,7 +428,7 @@
                 if (holder.state != null && holder.state != origBase) {
                     holder.state.makeInactive();
                 }
-                holder.state = tracker.getProcessStateLocked(pkgList.keyAt(i), info.uid,
+                holder.state = tracker.getProcessStateLocked(pkgList.keyAt(i), uid,
                         info.versionCode, processName);
                 if (holder.state != baseProcessTracker) {
                     holder.state.makeActive();
@@ -617,7 +619,7 @@
                     versionCode);
             if (baseProcessTracker != null) {
                 holder.state = tracker.getProcessStateLocked(
-                        pkg, info.uid, versionCode, processName);
+                        pkg, uid, versionCode, processName);
                 pkgList.put(pkg, holder);
                 if (holder.state != baseProcessTracker) {
                     holder.state.makeActive();
@@ -664,7 +666,7 @@
                 }
                 pkgList.clear();
                 ProcessStats.ProcessState ps = tracker.getProcessStateLocked(
-                        info.packageName, info.uid, info.versionCode, processName);
+                        info.packageName, uid, info.versionCode, processName);
                 ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
                         info.versionCode);
                 holder.state = ps;
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 8533f69..3174e69 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1009,6 +1009,14 @@
     public synchronized LegacyVpnInfo getLegacyVpnInfo() {
         // Check if the caller is authorized.
         enforceControlPermission();
+        return getLegacyVpnInfoPrivileged();
+    }
+
+    /**
+     * Return the information of the current ongoing legacy VPN.
+     * Callers are responsible for checking permissions if needed.
+     */
+    public synchronized LegacyVpnInfo getLegacyVpnInfoPrivileged() {
         if (mLegacyVpnRunner == null) return null;
 
         final LegacyVpnInfo info = new LegacyVpnInfo();
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 5cde8ea..b4a44a6 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -16,6 +16,7 @@
 
 package com.android.server.pm;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.PackageStats;
 import android.os.Build;
@@ -83,14 +84,15 @@
     }
 
     public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName,
-            String instructionSet, boolean vmSafeMode, boolean debuggable) {
+            String instructionSet, boolean vmSafeMode, boolean debuggable,
+            @Nullable String outputPath) {
         if (!isValidInstructionSet(instructionSet)) {
             Slog.e(TAG, "Invalid instruction set: " + instructionSet);
             return -1;
         }
 
         return mInstaller.dexopt(apkPath, uid, isPublic, pkgName, instructionSet, vmSafeMode,
-                debuggable);
+                debuggable, outputPath);
     }
 
     public int idmap(String targetApkPath, String overlayApkPath, int uid) {
@@ -134,6 +136,16 @@
         return mInstaller.execute(builder.toString());
     }
 
+    /**
+     * Removes packageDir or its subdirectory
+     */
+    public int rmPackageDir(String packageDir) {
+        StringBuilder builder = new StringBuilder("rmpackagedir");
+        builder.append(' ');
+        builder.append(packageDir);
+        return mInstaller.execute(builder.toString());
+    }
+
     public int remove(String name, int userId) {
         StringBuilder builder = new StringBuilder("remove");
         builder.append(' ');
@@ -331,6 +343,15 @@
         return (mInstaller.execute(builder.toString()) == 0);
     }
 
+    public int createOatDir(String oatDir, String dexInstructionSet) {
+        StringBuilder builder = new StringBuilder("createoatdir");
+        builder.append(' ');
+        builder.append(oatDir);
+        builder.append(' ');
+        builder.append(dexInstructionSet);
+        return mInstaller.execute(builder.toString());
+    }
+
     /**
      * Returns true iff. {@code instructionSet} is a valid instruction set.
      */
diff --git a/services/core/java/com/android/server/pm/InstructionSets.java b/services/core/java/com/android/server/pm/InstructionSets.java
index 79e7a20..5092ebf 100644
--- a/services/core/java/com/android/server/pm/InstructionSets.java
+++ b/services/core/java/com/android/server/pm/InstructionSets.java
@@ -74,6 +74,7 @@
      * a native bridge this might be different than the one shared libraries use.
      */
     public static String getDexCodeInstructionSet(String sharedLibraryIsa) {
+        // TODO b/19550105 Build mapping once instead of querying each time
         String dexCodeIsa = SystemProperties.get("ro.dalvik.vm.isa." + sharedLibraryIsa);
         return TextUtils.isEmpty(dexCodeIsa) ? sharedLibraryIsa : dexCodeIsa;
     }
@@ -111,4 +112,13 @@
 
         return allInstructionSets;
     }
+
+    public static String getPrimaryInstructionSet(ApplicationInfo info) {
+        if (info.primaryCpuAbi == null) {
+            return getPreferredInstructionSet();
+        }
+
+        return VMRuntime.getInstructionSet(info.primaryCpuAbi);
+    }
+
 }
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 2dbce0a..680ec4b 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -16,6 +16,7 @@
 
 package com.android.server.pm;
 
+import android.annotation.Nullable;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageParser;
 import android.os.UserHandle;
@@ -23,6 +24,7 @@
 import android.util.Log;
 import android.util.Slog;
 
+import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -38,7 +40,9 @@
  * Helper class for running dexopt command on packages.
  */
 final class PackageDexOptimizer {
-    static final String TAG = "PackageManager.DexOptimizer";
+    private static final String TAG = "PackageManager.DexOptimizer";
+    static final String OAT_DIR_NAME = "oat";
+    // TODO b/19550105 Remove error codes and use exceptions
     static final int DEX_OPT_SKIPPED = 0;
     static final int DEX_OPT_PERFORMED = 1;
     static final int DEX_OPT_DEFERRED = 2;
@@ -117,19 +121,30 @@
                     final byte isDexOptNeeded = DexFile.isDexOptNeededInternal(path,
                             pkg.packageName, dexCodeInstructionSet, defer);
                     if (forceDex || (!defer && isDexOptNeeded == DexFile.DEXOPT_NEEDED)) {
+                        File oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet);
                         Log.i(TAG, "Running dexopt on: " + path + " pkg="
                                 + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
-                                + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable);
+                                + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
+                                + " oatDir = " + oatDir);
                         final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
-                        final int ret = mPackageManagerService.mInstaller.dexopt(path, sharedGid,
-                                !pkg.isForwardLocked(), pkg.packageName, dexCodeInstructionSet,
-                                vmSafeMode, debuggable);
 
-                        if (ret < 0) {
-                            // Don't bother running dexopt again if we failed, it will probably
-                            // just result in an error again. Also, don't bother dexopting for other
-                            // paths & ISAs.
-                            return DEX_OPT_FAILED;
+                        if (oatDir != null) {
+                            int ret = mPackageManagerService.mInstaller.dexopt(
+                                    path, sharedGid, !pkg.isForwardLocked(), pkg.packageName,
+                                    dexCodeInstructionSet, vmSafeMode, debuggable,
+                                    oatDir.getAbsolutePath());
+                            if (ret < 0) {
+                                return DEX_OPT_FAILED;
+                            }
+                        } else {
+                            final int ret = mPackageManagerService.mInstaller
+                                    .dexopt(path, sharedGid,
+                                            !pkg.isForwardLocked(), pkg.packageName,
+                                            dexCodeInstructionSet,
+                                            vmSafeMode, debuggable, null);
+                            if (ret < 0) {
+                                return DEX_OPT_FAILED;
+                            }
                         }
 
                         performedDexOpt = true;
@@ -186,6 +201,36 @@
         return performedDexOpt ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
     }
 
+    /**
+     * Creates oat dir for the specified package. In certain cases oat directory
+     * <strong>cannot</strong> be created:
+     * <ul>
+     *      <li>{@code pkg} is a system app, which is not updated.</li>
+     *      <li>Package location is not a directory, i.e. monolithic install.</li>
+     * </ul>
+     *
+     * @return oat directory or null, if oat directory cannot be created.
+     */
+    @Nullable
+    private File createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet)
+            throws IOException {
+        if (pkg.isSystemApp() && !pkg.isUpdatedSystemApp()) {
+            return null;
+        }
+        File codePath = new File(pkg.codePath);
+        if (codePath.isDirectory()) {
+            File oatDir = getOatDir(codePath);
+            mPackageManagerService.mInstaller.createOatDir(oatDir.getAbsolutePath(),
+                    dexInstructionSet);
+            return oatDir;
+        }
+        return null;
+    }
+
+    static File getOatDir(File codePath) {
+        return new File(codePath, OAT_DIR_NAME);
+    }
+
     private void performDexOptLibsLI(ArrayList<String> libs, String[] instructionSets,
             boolean forceDex, boolean defer, ArraySet<String> done) {
         for (String libName : libs) {
@@ -209,7 +254,7 @@
 
     public void addPackageForDeferredDexopt(PackageParser.Package pkg) {
         if (mDeferredDexOpt == null) {
-            mDeferredDexOpt = new ArraySet<PackageParser.Package>();
+            mDeferredDexOpt = new ArraySet<>();
         }
         mDeferredDexOpt.add(pkg);
     }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 2150e9a..95d7a52 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -53,7 +53,6 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
-import android.os.FileUtils;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
@@ -225,9 +224,10 @@
             for (File stage : unclaimedStages) {
                 Slog.w(TAG, "Deleting orphan stage " + stage);
                 if (stage.isDirectory()) {
-                    FileUtils.deleteContents(stage);
+                    mPm.mInstaller.rmPackageDir(stage.getAbsolutePath());
+                } else {
+                    stage.delete();
                 }
-                stage.delete();
             }
 
             // Clean up orphaned icons
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index aba930f..67d3cbe 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -59,6 +59,7 @@
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
 import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
+import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
 
 import android.util.ArrayMap;
 
@@ -1872,9 +1873,10 @@
         removeDataDirsLI(ps.name);
         if (ps.codePath != null) {
             if (ps.codePath.isDirectory()) {
-                FileUtils.deleteContents(ps.codePath);
+                mInstaller.rmPackageDir(ps.codePath.getAbsolutePath());
+            } else {
+                ps.codePath.delete();
             }
-            ps.codePath.delete();
         }
         if (ps.resourcePath != null && !ps.resourcePath.equals(ps.codePath)) {
             if (ps.resourcePath.isDirectory()) {
@@ -4190,9 +4192,10 @@
                         e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
                     logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
                     if (file.isDirectory()) {
-                        FileUtils.deleteContents(file);
+                        mInstaller.rmPackageDir(file.getAbsolutePath());
+                    } else {
+                        file.delete();
                     }
-                    file.delete();
                 }
             }
         }
@@ -4640,7 +4643,7 @@
             // Give priority to system apps.
             for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) {
                 PackageParser.Package pkg = it.next();
-                if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) {
+                if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
                     if (DEBUG_DEXOPT) {
                         Log.i(TAG, "Adding system app " + sortedPkgs.size() + ": " + pkg.packageName);
                     }
@@ -4651,7 +4654,7 @@
             // Give priority to updated system apps.
             for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) {
                 PackageParser.Package pkg = it.next();
-                if (isUpdatedSystemApp(pkg)) {
+                if (pkg.isUpdatedSystemApp()) {
                     if (DEBUG_DEXOPT) {
                         Log.i(TAG, "Adding updated system app " + sortedPkgs.size() + ": " + pkg.packageName);
                     }
@@ -4772,14 +4775,6 @@
         return performDexOpt(packageName, instructionSet, false);
     }
 
-    private static String getPrimaryInstructionSet(ApplicationInfo info) {
-        if (info.primaryCpuAbi == null) {
-            return getPreferredInstructionSet();
-        }
-
-        return VMRuntime.getInstructionSet(info.primaryCpuAbi);
-    }
-
     public boolean performDexOpt(String packageName, String instructionSet, boolean backgroundDexopt) {
         boolean dexopt = mLazyDexOpt || backgroundDexopt;
         boolean updateUsage = !backgroundDexopt;  // Don't update usage if this is just a backgroundDexopt
@@ -5513,7 +5508,7 @@
         final String path = scanFile.getPath();
         final String codePath = pkg.applicationInfo.getCodePath();
         final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
-        if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) {
+        if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
             setBundledAppAbisAndRoots(pkg, pkgSetting);
 
             // If we haven't found any native libraries for the app, check if it has
@@ -5728,7 +5723,6 @@
                 throw new PackageManagerException(INSTALL_FAILED_DEXOPT, "scanPackageLI");
             }
         }
-
         if (mFactoryTest && pkg.requestedPermissions.contains(
                 android.Manifest.permission.FACTORY_TEST)) {
             pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;
@@ -5744,7 +5738,7 @@
                     for (int i=0; i<pkg.libraryNames.size(); i++) {
                         String name = pkg.libraryNames.get(i);
                         boolean allowed = false;
-                        if (isUpdatedSystemApp(pkg)) {
+                        if (pkg.isUpdatedSystemApp()) {
                             // New library entries can only be added through the
                             // system image.  This is important to get rid of a lot
                             // of nasty edge cases: for example if we allowed a non-
@@ -6350,7 +6344,7 @@
         final ApplicationInfo info = pkg.applicationInfo;
         final String codePath = pkg.codePath;
         final File codeFile = new File(codePath);
-        final boolean bundledApp = isSystemApp(info) && !isUpdatedSystemApp(info);
+        final boolean bundledApp = info.isSystemApp() && !info.isUpdatedSystemApp();
         final boolean asecApp = info.isForwardLocked() || isExternal(info);
 
         info.nativeLibraryRootDir = null;
@@ -7002,7 +6996,7 @@
             if (isSystemApp(pkg)) {
                 // For updated system applications, a system permission
                 // is granted only if it had been defined by the original application.
-                if (isUpdatedSystemApp(pkg)) {
+                if (pkg.isUpdatedSystemApp()) {
                     final PackageSetting sysPs = mSettings
                             .getDisabledSystemPkgLPr(pkg.packageName);
                     final GrantedPermissions origGp = sysPs.sharedUser != null
@@ -7090,7 +7084,7 @@
         }
 
         public final void addActivity(PackageParser.Activity a, String type) {
-            final boolean systemApp = isSystemApp(a.info.applicationInfo);
+            final boolean systemApp = a.info.applicationInfo.isSystemApp();
             mActivities.put(a.getComponentName(), a);
             if (DEBUG_SHOW_INFO)
                 Log.v(
@@ -7217,7 +7211,7 @@
             } else {
                 res.icon = info.icon;
             }
-            res.system = isSystemApp(res.activityInfo.applicationInfo);
+            res.system = res.activityInfo.applicationInfo.isSystemApp();
             return res;
         }
 
@@ -7433,7 +7427,7 @@
             res.labelRes = info.labelRes;
             res.nonLocalizedLabel = info.nonLocalizedLabel;
             res.icon = info.icon;
-            res.system = isSystemApp(res.serviceInfo.applicationInfo);
+            res.system = res.serviceInfo.applicationInfo.isSystemApp();
             return res;
         }
 
@@ -7656,7 +7650,7 @@
             res.labelRes = info.labelRes;
             res.nonLocalizedLabel = info.nonLocalizedLabel;
             res.icon = info.icon;
-            res.system = isSystemApp(res.providerInfo.applicationInfo);
+            res.system = res.providerInfo.applicationInfo.isSystemApp();
             return res;
         }
 
@@ -9386,9 +9380,10 @@
             }
 
             if (codeFile.isDirectory()) {
-                FileUtils.deleteContents(codeFile);
+                mInstaller.rmPackageDir(codeFile.getAbsolutePath());
+            } else {
+                codeFile.delete();
             }
-            codeFile.delete();
 
             if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) {
                 resourceFile.delete();
@@ -9830,22 +9825,6 @@
         return result;
     }
 
-    // Utility method used to ignore ADD/REMOVE events
-    // by directory observer.
-    private static boolean ignoreCodePath(String fullPathStr) {
-        String apkName = deriveCodePathName(fullPathStr);
-        int idx = apkName.lastIndexOf(INSTALL_PACKAGE_SUFFIX);
-        if (idx != -1 && ((idx+1) < apkName.length())) {
-            // Make sure the package ends with a numeral
-            String version = apkName.substring(idx+1);
-            try {
-                Integer.parseInt(version);
-                return true;
-            } catch (NumberFormatException e) {}
-        }
-        return false;
-    }
-    
     // Utility method that returns the relative package path with respect
     // to the installation directory. Like say for /data/data/com.test-1.apk
     // string com.test-1 is returned.
@@ -10454,13 +10433,23 @@
             return;
         }
 
+        // Run dexopt before old package gets removed, to minimize time when app is not available
+        int result = mPackageDexOptimizer
+                .performDexOpt(pkg, null /* instruction sets */, true /* forceDex */,
+                        false /* defer */, false /* inclDependencies */);
+        if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
+            res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);
+            return;
+        }
+
         if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
             res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
             return;
         }
 
         if (replace) {
-            replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
+            // Call replacePackageLI with SCAN_NO_DEX, since we already made dexopt
+            replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING | SCAN_NO_DEX, args.user,
                     installerPackageName, res);
         } else {
             installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
@@ -10502,10 +10491,6 @@
         return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
     }
 
-    private static boolean isSystemApp(ApplicationInfo info) {
-        return (info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
-    }
-
     private static boolean isSystemApp(PackageSetting ps) {
         return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
     }
@@ -10514,14 +10499,6 @@
         return (ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
     }
 
-    private static boolean isUpdatedSystemApp(PackageParser.Package pkg) {
-        return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
-    }
-
-    private static boolean isUpdatedSystemApp(ApplicationInfo info) {
-        return (info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
-    }
-
     private int packageFlagsToInstallFlags(PackageSetting ps) {
         int installFlags = 0;
         if (isExternal(ps)) {
diff --git a/services/core/java/com/android/server/updates/TZInfoInstallReceiver.java b/services/core/java/com/android/server/updates/TZInfoInstallReceiver.java
deleted file mode 100644
index 2fe68f8..0000000
--- a/services/core/java/com/android/server/updates/TZInfoInstallReceiver.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.updates;
-
-import android.util.Base64;
-
-import java.io.IOException;
-
-public class TZInfoInstallReceiver extends ConfigUpdateInstallReceiver {
-
-    public TZInfoInstallReceiver() {
-        super("/data/misc/zoneinfo/", "tzdata", "metadata/", "version");
-    }
-
-    @Override
-    protected void install(byte[] encodedContent, int version) throws IOException {
-        super.install(Base64.decode(encodedContent, Base64.DEFAULT), version);
-    }
-}
diff --git a/services/core/java/com/android/server/updates/TzDataInstallReceiver.java b/services/core/java/com/android/server/updates/TzDataInstallReceiver.java
new file mode 100644
index 0000000..b260e4e
--- /dev/null
+++ b/services/core/java/com/android/server/updates/TzDataInstallReceiver.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.updates;
+
+import android.util.Slog;
+
+import java.io.File;
+import java.io.IOException;
+import libcore.tzdata.update.TzDataBundleInstaller;
+
+/**
+ * An install receiver responsible for installing timezone data updates.
+ */
+public class TzDataInstallReceiver extends ConfigUpdateInstallReceiver {
+
+    private static final String TAG = "TZDataInstallReceiver";
+
+    private static final File TZ_DATA_DIR = new File("/data/misc/zoneinfo");
+    private static final String UPDATE_DIR_NAME = TZ_DATA_DIR.getPath() + "/updates/";
+    private static final String UPDATE_METADATA_DIR_NAME = "metadata/";
+    private static final String UPDATE_VERSION_FILE_NAME = "version";
+    private static final String UPDATE_CONTENT_FILE_NAME = "tzdata_bundle.zip";
+
+    private final TzDataBundleInstaller installer;
+
+    public TzDataInstallReceiver() {
+        super(UPDATE_DIR_NAME, UPDATE_CONTENT_FILE_NAME, UPDATE_METADATA_DIR_NAME,
+                UPDATE_VERSION_FILE_NAME);
+        installer = new TzDataBundleInstaller(TAG, TZ_DATA_DIR);
+    }
+
+    @Override
+    protected void install(byte[] content, int version) throws IOException {
+        boolean valid = installer.install(content);
+        Slog.i(TAG, "Timezone data install valid for this device: " + valid);
+        // Even if !valid, we call super.install(). Only in the event of an exception should we
+        // not. If we didn't do this we could attempt to install repeatedly.
+        super.install(content, version);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
index c09ea5c..3f5ae56 100644
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ b/services/core/java/com/android/server/wm/DimLayer.java
@@ -140,10 +140,9 @@
     }
 
     /**
-     * @param layer The new layer value.
-     * @param inTransaction Whether the call is made within a surface transaction.
+     * NOTE: Must be called with Surface transaction open.
      */
-    void adjustSurface(int layer, boolean inTransaction) {
+    private void adjustBounds() {
         final int dw, dh;
         final float xPos, yPos;
         if (!mStack.isFullscreen()) {
@@ -163,29 +162,31 @@
             yPos = -1 * dh / 6;
         }
 
-        try {
-            if (!inTransaction) {
-                SurfaceControl.openTransaction();
-            }
-            mDimSurface.setPosition(xPos, yPos);
-            mDimSurface.setSize(dw, dh);
-            mDimSurface.setLayer(layer);
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Failure setting size or layer", e);
-        } finally {
-            if (!inTransaction) {
-                SurfaceControl.closeTransaction();
-            }
-        }
+        mDimSurface.setPosition(xPos, yPos);
+        mDimSurface.setSize(dw, dh);
+
         mLastBounds.set(mBounds);
-        mLayer = layer;
     }
 
-    // Assumes that surface transactions are currently closed.
-    void setBounds(Rect bounds) {
+    /**
+     * @param bounds The new bounds to set
+     * @param inTransaction Whether the call is made within a surface transaction.
+     */
+    void setBounds(Rect bounds, boolean inTransaction) {
         mBounds.set(bounds);
         if (isDimming() && !mLastBounds.equals(bounds)) {
-            adjustSurface(mLayer, false);
+            try {
+                if (!inTransaction) {
+                    SurfaceControl.openTransaction();
+                }
+                adjustBounds();
+            } catch (RuntimeException e) {
+                Slog.w(TAG, "Failure setting size", e);
+            } finally {
+                if (!inTransaction) {
+                    SurfaceControl.closeTransaction();
+                }
+            }
         }
     }
 
@@ -224,9 +225,10 @@
             return;
         }
 
-        if (!mLastBounds.equals(mBounds) || mLayer != layer) {
-            adjustSurface(layer, true);
+        if (!mLastBounds.equals(mBounds)) {
+            adjustBounds();
         }
+        setLayer(layer);
 
         long curTime = SystemClock.uptimeMillis();
         final boolean animating = isAnimating();
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 802cf4b..d313e3f 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -126,8 +126,8 @@
             return false;
         }
 
-        mDimLayer.setBounds(bounds);
-        mAnimationBackgroundSurface.setBounds(bounds);
+        mDimLayer.setBounds(bounds, false);
+        mAnimationBackgroundSurface.setBounds(bounds, false);
         mBounds.set(bounds);
 
         return true;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7b389f5..de7cb33 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -508,6 +508,7 @@
     boolean mClientFreezingScreen = false;
     int mAppsFreezingScreen = 0;
     int mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+    int mLastKeyguardForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
 
     int mLayoutSeq = 0;
 
@@ -3126,6 +3127,10 @@
                 }
                 winAnimator.mEnteringAnimation = true;
                 if (toBeDisplayed) {
+                    if ((win.mAttrs.softInputMode & SOFT_INPUT_MASK_ADJUST)
+                            == SOFT_INPUT_ADJUST_RESIZE) {
+                        win.mLayoutNeeded = true;
+                    }
                     if (win.isDrawnLw() && okToDisplay()) {
                         winAnimator.applyEnterAnimationLocked();
                     }
@@ -3714,43 +3719,70 @@
         }
     }
 
-    public int getOrientationFromWindowsLocked() {
-        if (mDisplayFrozen || mOpeningApps.size() > 0 || mClosingApps.size() > 0) {
-            // If the display is frozen, some activities may be in the middle
-            // of restarting, and thus have removed their old window.  If the
-            // window has the flag to hide the lock screen, then the lock screen
-            // can re-appear and inflict its own orientation on us.  Keep the
-            // orientation stable until this all settles down.
-            return mLastWindowForcedOrientation;
+    public int getOrientationLocked() {
+        if (mDisplayFrozen) {
+            if (mLastWindowForcedOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
+                if (DEBUG_ORIENTATION) Slog.v(TAG, "Display is frozen, return "
+                        + mLastWindowForcedOrientation);
+                // If the display is frozen, some activities may be in the middle
+                // of restarting, and thus have removed their old window.  If the
+                // window has the flag to hide the lock screen, then the lock screen
+                // can re-appear and inflict its own orientation on us.  Keep the
+                // orientation stable until this all settles down.
+                return mLastWindowForcedOrientation;
+            }
+        } else {
+            // TODO(multidisplay): Change to the correct display.
+            final WindowList windows = getDefaultWindowListLocked();
+            int pos = windows.size() - 1;
+            while (pos >= 0) {
+                WindowState win = windows.get(pos);
+                pos--;
+                if (win.mAppToken != null) {
+                    // We hit an application window. so the orientation will be determined by the
+                    // app window. No point in continuing further.
+                    break;
+                }
+                if (!win.isVisibleLw() || !win.mPolicyVisibilityAfterAnim) {
+                    continue;
+                }
+                int req = win.mAttrs.screenOrientation;
+                if((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) ||
+                        (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){
+                    continue;
+                }
+
+                if (DEBUG_ORIENTATION) Slog.v(TAG, win + " forcing orientation to " + req);
+                if (mPolicy.isKeyguardHostWindow(win.mAttrs)) {
+                    mLastKeyguardForcedOrientation = req;
+                }
+                return (mLastWindowForcedOrientation = req);
+            }
+            mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+            if (mPolicy.isKeyguardLocked()) {
+                // The screen is locked and no top system window is requesting an orientation.
+                // Return either the orientation of the show-when-locked app (if there is any) or
+                // the orientation of the keyguard. No point in searching from the rest of apps.
+                WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw();
+                AppWindowToken appShowWhenLocked = winShowWhenLocked == null ?
+                        null : winShowWhenLocked.mAppToken;
+                if (appShowWhenLocked != null) {
+                    int req = appShowWhenLocked.requestedOrientation;
+                    if (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
+                        req = mLastKeyguardForcedOrientation;
+                    }
+                    if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + appShowWhenLocked
+                            + " -- show when locked, return " + req);
+                    return req;
+                }
+                if (DEBUG_ORIENTATION) Slog.v(TAG,
+                        "No one is requesting an orientation when the screen is locked");
+                return mLastKeyguardForcedOrientation;
+            }
         }
 
-        // TODO(multidisplay): Change to the correct display.
-        final WindowList windows = getDefaultWindowListLocked();
-        int pos = windows.size() - 1;
-        while (pos >= 0) {
-            WindowState win = windows.get(pos);
-            pos--;
-            if (win.mAppToken != null) {
-                // We hit an application window. so the orientation will be determined by the
-                // app window. No point in continuing further.
-                return (mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
-            }
-            if (!win.isVisibleLw() || !win.mPolicyVisibilityAfterAnim) {
-                continue;
-            }
-            int req = win.mAttrs.screenOrientation;
-            if((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) ||
-                    (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){
-                continue;
-            }
-
-            if (DEBUG_ORIENTATION) Slog.v(TAG, win + " forcing orientation to " + req);
-            return (mLastWindowForcedOrientation=req);
-        }
-        return (mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
-    }
-
-    public int getOrientationFromAppTokensLocked() {
+        // Top system windows are not requesting an orientation. Start searching from apps.
         int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
         boolean findingBehind = false;
         boolean lastFullscreen = false;
@@ -3822,8 +3854,11 @@
                 findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND);
             }
         }
-        if (DEBUG_ORIENTATION) Slog.v(TAG, "No app is requesting an orientation");
-        return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+        if (DEBUG_ORIENTATION) Slog.v(TAG, "No app is requesting an orientation, return "
+                + mForcedAppOrientation);
+        // The next app has not been requested to be visible, so we keep the current orientation
+        // to prevent freezing/unfreezing the display too early.
+        return mForcedAppOrientation;
     }
 
     @Override
@@ -3903,11 +3938,7 @@
     boolean updateOrientationFromAppTokensLocked(boolean inTransaction) {
         long ident = Binder.clearCallingIdentity();
         try {
-            int req = getOrientationFromWindowsLocked();
-            if (req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
-                req = getOrientationFromAppTokensLocked();
-            }
-
+            int req = getOrientationLocked();
             if (req != mForcedAppOrientation) {
                 mForcedAppOrientation = req;
                 //send a message to Policy indicating orientation change to take
@@ -4595,19 +4626,19 @@
                     wtoken.hiddenRequested, HIDE_STACK_CRAWLS ?
                             null : new RuntimeException("here").fillInStackTrace());
 
+            mOpeningApps.remove(wtoken);
+            mClosingApps.remove(wtoken);
+            wtoken.waitingToShow = wtoken.waitingToHide = false;
+            wtoken.hiddenRequested = !visible;
+
             // If we are preparing an app transition, then delay changing
             // the visibility of this token until we execute that transition.
             if (okToDisplay() && mAppTransition.isTransitionSet()) {
-                wtoken.hiddenRequested = !visible;
-
                 if (!wtoken.startingDisplayed) {
                     if (DEBUG_APP_TRANSITIONS) Slog.v(
                             TAG, "Setting dummy animation on: " + wtoken);
                     wtoken.mAppAnimator.setDummyAnimation();
                 }
-                mOpeningApps.remove(wtoken);
-                mClosingApps.remove(wtoken);
-                wtoken.waitingToShow = wtoken.waitingToHide = false;
                 wtoken.inPendingTransaction = true;
                 if (visible) {
                     mOpeningApps.add(wtoken);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 978f5c3..938c1ce 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1434,6 +1434,11 @@
             mOrientationChanging = false;
             mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
                     - mService.mDisplayFreezeTime);
+            // We are assuming the hosting process is dead or in a zombie state.
+            Slog.w(TAG, "Failed to report 'resized' to the client of " + this
+                    + ", removing this window.");
+            mService.mPendingRemove.add(this);
+            mService.requestTraversalLocked();
         }
     }
 
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 36299c2..38d10cf 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -22,7 +22,7 @@
 
 // STATUST: mingw does seem to redefine UNKNOWN_ERROR from our enum value, so a cast is necessary.
 
-#if HAVE_PRINTF_ZD
+#if !defined(_WIN32)
 #  define ZD "%zd"
 #  define ZD_TYPE ssize_t
 #  define STATUST(x) x
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 941a288..24f8168 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -20,7 +20,7 @@
 
 // SSIZE: mingw does not have signed size_t == ssize_t.
 // STATUST: mingw does seem to redefine UNKNOWN_ERROR from our enum value, so a cast is necessary.
-#if HAVE_PRINTF_ZD
+#if !defined(_WIN32)
 #  define SSIZE(x) x
 #  define STATUST(x) x
 #else
diff --git a/tools/aapt/StringPool.cpp b/tools/aapt/StringPool.cpp
index a18e9f1..9908c44 100644
--- a/tools/aapt/StringPool.cpp
+++ b/tools/aapt/StringPool.cpp
@@ -13,7 +13,7 @@
 #include "ResourceTable.h"
 
 // SSIZE: mingw does not have signed size_t == ssize_t.
-#if HAVE_PRINTF_ZD
+#if !defined(_WIN32)
 #  define ZD "%zd"
 #  define ZD_TYPE ssize_t
 #  define SSIZE(x) x
diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp
index b38b2ed..9033cf7 100644
--- a/tools/aapt/XMLNode.cpp
+++ b/tools/aapt/XMLNode.cpp
@@ -18,7 +18,7 @@
 
 // SSIZE: mingw does not have signed size_t == ssize_t.
 // STATUST: mingw does seem to redefine UNKNOWN_ERROR from our enum value, so a cast is necessary.
-#if HAVE_PRINTF_ZD
+#if !defined(_WIN32)
 #  define SSIZE(x) x
 #  define STATUST(x) x
 #else
diff --git a/tools/aapt/printapk.cpp b/tools/aapt/printapk.cpp
deleted file mode 100644
index def6e2e..0000000
--- a/tools/aapt/printapk.cpp
+++ /dev/null
@@ -1,127 +0,0 @@
-#include <utils/ResourceTypes.h>
-#include <utils/String8.h>
-#include <utils/String16.h>
-#include <zipfile/zipfile.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <stdlib.h>
-
-using namespace android;
-
-static int
-usage()
-{
-    fprintf(stderr,
-            "usage: apk APKFILE\n"
-            "\n"
-            "APKFILE   an android packge file produced by aapt.\n"
-            );
-    return 1;
-}
-
-
-int
-main(int argc, char** argv)
-{
-    const char* filename;
-    int fd;
-    ssize_t amt;
-    off_t size;
-    void* buf;
-    zipfile_t zip;
-    zipentry_t entry;
-    void* cookie;
-    void* resfile;
-    int bufsize;
-    int err;
-
-    if (argc != 2) {
-        return usage();
-    }
-
-    filename = argv[1];
-    fd = open(filename, O_RDONLY);
-    if (fd == -1) {
-        fprintf(stderr, "apk: couldn't open file for read: %s\n", filename);
-        return 1;
-    }
-
-    size = lseek(fd, 0, SEEK_END);
-    amt = lseek(fd, 0, SEEK_SET);
-
-    if (size < 0 || amt < 0) {
-        fprintf(stderr, "apk: error determining file size: %s\n", filename);
-        return 1;
-    }
-
-    buf = malloc(size);
-    if (buf == NULL) {
-        fprintf(stderr, "apk: file too big: %s\n", filename);
-        return 1;
-    }
-
-    amt = read(fd, buf, size);
-    if (amt != size) {
-        fprintf(stderr, "apk: error reading file: %s\n", filename);
-        return 1;
-    }
-
-    close(fd);
-
-    zip = init_zipfile(buf, size);
-    if (zip == NULL) {
-        fprintf(stderr, "apk: file doesn't seem to be a zip file: %s\n",
-                filename);
-        return 1;
-    }
-
-    printf("files:\n");
-    cookie = NULL;
-    while ((entry = iterate_zipfile(zip, &cookie))) {
-        char* name = get_zipentry_name(entry);
-        printf("  %s\n", name);
-        free(name);
-    }
-
-    entry = lookup_zipentry(zip, "resources.arsc");
-    if (entry != NULL) {
-        size = get_zipentry_size(entry);
-        bufsize = size + (size / 1000) + 1;
-        resfile = malloc(bufsize);
-
-        err = decompress_zipentry(entry, resfile, bufsize);
-        if (err != 0) {
-            fprintf(stderr, "apk: error decompressing resources.arsc");
-            return 1;
-        }
-
-        ResTable res(resfile, size, resfile);
-        res.print();
-#if 0
-        size_t tableCount = res.getTableCount();
-        printf("Tables: %d\n", (int)tableCount);
-        for (size_t tableIndex=0; tableIndex<tableCount; tableIndex++) {
-            const ResStringPool* strings = res.getTableStringBlock(tableIndex);
-            size_t stringCount = strings->size();
-            for (size_t stringIndex=0; stringIndex<stringCount; stringIndex++) {
-                size_t len;
-                const char16_t* ch = strings->stringAt(stringIndex, &len);
-                String8 s(String16(ch, len));
-                printf("  [%3d] %s\n", (int)stringIndex, s.string());
-            }
-        }
-
-        size_t basePackageCount = res.getBasePackageCount();
-        printf("Base Packages: %d\n", (int)basePackageCount);
-        for (size_t bpIndex=0; bpIndex<basePackageCount; bpIndex++) {
-            const String16 ch = res.getBasePackageName(bpIndex);
-            String8 s = String8(ch);
-            printf("  [%3d] %s\n", (int)bpIndex, s.string());
-        }
-#endif
-    }
-
-
-    return 0;
-}