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