Merge "Trapping backspace at EditText level to prevent popping Docs." into nyc-mr1-dev
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index d82629d..4098772 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -72,6 +72,8 @@
 static const char LAST_TIME_CHANGED_FILE_PATH[] = "/data/system/time/last_time_change";
 static const char ACCURATE_TIME_FLAG_FILE_NAME[] = "time_is_accurate";
 static const char ACCURATE_TIME_FLAG_FILE_PATH[] = "/data/system/time/time_is_accurate";
+// Java timestamp format. Don't show the clock if the date is before 2000-01-01 00:00:00.
+static const long long ACCURATE_TIME_EPOCH = 946684800000;
 static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
 static const int ANIM_ENTRY_NAME_MAX = 256;
 
@@ -932,8 +934,9 @@
         clock_gettime(CLOCK_REALTIME, &now);
         // Match the Java timestamp format
         long long rtcNow = (now.tv_sec * 1000LL) + (now.tv_nsec / 1000000LL);
-        if (lastChangedTime > rtcNow - MAX_TIME_IN_PAST
-            && lastChangedTime < rtcNow + MAX_TIME_IN_FUTURE) {
+        if (ACCURATE_TIME_EPOCH < rtcNow
+            && lastChangedTime > (rtcNow - MAX_TIME_IN_PAST)
+            && lastChangedTime < (rtcNow + MAX_TIME_IN_FUTURE)) {
             mTimeIsAccurate = true;
         }
       }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d4ac300..a64827a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3744,7 +3744,8 @@
     /**
      * Flag indicating that a drag can cross window boundaries.  When
      * {@link #startDragAndDrop(ClipData, DragShadowBuilder, Object, int)} is called
-     * with this flag set, all visible applications will be able to participate
+     * with this flag set, all visible applications with targetSdkVersion >=
+     * {@link android.os.Build.VERSION_CODES#N API 24} will be able to participate
      * in the drag operation and receive the dragged content.
      *
      * If this is the only flag set, then the drag recipient will only have access to text data
diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml
index 11e06f1..d4732f8 100644
--- a/docs/html/_redirects.yaml
+++ b/docs/html/_redirects.yaml
@@ -801,8 +801,8 @@
   to: http://android-developers.blogspot.com/2016/03/first-preview-of-android-n-developer.html
 - from: /reference/org/apache/http/...
   to: /about/versions/marshmallow/android-6.0-changes.html#behavior-apache-http-client
-- from: /shareables/
-  to: https://commondatastorage.googleapis.com/androiddevelopers/shareables/
+- from: /shareables/...
+  to: https://commondatastorage.googleapis.com/androiddevelopers/shareables/...
 - from: /downloads/
   to: https://commondatastorage.googleapis.com/androiddevelopers/
 - from: /training/performance/battery/network/action-any-traffic.html
diff --git a/location/java/android/location/GnssMeasurementsEvent.java b/location/java/android/location/GnssMeasurementsEvent.java
index 3151694..7db0466 100644
--- a/location/java/android/location/GnssMeasurementsEvent.java
+++ b/location/java/android/location/GnssMeasurementsEvent.java
@@ -98,13 +98,13 @@
             throw new InvalidParameterException("Parameter 'clock' must not be null.");
         }
         if (measurements == null || measurements.length == 0) {
-            throw new InvalidParameterException(
-                    "Parameter 'measurements' must not be null or empty.");
+            mReadOnlyMeasurements = Collections.emptyList();
+        } else {
+            Collection<GnssMeasurement> measurementCollection = Arrays.asList(measurements);
+            mReadOnlyMeasurements = Collections.unmodifiableCollection(measurementCollection);
         }
 
         mClock = clock;
-        Collection<GnssMeasurement> measurementCollection = Arrays.asList(measurements);
-        mReadOnlyMeasurements = Collections.unmodifiableCollection(measurementCollection);
     }
 
     /**
diff --git a/services/core/java/com/android/server/AnyMotionDetector.java b/services/core/java/com/android/server/AnyMotionDetector.java
index a8ae914d..f93c716 100644
--- a/services/core/java/com/android/server/AnyMotionDetector.java
+++ b/services/core/java/com/android/server/AnyMotionDetector.java
@@ -70,6 +70,9 @@
     /** The interval between accelerometer orientation measurements. */
     private static final long ORIENTATION_MEASUREMENT_INTERVAL_MILLIS = 5000;
 
