Merge "[ActivityManager] Avoid improper resume top activity."
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/ActivityThread.java b/core/java/android/app/ActivityThread.java
index bdd9e41..beb244b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -257,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;
@@ -345,7 +348,7 @@
         }
     }
 
-    final class ProviderClientRecord {
+    static final class ProviderClientRecord {
         final String[] mNames;
         final IContentProvider mProvider;
         final ContentProvider mLocalProvider;
@@ -4648,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);
@@ -4747,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;
@@ -4759,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;
@@ -5081,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/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/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/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 0582513..b44adcb 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -552,6 +552,7 @@
     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];
@@ -815,6 +816,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();
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/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index 846d1f1..dcc79be 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -536,26 +536,23 @@
         if (params.getUserAuthenticators().isEmpty()) {
             args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
         } else {
-        // TODO: Pass-in user authenticator IDs once the Keymaster API has stabilized
-//            for (int userAuthenticatorId : params.getUserAuthenticators()) {
-//                args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_ID, userAuthenticatorId);
-//            }
+            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());
         }
-        if (params.getKeyValidityStart() != null) {
-            args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, params.getKeyValidityStart());
-        }
-        if (params.getKeyValidityForOriginationEnd() != null) {
-            args.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
-                    params.getKeyValidityForOriginationEnd());
-        }
-        if (params.getKeyValidityForConsumptionEnd() != null) {
-            args.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
-                    params.getKeyValidityForConsumptionEnd());
-        }
+        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);
diff --git a/keystore/java/android/security/AndroidKeyStoreProvider.java b/keystore/java/android/security/AndroidKeyStoreProvider.java
index 39f9d9c..a7c2ddb 100644
--- a/keystore/java/android/security/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/AndroidKeyStoreProvider.java
@@ -16,8 +16,12 @@
 
 package android.security;
 
+import java.lang.reflect.Method;
 import java.security.Provider;
 
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+
 /**
  * A provider focused on providing JCA interfaces for the Android KeyStore.
  *
@@ -71,4 +75,42 @@
         put("Cipher." + transformation, implClass);
         put("Cipher." + transformation + " SupportedKeyClasses", KeyStoreSecretKey.class.getName());
     }
+
+    /**
+     * 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();
+        }
+        if ((!(cryptoPrimitive instanceof Mac)) && (!(cryptoPrimitive instanceof Cipher))) {
+            throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive);
+        }
+        Object spi;
+        // TODO: Replace this Reflection based codewith direct invocations once the libcore changes
+        // are in.
+        try {
+            Method getSpiMethod = cryptoPrimitive.getClass().getDeclaredMethod("getSpi");
+            getSpiMethod.setAccessible(true);
+            spi = getSpiMethod.invoke(cryptoPrimitive);
+        } catch (ReflectiveOperationException e) {
+            throw new IllegalArgumentException(
+                    "Unsupported crypto primitive: " + cryptoPrimitive, e);
+        }
+        if (!(spi instanceof KeyStoreCryptoOperation)) {
+            throw new IllegalArgumentException(
+                    "Crypto primitive not backed by Android KeyStore: " + cryptoPrimitive
+                    + ", spi: " + spi);
+        }
+        return ((KeyStoreCryptoOperation) spi).getOperationHandle();
+    }
 }
diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java
index 87f0d8e..5219086 100644
--- a/keystore/java/android/security/KeyStoreCipherSpi.java
+++ b/keystore/java/android/security/KeyStoreCipherSpi.java
@@ -45,7 +45,7 @@
  *
  * @hide
  */
