Avrcp.java revision 881675b362bde18acbbcf69c513175addca4a8ba
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 17package com.android.bluetooth.avrcp; 18 19import java.util.Timer; 20import java.util.TimerTask; 21 22import android.app.PendingIntent; 23import android.bluetooth.BluetoothA2dp; 24import android.bluetooth.BluetoothAvrcp; 25import android.content.Context; 26import android.content.Intent; 27import android.graphics.Bitmap; 28import android.media.AudioManager; 29import android.media.IRemoteControlDisplay; 30import android.media.MediaMetadataRetriever; 31import android.media.RemoteControlClient; 32import android.media.RemoteController; 33import android.media.RemoteController.MetadataEditor; 34import android.os.Bundle; 35import android.os.Handler; 36import android.os.HandlerThread; 37import android.os.Looper; 38import android.os.Message; 39import android.os.ParcelUuid; 40import android.os.PowerManager; 41import android.os.PowerManager.WakeLock; 42import android.os.RemoteException; 43import android.os.ServiceManager; 44import android.os.SystemClock; 45import android.util.Log; 46import android.view.KeyEvent; 47 48import com.android.bluetooth.btservice.AdapterService; 49import com.android.bluetooth.btservice.ProfileService; 50import com.android.bluetooth.Utils; 51import com.android.internal.util.IState; 52import com.android.internal.util.State; 53import com.android.internal.util.StateMachine; 54 55import java.lang.ref.WeakReference; 56import java.util.ArrayList; 57import java.util.List; 58import java.util.Set; 59/** 60 * support Bluetooth AVRCP profile. 61 * support metadata, play status and event notification 62 */ 63public 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 updatePlayPauseState(isPlaying ? RemoteControlClient.PLAYSTATE_PLAYING : 475 RemoteControlClient.PLAYSTATE_PAUSED, 476 RemoteControlClient.PLAYBACK_POSITION_INVALID); 477 } 478 } 479 480 private void updatePlayPauseState(int state, long currentPosMs) { 481 if (DEBUG) Log.v(TAG, 482 "updatePlayPauseState, old=" + mCurrentPlayState + ", state=" + state); 483 boolean oldPosValid = (mCurrentPosMs != 484 RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN); 485 int oldPlayStatus = convertPlayStateToPlayStatus(mCurrentPlayState); 486 int newPlayStatus = convertPlayStateToPlayStatus(state); 487 488 if ((mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) && 489 (mCurrentPlayState != state) && oldPosValid) { 490 mCurrentPosMs = getPlayPosition(); 491 } 492 493 if (currentPosMs != RemoteControlClient.PLAYBACK_POSITION_INVALID) { 494 mCurrentPosMs = currentPosMs; 495 } 496 if ((state == RemoteControlClient.PLAYSTATE_PLAYING) && 497 ((currentPosMs != RemoteControlClient.PLAYBACK_POSITION_INVALID) || 498 (mCurrentPlayState != RemoteControlClient.PLAYSTATE_PLAYING))) { 499 mPlayStartTimeMs = SystemClock.elapsedRealtime(); 500 } 501 mCurrentPlayState = state; 502 503 boolean newPosValid = (mCurrentPosMs != 504 RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN); 505 long playPosition = getPlayPosition(); 506 mHandler.removeMessages(MESSAGE_PLAY_INTERVAL_TIMEOUT); 507 /* need send play position changed notification when play status is changed */ 508 if ((mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) && 509 ((oldPlayStatus != newPlayStatus) || (oldPosValid != newPosValid) || 510 (newPosValid && ((playPosition >= mNextPosMs) || (playPosition <= mPrevPosMs))))) { 511 mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED; 512 registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)playPosition); 513 } 514 if ((mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) && newPosValid && 515 (state == RemoteControlClient.PLAYSTATE_PLAYING)) { 516 Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT); 517 mHandler.sendMessageDelayed(msg, mNextPosMs - playPosition); 518 } 519 520 if ((mPlayStatusChangedNT == NOTIFICATION_TYPE_INTERIM) && (oldPlayStatus != newPlayStatus)) { 521 mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED; 522 registerNotificationRspPlayStatusNative(mPlayStatusChangedNT, newPlayStatus); 523 } 524 } 525 526 private void updateTransportControls(int transportControlFlags) { 527 mTransportControlFlags = transportControlFlags; 528 } 529 530 class Metadata { 531 private String artist; 532 private String trackTitle; 533 private String albumTitle; 534 535 public Metadata() { 536 artist = null; 537 trackTitle = null; 538 albumTitle = null; 539 } 540 541 public String toString() { 542 return "Metadata[artist=" + artist + " trackTitle=" + trackTitle + " albumTitle=" + 543 albumTitle + "]"; 544 } 545 } 546 547 private void updateMetadata(MetadataEditor data) { 548 String oldMetadata = mMetadata.toString(); 549 mMetadata.artist = data.getString(MediaMetadataRetriever.METADATA_KEY_ARTIST, null); 550 mMetadata.trackTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_TITLE, null); 551 mMetadata.albumTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_ALBUM, null); 552 if (!oldMetadata.equals(mMetadata.toString())) { 553 mTrackNumber++; 554 if (mTrackChangedNT == NOTIFICATION_TYPE_INTERIM) { 555 mTrackChangedNT = NOTIFICATION_TYPE_CHANGED; 556 sendTrackChangedRsp(); 557 } 558 559 if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) { 560 mCurrentPosMs = 0L; 561 if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) { 562 mPlayStartTimeMs = SystemClock.elapsedRealtime(); 563 } 564 } 565 /* need send play position changed notification when track is changed */ 566 if (mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) { 567 mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED; 568 registerNotificationRspPlayPosNative(mPlayPosChangedNT, 569 (int)getPlayPosition()); 570 mHandler.removeMessages(MESSAGE_PLAY_INTERVAL_TIMEOUT); 571 } 572 } 573 if (DEBUG) Log.v(TAG, "mMetadata=" + mMetadata.toString()); 574 575 mSongLengthMs = data.getLong(MediaMetadataRetriever.METADATA_KEY_DURATION, 576 RemoteControlClient.PLAYBACK_POSITION_INVALID); 577 if (DEBUG) Log.v(TAG, "duration=" + mSongLengthMs); 578 } 579 580 private void getRcFeatures(byte[] address, int features) { 581 Message msg = mHandler.obtainMessage(MESSAGE_GET_RC_FEATURES, features, 0, 582 Utils.getAddressStringFromByte(address)); 583 mHandler.sendMessage(msg); 584 } 585 586 private void getPlayStatus() { 587 Message msg = mHandler.obtainMessage(MESSAGE_GET_PLAY_STATUS); 588 mHandler.sendMessage(msg); 589 } 590 591 private void getElementAttr(byte numAttr, int[] attrs) { 592 int i; 593 ArrayList<Integer> attrList = new ArrayList<Integer>(); 594 for (i = 0; i < numAttr; ++i) { 595 attrList.add(attrs[i]); 596 } 597 Message msg = mHandler.obtainMessage(MESSAGE_GET_ELEM_ATTRS, numAttr, 0, attrList); 598 mHandler.sendMessage(msg); 599 } 600 601 private void registerNotification(int eventId, int param) { 602 Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_NOTIFICATION, eventId, param); 603 mHandler.sendMessage(msg); 604 } 605 606 private void processRegisterNotification(int eventId, int param) { 607 switch (eventId) { 608 case EVT_PLAY_STATUS_CHANGED: 609 mPlayStatusChangedNT = NOTIFICATION_TYPE_INTERIM; 610 registerNotificationRspPlayStatusNative(mPlayStatusChangedNT, 611 convertPlayStateToPlayStatus(mCurrentPlayState)); 612 break; 613 614 case EVT_TRACK_CHANGED: 615 mTrackChangedNT = NOTIFICATION_TYPE_INTERIM; 616 sendTrackChangedRsp(); 617 break; 618 619 case EVT_PLAY_POS_CHANGED: 620 long songPosition = getPlayPosition(); 621 mPlayPosChangedNT = NOTIFICATION_TYPE_INTERIM; 622 mPlaybackIntervalMs = (long)param * 1000L; 623 if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) { 624 mNextPosMs = songPosition + mPlaybackIntervalMs; 625 mPrevPosMs = songPosition - mPlaybackIntervalMs; 626 if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) { 627 Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT); 628 mHandler.sendMessageDelayed(msg, mPlaybackIntervalMs); 629 } 630 } 631 registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)songPosition); 632 break; 633 634 } 635 } 636 637 private void handlePassthroughCmd(int id, int keyState) { 638 switch (id) { 639 case BluetoothAvrcp.PASSTHROUGH_ID_REWIND: 640 rewind(keyState); 641 break; 642 case BluetoothAvrcp.PASSTHROUGH_ID_FAST_FOR: 643 fastForward(keyState); 644 break; 645 } 646 } 647 648 private void fastForward(int keyState) { 649 Message msg = mHandler.obtainMessage(MESSAGE_FAST_FORWARD, keyState, 0); 650 mHandler.sendMessage(msg); 651 } 652 653 private void rewind(int keyState) { 654 Message msg = mHandler.obtainMessage(MESSAGE_REWIND, keyState, 0); 655 mHandler.sendMessage(msg); 656 } 657 658 private void changePositionBy(long amount) { 659 long currentPosMs = getPlayPosition(); 660 if (currentPosMs == -1L) return; 661 long newPosMs = Math.max(0L, currentPosMs + amount); 662 mRemoteController.seekTo(newPosMs); 663 } 664 665 private int getSkipMultiplier() { 666 long currentTime = SystemClock.elapsedRealtime(); 667 long multi = (long) Math.pow(2, (currentTime - mSkipStartTime)/SKIP_DOUBLE_INTERVAL); 668 return (int) Math.min(MAX_MULTIPLIER_VALUE, multi); 669 } 670 671 private void sendTrackChangedRsp() { 672 byte[] track = new byte[TRACK_ID_SIZE]; 673 /* track is stored in big endian format */ 674 for (int i = 0; i < TRACK_ID_SIZE; ++i) { 675 track[i] = (byte) (mTrackNumber >> (56 - 8 * i)); 676 } 677 registerNotificationRspTrackChangeNative(mTrackChangedNT, track); 678 } 679 680 private long getPlayPosition() { 681 long songPosition = -1L; 682 if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) { 683 if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) { 684 songPosition = SystemClock.elapsedRealtime() - 685 mPlayStartTimeMs + mCurrentPosMs; 686 } else { 687 songPosition = mCurrentPosMs; 688 } 689 } 690 if (DEBUG) Log.v(TAG, "position=" + songPosition); 691 return songPosition; 692 } 693 694 private String getAttributeString(int attrId) { 695 String attrStr = null; 696 switch (attrId) { 697 case MEDIA_ATTR_TITLE: 698 attrStr = mMetadata.trackTitle; 699 break; 700 701 case MEDIA_ATTR_ARTIST: 702 attrStr = mMetadata.artist; 703 break; 704 705 case MEDIA_ATTR_ALBUM: 706 attrStr = mMetadata.albumTitle; 707 break; 708 709 case MEDIA_ATTR_PLAYING_TIME: 710 if (mSongLengthMs != 0L) { 711 attrStr = Long.toString(mSongLengthMs); 712 } 713 break; 714 715 } 716 if (attrStr == null) { 717 attrStr = new String(); 718 } 719 if (DEBUG) Log.v(TAG, "getAttributeString:attrId=" + attrId + " str=" + attrStr); 720 return attrStr; 721 } 722 723 private int convertPlayStateToPlayStatus(int playState) { 724 int playStatus = PLAYSTATUS_ERROR; 725 switch (playState) { 726 case RemoteControlClient.PLAYSTATE_PLAYING: 727 case RemoteControlClient.PLAYSTATE_BUFFERING: 728 playStatus = PLAYSTATUS_PLAYING; 729 break; 730 731 case RemoteControlClient.PLAYSTATE_STOPPED: 732 case RemoteControlClient.PLAYSTATE_NONE: 733 playStatus = PLAYSTATUS_STOPPED; 734 break; 735 736 case RemoteControlClient.PLAYSTATE_PAUSED: 737 playStatus = PLAYSTATUS_PAUSED; 738 break; 739 740 case RemoteControlClient.PLAYSTATE_FAST_FORWARDING: 741 case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS: 742 playStatus = PLAYSTATUS_FWD_SEEK; 743 break; 744 745 case RemoteControlClient.PLAYSTATE_REWINDING: 746 case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS: 747 playStatus = PLAYSTATUS_REV_SEEK; 748 break; 749 750 case RemoteControlClient.PLAYSTATE_ERROR: 751 playStatus = PLAYSTATUS_ERROR; 752 break; 753 754 } 755 return playStatus; 756 } 757 758 private boolean isPlayingState(int playState) { 759 boolean isPlaying = false; 760 switch (playState) { 761 case RemoteControlClient.PLAYSTATE_PLAYING: 762 case RemoteControlClient.PLAYSTATE_BUFFERING: 763 isPlaying = true; 764 break; 765 default: 766 isPlaying = false; 767 break; 768 } 769 return isPlaying; 770 } 771 772 /** 773 * This is called from AudioService. It will return whether this device supports abs volume. 774 * NOT USED AT THE MOMENT. 775 */ 776 public boolean isAbsoluteVolumeSupported() { 777 return ((mFeatures & BTRC_FEAT_ABSOLUTE_VOLUME) != 0); 778 } 779 780 /** 781 * We get this call from AudioService. This will send a message to our handler object, 782 * requesting our handler to call setVolumeNative() 783 */ 784 public void adjustVolume(int direction) { 785 Message msg = mHandler.obtainMessage(MESSAGE_ADJUST_VOLUME, direction, 0); 786 mHandler.sendMessage(msg); 787 } 788 789 public void setAbsoluteVolume(int volume) { 790 int avrcpVolume = convertToAvrcpVolume(volume); 791 avrcpVolume = Math.min(AVRCP_MAX_VOL, Math.max(0, avrcpVolume)); 792 mHandler.removeMessages(MESSAGE_ADJUST_VOLUME); 793 Message msg = mHandler.obtainMessage(MESSAGE_SET_ABSOLUTE_VOLUME, avrcpVolume, 0); 794 mHandler.sendMessage(msg); 795 } 796 797 /* Called in the native layer as a btrc_callback to return the volume set on the carkit in the 798 * case when the volume is change locally on the carkit. This notification is not called when 799 * the volume is changed from the phone. 800 * 801 * This method will send a message to our handler to change the local stored volume and notify 802 * AudioService to update the UI 803 */ 804 private void volumeChangeCallback(int volume, int ctype) { 805 Message msg = mHandler.obtainMessage(MESSAGE_VOLUME_CHANGED, volume, ctype); 806 mHandler.sendMessage(msg); 807 } 808 809 private void notifyVolumeChanged(int volume) { 810 volume = convertToAudioStreamVolume(volume); 811 mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 812 AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_BLUETOOTH_ABS_VOLUME); 813 } 814 815 private int convertToAudioStreamVolume(int volume) { 816 // Rescale volume to match AudioSystem's volume 817 return (int) Math.round((double) volume*mAudioStreamMax/AVRCP_MAX_VOL); 818 } 819 820 private int convertToAvrcpVolume(int volume) { 821 return (int) Math.ceil((double) volume*AVRCP_MAX_VOL/mAudioStreamMax); 822 } 823 824 /** 825 * This is called from A2dpStateMachine to set A2dp audio state. 826 */ 827 public void setA2dpAudioState(int state) { 828 Message msg = mHandler.obtainMessage(MESSAGE_SET_A2DP_AUDIO_STATE, state, 0); 829 mHandler.sendMessage(msg); 830 } 831 832 // Do not modify without updating the HAL bt_rc.h files. 833 834 // match up with btrc_play_status_t enum of bt_rc.h 835 final static int PLAYSTATUS_STOPPED = 0; 836 final static int PLAYSTATUS_PLAYING = 1; 837 final static int PLAYSTATUS_PAUSED = 2; 838 final static int PLAYSTATUS_FWD_SEEK = 3; 839 final static int PLAYSTATUS_REV_SEEK = 4; 840 final static int PLAYSTATUS_ERROR = 255; 841 842 // match up with btrc_media_attr_t enum of bt_rc.h 843 final static int MEDIA_ATTR_TITLE = 1; 844 final static int MEDIA_ATTR_ARTIST = 2; 845 final static int MEDIA_ATTR_ALBUM = 3; 846 final static int MEDIA_ATTR_TRACK_NUM = 4; 847 final static int MEDIA_ATTR_NUM_TRACKS = 5; 848 final static int MEDIA_ATTR_GENRE = 6; 849 final static int MEDIA_ATTR_PLAYING_TIME = 7; 850 851 // match up with btrc_event_id_t enum of bt_rc.h 852 final static int EVT_PLAY_STATUS_CHANGED = 1; 853 final static int EVT_TRACK_CHANGED = 2; 854 final static int EVT_TRACK_REACHED_END = 3; 855 final static int EVT_TRACK_REACHED_START = 4; 856 final static int EVT_PLAY_POS_CHANGED = 5; 857 final static int EVT_BATT_STATUS_CHANGED = 6; 858 final static int EVT_SYSTEM_STATUS_CHANGED = 7; 859 final static int EVT_APP_SETTINGS_CHANGED = 8; 860 861 // match up with btrc_notification_type_t enum of bt_rc.h 862 final static int NOTIFICATION_TYPE_INTERIM = 0; 863 final static int NOTIFICATION_TYPE_CHANGED = 1; 864 865 // match up with BTRC_UID_SIZE of bt_rc.h 866 final static int TRACK_ID_SIZE = 8; 867 868 private native static void classInitNative(); 869 private native void initNative(); 870 private native void cleanupNative(); 871 private native boolean getPlayStatusRspNative(int playStatus, int songLen, int songPos); 872 private native boolean getElementAttrRspNative(byte numAttr, int[] attrIds, String[] textArray); 873 private native boolean registerNotificationRspPlayStatusNative(int type, int playStatus); 874 private native boolean registerNotificationRspTrackChangeNative(int type, byte[] track); 875 private native boolean registerNotificationRspPlayPosNative(int type, int playPos); 876 private native boolean setVolumeNative(int volume); 877 private native boolean sendPassThroughCommandNative(int keyCode, int keyState); 878 879} 880