Merge "Remove spurious syncmanager wake-ups" into mnc-dev
diff --git a/api/current.txt b/api/current.txt
index 68ecf96..e16294e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3341,6 +3341,7 @@
     method public boolean isImmersive();
     method public boolean isTaskRoot();
     method public boolean isVoiceInteraction();
+    method public boolean isVoiceInteractionRoot();
     method public final deprecated android.database.Cursor managedQuery(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
     method public boolean moveTaskToBack(boolean);
     method public boolean navigateUpTo(android.content.Intent);
diff --git a/api/system-current.txt b/api/system-current.txt
index 2e0613a..a784378 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3444,6 +3444,7 @@
     method public boolean isImmersive();
     method public boolean isTaskRoot();
     method public boolean isVoiceInteraction();
+    method public boolean isVoiceInteractionRoot();
     method public final deprecated android.database.Cursor managedQuery(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
     method public boolean moveTaskToBack(boolean);
     method public boolean navigateUpTo(android.content.Intent);
diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java
index 9c401c7f..3e4a66d 100644
--- a/core/java/android/accounts/AbstractAccountAuthenticator.java
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -138,9 +138,7 @@
                     new AccountAuthenticatorResponse(response),
                         accountType, authTokenType, features, options);
                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                    if (result != null) {
-                        result.keySet(); // force it to be unparcelled
-                    }
+                    result.keySet(); // force it to be unparcelled
                     Log.v(TAG, "addAccount: result " + AccountManager.sanitizeResult(result));
                 }
                 if (result != null) {
@@ -162,9 +160,7 @@
                 final Bundle result = AbstractAccountAuthenticator.this.confirmCredentials(
                     new AccountAuthenticatorResponse(response), account, options);
                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                    if (result != null) {
-                        result.keySet(); // force it to be unparcelled
-                    }
+                    result.keySet(); // force it to be unparcelled
                     Log.v(TAG, "confirmCredentials: result "
                             + AccountManager.sanitizeResult(result));
                 }
@@ -189,9 +185,7 @@
                 result.putString(AccountManager.KEY_AUTH_TOKEN_LABEL,
                         AbstractAccountAuthenticator.this.getAuthTokenLabel(authTokenType));
                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                    if (result != null) {
-                        result.keySet(); // force it to be unparcelled
-                    }
+                    result.keySet(); // force it to be unparcelled
                     Log.v(TAG, "getAuthTokenLabel: result "
                             + AccountManager.sanitizeResult(result));
                 }
@@ -215,9 +209,7 @@
                         new AccountAuthenticatorResponse(response), account,
                         authTokenType, loginOptions);
                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                    if (result != null) {
-                        result.keySet(); // force it to be unparcelled
-                    }
+                    result.keySet(); // force it to be unparcelled
                     Log.v(TAG, "getAuthToken: result " + AccountManager.sanitizeResult(result));
                 }
                 if (result != null) {
@@ -242,10 +234,7 @@
                     new AccountAuthenticatorResponse(response), account,
                         authTokenType, loginOptions);
                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                    // Result may be null.
-                    if (result != null) {
-                        result.keySet(); // force it to be unparcelled
-                    }
+                    result.keySet(); // force it to be unparcelled
                     Log.v(TAG, "updateCredentials: result "
                             + AccountManager.sanitizeResult(result));
                 }
