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