+    /** The maximum duration we will hold a wakelock to determine stationary status. */
+    private static final long WAKELOCK_TIMEOUT_MILLIS = 30000;
+
     /**
      * The duration in milliseconds after which an orientation measurement is considered
      * too stale to be used.
@@ -141,25 +144,30 @@
                 mCurrentGravityVector = null;
                 mPreviousGravityVector = null;
                 mWakeLock.acquire();
+                Message wakelockTimeoutMsg = Message.obtain(mHandler, mWakelockTimeout);
+                mHandler.sendMessageDelayed(wakelockTimeoutMsg, WAKELOCK_TIMEOUT_MILLIS);
                 startOrientationMeasurementLocked();
             }
         }
     }
 
     public void stop() {
-        if (mState == STATE_ACTIVE) {
-            synchronized (mLock) {
+        synchronized (mLock) {
+            if (mState == STATE_ACTIVE) {
                 mState = STATE_INACTIVE;
                 if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE.");
-                if (mMeasurementInProgress) {
-                    mMeasurementInProgress = false;
-                    mSensorManager.unregisterListener(mListener);
-                }
-                mHandler.removeCallbacks(mMeasurementTimeout);
-                mHandler.removeCallbacks(mSensorRestart);
-                mCurrentGravityVector = null;
-                mPreviousGravityVector = null;
+            }
+            if (mMeasurementInProgress) {
+                mMeasurementInProgress = false;
+                mSensorManager.unregisterListener(mListener);
+            }
+            mHandler.removeCallbacks(mMeasurementTimeout);
+            mHandler.removeCallbacks(mSensorRestart);
+            mCurrentGravityVector = null;
+            mPreviousGravityVector = null;
+            if (mWakeLock.isHeld()) {
                 mWakeLock.release();
+                mHandler.removeCallbacks(mWakelockTimeout);
             }
         }
     }
@@ -173,9 +181,8 @@
                 mMeasurementInProgress = true;
                 mRunningStats.reset();
             }
-            Message msg = Message.obtain(mHandler, mMeasurementTimeout);
-            msg.setAsynchronous(true);
-            mHandler.sendMessageDelayed(msg, ACCELEROMETER_DATA_TIMEOUT_MILLIS);
+            Message measurementTimeoutMsg = Message.obtain(mHandler, mMeasurementTimeout);
+            mHandler.sendMessageDelayed(measurementTimeoutMsg, ACCELEROMETER_DATA_TIMEOUT_MILLIS);
         }
     }
 
@@ -186,10 +193,12 @@
         if (mMeasurementInProgress) {
             mSensorManager.unregisterListener(mListener);
             mHandler.removeCallbacks(mMeasurementTimeout);
-            long detectionEndTime = SystemClock.elapsedRealtime();
             mMeasurementInProgress = false;
             mPreviousGravityVector = mCurrentGravityVector;
             mCurrentGravityVector = mRunningStats.getRunningAverage();
+            if (mRunningStats.getSampleCount() == 0) {
+                Slog.w(TAG, "No accelerometer data acquired for orientation measurement.");
+            }
             if (DEBUG) {
                 Slog.d(TAG, "mRunningStats = " + mRunningStats.toString());
                 String currentGravityVectorString = (mCurrentGravityVector == null) ?
@@ -203,7 +212,10 @@
             status = getStationaryStatus();
             if (DEBUG) Slog.d(TAG, "getStationaryStatus() returned " + status);
             if (status != RESULT_UNKNOWN) {
-                mWakeLock.release();
+                if (mWakeLock.isHeld()) {
+                    mWakeLock.release();
+                    mHandler.removeCallbacks(mWakelockTimeout);
+                }
                 if (DEBUG) {
                     Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE. status = " + status);
                 }
@@ -217,7 +229,6 @@
                         " scheduled in " + ORIENTATION_MEASUREMENT_INTERVAL_MILLIS +
                         " milliseconds.");
                 Message msg = Message.obtain(mHandler, mSensorRestart);
-                msg.setAsynchronous(true);
                 mHandler.sendMessageDelayed(msg, ORIENTATION_MEASUREMENT_INTERVAL_MILLIS);
             }
         }
@@ -271,6 +282,7 @@
                 }
             }
             if (status != RESULT_UNKNOWN) {
+                mHandler.removeCallbacks(mWakelockTimeout);
                 mCallback.onAnyMotionResult(status);
             }
         }
@@ -290,20 +302,30 @@
     };
 
     private final Runnable mMeasurementTimeout = new Runnable() {
-      @Override
-      public void run() {
-          int status = RESULT_UNKNOWN;
-          synchronized (mLock) {
-              if (DEBUG) Slog.i(TAG, "mMeasurementTimeout. Failed to collect sufficient accel " +
+        @Override
+        public void run() {
+            int status = RESULT_UNKNOWN;
+            synchronized (mLock) {
+                if (DEBUG) Slog.i(TAG, "mMeasurementTimeout. Failed to collect sufficient accel " +
                       "data within " + ACCELEROMETER_DATA_TIMEOUT_MILLIS + " ms. Stopping " +
                       "orientation measurement.");
-              status = stopOrientationMeasurementLocked();
-          }
-          if (status != RESULT_UNKNOWN) {
-              mCallback.onAnyMotionResult(status);
-          }
-      }
-  };
+                status = stopOrientationMeasurementLocked();
+            }
+            if (status != RESULT_UNKNOWN) {
+                mHandler.removeCallbacks(mWakelockTimeout);
+                mCallback.onAnyMotionResult(status);
+            }
+        }
+    };
+
+    private final Runnable mWakelockTimeout = new Runnable() {
+        @Override
+        public void run() {
+            synchronized (mLock) {
+                stop();
+            }
+        }
+    };
 
     /**
      * A timestamped three dimensional vector and some vector operations.
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index afed5ef..488f0e7 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -973,13 +973,12 @@
                 cancelSensingTimeoutAlarmLocked();
             }
         }
-        if (result == AnyMotionDetector.RESULT_MOVED) {
-            if (DEBUG) Slog.d(TAG, "RESULT_MOVED received.");
+        if ((result == AnyMotionDetector.RESULT_MOVED) ||
+            (result == AnyMotionDetector.RESULT_UNKNOWN)) {
             synchronized (this) {
-                handleMotionDetectedLocked(mConstants.INACTIVE_TIMEOUT, "sense_motion");
+                handleMotionDetectedLocked(mConstants.INACTIVE_TIMEOUT, "non_stationary");
             }
         } else if (result == AnyMotionDetector.RESULT_STATIONARY) {
-            if (DEBUG) Slog.d(TAG, "RESULT_STATIONARY received.");
             if (mState == STATE_SENSING) {
                 // If we are currently sensing, it is time to move to locating.
                 synchronized (this) {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 29c54e9..88d6c14 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -454,7 +454,7 @@
 
     @Override
     public String toString() {
-        return mPackageName + "/" + mTag;
+        return mPackageName + "/" + mTag + " (uid=" + mUserId + ")";
     }
 
     private void postAdjustLocalVolume(final int stream, final int direction, final int flags,
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index a4d2cd2..a3f09c0 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -47,10 +47,12 @@
 import android.os.IBinder;
 import android.os.Message;
 import android.os.PowerManager;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.speech.RecognizerIntent;
 import android.text.TextUtils;
@@ -67,6 +69,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -97,7 +100,9 @@
     private ContentResolver mContentResolver;
     private SettingsObserver mSettingsObserver;
 
-    private int mCurrentUserId = -1;
+    // List of user IDs running in the foreground.
+    // Multiple users can be in the foreground if the work profile is on.
+    private final List<Integer> mCurrentUserIdList = new ArrayList<>();
 
     // Used to notify system UI when remote volume was changed. TODO find a
     // better way to handle this.
@@ -181,22 +186,26 @@
     }
 
     @Override
-    public void onStartUser(int userHandle) {
+    public void onStartUser(int userId) {
+        if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
         updateUser();
     }
 
     @Override
-    public void onSwitchUser(int userHandle) {
+    public void onSwitchUser(int userId) {
+        if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
         updateUser();
     }
 
     @Override
-    public void onStopUser(int userHandle) {
+    public void onStopUser(int userId) {
+        if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
         synchronized (mLock) {
-            UserRecord user = mUserRecords.get(userHandle);
+            UserRecord user = mUserRecords.get(userId);
             if (user != null) {
                 destroyUserLocked(user);
             }
+            updateUser();
         }
     }
 
@@ -228,18 +237,23 @@
 
     private void updateUser() {
         synchronized (mLock) {
-            int userId = ActivityManager.getCurrentUser();
-            if (mCurrentUserId != userId) {
-                final int oldUserId = mCurrentUserId;
-                mCurrentUserId = userId; // do this first
-
-                UserRecord oldUser = mUserRecords.get(oldUserId);
-                if (oldUser != null) {
-                    oldUser.stopLocked();
+            UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
+            int currentUser = ActivityManager.getCurrentUser();
+            int[] userIds = manager.getEnabledProfileIds(currentUser);
+            mCurrentUserIdList.clear();
+            if (userIds != null && userIds.length > 0) {
+                for (int userId : userIds) {
+                    mCurrentUserIdList.add(userId);
                 }
-
-                UserRecord newUser = getOrCreateUser(userId);
-                newUser.startLocked();
+            } else {
+                // This shouldn't happen.
+                Log.w(TAG, "Failed to get enabled profiles.");
+                mCurrentUserIdList.add(currentUser);
+            }
+            for (int userId : mCurrentUserIdList) {
+                if (mUserRecords.get(userId) == null) {
+                    mUserRecords.put(userId, new UserRecord(getContext(), userId));
+                }
             }
         }
     }
@@ -272,7 +286,6 @@
      * @param user The user to dispose of
      */
     private void destroyUserLocked(UserRecord user) {
-        user.stopLocked();
         user.destroyLocked();
         mUserRecords.remove(user.mUserId);
     }