@@ -501,7 +490,7 @@
      * <ul>
      * <li> {@link AccountManager#KEY_INTENT}, or
      * <li> {@link AccountManager#KEY_ACCOUNT_NAME} and {@link AccountManager#KEY_ACCOUNT_TYPE} of
-     * the account whose credentials were updated, or
+     * the account that was added, or
      * <li> {@link AccountManager#KEY_ERROR_CODE} and {@link AccountManager#KEY_ERROR_MESSAGE} to
      * indicate an error
      * </ul>
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 8c84b4d..9394d2c 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -333,7 +333,7 @@
         try {
             return mService.getPassword(account);
         } catch (RemoteException e) {
-            // won't ever happen
+            // will never happen
             throw new RuntimeException(e);
         }
     }
@@ -362,7 +362,7 @@
         try {
             return mService.getUserData(account, key);
         } catch (RemoteException e) {
-            // won't ever happen
+            // will never happen
             throw new RuntimeException(e);
         }
     }
@@ -415,10 +415,8 @@
      *
      * <p>It is safe to call this method from the main thread.
      *
-     * <p>Clients of this method that have not been granted the
-     * {@link android.Manifest.permission#GET_ACCOUNTS} permission,
-     * will only see those accounts managed by AbstractAccountAuthenticators whose
-     * signature matches the client.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#GET_ACCOUNTS}.
      *
      * @return An array of {@link Account}, one for each account.  Empty
      *     (never null) if no accounts have been added.
@@ -440,10 +438,8 @@
      *
      * <p>It is safe to call this method from the main thread.
      *
-     * <p>Clients of this method that have not been granted the
-     * {@link android.Manifest.permission#GET_ACCOUNTS} permission,
-     * will only see those accounts managed by AbstractAccountAuthenticators whose
-     * signature matches the client.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#GET_ACCOUNTS}.
      *
      * @return An array of {@link Account}, one for each account.  Empty
      *     (never null) if no accounts have been added.
@@ -470,7 +466,7 @@
         try {
             return mService.getAccountsForPackage(packageName, uid);
         } catch (RemoteException re) {
-            // won't ever happen
+            // possible security exception
             throw new RuntimeException(re);
         }
     }
@@ -487,7 +483,7 @@
         try {
             return mService.getAccountsByTypeForPackage(type, packageName);
         } catch (RemoteException re) {
-            // won't ever happen
+            // possible security exception
             throw new RuntimeException(re);
         }
     }
@@ -501,10 +497,9 @@
      *
      * <p>It is safe to call this method from the main thread.
      *
-     * <p>Clients of this method that have not been granted the
-     * {@link android.Manifest.permission#GET_ACCOUNTS} permission,
-     * will only see those accounts managed by AbstractAccountAuthenticators whose
-     * signature matches the client.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#GET_ACCOUNTS} or share a uid with the
+     * authenticator that owns the account type.
      *
      * <p><b>NOTE:</b> If targeting your app to work on API level 22 and before,
      * GET_ACCOUNTS permission is needed for those platforms, irrespective of uid
@@ -590,8 +585,7 @@
      * {@link AccountManagerFuture} must not be used on the main thread.
      *
      * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#GET_ACCOUNTS} or be a signature
-     * match with the AbstractAccountAuthenticator that manages the account.
+     * {@link android.Manifest.permission#GET_ACCOUNTS}.
      *
      * @param account The {@link Account} to test
      * @param features An array of the account features to check
@@ -634,10 +628,9 @@
      * <p>This method may be called from any thread, but the returned
      * {@link AccountManagerFuture} must not be used on the main thread.
      *
-     * <p>Clients of this method that have not been granted the
-     * {@link android.Manifest.permission#GET_ACCOUNTS} permission,
-     * will only see those accounts managed by AbstractAccountAuthenticators whose
-     * signature matches the client.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#GET_ACCOUNTS} or share a uid with the
+     * authenticator that owns the account type.
      *
      * @param type The type of accounts to return, must not be null
      * @param features An array of the account features to require,
@@ -708,7 +701,7 @@
         try {
             return mService.addAccountExplicitly(account, password, userdata);
         } catch (RemoteException e) {
-            // Can happen if there was a SecurityException was thrown.
+            // won't ever happen
             throw new RuntimeException(e);
         }
     }
@@ -973,7 +966,7 @@
         try {
             return mService.removeAccountExplicitly(account);
         } catch (RemoteException e) {
-            // May happen if the caller doesn't match the signature of the authenticator.
+            // won't ever happen
             throw new RuntimeException(e);
         }
     }
@@ -1121,7 +1114,7 @@
         try {
             mService.setUserData(account, key, value);
         } catch (RemoteException e) {
-            // Will happen if there is not signature match.
+            // won't ever happen
             throw new RuntimeException(e);
         }
     }
@@ -1740,7 +1733,7 @@
      *     with these fields if an activity was supplied and the account
      *     credentials were successfully updated:
      * <ul>
-     * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account
+     * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account created
      * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
      * </ul>
      *
@@ -2508,13 +2501,11 @@
      * listeners are added in an Activity or Service's {@link Activity#onCreate}
      * and removed in {@link Activity#onDestroy}.
      *
-     * <p>The listener will only be informed of accounts that would be returned
-     * to the caller via {@link #getAccounts()}. Typically this means that to
-     * get any accounts, the caller will need to be grated the GET_ACCOUNTS
-     * permission.
-     *
      * <p>It is safe to call this method from the main thread.
      *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#GET_ACCOUNTS}.
+     *
      * @param listener The listener to send notifications to
      * @param handler {@link Handler} identifying the thread to use
      *     for notifications, null for the main thread
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index bdea608..3c8af0d 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1229,6 +1229,22 @@
     }
 
     /**
+     * Like {@link #isVoiceInteraction}, but only returns true if this is also the root
+     * of a voice interaction.  That is, returns true if this activity was directly
+     * started by the voice interaction service as the initiation of a voice interaction.
+     * Otherwise, for example if it was started by another activity while under voice
+     * interaction, returns false.
+     */
+    public boolean isVoiceInteractionRoot() {
+        try {
+            return mVoiceInteractor != null
+                    && ActivityManagerNative.getDefault().isRootVoiceInteraction(mToken);
+        } catch (RemoteException e) {
+        }
+        return false;
+    }
+
+    /**
      * Retrieve the active {@link VoiceInteractor} that the user is going through to
      * interact with this activity.
      */
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index b758a7a..e144c29 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2582,6 +2582,15 @@
             reply.writeInt(res ? 1 : 0);
             return true;
         }
+
+        case IS_ROOT_VOICE_INTERACTION_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            boolean res = isRootVoiceInteraction(token);
+            reply.writeNoException();
+            reply.writeInt(res ? 1 : 0);
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -5962,5 +5971,19 @@
         return res != 0;
     }
 
+    @Override
+    public boolean isRootVoiceInteraction(IBinder token) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        mRemote.transact(IS_ROOT_VOICE_INTERACTION_TRANSACTION, data, reply, 0);
+        reply.readException();
+        int res = reply.readInt();
+        data.recycle();
+        reply.recycle();
+        return res != 0;
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 9ebbb9b..90216af 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -515,6 +515,8 @@
     public boolean setProcessMemoryTrimLevel(String process, int uid, int level)
             throws RemoteException;
 
