Merge "Show Carrier Network Change icon SysUI even while connected" into mnc-dev
diff --git a/api/current.txt b/api/current.txt
index d38e75a..418cbe5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2716,6 +2716,7 @@
     method public final android.os.IBinder getIBinder();
     method public abstract android.os.Bundle hasFeatures(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String[]) throws android.accounts.NetworkErrorException;
     method public abstract android.os.Bundle updateCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
+    field public static final java.lang.String KEY_CUSTOM_TOKEN_EXPIRY = "android.accounts.expiry";
   }
 
   public class Account implements android.os.Parcelable {
diff --git a/api/system-current.txt b/api/system-current.txt
index 44d8c8e..5766edc 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -2797,6 +2797,7 @@
     method public final android.os.IBinder getIBinder();
     method public abstract android.os.Bundle hasFeatures(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String[]) throws android.accounts.NetworkErrorException;
     method public abstract android.os.Bundle updateCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
+    field public static final java.lang.String KEY_CUSTOM_TOKEN_EXPIRY = "android.accounts.expiry";
   }
 
   public class Account implements android.os.Parcelable {
@@ -6486,7 +6487,9 @@
     method public static boolean checkBluetoothAddress(java.lang.String);
     method public void closeProfileProxy(int, android.bluetooth.BluetoothProfile);
     method public boolean disable();
+    method public boolean disableBLE();
     method public boolean enable();
+    method public boolean enableBLE();
     method public java.lang.String getAddress();
     method public android.bluetooth.le.BluetoothLeAdvertiser getBluetoothLeAdvertiser();
     method public android.bluetooth.le.BluetoothLeScanner getBluetoothLeScanner();
@@ -6502,6 +6505,7 @@
     method public boolean isBleScanAlwaysAvailable();
     method public boolean isDiscovering();
     method public boolean isEnabled();
+    method public boolean isLeEnabled();
     method public boolean isMultipleAdvertisementSupported();
     method public boolean isOffloadedFilteringSupported();
     method public boolean isOffloadedScanBatchingSupported();
@@ -6512,6 +6516,7 @@
     method public deprecated boolean startLeScan(android.bluetooth.BluetoothAdapter.LeScanCallback);
     method public deprecated boolean startLeScan(java.util.UUID[], android.bluetooth.BluetoothAdapter.LeScanCallback);
     method public deprecated void stopLeScan(android.bluetooth.BluetoothAdapter.LeScanCallback);
+    field public static final java.lang.String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
     field public static final java.lang.String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED";
     field public static final java.lang.String ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
     field public static final java.lang.String ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED";
diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java
index dbc9051..3e4a66d 100644
--- a/core/java/android/accounts/AbstractAccountAuthenticator.java
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -108,6 +108,14 @@
 public abstract class AbstractAccountAuthenticator {
     private static final String TAG = "AccountAuthenticator";
 
+    /**
+     * Bundle key used for the {@code long} expiration time (in millis from the unix epoch) of the
+     * associated auth token.
+     *
+     * @see #getAuthToken
+     */
+    public static final String KEY_CUSTOM_TOKEN_EXPIRY = "android.accounts.expiry";
+
     private final Context mContext;
 
     public AbstractAccountAuthenticator(Context context) {
@@ -115,6 +123,7 @@
     }
 
     private class Transport extends IAccountAuthenticator.Stub {
+        @Override
         public void addAccount(IAccountAuthenticatorResponse response, String accountType,
                 String authTokenType, String[] features, Bundle options)
                 throws RemoteException {
@@ -140,6 +149,7 @@
             }
         }
 
+        @Override
         public void confirmCredentials(IAccountAuthenticatorResponse response,
                 Account account, Bundle options) throws RemoteException {
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -162,6 +172,7 @@
             }
         }
 
+        @Override
         public void getAuthTokenLabel(IAccountAuthenticatorResponse response,
                 String authTokenType)
                 throws RemoteException {
@@ -184,6 +195,7 @@
             }
         }
 
+        @Override
         public void getAuthToken(IAccountAuthenticatorResponse response,
                 Account account, String authTokenType, Bundle loginOptions)
                 throws RemoteException {
@@ -209,6 +221,7 @@
             }
         }
 