-public abstract class KeyStoreCipherSpi extends CipherSpi {
+public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCryptoOperation {
 
     public abstract static class AES extends KeyStoreCipherSpi {
         protected AES(@KeyStoreKeyConstraints.BlockModeEnum int blockMode,
@@ -129,6 +129,7 @@
      * error conditions in between.
      */
     private IBinder mOperationToken;
+    private Long mOperationHandle;
     private KeyStoreCryptoOperationChunkedStreamer mMainDataStreamer;
 
     protected KeyStoreCipherSpi(
@@ -192,6 +193,7 @@
             mOperationToken = null;
             mKeyStore.abort(operationToken);
         }
+        mOperationHandle = null;
         mMainDataStreamer = null;
         mAdditionalEntropyForBegin = null;
     }
@@ -222,14 +224,14 @@
         if (opResult == null) {
             throw new KeyStoreConnectException();
         } else if (opResult.resultCode != KeyStore.NO_ERROR) {
-            throw new CryptoOperationException("Failed to start keystore operation",
-                    KeymasterUtils.getExceptionForKeymasterError(opResult.resultCode));
+            throw KeymasterUtils.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(
@@ -249,7 +251,7 @@
         try {
             output = mMainDataStreamer.update(input, inputOffset, inputLen);
         } catch (KeymasterException e) {
-            throw new CryptoOperationException("Keystore operation failed", e);
+            throw KeymasterUtils.getCryptoOperationException(e);
         }
 
         if (output.length == 0) {
@@ -294,7 +296,7 @@
                 case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
                     throw new AEADBadTagException();
                 default:
-                    throw new CryptoOperationException("Keystore operation failed", e);
+                    throw KeymasterUtils.getCryptoOperationException(e);
             }
         }
 
@@ -348,6 +350,23 @@
         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.
 
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
index 2b279f6..993614b 100644
--- a/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
+++ b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
@@ -120,7 +120,7 @@
             if (opResult == null) {
                 throw new KeyStoreConnectException();
             } else if (opResult.resultCode != KeyStore.NO_ERROR) {
-                throw KeymasterUtils.getExceptionForKeymasterError(opResult.resultCode);
+                throw KeymasterUtils.getKeymasterException(opResult.resultCode);
             }
 
             if (opResult.inputConsumed == chunk.length) {
@@ -203,7 +203,7 @@
         if (opResult == null) {
             throw new KeyStoreConnectException();
         } else if (opResult.resultCode != KeyStore.NO_ERROR) {
-            throw KeymasterUtils.getExceptionForKeymasterError(opResult.resultCode);
+            throw KeymasterUtils.getKeymasterException(opResult.resultCode);
         }
 
         return concat(output, opResult.output);
@@ -227,7 +227,7 @@
         if (opResult == null) {
             throw new KeyStoreConnectException();
         } else if (opResult.resultCode != KeyStore.NO_ERROR) {
-            throw KeymasterUtils.getExceptionForKeymasterError(opResult.resultCode);
+            throw KeymasterUtils.getKeymasterException(opResult.resultCode);
         }
 
         if (opResult.inputConsumed < chunk.length) {
diff --git a/keystore/java/android/security/KeyStoreHmacSpi.java b/keystore/java/android/security/KeyStoreHmacSpi.java
index 939b41c..1297cc2 100644
--- a/keystore/java/android/security/KeyStoreHmacSpi.java
+++ b/keystore/java/android/security/KeyStoreHmacSpi.java
@@ -33,7 +33,7 @@
  *
  * @hide
  */
-public abstract class KeyStoreHmacSpi extends MacSpi {
+public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOperation {
 
     public static class HmacSHA256 extends KeyStoreHmacSpi {
         public HmacSHA256() {
@@ -50,6 +50,7 @@
     // 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;
@@ -87,6 +88,7 @@
             mOperationToken = null;
             mKeyStore.abort(operationToken);
         }
+        mOperationHandle = null;
         mChunkedStreamer = null;
 
         KeymasterArguments keymasterArgs = new KeymasterArguments();
@@ -101,13 +103,13 @@
         if (opResult == null) {
             throw new KeyStoreConnectException();
         } else if (opResult.resultCode != KeyStore.NO_ERROR) {
-            throw new CryptoOperationException("Failed to start keystore operation",
-                    KeymasterUtils.getExceptionForKeymasterError(opResult.resultCode));
+            throw KeymasterUtils.getCryptoOperationException(opResult.resultCode);
         }
         mOperationToken = opResult.token;
         if (mOperationToken == null) {
             throw new CryptoOperationException("Keystore returned null operation token");
         }
+        mOperationHandle = opResult.operationHandle;
         mChunkedStreamer = new KeyStoreCryptoOperationChunkedStreamer(
                 new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
                         mKeyStore, mOperationToken));
@@ -128,7 +130,7 @@
         try {
             output = mChunkedStreamer.update(input, offset, len);
         } catch (KeymasterException e) {
-            throw new CryptoOperationException("Keystore operation failed", e);
+            throw KeymasterUtils.getCryptoOperationException(e);
         }
         if ((output != null) && (output.length != 0)) {
             throw new CryptoOperationException("Update operation unexpectedly produced output");
@@ -145,7 +147,7 @@
         try {
             result = mChunkedStreamer.doFinal(null, 0, 0);
         } catch (KeymasterException e) {
-            throw new CryptoOperationException("Keystore operation failed", e);
+            throw KeymasterUtils.getCryptoOperationException(e);
         }
 
         engineReset();
@@ -157,11 +159,15 @@
         try {
             IBinder operationToken = mOperationToken;
             if (operationToken != null) {
-                mOperationToken = null;
                 mKeyStore.abort(operationToken);
             }
         } finally {
             super.finalize();
         }
     }
+
+    @Override
+    public Long getOperationHandle() {
+        return mOperationHandle;
+    }
 }
diff --git a/keystore/java/android/security/KeyStoreKeyConstraints.java b/keystore/java/android/security/KeyStoreKeyConstraints.java
index c011083..c27ccb1 100644
--- a/keystore/java/android/security/KeyStoreKeyConstraints.java
+++ b/keystore/java/android/security/KeyStoreKeyConstraints.java
@@ -23,7 +23,10 @@
 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.
@@ -520,4 +523,87 @@
             }
         }
     }
+
+    @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
index 7796de8..69533b4 100644
--- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
@@ -23,6 +23,7 @@
 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;
@@ -136,26 +137,23 @@
         if (spec.getUserAuthenticators().isEmpty()) {
             args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
         } else {
-        // TODO: Pass-in user authenticator IDs once the Keymaster API has stabilized
-//            for (int userAuthenticatorId : spec.getUserAuthenticators()) {
-//                args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_ID, userAuthenticatorId);
-//            }
+            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());
         }
-        if (spec.getKeyValidityStart() != null) {
-            args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, spec.getKeyValidityStart());
-        }
-        if (spec.getKeyValidityForOriginationEnd() != null) {
-            args.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
-                    spec.getKeyValidityForOriginationEnd());
-        }
-        if (spec.getKeyValidityForConsumptionEnd() != null) {
-            args.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
-                    spec.getKeyValidityForConsumptionEnd());
-        }
+        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)) {
@@ -175,8 +173,7 @@
         int errorCode = mKeyStore.generateKey(
                 keyAliasInKeystore, args, additionalEntropy, flags, new KeyCharacteristics());
         if (errorCode != KeyStore.NO_ERROR) {
-            throw new CryptoOperationException("Failed to generate key",
-                    KeymasterUtils.getExceptionForKeymasterError(errorCode));
+            throw KeymasterUtils.getCryptoOperationException(errorCode);
         }
         String keyAlgorithmJCA =
                 KeyStoreKeyConstraints.Algorithm.toJCASecretKeyAlgorithm(mAlgorithm, mDigest);
diff --git a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
index 8921ba1..f552759 100644
--- a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
@@ -22,7 +22,7 @@
 import java.security.InvalidKeyException;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.KeySpec;
-import java.util.Collections;
+import java.util.Date;
 import java.util.Set;
 
 import javax.crypto.SecretKey;
@@ -113,22 +113,41 @@
             throw new InvalidKeySpecException("Unsupported key characteristic", e);
         }
 