+    public boolean isRootVoiceInteraction(IBinder token) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -861,4 +863,5 @@
     int IS_SCREEN_CAPTURE_ALLOWED_ON_CURRENT_ACTIVITY_TRANSACTION
             = IBinder.FIRST_CALL_TRANSACTION+299;
     int SHOW_ASSIST_FROM_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+300;
+    int IS_ROOT_VOICE_INTERACTION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+301;
 }
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index c2d901d..69b8b95 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1810,9 +1810,14 @@
                 throw new SecurityException(
                         "Starting under voice control not allowed for: " + intent);
             case ActivityManager.START_NOT_CURRENT_USER_ACTIVITY:
-                throw new SecurityException(
-                        "Not allowed to start background user activity that shouldn't be"
-                        + " displayed for all users.");
+                // Fail silently for this case so we don't break current apps.
+                // TODO(b/22929608): Instead of failing silently or throwing an exception,
+                // we should properly position the activity in the stack (i.e. behind all current
+                // user activity/task) and not change the positioning of stacks.
+                Log.e(TAG,
+                        "Not allowed to start background user activity that shouldn't be displayed"
+                        + " for all users. Failing silently...");
+                break;
             default:
                 throw new AndroidRuntimeException("Unknown error code "
                         + res + " when starting " + intent);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index e7c58f4..4f42ed9 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -7915,6 +7915,8 @@
             return;
         }
 
+        // Record whether we've seen a non-zero time (for debugging b/22716723).
+        boolean seenNonZeroTime = false;
         for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
             String name = ent.getKey();
             KernelWakelockStats.Entry kws = ent.getValue();
@@ -7928,17 +7930,31 @@
             kwlt.updateCurrentReportedCount(kws.mCount);
             kwlt.updateCurrentReportedTotalTime(kws.mTotalTime);
             kwlt.setUpdateVersion(kws.mVersion);
+
+            if (kws.mVersion != wakelockStats.kernelWakelockVersion)
+            seenNonZeroTime |= kws.mTotalTime > 0;
         }
 
+        int numWakelocksSetStale = 0;
         if (wakelockStats.size() != mKernelWakelockStats.size()) {
             // Set timers to stale if they didn't appear in /proc/wakelocks this time.
             for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
                 SamplingTimer st = ent.getValue();
                 if (st.getUpdateVersion() != wakelockStats.kernelWakelockVersion) {
                     st.setStale();
+                    numWakelocksSetStale++;
                 }
             }
         }
+
+        if (!seenNonZeroTime) {
+            Slog.wtf(TAG, "All kernel wakelocks had time of zero");
+        }
+
+        if (numWakelocksSetStale == mKernelWakelockStats.size()) {
+            Slog.wtf(TAG, "All kernel wakelocks were set stale. new version=" +
+                    wakelockStats.kernelWakelockVersion);
+        }
     }
 
     // We use an anonymous class to access these variables,
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
index 0369c3f..6654ea5 100644
--- a/core/java/com/android/internal/os/KernelWakelockReader.java
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -75,6 +75,8 @@
                     is = new FileInputStream(sWakeupSourceFile);
                     wakeup_sources = true;
                 } catch (java.io.FileNotFoundException e2) {
+                    Slog.wtf(TAG, "neither " + sWakelockFile + " nor " +
+                            sWakeupSourceFile + " exists");
                     return null;
                 }
             }
@@ -82,6 +84,7 @@
             len = is.read(buffer);
             is.close();
         } catch (java.io.IOException e) {
+            Slog.wtf(TAG, "failed to read kernel wakelocks", e);
             return null;
         }
 
@@ -171,6 +174,13 @@
                             numUpdatedWlNames++;
                         }
                     }
+                } else if (!parsed) {
+                    try {
+                        Slog.wtf(TAG, "Failed to parse proc line: " +
+                                new String(wlBuffer, startIndex, endIndex - startIndex));
+                    } catch (Exception e) {
+                        Slog.wtf(TAG, "Failed to parse proc line!");
+                    }
                 }
                 startIndex = endIndex;
             }
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index e472e93..6dfb6e8 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -229,10 +229,11 @@
     SkRect dirty;
     mDamageAccumulator.finish(&dirty);
 
-    if (dirty.isEmpty() && Properties::skipEmptyFrames) {
-        mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
-        return;
-    }
+    // TODO: Re-enable after figuring out cause of b/22592975
+//    if (dirty.isEmpty() && Properties::skipEmptyFrames) {
+//        mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
+//        return;
+//    }
 
     mCurrentFrameInfo->markIssueDrawCommandsStart();
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 249eaa5..b0429ef 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -808,7 +808,7 @@
             // The pairing dialog now warns of phone-book access for paired devices.
             // No separate prompt is displayed after pairing.
             if (getPhonebookPermissionChoice() == CachedBluetoothDevice.ACCESS_UNKNOWN) {
-                setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_REJECTED);
+                setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_ALLOWED);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 5d74604..21cbef2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -79,6 +79,8 @@
                 return;
             }
             state.value = value;
+        } else {
+            state.value = mFlashlightController.isEnabled();
         }
         final AnimationIcon icon = state.value ? mEnable : mDisable;
         icon.setAllowAnimation(arg instanceof UserBoolean && ((UserBoolean) arg).userInitiated);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
