blob: 84027e2cc49f217269c4426f394f5df6b7192d89 [file] [log] [blame]
RoboErik01fe6612014-02-13 14:19:04 -08001/*
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
RoboErike34c09d2014-07-24 10:20:41 -070019import android.app.PendingIntent;
RoboErik6f0e4dd2014-06-17 16:56:27 -070020import android.content.ComponentName;
RoboErikef3c9e92014-06-19 16:07:28 -070021import android.content.Context;
RoboErik01fe6612014-02-13 14:19:04 -080022import android.content.Intent;
Gabriel Pealf0593bc2014-07-22 09:39:06 -070023import android.content.pm.ParceledListSlice;
24import android.media.AudioManager;
RoboErik0dac35a2014-08-12 15:48:49 -070025import android.media.AudioManagerInternal;
Gabriel Pealf0593bc2014-07-22 09:39:06 -070026import android.media.MediaMetadata;
27import android.media.Rating;
28import android.media.VolumeProvider;
RoboErik07c70772014-03-20 13:33:52 -070029import android.media.session.ISession;
30import android.media.session.ISessionCallback;
Gabriel Pealf0593bc2014-07-22 09:39:06 -070031import android.media.session.ISessionController;
32import android.media.session.ISessionControllerCallback;
RoboErik42ea7ee2014-05-16 16:27:35 -070033import android.media.session.MediaController;
RoboErik42ea7ee2014-05-16 16:27:35 -070034import android.media.session.MediaSession;
RoboErikef3c9e92014-06-19 16:07:28 -070035import android.media.session.ParcelableVolumeInfo;
Gabriel Pealf0593bc2014-07-22 09:39:06 -070036import android.media.session.PlaybackState;
RoboErik9db9bf72014-07-21 12:44:53 -070037import android.media.AudioAttributes;
Gabriel Pealf0593bc2014-07-22 09:39:06 -070038import android.net.Uri;
RoboErikf77b99f2014-06-27 11:38:59 -070039import android.os.Binder;
RoboErik01fe6612014-02-13 14:19:04 -080040import android.os.Bundle;
RoboErikd3c86422014-06-16 14:00:48 -070041import android.os.DeadObjectException;
RoboErik8ae0f342014-02-24 18:02:08 -080042import android.os.Handler;
RoboErik01fe6612014-02-13 14:19:04 -080043import android.os.IBinder;
RoboErik8ae0f342014-02-24 18:02:08 -080044import android.os.Looper;
45import android.os.Message;
RoboErik01fe6612014-02-13 14:19:04 -080046import android.os.RemoteException;
RoboErik8ae0f342014-02-24 18:02:08 -080047import android.os.ResultReceiver;
RoboErikf1372422014-04-23 14:38:17 -070048import android.os.SystemClock;
RoboErik01fe6612014-02-13 14:19:04 -080049import android.util.Log;
RoboErik8ae0f342014-02-24 18:02:08 -080050import android.util.Slog;
RoboErik01fe6612014-02-13 14:19:04 -080051import android.view.KeyEvent;
52
RoboErik0dac35a2014-08-12 15:48:49 -070053import com.android.server.LocalServices;
54
RoboErika278ea72014-04-24 14:49:01 -070055import java.io.PrintWriter;
RoboErik01fe6612014-02-13 14:19:04 -080056import java.util.ArrayList;
RoboErik07c70772014-03-20 13:33:52 -070057import java.util.UUID;
RoboErik01fe6612014-02-13 14:19:04 -080058
59/**
60 * This is the system implementation of a Session. Apps will interact with the
61 * MediaSession wrapper class instead.
62 */
63public class MediaSessionRecord implements IBinder.DeathRecipient {
RoboErik07c70772014-03-20 13:33:52 -070064 private static final String TAG = "MediaSessionRecord";
RoboErikd3c86422014-06-16 14:00:48 -070065 private static final boolean DEBUG = false;
RoboErik01fe6612014-02-13 14:19:04 -080066
RoboErika8f95142014-05-05 14:23:49 -070067 /**
RoboErika8f95142014-05-05 14:23:49 -070068 * The length of time a session will still be considered active after
69 * pausing in ms.
70 */
71 private static final int ACTIVE_BUFFER = 30000;
72
RoboErik19c95182014-06-23 15:38:48 -070073 /**
74 * The amount of time we'll send an assumed volume after the last volume
75 * command before reverting to the last reported volume.
76 */
77 private static final int OPTIMISTIC_VOLUME_TIMEOUT = 1000;
78
RoboErik8ae0f342014-02-24 18:02:08 -080079 private final MessageHandler mHandler;
80
RoboErika5b02322014-05-07 17:05:49 -070081 private final int mOwnerPid;
82 private final int mOwnerUid;
83 private final int mUserId;
RoboErikaa4e23b2014-07-24 18:35:11 -070084 private final String mPackageName;
RoboErik01fe6612014-02-13 14:19:04 -080085 private final String mTag;
86 private final ControllerStub mController;
87 private final SessionStub mSession;
88 private final SessionCb mSessionCb;
89 private final MediaSessionService mService;
90
RoboErik07c70772014-03-20 13:33:52 -070091 private final Object mLock = new Object();
92 private final ArrayList<ISessionControllerCallback> mControllerCallbacks =
93 new ArrayList<ISessionControllerCallback>();
RoboErik01fe6612014-02-13 14:19:04 -080094
RoboErike7880d82014-04-30 12:48:25 -070095 private long mFlags;
RoboErikb214efb2014-07-24 13:20:30 -070096 private PendingIntent mMediaButtonReceiver;
RoboErike34c09d2014-07-24 10:20:41 -070097 private PendingIntent mLaunchIntent;
RoboErik8ae0f342014-02-24 18:02:08 -080098
99 // TransportPerformer fields
100
Gabriel Pealf0593bc2014-07-22 09:39:06 -0700101 private Bundle mExtras;
RoboErik8ae0f342014-02-24 18:02:08 -0800102 private MediaMetadata mMetadata;
103 private PlaybackState mPlaybackState;
Gabriel Pealf0593bc2014-07-22 09:39:06 -0700104 private ParceledListSlice mQueue;
105 private CharSequence mQueueTitle;
RoboErik8ae0f342014-02-24 18:02:08 -0800106 private int mRatingType;
RoboErika8f95142014-05-05 14:23:49 -0700107 private long mLastActiveTime;
RoboErik8ae0f342014-02-24 18:02:08 -0800108 // End TransportPerformer fields
109
RoboErikb69ffd42014-05-30 14:57:59 -0700110 // Volume handling fields
RoboErik9db9bf72014-07-21 12:44:53 -0700111 private AudioAttributes mAudioAttrs;
RoboErikef3c9e92014-06-19 16:07:28 -0700112 private AudioManager mAudioManager;
RoboErik0dac35a2014-08-12 15:48:49 -0700113 private AudioManagerInternal mAudioManagerInternal;
RoboErik19c95182014-06-23 15:38:48 -0700114 private int mVolumeType = MediaSession.PLAYBACK_TYPE_LOCAL;
RoboErikef3c9e92014-06-19 16:07:28 -0700115 private int mVolumeControlType = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
RoboErikb69ffd42014-05-30 14:57:59 -0700116 private int mMaxVolume = 0;
117 private int mCurrentVolume = 0;
RoboErik19c95182014-06-23 15:38:48 -0700118 private int mOptimisticVolume = -1;
RoboErikb69ffd42014-05-30 14:57:59 -0700119 // End volume handling fields
120
RoboErika8f95142014-05-05 14:23:49 -0700121 private boolean mIsActive = false;
RoboErik4646d282014-05-13 10:13:04 -0700122 private boolean mDestroyed = false;
RoboErik01fe6612014-02-13 14:19:04 -0800123
RoboErika5b02322014-05-07 17:05:49 -0700124 public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName,
125 ISessionCallback cb, String tag, MediaSessionService service, Handler handler) {
126 mOwnerPid = ownerPid;
127 mOwnerUid = ownerUid;
128 mUserId = userId;
RoboErikaa4e23b2014-07-24 18:35:11 -0700129 mPackageName = ownerPackageName;
RoboErik01fe6612014-02-13 14:19:04 -0800130 mTag = tag;
131 mController = new ControllerStub();
132 mSession = new SessionStub();
133 mSessionCb = new SessionCb(cb);
134 mService = service;
RoboErik8ae0f342014-02-24 18:02:08 -0800135 mHandler = new MessageHandler(handler.getLooper());
RoboErikef3c9e92014-06-19 16:07:28 -0700136 mAudioManager = (AudioManager) service.getContext().getSystemService(Context.AUDIO_SERVICE);
RoboErik0dac35a2014-08-12 15:48:49 -0700137 mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
RoboErik9db9bf72014-07-21 12:44:53 -0700138 mAudioAttrs = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build();
RoboErik01fe6612014-02-13 14:19:04 -0800139 }
140
RoboErik07c70772014-03-20 13:33:52 -0700141 /**
RoboErik42ea7ee2014-05-16 16:27:35 -0700142 * Get the binder for the {@link MediaSession}.
RoboErik07c70772014-03-20 13:33:52 -0700143 *
144 * @return The session binder apps talk to.
145 */
146 public ISession getSessionBinder() {
RoboErik01fe6612014-02-13 14:19:04 -0800147 return mSession;
148 }
149
RoboErik07c70772014-03-20 13:33:52 -0700150 /**
RoboErik42ea7ee2014-05-16 16:27:35 -0700151 * Get the binder for the {@link MediaController}.
RoboErik07c70772014-03-20 13:33:52 -0700152 *
153 * @return The controller binder apps talk to.
154 */
155 public ISessionController getControllerBinder() {
RoboErik01fe6612014-02-13 14:19:04 -0800156 return mController;
157 }
158
RoboErik07c70772014-03-20 13:33:52 -0700159 /**
RoboErik07c70772014-03-20 13:33:52 -0700160 * Get the info for this session.
161 *
162 * @return Info that identifies this session.
163 */
RoboErikaa4e23b2014-07-24 18:35:11 -0700164 public String getPackageName() {
165 return mPackageName;
166 }
167
168 /**
169 * Get the tag for the session.
170 *
171 * @return The session's tag.
172 */
173 public String getTag() {
174 return mTag;
RoboErik07c70772014-03-20 13:33:52 -0700175 }
176
RoboErikb214efb2014-07-24 13:20:30 -0700177 /**
178 * Get the intent the app set for their media button receiver.
179 *
180 * @return The pending intent set by the app or null.
181 */
182 public PendingIntent getMediaButtonReceiver() {
RoboErik6f0e4dd2014-06-17 16:56:27 -0700183 return mMediaButtonReceiver;
184 }
185
RoboErik07c70772014-03-20 13:33:52 -0700186 /**
RoboErike7880d82014-04-30 12:48:25 -0700187 * Get this session's flags.
188 *
189 * @return The flags for this session.
190 */
191 public long getFlags() {
192 return mFlags;
193 }
194
195 /**
RoboErika8f95142014-05-05 14:23:49 -0700196 * Check if this session has the specified flag.
197 *
198 * @param flag The flag to check.
199 * @return True if this session has that flag set, false otherwise.
200 */
201 public boolean hasFlag(int flag) {
202 return (mFlags & flag) != 0;
203 }
204
205 /**
RoboErika5b02322014-05-07 17:05:49 -0700206 * Get the user id this session was created for.
207 *
208 * @return The user id for this session.
209 */
210 public int getUserId() {
211 return mUserId;
212 }
213
214 /**
RoboErike7880d82014-04-30 12:48:25 -0700215 * Check if this session has system priorty and should receive media buttons
216 * before any other sessions.
217 *
218 * @return True if this is a system priority session, false otherwise
219 */
220 public boolean isSystemPriority() {
RoboErik42ea7ee2014-05-16 16:27:35 -0700221 return (mFlags & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0;
RoboErike7880d82014-04-30 12:48:25 -0700222 }
223
224 /**
RoboErik1ff5b162014-07-15 17:23:18 -0700225 * Send a volume adjustment to the session owner. Direction must be one of
226 * {@link AudioManager#ADJUST_LOWER}, {@link AudioManager#ADJUST_RAISE},
227 * {@link AudioManager#ADJUST_SAME}.
RoboErikb69ffd42014-05-30 14:57:59 -0700228 *
RoboErik1ff5b162014-07-15 17:23:18 -0700229 * @param direction The direction to adjust volume in.
RoboErikb69ffd42014-05-30 14:57:59 -0700230 */
RoboErik0dac35a2014-08-12 15:48:49 -0700231 public void adjustVolume(int direction, int flags, String packageName, int uid) {
RoboErike9466802014-06-26 10:26:43 -0700232 if (isPlaybackActive(false)) {
233 flags &= ~AudioManager.FLAG_PLAY_SOUND;
234 }
RoboErik1ff5b162014-07-15 17:23:18 -0700235 if (direction > 1) {
236 direction = 1;
237 } else if (direction < -1) {
238 direction = -1;
239 }
RoboErik19c95182014-06-23 15:38:48 -0700240 if (mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL) {
RoboErik9db9bf72014-07-21 12:44:53 -0700241 int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
RoboErik0dac35a2014-08-12 15:48:49 -0700242 mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags, packageName,
243 uid);
RoboErikef3c9e92014-06-19 16:07:28 -0700244 } else {
245 if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) {
246 // Nothing to do, the volume cannot be changed
247 return;
248 }
RoboErik1ff5b162014-07-15 17:23:18 -0700249 mSessionCb.adjustVolume(direction);
RoboErik19c95182014-06-23 15:38:48 -0700250
251 int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume);
RoboErik1ff5b162014-07-15 17:23:18 -0700252 mOptimisticVolume = volumeBefore + direction;
RoboErik19c95182014-06-23 15:38:48 -0700253 mOptimisticVolume = Math.max(0, Math.min(mOptimisticVolume, mMaxVolume));
254 mHandler.removeCallbacks(mClearOptimisticVolumeRunnable);
255 mHandler.postDelayed(mClearOptimisticVolumeRunnable, OPTIMISTIC_VOLUME_TIMEOUT);
256 if (volumeBefore != mOptimisticVolume) {
257 pushVolumeUpdate();
258 }
259
260 if (DEBUG) {
261 Log.d(TAG, "Adjusted optimistic volume to " + mOptimisticVolume + " max is "
262 + mMaxVolume);
263 }
RoboErikb69ffd42014-05-30 14:57:59 -0700264 }
RoboErikb69ffd42014-05-30 14:57:59 -0700265 }
266
RoboErik0dac35a2014-08-12 15:48:49 -0700267 public void setVolumeTo(int value, int flags, String packageName, int uid) {
RoboErik19c95182014-06-23 15:38:48 -0700268 if (mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL) {
RoboErik9db9bf72014-07-21 12:44:53 -0700269 int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
RoboErik0dac35a2014-08-12 15:48:49 -0700270 mAudioManagerInternal.setStreamVolumeForUid(stream, value, flags, packageName, uid);
RoboErikef3c9e92014-06-19 16:07:28 -0700271 } else {
272 if (mVolumeControlType != VolumeProvider.VOLUME_CONTROL_ABSOLUTE) {
273 // Nothing to do. The volume can't be set directly.
274 return;
275 }
RoboErik19c95182014-06-23 15:38:48 -0700276 value = Math.max(0, Math.min(value, mMaxVolume));
RoboErikef3c9e92014-06-19 16:07:28 -0700277 mSessionCb.setVolumeTo(value);
RoboErik19c95182014-06-23 15:38:48 -0700278
279 int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume);
280 mOptimisticVolume = Math.max(0, Math.min(value, mMaxVolume));
281 mHandler.removeCallbacks(mClearOptimisticVolumeRunnable);
282 mHandler.postDelayed(mClearOptimisticVolumeRunnable, OPTIMISTIC_VOLUME_TIMEOUT);
283 if (volumeBefore != mOptimisticVolume) {
284 pushVolumeUpdate();
285 }
286
287 if (DEBUG) {
288 Log.d(TAG, "Set optimistic volume to " + mOptimisticVolume + " max is "
289 + mMaxVolume);
290 }
RoboErikb69ffd42014-05-30 14:57:59 -0700291 }
RoboErikb69ffd42014-05-30 14:57:59 -0700292 }
293
294 /**
RoboErika8f95142014-05-05 14:23:49 -0700295 * Check if this session has been set to active by the app.
RoboErik07c70772014-03-20 13:33:52 -0700296 *
RoboErika8f95142014-05-05 14:23:49 -0700297 * @return True if the session is active, false otherwise.
RoboErik07c70772014-03-20 13:33:52 -0700298 */
RoboErika8f95142014-05-05 14:23:49 -0700299 public boolean isActive() {
RoboErik4646d282014-05-13 10:13:04 -0700300 return mIsActive && !mDestroyed;
RoboErika8f95142014-05-05 14:23:49 -0700301 }
302
303 /**
304 * Check if the session is currently performing playback. This will also
305 * return true if the session was recently paused.
306 *
RoboErikb69ffd42014-05-30 14:57:59 -0700307 * @param includeRecentlyActive True if playback that was recently paused
308 * should count, false if it shouldn't.
RoboErika8f95142014-05-05 14:23:49 -0700309 * @return True if the session is performing playback, false otherwise.
310 */
RoboErikb69ffd42014-05-30 14:57:59 -0700311 public boolean isPlaybackActive(boolean includeRecentlyActive) {
RoboErika8f95142014-05-05 14:23:49 -0700312 int state = mPlaybackState == null ? 0 : mPlaybackState.getState();
RoboErik3c45c292014-07-08 16:47:31 -0700313 if (MediaSession.isActiveState(state)) {
RoboErika8f95142014-05-05 14:23:49 -0700314 return true;
315 }
RoboErikb69ffd42014-05-30 14:57:59 -0700316 if (includeRecentlyActive && state == mPlaybackState.STATE_PAUSED) {
RoboErika8f95142014-05-05 14:23:49 -0700317 long inactiveTime = SystemClock.uptimeMillis() - mLastActiveTime;
318 if (inactiveTime < ACTIVE_BUFFER) {
319 return true;
320 }
321 }
322 return false;
323 }
324
RoboErik4646d282014-05-13 10:13:04 -0700325 /**
RoboErikb69ffd42014-05-30 14:57:59 -0700326 * Get the type of playback, either local or remote.
327 *
328 * @return The current type of playback.
329 */
330 public int getPlaybackType() {
RoboErikef3c9e92014-06-19 16:07:28 -0700331 return mVolumeType;
RoboErikb69ffd42014-05-30 14:57:59 -0700332 }
333
334 /**
335 * Get the local audio stream being used. Only valid if playback type is
336 * local.
337 *
338 * @return The audio stream the session is using.
339 */
RoboErik9db9bf72014-07-21 12:44:53 -0700340 public AudioAttributes getAudioAttributes() {
341 return mAudioAttrs;
RoboErikb69ffd42014-05-30 14:57:59 -0700342 }
343
344 /**
345 * Get the type of volume control. Only valid if playback type is remote.
346 *
347 * @return The volume control type being used.
348 */
349 public int getVolumeControl() {
350 return mVolumeControlType;
351 }
352
353 /**
354 * Get the max volume that can be set. Only valid if playback type is
355 * remote.
356 *
357 * @return The max volume that can be set.
358 */
359 public int getMaxVolume() {
360 return mMaxVolume;
361 }
362
363 /**
364 * Get the current volume for this session. Only valid if playback type is
365 * remote.
366 *
367 * @return The current volume of the remote playback.
368 */
369 public int getCurrentVolume() {
370 return mCurrentVolume;
371 }
372
373 /**
RoboErik19c95182014-06-23 15:38:48 -0700374 * Get the volume we'd like it to be set to. This is only valid for a short
375 * while after a call to adjust or set volume.
376 *
377 * @return The current optimistic volume or -1.
378 */
379 public int getOptimisticVolume() {
380 return mOptimisticVolume;
381 }
382
RoboErika8f95142014-05-05 14:23:49 -0700383 public boolean isTransportControlEnabled() {
RoboErik42ea7ee2014-05-16 16:27:35 -0700384 return hasFlag(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
RoboErik07c70772014-03-20 13:33:52 -0700385 }
386
RoboErik01fe6612014-02-13 14:19:04 -0800387 @Override
388 public void binderDied() {
389 mService.sessionDied(this);
390 }
391
RoboErik4646d282014-05-13 10:13:04 -0700392 /**
393 * Finish cleaning up this session, including disconnecting if connected and
394 * removing the death observer from the callback binder.
395 */
396 public void onDestroy() {
397 synchronized (mLock) {
398 if (mDestroyed) {
399 return;
400 }
RoboErik4646d282014-05-13 10:13:04 -0700401 mDestroyed = true;
RoboErik24762bf2014-08-13 15:00:21 -0700402 mHandler.post(MessageHandler.MSG_DESTROYED);
RoboErik4646d282014-05-13 10:13:04 -0700403 }
404 }
405
406 public ISessionCallback getCallback() {
407 return mSessionCb.mCb;
408 }
409
RoboErik418c10c2014-05-19 09:25:25 -0700410 public void sendMediaButton(KeyEvent ke, int sequenceId, ResultReceiver cb) {
411 mSessionCb.sendMediaButton(ke, sequenceId, cb);
RoboErik8a2cfc32014-05-16 11:19:38 -0700412 }
413
RoboErika278ea72014-04-24 14:49:01 -0700414 public void dump(PrintWriter pw, String prefix) {
415 pw.println(prefix + mTag + " " + this);
416
417 final String indent = prefix + " ";
RoboErika5b02322014-05-07 17:05:49 -0700418 pw.println(indent + "ownerPid=" + mOwnerPid + ", ownerUid=" + mOwnerUid
419 + ", userId=" + mUserId);
RoboErikaa4e23b2014-07-24 18:35:11 -0700420 pw.println(indent + "package=" + mPackageName);
RoboErik7aef77b2014-08-08 15:56:54 -0700421 pw.println(indent + "launchIntent=" + mLaunchIntent);
422 pw.println(indent + "mediaButtonReceiver=" + mMediaButtonReceiver);
RoboErik4646d282014-05-13 10:13:04 -0700423 pw.println(indent + "active=" + mIsActive);
RoboErika8f95142014-05-05 14:23:49 -0700424 pw.println(indent + "flags=" + mFlags);
RoboErika278ea72014-04-24 14:49:01 -0700425 pw.println(indent + "rating type=" + mRatingType);
426 pw.println(indent + "controllers: " + mControllerCallbacks.size());
RoboErika8f95142014-05-05 14:23:49 -0700427 pw.println(indent + "state=" + (mPlaybackState == null ? null : mPlaybackState.toString()));
RoboErik7aef77b2014-08-08 15:56:54 -0700428 pw.println(indent + "audioAttrs=" + mAudioAttrs);
429 pw.println(indent + "volumeType=" + mVolumeType + ", controlType=" + mVolumeControlType
430 + ", max=" + mMaxVolume + ", current=" + mCurrentVolume);
RoboErik66dea732014-05-01 16:03:59 -0700431 pw.println(indent + "metadata:" + getShortMetadataString());
RoboErik7aef77b2014-08-08 15:56:54 -0700432 pw.println(indent + "queueTitle=" + mQueueTitle + ", size="
433 + (mQueue == null ? 0 : mQueue.getList().size()));
RoboErika278ea72014-04-24 14:49:01 -0700434 }
435
RoboErikaa4e23b2014-07-24 18:35:11 -0700436 @Override
437 public String toString() {
438 return mPackageName + "/" + mTag;
439 }
440
RoboErik66dea732014-05-01 16:03:59 -0700441 private String getShortMetadataString() {
442 int fields = mMetadata == null ? 0 : mMetadata.size();
RoboErik75847b92014-07-29 13:10:17 -0700443 MediaMetadata.Description description = mMetadata == null ? null : mMetadata
444 .getDescription();
445 return "size=" + fields + ", description=" + description;
RoboErik66dea732014-05-01 16:03:59 -0700446 }
447
RoboErik8ae0f342014-02-24 18:02:08 -0800448 private void pushPlaybackStateUpdate() {
RoboErik07c70772014-03-20 13:33:52 -0700449 synchronized (mLock) {
RoboErik4646d282014-05-13 10:13:04 -0700450 if (mDestroyed) {
451 return;
452 }
RoboErik8ae0f342014-02-24 18:02:08 -0800453 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
RoboErik07c70772014-03-20 13:33:52 -0700454 ISessionControllerCallback cb = mControllerCallbacks.get(i);
RoboErik8ae0f342014-02-24 18:02:08 -0800455 try {
456 cb.onPlaybackStateChanged(mPlaybackState);
RoboErikd3c86422014-06-16 14:00:48 -0700457 } catch (DeadObjectException e) {
RoboErik8ae0f342014-02-24 18:02:08 -0800458 mControllerCallbacks.remove(i);
RoboErik19c95182014-06-23 15:38:48 -0700459 Log.w(TAG, "Removed dead callback in pushPlaybackStateUpdate.", e);
RoboErikd3c86422014-06-16 14:00:48 -0700460 } catch (RemoteException e) {
461 Log.w(TAG, "unexpected exception in pushPlaybackStateUpdate.", e);
RoboErik8ae0f342014-02-24 18:02:08 -0800462 }
463 }
464 }
465 }
466
467 private void pushMetadataUpdate() {
RoboErik07c70772014-03-20 13:33:52 -0700468 synchronized (mLock) {
RoboErik4646d282014-05-13 10:13:04 -0700469 if (mDestroyed) {
470 return;
471 }
RoboErik8ae0f342014-02-24 18:02:08 -0800472 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
RoboErik07c70772014-03-20 13:33:52 -0700473 ISessionControllerCallback cb = mControllerCallbacks.get(i);
RoboErik8ae0f342014-02-24 18:02:08 -0800474 try {
475 cb.onMetadataChanged(mMetadata);
RoboErikd3c86422014-06-16 14:00:48 -0700476 } catch (DeadObjectException e) {
RoboErik19c95182014-06-23 15:38:48 -0700477 Log.w(TAG, "Removing dead callback in pushMetadataUpdate. ", e);
RoboErik8ae0f342014-02-24 18:02:08 -0800478 mControllerCallbacks.remove(i);
RoboErikd3c86422014-06-16 14:00:48 -0700479 } catch (RemoteException e) {
RoboErik19c95182014-06-23 15:38:48 -0700480 Log.w(TAG, "unexpected exception in pushMetadataUpdate. ", e);
481 }
482 }
483 }
484 }
485
Gabriel Pealf0593bc2014-07-22 09:39:06 -0700486 private void pushQueueUpdate() {
487 synchronized (mLock) {
488 if (mDestroyed) {
489 return;
490 }
491 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
492 ISessionControllerCallback cb = mControllerCallbacks.get(i);
493 try {
494 cb.onQueueChanged(mQueue);
495 } catch (DeadObjectException e) {
496 mControllerCallbacks.remove(i);
497 Log.w(TAG, "Removed dead callback in pushQueueUpdate.", e);
498 } catch (RemoteException e) {
499 Log.w(TAG, "unexpected exception in pushQueueUpdate.", e);
500 }
501 }
502 }
503 }
504
505 private void pushQueueTitleUpdate() {
506 synchronized (mLock) {
507 if (mDestroyed) {
508 return;
509 }
510 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
511 ISessionControllerCallback cb = mControllerCallbacks.get(i);
512 try {
513 cb.onQueueTitleChanged(mQueueTitle);
514 } catch (DeadObjectException e) {
515 mControllerCallbacks.remove(i);
516 Log.w(TAG, "Removed dead callback in pushQueueTitleUpdate.", e);
517 } catch (RemoteException e) {
518 Log.w(TAG, "unexpected exception in pushQueueTitleUpdate.", e);
519 }
520 }
521 }
522 }
523
524 private void pushExtrasUpdate() {
525 synchronized (mLock) {
526 if (mDestroyed) {
527 return;
528 }
529 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
530 ISessionControllerCallback cb = mControllerCallbacks.get(i);
531 try {
532 cb.onExtrasChanged(mExtras);
533 } catch (DeadObjectException e) {
534 mControllerCallbacks.remove(i);
535 Log.w(TAG, "Removed dead callback in pushExtrasUpdate.", e);
536 } catch (RemoteException e) {
537 Log.w(TAG, "unexpected exception in pushExtrasUpdate.", e);
538 }
539 }
540 }
541 }
542
RoboErik19c95182014-06-23 15:38:48 -0700543 private void pushVolumeUpdate() {
544 synchronized (mLock) {
545 if (mDestroyed) {
546 return;
547 }
548 ParcelableVolumeInfo info = mController.getVolumeAttributes();
549 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
550 ISessionControllerCallback cb = mControllerCallbacks.get(i);
551 try {
552 cb.onVolumeInfoChanged(info);
553 } catch (DeadObjectException e) {
554 Log.w(TAG, "Removing dead callback in pushVolumeUpdate. ", e);
555 } catch (RemoteException e) {
556 Log.w(TAG, "Unexpected exception in pushVolumeUpdate. ", e);
RoboErik8ae0f342014-02-24 18:02:08 -0800557 }
558 }
559 }
560 }
561
RoboErik8ae0f342014-02-24 18:02:08 -0800562 private void pushEvent(String event, Bundle data) {
RoboErik07c70772014-03-20 13:33:52 -0700563 synchronized (mLock) {
RoboErik4646d282014-05-13 10:13:04 -0700564 if (mDestroyed) {
565 return;
566 }
RoboErik8ae0f342014-02-24 18:02:08 -0800567 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
RoboErik07c70772014-03-20 13:33:52 -0700568 ISessionControllerCallback cb = mControllerCallbacks.get(i);
RoboErik8ae0f342014-02-24 18:02:08 -0800569 try {
570 cb.onEvent(event, data);
RoboErikd3c86422014-06-16 14:00:48 -0700571 } catch (DeadObjectException e) {
572 Log.w(TAG, "Removing dead callback in pushEvent.", e);
573 mControllerCallbacks.remove(i);
RoboErik8ae0f342014-02-24 18:02:08 -0800574 } catch (RemoteException e) {
RoboErikd3c86422014-06-16 14:00:48 -0700575 Log.w(TAG, "unexpected exception in pushEvent.", e);
RoboErik8ae0f342014-02-24 18:02:08 -0800576 }
577 }
578 }
579 }
580
RoboErik24762bf2014-08-13 15:00:21 -0700581 private void pushSessionDestroyed() {
582 synchronized (mLock) {
583 // This is the only method that may be (and can only be) called
584 // after the session is destroyed.
585 if (!mDestroyed) {
586 return;
587 }
588 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
589 ISessionControllerCallback cb = mControllerCallbacks.get(i);
590 try {
591 cb.onSessionDestroyed();
592 } catch (DeadObjectException e) {
593 Log.w(TAG, "Removing dead callback in pushEvent.", e);
594 mControllerCallbacks.remove(i);
595 } catch (RemoteException e) {
596 Log.w(TAG, "unexpected exception in pushEvent.", e);
597 }
598 }
599 // After notifying clear all listeners
600 mControllerCallbacks.clear();
601 }
602 }
603
RoboErikf1372422014-04-23 14:38:17 -0700604 private PlaybackState getStateWithUpdatedPosition() {
605 PlaybackState state = mPlaybackState;
606 long duration = -1;
607 if (mMetadata != null && mMetadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) {
608 duration = mMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
609 }
610 PlaybackState result = null;
611 if (state != null) {
RoboErik79fa4632014-05-27 16:49:09 -0700612 if (state.getState() == PlaybackState.STATE_PLAYING
613 || state.getState() == PlaybackState.STATE_FAST_FORWARDING
614 || state.getState() == PlaybackState.STATE_REWINDING) {
RoboErikf1372422014-04-23 14:38:17 -0700615 long updateTime = state.getLastPositionUpdateTime();
RoboErikc785a782014-07-14 13:40:43 -0700616 long currentTime = SystemClock.elapsedRealtime();
RoboErikf1372422014-04-23 14:38:17 -0700617 if (updateTime > 0) {
RoboErikc785a782014-07-14 13:40:43 -0700618 long position = (long) (state.getPlaybackSpeed()
619 * (currentTime - updateTime)) + state.getPosition();
RoboErikf1372422014-04-23 14:38:17 -0700620 if (duration >= 0 && position > duration) {
621 position = duration;
622 } else if (position < 0) {
623 position = 0;
624 }
RoboErikc785a782014-07-14 13:40:43 -0700625 PlaybackState.Builder builder = new PlaybackState.Builder(state);
626 builder.setState(state.getState(), position, state.getPlaybackSpeed(),
627 currentTime);
628 result = builder.build();
RoboErikf1372422014-04-23 14:38:17 -0700629 }
630 }
631 }
632 return result == null ? state : result;
633 }
634
RoboErikd3c86422014-06-16 14:00:48 -0700635 private int getControllerCbIndexForCb(ISessionControllerCallback cb) {
636 IBinder binder = cb.asBinder();
637 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
638 if (binder.equals(mControllerCallbacks.get(i).asBinder())) {
639 return i;
640 }
641 }
642 return -1;
643 }
644
RoboErik19c95182014-06-23 15:38:48 -0700645 private final Runnable mClearOptimisticVolumeRunnable = new Runnable() {
646 @Override
647 public void run() {
648 boolean needUpdate = (mOptimisticVolume != mCurrentVolume);
649 mOptimisticVolume = -1;
650 if (needUpdate) {
651 pushVolumeUpdate();
652 }
653 }
654 };
655
RoboErik07c70772014-03-20 13:33:52 -0700656 private final class SessionStub extends ISession.Stub {
RoboErik01fe6612014-02-13 14:19:04 -0800657 @Override
RoboErik8ae0f342014-02-24 18:02:08 -0800658 public void destroy() {
RoboErik4646d282014-05-13 10:13:04 -0700659 mService.destroySession(MediaSessionRecord.this);
RoboErik01fe6612014-02-13 14:19:04 -0800660 }
661
662 @Override
RoboErik8ae0f342014-02-24 18:02:08 -0800663 public void sendEvent(String event, Bundle data) {
664 mHandler.post(MessageHandler.MSG_SEND_EVENT, event, data);
RoboErik01fe6612014-02-13 14:19:04 -0800665 }
666
667 @Override
RoboErik07c70772014-03-20 13:33:52 -0700668 public ISessionController getController() {
RoboErik01fe6612014-02-13 14:19:04 -0800669 return mController;
670 }
671
672 @Override
RoboErika8f95142014-05-05 14:23:49 -0700673 public void setActive(boolean active) {
674 mIsActive = active;
675 mService.updateSession(MediaSessionRecord.this);
676 mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE);
RoboErik01fe6612014-02-13 14:19:04 -0800677 }
678
RoboErik8ae0f342014-02-24 18:02:08 -0800679 @Override
RoboErika8f95142014-05-05 14:23:49 -0700680 public void setFlags(int flags) {
RoboErik42ea7ee2014-05-16 16:27:35 -0700681 if ((flags & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
RoboErike7880d82014-04-30 12:48:25 -0700682 int pid = getCallingPid();
683 int uid = getCallingUid();
684 mService.enforcePhoneStatePermission(pid, uid);
685 }
RoboErike7880d82014-04-30 12:48:25 -0700686 mFlags = flags;
RoboErika8f95142014-05-05 14:23:49 -0700687 mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE);
RoboErike7880d82014-04-30 12:48:25 -0700688 }
689
690 @Override
RoboErikb214efb2014-07-24 13:20:30 -0700691 public void setMediaButtonReceiver(PendingIntent pi) {
692 mMediaButtonReceiver = pi;
RoboErik6f0e4dd2014-06-17 16:56:27 -0700693 }
694
695 @Override
RoboErike34c09d2014-07-24 10:20:41 -0700696 public void setLaunchPendingIntent(PendingIntent pi) {
697 mLaunchIntent = pi;
698 }
699
700 @Override
RoboErik8ae0f342014-02-24 18:02:08 -0800701 public void setMetadata(MediaMetadata metadata) {
702 mMetadata = metadata;
703 mHandler.post(MessageHandler.MSG_UPDATE_METADATA);
704 }
705
706 @Override
707 public void setPlaybackState(PlaybackState state) {
RoboErika8f95142014-05-05 14:23:49 -0700708 int oldState = mPlaybackState == null ? 0 : mPlaybackState.getState();
709 int newState = state == null ? 0 : state.getState();
RoboErik3c45c292014-07-08 16:47:31 -0700710 if (MediaSession.isActiveState(oldState) && newState == PlaybackState.STATE_PAUSED) {
RoboErika8f95142014-05-05 14:23:49 -0700711 mLastActiveTime = SystemClock.elapsedRealtime();
712 }
RoboErik8ae0f342014-02-24 18:02:08 -0800713 mPlaybackState = state;
RoboErika8f95142014-05-05 14:23:49 -0700714 mService.onSessionPlaystateChange(MediaSessionRecord.this, oldState, newState);
RoboErik8ae0f342014-02-24 18:02:08 -0800715 mHandler.post(MessageHandler.MSG_UPDATE_PLAYBACK_STATE);
716 }
717
718 @Override
Gabriel Pealf0593bc2014-07-22 09:39:06 -0700719 public void setQueue(ParceledListSlice queue) {
720 mQueue = queue;
721 mHandler.post(MessageHandler.MSG_UPDATE_QUEUE);
722 }
723
724 @Override
725 public void setQueueTitle(CharSequence title) {
726 mQueueTitle = title;
727 mHandler.post(MessageHandler.MSG_UPDATE_QUEUE_TITLE);
728 }
729
730 @Override
731 public void setExtras(Bundle extras) {
732 mExtras = extras;
733 mHandler.post(MessageHandler.MSG_UPDATE_EXTRAS);
734 }
735
736 @Override
RoboErik8ae0f342014-02-24 18:02:08 -0800737 public void setRatingType(int type) {
738 mRatingType = type;
739 }
RoboErik07c70772014-03-20 13:33:52 -0700740
741 @Override
RoboErikb69ffd42014-05-30 14:57:59 -0700742 public void setCurrentVolume(int volume) {
743 mCurrentVolume = volume;
RoboErik19c95182014-06-23 15:38:48 -0700744 mHandler.post(MessageHandler.MSG_UPDATE_VOLUME);
RoboErikb69ffd42014-05-30 14:57:59 -0700745 }
746
747 @Override
RoboErik9db9bf72014-07-21 12:44:53 -0700748 public void setPlaybackToLocal(AudioAttributes attributes) {
749 boolean typeChanged;
750 synchronized (mLock) {
751 typeChanged = mVolumeType == MediaSession.PLAYBACK_TYPE_REMOTE;
752 mVolumeType = MediaSession.PLAYBACK_TYPE_LOCAL;
753 if (attributes != null) {
754 mAudioAttrs = attributes;
755 } else {
756 Log.e(TAG, "Received null audio attributes, using existing attributes");
757 }
RoboErikb69ffd42014-05-30 14:57:59 -0700758 }
RoboErik19c95182014-06-23 15:38:48 -0700759 if (typeChanged) {
760 mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this);
761 }
RoboErikb69ffd42014-05-30 14:57:59 -0700762 }
763
RoboErik9db9bf72014-07-21 12:44:53 -0700764 @Override
765 public void setPlaybackToRemote(int control, int max) {
766 boolean typeChanged;
767 synchronized (mLock) {
768 typeChanged = mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL;
769 mVolumeType = MediaSession.PLAYBACK_TYPE_REMOTE;
770 mVolumeControlType = control;
771 mMaxVolume = max;
772 }
773 if (typeChanged) {
774 mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this);
775 }
RoboErikb69ffd42014-05-30 14:57:59 -0700776 }
RoboErik01fe6612014-02-13 14:19:04 -0800777 }
778
779 class SessionCb {
RoboErik07c70772014-03-20 13:33:52 -0700780 private final ISessionCallback mCb;
RoboErik01fe6612014-02-13 14:19:04 -0800781
RoboErik07c70772014-03-20 13:33:52 -0700782 public SessionCb(ISessionCallback cb) {
RoboErik01fe6612014-02-13 14:19:04 -0800783 mCb = cb;
784 }
785
RoboErik79fa4632014-05-27 16:49:09 -0700786 public boolean sendMediaButton(KeyEvent keyEvent, int sequenceId, ResultReceiver cb) {
RoboErik01fe6612014-02-13 14:19:04 -0800787 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
788 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
789 try {
RoboErik418c10c2014-05-19 09:25:25 -0700790 mCb.onMediaButton(mediaButtonIntent, sequenceId, cb);
RoboErik79fa4632014-05-27 16:49:09 -0700791 return true;
RoboErik01fe6612014-02-13 14:19:04 -0800792 } catch (RemoteException e) {
RoboErik8ae0f342014-02-24 18:02:08 -0800793 Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
RoboErik01fe6612014-02-13 14:19:04 -0800794 }
RoboErik79fa4632014-05-27 16:49:09 -0700795 return false;
RoboErik01fe6612014-02-13 14:19:04 -0800796 }
797
Gabriel Pealf364f942014-07-22 09:39:06 -0700798 public void sendCommand(String command, Bundle args, ResultReceiver cb) {
RoboErik01fe6612014-02-13 14:19:04 -0800799 try {
Gabriel Pealf364f942014-07-22 09:39:06 -0700800 mCb.onCommand(command, args, cb);
RoboErik01fe6612014-02-13 14:19:04 -0800801 } catch (RemoteException e) {
RoboErik8ae0f342014-02-24 18:02:08 -0800802 Slog.e(TAG, "Remote failure in sendCommand.", e);
RoboErik01fe6612014-02-13 14:19:04 -0800803 }
804 }
805
Gabriel Pealf364f942014-07-22 09:39:06 -0700806 public void sendCustomAction(String action, Bundle args) {
807 try {
808 mCb.onCustomAction(action, args);
809 } catch (RemoteException e) {
810 Slog.e(TAG, "Remote failure in sendCustomAction.", e);
811 }
812 }
813
RoboErik8ae0f342014-02-24 18:02:08 -0800814 public void play() {
815 try {
816 mCb.onPlay();
817 } catch (RemoteException e) {
818 Slog.e(TAG, "Remote failure in play.", e);
819 }
RoboErik01fe6612014-02-13 14:19:04 -0800820 }
821
Gabriel Pealf0593bc2014-07-22 09:39:06 -0700822 public void playUri(Uri uri, Bundle extras) {
823 try {
824 mCb.onPlayUri(uri, extras);
825 } catch (RemoteException e) {
826 Slog.e(TAG, "Remote failure in playUri.", e);
827 }
828 }
829
830 public void playFromSearch(String query, Bundle extras) {
831 try {
832 mCb.onPlayFromSearch(query, extras);
833 } catch (RemoteException e) {
834 Slog.e(TAG, "Remote failure in playFromSearch.", e);
835 }
836 }
837
838 public void skipToTrack(long id) {
839 try {
840 mCb.onSkipToTrack(id);
841 } catch (RemoteException e) {
842 Slog.e(TAG, "Remote failure in skipToTrack", e);
843 }
844 }
845
RoboErik8ae0f342014-02-24 18:02:08 -0800846 public void pause() {
847 try {
848 mCb.onPause();
849 } catch (RemoteException e) {
850 Slog.e(TAG, "Remote failure in pause.", e);
851 }
852 }
853
854 public void stop() {
855 try {
856 mCb.onStop();
857 } catch (RemoteException e) {
858 Slog.e(TAG, "Remote failure in stop.", e);
859 }
860 }
861
862 public void next() {
863 try {
864 mCb.onNext();
865 } catch (RemoteException e) {
866 Slog.e(TAG, "Remote failure in next.", e);
867 }
868 }
869
870 public void previous() {
871 try {
872 mCb.onPrevious();
873 } catch (RemoteException e) {
874 Slog.e(TAG, "Remote failure in previous.", e);
875 }
876 }
877
878 public void fastForward() {
879 try {
880 mCb.onFastForward();
881 } catch (RemoteException e) {
882 Slog.e(TAG, "Remote failure in fastForward.", e);
883 }
884 }
885
886 public void rewind() {
887 try {
888 mCb.onRewind();
889 } catch (RemoteException e) {
890 Slog.e(TAG, "Remote failure in rewind.", e);
891 }
892 }
893
894 public void seekTo(long pos) {
895 try {
896 mCb.onSeekTo(pos);
897 } catch (RemoteException e) {
898 Slog.e(TAG, "Remote failure in seekTo.", e);
899 }
900 }
901
902 public void rate(Rating rating) {
903 try {
904 mCb.onRate(rating);
905 } catch (RemoteException e) {
906 Slog.e(TAG, "Remote failure in rate.", e);
907 }
908 }
RoboErikb69ffd42014-05-30 14:57:59 -0700909
RoboErik1ff5b162014-07-15 17:23:18 -0700910 public void adjustVolume(int direction) {
RoboErikb69ffd42014-05-30 14:57:59 -0700911 try {
RoboErik1ff5b162014-07-15 17:23:18 -0700912 mCb.onAdjustVolume(direction);
RoboErikb69ffd42014-05-30 14:57:59 -0700913 } catch (RemoteException e) {
RoboErik1ff5b162014-07-15 17:23:18 -0700914 Slog.e(TAG, "Remote failure in adjustVolume.", e);
RoboErikb69ffd42014-05-30 14:57:59 -0700915 }
916 }
917
918 public void setVolumeTo(int value) {
919 try {
920 mCb.onSetVolumeTo(value);
921 } catch (RemoteException e) {
RoboErik1ff5b162014-07-15 17:23:18 -0700922 Slog.e(TAG, "Remote failure in setVolumeTo.", e);
RoboErikb69ffd42014-05-30 14:57:59 -0700923 }
924 }
RoboErik01fe6612014-02-13 14:19:04 -0800925 }
926
RoboErik07c70772014-03-20 13:33:52 -0700927 class ControllerStub extends ISessionController.Stub {
RoboErik01fe6612014-02-13 14:19:04 -0800928 @Override
Gabriel Pealf364f942014-07-22 09:39:06 -0700929 public void sendCommand(String command, Bundle args, ResultReceiver cb)
RoboErik8ae0f342014-02-24 18:02:08 -0800930 throws RemoteException {
Gabriel Pealf364f942014-07-22 09:39:06 -0700931 mSessionCb.sendCommand(command, args, cb);
RoboErik01fe6612014-02-13 14:19:04 -0800932 }
933
934 @Override
RoboErik79fa4632014-05-27 16:49:09 -0700935 public boolean sendMediaButton(KeyEvent mediaButtonIntent) {
936 return mSessionCb.sendMediaButton(mediaButtonIntent, 0, null);
RoboErik01fe6612014-02-13 14:19:04 -0800937 }
938
RoboErik01fe6612014-02-13 14:19:04 -0800939 @Override
RoboErik07c70772014-03-20 13:33:52 -0700940 public void registerCallbackListener(ISessionControllerCallback cb) {
941 synchronized (mLock) {
RoboErik24762bf2014-08-13 15:00:21 -0700942 // If this session is already destroyed tell the caller and
943 // don't add them.
944 if (mDestroyed) {
945 try {
946 cb.onSessionDestroyed();
947 } catch (Exception e) {
948 // ignored
949 }
950 return;
951 }
RoboErikd3c86422014-06-16 14:00:48 -0700952 if (getControllerCbIndexForCb(cb) < 0) {
RoboErik8ae0f342014-02-24 18:02:08 -0800953 mControllerCallbacks.add(cb);
RoboErikd3c86422014-06-16 14:00:48 -0700954 if (DEBUG) {
955 Log.d(TAG, "registering controller callback " + cb);
956 }
RoboErik8ae0f342014-02-24 18:02:08 -0800957 }
RoboErik01fe6612014-02-13 14:19:04 -0800958 }
959 }
960
RoboErik01fe6612014-02-13 14:19:04 -0800961 @Override
RoboErik07c70772014-03-20 13:33:52 -0700962 public void unregisterCallbackListener(ISessionControllerCallback cb)
RoboErik01fe6612014-02-13 14:19:04 -0800963 throws RemoteException {
RoboErik07c70772014-03-20 13:33:52 -0700964 synchronized (mLock) {
RoboErikd3c86422014-06-16 14:00:48 -0700965 int index = getControllerCbIndexForCb(cb);
966 if (index != -1) {
967 mControllerCallbacks.remove(index);
968 }
969 if (DEBUG) {
970 Log.d(TAG, "unregistering callback " + cb + ". index=" + index);
971 }
RoboErik8ae0f342014-02-24 18:02:08 -0800972 }
RoboErik01fe6612014-02-13 14:19:04 -0800973 }
974
RoboErik01fe6612014-02-13 14:19:04 -0800975 @Override
RoboErikaa4e23b2014-07-24 18:35:11 -0700976 public String getPackageName() {
977 return mPackageName;
978 }
979
980 @Override
981 public String getTag() {
982 return mTag;
RoboErikfb442b02014-06-08 11:15:01 -0700983 }
984
985 @Override
RoboErike34c09d2014-07-24 10:20:41 -0700986 public PendingIntent getLaunchPendingIntent() {
987 return mLaunchIntent;
988 }
989
990 @Override
RoboErik73e23e22014-06-10 17:10:41 -0700991 public long getFlags() {
992 return mFlags;
993 }
994
995 @Override
RoboErikef3c9e92014-06-19 16:07:28 -0700996 public ParcelableVolumeInfo getVolumeAttributes() {
997 synchronized (mLock) {
998 int type;
999 int max;
1000 int current;
RoboErik19c95182014-06-23 15:38:48 -07001001 if (mVolumeType == MediaSession.PLAYBACK_TYPE_REMOTE) {
RoboErikef3c9e92014-06-19 16:07:28 -07001002 type = mVolumeControlType;
1003 max = mMaxVolume;
RoboErik19c95182014-06-23 15:38:48 -07001004 current = mOptimisticVolume != -1 ? mOptimisticVolume
1005 : mCurrentVolume;
RoboErikef3c9e92014-06-19 16:07:28 -07001006 } else {
RoboErik9db9bf72014-07-21 12:44:53 -07001007 int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
RoboErikef3c9e92014-06-19 16:07:28 -07001008 type = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
RoboErik9db9bf72014-07-21 12:44:53 -07001009 max = mAudioManager.getStreamMaxVolume(stream);
1010 current = mAudioManager.getStreamVolume(stream);
RoboErikef3c9e92014-06-19 16:07:28 -07001011 }
RoboErik9db9bf72014-07-21 12:44:53 -07001012 return new ParcelableVolumeInfo(mVolumeType, mAudioAttrs, type, max, current);
RoboErikef3c9e92014-06-19 16:07:28 -07001013 }
1014 }
1015
1016 @Override
RoboErik0dac35a2014-08-12 15:48:49 -07001017 public void adjustVolume(int direction, int flags, String packageName) {
1018 int uid = Binder.getCallingUid();
RoboErikf77b99f2014-06-27 11:38:59 -07001019 final long token = Binder.clearCallingIdentity();
1020 try {
RoboErik0dac35a2014-08-12 15:48:49 -07001021 MediaSessionRecord.this.adjustVolume(direction, flags, packageName, uid);
RoboErikf77b99f2014-06-27 11:38:59 -07001022 } finally {
1023 Binder.restoreCallingIdentity(token);
1024 }
RoboErikef3c9e92014-06-19 16:07:28 -07001025 }
1026
1027 @Override
RoboErik0dac35a2014-08-12 15:48:49 -07001028 public void setVolumeTo(int value, int flags, String packageName) {
1029 int uid = Binder.getCallingUid();
RoboErikf77b99f2014-06-27 11:38:59 -07001030 final long token = Binder.clearCallingIdentity();
1031 try {
RoboErik0dac35a2014-08-12 15:48:49 -07001032 MediaSessionRecord.this.setVolumeTo(value, flags, packageName, uid);
RoboErikf77b99f2014-06-27 11:38:59 -07001033 } finally {
1034 Binder.restoreCallingIdentity(token);
1035 }
RoboErikef3c9e92014-06-19 16:07:28 -07001036 }
1037
1038 @Override
RoboErik8ae0f342014-02-24 18:02:08 -08001039 public void play() throws RemoteException {
1040 mSessionCb.play();
1041 }
1042
1043 @Override
Gabriel Pealf0593bc2014-07-22 09:39:06 -07001044 public void playUri(Uri uri, Bundle extras) throws RemoteException {
1045 mSessionCb.playUri(uri, extras);
1046 }
1047
1048 @Override
1049 public void playFromSearch(String query, Bundle extras) throws RemoteException {
1050 mSessionCb.playFromSearch(query, extras);
1051 }
1052
1053 @Override
1054 public void skipToTrack(long id) {
1055 mSessionCb.skipToTrack(id);
1056 }
1057
1058
1059 @Override
RoboErik8ae0f342014-02-24 18:02:08 -08001060 public void pause() throws RemoteException {
1061 mSessionCb.pause();
1062 }
1063
1064 @Override
1065 public void stop() throws RemoteException {
1066 mSessionCb.stop();
1067 }
1068
1069 @Override
1070 public void next() throws RemoteException {
1071 mSessionCb.next();
1072 }
1073
1074 @Override
1075 public void previous() throws RemoteException {
1076 mSessionCb.previous();
1077 }
1078
1079 @Override
1080 public void fastForward() throws RemoteException {
1081 mSessionCb.fastForward();
1082 }
1083
1084 @Override
1085 public void rewind() throws RemoteException {
1086 mSessionCb.rewind();
1087 }
1088
1089 @Override
1090 public void seekTo(long pos) throws RemoteException {
1091 mSessionCb.seekTo(pos);
1092 }
1093
1094 @Override
1095 public void rate(Rating rating) throws RemoteException {
1096 mSessionCb.rate(rating);
1097 }
1098
Gabriel Pealf364f942014-07-22 09:39:06 -07001099 @Override
1100 public void sendCustomAction(String action, Bundle args)
1101 throws RemoteException {
1102 mSessionCb.sendCustomAction(action, args);
1103 }
1104
RoboErik8ae0f342014-02-24 18:02:08 -08001105
1106 @Override
1107 public MediaMetadata getMetadata() {
1108 return mMetadata;
1109 }
1110
1111 @Override
1112 public PlaybackState getPlaybackState() {
RoboErikf1372422014-04-23 14:38:17 -07001113 return getStateWithUpdatedPosition();
RoboErik01fe6612014-02-13 14:19:04 -08001114 }
RoboErik8ae0f342014-02-24 18:02:08 -08001115
1116 @Override
Gabriel Pealf0593bc2014-07-22 09:39:06 -07001117 public ParceledListSlice getQueue() {
1118 return mQueue;
1119 }
1120
1121 @Override
1122 public CharSequence getQueueTitle() {
1123 return mQueueTitle;
1124 }
1125
1126 @Override
1127 public Bundle getExtras() {
1128 return mExtras;
1129 }
1130
1131 @Override
RoboErik8ae0f342014-02-24 18:02:08 -08001132 public int getRatingType() {
1133 return mRatingType;
1134 }
1135
1136 @Override
RoboErik07c70772014-03-20 13:33:52 -07001137 public boolean isTransportControlEnabled() {
RoboErika8f95142014-05-05 14:23:49 -07001138 return MediaSessionRecord.this.isTransportControlEnabled();
RoboErik8ae0f342014-02-24 18:02:08 -08001139 }
1140 }
1141
1142 private class MessageHandler extends Handler {
1143 private static final int MSG_UPDATE_METADATA = 1;
1144 private static final int MSG_UPDATE_PLAYBACK_STATE = 2;
Gabriel Pealf0593bc2014-07-22 09:39:06 -07001145 private static final int MSG_UPDATE_QUEUE = 3;
1146 private static final int MSG_UPDATE_QUEUE_TITLE = 4;
1147 private static final int MSG_UPDATE_EXTRAS = 5;
1148 private static final int MSG_SEND_EVENT = 6;
1149 private static final int MSG_UPDATE_SESSION_STATE = 7;
1150 private static final int MSG_UPDATE_VOLUME = 8;
RoboErik24762bf2014-08-13 15:00:21 -07001151 private static final int MSG_DESTROYED = 9;
RoboErik8ae0f342014-02-24 18:02:08 -08001152
1153 public MessageHandler(Looper looper) {
1154 super(looper);
1155 }
1156 @Override
1157 public void handleMessage(Message msg) {
1158 switch (msg.what) {
1159 case MSG_UPDATE_METADATA:
1160 pushMetadataUpdate();
1161 break;
1162 case MSG_UPDATE_PLAYBACK_STATE:
1163 pushPlaybackStateUpdate();
1164 break;
Gabriel Pealf0593bc2014-07-22 09:39:06 -07001165 case MSG_UPDATE_QUEUE:
1166 pushQueueUpdate();
1167 break;
1168 case MSG_UPDATE_QUEUE_TITLE:
1169 pushQueueTitleUpdate();
1170 break;
1171 case MSG_UPDATE_EXTRAS:
1172 pushExtrasUpdate();
1173 break;
RoboErik8ae0f342014-02-24 18:02:08 -08001174 case MSG_SEND_EVENT:
1175 pushEvent((String) msg.obj, msg.getData());
1176 break;
RoboErika8f95142014-05-05 14:23:49 -07001177 case MSG_UPDATE_SESSION_STATE:
1178 // TODO add session state
1179 break;
RoboErik19c95182014-06-23 15:38:48 -07001180 case MSG_UPDATE_VOLUME:
1181 pushVolumeUpdate();
1182 break;
RoboErik24762bf2014-08-13 15:00:21 -07001183 case MSG_DESTROYED:
1184 pushSessionDestroyed();
RoboErik8ae0f342014-02-24 18:02:08 -08001185 }
1186 }
1187
1188 public void post(int what) {
1189 post(what, null);
1190 }
1191
1192 public void post(int what, Object obj) {
1193 obtainMessage(what, obj).sendToTarget();
1194 }
1195
1196 public void post(int what, Object obj, Bundle data) {
1197 Message msg = obtainMessage(what, obj);
1198 msg.setData(data);
1199 msg.sendToTarget();
1200 }
RoboErik01fe6612014-02-13 14:19:04 -08001201 }
1202
1203}