-        // TODO: Read user authentication IDs once the Keymaster API has stabilized
-        Set<Integer> userAuthenticators = Collections.emptySet();
-        Set<Integer> teeBackedUserAuthenticators = Collections.emptySet();
-//        Set<Integer> userAuthenticators = new HashSet<Integer>(
-//                getInts(keyCharacteristics, KeymasterDefs.KM_TAG_USER_AUTH_ID));
-//        Set<Integer> teeBackedUserAuthenticators = new HashSet<Integer>(
-//                keyCharacteristics.hwEnforced.getInts(KeymasterDefs.KM_TAG_USER_AUTH_ID));
+        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,
-                KeymasterUtils.getDate(keyCharacteristics, KeymasterDefs.KM_TAG_ACTIVE_DATETIME),
-                KeymasterUtils.getDate(keyCharacteristics,
-                        KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME),
-                KeymasterUtils.getDate(keyCharacteristics,
-                        KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME),
+                keyValidityStart,
+                keyValidityForOriginationEnd,
+                keyValidityForConsumptionEnd,
                 purposes,
                 algorithm,
                 padding,
diff --git a/keystore/java/android/security/KeymasterUtils.java b/keystore/java/android/security/KeymasterUtils.java
index 6bb9636..2645cf4 100644
--- a/keystore/java/android/security/KeymasterUtils.java
+++ b/keystore/java/android/security/KeymasterUtils.java
@@ -29,7 +29,7 @@
 public abstract class KeymasterUtils {
     private KeymasterUtils() {}
 
-    public static KeymasterException getExceptionForKeymasterError(int keymasterErrorCode) {
+    public static KeymasterException getKeymasterException(int keymasterErrorCode) {
         switch (keymasterErrorCode) {
             case KeymasterDefs.KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT:
                 // The name of this parameter significantly differs between Keymaster and framework
@@ -42,6 +42,19 @@
         }
     }
 
+    public static CryptoOperationException getCryptoOperationException(KeymasterException e) {
+        switch (e.getErrorCode()) {
+            case KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED:
+                return new UserNotAuthenticatedException();
+            default:
+                return new CryptoOperationException("Crypto operation failed", e);
+        }
+    }
+
+    public static CryptoOperationException getCryptoOperationException(int keymasterErrorCode) {
+        return getCryptoOperationException(getKeymasterException(keymasterErrorCode));
+    }
+
     public static Integer getInt(KeyCharacteristics keyCharacteristics, int tag) {
         if (keyCharacteristics.hwEnforced.containsTag(tag)) {
             return keyCharacteristics.hwEnforced.getInt(tag, -1);
diff --git a/keystore/java/android/security/UserNotAuthenticatedException.java b/keystore/java/android/security/UserNotAuthenticatedException.java
new file mode 100644
index 0000000..b45817c
--- /dev/null
+++ b/keystore/java/android/security/UserNotAuthenticatedException.java
@@ -0,0 +1,33 @@
+/*
+ * 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 {
+    public UserNotAuthenticatedException() {
+        super("User not authenticated");
+    }
+
+    public UserNotAuthenticatedException(String message) {
+        super(message);
+    }
+}
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/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;
     }
 
     /**