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