1 /* 2 * Copyright 2018 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.media; 18 19 import android.app.PendingIntent; 20 import android.content.Context; 21 import android.media.MediaController2; 22 import android.media.MediaItem2; 23 import android.media.MediaMetadata2; 24 import android.media.SessionCommand2; 25 import android.media.MediaSession2.CommandButton; 26 import android.media.SessionCommandGroup2; 27 import android.os.Bundle; 28 import android.os.ResultReceiver; 29 import android.text.TextUtils; 30 import android.util.Log; 31 32 import com.android.media.MediaController2Impl.PlaybackInfoImpl; 33 import com.android.media.MediaSession2Impl.CommandButtonImpl; 34 35 import java.lang.ref.WeakReference; 36 import java.util.ArrayList; 37 import java.util.List; 38 39 public class MediaController2Stub extends IMediaController2.Stub { 40 private static final String TAG = "MediaController2Stub"; 41 private static final boolean DEBUG = true; // TODO(jaewan): Change 42 43 private final WeakReference<MediaController2Impl> mController; 44 MediaController2Stub(MediaController2Impl controller)45 MediaController2Stub(MediaController2Impl controller) { 46 mController = new WeakReference<>(controller); 47 } 48 getController()49 private MediaController2Impl getController() throws IllegalStateException { 50 final MediaController2Impl controller = mController.get(); 51 if (controller == null) { 52 throw new IllegalStateException("Controller is released"); 53 } 54 return controller; 55 } 56 getBrowser()57 private MediaBrowser2Impl getBrowser() throws IllegalStateException { 58 final MediaController2Impl controller = getController(); 59 if (controller instanceof MediaBrowser2Impl) { 60 return (MediaBrowser2Impl) controller; 61 } 62 return null; 63 } 64 destroy()65 public void destroy() { 66 mController.clear(); 67 } 68 69 @Override onPlayerStateChanged(int state)70 public void onPlayerStateChanged(int state) { 71 final MediaController2Impl controller; 72 try { 73 controller = getController(); 74 } catch (IllegalStateException e) { 75 Log.w(TAG, "Don't fail silently here. Highly likely a bug"); 76 return; 77 } 78 controller.pushPlayerStateChanges(state); 79 } 80 81 @Override onPositionChanged(long eventTimeMs, long positionMs)82 public void onPositionChanged(long eventTimeMs, long positionMs) { 83 final MediaController2Impl controller; 84 try { 85 controller = getController(); 86 } catch (IllegalStateException e) { 87 Log.w(TAG, "Don't fail silently here. Highly likely a bug"); 88 return; 89 } 90 if (eventTimeMs < 0) { 91 Log.w(TAG, "onPositionChanged(): Ignoring negative eventTimeMs"); 92 return; 93 } 94 if (positionMs < 0) { 95 Log.w(TAG, "onPositionChanged(): Ignoring negative positionMs"); 96 return; 97 } 98 controller.pushPositionChanges(eventTimeMs, positionMs); 99 } 100 101 @Override onPlaybackSpeedChanged(float speed)102 public void onPlaybackSpeedChanged(float speed) { 103 final MediaController2Impl controller; 104 try { 105 controller = getController(); 106 } catch (IllegalStateException e) { 107 Log.w(TAG, "Don't fail silently here. Highly likely a bug"); 108 return; 109 } 110 controller.pushPlaybackSpeedChanges(speed); 111 } 112 113 @Override onBufferedPositionChanged(long bufferedPositionMs)114 public void onBufferedPositionChanged(long bufferedPositionMs) { 115 final MediaController2Impl controller; 116 try { 117 controller = getController(); 118 } catch (IllegalStateException e) { 119 Log.w(TAG, "Don't fail silently here. Highly likely a bug"); 120 return; 121 } 122 if (bufferedPositionMs < 0) { 123 Log.w(TAG, "onBufferedPositionChanged(): Ignoring negative bufferedPositionMs"); 124 return; 125 } 126 controller.pushBufferedPositionChanges(bufferedPositionMs); 127 } 128 129 @Override onPlaylistChanged(List<Bundle> playlistBundle, Bundle metadataBundle)130 public void onPlaylistChanged(List<Bundle> playlistBundle, Bundle metadataBundle) { 131 final MediaController2Impl controller; 132 try { 133 controller = getController(); 134 } catch (IllegalStateException e) { 135 Log.w(TAG, "Don't fail silently here. Highly likely a bug"); 136 return; 137 } 138 if (playlistBundle == null) { 139 Log.w(TAG, "onPlaylistChanged(): Ignoring null playlist from " + controller); 140 return; 141 } 142 List<MediaItem2> playlist = new ArrayList<>(); 143 for (Bundle bundle : playlistBundle) { 144 MediaItem2 item = MediaItem2.fromBundle(bundle); 145 if (item == null) { 146 Log.w(TAG, "onPlaylistChanged(): Ignoring null item in playlist"); 147 } else { 148 playlist.add(item); 149 } 150 } 151 MediaMetadata2 metadata = MediaMetadata2.fromBundle(metadataBundle); 152 controller.pushPlaylistChanges(playlist, metadata); 153 } 154 155 @Override onPlaylistMetadataChanged(Bundle metadataBundle)156 public void onPlaylistMetadataChanged(Bundle metadataBundle) throws RuntimeException { 157 final MediaController2Impl controller; 158 try { 159 controller = getController(); 160 } catch (IllegalStateException e) { 161 Log.w(TAG, "Don't fail silently here. Highly likely a bug"); 162 return; 163 } 164 MediaMetadata2 metadata = MediaMetadata2.fromBundle(metadataBundle); 165 controller.pushPlaylistMetadataChanges(metadata); 166 } 167 168 @Override onRepeatModeChanged(int repeatMode)169 public void onRepeatModeChanged(int repeatMode) { 170 final MediaController2Impl controller; 171 try { 172 controller = getController(); 173 } catch (IllegalStateException e) { 174 Log.w(TAG, "Don't fail silently here. Highly likely a bug"); 175 return; 176 } 177 controller.pushRepeatModeChanges(repeatMode); 178 } 179 180 @Override onPlaybackInfoChanged(Bundle playbackInfo)181 public void onPlaybackInfoChanged(Bundle playbackInfo) throws RuntimeException { 182 if (DEBUG) { 183 Log.d(TAG, "onPlaybackInfoChanged"); 184 } 185 final MediaController2Impl controller; 186 try { 187 controller = getController(); 188 } catch (IllegalStateException e) { 189 Log.w(TAG, "Don't fail silently here. Highly likely a bug"); 190 return; 191 } 192 MediaController2.PlaybackInfo info = PlaybackInfoImpl.fromBundle(playbackInfo); 193 if (info == null) { 194 Log.w(TAG, "onPlaybackInfoChanged(): Ignoring null playbackInfo"); 195 return; 196 } 197 controller.pushPlaybackInfoChanges(info); 198 } 199 200 @Override onShuffleModeChanged(int shuffleMode)201 public void onShuffleModeChanged(int shuffleMode) { 202 final MediaController2Impl controller; 203 try { 204 controller = getController(); 205 } catch (IllegalStateException e) { 206 Log.w(TAG, "Don't fail silently here. Highly likely a bug"); 207 return; 208 } 209 controller.pushShuffleModeChanges(shuffleMode); 210 } 211 212 @Override onError(int errorCode, Bundle extras)213 public void onError(int errorCode, Bundle extras) { 214 final MediaController2Impl controller; 215 try { 216 controller = getController(); 217 } catch (IllegalStateException e) { 218 Log.w(TAG, "Don't fail silently here. Highly likely a bug"); 219 return; 220 } 221 controller.pushError(errorCode, extras); 222 } 223 224 @Override onConnected(IMediaSession2 sessionBinder, Bundle commandGroup, int playerState, long positionEventTimeMs, long positionMs, float playbackSpeed, long bufferedPositionMs, Bundle playbackInfo, int shuffleMode, int repeatMode, List<Bundle> itemBundleList, PendingIntent sessionActivity)225 public void onConnected(IMediaSession2 sessionBinder, Bundle commandGroup, 226 int playerState, long positionEventTimeMs, long positionMs, float playbackSpeed, 227 long bufferedPositionMs, Bundle playbackInfo, int shuffleMode, int repeatMode, 228 List<Bundle> itemBundleList, PendingIntent sessionActivity) { 229 final MediaController2Impl controller = mController.get(); 230 if (controller == null) { 231 if (DEBUG) { 232 Log.d(TAG, "onConnected after MediaController2.close()"); 233 } 234 return; 235 } 236 final Context context = controller.getContext(); 237 List<MediaItem2> itemList = null; 238 if (itemBundleList != null) { 239 itemList = new ArrayList<>(); 240 for (int i = 0; i < itemBundleList.size(); i++) { 241 MediaItem2 item = MediaItem2.fromBundle(itemBundleList.get(i)); 242 if (item != null) { 243 itemList.add(item); 244 } 245 } 246 } 247 controller.onConnectedNotLocked(sessionBinder, 248 SessionCommandGroup2.fromBundle(commandGroup), 249 playerState, positionEventTimeMs, positionMs, playbackSpeed, bufferedPositionMs, 250 PlaybackInfoImpl.fromBundle(playbackInfo), repeatMode, shuffleMode, 251 itemList, sessionActivity); 252 } 253 254 @Override onDisconnected()255 public void onDisconnected() { 256 final MediaController2Impl controller = mController.get(); 257 if (controller == null) { 258 if (DEBUG) { 259 Log.d(TAG, "onDisconnected after MediaController2.close()"); 260 } 261 return; 262 } 263 controller.getInstance().close(); 264 } 265 266 @Override onCustomLayoutChanged(List<Bundle> commandButtonlist)267 public void onCustomLayoutChanged(List<Bundle> commandButtonlist) { 268 if (commandButtonlist == null) { 269 Log.w(TAG, "onCustomLayoutChanged(): Ignoring null commandButtonlist"); 270 return; 271 } 272 final MediaController2Impl controller; 273 try { 274 controller = getController(); 275 } catch (IllegalStateException e) { 276 Log.w(TAG, "Don't fail silently here. Highly likely a bug"); 277 return; 278 } 279 if (controller == null) { 280 // TODO(jaewan): Revisit here. Could be a bug 281 return; 282 } 283 List<CommandButton> layout = new ArrayList<>(); 284 for (int i = 0; i < commandButtonlist.size(); i++) { 285 CommandButton button = CommandButtonImpl.fromBundle(commandButtonlist.get(i)); 286 if (button != null) { 287 layout.add(button); 288 } 289 } 290 controller.onCustomLayoutChanged(layout); 291 } 292 293 @Override onAllowedCommandsChanged(Bundle commandsBundle)294 public void onAllowedCommandsChanged(Bundle commandsBundle) { 295 final MediaController2Impl controller; 296 try { 297 controller = getController(); 298 } catch (IllegalStateException e) { 299 Log.w(TAG, "Don't fail silently here. Highly likely a bug"); 300 return; 301 } 302 if (controller == null) { 303 // TODO(jaewan): Revisit here. Could be a bug 304 return; 305 } 306 SessionCommandGroup2 commands = SessionCommandGroup2.fromBundle(commandsBundle); 307 if (commands == null) { 308 Log.w(TAG, "onAllowedCommandsChanged(): Ignoring null commands"); 309 return; 310 } 311 controller.onAllowedCommandsChanged(commands); 312 } 313 314 @Override onCustomCommand(Bundle commandBundle, Bundle args, ResultReceiver receiver)315 public void onCustomCommand(Bundle commandBundle, Bundle args, ResultReceiver receiver) { 316 final MediaController2Impl controller; 317 try { 318 controller = getController(); 319 } catch (IllegalStateException e) { 320 Log.w(TAG, "Don't fail silently here. Highly likely a bug"); 321 return; 322 } 323 SessionCommand2 command = SessionCommand2.fromBundle(commandBundle); 324 if (command == null) { 325 Log.w(TAG, "onCustomCommand(): Ignoring null command"); 326 return; 327 } 328 controller.onCustomCommand(command, args, receiver); 329 } 330 331 //////////////////////////////////////////////////////////////////////////////////////////// 332 // MediaBrowser specific 333 //////////////////////////////////////////////////////////////////////////////////////////// 334 @Override onGetLibraryRootDone(Bundle rootHints, String rootMediaId, Bundle rootExtra)335 public void onGetLibraryRootDone(Bundle rootHints, String rootMediaId, Bundle rootExtra) 336 throws RuntimeException { 337 final MediaBrowser2Impl browser; 338 try { 339 browser = getBrowser(); 340 } catch (IllegalStateException e) { 341 Log.w(TAG, "Don't fail silently here. Highly likely a bug"); 342 return; 343 } 344 if (browser == null) { 345 // TODO(jaewan): Revisit here. Could be a bug 346 return; 347 } 348 browser.onGetLibraryRootDone(rootHints, rootMediaId, rootExtra); 349 } 350 351 352 @Override onGetItemDone(String mediaId, Bundle itemBundle)353 public void onGetItemDone(String mediaId, Bundle itemBundle) throws RuntimeException { 354 if (mediaId == null) { 355 Log.w(TAG, "onGetItemDone(): Ignoring null mediaId"); 356 return; 357 } 358 final MediaBrowser2Impl browser; 359 try { 360 browser = getBrowser(); 361 } catch (IllegalStateException e) { 362 Log.w(TAG, "Don't fail silently here. Highly likely a bug"); 363 return; 364 } 365 if (browser == null) { 366 // TODO(jaewan): Revisit here. Could be a bug 367 return; 368 } 369 browser.onGetItemDone(mediaId, MediaItem2.fromBundle(itemBundle)); 370 } 371 372 @Override onGetChildrenDone(String parentId, int page, int pageSize, List<Bundle> itemBundleList, Bundle extras)373 public void onGetChildrenDone(String parentId, int page, int pageSize, 374 List<Bundle> itemBundleList, Bundle extras) throws RuntimeException { 375 if (parentId == null) { 376 Log.w(TAG, "onGetChildrenDone(): Ignoring null parentId"); 377 return; 378 } 379 final MediaBrowser2Impl browser; 380 try { 381 browser = getBrowser(); 382 } catch (IllegalStateException e) { 383 Log.w(TAG, "Don't fail silently here. Highly likely a bug"); 384 return; 385 } 386 if (browser == null) { 387 // TODO(jaewan): Revisit here. Could be a bug 388 return; 389 } 390 391 List<MediaItem2> result = null; 392 if (itemBundleList != null) { 393 result = new ArrayList<>(); 394 for (Bundle bundle : itemBundleList) { 395 result.add(MediaItem2.fromBundle(bundle)); 396 } 397 } 398 browser.onGetChildrenDone(parentId, page, pageSize, result, extras); 399 } 400 401 @Override onSearchResultChanged(String query, int itemCount, Bundle extras)402 public void onSearchResultChanged(String query, int itemCount, Bundle extras) 403 throws RuntimeException { 404 if (TextUtils.isEmpty(query)) { 405 Log.w(TAG, "onSearchResultChanged(): Ignoring empty query"); 406 return; 407 } 408 final MediaBrowser2Impl browser; 409 try { 410 browser = getBrowser(); 411 } catch (IllegalStateException e) { 412 Log.w(TAG, "Don't fail silently here. Highly likely a bug"); 413 return; 414 } 415 if (browser == null) { 416 // TODO(jaewan): Revisit here. Could be a bug 417 return; 418 } 419 browser.onSearchResultChanged(query, itemCount, extras); 420 } 421 422 @Override onGetSearchResultDone(String query, int page, int pageSize, List<Bundle> itemBundleList, Bundle extras)423 public void onGetSearchResultDone(String query, int page, int pageSize, 424 List<Bundle> itemBundleList, Bundle extras) throws RuntimeException { 425 if (TextUtils.isEmpty(query)) { 426 Log.w(TAG, "onGetSearchResultDone(): Ignoring empty query"); 427 return; 428 } 429 final MediaBrowser2Impl browser; 430 try { 431 browser = getBrowser(); 432 } catch (IllegalStateException e) { 433 Log.w(TAG, "Don't fail silently here. Highly likely a bug"); 434 return; 435 } 436 if (browser == null) { 437 // TODO(jaewan): Revisit here. Could be a bug 438 return; 439 } 440 441 List<MediaItem2> result = null; 442 if (itemBundleList != null) { 443 result = new ArrayList<>(); 444 for (Bundle bundle : itemBundleList) { 445 result.add(MediaItem2.fromBundle(bundle)); 446 } 447 } 448 browser.onGetSearchResultDone(query, page, pageSize, result, extras); 449 } 450 451 @Override onChildrenChanged(String parentId, int itemCount, Bundle extras)452 public void onChildrenChanged(String parentId, int itemCount, Bundle extras) { 453 if (parentId == null) { 454 Log.w(TAG, "onChildrenChanged(): Ignoring null parentId"); 455 return; 456 } 457 final MediaBrowser2Impl browser; 458 try { 459 browser = getBrowser(); 460 } catch (IllegalStateException e) { 461 Log.w(TAG, "Don't fail silently here. Highly likely a bug"); 462 return; 463 } 464 if (browser == null) { 465 // TODO(jaewan): Revisit here. Could be a bug 466 return; 467 } 468 browser.onChildrenChanged(parentId, itemCount, extras); 469 } 470 } 471