MediaSessionRecord.java revision 8ae0f34db936a649ddaf9cdd086c224f6514efeb
1/*
2 * Copyright (C) 2014 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
17package com.android.server.media;
18
19import android.content.Intent;
20import android.media.session.IMediaController;
21import android.media.session.IMediaControllerCallback;
22import android.media.session.IMediaSession;
23import android.media.session.IMediaSessionCallback;
24import android.media.session.MediaMetadata;
25import android.media.session.PlaybackState;
26import android.media.Rating;
27import android.os.Bundle;
28import android.os.Handler;
29import android.os.IBinder;
30import android.os.Looper;
31import android.os.Message;
32import android.os.RemoteException;
33import android.os.ResultReceiver;
34import android.util.Log;
35import android.util.Slog;
36import android.view.KeyEvent;
37
38import java.util.ArrayList;
39import java.util.List;
40
41/**
42 * This is the system implementation of a Session. Apps will interact with the
43 * MediaSession wrapper class instead.
44 */
45public class MediaSessionRecord implements IBinder.DeathRecipient {
46    private static final String TAG = "MediaSessionImpl";
47
48    private final MessageHandler mHandler;
49
50    private final int mPid;
51    private final String mPackageName;
52    private final String mTag;
53    private final ControllerStub mController;
54    private final SessionStub mSession;
55    private final SessionCb mSessionCb;
56    private final MediaSessionService mService;
57
58    private final Object mControllerLock = new Object();
59    private final ArrayList<IMediaControllerCallback> mControllerCallbacks =
60            new ArrayList<IMediaControllerCallback>();
61    private final ArrayList<String> mInterfaces = new ArrayList<String>();
62
63    private boolean mTransportPerformerEnabled = false;
64    private Bundle mRoute;
65
66    // TransportPerformer fields
67
68    private MediaMetadata mMetadata;
69    private PlaybackState mPlaybackState;
70    private int mRatingType;
71    // End TransportPerformer fields
72
73    private boolean mIsPublished = false;
74
75    public MediaSessionRecord(int pid, String packageName, IMediaSessionCallback cb, String tag,
76            MediaSessionService service, Handler handler) {
77        mPid = pid;
78        mPackageName = packageName;
79        mTag = tag;
80        mController = new ControllerStub();
81        mSession = new SessionStub();
82        mSessionCb = new SessionCb(cb);
83        mService = service;
84        mHandler = new MessageHandler(handler.getLooper());
85    }
86
87    public IMediaSession getSessionBinder() {
88        return mSession;
89    }
90
91    public IMediaController getControllerBinder() {
92        return mController;
93    }
94
95    @Override
96    public void binderDied() {
97        mService.sessionDied(this);
98    }
99
100    public boolean isPublished() {
101        return mIsPublished;
102    }
103
104    private void onDestroy() {
105        mService.destroySession(this);
106    }
107
108    private void pushPlaybackStateUpdate() {
109        synchronized (mControllerLock) {
110            for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
111                IMediaControllerCallback cb = mControllerCallbacks.get(i);
112                try {
113                    cb.onPlaybackStateChanged(mPlaybackState);
114                } catch (RemoteException e) {
115                    Log.w(TAG, "Removing dead callback in pushPlaybackStateUpdate.", e);
116                    mControllerCallbacks.remove(i);
117                }
118            }
119        }
120    }
121
122    private void pushMetadataUpdate() {
123        synchronized (mControllerLock) {
124            for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
125                IMediaControllerCallback cb = mControllerCallbacks.get(i);
126                try {
127                    cb.onMetadataChanged(mMetadata);
128                } catch (RemoteException e) {
129                    Log.w(TAG, "Removing dead callback in pushMetadataUpdate.", e);
130                    mControllerCallbacks.remove(i);
131                }
132            }
133        }
134    }
135
136    private void pushRouteUpdate() {
137        synchronized (mControllerLock) {
138            for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
139                IMediaControllerCallback cb = mControllerCallbacks.get(i);
140                try {
141                    cb.onRouteChanged(mRoute);
142                } catch (RemoteException e) {
143                    Log.w(TAG, "Removing dead callback in pushRouteUpdate.", e);
144                    mControllerCallbacks.remove(i);
145                }
146            }
147        }
148    }
149
150    private void pushEvent(String event, Bundle data) {
151        synchronized (mControllerLock) {
152            for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
153                IMediaControllerCallback cb = mControllerCallbacks.get(i);
154                try {
155                    cb.onEvent(event, data);
156                } catch (RemoteException e) {
157                    Log.w(TAG, "Removing dead callback in pushRouteUpdate.", e);
158                    mControllerCallbacks.remove(i);
159                }
160            }
161        }
162    }
163
164    private final class SessionStub extends IMediaSession.Stub {
165
166        @Override
167        public void destroy() {
168            onDestroy();
169        }
170
171        @Override
172        public void sendEvent(String event, Bundle data) {
173            mHandler.post(MessageHandler.MSG_SEND_EVENT, event, data);
174        }
175
176        @Override
177        public IMediaController getMediaController() {
178            return mController;
179        }
180
181        @Override
182        public void setRouteState(Bundle routeState) {
183        }
184
185        @Override
186        public void setRoute(Bundle mediaRouteDescriptor) {
187            mRoute = mediaRouteDescriptor;
188            mHandler.post(MessageHandler.MSG_UPDATE_ROUTE);
189        }
190
191        @Override
192        public void publish() {
193            mIsPublished = true; // TODO push update to service
194        }
195        @Override
196        public void setTransportPerformerEnabled() {
197            mTransportPerformerEnabled = true;
198        }
199
200        @Override
201        public List<String> getSupportedInterfaces() {
202            return mInterfaces;
203        }
204
205        @Override
206        public void setMetadata(MediaMetadata metadata) {
207            mMetadata = metadata;
208            mHandler.post(MessageHandler.MSG_UPDATE_METADATA);
209        }
210
211        @Override
212        public void setPlaybackState(PlaybackState state) {
213            mPlaybackState = state;
214            mHandler.post(MessageHandler.MSG_UPDATE_PLAYBACK_STATE);
215        }
216
217        @Override
218        public void setRatingType(int type) {
219            mRatingType = type;
220        }
221    }
222
223    class SessionCb {
224        private final IMediaSessionCallback mCb;
225
226        public SessionCb(IMediaSessionCallback cb) {
227            mCb = cb;
228        }
229
230        public void sendMediaButton(KeyEvent keyEvent) {
231            Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
232            mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
233            try {
234                mCb.onMediaButton(mediaButtonIntent);
235            } catch (RemoteException e) {
236                Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
237            }
238        }
239
240        public void sendCommand(String command, Bundle extras, ResultReceiver cb) {
241            try {
242                mCb.onCommand(command, extras, cb);
243            } catch (RemoteException e) {
244                Slog.e(TAG, "Remote failure in sendCommand.", e);
245            }
246        }
247
248        public void play() {
249            try {
250                mCb.onPlay();
251            } catch (RemoteException e) {
252                Slog.e(TAG, "Remote failure in play.", e);
253            }
254        }
255
256        public void pause() {
257            try {
258                mCb.onPause();
259            } catch (RemoteException e) {
260                Slog.e(TAG, "Remote failure in pause.", e);
261            }
262        }
263
264        public void stop() {
265            try {
266                mCb.onStop();
267            } catch (RemoteException e) {
268                Slog.e(TAG, "Remote failure in stop.", e);
269            }
270        }
271
272        public void next() {
273            try {
274                mCb.onNext();
275            } catch (RemoteException e) {
276                Slog.e(TAG, "Remote failure in next.", e);
277            }
278        }
279
280        public void previous() {
281            try {
282                mCb.onPrevious();
283            } catch (RemoteException e) {
284                Slog.e(TAG, "Remote failure in previous.", e);
285            }
286        }
287
288        public void fastForward() {
289            try {
290                mCb.onFastForward();
291            } catch (RemoteException e) {
292                Slog.e(TAG, "Remote failure in fastForward.", e);
293            }
294        }
295
296        public void rewind() {
297            try {
298                mCb.onRewind();
299            } catch (RemoteException e) {
300                Slog.e(TAG, "Remote failure in rewind.", e);
301            }
302        }
303
304        public void seekTo(long pos) {
305            try {
306                mCb.onSeekTo(pos);
307            } catch (RemoteException e) {
308                Slog.e(TAG, "Remote failure in seekTo.", e);
309            }
310        }
311
312        public void rate(Rating rating) {
313            try {
314                mCb.onRate(rating);
315            } catch (RemoteException e) {
316                Slog.e(TAG, "Remote failure in rate.", e);
317            }
318        }
319    }
320
321    class ControllerStub extends IMediaController.Stub {
322        @Override
323        public void sendCommand(String command, Bundle extras, ResultReceiver cb)
324                throws RemoteException {
325            mSessionCb.sendCommand(command, extras, cb);
326        }
327
328        @Override
329        public void sendMediaButton(KeyEvent mediaButtonIntent) {
330            mSessionCb.sendMediaButton(mediaButtonIntent);
331        }
332
333        @Override
334        public void registerCallbackListener(IMediaControllerCallback cb) {
335            synchronized (mControllerLock) {
336                if (!mControllerCallbacks.contains(cb)) {
337                    mControllerCallbacks.add(cb);
338                }
339            }
340        }
341
342        @Override
343        public void unregisterCallbackListener(IMediaControllerCallback cb)
344                throws RemoteException {
345            synchronized (mControllerLock) {
346                mControllerCallbacks.remove(cb);
347            }
348        }
349
350        @Override
351        public void play() throws RemoteException {
352            mSessionCb.play();
353        }
354
355        @Override
356        public void pause() throws RemoteException {
357            mSessionCb.pause();
358        }
359
360        @Override
361        public void stop() throws RemoteException {
362            mSessionCb.stop();
363        }
364
365        @Override
366        public void next() throws RemoteException {
367            mSessionCb.next();
368        }
369
370        @Override
371        public void previous() throws RemoteException {
372            mSessionCb.previous();
373        }
374
375        @Override
376        public void fastForward() throws RemoteException {
377            mSessionCb.fastForward();
378        }
379
380        @Override
381        public void rewind() throws RemoteException {
382            mSessionCb.rewind();
383        }
384
385        @Override
386        public void seekTo(long pos) throws RemoteException {
387            mSessionCb.seekTo(pos);
388        }
389
390        @Override
391        public void rate(Rating rating) throws RemoteException {
392            mSessionCb.rate(rating);
393        }
394
395
396        @Override
397        public MediaMetadata getMetadata() {
398            return mMetadata;
399        }
400
401        @Override
402        public PlaybackState getPlaybackState() {
403            return mPlaybackState;
404        }
405
406        @Override
407        public int getRatingType() {
408            return mRatingType;
409        }
410
411        @Override
412        public boolean isTransportControlEnabled() throws RemoteException {
413            return mTransportPerformerEnabled;
414        }
415    }
416
417    private class MessageHandler extends Handler {
418        private static final int MSG_UPDATE_METADATA = 1;
419        private static final int MSG_UPDATE_PLAYBACK_STATE = 2;
420        private static final int MSG_UPDATE_ROUTE = 3;
421        private static final int MSG_SEND_EVENT = 4;
422
423        public MessageHandler(Looper looper) {
424            super(looper);
425        }
426        @Override
427        public void handleMessage(Message msg) {
428            switch (msg.what) {
429                case MSG_UPDATE_METADATA:
430                    pushMetadataUpdate();
431                    break;
432                case MSG_UPDATE_PLAYBACK_STATE:
433                    pushPlaybackStateUpdate();
434                    break;
435                case MSG_UPDATE_ROUTE:
436                    pushRouteUpdate();
437                    break;
438                case MSG_SEND_EVENT:
439                    pushEvent((String) msg.obj, msg.getData());
440                    break;
441            }
442        }
443
444        public void post(int what) {
445            post(what, null);
446        }
447
448        public void post(int what, Object obj) {
449            obtainMessage(what, obj).sendToTarget();
450        }
451
452        public void post(int what, Object obj, Bundle data) {
453            Message msg = obtainMessage(what, obj);
454            msg.setData(data);
455            msg.sendToTarget();
456        }
457    }
458
459}
460