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