• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.bluetooth.avrcpcontroller;
18 
19 import android.media.MediaMetadata;
20 import android.media.browse.MediaBrowser.MediaItem;
21 import android.media.session.MediaController;
22 import android.media.session.MediaSession;
23 import android.media.session.PlaybackState;
24 import android.os.Bundle;
25 import android.service.media.MediaBrowserService;
26 import android.util.Log;
27 
28 import com.android.bluetooth.R;
29 
30 import java.util.ArrayList;
31 import java.util.List;
32 
33 /**
34  * Implements the MediaBrowserService interface to AVRCP and A2DP
35  *
36  * This service provides a means for external applications to access A2DP and AVRCP.
37  * The applications are expected to use MediaBrowser (see API) and all the music
38  * browsing/playback/metadata can be controlled via MediaBrowser and MediaController.
39  *
40  * The current behavior of MediaSession exposed by this service is as follows:
41  * 1. MediaSession is active (i.e. SystemUI and other overview UIs can see updates) when device is
42  * connected and first starts playing. Before it starts playing we do not active the session.
43  * 1.1 The session is active throughout the duration of connection.
44  * 2. The session is de-activated when the device disconnects. It will be connected again when (1)
45  * happens.
46  */
47 public class BluetoothMediaBrowserService extends MediaBrowserService {
48     private static final String TAG = "BluetoothMediaBrowserService";
49     private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
50 
51     private static BluetoothMediaBrowserService sBluetoothMediaBrowserService;
52 
53     private MediaSession mSession;
54 
55     // Browsing related structures.
56     private List<MediaSession.QueueItem> mMediaQueue = new ArrayList<>();
57 
58     /**
59      * Initialize this BluetoothMediaBrowserService, creating our MediaSession, MediaPlayer and
60      * MediaMetaData, and setting up mechanisms to talk with the AvrcpControllerService.
61      */
62     @Override
onCreate()63     public void onCreate() {
64         if (DBG) Log.d(TAG, "onCreate");
65         super.onCreate();
66 
67         // Create and configure the MediaSession
68         mSession = new MediaSession(this, TAG);
69         setSessionToken(mSession.getSessionToken());
70         mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS
71                 | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
72         mSession.setQueueTitle(getString(R.string.bluetooth_a2dp_sink_queue_name));
73         mSession.setQueue(mMediaQueue);
74         PlaybackState.Builder playbackStateBuilder = new PlaybackState.Builder();
75         playbackStateBuilder.setState(PlaybackState.STATE_ERROR,
76                 PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f).setActions(0);
77         playbackStateBuilder.setErrorMessage(getString(R.string.bluetooth_disconnected));
78         mSession.setPlaybackState(playbackStateBuilder.build());
79         sBluetoothMediaBrowserService = this;
80     }
81 
getContents(final String parentMediaId)82     List<MediaItem> getContents(final String parentMediaId) {
83         AvrcpControllerService avrcpControllerService =
84                 AvrcpControllerService.getAvrcpControllerService();
85         if (avrcpControllerService == null) {
86             return new ArrayList(0);
87         } else {
88             return avrcpControllerService.getContents(parentMediaId);
89         }
90     }
91 
92     @Override
onLoadChildren(final String parentMediaId, final Result<List<MediaItem>> result)93     public synchronized void onLoadChildren(final String parentMediaId,
94             final Result<List<MediaItem>> result) {
95         if (DBG) Log.d(TAG, "onLoadChildren parentMediaId=" + parentMediaId);
96         List<MediaItem> contents = getContents(parentMediaId);
97         if (contents == null) {
98             result.detach();
99         } else {
100             result.sendResult(contents);
101         }
102     }
103 
104     @Override
onGetRoot(String clientPackageName, int clientUid, Bundle rootHints)105     public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) {
106         if (DBG) Log.d(TAG, "onGetRoot");
107         return new BrowserRoot(BrowseTree.ROOT, null);
108     }
109 
updateNowPlayingQueue(BrowseTree.BrowseNode node)110     private void updateNowPlayingQueue(BrowseTree.BrowseNode node) {
111         List<MediaItem> songList = node.getContents();
112         mMediaQueue.clear();
113         if (songList != null) {
114             for (MediaItem song : songList) {
115                 mMediaQueue.add(new MediaSession.QueueItem(song.getDescription(),
116                         mMediaQueue.size()));
117             }
118         }
119         mSession.setQueue(mMediaQueue);
120     }
121 
notifyChanged(BrowseTree.BrowseNode node)122     static synchronized void notifyChanged(BrowseTree.BrowseNode node) {
123         if (sBluetoothMediaBrowserService != null) {
124             if (node.getScope() == AvrcpControllerService.BROWSE_SCOPE_NOW_PLAYING) {
125                 sBluetoothMediaBrowserService.updateNowPlayingQueue(node);
126             } else {
127                 sBluetoothMediaBrowserService.notifyChildrenChanged(node.getID());
128             }
129         }
130     }
131 
addressedPlayerChanged(MediaSession.Callback callback)132     static synchronized void addressedPlayerChanged(MediaSession.Callback callback) {
133         if (sBluetoothMediaBrowserService != null) {
134             sBluetoothMediaBrowserService.mSession.setCallback(callback);
135         } else {
136             Log.w(TAG, "addressedPlayerChanged Unavailable");
137         }
138     }
139 
trackChanged(MediaMetadata mediaMetadata)140     static synchronized void trackChanged(MediaMetadata mediaMetadata) {
141         if (sBluetoothMediaBrowserService != null) {
142             sBluetoothMediaBrowserService.mSession.setMetadata(mediaMetadata);
143         } else {
144             Log.w(TAG, "trackChanged Unavailable");
145         }
146     }
147 
notifyChanged(PlaybackState playbackState)148     static synchronized void notifyChanged(PlaybackState playbackState) {
149         Log.d(TAG, "notifyChanged PlaybackState" + playbackState);
150         if (sBluetoothMediaBrowserService != null) {
151             sBluetoothMediaBrowserService.mSession.setPlaybackState(playbackState);
152         } else {
153             Log.w(TAG, "notifyChanged Unavailable");
154         }
155     }
156 
157     /**
158      * Send AVRCP Play command
159      */
play()160     public static synchronized void play() {
161         if (sBluetoothMediaBrowserService != null) {
162             sBluetoothMediaBrowserService.mSession.getController().getTransportControls().play();
163         } else {
164             Log.w(TAG, "play Unavailable");
165         }
166     }
167 
168     /**
169      * Send AVRCP Pause command
170      */
pause()171     public static synchronized void pause() {
172         if (sBluetoothMediaBrowserService != null) {
173             sBluetoothMediaBrowserService.mSession.getController().getTransportControls().pause();
174         } else {
175             Log.w(TAG, "pause Unavailable");
176         }
177     }
178 
179     /**
180      * Get object for controlling playback
181      */
getTransportControls()182     public static synchronized MediaController.TransportControls getTransportControls() {
183         if (sBluetoothMediaBrowserService != null) {
184             return sBluetoothMediaBrowserService.mSession.getController().getTransportControls();
185         } else {
186             Log.w(TAG, "transportControls Unavailable");
187             return null;
188         }
189     }
190 
191     /**
192      * Set Media session active whenever we have Focus of any kind
193      */
setActive(boolean active)194     public static synchronized void setActive(boolean active) {
195         if (sBluetoothMediaBrowserService != null) {
196             sBluetoothMediaBrowserService.mSession.setActive(active);
197         } else {
198             Log.w(TAG, "setActive Unavailable");
199         }
200     }
201 }
202