index cd1914c..29a8f67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
@@ -93,6 +93,10 @@
         }
     }
 
+    public synchronized boolean isEnabled() {
+        return mFlashlightEnabled;
+    }
+
     public synchronized boolean isAvailable() {
         return mTorchAvailable;
     }
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 10a4cd1..50bd544 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -1506,6 +1506,7 @@
                                 //Unbind
                                 mContext.unbindService(mConnection);
                             }
+                            mBluetoothGatt = null;
                         }
                         SystemClock.sleep(100);
 
@@ -1811,6 +1812,7 @@
                 //Unbind
                 mContext.unbindService(mConnection);
             }
+            mBluetoothGatt = null;
         }
 
         mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 83e8db0..32fd56a 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -527,14 +527,14 @@
                     + ", pid " + Binder.getCallingPid());
         }
         if (account == null) throw new IllegalArgumentException("account is null");
-        int userId = UserHandle.getCallingUserId();
-        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
+        if (!isAccountManagedByCaller(account.type, callingUid)) {
             String msg = String.format(
                     "uid %s cannot get secrets for accounts of type: %s",
                     callingUid,
                     account.type);
             throw new SecurityException(msg);
         }
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
@@ -627,14 +627,14 @@
         }
         if (account == null) throw new IllegalArgumentException("account is null");
         if (key == null) throw new IllegalArgumentException("key is null");
-        int userId = UserHandle.getCallingUserId();
-        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
+        if (!isAccountManagedByCaller(account.type, callingUid)) {
             String msg = String.format(
                     "uid %s cannot get user data for accounts of type: %s",
                     callingUid,
                     account.type);
             throw new SecurityException(msg);
         }
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
@@ -664,32 +664,22 @@
 
         final long identityToken = clearCallingIdentity();
         try {
-            return getAuthenticatorTypesInternal(userId);
-
+            Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
+                    authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
+            AuthenticatorDescription[] types =
+                    new AuthenticatorDescription[authenticatorCollection.size()];
+            int i = 0;
+            for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
+                    : authenticatorCollection) {
+                types[i] = authenticator.type;
+                i++;
+            }
+            return types;
         } finally {
             restoreCallingIdentity(identityToken);
         }
     }
 
-    /**
-     * Should only be called inside of a clearCallingIdentity block.
-     */
-    private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) {
-        Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
-                authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
-        AuthenticatorDescription[] types =
-                new AuthenticatorDescription[authenticatorCollection.size()];
-        int i = 0;
-        for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
-                : authenticatorCollection) {
-            types[i] = authenticator.type;
-            i++;
-        }
-        return types;
-    }
-
-
-
     private boolean isCrossUser(int callingUid, int userId) {
         return (userId != UserHandle.getCallingUserId()
                 && callingUid != Process.myUid()
@@ -707,8 +697,7 @@
                     + ", pid " + Binder.getCallingPid());
         }
         if (account == null) throw new IllegalArgumentException("account is null");
-        int userId = UserHandle.getCallingUserId();
-        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
+        if (!isAccountManagedByCaller(account.type, callingUid)) {
             String msg = String.format(
                     "uid %s cannot explicitly add accounts of type: %s",
                     callingUid,
@@ -724,10 +713,12 @@
          */
 
         // fails if the account already exists
+        int uid = getCallingUid();
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
-            return addAccountInternal(accounts, account, password, extras, false, callingUid);
+            return addAccountInternal(accounts, account, password, extras, false, uid);
         } finally {
             restoreCallingIdentity(identityToken);
         }
@@ -803,26 +794,25 @@
         if (account == null) {
             throw new IllegalArgumentException("account is null");
         }
-        int userId = UserHandle.getCallingUserId();
-        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
+        if (!isAccountManagedByCaller(account.type, callingUid)) {
             String msg = String.format(
                     "uid %s cannot notify authentication for accounts of type: %s",
                     callingUid,
                     account.type);
             throw new SecurityException(msg);
         }
-
+        int userId = Binder.getCallingUserHandle().getIdentifier();
         if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) {
             return false;
         }
-
+        int user = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
-            UserAccounts accounts = getUserAccounts(userId);
-            return updateLastAuthenticatedTime(account);
+            UserAccounts accounts = getUserAccounts(user);
         } finally {
             restoreCallingIdentity(identityToken);
         }
+        return updateLastAuthenticatedTime(account);
     }
 
     private boolean updateLastAuthenticatedTime(Account account) {
@@ -995,9 +985,8 @@
         if (response == null) throw new IllegalArgumentException("response is null");
         if (account == null) throw new IllegalArgumentException("account is null");
         if (features == null) throw new IllegalArgumentException("features is null");
+        checkReadAccountsPermitted(callingUid, account.type);
         int userId = UserHandle.getCallingUserId();
-        checkReadAccountsPermitted(callingUid, account.type, userId);
-
         long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
@@ -1073,14 +1062,14 @@
                 + ", pid " + Binder.getCallingPid());
         }
         if (accountToRename == null) throw new IllegalArgumentException("account is null");
-        int userId = UserHandle.getCallingUserId();
-        if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) {
+        if (!isAccountManagedByCaller(accountToRename.type, callingUid)) {
             String msg = String.format(
                     "uid %s cannot rename accounts of type: %s",
                     callingUid,
                     accountToRename.type);
             throw new SecurityException(msg);
         }
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
@@ -1222,15 +1211,14 @@
          * authenticator.  This will let users remove accounts (via Settings in the system) but not
          * arbitrary applications (like competing authenticators).
          */
-        UserHandle user = new UserHandle(userId);
-        if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
-                && !isSystemUid(callingUid)) {
+        if (!isAccountManagedByCaller(account.type, callingUid) && !isSystemUid(callingUid)) {
             String msg = String.format(
                     "uid %s cannot remove accounts of type: %s",
                     callingUid,
                     account.type);
             throw new SecurityException(msg);
         }
+
         if (!canUserModifyAccounts(userId)) {
             try {
                 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
@@ -1247,7 +1235,10 @@
             }
             return;
         }
+
+        UserHandle user = new UserHandle(userId);
         long identityToken = clearCallingIdentity();
+
         UserAccounts accounts = getUserAccounts(userId);
         cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
         synchronized(accounts.credentialsPermissionNotificationIds) {
@@ -1277,7 +1268,6 @@
                     + ", caller's uid " + callingUid
                     + ", pid " + Binder.getCallingPid());
         }
-        int userId = Binder.getCallingUserHandle().getIdentifier();
         if (account == null) {
             /*
              * Null accounts should result in returning false, as per
@@ -1285,18 +1275,22 @@
              */
             Log.e(TAG, "account is null");
             return false;
-        } else if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
+        } else if (!isAccountManagedByCaller(account.type, callingUid)) {
             String msg = String.format(
                     "uid %s cannot explicitly add accounts of type: %s",
                     callingUid,
                     account.type);
             throw new SecurityException(msg);
         }
+
         UserAccounts accounts = getUserAccountsForCaller();
+        int userId = Binder.getCallingUserHandle().getIdentifier();
         if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) {
             return false;
         }
+
         logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, TABLE_ACCOUNTS);
+
         long identityToken = clearCallingIdentity();
         try {
             return removeAccountInternal(accounts, account);
@@ -1530,14 +1524,14 @@
         }
         if (account == null) throw new IllegalArgumentException("account is null");
         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
-        int userId = UserHandle.getCallingUserId();
-        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
+        if (!isAccountManagedByCaller(account.type, callingUid)) {
             String msg = String.format(
                     "uid %s cannot peek the authtokens associated with accounts of type: %s",
                     callingUid,
                     account.type);
             throw new SecurityException(msg);
         }
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
@@ -1558,14 +1552,14 @@
         }
         if (account == null) throw new IllegalArgumentException("account is null");
         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
-        int userId = UserHandle.getCallingUserId();
-        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
+        if (!isAccountManagedByCaller(account.type, callingUid)) {
             String msg = String.format(
                     "uid %s cannot set auth tokens associated with accounts of type: %s",
                     callingUid,
                     account.type);
             throw new SecurityException(msg);
         }
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
@@ -1584,14 +1578,14 @@
                     + ", pid " + Binder.getCallingPid());
         }
         if (account == null) throw new IllegalArgumentException("account is null");
-        int userId = UserHandle.getCallingUserId();
-        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
+        if (!isAccountManagedByCaller(account.type, callingUid)) {
             String msg = String.format(
                     "uid %s cannot set secrets for accounts of type: %s",
                     callingUid,
                     account.type);
             throw new SecurityException(msg);
         }
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
@@ -1648,14 +1642,14 @@
                     + ", pid " + Binder.getCallingPid());
         }
         if (account == null) throw new IllegalArgumentException("account is null");
-        int userId = UserHandle.getCallingUserId();
-        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
+        if (!isAccountManagedByCaller(account.type, callingUid)) {
             String msg = String.format(
                     "uid %s cannot clear passwords for accounts of type: %s",
                     callingUid,
                     account.type);
             throw new SecurityException(msg);
         }
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
@@ -1676,14 +1670,14 @@
         }
         if (key == null) throw new IllegalArgumentException("key is null");
         if (account == null) throw new IllegalArgumentException("account is null");
-        int userId = UserHandle.getCallingUserId();
-        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
+        if (!isAccountManagedByCaller(account.type, callingUid)) {
             String msg = String.format(
                     "uid %s cannot set user data for accounts of type: %s",
                     callingUid,
                     account.type);
             throw new SecurityException(msg);
         }
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
@@ -1846,8 +1840,8 @@
 
         // skip the check if customTokens
         final int callerUid = Binder.getCallingUid();
-        final boolean permissionGranted =
-                customTokens || permissionIsGranted(account, authTokenType, callerUid, userId);
+        final boolean permissionGranted = customTokens ||
+            permissionIsGranted(account, authTokenType, callerUid);
 
         // Get the calling package. We will use it for the purpose of caching.
         final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
@@ -2369,14 +2363,14 @@
         }
         if (response == null) throw new IllegalArgumentException("response is null");
         if (accountType == null) throw new IllegalArgumentException("accountType is null");