@@ -436,9 +449,9 @@
         }
 
         mAllSessions.add(session);
-        mPriorityStack.addSession(session);
+        mPriorityStack.addSession(session, mCurrentUserIdList.contains(userId));
 
-        UserRecord user = getOrCreateUser(userId);
+        UserRecord user = mUserRecords.get(userId);
         user.addSessionLocked(session);
 
         mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0);
@@ -449,15 +462,6 @@
         return session;
     }
 
-    private UserRecord getOrCreateUser(int userId) {
-        UserRecord user = mUserRecords.get(userId);
-        if (user == null) {
-            user = new UserRecord(getContext(), userId);
-            mUserRecords.put(userId, user);
-        }
-        return user;
-    }
-
     private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
         for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
             if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
@@ -536,13 +540,6 @@
             restoreMediaButtonReceiver();
         }
 
-        public void startLocked() {
-        }
-
-        public void stopLocked() {
-            // nothing for now
-        }
-
         public void destroyLocked() {
             for (int i = mSessions.size() - 1; i >= 0; i--) {
                 MediaSessionRecord session = mSessions.get(i);
@@ -578,7 +575,7 @@
 
         private void restoreMediaButtonReceiver() {
             String receiverName = Settings.Secure.getStringForUser(mContentResolver,
-                    Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT);
+                    Settings.System.MEDIA_BUTTON_RECEIVER, mUserId);
             if (!TextUtils.isEmpty(receiverName)) {
                 ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
                 if (eventReceiver == null) {
@@ -767,12 +764,22 @@
                 synchronized (mLock) {
                     // If we don't have a media button receiver to fall back on
                     // include non-playing sessions for dispatching
-                    UserRecord ur = mUserRecords.get(mCurrentUserId);
-                    boolean useNotPlayingSessions = (ur == null) ||
-                            (ur.mLastMediaButtonReceiver == null
-                                && ur.mRestoredMediaButtonReceiver == null);
-                    MediaSessionRecord session = mPriorityStack
-                            .getDefaultMediaButtonSession(mCurrentUserId, useNotPlayingSessions);
+                    boolean useNotPlayingSessions = true;
+                    for (int userId : mCurrentUserIdList) {
+                        UserRecord ur = mUserRecords.get(userId);
+                        if (ur.mLastMediaButtonReceiver != null
+                                || ur.mRestoredMediaButtonReceiver != null) {
+                            useNotPlayingSessions = false;
+                            break;
+                        }
+                    }
+
+                    if (DEBUG) {
+                        Log.d(TAG, "dispatchMediaKeyEvent, useNotPlayingSessions="
+                                + useNotPlayingSessions);
+                    }
+                    MediaSessionRecord session = mPriorityStack.getDefaultMediaButtonSession(
+                            mCurrentUserIdList, useNotPlayingSessions);
                     if (isVoiceKey(keyEvent.getKeyCode())) {
                         handleVoiceKeyEventLocked(keyEvent, needWakeLock, session);
                     } else {
@@ -786,13 +793,11 @@
 
         @Override
         public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     MediaSessionRecord session = mPriorityStack
-                            .getDefaultVolumeSession(mCurrentUserId);
+                            .getDefaultVolumeSession(mCurrentUserIdList);
                     dispatchAdjustVolumeLocked(suggestedStream, delta, flags, session);
                 }
             } finally {
@@ -899,7 +904,7 @@
                 }
             } else {
                 session.adjustVolume(direction, flags, getContext().getPackageName(),
-                        UserHandle.myUserId(), true);
+                        Process.SYSTEM_UID, true);
             }
         }
 
@@ -946,13 +951,16 @@
                 // won't release it later
                 session.sendMediaButton(keyEvent,
                         needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
-                        mKeyEventReceiver, getContext().getApplicationInfo().uid,
+                        mKeyEventReceiver, Process.SYSTEM_UID,
                         getContext().getPackageName());
             } else {
                 // Launch the last PendingIntent we had with priority
-                UserRecord user = mUserRecords.get(mCurrentUserId);
-                if (user != null && (user.mLastMediaButtonReceiver != null
-                        || user.mRestoredMediaButtonReceiver != null)) {
+                for (int userId : mCurrentUserIdList) {
+                    UserRecord user = mUserRecords.get(userId);
+                    if (user.mLastMediaButtonReceiver == null
+                            && user.mRestoredMediaButtonReceiver == null) {
+                        continue;
+                    }
                     if (DEBUG) {
                         Log.d(TAG, "Sending media key to last known PendingIntent "
                                 + user.mLastMediaButtonReceiver + " or restored Intent "
@@ -972,30 +980,30 @@
                         } else {
                             mediaButtonIntent.setComponent(user.mRestoredMediaButtonReceiver);
                             getContext().sendBroadcastAsUser(mediaButtonIntent,
-                                    new UserHandle(mCurrentUserId));
+                                    UserHandle.of(userId));
                         }
                     } catch (CanceledException e) {
                         Log.i(TAG, "Error sending key event to media button receiver "
                                 + user.mLastMediaButtonReceiver, e);
                     }
-                } else {
-                    if (DEBUG) {
-                        Log.d(TAG, "Sending media key ordered broadcast");
-                    }
-                    if (needWakeLock) {
-                        mMediaEventWakeLock.acquire();
-                    }
-                    // Fallback to legacy behavior
-                    Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
-                    keyIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-                    keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
-                    if (needWakeLock) {
-                        keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED,
-                                WAKELOCK_RELEASE_ON_FINISHED);
-                    }
-                    getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.CURRENT,
-                            null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
+                    return;
                 }
