Merge changes from topic "mediasession2_stack"
* changes: MediaSessionService: Keep Media1 and Media2 session in the one place Introduce common interfaces for both session1 and session2
This commit is contained in:
committed by
Android (Google) Code Review
commit
f90b3a9a35
@@ -141,6 +141,9 @@ public class MediaController2 implements AutoCloseable {
|
||||
// Note: unbindService() throws IllegalArgumentException when it's called twice.
|
||||
return;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "closing " + this);
|
||||
}
|
||||
mClosed = true;
|
||||
if (mServiceConnection != null) {
|
||||
// Note: This should be called even when the bindService() has returned false.
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright 2019 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.media;
|
||||
|
||||
import android.media.MediaController2;
|
||||
import android.media.Session2CommandGroup;
|
||||
import android.media.Session2Token;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerExecutor;
|
||||
import android.os.Looper;
|
||||
import android.os.ResultReceiver;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* Keeps the record of {@link Session2Token} helps to send command to the corresponding session.
|
||||
*/
|
||||
// TODO(jaewan): Do not call service method directly -- introduce listener instead.
|
||||
public class MediaSession2Record implements MediaSessionRecordImpl {
|
||||
private static final String TAG = "MediaSession2Record";
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
private final Object mLock = new Object();
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private final Session2Token mSessionToken;
|
||||
@GuardedBy("mLock")
|
||||
private final HandlerExecutor mHandlerExecutor;
|
||||
@GuardedBy("mLock")
|
||||
private final MediaController2 mController;
|
||||
@GuardedBy("mLock")
|
||||
private final MediaSessionService mService;
|
||||
@GuardedBy("mLock")
|
||||
private boolean mIsConnected;
|
||||
|
||||
public MediaSession2Record(Session2Token sessionToken, MediaSessionService service,
|
||||
Looper handlerLooper) {
|
||||
mSessionToken = sessionToken;
|
||||
mService = service;
|
||||
mHandlerExecutor = new HandlerExecutor(new Handler(handlerLooper));
|
||||
mController = new MediaController2.Builder(service.getContext(), sessionToken)
|
||||
.setControllerCallback(mHandlerExecutor, new Controller2Callback())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPackageName() {
|
||||
return mSessionToken.getPackageName();
|
||||
}
|
||||
|
||||
public Session2Token getSession2Token() {
|
||||
return mSessionToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUid() {
|
||||
return mSessionToken.getUid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUserId() {
|
||||
return UserHandle.getUserId(mSessionToken.getUid());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSystemPriority() {
|
||||
// System priority session is currently only allowed for telephony, and it's OK to stick to
|
||||
// the media1 API at this moment.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void adjustVolume(String packageName, String opPackageName, int pid, int uid,
|
||||
boolean asSystemService, int direction, int flags, boolean useSuggested) {
|
||||
// TODO(jaewan): Add API to adjust volume.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
synchronized (mLock) {
|
||||
return mIsConnected;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkPlaybackActiveState(boolean expected) {
|
||||
synchronized (mLock) {
|
||||
return mIsConnected && mController.isPlaybackActive() == expected;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlaybackTypeLocal() {
|
||||
// TODO(jaewan): Implement -- need API to know whether the playback is remote or local.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
synchronized (mLock) {
|
||||
// Call close regardless of the mIsAvailable. This may be called when it's not yet
|
||||
// connected.
|
||||
mController.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService,
|
||||
KeyEvent ke, int sequenceId, ResultReceiver cb) {
|
||||
// TODO(jaewan): Implement.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(PrintWriter pw, String prefix) {
|
||||
pw.println(prefix + "token=" + mSessionToken);
|
||||
pw.println(prefix + "controller=" + mController);
|
||||
|
||||
final String indent = prefix + " ";
|
||||
pw.println(indent + "playbackActive=" + mController.isPlaybackActive());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// TODO(jaewan): Also add getId().
|
||||
return getPackageName() + " (userId=" + getUserId() + ")";
|
||||
}
|
||||
|
||||
private class Controller2Callback extends MediaController2.ControllerCallback {
|
||||
@Override
|
||||
public void onConnected(MediaController2 controller, Session2CommandGroup allowedCommands) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "connected to " + mSessionToken + ", allowed=" + allowedCommands);
|
||||
}
|
||||
synchronized (mLock) {
|
||||
mIsConnected = true;
|
||||
}
|
||||
mService.onSessionActiveStateChanged(MediaSession2Record.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnected(MediaController2 controller) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "disconnected from " + mSessionToken);
|
||||
}
|
||||
synchronized (mLock) {
|
||||
mIsConnected = false;
|
||||
}
|
||||
mService.onSessionDied(MediaSession2Record.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackActiveChanged(MediaController2 controller, boolean playbackActive) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "playback active changed, " + mSessionToken + ", active="
|
||||
+ playbackActive);
|
||||
}
|
||||
mService.onSessionPlaybackStateChanged(MediaSession2Record.this, playbackActive);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,13 +56,15 @@ import com.android.server.LocalServices;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is the system implementation of a Session. Apps will interact with the
|
||||
* MediaSession wrapper class instead.
|
||||
*/
|
||||
public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable {
|
||||
// TODO(jaewan): Do not call service method directly -- introduce listener instead.
|
||||
public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionRecordImpl {
|
||||
private static final String TAG = "MediaSessionRecord";
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
@@ -72,6 +74,24 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable
|
||||
*/
|
||||
private static final int OPTIMISTIC_VOLUME_TIMEOUT = 1000;
|
||||
|
||||
/**
|
||||
* These are states that usually indicate the user took an action and should
|
||||
* bump priority regardless of the old state.
|
||||
*/
|
||||
private static final List<Integer> ALWAYS_PRIORITY_STATES = Arrays.asList(
|
||||
PlaybackState.STATE_FAST_FORWARDING,
|
||||
PlaybackState.STATE_REWINDING,
|
||||
PlaybackState.STATE_SKIPPING_TO_PREVIOUS,
|
||||
PlaybackState.STATE_SKIPPING_TO_NEXT);
|
||||
/**
|
||||
* These are states that usually indicate the user took an action if they
|
||||
* were entered from a non-priority state.
|
||||
*/
|
||||
private static final List<Integer> TRANSITION_PRIORITY_STATES = Arrays.asList(
|
||||
PlaybackState.STATE_BUFFERING,
|
||||
PlaybackState.STATE_CONNECTING,
|
||||
PlaybackState.STATE_PLAYING);
|
||||
|
||||
private final MessageHandler mHandler;
|
||||
|
||||
private final int mOwnerPid;
|
||||
@@ -170,6 +190,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable
|
||||
*
|
||||
* @return Info that identifies this session.
|
||||
*/
|
||||
@Override
|
||||
public String getPackageName() {
|
||||
return mPackageName;
|
||||
}
|
||||
@@ -188,6 +209,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable
|
||||
*
|
||||
* @return The UID for this session.
|
||||
*/
|
||||
@Override
|
||||
public int getUid() {
|
||||
return mOwnerUid;
|
||||
}
|
||||
@@ -197,6 +219,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable
|
||||
*
|
||||
* @return The user id for this session.
|
||||
*/
|
||||
@Override
|
||||
public int getUserId() {
|
||||
return mUserId;
|
||||
}
|
||||
@@ -207,6 +230,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable
|
||||
*
|
||||
* @return True if this is a system priority session, false otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean isSystemPriority() {
|
||||
return (mFlags & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0;
|
||||
}
|
||||
@@ -220,7 +244,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable
|
||||
* @param opPackageName The op package that made the original volume request.
|
||||
* @param pid The pid that made the original volume request.
|
||||
* @param uid The uid that made the original volume request.
|
||||
* @param caller caller binder. can be {@code null} if it's from the volume key.
|
||||
* @param asSystemService {@code true} if the event sent to the session as if it was come from
|
||||
* the system service instead of the app process. This helps sessions to distinguish
|
||||
* between the key injection by the app and key events from the hardware devices.
|
||||
@@ -318,9 +341,13 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable
|
||||
|
||||
/**
|
||||
* Check if this session has been set to active by the app.
|
||||
* <p>
|
||||
* It's not used to prioritize sessions for dispatching media keys since API 26, but still used
|
||||
* to filter session list in MediaSessionManager#getActiveSessions().
|
||||
*
|
||||
* @return True if the session is active, false otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return mIsActive && !mDestroyed;
|
||||
}
|
||||
@@ -333,6 +360,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable
|
||||
* @param expected True if playback is expected to be active. false otherwise.
|
||||
* @return True if the session's playback matches with the expectation. false otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean checkPlaybackActiveState(boolean expected) {
|
||||
if (mPlaybackState == null) {
|
||||
return false;
|
||||
@@ -345,13 +373,14 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable
|
||||
*
|
||||
* @return {@code true} if the playback is local.
|
||||
*/
|
||||
public boolean isPlaybackLocal() {
|
||||
@Override
|
||||
public boolean isPlaybackTypeLocal() {
|
||||
return mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
mService.sessionDied(this);
|
||||
mService.onSessionDied(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -383,7 +412,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable
|
||||
* @param sequenceId (optional) sequence id. Use this only when a wake lock is needed.
|
||||
* @param cb (optional) result receiver to receive callback. Use this only when a wake lock is
|
||||
* needed.
|
||||
* @return {@code true} if the attempt to send media button was successfuly.
|
||||
* @return {@code true} if the attempt to send media button was successfully.
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService,
|
||||
@@ -392,6 +421,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable
|
||||
cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(PrintWriter pw, String prefix) {
|
||||
pw.println(prefix + mTag + " " + this);
|
||||
|
||||
@@ -712,7 +742,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable
|
||||
public void destroySession() throws RemoteException {
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mService.destroySession(MediaSessionRecord.this);
|
||||
mService.onSessionDied(MediaSessionRecord.this);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
@@ -734,7 +764,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable
|
||||
mIsActive = active;
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mService.updateSession(MediaSessionRecord.this);
|
||||
mService.onSessionActiveStateChanged(MediaSessionRecord.this);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
@@ -801,12 +831,16 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable
|
||||
? PlaybackState.STATE_NONE : mPlaybackState.getState();
|
||||
int newState = state == null
|
||||
? PlaybackState.STATE_NONE : state.getState();
|
||||
boolean shouldUpdatePriority = ALWAYS_PRIORITY_STATES.contains(newState)
|
||||
|| (!TRANSITION_PRIORITY_STATES.contains(oldState)
|
||||
&& TRANSITION_PRIORITY_STATES.contains(newState));
|
||||
synchronized (mLock) {
|
||||
mPlaybackState = state;
|
||||
}
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mService.onSessionPlaystateChanged(MediaSessionRecord.this, oldState, newState);
|
||||
mService.onSessionPlaybackStateChanged(
|
||||
MediaSessionRecord.this, shouldUpdatePriority);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright 2019 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.media;
|
||||
|
||||
import android.media.AudioManager;
|
||||
import android.os.ResultReceiver;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* Common interfaces between {@link MediaSessionRecord} and {@link MediaSession2Record}.
|
||||
*/
|
||||
public interface MediaSessionRecordImpl extends AutoCloseable {
|
||||
|
||||
/**
|
||||
* Get the info for this session.
|
||||
*
|
||||
* @return Info that identifies this session.
|
||||
*/
|
||||
String getPackageName();
|
||||
|
||||
/**
|
||||
* Get the UID this session was created for.
|
||||
*
|
||||
* @return The UID for this session.
|
||||
*/
|
||||
int getUid();
|
||||
|
||||
/**
|
||||
* Get the user id this session was created for.
|
||||
*
|
||||
* @return The user id for this session.
|
||||
*/
|
||||
int getUserId();
|
||||
|
||||
/**
|
||||
* Check if this session has system priorty and should receive media buttons
|
||||
* before any other sessions.
|
||||
*
|
||||
* @return True if this is a system priority session, false otherwise
|
||||
*/
|
||||
boolean isSystemPriority();
|
||||
|
||||
/**
|
||||
* Send a volume adjustment to the session owner. Direction must be one of
|
||||
* {@link AudioManager#ADJUST_LOWER}, {@link AudioManager#ADJUST_RAISE},
|
||||
* {@link AudioManager#ADJUST_SAME}.
|
||||
*
|
||||
* @param packageName The package that made the original volume request.
|
||||
* @param opPackageName The op package that made the original volume request.
|
||||
* @param pid The pid that made the original volume request.
|
||||
* @param uid The uid that made the original volume request.
|
||||
* @param asSystemService {@code true} if the event sent to the session as if it was come from
|
||||
* the system service instead of the app process. This helps sessions to distinguish
|
||||
* between the key injection by the app and key events from the hardware devices.
|
||||
* Should be used only when the volume key events aren't handled by foreground
|
||||
* activity. {@code false} otherwise to tell session about the real caller.
|
||||
* @param direction The direction to adjust volume in.
|
||||
* @param flags Any of the flags from {@link AudioManager}.
|
||||
* @param useSuggested True to use adjustSuggestedStreamVolume instead of
|
||||
*/
|
||||
void adjustVolume(String packageName, String opPackageName, int pid, int uid,
|
||||
boolean asSystemService, int direction, int flags, boolean useSuggested);
|
||||
|
||||
/**
|
||||
* Check if this session has been set to active by the app. (i.e. ready to receive command and
|
||||
* getters are available).
|
||||
*
|
||||
* @return True if the session is active, false otherwise.
|
||||
*/
|
||||
// TODO(jaewan): Find better naming, or remove this from the MediaSessionRecordImpl.
|
||||
boolean isActive();
|
||||
|
||||
/**
|
||||
* Check if the session's playback active state matches with the expectation. This always return
|
||||
* {@code false} if the playback state is unknown (e.g. {@code null}), where we cannot know the
|
||||
* actual playback state associated with the session.
|
||||
*
|
||||
* @param expected True if playback is expected to be active. false otherwise.
|
||||
* @return True if the session's playback matches with the expectation. false otherwise.
|
||||
*/
|
||||
boolean checkPlaybackActiveState(boolean expected);
|
||||
|
||||
/**
|
||||
* Check whether the playback type is local or remote.
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li>Local: volume changes the stream volume because playback happens on this device.</li>
|
||||
* <li>Remote: volume is sent to the apps callback because playback happens on the remote
|
||||
* device and we cannot know how to control the volume of it.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return {@code true} if the playback is local. {@code false} if the playback is remote.
|
||||
*/
|
||||
boolean isPlaybackTypeLocal();
|
||||
|
||||
/**
|
||||
* Sends media button.
|
||||
*
|
||||
* @param packageName caller package name
|
||||
* @param pid caller pid
|
||||
* @param uid caller uid
|
||||
* @param asSystemService {@code true} if the event sent to the session as if it was come from
|
||||
* the system service instead of the app process.
|
||||
* @param ke key events
|
||||
* @param sequenceId (optional) sequence id. Use this only when a wake lock is needed.
|
||||
* @param cb (optional) result receiver to receive callback. Use this only when a wake lock is
|
||||
* needed.
|
||||
* @return {@code true} if the attempt to send media button was successfully.
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService,
|
||||
KeyEvent ke, int sequenceId, ResultReceiver cb);
|
||||
|
||||
/**
|
||||
* Dumps internal state
|
||||
*
|
||||
* @param pw print writer
|
||||
* @param prefix prefix
|
||||
*/
|
||||
void dump(PrintWriter pw, String prefix);
|
||||
|
||||
/**
|
||||
* Override {@link AutoCloseable#close} to tell not to throw exception.
|
||||
*/
|
||||
@Override
|
||||
void close();
|
||||
}
|
||||
@@ -40,10 +40,7 @@ import android.media.AudioManager;
|
||||
import android.media.AudioManagerInternal;
|
||||
import android.media.AudioPlaybackConfiguration;
|
||||
import android.media.AudioSystem;
|
||||
import android.media.IAudioService;
|
||||
import android.media.IRemoteVolumeController;
|
||||
import android.media.MediaController2;
|
||||
import android.media.Session2CommandGroup;
|
||||
import android.media.Session2Token;
|
||||
import android.media.session.IActiveSessionsListener;
|
||||
import android.media.session.IOnMediaKeyEventDispatchedListener;
|
||||
@@ -61,7 +58,6 @@ import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerExecutor;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.os.PowerManager;
|
||||
@@ -123,11 +119,6 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
@GuardedBy("mLock")
|
||||
private final ArrayList<SessionsListenerRecord> mSessionsListeners =
|
||||
new ArrayList<SessionsListenerRecord>();
|
||||
// Map user id as index to list of Session2Tokens
|
||||
// TODO: Keep session2 info in MediaSessionStack for prioritizing both session1 and session2 in
|
||||
// one place.
|
||||
@GuardedBy("mLock")
|
||||
private final SparseArray<List<Session2Token>> mSession2TokensPerUser = new SparseArray<>();
|
||||
@GuardedBy("mLock")
|
||||
private final List<Session2TokensListenerRecord> mSession2TokensListenerRecords =
|
||||
new ArrayList<>();
|
||||
@@ -189,16 +180,11 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
updateUser();
|
||||
}
|
||||
|
||||
private IAudioService getAudioService() {
|
||||
IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
|
||||
return IAudioService.Stub.asInterface(b);
|
||||
}
|
||||
|
||||
private boolean isGlobalPriorityActiveLocked() {
|
||||
return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive();
|
||||
}
|
||||
|
||||
void updateSession(MediaSessionRecord record) {
|
||||
void onSessionActiveStateChanged(MediaSessionRecordImpl record) {
|
||||
synchronized (mLock) {
|
||||
FullUserRecord user = getFullUserRecordLocked(record.getUserId());
|
||||
if (user == null) {
|
||||
@@ -215,12 +201,14 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
Log.w(TAG, "Unknown session updated. Ignoring.");
|
||||
return;
|
||||
}
|
||||
user.mPriorityStack.onSessionStateChange(record);
|
||||
user.mPriorityStack.onSessionActiveStateChanged(record);
|
||||
}
|
||||
mHandler.postSessionsChanged(record.getUserId());
|
||||
|
||||
mHandler.postSessionsChanged(record);
|
||||
}
|
||||
}
|
||||
|
||||
// Currently only media1 can become global priority session.
|
||||
void setGlobalPrioritySession(MediaSessionRecord record) {
|
||||
synchronized (mLock) {
|
||||
FullUserRecord user = getFullUserRecordLocked(record.getUserId());
|
||||
@@ -266,11 +254,13 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
List<Session2Token> getSession2TokensLocked(int userId) {
|
||||
List<Session2Token> list = new ArrayList<>();
|
||||
if (userId == USER_ALL) {
|
||||
for (int i = 0; i < mSession2TokensPerUser.size(); i++) {
|
||||
list.addAll(mSession2TokensPerUser.valueAt(i));
|
||||
int size = mUserRecords.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
list.addAll(mUserRecords.valueAt(i).mPriorityStack.getSession2Tokens(userId));
|
||||
}
|
||||
} else {
|
||||
list.addAll(mSession2TokensPerUser.get(userId));
|
||||
FullUserRecord user = getFullUserRecordLocked(userId);
|
||||
list.addAll(user.mPriorityStack.getSession2Tokens(userId));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
@@ -297,14 +287,15 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
}
|
||||
}
|
||||
|
||||
void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) {
|
||||
void onSessionPlaybackStateChanged(MediaSessionRecordImpl record,
|
||||
boolean shouldUpdatePriority) {
|
||||
synchronized (mLock) {
|
||||
FullUserRecord user = getFullUserRecordLocked(record.getUserId());
|
||||
if (user == null || !user.mPriorityStack.contains(record)) {
|
||||
Log.d(TAG, "Unknown session changed playback state. Ignoring.");
|
||||
return;
|
||||
}
|
||||
user.mPriorityStack.onPlaystateChanged(record, oldState, newState);
|
||||
user.mPriorityStack.onPlaybackStateChanged(record, shouldUpdatePriority);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,7 +338,6 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
user.destroySessionsForUserLocked(userId);
|
||||
}
|
||||
}
|
||||
mSession2TokensPerUser.remove(userId);
|
||||
updateUser();
|
||||
}
|
||||
}
|
||||
@@ -366,13 +356,7 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
}
|
||||
}
|
||||
|
||||
void sessionDied(MediaSessionRecord session) {
|
||||
synchronized (mLock) {
|
||||
destroySessionLocked(session);
|
||||
}
|
||||
}
|
||||
|
||||
void destroySession(MediaSessionRecord session) {
|
||||
void onSessionDied(MediaSessionRecordImpl session) {
|
||||
synchronized (mLock) {
|
||||
destroySessionLocked(session);
|
||||
}
|
||||
@@ -393,9 +377,6 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id));
|
||||
}
|
||||
}
|
||||
if (mSession2TokensPerUser.get(userInfo.id) == null) {
|
||||
mSession2TokensPerUser.put(userInfo.id, new ArrayList<>());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ensure that the current full user exists.
|
||||
@@ -405,9 +386,6 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId);
|
||||
mCurrentFullUserRecord = new FullUserRecord(currentFullUserId);
|
||||
mUserRecords.put(currentFullUserId, mCurrentFullUserRecord);
|
||||
if (mSession2TokensPerUser.get(currentFullUserId) == null) {
|
||||
mSession2TokensPerUser.put(currentFullUserId, new ArrayList<>());
|
||||
}
|
||||
}
|
||||
mFullUserIds.put(currentFullUserId, currentFullUserId);
|
||||
}
|
||||
@@ -444,7 +422,7 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
* 5. We need to unlink to death from the cb binder
|
||||
* 6. We need to tell the session to do any final cleanup (onDestroy)
|
||||
*/
|
||||
private void destroySessionLocked(MediaSessionRecord session) {
|
||||
private void destroySessionLocked(MediaSessionRecordImpl session) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Destroying " + session);
|
||||
}
|
||||
@@ -461,7 +439,7 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
}
|
||||
|
||||
session.close();
|
||||
mHandler.postSessionsChanged(session.getUserId());
|
||||
mHandler.postSessionsChanged(session);
|
||||
}
|
||||
|
||||
private void enforcePackageName(String packageName, int uid) {
|
||||
@@ -541,15 +519,6 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
return false;
|
||||
}
|
||||
|
||||
private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
|
||||
String callerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo)
|
||||
throws RemoteException {
|
||||
synchronized (mLock) {
|
||||
return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb,
|
||||
tag, sessionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* When a session is created the following things need to happen.
|
||||
* 1. Its callback binder needs a link to death
|
||||
@@ -557,29 +526,31 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
* 3. It needs to be added to the priority stack.
|
||||
* 4. It needs to be added to the relevant user record.
|
||||
*/
|
||||
private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
|
||||
private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
|
||||
String callerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo) {
|
||||
FullUserRecord user = getFullUserRecordLocked(userId);
|
||||
if (user == null) {
|
||||
Log.w(TAG, "Request from invalid user: " + userId + ", pkg=" + callerPackageName);
|
||||
throw new RuntimeException("Session request from invalid user.");
|
||||
}
|
||||
synchronized (mLock) {
|
||||
FullUserRecord user = getFullUserRecordLocked(userId);
|
||||
if (user == null) {
|
||||
Log.w(TAG, "Request from invalid user: " + userId + ", pkg=" + callerPackageName);
|
||||
throw new RuntimeException("Session request from invalid user.");
|
||||
}
|
||||
|
||||
final MediaSessionRecord session;
|
||||
try {
|
||||
session = new MediaSessionRecord(callerPid, callerUid, userId,
|
||||
callerPackageName, cb, tag, sessionInfo, this, mHandler.getLooper());
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException("Media Session owner died prematurely.", e);
|
||||
}
|
||||
final MediaSessionRecord session;
|
||||
try {
|
||||
session = new MediaSessionRecord(callerPid, callerUid, userId,
|
||||
callerPackageName, cb, tag, sessionInfo, this, mHandler.getLooper());
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException("Media Session owner died prematurely.", e);
|
||||
}
|
||||
|
||||
user.mPriorityStack.addSession(session);
|
||||
mHandler.postSessionsChanged(userId);
|
||||
user.mPriorityStack.addSession(session);
|
||||
mHandler.postSessionsChanged(session);
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
|
||||
}
|
||||
return session;
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
|
||||
@@ -600,16 +571,16 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void pushSessionsChanged(int userId) {
|
||||
private void pushSession1Changed(int userId) {
|
||||
synchronized (mLock) {
|
||||
FullUserRecord user = getFullUserRecordLocked(userId);
|
||||
if (user == null) {
|
||||
Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId);
|
||||
Log.w(TAG, "pushSession1ChangedOnHandler failed. No user with id=" + userId);
|
||||
return;
|
||||
}
|
||||
List<MediaSessionRecord> records = getActiveSessionsLocked(userId);
|
||||
int size = records.size();
|
||||
ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
|
||||
ArrayList<MediaSession.Token> tokens = new ArrayList<>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
tokens.add(records.get(i).getSessionToken());
|
||||
}
|
||||
@@ -629,6 +600,27 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
}
|
||||
}
|
||||
|
||||
void pushSession2Changed(int userId) {
|
||||
synchronized (mLock) {
|
||||
List<Session2Token> allSession2Tokens = getSession2TokensLocked(USER_ALL);
|
||||
List<Session2Token> session2Tokens = getSession2TokensLocked(userId);
|
||||
|
||||
for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) {
|
||||
Session2TokensListenerRecord listenerRecord = mSession2TokensListenerRecords.get(i);
|
||||
try {
|
||||
if (listenerRecord.userId == USER_ALL) {
|
||||
listenerRecord.listener.onSession2TokensChanged(allSession2Tokens);
|
||||
} else if (listenerRecord.userId == userId) {
|
||||
listenerRecord.listener.onSession2TokensChanged(session2Tokens);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed to notify Session2Token change. Removing listener.", e);
|
||||
mSession2TokensListenerRecords.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void pushRemoteVolumeUpdateLocked(int userId) {
|
||||
FullUserRecord user = getFullUserRecordLocked(userId);
|
||||
if (user == null) {
|
||||
@@ -638,8 +630,13 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
|
||||
synchronized (mLock) {
|
||||
int size = mRemoteVolumeControllers.beginBroadcast();
|
||||
MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId);
|
||||
MediaSession.Token token = record == null ? null : record.getSessionToken();
|
||||
MediaSessionRecordImpl record = user.mPriorityStack.getDefaultRemoteSession(userId);
|
||||
if (record instanceof MediaSession2Record) {
|
||||
// TODO(jaewan): Implement
|
||||
return;
|
||||
}
|
||||
MediaSession.Token token = record == null
|
||||
? null : ((MediaSessionRecord) record).getSessionToken();
|
||||
|
||||
for (int i = size - 1; i >= 0; i--) {
|
||||
try {
|
||||
@@ -653,34 +650,15 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
}
|
||||
}
|
||||
|
||||
void pushSession2TokensChangedLocked(int userId) {
|
||||
List<Session2Token> allSession2Tokens = getSession2TokensLocked(USER_ALL);
|
||||
List<Session2Token> session2Tokens = getSession2TokensLocked(userId);
|
||||
|
||||
for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) {
|
||||
Session2TokensListenerRecord listenerRecord = mSession2TokensListenerRecords.get(i);
|
||||
try {
|
||||
if (listenerRecord.userId == USER_ALL) {
|
||||
listenerRecord.listener.onSession2TokensChanged(allSession2Tokens);
|
||||
} else if (listenerRecord.userId == userId) {
|
||||
listenerRecord.listener.onSession2TokensChanged(session2Tokens);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed to notify Session2Token change. Removing listener.", e);
|
||||
mSession2TokensListenerRecords.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the media button receiver for the {@code record} is changed.
|
||||
*
|
||||
* @param record the media session whose media button receiver is updated.
|
||||
*/
|
||||
public void onMediaButtonReceiverChanged(MediaSessionRecord record) {
|
||||
public void onMediaButtonReceiverChanged(MediaSessionRecordImpl record) {
|
||||
synchronized (mLock) {
|
||||
FullUserRecord user = getFullUserRecordLocked(record.getUserId());
|
||||
MediaSessionRecord mediaButtonSession =
|
||||
MediaSessionRecordImpl mediaButtonSession =
|
||||
user.mPriorityStack.getMediaButtonSession();
|
||||
if (record == mediaButtonSession) {
|
||||
user.rememberMediaButtonReceiverLocked(mediaButtonSession);
|
||||
@@ -868,39 +846,34 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
pw.println(indent + "Restored MediaButtonReceiverComponentType: "
|
||||
+ mRestoredMediaButtonReceiverComponentType);
|
||||
mPriorityStack.dump(pw, indent);
|
||||
pw.println(indent + "Session2Tokens:");
|
||||
for (int i = 0; i < mSession2TokensPerUser.size(); i++) {
|
||||
List<Session2Token> list = mSession2TokensPerUser.valueAt(i);
|
||||
if (list == null || list.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
for (Session2Token token : list) {
|
||||
pw.println(indent + " " + token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession,
|
||||
MediaSessionRecord newMediaButtonSession) {
|
||||
public void onMediaButtonSessionChanged(MediaSessionRecordImpl oldMediaButtonSession,
|
||||
MediaSessionRecordImpl newMediaButtonSession) {
|
||||
if (DEBUG_KEY_EVENT) {
|
||||
Log.d(TAG, "Media button session is changed to " + newMediaButtonSession);
|
||||
}
|
||||
synchronized (mLock) {
|
||||
if (oldMediaButtonSession != null) {
|
||||
mHandler.postSessionsChanged(oldMediaButtonSession.getUserId());
|
||||
mHandler.postSessionsChanged(oldMediaButtonSession);
|
||||
}
|
||||
if (newMediaButtonSession != null) {
|
||||
rememberMediaButtonReceiverLocked(newMediaButtonSession);
|
||||
mHandler.postSessionsChanged(newMediaButtonSession.getUserId());
|
||||
mHandler.postSessionsChanged(newMediaButtonSession);
|
||||
}
|
||||
pushAddressedPlayerChangedLocked();
|
||||
}
|
||||
}
|
||||
|
||||
// Remember media button receiver and keep it in the persistent storage.
|
||||
public void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
|
||||
PendingIntent receiver = record.getMediaButtonReceiver();
|
||||
public void rememberMediaButtonReceiverLocked(MediaSessionRecordImpl record) {
|
||||
if (record instanceof MediaSession2Record) {
|
||||
// TODO(jaewan): Implement
|
||||
return;
|
||||
}
|
||||
MediaSessionRecord sessionRecord = (MediaSessionRecord) record;
|
||||
PendingIntent receiver = sessionRecord.getMediaButtonReceiver();
|
||||
mLastMediaButtonReceiver = receiver;
|
||||
mRestoredMediaButtonReceiver = null;
|
||||
mRestoredMediaButtonReceiverComponentType = COMPONENT_TYPE_INVALID;
|
||||
@@ -925,10 +898,15 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
private void pushAddressedPlayerChangedLocked(
|
||||
IOnMediaKeyEventSessionChangedListener callback) {
|
||||
try {
|
||||
MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked();
|
||||
MediaSessionRecordImpl mediaButtonSession = getMediaButtonSessionLocked();
|
||||
if (mediaButtonSession != null) {
|
||||
callback.onMediaKeyEventSessionChanged(mediaButtonSession.getPackageName(),
|
||||
mediaButtonSession.getSessionToken());
|
||||
if (mediaButtonSession instanceof MediaSessionRecord) {
|
||||
MediaSessionRecord session1 = (MediaSessionRecord) mediaButtonSession;
|
||||
callback.onMediaKeyEventSessionChanged(session1.getPackageName(),
|
||||
session1.getSessionToken());
|
||||
} else {
|
||||
// TODO(jaewan): Implement
|
||||
}
|
||||
} else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
|
||||
callback.onMediaKeyEventSessionChanged(
|
||||
mCurrentFullUserRecord.mLastMediaButtonReceiver
|
||||
@@ -951,7 +929,7 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
}
|
||||
}
|
||||
|
||||
private MediaSessionRecord getMediaButtonSessionLocked() {
|
||||
private MediaSessionRecordImpl getMediaButtonSessionLocked() {
|
||||
return isGlobalPriorityActiveLocked()
|
||||
? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
|
||||
}
|
||||
@@ -1132,14 +1110,13 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid
|
||||
+ " but actually=" + sessionToken.getUid());
|
||||
}
|
||||
Controller2Callback callback = new Controller2Callback(sessionToken);
|
||||
// Note: It's safe not to keep controller here because it wouldn't be GC'ed until
|
||||
// it's closed.
|
||||
// TODO: Keep controller as well for better readability
|
||||
// because the GC behavior isn't straightforward.
|
||||
MediaController2 controller = new MediaController2.Builder(mContext, sessionToken)
|
||||
.setControllerCallback(new HandlerExecutor(mHandler), callback)
|
||||
.build();
|
||||
MediaSession2Record record = new MediaSession2Record(
|
||||
sessionToken, MediaSessionService.this, mHandler.getLooper());
|
||||
synchronized (mLock) {
|
||||
FullUserRecord user = getFullUserRecordLocked(record.getUserId());
|
||||
user.mPriorityStack.addSession(record);
|
||||
}
|
||||
// Do not immediately notify changes -- do so when framework can dispatch command
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
@@ -1180,7 +1157,8 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
null /* optional packageName */);
|
||||
List<Session2Token> result;
|
||||
synchronized (mLock) {
|
||||
result = getSession2TokensLocked(resolvedUserId);
|
||||
FullUserRecord user = getFullUserRecordLocked(userId);
|
||||
result = user.mPriorityStack.getSession2Tokens(resolvedUserId);
|
||||
}
|
||||
return new ParceledListSlice(result);
|
||||
} finally {
|
||||
@@ -2018,7 +1996,7 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
|
||||
private void dispatchAdjustVolumeLocked(String packageName, String opPackageName, int pid,
|
||||
int uid, boolean asSystemService, int suggestedStream, int direction, int flags) {
|
||||
MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
|
||||
MediaSessionRecordImpl session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
|
||||
: mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
|
||||
|
||||
boolean preferSuggestedStream = false;
|
||||
@@ -2109,7 +2087,13 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
|
||||
private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid,
|
||||
boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
|
||||
MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked();
|
||||
if (mCurrentFullUserRecord.getMediaButtonSessionLocked()
|
||||
instanceof MediaSession2Record) {
|
||||
// TODO(jaewan): Implement
|
||||
return;
|
||||
}
|
||||
MediaSessionRecord session =
|
||||
(MediaSessionRecord) mCurrentFullUserRecord.getMediaButtonSessionLocked();
|
||||
if (session != null) {
|
||||
if (DEBUG_KEY_EVENT) {
|
||||
Log.d(TAG, "Sending " + keyEvent + " to " + session);
|
||||
@@ -2389,15 +2373,19 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
}
|
||||
|
||||
final class MessageHandler extends Handler {
|
||||
private static final int MSG_SESSIONS_CHANGED = 1;
|
||||
private static final int MSG_VOLUME_INITIAL_DOWN = 2;
|
||||
private static final int MSG_SESSIONS_1_CHANGED = 1;
|
||||
private static final int MSG_SESSIONS_2_CHANGED = 2;
|
||||
private static final int MSG_VOLUME_INITIAL_DOWN = 3;
|
||||
private final SparseArray<Integer> mIntegerCache = new SparseArray<>();
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_SESSIONS_CHANGED:
|
||||
pushSessionsChanged((int) msg.obj);
|
||||
case MSG_SESSIONS_1_CHANGED:
|
||||
pushSession1Changed((int) msg.obj);
|
||||
break;
|
||||
case MSG_SESSIONS_2_CHANGED:
|
||||
pushSession2Changed((int) msg.obj);
|
||||
break;
|
||||
case MSG_VOLUME_INITIAL_DOWN:
|
||||
synchronized (mLock) {
|
||||
@@ -2412,41 +2400,19 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
}
|
||||
}
|
||||
|
||||
public void postSessionsChanged(int userId) {
|
||||
public void postSessionsChanged(MediaSessionRecordImpl record) {
|
||||
// Use object instead of the arguments when posting message to remove pending requests.
|
||||
Integer userIdInteger = mIntegerCache.get(userId);
|
||||
Integer userIdInteger = mIntegerCache.get(record.getUserId());
|
||||
if (userIdInteger == null) {
|
||||
userIdInteger = Integer.valueOf(userId);
|
||||
mIntegerCache.put(userId, userIdInteger);
|
||||
userIdInteger = Integer.valueOf(record.getUserId());
|
||||
mIntegerCache.put(record.getUserId(), userIdInteger);
|
||||
}
|
||||
removeMessages(MSG_SESSIONS_CHANGED, userIdInteger);
|
||||
obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget();
|
||||
|
||||
int msg = (record instanceof MediaSessionRecord)
|
||||
? MSG_SESSIONS_1_CHANGED : MSG_SESSIONS_2_CHANGED;
|
||||
removeMessages(msg, userIdInteger);
|
||||
obtainMessage(msg, userIdInteger).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
private class Controller2Callback extends MediaController2.ControllerCallback {
|
||||
private final Session2Token mToken;
|
||||
|
||||
Controller2Callback(Session2Token token) {
|
||||
mToken = token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected(MediaController2 controller, Session2CommandGroup allowedCommands) {
|
||||
synchronized (mLock) {
|
||||
int userId = UserHandle.getUserId(mToken.getUid());
|
||||
mSession2TokensPerUser.get(userId).add(mToken);
|
||||
pushSession2TokensChangedLocked(userId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnected(MediaController2 controller) {
|
||||
synchronized (mLock) {
|
||||
int userId = UserHandle.getUserId(mToken.getUid());
|
||||
mSession2TokensPerUser.get(userId).remove(mToken);
|
||||
pushSession2TokensChangedLocked(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
package com.android.server.media;
|
||||
|
||||
import android.media.Session2Token;
|
||||
import android.media.session.MediaSession;
|
||||
import android.media.session.PlaybackState;
|
||||
import android.os.Debug;
|
||||
import android.os.UserHandle;
|
||||
import android.util.IntArray;
|
||||
@@ -45,51 +45,30 @@ class MediaSessionStack {
|
||||
/**
|
||||
* Called when the media button session is changed.
|
||||
*/
|
||||
void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession,
|
||||
MediaSessionRecord newMediaButtonSession);
|
||||
void onMediaButtonSessionChanged(MediaSessionRecordImpl oldMediaButtonSession,
|
||||
MediaSessionRecordImpl newMediaButtonSession);
|
||||
}
|
||||
|
||||
/**
|
||||
* These are states that usually indicate the user took an action and should
|
||||
* bump priority regardless of the old state.
|
||||
* Sorted list of the media sessions
|
||||
*/
|
||||
private static final int[] ALWAYS_PRIORITY_STATES = {
|
||||
PlaybackState.STATE_FAST_FORWARDING,
|
||||
PlaybackState.STATE_REWINDING,
|
||||
PlaybackState.STATE_SKIPPING_TO_PREVIOUS,
|
||||
PlaybackState.STATE_SKIPPING_TO_NEXT };
|
||||
/**
|
||||
* These are states that usually indicate the user took an action if they
|
||||
* were entered from a non-priority state.
|
||||
*/
|
||||
private static final int[] TRANSITION_PRIORITY_STATES = {
|
||||
PlaybackState.STATE_BUFFERING,
|
||||
PlaybackState.STATE_CONNECTING,
|
||||
PlaybackState.STATE_PLAYING };
|
||||
|
||||
/**
|
||||
* Sorted list of the media sessions.
|
||||
* The session of which PlaybackState is changed to ALWAYS_PRIORITY_STATES or
|
||||
* TRANSITION_PRIORITY_STATES comes first.
|
||||
* @see #shouldUpdatePriority
|
||||
*/
|
||||
private final List<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
|
||||
private final List<MediaSessionRecordImpl> mSessions = new ArrayList<>();
|
||||
|
||||
private final AudioPlayerStateMonitor mAudioPlayerStateMonitor;
|
||||
private final OnMediaButtonSessionChangedListener mOnMediaButtonSessionChangedListener;
|
||||
|
||||
/**
|
||||
* The media button session which receives media key events.
|
||||
* It could be null if the previous media buttion session is released.
|
||||
* It could be null if the previous media button session is released.
|
||||
*/
|
||||
private MediaSessionRecord mMediaButtonSession;
|
||||
private MediaSessionRecordImpl mMediaButtonSession;
|
||||
|
||||
private MediaSessionRecord mCachedVolumeDefault;
|
||||
private MediaSessionRecordImpl mCachedVolumeDefault;
|
||||
|
||||
/**
|
||||
* Cache the result of the {@link #getActiveSessions} per user.
|
||||
*/
|
||||
private final SparseArray<ArrayList<MediaSessionRecord>> mCachedActiveLists =
|
||||
private final SparseArray<List<MediaSessionRecord>> mCachedActiveLists =
|
||||
new SparseArray<>();
|
||||
|
||||
MediaSessionStack(AudioPlayerStateMonitor monitor, OnMediaButtonSessionChangedListener listener) {
|
||||
@@ -102,7 +81,7 @@ class MediaSessionStack {
|
||||
*
|
||||
* @param record The record to add.
|
||||
*/
|
||||
public void addSession(MediaSessionRecord record) {
|
||||
public void addSession(MediaSessionRecordImpl record) {
|
||||
mSessions.add(record);
|
||||
clearCache(record.getUserId());
|
||||
|
||||
@@ -117,7 +96,7 @@ class MediaSessionStack {
|
||||
*
|
||||
* @param record The record to remove.
|
||||
*/
|
||||
public void removeSession(MediaSessionRecord record) {
|
||||
public void removeSession(MediaSessionRecordImpl record) {
|
||||
mSessions.remove(record);
|
||||
if (mMediaButtonSession == record) {
|
||||
// When the media button session is removed, nullify the media button session and do not
|
||||
@@ -131,7 +110,7 @@ class MediaSessionStack {
|
||||
/**
|
||||
* Return if the record exists in the priority tracker.
|
||||
*/
|
||||
public boolean contains(MediaSessionRecord record) {
|
||||
public boolean contains(MediaSessionRecordImpl record) {
|
||||
return mSessions.contains(record);
|
||||
}
|
||||
|
||||
@@ -142,9 +121,12 @@ class MediaSessionStack {
|
||||
* @return the MediaSessionRecord. Can be {@code null} if the session is gone meanwhile.
|
||||
*/
|
||||
public MediaSessionRecord getMediaSessionRecord(MediaSession.Token sessionToken) {
|
||||
for (MediaSessionRecord record : mSessions) {
|
||||
if (Objects.equals(record.getSessionToken(), sessionToken)) {
|
||||
return record;
|
||||
for (MediaSessionRecordImpl record : mSessions) {
|
||||
if (record instanceof MediaSessionRecord) {
|
||||
MediaSessionRecord session1 = (MediaSessionRecord) record;
|
||||
if (Objects.equals(session1.getSessionToken(), sessionToken)) {
|
||||
return session1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -154,15 +136,15 @@ class MediaSessionStack {
|
||||
* Notify the priority tracker that a session's playback state changed.
|
||||
*
|
||||
* @param record The record that changed.
|
||||
* @param oldState Its old playback state.
|
||||
* @param newState Its new playback state.
|
||||
* @param shouldUpdatePriority {@code true} if the record needs to prioritized
|
||||
*/
|
||||
public void onPlaystateChanged(MediaSessionRecord record, int oldState, int newState) {
|
||||
if (shouldUpdatePriority(oldState, newState)) {
|
||||
public void onPlaybackStateChanged(
|
||||
MediaSessionRecordImpl record, boolean shouldUpdatePriority) {
|
||||
if (shouldUpdatePriority) {
|
||||
mSessions.remove(record);
|
||||
mSessions.add(0, record);
|
||||
clearCache(record.getUserId());
|
||||
} else if (!MediaSession.isActiveState(newState)) {
|
||||
} else if (record.checkPlaybackActiveState(false)) {
|
||||
// Just clear the volume cache when a state goes inactive
|
||||
mCachedVolumeDefault = null;
|
||||
}
|
||||
@@ -172,7 +154,7 @@ class MediaSessionStack {
|
||||
// In that case, we pick the media session whose PlaybackState matches
|
||||
// the audio playback configuration.
|
||||
if (mMediaButtonSession != null && mMediaButtonSession.getUid() == record.getUid()) {
|
||||
MediaSessionRecord newMediaButtonSession =
|
||||
MediaSessionRecordImpl newMediaButtonSession =
|
||||
findMediaButtonSession(mMediaButtonSession.getUid());
|
||||
if (newMediaButtonSession != mMediaButtonSession) {
|
||||
updateMediaButtonSession(newMediaButtonSession);
|
||||
@@ -185,7 +167,7 @@ class MediaSessionStack {
|
||||
*
|
||||
* @param record The record that changed.
|
||||
*/
|
||||
public void onSessionStateChange(MediaSessionRecord record) {
|
||||
public void onSessionActiveStateChanged(MediaSessionRecordImpl record) {
|
||||
// For now just clear the cache. Eventually we'll selectively clear
|
||||
// depending on what changed.
|
||||
clearCache(record.getUserId());
|
||||
@@ -203,7 +185,7 @@ class MediaSessionStack {
|
||||
}
|
||||
IntArray audioPlaybackUids = mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids();
|
||||
for (int i = 0; i < audioPlaybackUids.size(); i++) {
|
||||
MediaSessionRecord mediaButtonSession =
|
||||
MediaSessionRecordImpl mediaButtonSession =
|
||||
findMediaButtonSession(audioPlaybackUids.get(i));
|
||||
if (mediaButtonSession != null) {
|
||||
// Found the media button session.
|
||||
@@ -225,9 +207,9 @@ class MediaSessionStack {
|
||||
* @return The media button session. Returns {@code null} if the app doesn't have a media
|
||||
* session.
|
||||
*/
|
||||
private MediaSessionRecord findMediaButtonSession(int uid) {
|
||||
MediaSessionRecord mediaButtonSession = null;
|
||||
for (MediaSessionRecord session : mSessions) {
|
||||
private MediaSessionRecordImpl findMediaButtonSession(int uid) {
|
||||
MediaSessionRecordImpl mediaButtonSession = null;
|
||||
for (MediaSessionRecordImpl session : mSessions) {
|
||||
if (uid == session.getUid()) {
|
||||
if (session.checkPlaybackActiveState(
|
||||
mAudioPlayerStateMonitor.isPlaybackActive(session.getUid()))) {
|
||||
@@ -253,8 +235,8 @@ class MediaSessionStack {
|
||||
* for all users in this {@link MediaSessionStack}.
|
||||
* @return All the active sessions in priority order.
|
||||
*/
|
||||
public ArrayList<MediaSessionRecord> getActiveSessions(int userId) {
|
||||
ArrayList<MediaSessionRecord> cachedActiveList = mCachedActiveLists.get(userId);
|
||||
public List<MediaSessionRecord> getActiveSessions(int userId) {
|
||||
List<MediaSessionRecord> cachedActiveList = mCachedActiveLists.get(userId);
|
||||
if (cachedActiveList == null) {
|
||||
cachedActiveList = getPriorityList(true, userId);
|
||||
mCachedActiveLists.put(userId, cachedActiveList);
|
||||
@@ -262,27 +244,47 @@ class MediaSessionStack {
|
||||
return cachedActiveList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the session2 tokens.
|
||||
*
|
||||
* @param userId The user to check. It can be {@link UserHandle#USER_ALL} to get all session2
|
||||
* tokens for all users in this {@link MediaSessionStack}.
|
||||
* @return All session2 tokens.
|
||||
*/
|
||||
public List<Session2Token> getSession2Tokens(int userId) {
|
||||
ArrayList<Session2Token> session2Records = new ArrayList<>();
|
||||
for (MediaSessionRecordImpl record : mSessions) {
|
||||
if ((userId == UserHandle.USER_ALL || record.getUserId() == userId)
|
||||
&& record.isActive()
|
||||
&& record instanceof MediaSession2Record) {
|
||||
MediaSession2Record session2 = (MediaSession2Record) record;
|
||||
session2Records.add(session2.getSession2Token());
|
||||
}
|
||||
}
|
||||
return session2Records;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the media button session which receives the media button events.
|
||||
*
|
||||
* @return The media button session or null.
|
||||
*/
|
||||
public MediaSessionRecord getMediaButtonSession() {
|
||||
public MediaSessionRecordImpl getMediaButtonSession() {
|
||||
return mMediaButtonSession;
|
||||
}
|
||||
|
||||
private void updateMediaButtonSession(MediaSessionRecord newMediaButtonSession) {
|
||||
MediaSessionRecord oldMediaButtonSession = mMediaButtonSession;
|
||||
private void updateMediaButtonSession(MediaSessionRecordImpl newMediaButtonSession) {
|
||||
MediaSessionRecordImpl oldMediaButtonSession = mMediaButtonSession;
|
||||
mMediaButtonSession = newMediaButtonSession;
|
||||
mOnMediaButtonSessionChangedListener.onMediaButtonSessionChanged(
|
||||
oldMediaButtonSession, newMediaButtonSession);
|
||||
}
|
||||
|
||||
public MediaSessionRecord getDefaultVolumeSession() {
|
||||
public MediaSessionRecordImpl getDefaultVolumeSession() {
|
||||
if (mCachedVolumeDefault != null) {
|
||||
return mCachedVolumeDefault;
|
||||
}
|
||||
ArrayList<MediaSessionRecord> records = getPriorityList(true, UserHandle.USER_ALL);
|
||||
List<MediaSessionRecord> records = getPriorityList(true, UserHandle.USER_ALL);
|
||||
int size = records.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
MediaSessionRecord record = records.get(i);
|
||||
@@ -294,13 +296,13 @@ class MediaSessionStack {
|
||||
return null;
|
||||
}
|
||||
|
||||
public MediaSessionRecord getDefaultRemoteSession(int userId) {
|
||||
ArrayList<MediaSessionRecord> records = getPriorityList(true, userId);
|
||||
public MediaSessionRecordImpl getDefaultRemoteSession(int userId) {
|
||||
List<MediaSessionRecord> records = getPriorityList(true, userId);
|
||||
|
||||
int size = records.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
MediaSessionRecord record = records.get(i);
|
||||
if (!record.isPlaybackLocal()) {
|
||||
if (!record.isPlaybackTypeLocal()) {
|
||||
return record;
|
||||
}
|
||||
}
|
||||
@@ -308,16 +310,11 @@ class MediaSessionStack {
|
||||
}
|
||||
|
||||
public void dump(PrintWriter pw, String prefix) {
|
||||
ArrayList<MediaSessionRecord> sortedSessions = getPriorityList(false,
|
||||
UserHandle.USER_ALL);
|
||||
int count = sortedSessions.size();
|
||||
pw.println(prefix + "Media button session is " + mMediaButtonSession);
|
||||
pw.println(prefix + "Sessions Stack - have " + count + " sessions:");
|
||||
pw.println(prefix + "Sessions Stack - have " + mSessions.size() + " sessions:");
|
||||
String indent = prefix + " ";
|
||||
for (int i = 0; i < count; i++) {
|
||||
MediaSessionRecord record = sortedSessions.get(i);
|
||||
for (MediaSessionRecordImpl record : mSessions) {
|
||||
record.dump(pw, indent);
|
||||
pw.println();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,17 +332,19 @@ class MediaSessionStack {
|
||||
* will return sessions for all users.
|
||||
* @return The priority sorted list of sessions.
|
||||
*/
|
||||
public ArrayList<MediaSessionRecord> getPriorityList(boolean activeOnly, int userId) {
|
||||
ArrayList<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>();
|
||||
public List<MediaSessionRecord> getPriorityList(boolean activeOnly, int userId) {
|
||||
List<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>();
|
||||
int lastPlaybackActiveIndex = 0;
|
||||
int lastActiveIndex = 0;
|
||||
|
||||
int size = mSessions.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
final MediaSessionRecord session = mSessions.get(i);
|
||||
for (MediaSessionRecordImpl record : mSessions) {
|
||||
if (!(record instanceof MediaSessionRecord)) {
|
||||
continue;
|
||||
}
|
||||
final MediaSessionRecord session = (MediaSessionRecord) record;
|
||||
|
||||
if (userId != UserHandle.USER_ALL && userId != session.getUserId()) {
|
||||
// Filter out sessions for the wrong user
|
||||
if ((userId != UserHandle.USER_ALL && userId != session.getUserId())) {
|
||||
// Filter out sessions for the wrong user or session2.
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -369,26 +368,6 @@ class MediaSessionStack {
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean shouldUpdatePriority(int oldState, int newState) {
|
||||
if (containsState(newState, ALWAYS_PRIORITY_STATES)) {
|
||||
return true;
|
||||
}
|
||||
if (!containsState(oldState, TRANSITION_PRIORITY_STATES)
|
||||
&& containsState(newState, TRANSITION_PRIORITY_STATES)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean containsState(int state, int[] states) {
|
||||
for (int i = 0; i < states.length; i++) {
|
||||
if (states[i] == state) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void clearCache(int userId) {
|
||||
mCachedVolumeDefault = null;
|
||||
mCachedActiveLists.remove(userId);
|
||||
|
||||
Reference in New Issue
Block a user