-        int userId = UserHandle.getCallingUserId();
-        if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) {
+        if (!isAccountManagedByCaller(accountType, callingUid) && !isSystemUid(callingUid)) {
             String msg = String.format(
                     "uid %s cannot edit authenticator properites for account type: %s",
                     callingUid,
                     accountType);
             throw new SecurityException(msg);
         }
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
@@ -2499,22 +2493,20 @@
     }
 
     /**
-     * Returns the accounts visible to the client within the context of a specific user
+     * Returns the accounts for a specific user
      * @hide
      */
     public Account[] getAccounts(int userId) {
         int callingUid = Binder.getCallingUid();
-        List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId);
-        if (visibleAccountTypes.isEmpty()) {
+        if (!isReadAccountsPermitted(callingUid, null)) {
             return new Account[0];
         }
         long identityToken = clearCallingIdentity();
         try {
-            return getAccountsInternal(
-                    userId,
-                    callingUid,
-                    null,  // packageName
-                    visibleAccountTypes);
+            UserAccounts accounts = getUserAccounts(userId);
+            synchronized (accounts.cacheLock) {
+                return getAccountsFromCacheLocked(accounts, null, callingUid, null);
+            }
         } finally {
             restoreCallingIdentity(identityToken);
         }
@@ -2596,52 +2588,22 @@
             callingUid = packageUid;
         }
 
-        List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId);
-        if (visibleAccountTypes.isEmpty()
-                || (type != null && !visibleAccountTypes.contains(type))) {
+        // Authenticators should be able to see their own accounts regardless of permissions.
+        if (TextUtils.isEmpty(type) && !isReadAccountsPermitted(callingUid, type)) {
             return new Account[0];
-        } else if (visibleAccountTypes.contains(type)) {
-            // Prune the list down to just the requested type.
-            visibleAccountTypes = new ArrayList<>();
-            visibleAccountTypes.add(type);
-        } // else aggregate all the visible accounts (it won't matter if the list is empty).
+        }
 
         long identityToken = clearCallingIdentity();
         try {
-            return getAccountsInternal(
-                    userId,
-                    callingUid,
-                    callingPackage,
-                    visibleAccountTypes);
+            UserAccounts accounts = getUserAccounts(userId);
+            synchronized (accounts.cacheLock) {
+                return getAccountsFromCacheLocked(accounts, type, callingUid, callingPackage);
+            }
         } finally {
             restoreCallingIdentity(identityToken);
         }
     }
 
-    private Account[] getAccountsInternal(
-            int userId,
-            int callingUid,
-            String callingPackage,
-            List<String> visibleAccountTypes) {
-        UserAccounts accounts = getUserAccounts(userId);
-        synchronized (accounts.cacheLock) {
-            UserAccounts userAccounts = getUserAccounts(userId);
-            ArrayList<Account> visibleAccounts = new ArrayList<>();
-            for (String visibleType : visibleAccountTypes) {
-                Account[] accountsForType = getAccountsFromCacheLocked(
-                        userAccounts, visibleType, callingUid, callingPackage);
-                if (accountsForType != null) {
-                    visibleAccounts.addAll(Arrays.asList(accountsForType));
-                }
-            }
-            Account[] result = new Account[visibleAccounts.size()];
-            for (int i = 0; i < visibleAccounts.size(); i++) {
-                result[i] = visibleAccounts.get(i);
-            }
-            return result;
-        }
-    }
-
     @Override
     public boolean addSharedAccountAsUser(Account account, int userId) {
         userId = handleIncomingUser(userId);
@@ -2777,12 +2739,8 @@
         }
         if (response == null) throw new IllegalArgumentException("response is null");
         if (type == null) throw new IllegalArgumentException("accountType is null");
-        int userId = UserHandle.getCallingUserId();
-
-        List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId);
-        if (!visibleAccountTypes.contains(type)) {
+        if (!isReadAccountsPermitted(callingUid, type)) {
             Bundle result = new Bundle();
-            // Need to return just the accounts that are from matching signatures.
             result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]);
             try {
                 response.onResult(result);
@@ -2791,6 +2749,7 @@
             }
             return;
         }
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
             UserAccounts userAccounts = getUserAccounts(userId);
@@ -2804,11 +2763,7 @@
                 onResult(response, result);
                 return;
             }
-            new GetAccountsByTypeAndFeatureSession(
-                    userAccounts,
-                    response,
-                    type,
-                    features,
+            new GetAccountsByTypeAndFeatureSession(userAccounts, response, type, features,
                     callingUid).bind();
         } finally {
             restoreCallingIdentity(identityToken);
@@ -3741,11 +3696,10 @@
         return false;
     }
 