+        @Override
         public void updateCredentials(IAccountAuthenticatorResponse response, Account account,
                 String authTokenType, Bundle loginOptions) throws RemoteException {
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -234,6 +247,7 @@
             }
         }
 
+        @Override
         public void editProperties(IAccountAuthenticatorResponse response,
                 String accountType) throws RemoteException {
             checkBinderPermission();
@@ -248,6 +262,7 @@
             }
         }
 
+        @Override
         public void hasFeatures(IAccountAuthenticatorResponse response,
                 Account account, String[] features) throws RemoteException {
             checkBinderPermission();
@@ -262,6 +277,7 @@
             }
         }
 
+        @Override
         public void getAccountRemovalAllowed(IAccountAuthenticatorResponse response,
                 Account account) throws RemoteException {
             checkBinderPermission();
@@ -276,6 +292,7 @@
             }
         }
 
+        @Override
         public void getAccountCredentialsForCloning(IAccountAuthenticatorResponse response,
                 Account account) throws RemoteException {
             checkBinderPermission();
@@ -291,6 +308,7 @@
             }
         }
 
+        @Override
         public void addAccountFromCredentials(IAccountAuthenticatorResponse response,
                 Account account,
                 Bundle accountCredentials) throws RemoteException {
@@ -410,21 +428,42 @@
     public abstract Bundle confirmCredentials(AccountAuthenticatorResponse response,
             Account account, Bundle options)
             throws NetworkErrorException;
+
     /**
-     * Gets the authtoken for an account.
+     * Gets an authtoken for an account.
+     *
+     * If not {@code null}, the resultant {@link Bundle} will contain different sets of keys
+     * depending on whether a token was successfully issued and, if not, whether one
+     * could be issued via some {@link android.app.Activity}.
+     * <p>
+     * If a token cannot be provided without some additional activity, the Bundle should contain
+     * {@link AccountManager#KEY_INTENT} with an associated {@link Intent}. On the other hand, if
+     * there is no such activity, then a Bundle containing
+     * {@link AccountManager#KEY_ERROR_CODE} and {@link AccountManager#KEY_ERROR_MESSAGE} should be
+     * returned.
+     * <p>
+     * If a token can be successfully issued, the implementation should return the
+     * {@link AccountManager#KEY_ACCOUNT_NAME} and {@link AccountManager#KEY_ACCOUNT_TYPE} of the
+     * account associated with the token as well as the {@link AccountManager#KEY_AUTHTOKEN}. In
+     * addition {@link AbstractAccountAuthenticator} implementations that declare themselves
+     * {@code android:customTokens=true} may also provide a non-negative {@link
+     * #KEY_CUSTOM_TOKEN_EXPIRY} long value containing the expiration timestamp of the expiration
+     * time (in millis since the unix epoch).
+     * <p>
+     * Implementers should assume that tokens will be cached on the basis of account and
+     * authTokenType. The system may ignore the contents of the supplied options Bundle when
+     * determining to re-use a cached token. Furthermore, implementers should assume a supplied
+     * expiration time will be treated as non-binding advice.
+     * <p>
+     * Finally, note that for android:customTokens=false authenticators, tokens are cached
+     * indefinitely until some client calls {@link
+     * AccountManager#invalidateAuthToken(String,String)}.
+     *
      * @param response to send the result back to the AccountManager, will never be null
      * @param account the account whose credentials are to be retrieved, will never be null
      * @param authTokenType the type of auth token to retrieve, will never be null
      * @param options a Bundle of authenticator-specific options, may be null
-     * @return a Bundle result or null if the result is to be returned via the response. The result
-     * will contain either:
-     * <ul>
-     * <li> {@link AccountManager#KEY_INTENT}, or
-     * <li> {@link AccountManager#KEY_ACCOUNT_NAME}, {@link AccountManager#KEY_ACCOUNT_TYPE},
-     * and {@link AccountManager#KEY_AUTHTOKEN}, or
-     * <li> {@link AccountManager#KEY_ERROR_CODE} and {@link AccountManager#KEY_ERROR_MESSAGE} to
-     * indicate an error
-     * </ul>
+     * @return a Bundle result or null if the result is to be returned via the response.
      * @throws NetworkErrorException if the authenticator could not honor the request due to a
      * network error
      */
@@ -518,6 +557,7 @@
     public Bundle getAccountCredentialsForCloning(final AccountAuthenticatorResponse response,
             final Account account) throws NetworkErrorException {
         new Thread(new Runnable() {
+            @Override
             public void run() {
                 Bundle result = new Bundle();
                 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
@@ -543,6 +583,7 @@
             Account account,
             Bundle accountCredentials) throws NetworkErrorException {
         new Thread(new Runnable() {
+            @Override
             public void run() {
                 Bundle result = new Bundle();
                 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index b22b914..8107a97 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -410,6 +410,7 @@
      * Broadcast Action: The Bluetooth adapter state has changed in LE only mode.
      * @hide
      */
+    @SystemApi
     public static final String ACTION_BLE_STATE_CHANGED =
         "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
 
@@ -620,17 +621,18 @@
      * @return true if the local Bluetooth LE adapter is turned on
      * @hide
      */
-     public boolean isLeEnabled() {
-        final int state = getLeState();
-        if (state == BluetoothAdapter.STATE_ON) {
-            if (DBG) Log.d (TAG, "STATE_ON");
-        } else if (state == BluetoothAdapter.STATE_BLE_ON) {
-            if (DBG) Log.d (TAG, "STATE_BLE_ON");
-        } else {
-            if (DBG) Log.d (TAG, "STATE_OFF");
-            return false;
-        }
-        return true;
+    @SystemApi
+    public boolean isLeEnabled() {
+       final int state = getLeState();
+       if (state == BluetoothAdapter.STATE_ON) {
+           if (DBG) Log.d (TAG, "STATE_ON");
+       } else if (state == BluetoothAdapter.STATE_BLE_ON) {
+           if (DBG) Log.d (TAG, "STATE_BLE_ON");
+       } else {
+           if (DBG) Log.d (TAG, "STATE_OFF");
+           return false;
+       }
+       return true;
     }
 
     /**
@@ -680,6 +682,7 @@
      *         immediate error
      * @hide
      */
+    @SystemApi
     public boolean disableBLE() {
         if (!isBleScanAlwaysAvailable()) return false;
 
@@ -742,6 +745,7 @@
      *         immediate error
      * @hide
      */
+    @SystemApi
     public boolean enableBLE() {
         if (!isBleScanAlwaysAvailable()) return false;
 
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index f0c3f2d..8bcd5d1 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -160,7 +160,6 @@
 
         final TypedValue v = mValue;
         if (getValueAt(index, v)) {
-            StrictMode.noteResourceMismatch(v);
             return v.coerceToString();
         }
 
@@ -183,24 +182,6 @@
      */
     @Nullable
     public String getString(int index) {
-        return getString(index, true);
-    }
-
-    /**
-     * Returns a string representation of the value at the given index,
-     * optionally throwing a resource mismatch strict mode violation if the
-     * value must be coerced to a string.
-     *
-     * @param index the index of the attribute to retrieve
-     * @param strict {@code true} to throw a strict mode violation for string
-     *               coercion, {@code false} otherwise
-     * @return a string representation of the value at the given index, or
-     *         {@code null} if the resource could not be coerced to a string
-     * @see StrictMode#noteResourceMismatch(Object)
-     * @hide Used internally for view attribute inspection.
-     */
-    @Nullable
-    public String getString(int index, boolean strict) {
         if (mRecycled) {
             throw new RuntimeException("Cannot make calls to a recycled instance!");
         }
@@ -216,9 +197,6 @@
 
         final TypedValue v = mValue;
         if (getValueAt(index, v)) {
-            if (strict) {
-                StrictMode.noteResourceMismatch(v);
-            }
             final CharSequence cs = v.coerceToString();
             return cs != null ? cs.toString() : null;
         }
@@ -292,7 +270,6 @@
 
         final TypedValue v = mValue;
         if (getValueAt(index, v)) {
-            StrictMode.noteResourceMismatch(v);
             final CharSequence cs = v.coerceToString();
             return cs != null ? cs.toString() : null;
         }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3e621b1..cfd504d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4510,7 +4510,7 @@
             }
 
             attributes[i] = resourceName;
-            attributes[i + 1] = t.getString(index, false);
+            attributes[i + 1] = t.getString(index);
             i += 2;
         }
 
@@ -6351,7 +6351,7 @@
             }
             View next = rootView.findViewInsideOutShouldExist(this,
                     mAccessibilityTraversalBeforeId);
-            if (next != null) {
+            if (next != null && next.includeForAccessibility()) {
                 info.setTraversalBefore(next);
             }
         }
@@ -6363,7 +6363,7 @@
             }
             View next = rootView.findViewInsideOutShouldExist(this,
                     mAccessibilityTraversalAfterId);
-            if (next != null) {
+            if (next != null && next.includeForAccessibility()) {
                 info.setTraversalAfter(next);
             }
         }
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index d214a20..3315c89 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -17,6 +17,7 @@
 package com.android.server.accounts;
 
 import android.Manifest;
+import android.accounts.AbstractAccountAuthenticator;
 import android.accounts.Account;
 import android.accounts.AccountAndUser;
 import android.accounts.AccountAuthenticatorResponse;
@@ -49,6 +50,7 @@
 import android.content.pm.RegisteredServicesCache;
 import android.content.pm.RegisteredServicesCacheListener;
 import android.content.pm.ResolveInfo;
+import android.content.pm.Signature;
 import android.content.pm.UserInfo;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
@@ -84,6 +86,11 @@
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.sql.Timestamp;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -93,6 +100,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -166,6 +174,10 @@
     private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
             new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
     private static final Intent ACCOUNTS_CHANGED_INTENT;
+    static {
+        ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
+        ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+    }
 
     private static final String COUNT_OF_MATCHING_GRANTS = ""
             + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
@@ -177,6 +189,7 @@
 
     private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT =
             AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
+
     private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE,
             AUTHTOKENS_AUTHTOKEN};
 
@@ -205,6 +218,10 @@
         /** protected by the {@link #cacheLock} */
         private final HashMap<Account, HashMap<String, String>> authTokenCache =
                 new HashMap<Account, HashMap<String, String>>();
+
+        /** protected by the {@link #cacheLock} */
+        private final HashMap<Account, WeakReference<TokenCache>> accountTokenCaches = new HashMap<>();
+
         /**
          * protected by the {@link #cacheLock}
          *
@@ -237,12 +254,6 @@
             new AtomicReference<AccountManagerService>();
     private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
 
-    static {
-        ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
-        ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-    }
-
-
     /**
      * This should only be called by system code. One should only call this after the service
      * has started.
@@ -425,6 +436,7 @@
                         final Account account = new Account(accountName, accountType);
                         accounts.userDataCache.remove(account);
                         accounts.authTokenCache.remove(account);
+                        accounts.accountTokenCaches.remove(account);
                     } else {
                         ArrayList<String> accountNames = accountNamesByType.get(accountType);
                         if (accountNames == null) {
@@ -1337,9 +1349,10 @@
 
     @Override
     public void invalidateAuthToken(String accountType, String authToken) {
+        int callerUid = Binder.getCallingUid();
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "invalidateAuthToken: accountType " + accountType
-                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", caller's uid " + callerUid
                     + ", pid " + Binder.getCallingPid());
         }
         if (accountType == null) throw new IllegalArgumentException("accountType is null");
@@ -1353,6 +1366,7 @@
                 db.beginTransaction();
                 try {
                     invalidateAuthTokenLocked(accounts, db, accountType, authToken);
+                    invalidateCustomTokenLocked(accounts, accountType, authToken);
                     db.setTransactionSuccessful();
                 } finally {
                     db.endTransaction();
@@ -1363,6 +1377,26 @@
         }
     }
 
+    private void invalidateCustomTokenLocked(
+            UserAccounts accounts,
+            String accountType,
+            String authToken) {
+        if (authToken == null || accountType == null) {
+            return;
+        }
+        // Also wipe out cached token in memory.
+        for (Account a : accounts.accountTokenCaches.keySet()) {
+            if (a.type.equals(accountType)) {
+                WeakReference<TokenCache> tokenCacheRef =
+                        accounts.accountTokenCaches.get(a);
+                TokenCache cache = null;
+                if (tokenCacheRef != null && (cache = tokenCacheRef.get()) != null) {
+                    cache.remove(authToken);
+                }
+            }
+        }
+    }
+
     private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db,
             String accountType, String authToken) {
         if (authToken == null || accountType == null) {
@@ -1385,14 +1419,41 @@
                 String accountName = cursor.getString(1);
                 String authTokenType = cursor.getString(2);
                 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
-                writeAuthTokenIntoCacheLocked(accounts, db, new Account(accountName, accountType),
-                        authTokenType, null);
+                writeAuthTokenIntoCacheLocked(
+                        accounts,
+                        db,
+                        new Account(accountName, accountType),
+                        authTokenType,
+                        null);
             }
         } finally {
             cursor.close();
         }
     }
 
+    private void saveCachedToken(
+            UserAccounts accounts,
+            Account account,
+            String callerPkg,
+            byte[] callerSigDigest,
+            String tokenType,
+            String token,
+            long expiryMillis) {
+
+        if (account == null || tokenType == null || callerPkg == null || callerSigDigest == null) {
+            return;
+        }
+        cancelNotification(getSigninRequiredNotificationId(accounts, account),
+                new UserHandle(accounts.userId));
+        synchronized (accounts.cacheLock) {
+            TokenCache cache = getTokenCacheForAccountLocked(accounts, account);
+            if (cache != null) {
+                cache.put(token, tokenType, callerPkg, callerSigDigest, expiryMillis);
+            }
+            return;
+        }
+    }
+
     private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type,
             String authToken) {
         if (account == null || type == null) {
@@ -1510,6 +1571,7 @@
                     db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
                     db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
                     accounts.authTokenCache.remove(account);
+                    accounts.accountTokenCaches.remove(account);
                     db.setTransactionSuccessful();
 
                     String action = (password == null || password.length() == 0) ?
@@ -1673,9 +1735,14 @@
     }
 
     @Override
-    public void getAuthToken(IAccountManagerResponse response, final Account account,
-            final String authTokenType, final boolean notifyOnAuthFailure,
-            final boolean expectActivityLaunch, Bundle loginOptionsIn) {
+    public void getAuthToken(
+            IAccountManagerResponse response,
+            final Account account,
+            final String authTokenType,
+            final boolean notifyOnAuthFailure,
+            final boolean expectActivityLaunch,
+            final Bundle loginOptions) {
+
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "getAuthToken: " + account
                     + ", response " + response
@@ -1707,19 +1774,33 @@
         final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
         authenticatorInfo = mAuthenticatorCache.getServiceInfo(
                 AuthenticatorDescription.newKey(account.type), accounts.userId);
+
         final boolean customTokens =
-            authenticatorInfo != null && authenticatorInfo.type.customTokens;
+                authenticatorInfo != null && authenticatorInfo.type.customTokens;
 
         // skip the check if customTokens
         final int callerUid = Binder.getCallingUid();
         final boolean permissionGranted = customTokens ||
             permissionIsGranted(account, authTokenType, callerUid);
 
-        final Bundle loginOptions = (loginOptionsIn == null) ? new Bundle() :
-            loginOptionsIn;
+        // Get the calling package. We will use it for the purpose of caching.
+        final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
+        List<String> callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
+        if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) {
+            String msg = String.format(
+                    "Uid %s is attempting to illegally masquerade as package %s!",
+                    callerUid,
+                    callerPkg);
+            throw new SecurityException(msg);
+        }
+
         // let authenticator know the identity of the caller
         loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
         loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
+
+        // Distill the caller's package signatures into a single digest.
+        final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
+
         if (notifyOnAuthFailure) {
             loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
         }
@@ -1740,6 +1821,28 @@
                 }
             }
 
+            if (customTokens) {
+                /*
+                 * Look up tokens in the new cache only if the loginOptions don't have parameters
+                 * outside of those expected to be injected by the AccountManager, e.g.
+                 * ANDORID_PACKAGE_NAME.
+                 */
+                String token = readCachedTokenInternal(
+                        accounts,
+                        account,
+                        authTokenType,
+                        callerPkg,
+                        callerPkgSigDigest);
+                if (token != null) {
+                    Bundle result = new Bundle();
+                    result.putString(AccountManager.KEY_AUTHTOKEN, token);
+                    result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
+                    result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
+                    onResult(response, result);
+                    return;
+                }
+            }
+
             new Session(accounts, response, account.type, expectActivityLaunch,
                     false /* stripAuthTokenFromResult */, account.name,
                     false /* authDetailsRequired */) {
@@ -1786,9 +1889,26 @@
                                         "the type and name should not be empty");
                                 return;
                             }
+                            Account resultAccount = new Account(name, type);
                             if (!customTokens) {
-                                saveAuthTokenToDatabase(mAccounts, new Account(name, type),
-                                        authTokenType, authToken);
+                                saveAuthTokenToDatabase(
+                                        mAccounts,
+                                        resultAccount,
+                                        authTokenType,
+                                        authToken);
+                            }
+                            long expiryMillis = result.getLong(
+                                    AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0L);
+                            if (customTokens
+                                    && expiryMillis > System.currentTimeMillis()) {
+                                saveCachedToken(
+                                        mAccounts,
+                                        account,
+                                        callerPkg,
+                                        callerPkgSigDigest,
+                                        authTokenType,
+                                        authToken,
+                                        expiryMillis);
                             }
                         }
 
@@ -1807,6 +1927,25 @@
         }
     }
 
+    private byte[] calculatePackageSignatureDigest(String callerPkg) {
+        MessageDigest digester;
+        try {
+            digester = MessageDigest.getInstance("SHA-256");
+            PackageInfo pkgInfo = mPackageManager.getPackageInfo(
+                    callerPkg, PackageManager.GET_SIGNATURES);
+            for (Signature sig : pkgInfo.signatures) {
+                digester.update(sig.toByteArray());
+            }
+        } catch (NoSuchAlgorithmException x) {
+            Log.wtf(TAG, "SHA-256 should be available", x);
+            digester = null;
+        } catch (NameNotFoundException e) {
+            Log.w(TAG, "Could not find packageinfo for: " + callerPkg);
+            digester = null;
+        }
+        return (digester == null) ? null : digester.digest();
+    }
+
     private void createNoCredentialsPermissionNotification(Account account, Intent intent,
             int userId) {
         int uid = intent.getIntExtra(
@@ -2745,13 +2884,13 @@
             if (result != null) {
                 boolean isSuccessfulConfirmCreds = result.getBoolean(
                         AccountManager.KEY_BOOLEAN_RESULT, false);
-                boolean isSuccessfulUpdateCreds = 
+                boolean isSuccessfulUpdateCreds =
                         result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
                         && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
-                // We should only update lastAuthenticated time, if 
+                // We should only update lastAuthenticated time, if
                 // mUpdateLastAuthenticatedTime is true and the confirmRequest
                 // or updateRequest was successful
-                boolean needUpdate = mUpdateLastAuthenticatedTime 
+                boolean needUpdate = mUpdateLastAuthenticatedTime
                         && (isSuccessfulConfirmCreds || isSuccessfulUpdateCreds);
                 if (needUpdate || mAuthDetailsRequired) {
                     boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
@@ -3398,7 +3537,6 @@
                 return;
             }
         }
-
         String msg = "caller uid " + uid + " lacks any of " + TextUtils.join(",", permissions);
         Log.w(TAG, "  " + msg);
         throw new SecurityException(msg);
@@ -3796,6 +3934,18 @@
         }
     }
 
+    protected String readCachedTokenInternal(
+            UserAccounts accounts,
+            Account account,
+            String tokenType,
+            String callingPackage,
+            byte[] pkgSigDigest) {
+        synchronized (accounts.cacheLock) {
+            TokenCache cache = getTokenCacheForAccountLocked(accounts, account);
+            return cache.get(tokenType, callingPackage, pkgSigDigest);
+        }
+    }
+
     protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
             Account account, String key, String value) {
         HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
@@ -3877,6 +4027,17 @@
         return authTokensForAccount;
     }
 
+    protected TokenCache getTokenCacheForAccountLocked(UserAccounts accounts, Account account) {
+        WeakReference<TokenCache> cacheRef = accounts.accountTokenCaches.get(account);
+        TokenCache cache;
+        if (cacheRef == null || (cache = cacheRef.get()) == null) {
+            cache = new TokenCache();
+            cacheRef = new WeakReference<>(cache);
+            accounts.accountTokenCaches.put(account, cacheRef);
+        }
+        return cache;
+    }
+
     private Context getContextForUser(UserHandle user) {
         try {
             return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
diff --git a/services/core/java/com/android/server/accounts/TokenCache.java b/services/core/java/com/android/server/accounts/TokenCache.java
new file mode 100644
index 0000000..70a7010
--- /dev/null
+++ b/services/core/java/com/android/server/accounts/TokenCache.java
@@ -0,0 +1,163 @@
+/*
+ * 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.accounts;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * TokenCaches manage tokens associated with an account in memory.
+ */
+/* default */ class TokenCache {
+
+    private static class Value {
+        public final String token;
+        public final long expiryEpochMillis;
+
+        public Value(String token, long expiryEpochMillis) {
+            this.token = token;
+            this.expiryEpochMillis = expiryEpochMillis;
+        }
+    }
+
+    private static class Key {
+        public final String packageName;
+        public final String tokenType;
+        public final byte[] sigDigest;
+
+        public Key(String tokenType, String packageName, byte[] sigDigest) {
+            this.tokenType = tokenType;
+            this.packageName = packageName;
+            this.sigDigest = sigDigest;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o != null && o instanceof Key) {
+                Key cacheKey = (Key) o;
+                return Objects.equals(packageName, cacheKey.packageName)
+                        && Objects.equals(tokenType, cacheKey.tokenType)
+                        && Arrays.equals(sigDigest, cacheKey.sigDigest);
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            return packageName.hashCode() ^ tokenType.hashCode() ^ Arrays.hashCode(sigDigest);
+        }
+    }
+
+    /**
+     * Map associating basic token lookup information with with actual tokens (and optionally their
+     * expiration times). 
+     */
+    private HashMap<Key, Value> mCachedTokens = new HashMap<>();
+
+    /**
+     * Map associated tokens with an Evictor that will manage evicting the token from the cache.
+     * This reverse lookup is needed because very little information is given at token invalidation
+     * time.
+     */
+    private HashMap<String, Evictor> mTokenEvictors = new HashMap<>();
+
+    private class Evictor {
+        private final String mToken;
+        private final List<Key> mKeys;
+
+        public Evictor(String token) {
+            mKeys = new ArrayList<>();
+            mToken = token;
+        }
+
+        public void add(Key k) {
+            mKeys.add(k);
+        }
+
+        public void evict() {
+            for (Key k : mKeys) {
+                mCachedTokens.remove(k);
+            }
+            // Clear out the evictor reference.
+            mTokenEvictors.remove(mToken);
+        }
+    }
+
+    /**
+     * Caches the specified token until the specified expiryMillis. The token will be associated
+     * with the given token type, package name, and digest of signatures.
+     *
+     * @param token
+     * @param tokenType
+     * @param packageName
+     * @param sigDigest
+     * @param expiryMillis
+     */
+    public void put(
+            String token,
+            String tokenType,
+            String packageName,
+            byte[] sigDigest,
+            long expiryMillis) {
+        if (token == null || System.currentTimeMillis() > expiryMillis) {
+            return;
+        }
+        Key k = new Key(tokenType, packageName, sigDigest);
+        // Prep evictor. No token should be cached without a corresponding evictor.
+        Evictor evictor = mTokenEvictors.get(token);
+        if (evictor == null) {
+            evictor = new Evictor(token);
+        }
+        evictor.add(k);
+        mTokenEvictors.put(token, evictor);
+        // Then cache values.
+        Value v = new Value(token, expiryMillis);
+        mCachedTokens.put(k, v);
+    }
+
+    /**
+     * Evicts the specified token from the cache. This should be called as part of a token
+     * invalidation workflow.
+     */
+    public void remove(String token) {
+        Evictor evictor = mTokenEvictors.get(token);
+        if (evictor == null) {
+            // This condition is expected if the token isn't cached.
+            return;
+        }
+        evictor.evict();
+    }
+
+    /**
+     * Gets a token from the cache if possible.
+     */
+    public String get(String tokenType, String packageName, byte[] sigDigest) {
+        Key k = new Key(tokenType, packageName, sigDigest);
+        Value v = mCachedTokens.get(k);
+        long currentTime = System.currentTimeMillis();
+        if (v != null && currentTime < v.expiryEpochMillis) {
+            return v.token;
+        } else if (v != null) {
+            remove(v.token);
+        }
+        return null;
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index fa4d204..333db5d 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -518,7 +518,7 @@
             // r.record is null if findServiceLocked() failed the caller permission check
             if (r.record == null) {
                 throw new SecurityException(
-                        "Permission Denial: Accessing service " + r.record.name
+                        "Permission Denial: Accessing service"
                         + " from pid=" + Binder.getCallingPid()
                         + ", uid=" + Binder.getCallingUid()
                         + " requires " + r.permission);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f9bfe72..9b619c7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4991,7 +4991,7 @@
                 scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
                         scanFlags, currentTime, null);
             } catch (PackageManagerException e) {
-                Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
+                Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage(), e);
 
                 // Delete invalid userdata apps
                 if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7bd5b78..c8397e2 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -497,6 +497,12 @@
      * be done once per window. */
     private WindowState mWinDismissingKeyguard;
 
+    /** When window is currently dismissing the keyguard, dismissing the keyguard must handle
+     * the keygaurd secure state change instantly case, e.g. the use case of inserting a PIN
+     * lock SIM card. This variable is used to record the previous keyguard secure state for
+     * monitoring secure state change on window dismissing keyguard. */
+    private boolean mSecureDismissingKeyguard;
+
     /** The window that is currently showing "over" the keyguard. If there is an app window
      * belonging to another app on top of this the keyguard shows. If there is a fullscreen
      * app window under this, still dismiss the keyguard but don't show the app underneath. Show
@@ -4272,9 +4278,11 @@
                             mDismissKeyguard == DISMISS_KEYGUARD_NONE) {
                         if (DEBUG_LAYOUT) Slog.v(TAG,
                                 "Setting mDismissKeyguard true by win " + win);
-                        mDismissKeyguard = mWinDismissingKeyguard == win ?
-                                DISMISS_KEYGUARD_CONTINUE : DISMISS_KEYGUARD_START;
+                        mDismissKeyguard = (mWinDismissingKeyguard == win
+                                && mSecureDismissingKeyguard == mKeyguardSecure)
+                                ? DISMISS_KEYGUARD_CONTINUE : DISMISS_KEYGUARD_START;
                         mWinDismissingKeyguard = win;
+                        mSecureDismissingKeyguard = mKeyguardSecure;
                         mForceStatusBarFromKeyguard = mShowingLockscreen && mKeyguardSecure;
                     } else if (mAppsToBeHidden.isEmpty() && showWhenLocked) {
                         if (DEBUG_LAYOUT) Slog.v(TAG,
@@ -4460,6 +4468,7 @@
                 }
             } else {
                 mWinDismissingKeyguard = null;
+                mSecureDismissingKeyguard = false;
                 mKeyguardHidden = false;
                 if (setKeyguardOccludedLw(false)) {
                     changes |= FINISH_LAYOUT_REDO_LAYOUT
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5c9f87e..f0e8efe 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2272,6 +2272,16 @@
             } else if (changingTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
                 mLastWallpaperDisplayOffsetY = changingTarget.mWallpaperDisplayOffsetY;
             }
+            if (target.mWallpaperXStep >= 0) {
+                mLastWallpaperXStep = target.mWallpaperXStep;
+            } else if (changingTarget.mWallpaperXStep >= 0) {
+                mLastWallpaperXStep = changingTarget.mWallpaperXStep;
+            }
+            if (target.mWallpaperYStep >= 0) {
+                mLastWallpaperYStep = target.mWallpaperYStep;
+            } else if (changingTarget.mWallpaperYStep >= 0) {
+                mLastWallpaperYStep = changingTarget.mWallpaperYStep;
+            }
         }
 
         for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {