Loading android/app/src/com/android/bluetooth/avrcp/Avrcp.java +144 −158 Original line number Diff line number Diff line Loading @@ -28,9 +28,10 @@ import android.content.SharedPreferences; import android.graphics.Bitmap; import android.media.AudioManager; import android.media.MediaMetadataRetriever; import android.media.RemoteControlClient; import android.media.RemoteController; import android.media.RemoteController.MetadataEditor; import android.media.MediaMetadata; import android.media.session.MediaController; import android.media.session.MediaSessionManager; import android.media.session.PlaybackState; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; Loading Loading @@ -70,11 +71,13 @@ public final class Avrcp { private Context mContext; private final AudioManager mAudioManager; private AvrcpMessageHandler mHandler; private RemoteController mRemoteController; private RemoteControllerWeak mRemoteControllerCb; private MediaSessionManager mMediaSessionManager; private MediaSessionChangeListener mSessionChangeListener; private MediaController mMediaController; private MediaControllerListener mMediaControllerCb; private Metadata mMetadata; private int mTransportControlFlags; private int mCurrentPlayState; private PlaybackState mCurrentPlayState; private int mPlayStatusChangedNT; private int mTrackChangedNT; private long mTrackNumber; Loading Loading @@ -134,10 +137,6 @@ public final class Avrcp { private static final int MESSAGE_REWIND = 11; private static final int MESSAGE_CHANGE_PLAY_POS = 12; private static final int MESSAGE_SET_A2DP_AUDIO_STATE = 13; private static final int MSG_UPDATE_STATE = 100; private static final int MSG_SET_METADATA = 101; private static final int MSG_SET_TRANSPORT_CONTROLS = 102; private static final int MSG_SET_GENERATION_ID = 104; private static final int BUTTON_TIMEOUT_TIME = 2000; private static final int BASE_SKIP_AMOUNT = 2000; Loading @@ -157,11 +156,11 @@ public final class Avrcp { private Avrcp(Context context) { mMetadata = new Metadata(); mCurrentPlayState = RemoteControlClient.PLAYSTATE_NONE; // until we get a callback mCurrentPlayState = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE, -1L, 0.0f).build(); mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED; mTrackChangedNT = NOTIFICATION_TYPE_CHANGED; mTrackNumber = -1L; mCurrentPosMs = RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN; mCurrentPosMs = PlaybackState.PLAYBACK_POSITION_UNKNOWN; mPlayStartTimeMs = -1L; mSongLengthMs = 0L; mPlaybackIntervalMs = 0L; Loading @@ -183,6 +182,7 @@ public final class Avrcp { initNative(); mMediaSessionManager = (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE); mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mAudioStreamMax = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); mVolumeStep = Math.max(AVRCP_BASE_VOLUME_STEP, AVRCP_MAX_VOL/mAudioStreamMax); Loading @@ -197,10 +197,14 @@ public final class Avrcp { thread.start(); Looper looper = thread.getLooper(); mHandler = new AvrcpMessageHandler(looper); mRemoteControllerCb = new RemoteControllerWeak(mHandler); mRemoteController = new RemoteController(mContext, mRemoteControllerCb); mAudioManager.registerRemoteController(mRemoteController); mRemoteController.setSynchronizationMode(RemoteController.POSITION_SYNCHRONIZATION_CHECK); mSessionChangeListener = new MediaSessionChangeListener(); mMediaSessionManager.addOnActiveSessionsChangedListener(mSessionChangeListener, null, mHandler); List<MediaController> sessions = mMediaSessionManager.getActiveSessions(null); mMediaControllerCb = new MediaControllerListener(); if (sessions.size() > 0) { updateCurrentMediaController(sessions.get(0)); } } public static Avrcp make(Context context) { Loading @@ -216,7 +220,7 @@ public final class Avrcp { if (looper != null) { looper.quit(); } mAudioManager.unregisterRemoteController(mRemoteController); mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionChangeListener); } public void cleanup() { Loading @@ -225,61 +229,52 @@ public final class Avrcp { mVolumeMapping.clear(); } private static class RemoteControllerWeak implements RemoteController.OnClientUpdateListener { private final WeakReference<Handler> mLocalHandler; public RemoteControllerWeak(Handler handler) { mLocalHandler = new WeakReference<Handler>(handler); private class MediaControllerListener extends MediaController.Callback { @Override public void onMetadataChanged(MediaMetadata metadata) { Log.v(TAG, "MediaController metadata changed"); updateMetadata(metadata); } @Override public void onClientChange(boolean clearing) { Handler handler = mLocalHandler.get(); if (handler != null) { handler.obtainMessage(MSG_SET_GENERATION_ID, 0, (clearing ? 1 : 0), null).sendToTarget(); } public void onPlaybackStateChanged(PlaybackState state) { Log.v(TAG, "MediaController playback changed: " + state.toString()); updatePlayPauseState(state); } @Override public void onClientPlaybackStateUpdate(int state) { // Should never be called with the existing code, but just in case if (DEBUG) Log.v(TAG, "RemoteControlDisplayer: Update playbackState state=" + state + " position=null"); Handler handler = mLocalHandler.get(); if (handler != null) { handler.obtainMessage(MSG_UPDATE_STATE, 0, state, new Long(RemoteControlClient.PLAYBACK_POSITION_INVALID)).sendToTarget(); public void onSessionDestroyed() { Log.v(TAG, "MediaController session destroyed"); } } @Override public void onClientPlaybackStateUpdate(int state, long stateChangeTimeMs, long currentPosMs, float speed) { if (DEBUG) Log.v(TAG, "RemoteControlDisplayer: Update playbackState state=" + state + " position=" + currentPosMs); Handler handler = mLocalHandler.get(); if (handler != null) { handler.obtainMessage(MSG_UPDATE_STATE, 0, state, new Long(currentPosMs)).sendToTarget(); } private class MediaSessionChangeListener implements MediaSessionManager.OnActiveSessionsChangedListener { public MediaSessionChangeListener() { } @Override public void onClientTransportControlUpdate(int transportControlFlags) { Handler handler = mLocalHandler.get(); if (handler != null) { handler.obtainMessage(MSG_SET_TRANSPORT_CONTROLS, 0, transportControlFlags) .sendToTarget(); public void onActiveSessionsChanged(List<MediaController> controllers) { Log.v(TAG, "Active sessions changed, " + controllers.size() + " sessions"); if (controllers.size() > 0) { updateCurrentMediaController(controllers.get(0)); } } } @Override public void onClientMetadataUpdate(MetadataEditor metadataEditor) { Log.v(TAG, "RemoteControlDisplayer: Update Metadata"); Handler handler = mLocalHandler.get(); if (handler != null) { handler.obtainMessage(MSG_SET_METADATA, 0, 0, metadataEditor).sendToTarget(); private void updateCurrentMediaController(MediaController controller) { Log.v(TAG, "Updating media controller to " + controller); if (mMediaController != null) { mMediaController.unregisterCallback(mMediaControllerCb); } mMediaController = controller; if (mMediaController == null) { updateMetadata(null); updatePlayPauseState(null); return; } mMediaController.registerCallback(mMediaControllerCb, mHandler); updateMetadata(mMediaController.getMetadata()); updatePlayPauseState(mMediaController.getPlaybackState()); } /** Handles Avrcp messages. */ Loading @@ -291,22 +286,6 @@ public final class Avrcp { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_UPDATE_STATE: updatePlayPauseState(msg.arg2, ((Long) msg.obj).longValue()); break; case MSG_SET_METADATA: updateMetadata((MetadataEditor) msg.obj); break; case MSG_SET_TRANSPORT_CONTROLS: updateTransportControls(msg.arg2); break; case MSG_SET_GENERATION_ID: Log.v(TAG, "New genId = " + msg.arg1 + ", clearing = " + msg.arg2); break; case MESSAGE_GET_RC_FEATURES: String address = (String) msg.obj; if (DEBUG) Log.v(TAG, "MESSAGE_GET_RC_FEATURES: address="+address+ Loading Loading @@ -395,8 +374,7 @@ public final class Avrcp { } } if (mLocalVolume != volIndex && (msg.arg2 == AVRC_RSP_ACCEPT || if (mLocalVolume != volIndex && (msg.arg2 == AVRC_RSP_ACCEPT || msg.arg2 == AVRC_RSP_CHANGED || msg.arg2 == AVRC_RSP_INTERIM)) { /* If the volume has successfully changed */ Loading Loading @@ -567,22 +545,22 @@ public final class Avrcp { case MESSAGE_FAST_FORWARD: case MESSAGE_REWIND: if (msg.what == MESSAGE_FAST_FORWARD) { if((mTransportControlFlags & RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD) != 0) { if ((mCurrentPlayState.getActions() & PlaybackState.ACTION_FAST_FORWARD) != 0) { int keyState = msg.arg1 == KEY_STATE_PRESS ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP; KeyEvent keyEvent = new KeyEvent(keyState, KeyEvent.KEYCODE_MEDIA_FAST_FORWARD); mRemoteController.sendMediaKeyEvent(keyEvent); mMediaController.dispatchMediaButtonEvent(keyEvent); break; } } else if((mTransportControlFlags & RemoteControlClient.FLAG_KEY_MEDIA_REWIND) != 0) { } else if ((mCurrentPlayState.getActions() & PlaybackState.ACTION_REWIND) != 0) { int keyState = msg.arg1 == KEY_STATE_PRESS ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP; KeyEvent keyEvent = new KeyEvent(keyState, KeyEvent.KEYCODE_MEDIA_REWIND); mRemoteController.sendMediaKeyEvent(keyEvent); mMediaController.dispatchMediaButtonEvent(keyEvent); break; } Loading Loading @@ -640,40 +618,53 @@ public final class Avrcp { boolean isPlaying = (state == BluetoothA2dp.STATE_PLAYING); if (isPlaying != isPlayingState(mCurrentPlayState)) { /* if a2dp is streaming, check to make sure music is active */ if ( (isPlaying) && !mAudioManager.isMusicActive()) if (isPlaying && !mAudioManager.isMusicActive()) return; updatePlayPauseState(isPlaying ? RemoteControlClient.PLAYSTATE_PLAYING : RemoteControlClient.PLAYSTATE_PAUSED, RemoteControlClient.PLAYBACK_POSITION_INVALID); PlaybackState.Builder builder = new PlaybackState.Builder(); if (isPlaying) { builder.setState(PlaybackState.STATE_PLAYING, PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f); } else { builder.setState(PlaybackState.STATE_PAUSED, PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f); } updatePlayPauseState(builder.build()); } } private void updatePlayPauseState(int state, long currentPosMs) { private void updatePlayPauseState(PlaybackState state) { if (DEBUG) Log.v(TAG, "updatePlayPauseState, old=" + mCurrentPlayState + ", state=" + state); boolean oldPosValid = (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN); "updatePlayPauseState: old=" + mCurrentPlayState + ", state=" + state); if (state == null) { state = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE, PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f).build(); } boolean oldPosValid = (mCurrentPosMs != PlaybackState.PLAYBACK_POSITION_UNKNOWN); int oldPlayStatus = convertPlayStateToPlayStatus(mCurrentPlayState); int newPlayStatus = convertPlayStateToPlayStatus(state); if ((mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) && if ((mCurrentPlayState.getState() == PlaybackState.STATE_PLAYING) && (mCurrentPlayState != state) && oldPosValid) { mCurrentPosMs = getPlayPosition(); } if (currentPosMs != RemoteControlClient.PLAYBACK_POSITION_INVALID) { mCurrentPosMs = currentPosMs; if (state.getState() == PlaybackState.STATE_NONE || state.getState() == PlaybackState.STATE_ERROR) { mCurrentPosMs = PlaybackState.PLAYBACK_POSITION_UNKNOWN; } else { mCurrentPosMs = state.getPosition(); } if ((state == RemoteControlClient.PLAYSTATE_PLAYING) && ((currentPosMs != RemoteControlClient.PLAYBACK_POSITION_INVALID) || (mCurrentPlayState != RemoteControlClient.PLAYSTATE_PLAYING))) { if ((state.getState() == PlaybackState.STATE_PLAYING) && (mCurrentPlayState.getState() != PlaybackState.STATE_PLAYING)) { mPlayStartTimeMs = SystemClock.elapsedRealtime(); } mCurrentPlayState = state; boolean newPosValid = (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN); boolean newPosValid = mCurrentPosMs != PlaybackState.PLAYBACK_POSITION_UNKNOWN; long playPosition = getPlayPosition(); mHandler.removeMessages(MESSAGE_PLAY_INTERVAL_TIMEOUT); /* need send play position changed notification when play status is changed */ if ((mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) && Loading @@ -683,7 +674,7 @@ public final class Avrcp { registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)playPosition); } if ((mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) && newPosValid && (state == RemoteControlClient.PLAYSTATE_PLAYING)) { (state.getState() == PlaybackState.STATE_PLAYING)) { Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT); mHandler.sendMessageDelayed(msg, mNextPosMs - playPosition); } Loading Loading @@ -715,11 +706,17 @@ public final class Avrcp { } } private void updateMetadata(MetadataEditor data) { private void updateMetadata(MediaMetadata data) { String oldMetadata = mMetadata.toString(); mMetadata.artist = data.getString(MediaMetadataRetriever.METADATA_KEY_ARTIST, null); mMetadata.trackTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_TITLE, null); mMetadata.albumTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_ALBUM, null); if (data == null) { mMetadata = new Metadata(); mSongLengthMs = 0L; } else { mMetadata.artist = data.getString(MediaMetadata.METADATA_KEY_ARTIST); mMetadata.trackTitle = data.getString(MediaMetadata.METADATA_KEY_TITLE); mMetadata.albumTitle = data.getString(MediaMetadata.METADATA_KEY_ALBUM); mSongLengthMs = data.getLong(MediaMetadata.METADATA_KEY_DURATION); } if (!oldMetadata.equals(mMetadata.toString())) { Log.v(TAG, "Metadata Changed to " + mMetadata.toString()); mTrackNumber++; Loading @@ -728,11 +725,10 @@ public final class Avrcp { sendTrackChangedRsp(); } if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) { if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) { if (mCurrentPosMs != PlaybackState.PLAYBACK_POSITION_UNKNOWN && isPlayingState(mCurrentPlayState)) { mPlayStartTimeMs = SystemClock.elapsedRealtime(); } } /* need send play position changed notification when track is changed */ if (mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) { mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED; Loading @@ -744,8 +740,6 @@ public final class Avrcp { Log.v(TAG, "Metadata updated but no change!"); } mSongLengthMs = data.getLong(MediaMetadataRetriever.METADATA_KEY_DURATION, RemoteControlClient.PLAYBACK_POSITION_INVALID); } private void getRcFeatures(byte[] address, int features) { Loading Loading @@ -791,10 +785,10 @@ public final class Avrcp { long songPosition = getPlayPosition(); mPlayPosChangedNT = NOTIFICATION_TYPE_INTERIM; mPlaybackIntervalMs = (long)param * 1000L; if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) { if (mCurrentPosMs != PlaybackState.PLAYBACK_POSITION_UNKNOWN) { mNextPosMs = songPosition + mPlaybackIntervalMs; mPrevPosMs = songPosition - mPlaybackIntervalMs; if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) { if (isPlayingState(mCurrentPlayState)) { Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT); mHandler.sendMessageDelayed(msg, mPlaybackIntervalMs); } Loading Loading @@ -830,7 +824,7 @@ public final class Avrcp { long currentPosMs = getPlayPosition(); if (currentPosMs == -1L) return; long newPosMs = Math.max(0L, currentPosMs + amount); mRemoteController.seekTo(newPosMs); mMediaController.getTransportControls().seekTo(newPosMs); } private int getSkipMultiplier() { Loading @@ -846,7 +840,7 @@ public final class Avrcp { 0xFFFFFFFFFFFFFFFF in the interim response */ long trackNumberRsp = -1L; if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) { if (isPlayingState(mCurrentPlayState)) { trackNumberRsp = mTrackNumber; } Loading @@ -859,8 +853,8 @@ public final class Avrcp { private long getPlayPosition() { long songPosition = -1L; if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) { if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) { if (mCurrentPosMs != PlaybackState.PLAYBACK_POSITION_UNKNOWN) { if (mCurrentPlayState.getState() == PlaybackState.STATE_PLAYING) { songPosition = SystemClock.elapsedRealtime() - mPlayStartTimeMs + mCurrentPosMs; } else { Loading Loading @@ -900,34 +894,35 @@ public final class Avrcp { return attrStr; } private int convertPlayStateToPlayStatus(int playState) { private int convertPlayStateToPlayStatus(PlaybackState state) { int playStatus = PLAYSTATUS_ERROR; switch (playState) { case RemoteControlClient.PLAYSTATE_PLAYING: case RemoteControlClient.PLAYSTATE_BUFFERING: switch (state.getState()) { case PlaybackState.STATE_PLAYING: case PlaybackState.STATE_BUFFERING: playStatus = PLAYSTATUS_PLAYING; break; case RemoteControlClient.PLAYSTATE_STOPPED: case RemoteControlClient.PLAYSTATE_NONE: case PlaybackState.STATE_STOPPED: case PlaybackState.STATE_NONE: playStatus = PLAYSTATUS_STOPPED; break; case RemoteControlClient.PLAYSTATE_PAUSED: case PlaybackState.STATE_PAUSED: playStatus = PLAYSTATUS_PAUSED; break; case RemoteControlClient.PLAYSTATE_FAST_FORWARDING: case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS: case PlaybackState.STATE_FAST_FORWARDING: case PlaybackState.STATE_SKIPPING_TO_NEXT: case PlaybackState.STATE_SKIPPING_TO_QUEUE_ITEM: playStatus = PLAYSTATUS_FWD_SEEK; break; case RemoteControlClient.PLAYSTATE_REWINDING: case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS: case PlaybackState.STATE_REWINDING: case PlaybackState.STATE_SKIPPING_TO_PREVIOUS: playStatus = PLAYSTATUS_REV_SEEK; break; case RemoteControlClient.PLAYSTATE_ERROR: case PlaybackState.STATE_ERROR: playStatus = PLAYSTATUS_ERROR; break; Loading @@ -935,18 +930,9 @@ public final class Avrcp { return playStatus; } private boolean isPlayingState(int playState) { boolean isPlaying = false; switch (playState) { case RemoteControlClient.PLAYSTATE_PLAYING: case RemoteControlClient.PLAYSTATE_BUFFERING: isPlaying = true; break; default: isPlaying = false; break; } return isPlaying; private boolean isPlayingState(PlaybackState state) { return (state.getState() == PlaybackState.STATE_PLAYING) || (state.getState() == PlaybackState.STATE_BUFFERING); } /** Loading Loading
android/app/src/com/android/bluetooth/avrcp/Avrcp.java +144 −158 Original line number Diff line number Diff line Loading @@ -28,9 +28,10 @@ import android.content.SharedPreferences; import android.graphics.Bitmap; import android.media.AudioManager; import android.media.MediaMetadataRetriever; import android.media.RemoteControlClient; import android.media.RemoteController; import android.media.RemoteController.MetadataEditor; import android.media.MediaMetadata; import android.media.session.MediaController; import android.media.session.MediaSessionManager; import android.media.session.PlaybackState; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; Loading Loading @@ -70,11 +71,13 @@ public final class Avrcp { private Context mContext; private final AudioManager mAudioManager; private AvrcpMessageHandler mHandler; private RemoteController mRemoteController; private RemoteControllerWeak mRemoteControllerCb; private MediaSessionManager mMediaSessionManager; private MediaSessionChangeListener mSessionChangeListener; private MediaController mMediaController; private MediaControllerListener mMediaControllerCb; private Metadata mMetadata; private int mTransportControlFlags; private int mCurrentPlayState; private PlaybackState mCurrentPlayState; private int mPlayStatusChangedNT; private int mTrackChangedNT; private long mTrackNumber; Loading Loading @@ -134,10 +137,6 @@ public final class Avrcp { private static final int MESSAGE_REWIND = 11; private static final int MESSAGE_CHANGE_PLAY_POS = 12; private static final int MESSAGE_SET_A2DP_AUDIO_STATE = 13; private static final int MSG_UPDATE_STATE = 100; private static final int MSG_SET_METADATA = 101; private static final int MSG_SET_TRANSPORT_CONTROLS = 102; private static final int MSG_SET_GENERATION_ID = 104; private static final int BUTTON_TIMEOUT_TIME = 2000; private static final int BASE_SKIP_AMOUNT = 2000; Loading @@ -157,11 +156,11 @@ public final class Avrcp { private Avrcp(Context context) { mMetadata = new Metadata(); mCurrentPlayState = RemoteControlClient.PLAYSTATE_NONE; // until we get a callback mCurrentPlayState = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE, -1L, 0.0f).build(); mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED; mTrackChangedNT = NOTIFICATION_TYPE_CHANGED; mTrackNumber = -1L; mCurrentPosMs = RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN; mCurrentPosMs = PlaybackState.PLAYBACK_POSITION_UNKNOWN; mPlayStartTimeMs = -1L; mSongLengthMs = 0L; mPlaybackIntervalMs = 0L; Loading @@ -183,6 +182,7 @@ public final class Avrcp { initNative(); mMediaSessionManager = (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE); mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mAudioStreamMax = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); mVolumeStep = Math.max(AVRCP_BASE_VOLUME_STEP, AVRCP_MAX_VOL/mAudioStreamMax); Loading @@ -197,10 +197,14 @@ public final class Avrcp { thread.start(); Looper looper = thread.getLooper(); mHandler = new AvrcpMessageHandler(looper); mRemoteControllerCb = new RemoteControllerWeak(mHandler); mRemoteController = new RemoteController(mContext, mRemoteControllerCb); mAudioManager.registerRemoteController(mRemoteController); mRemoteController.setSynchronizationMode(RemoteController.POSITION_SYNCHRONIZATION_CHECK); mSessionChangeListener = new MediaSessionChangeListener(); mMediaSessionManager.addOnActiveSessionsChangedListener(mSessionChangeListener, null, mHandler); List<MediaController> sessions = mMediaSessionManager.getActiveSessions(null); mMediaControllerCb = new MediaControllerListener(); if (sessions.size() > 0) { updateCurrentMediaController(sessions.get(0)); } } public static Avrcp make(Context context) { Loading @@ -216,7 +220,7 @@ public final class Avrcp { if (looper != null) { looper.quit(); } mAudioManager.unregisterRemoteController(mRemoteController); mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionChangeListener); } public void cleanup() { Loading @@ -225,61 +229,52 @@ public final class Avrcp { mVolumeMapping.clear(); } private static class RemoteControllerWeak implements RemoteController.OnClientUpdateListener { private final WeakReference<Handler> mLocalHandler; public RemoteControllerWeak(Handler handler) { mLocalHandler = new WeakReference<Handler>(handler); private class MediaControllerListener extends MediaController.Callback { @Override public void onMetadataChanged(MediaMetadata metadata) { Log.v(TAG, "MediaController metadata changed"); updateMetadata(metadata); } @Override public void onClientChange(boolean clearing) { Handler handler = mLocalHandler.get(); if (handler != null) { handler.obtainMessage(MSG_SET_GENERATION_ID, 0, (clearing ? 1 : 0), null).sendToTarget(); } public void onPlaybackStateChanged(PlaybackState state) { Log.v(TAG, "MediaController playback changed: " + state.toString()); updatePlayPauseState(state); } @Override public void onClientPlaybackStateUpdate(int state) { // Should never be called with the existing code, but just in case if (DEBUG) Log.v(TAG, "RemoteControlDisplayer: Update playbackState state=" + state + " position=null"); Handler handler = mLocalHandler.get(); if (handler != null) { handler.obtainMessage(MSG_UPDATE_STATE, 0, state, new Long(RemoteControlClient.PLAYBACK_POSITION_INVALID)).sendToTarget(); public void onSessionDestroyed() { Log.v(TAG, "MediaController session destroyed"); } } @Override public void onClientPlaybackStateUpdate(int state, long stateChangeTimeMs, long currentPosMs, float speed) { if (DEBUG) Log.v(TAG, "RemoteControlDisplayer: Update playbackState state=" + state + " position=" + currentPosMs); Handler handler = mLocalHandler.get(); if (handler != null) { handler.obtainMessage(MSG_UPDATE_STATE, 0, state, new Long(currentPosMs)).sendToTarget(); } private class MediaSessionChangeListener implements MediaSessionManager.OnActiveSessionsChangedListener { public MediaSessionChangeListener() { } @Override public void onClientTransportControlUpdate(int transportControlFlags) { Handler handler = mLocalHandler.get(); if (handler != null) { handler.obtainMessage(MSG_SET_TRANSPORT_CONTROLS, 0, transportControlFlags) .sendToTarget(); public void onActiveSessionsChanged(List<MediaController> controllers) { Log.v(TAG, "Active sessions changed, " + controllers.size() + " sessions"); if (controllers.size() > 0) { updateCurrentMediaController(controllers.get(0)); } } } @Override public void onClientMetadataUpdate(MetadataEditor metadataEditor) { Log.v(TAG, "RemoteControlDisplayer: Update Metadata"); Handler handler = mLocalHandler.get(); if (handler != null) { handler.obtainMessage(MSG_SET_METADATA, 0, 0, metadataEditor).sendToTarget(); private void updateCurrentMediaController(MediaController controller) { Log.v(TAG, "Updating media controller to " + controller); if (mMediaController != null) { mMediaController.unregisterCallback(mMediaControllerCb); } mMediaController = controller; if (mMediaController == null) { updateMetadata(null); updatePlayPauseState(null); return; } mMediaController.registerCallback(mMediaControllerCb, mHandler); updateMetadata(mMediaController.getMetadata()); updatePlayPauseState(mMediaController.getPlaybackState()); } /** Handles Avrcp messages. */ Loading @@ -291,22 +286,6 @@ public final class Avrcp { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_UPDATE_STATE: updatePlayPauseState(msg.arg2, ((Long) msg.obj).longValue()); break; case MSG_SET_METADATA: updateMetadata((MetadataEditor) msg.obj); break; case MSG_SET_TRANSPORT_CONTROLS: updateTransportControls(msg.arg2); break; case MSG_SET_GENERATION_ID: Log.v(TAG, "New genId = " + msg.arg1 + ", clearing = " + msg.arg2); break; case MESSAGE_GET_RC_FEATURES: String address = (String) msg.obj; if (DEBUG) Log.v(TAG, "MESSAGE_GET_RC_FEATURES: address="+address+ Loading Loading @@ -395,8 +374,7 @@ public final class Avrcp { } } if (mLocalVolume != volIndex && (msg.arg2 == AVRC_RSP_ACCEPT || if (mLocalVolume != volIndex && (msg.arg2 == AVRC_RSP_ACCEPT || msg.arg2 == AVRC_RSP_CHANGED || msg.arg2 == AVRC_RSP_INTERIM)) { /* If the volume has successfully changed */ Loading Loading @@ -567,22 +545,22 @@ public final class Avrcp { case MESSAGE_FAST_FORWARD: case MESSAGE_REWIND: if (msg.what == MESSAGE_FAST_FORWARD) { if((mTransportControlFlags & RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD) != 0) { if ((mCurrentPlayState.getActions() & PlaybackState.ACTION_FAST_FORWARD) != 0) { int keyState = msg.arg1 == KEY_STATE_PRESS ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP; KeyEvent keyEvent = new KeyEvent(keyState, KeyEvent.KEYCODE_MEDIA_FAST_FORWARD); mRemoteController.sendMediaKeyEvent(keyEvent); mMediaController.dispatchMediaButtonEvent(keyEvent); break; } } else if((mTransportControlFlags & RemoteControlClient.FLAG_KEY_MEDIA_REWIND) != 0) { } else if ((mCurrentPlayState.getActions() & PlaybackState.ACTION_REWIND) != 0) { int keyState = msg.arg1 == KEY_STATE_PRESS ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP; KeyEvent keyEvent = new KeyEvent(keyState, KeyEvent.KEYCODE_MEDIA_REWIND); mRemoteController.sendMediaKeyEvent(keyEvent); mMediaController.dispatchMediaButtonEvent(keyEvent); break; } Loading Loading @@ -640,40 +618,53 @@ public final class Avrcp { boolean isPlaying = (state == BluetoothA2dp.STATE_PLAYING); if (isPlaying != isPlayingState(mCurrentPlayState)) { /* if a2dp is streaming, check to make sure music is active */ if ( (isPlaying) && !mAudioManager.isMusicActive()) if (isPlaying && !mAudioManager.isMusicActive()) return; updatePlayPauseState(isPlaying ? RemoteControlClient.PLAYSTATE_PLAYING : RemoteControlClient.PLAYSTATE_PAUSED, RemoteControlClient.PLAYBACK_POSITION_INVALID); PlaybackState.Builder builder = new PlaybackState.Builder(); if (isPlaying) { builder.setState(PlaybackState.STATE_PLAYING, PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f); } else { builder.setState(PlaybackState.STATE_PAUSED, PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f); } updatePlayPauseState(builder.build()); } } private void updatePlayPauseState(int state, long currentPosMs) { private void updatePlayPauseState(PlaybackState state) { if (DEBUG) Log.v(TAG, "updatePlayPauseState, old=" + mCurrentPlayState + ", state=" + state); boolean oldPosValid = (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN); "updatePlayPauseState: old=" + mCurrentPlayState + ", state=" + state); if (state == null) { state = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE, PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f).build(); } boolean oldPosValid = (mCurrentPosMs != PlaybackState.PLAYBACK_POSITION_UNKNOWN); int oldPlayStatus = convertPlayStateToPlayStatus(mCurrentPlayState); int newPlayStatus = convertPlayStateToPlayStatus(state); if ((mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) && if ((mCurrentPlayState.getState() == PlaybackState.STATE_PLAYING) && (mCurrentPlayState != state) && oldPosValid) { mCurrentPosMs = getPlayPosition(); } if (currentPosMs != RemoteControlClient.PLAYBACK_POSITION_INVALID) { mCurrentPosMs = currentPosMs; if (state.getState() == PlaybackState.STATE_NONE || state.getState() == PlaybackState.STATE_ERROR) { mCurrentPosMs = PlaybackState.PLAYBACK_POSITION_UNKNOWN; } else { mCurrentPosMs = state.getPosition(); } if ((state == RemoteControlClient.PLAYSTATE_PLAYING) && ((currentPosMs != RemoteControlClient.PLAYBACK_POSITION_INVALID) || (mCurrentPlayState != RemoteControlClient.PLAYSTATE_PLAYING))) { if ((state.getState() == PlaybackState.STATE_PLAYING) && (mCurrentPlayState.getState() != PlaybackState.STATE_PLAYING)) { mPlayStartTimeMs = SystemClock.elapsedRealtime(); } mCurrentPlayState = state; boolean newPosValid = (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN); boolean newPosValid = mCurrentPosMs != PlaybackState.PLAYBACK_POSITION_UNKNOWN; long playPosition = getPlayPosition(); mHandler.removeMessages(MESSAGE_PLAY_INTERVAL_TIMEOUT); /* need send play position changed notification when play status is changed */ if ((mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) && Loading @@ -683,7 +674,7 @@ public final class Avrcp { registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)playPosition); } if ((mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) && newPosValid && (state == RemoteControlClient.PLAYSTATE_PLAYING)) { (state.getState() == PlaybackState.STATE_PLAYING)) { Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT); mHandler.sendMessageDelayed(msg, mNextPosMs - playPosition); } Loading Loading @@ -715,11 +706,17 @@ public final class Avrcp { } } private void updateMetadata(MetadataEditor data) { private void updateMetadata(MediaMetadata data) { String oldMetadata = mMetadata.toString(); mMetadata.artist = data.getString(MediaMetadataRetriever.METADATA_KEY_ARTIST, null); mMetadata.trackTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_TITLE, null); mMetadata.albumTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_ALBUM, null); if (data == null) { mMetadata = new Metadata(); mSongLengthMs = 0L; } else { mMetadata.artist = data.getString(MediaMetadata.METADATA_KEY_ARTIST); mMetadata.trackTitle = data.getString(MediaMetadata.METADATA_KEY_TITLE); mMetadata.albumTitle = data.getString(MediaMetadata.METADATA_KEY_ALBUM); mSongLengthMs = data.getLong(MediaMetadata.METADATA_KEY_DURATION); } if (!oldMetadata.equals(mMetadata.toString())) { Log.v(TAG, "Metadata Changed to " + mMetadata.toString()); mTrackNumber++; Loading @@ -728,11 +725,10 @@ public final class Avrcp { sendTrackChangedRsp(); } if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) { if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) { if (mCurrentPosMs != PlaybackState.PLAYBACK_POSITION_UNKNOWN && isPlayingState(mCurrentPlayState)) { mPlayStartTimeMs = SystemClock.elapsedRealtime(); } } /* need send play position changed notification when track is changed */ if (mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) { mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED; Loading @@ -744,8 +740,6 @@ public final class Avrcp { Log.v(TAG, "Metadata updated but no change!"); } mSongLengthMs = data.getLong(MediaMetadataRetriever.METADATA_KEY_DURATION, RemoteControlClient.PLAYBACK_POSITION_INVALID); } private void getRcFeatures(byte[] address, int features) { Loading Loading @@ -791,10 +785,10 @@ public final class Avrcp { long songPosition = getPlayPosition(); mPlayPosChangedNT = NOTIFICATION_TYPE_INTERIM; mPlaybackIntervalMs = (long)param * 1000L; if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) { if (mCurrentPosMs != PlaybackState.PLAYBACK_POSITION_UNKNOWN) { mNextPosMs = songPosition + mPlaybackIntervalMs; mPrevPosMs = songPosition - mPlaybackIntervalMs; if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) { if (isPlayingState(mCurrentPlayState)) { Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT); mHandler.sendMessageDelayed(msg, mPlaybackIntervalMs); } Loading Loading @@ -830,7 +824,7 @@ public final class Avrcp { long currentPosMs = getPlayPosition(); if (currentPosMs == -1L) return; long newPosMs = Math.max(0L, currentPosMs + amount); mRemoteController.seekTo(newPosMs); mMediaController.getTransportControls().seekTo(newPosMs); } private int getSkipMultiplier() { Loading @@ -846,7 +840,7 @@ public final class Avrcp { 0xFFFFFFFFFFFFFFFF in the interim response */ long trackNumberRsp = -1L; if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) { if (isPlayingState(mCurrentPlayState)) { trackNumberRsp = mTrackNumber; } Loading @@ -859,8 +853,8 @@ public final class Avrcp { private long getPlayPosition() { long songPosition = -1L; if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) { if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) { if (mCurrentPosMs != PlaybackState.PLAYBACK_POSITION_UNKNOWN) { if (mCurrentPlayState.getState() == PlaybackState.STATE_PLAYING) { songPosition = SystemClock.elapsedRealtime() - mPlayStartTimeMs + mCurrentPosMs; } else { Loading Loading @@ -900,34 +894,35 @@ public final class Avrcp { return attrStr; } private int convertPlayStateToPlayStatus(int playState) { private int convertPlayStateToPlayStatus(PlaybackState state) { int playStatus = PLAYSTATUS_ERROR; switch (playState) { case RemoteControlClient.PLAYSTATE_PLAYING: case RemoteControlClient.PLAYSTATE_BUFFERING: switch (state.getState()) { case PlaybackState.STATE_PLAYING: case PlaybackState.STATE_BUFFERING: playStatus = PLAYSTATUS_PLAYING; break; case RemoteControlClient.PLAYSTATE_STOPPED: case RemoteControlClient.PLAYSTATE_NONE: case PlaybackState.STATE_STOPPED: case PlaybackState.STATE_NONE: playStatus = PLAYSTATUS_STOPPED; break; case RemoteControlClient.PLAYSTATE_PAUSED: case PlaybackState.STATE_PAUSED: playStatus = PLAYSTATUS_PAUSED; break; case RemoteControlClient.PLAYSTATE_FAST_FORWARDING: case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS: case PlaybackState.STATE_FAST_FORWARDING: case PlaybackState.STATE_SKIPPING_TO_NEXT: case PlaybackState.STATE_SKIPPING_TO_QUEUE_ITEM: playStatus = PLAYSTATUS_FWD_SEEK; break; case RemoteControlClient.PLAYSTATE_REWINDING: case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS: case PlaybackState.STATE_REWINDING: case PlaybackState.STATE_SKIPPING_TO_PREVIOUS: playStatus = PLAYSTATUS_REV_SEEK; break; case RemoteControlClient.PLAYSTATE_ERROR: case PlaybackState.STATE_ERROR: playStatus = PLAYSTATUS_ERROR; break; Loading @@ -935,18 +930,9 @@ public final class Avrcp { return playStatus; } private boolean isPlayingState(int playState) { boolean isPlaying = false; switch (playState) { case RemoteControlClient.PLAYSTATE_PLAYING: case RemoteControlClient.PLAYSTATE_BUFFERING: isPlaying = true; break; default: isPlaying = false; break; } return isPlaying; private boolean isPlayingState(PlaybackState state) { return (state.getState() == PlaybackState.STATE_PLAYING) || (state.getState() == PlaybackState.STATE_BUFFERING); } /** Loading