+                if (DEBUG) {
+                    Log.d(TAG, "Sending media key ordered broadcast");
+                }
+                if (needWakeLock) {
+                    mMediaEventWakeLock.acquire();
+                }
+                // Fallback to legacy behavior
+                Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
+                keyIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+                if (needWakeLock) {
+                    keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
+                }
+                // Send broadcast only to the full user.
+                getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.CURRENT,
+                        null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
             }
         }
 
@@ -1025,6 +1033,7 @@
                 if (voiceIntent != null) {
                     voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                             | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                    if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
                     getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
                 }
             } catch (ActivityNotFoundException e) {
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index cc007ef..3327b36 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -32,7 +32,7 @@
  * Keeps track of media sessions and their priority for notifications, media
  * button dispatch, etc.
  */
-public class MediaSessionStack {
+class MediaSessionStack {
     /**
      * These are states that usually indicate the user took an action and should
      * bump priority regardless of the old state.
@@ -68,13 +68,10 @@
      * Checks if a media session is created from the most recent app.
      *
      * @param record A media session record to be examined.
-     * @return true if the media session's package name equals to the most recent app, false
-     * otherwise.
+     * @return {@code true} if the media session's package name equals to the most recent app, false
+     *            otherwise.
      */
     private static boolean isFromMostRecentApp(MediaSessionRecord record) {
-        if (ActivityManager.getCurrentUser() != record.getUserId()) {
-            return false;
-        }
         try {
             List<ActivityManager.RecentTaskInfo> tasks =
                     ActivityManagerNative.getDefault().getRecentTasks(1,
@@ -84,9 +81,10 @@
                             ActivityManager.RECENT_WITH_EXCLUDED, record.getUserId()).getList();
             if (tasks != null && !tasks.isEmpty()) {
                 ActivityManager.RecentTaskInfo recentTask = tasks.get(0);
-                if (recentTask.baseIntent != null)
+                if (recentTask.userId == record.getUserId() && recentTask.baseIntent != null) {
                     return recentTask.baseIntent.getComponent().getPackageName()
                             .equals(record.getPackageName());
+                }
             }
         } catch (RemoteException e) {
             return false;
@@ -98,11 +96,12 @@
      * Add a record to the priority tracker.
      *
      * @param record The record to add.
+     * @param fromForegroundUser {@code true} if the session is created by the foreground user.
      */
-    public void addSession(MediaSessionRecord record) {
+    public void addSession(MediaSessionRecord record, boolean fromForegroundUser) {
         mSessions.add(record);
         clearCache();
-        if (isFromMostRecentApp(record)) {
+        if (fromForegroundUser && isFromMostRecentApp(record)) {
             mLastInterestingRecord = record;
         }
     }
@@ -210,12 +209,13 @@
     /**
      * Get the highest priority session that can handle media buttons.
      *
-     * @param userId The user to check.
+     * @param userIdList The user lists to check.
      * @param includeNotPlaying Return a non-playing session if nothing else is
      *            available
      * @return The default media button session or null.
      */
-    public MediaSessionRecord getDefaultMediaButtonSession(int userId, boolean includeNotPlaying) {
+    public MediaSessionRecord getDefaultMediaButtonSession(
+            List<Integer> userIdList, boolean includeNotPlaying) {
         if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) {
             return mGlobalPrioritySession;
         }
@@ -223,7 +223,7 @@
             return mCachedButtonReceiver;
         }
         ArrayList<MediaSessionRecord> records = getPriorityListLocked(true,
-                MediaSession.FLAG_HANDLES_MEDIA_BUTTONS, userId);
+                MediaSession.FLAG_HANDLES_MEDIA_BUTTONS, userIdList);
         if (records.size() > 0) {
             MediaSessionRecord record = records.get(0);
             if (record.isPlaybackActive(false)) {
@@ -248,14 +248,14 @@
         return mCachedButtonReceiver;
     }
 
-    public MediaSessionRecord getDefaultVolumeSession(int userId) {
+    public MediaSessionRecord getDefaultVolumeSession(List<Integer> userIdList) {
         if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) {
             return mGlobalPrioritySession;
         }
         if (mCachedVolumeDefault != null) {
             return mCachedVolumeDefault;
         }
-        ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId);
+        ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userIdList);
         int size = records.size();
         for (int i = 0; i < size; i++) {
             MediaSessionRecord record = records.get(i);
@@ -298,6 +298,13 @@
         }
     }
 
+    private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags,
+            int userId) {
+        List<Integer> userIdList = new ArrayList<>();
+        userIdList.add(userId);
+        return getPriorityListLocked(activeOnly, withFlags, userIdList);
+    }
+
     /**
      * Get a priority sorted list of sessions. Can filter to only return active
      * sessions or sessions with specific flags.
@@ -306,22 +313,23 @@
      *            all sessions.
      * @param withFlags Only return sessions with all the specified flags set. 0
      *            returns all sessions.
-     * @param userId The user to get sessions for. {@link UserHandle#USER_ALL}
+     * @param userIdList The user to get sessions for. {@link UserHandle#USER_ALL}
      *            will return sessions for all users.
      * @return The priority sorted list of sessions.
      */
     private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags,
-            int userId) {
+            List<Integer> userIdList) {
         ArrayList<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>();
         int lastLocalIndex = 0;
         int lastActiveIndex = 0;
         int lastPublishedIndex = 0;
 
+        boolean filterUser = !userIdList.contains(UserHandle.USER_ALL);
         int size = mSessions.size();
         for (int i = 0; i < size; i++) {
             final MediaSessionRecord session = mSessions.get(i);
 
-            if (userId != UserHandle.USER_ALL && userId != session.getUserId()) {
+            if (filterUser && !userIdList.contains(session.getUserId())) {
                 // Filter out sessions for the wrong user
                 continue;
             }