-    private boolean permissionIsGranted(
-            Account account, String authTokenType, int callerUid, int userId) {
+    private boolean permissionIsGranted(Account account, String authTokenType, int callerUid) {
         final boolean isPrivileged = isPrivileged(callerUid);
         final boolean fromAuthenticator = account != null
-                && isAccountManagedByCaller(account.type, callerUid, userId);
+                && isAccountManagedByCaller(account.type, callerUid);
         final boolean hasExplicitGrants = account != null
                 && hasExplicitlyGrantedPermission(account, authTokenType, callerUid);
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -3757,45 +3711,23 @@
         return fromAuthenticator || hasExplicitGrants || isPrivileged;
     }
 
-    private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId) {
+    private boolean isAccountManagedByCaller(String accountType, int callingUid) {
         if (accountType == null) {
             return false;
-        } else {
-            return getTypesVisibleToCaller(callingUid, userId).contains(accountType);
         }
-    }
-
-    private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) {
-        if (accountType == null) {
-            return false;
-        } else {
-            return getTypesManagedByCaller(callingUid, userId).contains(accountType);
-        }
-    }
-
-    private List<String> getTypesVisibleToCaller(int callingUid, int userId) {
-        boolean isPermitted =
-                isPermitted(callingUid, Manifest.permission.GET_ACCOUNTS,
-                        Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
-        Log.i(TAG, String.format("getTypesVisibleToCaller: isPermitted? %s", isPermitted));
-        return getTypesForCaller(callingUid, userId, isPermitted);
-    }
-
-    private List<String> getTypesManagedByCaller(int callingUid, int userId) {
-        return getTypesForCaller(callingUid, userId, false);
-    }
-
-    private List<String> getTypesForCaller(
-            int callingUid, int userId, boolean isOtherwisePermitted) {
-        List<String> managedAccountTypes = new ArrayList<>();
+        final int callingUserId = UserHandle.getUserId(callingUid);
         for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
-                mAuthenticatorCache.getAllServices(userId)) {
-            final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
-            if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) {
-                managedAccountTypes.add(serviceInfo.type.type);
+                mAuthenticatorCache.getAllServices(callingUserId)) {
+            if (serviceInfo.type.type.equals(accountType)) {
+                /*
+                 * We can't simply compare uids because uids can be recycled before the
+                 * authenticator cache is updated.
+                 */
+                final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
+                return sigChk == PackageManager.SIGNATURE_MATCH;
             }
         }
-        return managedAccountTypes;
+        return false;
     }
 
     private boolean isAccountPresentForCaller(String accountName, String accountType) {
@@ -3860,12 +3792,28 @@
         return false;
     }
 
+    private boolean isReadAccountsPermitted(int callingUid, String accountType) {
+        /*
+         * Settings app (which is in the same uid as AcocuntManagerService), apps with the
+         * GET_ACCOUNTS permission or authenticators that own the account type should be able to
+         * access accounts of the specified account.
+         */
+        boolean isPermitted =
+                isPermitted(callingUid, Manifest.permission.GET_ACCOUNTS,
+                        Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
+        boolean isAccountManagedByCaller = isAccountManagedByCaller(accountType, callingUid);
+        Log.w(TAG, String.format(
+                "isReadAccountPermitted: isPermitted: %s, isAM: %s",
+                isPermitted,
+                isAccountManagedByCaller));
+        return isPermitted || isAccountManagedByCaller;
+    }
+
     /** Succeeds if any of the specified permissions are granted. */
     private void checkReadAccountsPermitted(
             int callingUid,
-            String accountType,
-            int userId) {
-        if (!isAccountVisibleToCaller(accountType, callingUid, userId)) {
+            String accountType) {
+        if (!isReadAccountsPermitted(callingUid, accountType)) {
             String msg = String.format(
                     "caller uid %s cannot access %s accounts",
                     callingUid,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e8ffdb6..b8d32c3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6552,6 +6552,17 @@
     }
 
     @Override
+    public boolean isRootVoiceInteraction(IBinder token) {
+        synchronized(this) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return false;
+            }
+            return r.rootVoiceInteraction;
+        }
+    }
+
+    @Override
     public IIntentSender getIntentSender(int type,
             String packageName, IBinder token, String resultWho,
             int requestCode, Intent[] intents, String[] resolvedTypes,
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 0957eb5..3de2009 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -107,6 +107,7 @@
     boolean fullscreen; // covers the full screen?
     final boolean noDisplay;  // activity is not displayed?
     final boolean componentSpecified;  // did caller specifiy an explicit component?
+    final boolean rootVoiceInteraction;  // was this the root activity of a voice interaction?
 
     static final int APPLICATION_ACTIVITY_TYPE = 0;
     static final int HOME_ACTIVITY_TYPE = 1;
@@ -207,6 +208,9 @@
         pw.print(prefix); pw.print("stateNotNeeded="); pw.print(stateNotNeeded);
                 pw.print(" componentSpecified="); pw.print(componentSpecified);
                 pw.print(" mActivityType="); pw.println(mActivityType);
+        if (rootVoiceInteraction) {
+            pw.print(prefix); pw.print("rootVoiceInteraction="); pw.println(rootVoiceInteraction);
+        }
         pw.print(prefix); pw.print("compat="); pw.print(compat);
                 pw.print(" labelRes=0x"); pw.print(Integer.toHexString(labelRes));
                 pw.print(" icon=0x"); pw.print(Integer.toHexString(icon));
@@ -432,7 +436,8 @@
             int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType,
             ActivityInfo aInfo, Configuration _configuration,
             ActivityRecord _resultTo, String _resultWho, int _reqCode,
-            boolean _componentSpecified, ActivityStackSupervisor supervisor,
+            boolean _componentSpecified, boolean _rootVoiceInteraction,
+            ActivityStackSupervisor supervisor,
             ActivityContainer container, Bundle options) {
         service = _service;
         appToken = new Token(this, service);
@@ -444,6 +449,7 @@
         shortComponentName = _intent.getComponent().flattenToShortString();
         resolvedType = _resolvedType;
         componentSpecified = _componentSpecified;
+        rootVoiceInteraction = _rootVoiceInteraction;
         configuration = _configuration;
         stackConfigOverride = (container != null)
                 ? container.mStack.mOverrideConfig : Configuration.EMPTY;
@@ -1257,7 +1263,7 @@
         }
         final ActivityRecord r = new ActivityRecord(service, /*caller*/null, launchedFromUid,
                 launchedFromPackage, intent, resolvedType, aInfo, service.getConfiguration(),
-                null, null, 0, componentSpecified, stackSupervisor, null, null);
+                null, null, 0, componentSpecified, false, stackSupervisor, null, null);
 
         r.persistentState = persistentState;
         r.taskDescription = taskDescription;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 0be2f6f..71fd49b 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1506,6 +1506,7 @@
             if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
                     && sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) {
                 try {
+                    intent.addCategory(Intent.CATEGORY_VOICE);
                     if (!AppGlobals.getPackageManager().activitySupportsIntent(
                             intent.getComponent(), intent, resolvedType)) {
                         err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
@@ -1626,7 +1627,7 @@
 
         ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
                 intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
-                requestCode, componentSpecified, this, container, options);
+                requestCode, componentSpecified, voiceSession != null, this, container, options);
         if (outActivity != null) {
             outActivity[0] = r;
         }
@@ -3721,19 +3722,19 @@
 
     @Override
     public void onDisplayAdded(int displayId) {
-        Slog.v(TAG, "Display added displayId=" + displayId);
+        if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId);
         mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_ADDED, displayId, 0));
     }
 
     @Override
     public void onDisplayRemoved(int displayId) {
-        Slog.v(TAG, "Display removed displayId=" + displayId);
+        if (DEBUG_STACK) Slog.v(TAG, "Display removed displayId=" + displayId);
         mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_REMOVED, displayId, 0));
     }
 
     @Override
     public void onDisplayChanged(int displayId) {
-        Slog.v(TAG, "Display changed displayId=" + displayId);
+        if (DEBUG_STACK) Slog.v(TAG, "Display changed displayId=" + displayId);
         mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_CHANGED, displayId, 0));
     }
 
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index a7e6471..1fbfd9f 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -240,8 +240,12 @@
                 }
                 didSomething = true;
                 receivers.remove(i);
