1 /* 2 * Copyright (C) 2012 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.avrcp; 18 19 import java.util.Timer; 20 import java.util.TimerTask; 21 22 import android.app.PendingIntent; 23 import android.bluetooth.BluetoothA2dp; 24 import android.bluetooth.BluetoothAvrcp; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.graphics.Bitmap; 28 import android.media.AudioManager; 29 import android.media.IRemoteControlDisplay; 30 import android.media.MediaMetadataRetriever; 31 import android.media.RemoteControlClient; 32 import android.media.RemoteController; 33 import android.media.RemoteController.MetadataEditor; 34 import android.os.Bundle; 35 import android.os.Handler; 36 import android.os.HandlerThread; 37 import android.os.Looper; 38 import android.os.Message; 39 import android.os.ParcelUuid; 40 import android.os.PowerManager; 41 import android.os.PowerManager.WakeLock; 42 import android.os.RemoteException; 43 import android.os.ServiceManager; 44 import android.os.SystemClock; 45 import android.util.Log; 46 import android.view.KeyEvent; 47 48 import com.android.bluetooth.btservice.AdapterService; 49 import com.android.bluetooth.btservice.ProfileService; 50 import com.android.bluetooth.Utils; 51 import com.android.internal.util.IState; 52 import com.android.internal.util.State; 53 import com.android.internal.util.StateMachine; 54 55 import java.lang.ref.WeakReference; 56 import java.util.ArrayList; 57 import java.util.List; 58 import java.util.Set; 59 /** 60 * support Bluetooth AVRCP profile. 61 * support metadata, play status and event notification 62 */ 63 public final class Avrcp { 64 private static final boolean DEBUG = false; 65 private static final String TAG = "Avrcp"; 66 67 private Context mContext; 68 private final AudioManager mAudioManager; 69 private AvrcpMessageHandler mHandler; 70 private RemoteController mRemoteController; 71 private RemoteControllerWeak mRemoteControllerCb; 72 private Metadata mMetadata; 73 private int mTransportControlFlags; 74 private int mCurrentPlayState; 75 private int mPlayStatusChangedNT; 76 private int mTrackChangedNT; 77 private long mTrackNumber; 78 private long mCurrentPosMs; 79 private long mPlayStartTimeMs; 80 private long mSongLengthMs; 81 private long mPlaybackIntervalMs; 82 private int mPlayPosChangedNT; 83 private long mNextPosMs; 84 private long mPrevPosMs; 85 private long mSkipStartTime; 86 private int mFeatures; 87 private int mAbsoluteVolume; 88 private int mLastSetVolume; 89 private int mLastDirection; 90 private final int mVolumeStep; 91 private final int mAudioStreamMax; 92 private boolean mVolCmdInProgress; 93 private int mAbsVolRetryTimes; 94 private int mSkipAmount; 95 96 /* BTRC features */ 97 public static final int BTRC_FEAT_METADATA = 0x01; 98 public static final int BTRC_FEAT_ABSOLUTE_VOLUME = 0x02; 99 public static final int BTRC_FEAT_BROWSE = 0x04; 100 101 /* AVRC response codes, from avrc_defs */ 102 private static final int AVRC_RSP_NOT_IMPL = 8; 103 private static final int AVRC_RSP_ACCEPT = 9; 104 private static final int AVRC_RSP_REJ = 10; 105 private static final int AVRC_RSP_IN_TRANS = 11; 106 private static final int AVRC_RSP_IMPL_STBL = 12; 107 private static final int AVRC_RSP_CHANGED = 13; 108 private static final int AVRC_RSP_INTERIM = 15; 109 110 private static final int MESSAGE_GET_RC_FEATURES = 1; 111 private static final int MESSAGE_GET_PLAY_STATUS = 2; 112 private static final int MESSAGE_GET_ELEM_ATTRS = 3; 113 private static final int MESSAGE_REGISTER_NOTIFICATION = 4; 114 private static final int MESSAGE_PLAY_INTERVAL_TIMEOUT = 5; 115 private static final int MESSAGE_VOLUME_CHANGED = 6; 116 private static final int MESSAGE_ADJUST_VOLUME = 7; 117 private static final int MESSAGE_SET_ABSOLUTE_VOLUME = 8; 118 private static final int MESSAGE_ABS_VOL_TIMEOUT = 9; 119 private static final int MESSAGE_FAST_FORWARD = 10; 120 private static final int MESSAGE_REWIND = 11; 121 private static final int MESSAGE_CHANGE_PLAY_POS = 12; 122 private static final int MESSAGE_SET_A2DP_AUDIO_STATE = 13; 123 private static final int MSG_UPDATE_STATE = 100; 124 private static final int MSG_SET_METADATA = 101; 125 private static final int MSG_SET_TRANSPORT_CONTROLS = 102; 126 private static final int MSG_SET_GENERATION_ID = 104; 127 128 private static final int BUTTON_TIMEOUT_TIME = 2000; 129 private static final int BASE_SKIP_AMOUNT = 2000; 130 private static final int KEY_STATE_PRESS = 1; 131 private static final int KEY_STATE_RELEASE = 0; 132 private static final int SKIP_PERIOD = 400; 133 private static final int SKIP_DOUBLE_INTERVAL = 3000; 134 private static final long MAX_MULTIPLIER_VALUE = 128L; 135 private static final int CMD_TIMEOUT_DELAY = 2000; 136 private static final int MAX_ERROR_RETRY_TIMES = 3; 137 private static final int AVRCP_MAX_VOL = 127; 138 private static final int AVRCP_BASE_VOLUME_STEP = 1; 139 140 static { 141 classInitNative(); 142 } 143 144 private Avrcp(Context context) { 145 mMetadata = new Metadata(); 146 mCurrentPlayState = RemoteControlClient.PLAYSTATE_NONE; // until we get a callback 147 mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED; 148 mTrackChangedNT = NOTIFICATION_TYPE_CHANGED; 149 mTrackNumber = -1L; 150 mCurrentPosMs = 0L; 151 mPlayStartTimeMs = -1L; 152 mSongLengthMs = 0L; 153 mPlaybackIntervalMs = 0L; 154 mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED; 155 mFeatures = 0; 156 mAbsoluteVolume = -1; 157 mLastSetVolume = -1; 158 mLastDirection = 0; 159 mVolCmdInProgress = false; 160 mAbsVolRetryTimes = 0; 161 162 mContext = context; 163 164 initNative(); 165 166 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 167 mAudioStreamMax = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); 168 mVolumeStep = Math.max(AVRCP_BASE_VOLUME_STEP, AVRCP_MAX_VOL/mAudioStreamMax); 169 } 170 171 private void start() { 172 HandlerThread thread = new HandlerThread("BluetoothAvrcpHandler"); 173 thread.start(); 174 Looper looper = thread.getLooper(); 175 mHandler = new AvrcpMessageHandler(looper); 176 mRemoteControllerCb = new RemoteControllerWeak(mHandler); 177 mRemoteController = new RemoteController(mContext, mRemoteControllerCb); 178 mAudioManager.registerRemoteController(mRemoteController); 179 mRemoteController.setSynchronizationMode(RemoteController.POSITION_SYNCHRONIZATION_CHECK); 180 } 181 182 public static Avrcp make(Context context) { 183 if (DEBUG) Log.v(TAG, "make"); 184 Avrcp ar = new Avrcp(context); 185 ar.start(); 186 return ar; 187 } 188 189 public void doQuit() { 190 mHandler.removeCallbacksAndMessages(null); 191 Looper looper = mHandler.getLooper(); 192 if (looper != null) { 193 looper.quit(); 194 } 195 mAudioManager.unregisterRemoteController(mRemoteController); 196 } 197 198 public void cleanup() { 199 cleanupNative(); 200 } 201 202 private static class RemoteControllerWeak implements RemoteController.OnClientUpdateListener { 203 private final WeakReference<Handler> mLocalHandler; 204 205 public RemoteControllerWeak(Handler handler) { 206 mLocalHandler = new WeakReference<Handler>(handler); 207 } 208 209 @Override 210 public void onClientChange(boolean clearing) { 211 Handler handler = mLocalHandler.get(); 212 if (handler != null) { 213 handler.obtainMessage(MSG_SET_GENERATION_ID, 214 0, (clearing ? 1 : 0), null).sendToTarget(); 215 } 216 } 217 218 @Override 219 public void onClientPlaybackStateUpdate(int state) { 220 // Should never be called with the existing code, but just in case 221 Handler handler = mLocalHandler.get(); 222 if (handler != null) { 223 handler.obtainMessage(MSG_UPDATE_STATE, 0, state, 224 new Long(RemoteControlClient.PLAYBACK_POSITION_INVALID)).sendToTarget(); 225 } 226 } 227 228 @Override 229 public void onClientPlaybackStateUpdate(int state, long stateChangeTimeMs, 230 long currentPosMs, float speed) { 231 Handler handler = mLocalHandler.get(); 232 if (handler != null) { 233 handler.obtainMessage(MSG_UPDATE_STATE, 0, state, 234 new Long(currentPosMs)).sendToTarget(); 235 } 236 } 237 238 @Override 239 public void onClientTransportControlUpdate(int transportControlFlags) { 240 Handler handler = mLocalHandler.get(); 241 if (handler != null) { 242 handler.obtainMessage(MSG_SET_TRANSPORT_CONTROLS, 0, transportControlFlags) 243 .sendToTarget(); 244 } 245 } 246 247 @Override 248 public void onClientMetadataUpdate(MetadataEditor metadataEditor) { 249 Handler handler = mLocalHandler.get(); 250 if (handler != null) { 251 handler.obtainMessage(MSG_SET_METADATA, 0, 0, metadataEditor).sendToTarget(); 252 } 253 } 254 } 255 256 /** Handles Avrcp messages. */ 257 private final class AvrcpMessageHandler extends Handler { 258 private AvrcpMessageHandler(Looper looper) { 259 super(looper); 260 } 261 262 @Override 263 public void handleMessage(Message msg) { 264 switch (msg.what) { 265 case MSG_UPDATE_STATE: 266 updatePlayPauseState(msg.arg2, ((Long) msg.obj).longValue()); 267 break; 268 269 case MSG_SET_METADATA: 270 updateMetadata((MetadataEditor) msg.obj); 271 break; 272 273 case MSG_SET_TRANSPORT_CONTROLS: 274 updateTransportControls(msg.arg2); 275 break; 276 277 case MSG_SET_GENERATION_ID: 278 if (DEBUG) Log.v(TAG, "New genId = " + msg.arg1 + ", clearing = " + msg.arg2); 279 break; 280 281 case MESSAGE_GET_RC_FEATURES: 282 String address = (String) msg.obj; 283 if (DEBUG) Log.v(TAG, "MESSAGE_GET_RC_FEATURES: address="+address+ 284 ", features="+msg.arg1); 285 mFeatures = msg.arg1; 286 mAudioManager.avrcpSupportsAbsoluteVolume(address, isAbsoluteVolumeSupported()); 287 break; 288 289 case MESSAGE_GET_PLAY_STATUS: 290 if (DEBUG) Log.v(TAG, "MESSAGE_GET_PLAY_STATUS"); 291 getPlayStatusRspNative(convertPlayStateToPlayStatus(mCurrentPlayState), 292 (int)mSongLengthMs, (int)getPlayPosition()); 293 break; 294 295 case MESSAGE_GET_ELEM_ATTRS: 296 { 297 String[] textArray; 298 int[] attrIds; 299 byte numAttr = (byte) msg.arg1; 300 ArrayList<Integer> attrList = (ArrayList<Integer>) msg.obj; 301 if (DEBUG) Log.v(TAG, "MESSAGE_GET_ELEM_ATTRS:numAttr=" + numAttr); 302 attrIds = new int[numAttr]; 303 textArray = new String[numAttr]; 304 for (int i = 0; i < numAttr; ++i) { 305 attrIds[i] = attrList.get(i).intValue(); 306 textArray[i] = getAttributeString(attrIds[i]); 307 } 308 getElementAttrRspNative(numAttr, attrIds, textArray); 309 break; 310 } 311 case MESSAGE_REGISTER_NOTIFICATION: 312 if (DEBUG) Log.v(TAG, "MESSAGE_REGISTER_NOTIFICATION:event=" + msg.arg1 + 313 " param=" + msg.arg2); 314 processRegisterNotification(msg.arg1, msg.arg2); 315 break; 316 317 case MESSAGE_PLAY_INTERVAL_TIMEOUT: 318 if (DEBUG) Log.v(TAG, "MESSAGE_PLAY_INTERVAL_TIMEOUT"); 319 mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED; 320 registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)getPlayPosition()); 321 break; 322 323 case MESSAGE_VOLUME_CHANGED: 324 if (DEBUG) Log.v(TAG, "MESSAGE_VOLUME_CHANGED: volume=" + ((byte)msg.arg1 & 0x7f) 325 + " ctype=" + msg.arg2); 326 327 if (msg.arg2 == AVRC_RSP_ACCEPT || msg.arg2 == AVRC_RSP_REJ) { 328 if (mVolCmdInProgress == false) { 329 Log.e(TAG, "Unsolicited response, ignored"); 330 break; 331 } 332 removeMessages(MESSAGE_ABS_VOL_TIMEOUT); 333 mVolCmdInProgress = false; 334 mAbsVolRetryTimes = 0; 335 } 336 if (mAbsoluteVolume != msg.arg1 && (msg.arg2 == AVRC_RSP_ACCEPT || 337 msg.arg2 == AVRC_RSP_CHANGED || 338 msg.arg2 == AVRC_RSP_INTERIM)) { 339 byte absVol = (byte)((byte)msg.arg1 & 0x7f); // discard MSB as it is RFD 340 notifyVolumeChanged(absVol); 341 mAbsoluteVolume = absVol; 342 long pecentVolChanged = ((long)absVol * 100) / 0x7f; 343 Log.e(TAG, "percent volume changed: " + pecentVolChanged + "%"); 344 } else if (msg.arg2 == AVRC_RSP_REJ) { 345 Log.e(TAG, "setAbsoluteVolume call rejected"); 346 } 347 break; 348 349 case MESSAGE_ADJUST_VOLUME: 350 if (DEBUG) Log.d(TAG, "MESSAGE_ADJUST_VOLUME: direction=" + msg.arg1); 351 if (mVolCmdInProgress) { 352 if (DEBUG) Log.w(TAG, "There is already a volume command in progress."); 353 break; 354 } 355 // Wait on verification on volume from device, before changing the volume. 356 if (mAbsoluteVolume != -1 && (msg.arg1 == -1 || msg.arg1 == 1)) { 357 int setVol = Math.min(AVRCP_MAX_VOL, 358 Math.max(0, mAbsoluteVolume + msg.arg1*mVolumeStep)); 359 if (setVolumeNative(setVol)) { 360 sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT), 361 CMD_TIMEOUT_DELAY); 362 mVolCmdInProgress = true; 363 mLastDirection = msg.arg1; 364 mLastSetVolume = setVol; 365 } 366 } else { 367 Log.e(TAG, "Unknown direction in MESSAGE_ADJUST_VOLUME"); 368 } 369 break; 370 371 case MESSAGE_SET_ABSOLUTE_VOLUME: 372 if (DEBUG) Log.v(TAG, "MESSAGE_SET_ABSOLUTE_VOLUME"); 373 if (mVolCmdInProgress) { 374 if (DEBUG) Log.w(TAG, "There is already a volume command in progress."); 375 break; 376 } 377 if (setVolumeNative(msg.arg1)) { 378 sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT), CMD_TIMEOUT_DELAY); 379 mVolCmdInProgress = true; 380 mLastSetVolume = msg.arg1; 381 } 382 break; 383 384 case MESSAGE_ABS_VOL_TIMEOUT: 385 if (DEBUG) Log.v(TAG, "MESSAGE_ABS_VOL_TIMEOUT: Volume change cmd timed out."); 386 mVolCmdInProgress = false; 387 if (mAbsVolRetryTimes >= MAX_ERROR_RETRY_TIMES) { 388 mAbsVolRetryTimes = 0; 389 } else { 390 mAbsVolRetryTimes += 1; 391 if (setVolumeNative(mLastSetVolume)) { 392 sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT), 393 CMD_TIMEOUT_DELAY); 394 mVolCmdInProgress = true; 395 } 396 } 397 break; 398 399 case MESSAGE_FAST_FORWARD: 400 case MESSAGE_REWIND: 401 if(msg.what == MESSAGE_FAST_FORWARD) { 402 if((mTransportControlFlags & 403 RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD) != 0) { 404 int keyState = msg.arg1 == KEY_STATE_PRESS ? 405 KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP; 406 KeyEvent keyEvent = 407 new KeyEvent(keyState, KeyEvent.KEYCODE_MEDIA_FAST_FORWARD); 408 mRemoteController.sendMediaKeyEvent(keyEvent); 409 break; 410 } 411 } else if((mTransportControlFlags & 412 RemoteControlClient.FLAG_KEY_MEDIA_REWIND) != 0) { 413 int keyState = msg.arg1 == KEY_STATE_PRESS ? 414 KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP; 415 KeyEvent keyEvent = 416 new KeyEvent(keyState, KeyEvent.KEYCODE_MEDIA_REWIND); 417 mRemoteController.sendMediaKeyEvent(keyEvent); 418 break; 419 } 420 421 int skipAmount; 422 if (msg.what == MESSAGE_FAST_FORWARD) { 423 if (DEBUG) Log.v(TAG, "MESSAGE_FAST_FORWARD"); 424 removeMessages(MESSAGE_FAST_FORWARD); 425 skipAmount = BASE_SKIP_AMOUNT; 426 } else { 427 if (DEBUG) Log.v(TAG, "MESSAGE_REWIND"); 428 removeMessages(MESSAGE_REWIND); 429 skipAmount = -BASE_SKIP_AMOUNT; 430 } 431 432 if (hasMessages(MESSAGE_CHANGE_PLAY_POS) && 433 (skipAmount != mSkipAmount)) { 434 Log.w(TAG, "missing release button event:" + mSkipAmount); 435 } 436 437 if ((!hasMessages(MESSAGE_CHANGE_PLAY_POS)) || 438 (skipAmount != mSkipAmount)) { 439 mSkipStartTime = SystemClock.elapsedRealtime(); 440 } 441 442 removeMessages(MESSAGE_CHANGE_PLAY_POS); 443 if (msg.arg1 == KEY_STATE_PRESS) { 444 mSkipAmount = skipAmount; 445 changePositionBy(mSkipAmount * getSkipMultiplier()); 446 Message posMsg = obtainMessage(MESSAGE_CHANGE_PLAY_POS); 447 posMsg.arg1 = 1; 448 sendMessageDelayed(posMsg, SKIP_PERIOD); 449 } 450 451 break; 452 453 case MESSAGE_CHANGE_PLAY_POS: 454 if (DEBUG) Log.v(TAG, "MESSAGE_CHANGE_PLAY_POS:" + msg.arg1); 455 changePositionBy(mSkipAmount * getSkipMultiplier()); 456 if (msg.arg1 * SKIP_PERIOD < BUTTON_TIMEOUT_TIME) { 457 Message posMsg = obtainMessage(MESSAGE_CHANGE_PLAY_POS); 458 posMsg.arg1 = msg.arg1 + 1; 459 sendMessageDelayed(posMsg, SKIP_PERIOD); 460 } 461 break; 462 463 case MESSAGE_SET_A2DP_AUDIO_STATE: 464 if (DEBUG) Log.v(TAG, "MESSAGE_SET_A2DP_AUDIO_STATE:" + msg.arg1); 465 updateA2dpAudioState(msg.arg1); 466 break; 467 } 468 } 469 } 470 471 private void updateA2dpAudioState(int state) { 472 boolean isPlaying = (state == BluetoothA2dp.STATE_PLAYING); 473 if (isPlaying != isPlayingState(mCurrentPlayState)) { 474 /* if a2dp is streaming, check to make sure music is active */ 475 if ( (isPlaying) && !mAudioManager.isMusicActive()) 476 return; 477 updatePlayPauseState(isPlaying ? RemoteControlClient.PLAYSTATE_PLAYING : 478 RemoteControlClient.PLAYSTATE_PAUSED, 479 RemoteControlClient.PLAYBACK_POSITION_INVALID); 480 } 481 } 482 483 private void updatePlayPauseState(int state, long currentPosMs) { 484 if (DEBUG) Log.v(TAG, 485 "updatePlayPauseState, old=" + mCurrentPlayState + ", state=" + state); 486 boolean oldPosValid = (mCurrentPosMs != 487 RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN); 488 int oldPlayStatus = convertPlayStateToPlayStatus(mCurrentPlayState); 489 int newPlayStatus = convertPlayStateToPlayStatus(state); 490 491 if ((mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) && 492 (mCurrentPlayState != state) && oldPosValid) { 493 mCurrentPosMs = getPlayPosition(); 494 } 495 496 if (currentPosMs != RemoteControlClient.PLAYBACK_POSITION_INVALID) { 497 mCurrentPosMs = currentPosMs; 498 } 499 if ((state == RemoteControlClient.PLAYSTATE_PLAYING) && 500 ((currentPosMs != RemoteControlClient.PLAYBACK_POSITION_INVALID) || 501 (mCurrentPlayState != RemoteControlClient.PLAYSTATE_PLAYING))) { 502 mPlayStartTimeMs = SystemClock.elapsedRealtime(); 503 } 504 mCurrentPlayState = state; 505 506 boolean newPosValid = (mCurrentPosMs != 507 RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN); 508 long playPosition = getPlayPosition(); 509 mHandler.removeMessages(MESSAGE_PLAY_INTERVAL_TIMEOUT); 510 /* need send play position changed notification when play status is changed */ 511 if ((mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) && 512 ((oldPlayStatus != newPlayStatus) || (oldPosValid != newPosValid) || 513 (newPosValid && ((playPosition >= mNextPosMs) || (playPosition <= mPrevPosMs))))) { 514 mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED; 515 registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)playPosition); 516 } 517 if ((mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) && newPosValid && 518 (state == RemoteControlClient.PLAYSTATE_PLAYING)) { 519 Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT); 520 mHandler.sendMessageDelayed(msg, mNextPosMs - playPosition); 521 } 522 523 if ((mPlayStatusChangedNT == NOTIFICATION_TYPE_INTERIM) && (oldPlayStatus != newPlayStatus)) { 524 mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED; 525 registerNotificationRspPlayStatusNative(mPlayStatusChangedNT, newPlayStatus); 526 } 527 } 528 529 private void updateTransportControls(int transportControlFlags) { 530 mTransportControlFlags = transportControlFlags; 531 } 532 533 class Metadata { 534 private String artist; 535 private String trackTitle; 536 private String albumTitle; 537 538 public Metadata() { 539 artist = null; 540 trackTitle = null; 541 albumTitle = null; 542 } 543 544 public String toString() { 545 return "Metadata[artist=" + artist + " trackTitle=" + trackTitle + " albumTitle=" + 546 albumTitle + "]"; 547 } 548 } 549 550 private void updateMetadata(MetadataEditor data) { 551 String oldMetadata = mMetadata.toString(); 552 mMetadata.artist = data.getString(MediaMetadataRetriever.METADATA_KEY_ARTIST, null); 553 mMetadata.trackTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_TITLE, null); 554 mMetadata.albumTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_ALBUM, null); 555 if (!oldMetadata.equals(mMetadata.toString())) { 556 mTrackNumber++; 557 if (mTrackChangedNT == NOTIFICATION_TYPE_INTERIM) { 558 mTrackChangedNT = NOTIFICATION_TYPE_CHANGED; 559 sendTrackChangedRsp(); 560 } 561 562 if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) { 563 mCurrentPosMs = 0L; 564 if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) { 565 mPlayStartTimeMs = SystemClock.elapsedRealtime(); 566 } 567 } 568 /* need send play position changed notification when track is changed */ 569 if (mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) { 570 mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED; 571 registerNotificationRspPlayPosNative(mPlayPosChangedNT, 572 (int)getPlayPosition()); 573 mHandler.removeMessages(MESSAGE_PLAY_INTERVAL_TIMEOUT); 574 } 575 } 576 if (DEBUG) Log.v(TAG, "mMetadata=" + mMetadata.toString()); 577 578 mSongLengthMs = data.getLong(MediaMetadataRetriever.METADATA_KEY_DURATION, 579 RemoteControlClient.PLAYBACK_POSITION_INVALID); 580 if (DEBUG) Log.v(TAG, "duration=" + mSongLengthMs); 581 } 582 583 private void getRcFeatures(byte[] address, int features) { 584 Message msg = mHandler.obtainMessage(MESSAGE_GET_RC_FEATURES, features, 0, 585 Utils.getAddressStringFromByte(address)); 586 mHandler.sendMessage(msg); 587 } 588 589 private void getPlayStatus() { 590 Message msg = mHandler.obtainMessage(MESSAGE_GET_PLAY_STATUS); 591 mHandler.sendMessage(msg); 592 } 593 594 private void getElementAttr(byte numAttr, int[] attrs) { 595 int i; 596 ArrayList<Integer> attrList = new ArrayList<Integer>(); 597 for (i = 0; i < numAttr; ++i) { 598 attrList.add(attrs[i]); 599 } 600 Message msg = mHandler.obtainMessage(MESSAGE_GET_ELEM_ATTRS, numAttr, 0, attrList); 601 mHandler.sendMessage(msg); 602 } 603 604 private void registerNotification(int eventId, int param) { 605 Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_NOTIFICATION, eventId, param); 606 mHandler.sendMessage(msg); 607 } 608 609 private void processRegisterNotification(int eventId, int param) { 610 switch (eventId) { 611 case EVT_PLAY_STATUS_CHANGED: 612 mPlayStatusChangedNT = NOTIFICATION_TYPE_INTERIM; 613 registerNotificationRspPlayStatusNative(mPlayStatusChangedNT, 614 convertPlayStateToPlayStatus(mCurrentPlayState)); 615 break; 616 617 case EVT_TRACK_CHANGED: 618 mTrackChangedNT = NOTIFICATION_TYPE_INTERIM; 619 sendTrackChangedRsp(); 620 break; 621 622 case EVT_PLAY_POS_CHANGED: 623 long songPosition = getPlayPosition(); 624 mPlayPosChangedNT = NOTIFICATION_TYPE_INTERIM; 625 mPlaybackIntervalMs = (long)param * 1000L; 626 if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) { 627 mNextPosMs = songPosition + mPlaybackIntervalMs; 628 mPrevPosMs = songPosition - mPlaybackIntervalMs; 629 if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) { 630 Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT); 631 mHandler.sendMessageDelayed(msg, mPlaybackIntervalMs); 632 } 633 } 634 registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)songPosition); 635 break; 636 637 } 638 } 639 640 private void handlePassthroughCmd(int id, int keyState) { 641 switch (id) { 642 case BluetoothAvrcp.PASSTHROUGH_ID_REWIND: 643 rewind(keyState); 644 break; 645 case BluetoothAvrcp.PASSTHROUGH_ID_FAST_FOR: 646 fastForward(keyState); 647 break; 648 } 649 } 650 651 private void fastForward(int keyState) { 652 Message msg = mHandler.obtainMessage(MESSAGE_FAST_FORWARD, keyState, 0); 653 mHandler.sendMessage(msg); 654 } 655 656 private void rewind(int keyState) { 657 Message msg = mHandler.obtainMessage(MESSAGE_REWIND, keyState, 0); 658 mHandler.sendMessage(msg); 659 } 660 661 private void changePositionBy(long amount) { 662 long currentPosMs = getPlayPosition(); 663 if (currentPosMs == -1L) return; 664 long newPosMs = Math.max(0L, currentPosMs + amount); 665 mRemoteController.seekTo(newPosMs); 666 } 667 668 private int getSkipMultiplier() { 669 long currentTime = SystemClock.elapsedRealtime(); 670 long multi = (long) Math.pow(2, (currentTime - mSkipStartTime)/SKIP_DOUBLE_INTERVAL); 671 return (int) Math.min(MAX_MULTIPLIER_VALUE, multi); 672 } 673 674 private void sendTrackChangedRsp() { 675 byte[] track = new byte[TRACK_ID_SIZE]; 676 /* track is stored in big endian format */ 677 for (int i = 0; i < TRACK_ID_SIZE; ++i) { 678 track[i] = (byte) (mTrackNumber >> (56 - 8 * i)); 679 } 680 registerNotificationRspTrackChangeNative(mTrackChangedNT, track); 681 } 682 683 private long getPlayPosition() { 684 long songPosition = -1L; 685 if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) { 686 if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) { 687 songPosition = SystemClock.elapsedRealtime() - 688 mPlayStartTimeMs + mCurrentPosMs; 689 } else { 690 songPosition = mCurrentPosMs; 691 } 692 } 693 if (DEBUG) Log.v(TAG, "position=" + songPosition); 694 return songPosition; 695 } 696 697 private String getAttributeString(int attrId) { 698 String attrStr = null; 699 switch (attrId) { 700 case MEDIA_ATTR_TITLE: 701 attrStr = mMetadata.trackTitle; 702 break; 703 704 case MEDIA_ATTR_ARTIST: 705 attrStr = mMetadata.artist; 706 break; 707 708 case MEDIA_ATTR_ALBUM: 709 attrStr = mMetadata.albumTitle; 710 break; 711 712 case MEDIA_ATTR_PLAYING_TIME: 713 if (mSongLengthMs != 0L) { 714 attrStr = Long.toString(mSongLengthMs); 715 } 716 break; 717 718 } 719 if (attrStr == null) { 720 attrStr = new String(); 721 } 722 if (DEBUG) Log.v(TAG, "getAttributeString:attrId=" + attrId + " str=" + attrStr); 723 return attrStr; 724 } 725 726 private int convertPlayStateToPlayStatus(int playState) { 727 int playStatus = PLAYSTATUS_ERROR; 728 switch (playState) { 729 case RemoteControlClient.PLAYSTATE_PLAYING: 730 case RemoteControlClient.PLAYSTATE_BUFFERING: 731 playStatus = PLAYSTATUS_PLAYING; 732 break; 733 734 case RemoteControlClient.PLAYSTATE_STOPPED: 735 case RemoteControlClient.PLAYSTATE_NONE: 736 playStatus = PLAYSTATUS_STOPPED; 737 break; 738 739 case RemoteControlClient.PLAYSTATE_PAUSED: 740 playStatus = PLAYSTATUS_PAUSED; 741 break; 742 743 case RemoteControlClient.PLAYSTATE_FAST_FORWARDING: 744 case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS: 745 playStatus = PLAYSTATUS_FWD_SEEK; 746 break; 747 748 case RemoteControlClient.PLAYSTATE_REWINDING: 749 case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS: 750 playStatus = PLAYSTATUS_REV_SEEK; 751 break; 752 753 case RemoteControlClient.PLAYSTATE_ERROR: 754 playStatus = PLAYSTATUS_ERROR; 755 break; 756 757 } 758 return playStatus; 759 } 760 761 private boolean isPlayingState(int playState) { 762 boolean isPlaying = false; 763 switch (playState) { 764 case RemoteControlClient.PLAYSTATE_PLAYING: 765 case RemoteControlClient.PLAYSTATE_BUFFERING: 766 isPlaying = true; 767 break; 768 default: 769 isPlaying = false; 770 break; 771 } 772 return isPlaying; 773 } 774 775 /** 776 * This is called from AudioService. It will return whether this device supports abs volume. 777 * NOT USED AT THE MOMENT. 778 */ 779 public boolean isAbsoluteVolumeSupported() { 780 return ((mFeatures & BTRC_FEAT_ABSOLUTE_VOLUME) != 0); 781 } 782 783 /** 784 * We get this call from AudioService. This will send a message to our handler object, 785 * requesting our handler to call setVolumeNative() 786 */ 787 public void adjustVolume(int direction) { 788 Message msg = mHandler.obtainMessage(MESSAGE_ADJUST_VOLUME, direction, 0); 789 mHandler.sendMessage(msg); 790 } 791 792 public void setAbsoluteVolume(int volume) { 793 int avrcpVolume = convertToAvrcpVolume(volume); 794 avrcpVolume = Math.min(AVRCP_MAX_VOL, Math.max(0, avrcpVolume)); 795 mHandler.removeMessages(MESSAGE_ADJUST_VOLUME); 796 Message msg = mHandler.obtainMessage(MESSAGE_SET_ABSOLUTE_VOLUME, avrcpVolume, 0); 797 mHandler.sendMessage(msg); 798 } 799 800 /* Called in the native layer as a btrc_callback to return the volume set on the carkit in the 801 * case when the volume is change locally on the carkit. This notification is not called when 802 * the volume is changed from the phone. 803 * 804 * This method will send a message to our handler to change the local stored volume and notify 805 * AudioService to update the UI 806 */ 807 private void volumeChangeCallback(int volume, int ctype) { 808 Message msg = mHandler.obtainMessage(MESSAGE_VOLUME_CHANGED, volume, ctype); 809 mHandler.sendMessage(msg); 810 } 811 812 private void notifyVolumeChanged(int volume) { 813 volume = convertToAudioStreamVolume(volume); 814 mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 815 AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_BLUETOOTH_ABS_VOLUME); 816 } 817 818 private int convertToAudioStreamVolume(int volume) { 819 // Rescale volume to match AudioSystem's volume 820 return (int) Math.round((double) volume*mAudioStreamMax/AVRCP_MAX_VOL); 821 } 822 823 private int convertToAvrcpVolume(int volume) { 824 return (int) Math.ceil((double) volume*AVRCP_MAX_VOL/mAudioStreamMax); 825 } 826 827 /** 828 * This is called from A2dpStateMachine to set A2dp audio state. 829 */ 830 public void setA2dpAudioState(int state) { 831 Message msg = mHandler.obtainMessage(MESSAGE_SET_A2DP_AUDIO_STATE, state, 0); 832 mHandler.sendMessage(msg); 833 } 834 835 // Do not modify without updating the HAL bt_rc.h files. 836 837 // match up with btrc_play_status_t enum of bt_rc.h 838 final static int PLAYSTATUS_STOPPED = 0; 839 final static int PLAYSTATUS_PLAYING = 1; 840 final static int PLAYSTATUS_PAUSED = 2; 841 final static int PLAYSTATUS_FWD_SEEK = 3; 842 final static int PLAYSTATUS_REV_SEEK = 4; 843 final static int PLAYSTATUS_ERROR = 255; 844 845 // match up with btrc_media_attr_t enum of bt_rc.h 846 final static int MEDIA_ATTR_TITLE = 1; 847 final static int MEDIA_ATTR_ARTIST = 2; 848 final static int MEDIA_ATTR_ALBUM = 3; 849 final static int MEDIA_ATTR_TRACK_NUM = 4; 850 final static int MEDIA_ATTR_NUM_TRACKS = 5; 851 final static int MEDIA_ATTR_GENRE = 6; 852 final static int MEDIA_ATTR_PLAYING_TIME = 7; 853 854 // match up with btrc_event_id_t enum of bt_rc.h 855 final static int EVT_PLAY_STATUS_CHANGED = 1; 856 final static int EVT_TRACK_CHANGED = 2; 857 final static int EVT_TRACK_REACHED_END = 3; 858 final static int EVT_TRACK_REACHED_START = 4; 859 final static int EVT_PLAY_POS_CHANGED = 5; 860 final static int EVT_BATT_STATUS_CHANGED = 6; 861 final static int EVT_SYSTEM_STATUS_CHANGED = 7; 862 final static int EVT_APP_SETTINGS_CHANGED = 8; 863 864 // match up with btrc_notification_type_t enum of bt_rc.h 865 final static int NOTIFICATION_TYPE_INTERIM = 0; 866 final static int NOTIFICATION_TYPE_CHANGED = 1; 867 868 // match up with BTRC_UID_SIZE of bt_rc.h 869 final static int TRACK_ID_SIZE = 8; 870 871 private native static void classInitNative(); 872 private native void initNative(); 873 private native void cleanupNative(); 874 private native boolean getPlayStatusRspNative(int playStatus, int songLen, int songPos); 875 private native boolean getElementAttrRspNative(byte numAttr, int[] attrIds, String[] textArray); 876 private native boolean registerNotificationRspPlayStatusNative(int type, int playStatus); 877 private native boolean registerNotificationRspTrackChangeNative(int type, byte[] track); 878 private native boolean registerNotificationRspPlayPosNative(int type, int playPos); 879 private native boolean setVolumeNative(int volume); 880 private native boolean sendPassThroughCommandNative(int keyCode, int keyState); 881 882 } 883