+                if (i < nextReceiver) {
+                    nextReceiver--;
+                }
             }
         }
+        nextReceiver = Math.min(nextReceiver, receivers.size());
 
         return didSomething;
     }
diff --git a/tests/VoiceInteraction/res/layout/test_interaction.xml b/tests/VoiceInteraction/res/layout/test_interaction.xml
index d1a7ad5..277117e 100644
--- a/tests/VoiceInteraction/res/layout/test_interaction.xml
+++ b/tests/VoiceInteraction/res/layout/test_interaction.xml
@@ -39,6 +39,19 @@
         android:layout_marginTop="16dp"
         android:orientation="horizontal">
 
+        <Button android:id="@+id/airplane"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/launchAirplane"
+            />
+
+    </LinearLayout>
+
+    <LinearLayout android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:orientation="horizontal">
+
         <Button android:id="@+id/complete"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
diff --git a/tests/VoiceInteraction/res/values/strings.xml b/tests/VoiceInteraction/res/values/strings.xml
index 29ffe21..c665c23 100644
--- a/tests/VoiceInteraction/res/values/strings.xml
+++ b/tests/VoiceInteraction/res/values/strings.xml
@@ -21,6 +21,7 @@
     <string name="tree">Tree</string>
     <string name="text">Text</string>
     <string name="asyncStructure">(Async structure goes here)</string>
+    <string name="launchAirplane">Launch airplane mode</string>
     <string name="confirm">Confirm</string>
     <string name="abort">Abort</string>
     <string name="complete">Complete</string>
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
index e10d89f..ada0e21 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
@@ -21,6 +21,7 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.os.Bundle;
+import android.provider.Settings;
 import android.service.voice.VoiceInteractionService;
 import android.util.Log;
 import android.view.View;
@@ -39,6 +40,7 @@
     VoiceInteractor mInteractor;
     VoiceInteractor.Request mCurrentRequest = null;
     TextView mLog;
+    Button mAirplaneButton;
     Button mAbortButton;
     Button mCompleteButton;
     Button mCommandButton;
@@ -65,6 +67,8 @@
 
         setContentView(R.layout.test_interaction);
         mLog = (TextView)findViewById(R.id.log);
+        mAirplaneButton = (Button)findViewById(R.id.airplane);
+        mAirplaneButton.setOnClickListener(this);
         mAbortButton = (Button)findViewById(R.id.abort);
         mAbortButton.setOnClickListener(this);
         mCompleteButton = (Button)findViewById(R.id.complete);
@@ -122,7 +126,12 @@
 
     @Override
     public void onClick(View v) {
-        if (v == mAbortButton) {
+        if (v == mAirplaneButton) {
+            Intent intent = new Intent(Settings.ACTION_VOICE_CONTROL_AIRPLANE_MODE);
+            intent.addCategory(Intent.CATEGORY_VOICE);
+            intent.putExtra(Settings.EXTRA_AIRPLANE_MODE_ENABLED, true);
+            startActivity(intent);
+        } else if (v == mAbortButton) {
             VoiceInteractor.AbortVoiceRequest req = new TestAbortVoice();
             mInteractor.submitRequest(req, REQUEST_ABORT);
         } else if (v == mCompleteButton) {