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.avrcp; 18 19 import java.util.Timer; 20 import java.util.TimerTask; 21 22 import android.bluetooth.BluetoothA2dp; 23 import android.bluetooth.BluetoothAvrcp; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.res.Resources; 27 import android.content.SharedPreferences; 28 import android.graphics.Bitmap; 29 import android.media.AudioManager; 30 import android.media.MediaDescription; 31 import android.media.MediaMetadata; 32 import android.media.session.MediaController; 33 import android.media.session.MediaSessionManager; 34 import android.media.session.PlaybackState; 35 import android.os.Bundle; 36 import android.os.Handler; 37 import android.os.HandlerThread; 38 import android.os.Looper; 39 import android.os.Message; 40 import android.os.ParcelUuid; 41 import android.os.PowerManager; 42 import android.os.PowerManager.WakeLock; 43 import android.os.RemoteException; 44 import android.os.ServiceManager; 45 import android.os.SystemClock; 46 import android.util.Log; 47 import android.view.KeyEvent; 48 49 import com.android.bluetooth.R; 50 import com.android.bluetooth.btservice.AdapterService; 51 import com.android.bluetooth.btservice.ProfileService; 52 import com.android.bluetooth.Utils; 53 import com.android.internal.util.IState; 54 import com.android.internal.util.State; 55 import com.android.internal.util.StateMachine; 56 57 import java.lang.ref.WeakReference; 58 import java.util.ArrayList; 59 import java.util.HashMap; 60 import java.util.List; 61 import java.util.Set; 62 /** 63 * support Bluetooth AVRCP profile. 64 * support metadata, play status and event notification 65 */ 66 public final class Avrcp { 67 private static final boolean DEBUG = false; 68 private static final String TAG = "Avrcp"; 69 private static final String ABSOLUTE_VOLUME_BLACKLIST = "absolute_volume_blacklist"; 70 71 private Context mContext; 72 private final AudioManager mAudioManager; 73 private AvrcpMessageHandler mHandler; 74 private MediaSessionManager mMediaSessionManager; 75 private MediaSessionChangeListener mSessionChangeListener; 76 private MediaController mMediaController; 77 private MediaControllerListener mMediaControllerCb; 78 private MediaAttributes mMediaAttributes; 79 private int mTransportControlFlags; 80 private PlaybackState mCurrentPlayState; 81 private long mLastStateUpdate; 82 private int mPlayStatusChangedNT; 83 private int mTrackChangedNT; 84 private int mPlayPosChangedNT; 85 private long mTrackNumber; 86 private long mSongLengthMs; 87 private long mPlaybackIntervalMs; 88 private long mLastReportedPosition; 89 private long mNextPosMs; 90 private long mPrevPosMs; 91 private long mSkipStartTime; 92 private int mFeatures; 93 private int mRemoteVolume; 94 private int mLastRemoteVolume; 95 private int mInitialRemoteVolume; 96 97 /* Local volume in audio index 0-15 */ 98 private int mLocalVolume; 99 private int mLastLocalVolume; 100 private int mAbsVolThreshold; 101 102 private String mAddress; 103 private HashMap<Integer, Integer> mVolumeMapping; 104 105 private int mLastDirection; 106 private final int mVolumeStep; 107 private final int mAudioStreamMax; 108 private boolean mVolCmdAdjustInProgress; 109 private boolean mVolCmdSetInProgress; 110 private int mAbsVolRetryTimes; 111 private int mSkipAmount; 112 113 /* BTRC features */ 114 public static final int BTRC_FEAT_METADATA = 0x01; 115 public static final int BTRC_FEAT_ABSOLUTE_VOLUME = 0x02; 116 public static final int BTRC_FEAT_BROWSE = 0x04; 117 118 /* AVRC response codes, from avrc_defs */ 119 private static final int AVRC_RSP_NOT_IMPL = 8; 120 private static final int AVRC_RSP_ACCEPT = 9; 121 private static final int AVRC_RSP_REJ = 10; 122 private static final int AVRC_RSP_IN_TRANS = 11; 123 private static final int AVRC_RSP_IMPL_STBL = 12; 124 private static final int AVRC_RSP_CHANGED = 13; 125 private static final int AVRC_RSP_INTERIM = 15; 126 127 private static final int MESSAGE_GET_RC_FEATURES = 1; 128 private static final int MESSAGE_GET_PLAY_STATUS = 2; 129 private static final int MESSAGE_GET_ELEM_ATTRS = 3; 130 private static final int MESSAGE_REGISTER_NOTIFICATION = 4; 131 private static final int MESSAGE_PLAY_INTERVAL_TIMEOUT = 5; 132 private static final int MESSAGE_VOLUME_CHANGED = 6; 133 private static final int MESSAGE_ADJUST_VOLUME = 7; 134 private static final int MESSAGE_SET_ABSOLUTE_VOLUME = 8; 135 private static final int MESSAGE_ABS_VOL_TIMEOUT = 9; 136 private static final int MESSAGE_FAST_FORWARD = 10; 137 private static final int MESSAGE_REWIND = 11; 138 private static final int MESSAGE_CHANGE_PLAY_POS = 12; 139 private static final int MESSAGE_SET_A2DP_AUDIO_STATE = 13; 140 141 private static final int BUTTON_TIMEOUT_TIME = 2000; 142 private static final int BASE_SKIP_AMOUNT = 2000; 143 private static final int KEY_STATE_PRESS = 1; 144 private static final int KEY_STATE_RELEASE = 0; 145 private static final int SKIP_PERIOD = 400; 146 private static final int SKIP_DOUBLE_INTERVAL = 3000; 147 private static final long MAX_MULTIPLIER_VALUE = 128L; 148 private static final int CMD_TIMEOUT_DELAY = 2000; 149 private static final int MAX_ERROR_RETRY_TIMES = 6; 150 private static final int AVRCP_MAX_VOL = 127; 151 private static final int AVRCP_BASE_VOLUME_STEP = 1; 152 153 static { classInitNative()154 classInitNative(); 155 } 156 Avrcp(Context context)157 private Avrcp(Context context) { 158 mMediaAttributes = new MediaAttributes(null); 159 mCurrentPlayState = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE, -1L, 0.0f).build(); 160 mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED; 161 mTrackChangedNT = NOTIFICATION_TYPE_CHANGED; 162 mTrackNumber = -1L; 163 mLastStateUpdate = -1L; 164 mSongLengthMs = 0L; 165 mPlaybackIntervalMs = 0L; 166 mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED; 167 mLastReportedPosition = -1; 168 mNextPosMs = -1; 169 mPrevPosMs = -1; 170 mFeatures = 0; 171 mRemoteVolume = -1; 172 mInitialRemoteVolume = -1; 173 mLastRemoteVolume = -1; 174 mLastDirection = 0; 175 mVolCmdAdjustInProgress = false; 176 mVolCmdSetInProgress = false; 177 mAbsVolRetryTimes = 0; 178 mLocalVolume = -1; 179 mLastLocalVolume = -1; 180 mAbsVolThreshold = 0; 181 mVolumeMapping = new HashMap<Integer, Integer>(); 182 183 mContext = context; 184 185 initNative(); 186 187 mMediaSessionManager = (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE); 188 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 189 mAudioStreamMax = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); 190 mVolumeStep = Math.max(AVRCP_BASE_VOLUME_STEP, AVRCP_MAX_VOL/mAudioStreamMax); 191 Resources resources = context.getResources(); 192 if (resources != null) { 193 mAbsVolThreshold = resources.getInteger(R.integer.a2dp_absolute_volume_initial_threshold); 194 } 195 } 196 start()197 private void start() { 198 HandlerThread thread = new HandlerThread("BluetoothAvrcpHandler"); 199 thread.start(); 200 Looper looper = thread.getLooper(); 201 mHandler = new AvrcpMessageHandler(looper); 202 203 mSessionChangeListener = new MediaSessionChangeListener(); 204 mMediaSessionManager.addOnActiveSessionsChangedListener(mSessionChangeListener, null, mHandler); 205 List<MediaController> sessions = mMediaSessionManager.getActiveSessions(null); 206 mMediaControllerCb = new MediaControllerListener(); 207 if (sessions.size() > 0) { 208 updateCurrentMediaController(sessions.get(0)); 209 } 210 } 211 make(Context context)212 public static Avrcp make(Context context) { 213 if (DEBUG) Log.v(TAG, "make"); 214 Avrcp ar = new Avrcp(context); 215 ar.start(); 216 return ar; 217 } 218 doQuit()219 public void doQuit() { 220 mHandler.removeCallbacksAndMessages(null); 221 Looper looper = mHandler.getLooper(); 222 if (looper != null) { 223 looper.quit(); 224 } 225 mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionChangeListener); 226 } 227 cleanup()228 public void cleanup() { 229 cleanupNative(); 230 if (mVolumeMapping != null) 231 mVolumeMapping.clear(); 232 } 233 234 private class MediaControllerListener extends MediaController.Callback { 235 @Override onMetadataChanged(MediaMetadata metadata)236 public void onMetadataChanged(MediaMetadata metadata) { 237 Log.v(TAG, "MediaController metadata changed"); 238 updateMetadata(metadata); 239 } 240 241 @Override onPlaybackStateChanged(PlaybackState state)242 public void onPlaybackStateChanged(PlaybackState state) { 243 Log.v(TAG, "MediaController playback changed: " + state.toString()); 244 updatePlaybackState(state); 245 } 246 247 @Override onSessionDestroyed()248 public void onSessionDestroyed() { 249 Log.v(TAG, "MediaController session destroyed"); 250 } 251 } 252 253 private class MediaSessionChangeListener implements MediaSessionManager.OnActiveSessionsChangedListener { MediaSessionChangeListener()254 public MediaSessionChangeListener() { 255 } 256 257 @Override onActiveSessionsChanged(List<MediaController> controllers)258 public void onActiveSessionsChanged(List<MediaController> controllers) { 259 Log.v(TAG, "Active sessions changed, " + controllers.size() + " sessions"); 260 if (controllers.size() > 0) { 261 updateCurrentMediaController(controllers.get(0)); 262 } 263 } 264 } 265 updateCurrentMediaController(MediaController controller)266 private void updateCurrentMediaController(MediaController controller) { 267 Log.v(TAG, "Updating media controller to " + controller); 268 if (mMediaController != null) { 269 mMediaController.unregisterCallback(mMediaControllerCb); 270 } 271 mMediaController = controller; 272 if (mMediaController == null) { 273 updateMetadata(null); 274 return; 275 } 276 mMediaController.registerCallback(mMediaControllerCb, mHandler); 277 updateMetadata(mMediaController.getMetadata()); 278 } 279 280 /** Handles Avrcp messages. */ 281 private final class AvrcpMessageHandler extends Handler { AvrcpMessageHandler(Looper looper)282 private AvrcpMessageHandler(Looper looper) { 283 super(looper); 284 } 285 286 @Override handleMessage(Message msg)287 public void handleMessage(Message msg) { 288 switch (msg.what) { 289 case MESSAGE_GET_RC_FEATURES: 290 String address = (String) msg.obj; 291 if (DEBUG) Log.v(TAG, "MESSAGE_GET_RC_FEATURES: address="+address+ 292 ", features="+msg.arg1); 293 mFeatures = msg.arg1; 294 mFeatures = modifyRcFeatureFromBlacklist(mFeatures, address); 295 mAudioManager.avrcpSupportsAbsoluteVolume(address, isAbsoluteVolumeSupported()); 296 mLastLocalVolume = -1; 297 mRemoteVolume = -1; 298 mLocalVolume = -1; 299 mInitialRemoteVolume = -1; 300 mAddress = address; 301 if (mVolumeMapping != null) 302 mVolumeMapping.clear(); 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 String[] textArray; 313 int[] attrIds; 314 byte numAttr = (byte) msg.arg1; 315 ArrayList<Integer> attrList = (ArrayList<Integer>) msg.obj; 316 Log.v(TAG, "MESSAGE_GET_ELEM_ATTRS:numAttr=" + numAttr); 317 attrIds = new int[numAttr]; 318 textArray = new String[numAttr]; 319 for (int i = 0; i < numAttr; ++i) { 320 attrIds[i] = attrList.get(i).intValue(); 321 textArray[i] = mMediaAttributes.getString(attrIds[i]); 322 Log.v(TAG, "getAttributeString:attrId=" + attrIds[i] + 323 " str=" + textArray[i]); 324 } 325 getElementAttrRspNative(numAttr, attrIds, textArray); 326 break; 327 328 case MESSAGE_REGISTER_NOTIFICATION: 329 if (DEBUG) Log.v(TAG, "MESSAGE_REGISTER_NOTIFICATION:event=" + msg.arg1 + 330 " param=" + msg.arg2); 331 processRegisterNotification(msg.arg1, msg.arg2); 332 break; 333 334 case MESSAGE_PLAY_INTERVAL_TIMEOUT: 335 if (DEBUG) Log.v(TAG, "MESSAGE_PLAY_INTERVAL_TIMEOUT"); 336 sendPlayPosNotificationRsp(false); 337 break; 338 339 case MESSAGE_VOLUME_CHANGED: 340 if (!isAbsoluteVolumeSupported()) { 341 if (DEBUG) Log.v(TAG, "ignore MESSAGE_VOLUME_CHANGED"); 342 break; 343 } 344 345 if (DEBUG) Log.v(TAG, "MESSAGE_VOLUME_CHANGED: volume=" + ((byte)msg.arg1 & 0x7f) 346 + " ctype=" + msg.arg2); 347 348 349 boolean volAdj = false; 350 if (msg.arg2 == AVRC_RSP_ACCEPT || msg.arg2 == AVRC_RSP_REJ) { 351 if (mVolCmdAdjustInProgress == false && mVolCmdSetInProgress == false) { 352 Log.e(TAG, "Unsolicited response, ignored"); 353 break; 354 } 355 removeMessages(MESSAGE_ABS_VOL_TIMEOUT); 356 357 volAdj = mVolCmdAdjustInProgress; 358 mVolCmdAdjustInProgress = false; 359 mVolCmdSetInProgress = false; 360 mAbsVolRetryTimes = 0; 361 } 362 363 byte absVol = (byte)((byte)msg.arg1 & 0x7f); // discard MSB as it is RFD 364 // convert remote volume to local volume 365 int volIndex = convertToAudioStreamVolume(absVol); 366 if (mInitialRemoteVolume == -1) { 367 mInitialRemoteVolume = absVol; 368 if (mAbsVolThreshold > 0 && mAbsVolThreshold < mAudioStreamMax && volIndex > mAbsVolThreshold) { 369 if (DEBUG) Log.v(TAG, "remote inital volume too high " + volIndex + ">" + mAbsVolThreshold); 370 Message msg1 = mHandler.obtainMessage(MESSAGE_SET_ABSOLUTE_VOLUME, mAbsVolThreshold , 0); 371 mHandler.sendMessage(msg1); 372 mRemoteVolume = absVol; 373 mLocalVolume = volIndex; 374 break; 375 } 376 } 377 378 if (mLocalVolume != volIndex && (msg.arg2 == AVRC_RSP_ACCEPT || 379 msg.arg2 == AVRC_RSP_CHANGED || 380 msg.arg2 == AVRC_RSP_INTERIM)) { 381 /* If the volume has successfully changed */ 382 mLocalVolume = volIndex; 383 if (mLastLocalVolume != -1 && msg.arg2 == AVRC_RSP_ACCEPT) { 384 if (mLastLocalVolume != volIndex) { 385 /* remote volume changed more than requested due to 386 * local and remote has different volume steps */ 387 if (DEBUG) Log.d(TAG, "Remote returned volume does not match desired volume " 388 + mLastLocalVolume + " vs " 389 + volIndex); 390 mLastLocalVolume = mLocalVolume; 391 } 392 } 393 // remember the remote volume value, as it's the one supported by remote 394 if (volAdj) { 395 synchronized (mVolumeMapping) { 396 mVolumeMapping.put(volIndex, (int)absVol); 397 if (DEBUG) Log.v(TAG, "remember volume mapping " +volIndex+ "-"+absVol); 398 } 399 } 400 401 notifyVolumeChanged(mLocalVolume); 402 mRemoteVolume = absVol; 403 long pecentVolChanged = ((long)absVol * 100) / 0x7f; 404 Log.e(TAG, "percent volume changed: " + pecentVolChanged + "%"); 405 } else if (msg.arg2 == AVRC_RSP_REJ) { 406 Log.e(TAG, "setAbsoluteVolume call rejected"); 407 } else if (volAdj && mLastRemoteVolume > 0 && mLastRemoteVolume < AVRCP_MAX_VOL && 408 mLocalVolume == volIndex && 409 (msg.arg2 == AVRC_RSP_ACCEPT )) { 410 /* oops, the volume is still same, remote does not like the value 411 * retry a volume one step up/down */ 412 if (DEBUG) Log.d(TAG, "Remote device didn't tune volume, let's try one more step."); 413 int retry_volume = Math.min(AVRCP_MAX_VOL, 414 Math.max(0, mLastRemoteVolume + mLastDirection)); 415 if (setVolumeNative(retry_volume)) { 416 mLastRemoteVolume = retry_volume; 417 sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT), 418 CMD_TIMEOUT_DELAY); 419 mVolCmdAdjustInProgress = true; 420 } 421 } 422 break; 423 424 case MESSAGE_ADJUST_VOLUME: 425 if (!isAbsoluteVolumeSupported()) { 426 if (DEBUG) Log.v(TAG, "ignore MESSAGE_ADJUST_VOLUME"); 427 break; 428 } 429 430 if (DEBUG) Log.d(TAG, "MESSAGE_ADJUST_VOLUME: direction=" + msg.arg1); 431 432 if (mVolCmdAdjustInProgress || mVolCmdSetInProgress) { 433 if (DEBUG) Log.w(TAG, "There is already a volume command in progress."); 434 break; 435 } 436 437 // Remote device didn't set initial volume. Let's black list it 438 if (mInitialRemoteVolume == -1) { 439 Log.d(TAG, "remote " + mAddress + " never tell us initial volume, black list it."); 440 blackListCurrentDevice(); 441 break; 442 } 443 444 // Wait on verification on volume from device, before changing the volume. 445 if (mRemoteVolume != -1 && (msg.arg1 == -1 || msg.arg1 == 1)) { 446 int setVol = -1; 447 int targetVolIndex = -1; 448 if (mLocalVolume == 0 && msg.arg1 == -1) { 449 if (DEBUG) Log.w(TAG, "No need to Vol down from 0."); 450 break; 451 } 452 if (mLocalVolume == mAudioStreamMax && msg.arg1 == 1) { 453 if (DEBUG) Log.w(TAG, "No need to Vol up from max."); 454 break; 455 } 456 457 targetVolIndex = mLocalVolume + msg.arg1; 458 if (DEBUG) Log.d(TAG, "Adjusting volume to " + targetVolIndex); 459 460 Integer i; 461 synchronized (mVolumeMapping) { 462 i = mVolumeMapping.get(targetVolIndex); 463 } 464 465 if (i != null) { 466 /* if we already know this volume mapping, use it */ 467 setVol = i.byteValue(); 468 if (setVol == mRemoteVolume) { 469 if (DEBUG) Log.d(TAG, "got same volume from mapping for " + targetVolIndex + ", ignore."); 470 setVol = -1; 471 } 472 if (DEBUG) Log.d(TAG, "set volume from mapping " + targetVolIndex + "-" + setVol); 473 } 474 475 if (setVol == -1) { 476 /* otherwise use phone steps */ 477 setVol = Math.min(AVRCP_MAX_VOL, 478 convertToAvrcpVolume(Math.max(0, targetVolIndex))); 479 if (DEBUG) Log.d(TAG, "set volume from local volume "+ targetVolIndex+"-"+ setVol); 480 } 481 482 if (setVolumeNative(setVol)) { 483 sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT), 484 CMD_TIMEOUT_DELAY); 485 mVolCmdAdjustInProgress = true; 486 mLastDirection = msg.arg1; 487 mLastRemoteVolume = setVol; 488 mLastLocalVolume = targetVolIndex; 489 } else { 490 if (DEBUG) Log.d(TAG, "setVolumeNative failed"); 491 } 492 } else { 493 Log.e(TAG, "Unknown direction in MESSAGE_ADJUST_VOLUME"); 494 } 495 break; 496 497 case MESSAGE_SET_ABSOLUTE_VOLUME: 498 if (!isAbsoluteVolumeSupported()) { 499 if (DEBUG) Log.v(TAG, "ignore MESSAGE_SET_ABSOLUTE_VOLUME"); 500 break; 501 } 502 503 if (DEBUG) Log.v(TAG, "MESSAGE_SET_ABSOLUTE_VOLUME"); 504 505 if (mVolCmdSetInProgress || mVolCmdAdjustInProgress) { 506 if (DEBUG) Log.w(TAG, "There is already a volume command in progress."); 507 break; 508 } 509 510 // Remote device didn't set initial volume. Let's black list it 511 if (mInitialRemoteVolume == -1) { 512 if (DEBUG) Log.d(TAG, "remote " + mAddress + " never tell us initial volume, black list it."); 513 blackListCurrentDevice(); 514 break; 515 } 516 517 int avrcpVolume = convertToAvrcpVolume(msg.arg1); 518 avrcpVolume = Math.min(AVRCP_MAX_VOL, Math.max(0, avrcpVolume)); 519 if (DEBUG) Log.d(TAG, "Setting volume to " + msg.arg1 + "-" + avrcpVolume); 520 if (setVolumeNative(avrcpVolume)) { 521 sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT), CMD_TIMEOUT_DELAY); 522 mVolCmdSetInProgress = true; 523 mLastRemoteVolume = avrcpVolume; 524 mLastLocalVolume = msg.arg1; 525 } else { 526 if (DEBUG) Log.d(TAG, "setVolumeNative failed"); 527 } 528 break; 529 530 case MESSAGE_ABS_VOL_TIMEOUT: 531 if (DEBUG) Log.v(TAG, "MESSAGE_ABS_VOL_TIMEOUT: Volume change cmd timed out."); 532 mVolCmdAdjustInProgress = false; 533 mVolCmdSetInProgress = false; 534 if (mAbsVolRetryTimes >= MAX_ERROR_RETRY_TIMES) { 535 mAbsVolRetryTimes = 0; 536 /* too many volume change failures, black list the device */ 537 blackListCurrentDevice(); 538 } else { 539 mAbsVolRetryTimes += 1; 540 if (setVolumeNative(mLastRemoteVolume)) { 541 sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT), 542 CMD_TIMEOUT_DELAY); 543 mVolCmdSetInProgress = true; 544 } 545 } 546 break; 547 548 case MESSAGE_FAST_FORWARD: 549 case MESSAGE_REWIND: 550 if (msg.what == MESSAGE_FAST_FORWARD) { 551 if ((mCurrentPlayState.getActions() & 552 PlaybackState.ACTION_FAST_FORWARD) != 0) { 553 int keyState = msg.arg1 == KEY_STATE_PRESS ? 554 KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP; 555 KeyEvent keyEvent = 556 new KeyEvent(keyState, KeyEvent.KEYCODE_MEDIA_FAST_FORWARD); 557 mMediaController.dispatchMediaButtonEvent(keyEvent); 558 break; 559 } 560 } else if ((mCurrentPlayState.getActions() & 561 PlaybackState.ACTION_REWIND) != 0) { 562 int keyState = msg.arg1 == KEY_STATE_PRESS ? 563 KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP; 564 KeyEvent keyEvent = 565 new KeyEvent(keyState, KeyEvent.KEYCODE_MEDIA_REWIND); 566 mMediaController.dispatchMediaButtonEvent(keyEvent); 567 break; 568 } 569 570 int skipAmount; 571 if (msg.what == MESSAGE_FAST_FORWARD) { 572 if (DEBUG) Log.v(TAG, "MESSAGE_FAST_FORWARD"); 573 removeMessages(MESSAGE_FAST_FORWARD); 574 skipAmount = BASE_SKIP_AMOUNT; 575 } else { 576 if (DEBUG) Log.v(TAG, "MESSAGE_REWIND"); 577 removeMessages(MESSAGE_REWIND); 578 skipAmount = -BASE_SKIP_AMOUNT; 579 } 580 581 if (hasMessages(MESSAGE_CHANGE_PLAY_POS) && 582 (skipAmount != mSkipAmount)) { 583 Log.w(TAG, "missing release button event:" + mSkipAmount); 584 } 585 586 if ((!hasMessages(MESSAGE_CHANGE_PLAY_POS)) || 587 (skipAmount != mSkipAmount)) { 588 mSkipStartTime = SystemClock.elapsedRealtime(); 589 } 590 591 removeMessages(MESSAGE_CHANGE_PLAY_POS); 592 if (msg.arg1 == KEY_STATE_PRESS) { 593 mSkipAmount = skipAmount; 594 changePositionBy(mSkipAmount * getSkipMultiplier()); 595 Message posMsg = obtainMessage(MESSAGE_CHANGE_PLAY_POS); 596 posMsg.arg1 = 1; 597 sendMessageDelayed(posMsg, SKIP_PERIOD); 598 } 599 600 break; 601 602 case MESSAGE_CHANGE_PLAY_POS: 603 if (DEBUG) Log.v(TAG, "MESSAGE_CHANGE_PLAY_POS:" + msg.arg1); 604 changePositionBy(mSkipAmount * getSkipMultiplier()); 605 if (msg.arg1 * SKIP_PERIOD < BUTTON_TIMEOUT_TIME) { 606 Message posMsg = obtainMessage(MESSAGE_CHANGE_PLAY_POS); 607 posMsg.arg1 = msg.arg1 + 1; 608 sendMessageDelayed(posMsg, SKIP_PERIOD); 609 } 610 break; 611 612 case MESSAGE_SET_A2DP_AUDIO_STATE: 613 if (DEBUG) Log.v(TAG, "MESSAGE_SET_A2DP_AUDIO_STATE:" + msg.arg1); 614 updateA2dpAudioState(msg.arg1); 615 break; 616 } 617 } 618 } 619 updateA2dpAudioState(int state)620 private void updateA2dpAudioState(int state) { 621 boolean isPlaying = (state == BluetoothA2dp.STATE_PLAYING); 622 if (isPlaying != isPlayingState(mCurrentPlayState)) { 623 /* if a2dp is streaming, check to make sure music is active */ 624 if (isPlaying && !mAudioManager.isMusicActive()) 625 return; 626 PlaybackState.Builder builder = new PlaybackState.Builder(); 627 if (isPlaying) { 628 builder.setState(PlaybackState.STATE_PLAYING, 629 PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f); 630 } else { 631 builder.setState(PlaybackState.STATE_PAUSED, 632 PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f); 633 } 634 updatePlaybackState(builder.build()); 635 } 636 } 637 updatePlaybackState(PlaybackState state)638 private void updatePlaybackState(PlaybackState state) { 639 if (state == null) { 640 state = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE, 641 PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f).build(); 642 } 643 644 int oldPlayStatus = convertPlayStateToPlayStatus(mCurrentPlayState); 645 int newPlayStatus = convertPlayStateToPlayStatus(state); 646 647 if (DEBUG) { 648 Log.v(TAG, "updatePlaybackState (" + mPlayStatusChangedNT + "): "+ 649 "old=" + mCurrentPlayState + "(" + oldPlayStatus + "), "+ 650 "new=" + state + "(" + newPlayStatus + ")"); 651 } 652 653 mCurrentPlayState = state; 654 mLastStateUpdate = SystemClock.elapsedRealtime(); 655 656 sendPlayPosNotificationRsp(false); 657 658 if (mPlayStatusChangedNT == NOTIFICATION_TYPE_INTERIM && 659 (oldPlayStatus != newPlayStatus)) { 660 mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED; 661 registerNotificationRspPlayStatusNative(mPlayStatusChangedNT, newPlayStatus); 662 } 663 } 664 updateTransportControls(int transportControlFlags)665 private void updateTransportControls(int transportControlFlags) { 666 mTransportControlFlags = transportControlFlags; 667 } 668 669 class MediaAttributes { 670 private boolean exists; 671 private String title; 672 private String artistName; 673 private String albumName; 674 private String mediaNumber; 675 private String mediaTotalNumber; 676 private String genre; 677 private String playingTimeMs; 678 679 private static final int ATTR_TITLE = 1; 680 private static final int ATTR_ARTIST_NAME = 2; 681 private static final int ATTR_ALBUM_NAME = 3; 682 private static final int ATTR_MEDIA_NUMBER = 4; 683 private static final int ATTR_MEDIA_TOTAL_NUMBER = 5; 684 private static final int ATTR_GENRE = 6; 685 private static final int ATTR_PLAYING_TIME_MS = 7; 686 687 MediaAttributes(MediaMetadata data)688 public MediaAttributes(MediaMetadata data) { 689 exists = data != null; 690 if (!exists) 691 return; 692 693 artistName = stringOrBlank(data.getString(MediaMetadata.METADATA_KEY_ARTIST)); 694 albumName = stringOrBlank(data.getString(MediaMetadata.METADATA_KEY_ALBUM)); 695 mediaNumber = longStringOrBlank(data.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER)); 696 mediaTotalNumber = longStringOrBlank(data.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS)); 697 genre = stringOrBlank(data.getString(MediaMetadata.METADATA_KEY_GENRE)); 698 playingTimeMs = longStringOrBlank(data.getLong(MediaMetadata.METADATA_KEY_DURATION)); 699 700 // Try harder for the title. 701 title = data.getString(MediaMetadata.METADATA_KEY_TITLE); 702 703 if (title == null) { 704 MediaDescription desc = data.getDescription(); 705 if (desc != null) { 706 CharSequence val = desc.getDescription(); 707 if (val != null) 708 title = val.toString(); 709 } 710 } 711 712 if (title == null) 713 title = new String(); 714 } 715 equals(MediaAttributes other)716 public boolean equals(MediaAttributes other) { 717 if (other == null) 718 return false; 719 720 if (exists != other.exists) 721 return false; 722 723 if (exists == false) 724 return true; 725 726 return (title.equals(other.title)) && 727 (artistName.equals(other.artistName)) && 728 (albumName.equals(other.albumName)) && 729 (mediaNumber.equals(other.mediaNumber)) && 730 (mediaTotalNumber.equals(other.mediaTotalNumber)) && 731 (genre.equals(other.genre)) && 732 (playingTimeMs.equals(other.playingTimeMs)); 733 } 734 getString(int attrId)735 public String getString(int attrId) { 736 if (!exists) 737 return new String(); 738 739 switch (attrId) { 740 case ATTR_TITLE: 741 return title; 742 case ATTR_ARTIST_NAME: 743 return artistName; 744 case ATTR_ALBUM_NAME: 745 return albumName; 746 case ATTR_MEDIA_NUMBER: 747 return mediaNumber; 748 case ATTR_MEDIA_TOTAL_NUMBER: 749 return mediaTotalNumber; 750 case ATTR_GENRE: 751 return genre; 752 case ATTR_PLAYING_TIME_MS: 753 return playingTimeMs; 754 default: 755 return new String(); 756 } 757 } 758 stringOrBlank(String s)759 private String stringOrBlank(String s) { 760 return s == null ? new String() : s; 761 } 762 longStringOrBlank(Long s)763 private String longStringOrBlank(Long s) { 764 return s == null ? new String() : s.toString(); 765 } 766 toString()767 public String toString() { 768 if (!exists) 769 return "[MediaAttributes: none]"; 770 771 return "[MediaAttributes: " + title + " - " + albumName + " by " 772 + artistName + " (" + mediaNumber + "/" + mediaTotalNumber + ") " 773 + genre + "]"; 774 } 775 } 776 updateMetadata(MediaMetadata data)777 private void updateMetadata(MediaMetadata data) { 778 MediaAttributes oldAttributes = mMediaAttributes; 779 mMediaAttributes = new MediaAttributes(data); 780 if (data == null) { 781 mSongLengthMs = 0L; 782 } else { 783 mSongLengthMs = data.getLong(MediaMetadata.METADATA_KEY_DURATION); 784 } 785 if (!oldAttributes.equals(mMediaAttributes)) { 786 Log.v(TAG, "MediaAttributes Changed to " + mMediaAttributes.toString()); 787 mTrackNumber++; 788 789 if (mTrackChangedNT == NOTIFICATION_TYPE_INTERIM) { 790 mTrackChangedNT = NOTIFICATION_TYPE_CHANGED; 791 sendTrackChangedRsp(); 792 } 793 } else { 794 Log.v(TAG, "Updated " + mMediaAttributes.toString() + " but no change!"); 795 } 796 797 // Update the play state, which sends play state and play position 798 // notifications if needed. 799 if (mMediaController != null) { 800 updatePlaybackState(mMediaController.getPlaybackState()); 801 } else { 802 updatePlaybackState(null); 803 } 804 } 805 getRcFeatures(byte[] address, int features)806 private void getRcFeatures(byte[] address, int features) { 807 Message msg = mHandler.obtainMessage(MESSAGE_GET_RC_FEATURES, features, 0, 808 Utils.getAddressStringFromByte(address)); 809 mHandler.sendMessage(msg); 810 } 811 getPlayStatus()812 private void getPlayStatus() { 813 Message msg = mHandler.obtainMessage(MESSAGE_GET_PLAY_STATUS); 814 mHandler.sendMessage(msg); 815 } 816 getElementAttr(byte numAttr, int[] attrs)817 private void getElementAttr(byte numAttr, int[] attrs) { 818 int i; 819 ArrayList<Integer> attrList = new ArrayList<Integer>(); 820 for (i = 0; i < numAttr; ++i) { 821 attrList.add(attrs[i]); 822 } 823 Message msg = mHandler.obtainMessage(MESSAGE_GET_ELEM_ATTRS, numAttr, 0, attrList); 824 mHandler.sendMessage(msg); 825 } 826 registerNotification(int eventId, int param)827 private void registerNotification(int eventId, int param) { 828 Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_NOTIFICATION, eventId, param); 829 mHandler.sendMessage(msg); 830 } 831 processRegisterNotification(int eventId, int param)832 private void processRegisterNotification(int eventId, int param) { 833 switch (eventId) { 834 case EVT_PLAY_STATUS_CHANGED: 835 mPlayStatusChangedNT = NOTIFICATION_TYPE_INTERIM; 836 registerNotificationRspPlayStatusNative(mPlayStatusChangedNT, 837 convertPlayStateToPlayStatus(mCurrentPlayState)); 838 break; 839 840 case EVT_TRACK_CHANGED: 841 mTrackChangedNT = NOTIFICATION_TYPE_INTERIM; 842 sendTrackChangedRsp(); 843 break; 844 845 case EVT_PLAY_POS_CHANGED: 846 mPlayPosChangedNT = NOTIFICATION_TYPE_INTERIM; 847 mPlaybackIntervalMs = (long)param * 1000L; 848 sendPlayPosNotificationRsp(true); 849 break; 850 851 } 852 } 853 handlePassthroughCmd(int id, int keyState)854 private void handlePassthroughCmd(int id, int keyState) { 855 switch (id) { 856 case BluetoothAvrcp.PASSTHROUGH_ID_REWIND: 857 rewind(keyState); 858 break; 859 case BluetoothAvrcp.PASSTHROUGH_ID_FAST_FOR: 860 fastForward(keyState); 861 break; 862 } 863 } 864 fastForward(int keyState)865 private void fastForward(int keyState) { 866 Message msg = mHandler.obtainMessage(MESSAGE_FAST_FORWARD, keyState, 0); 867 mHandler.sendMessage(msg); 868 } 869 rewind(int keyState)870 private void rewind(int keyState) { 871 Message msg = mHandler.obtainMessage(MESSAGE_REWIND, keyState, 0); 872 mHandler.sendMessage(msg); 873 } 874 changePositionBy(long amount)875 private void changePositionBy(long amount) { 876 long currentPosMs = getPlayPosition(); 877 if (currentPosMs == -1L) return; 878 long newPosMs = Math.max(0L, currentPosMs + amount); 879 mMediaController.getTransportControls().seekTo(newPosMs); 880 } 881 getSkipMultiplier()882 private int getSkipMultiplier() { 883 long currentTime = SystemClock.elapsedRealtime(); 884 long multi = (long) Math.pow(2, (currentTime - mSkipStartTime)/SKIP_DOUBLE_INTERVAL); 885 return (int) Math.min(MAX_MULTIPLIER_VALUE, multi); 886 } 887 sendTrackChangedRsp()888 private void sendTrackChangedRsp() { 889 byte[] track = new byte[TRACK_ID_SIZE]; 890 891 /* If no track is currently selected, then return 892 0xFFFFFFFFFFFFFFFF in the interim response */ 893 long trackNumberRsp = -1L; 894 895 if (isPlayingState(mCurrentPlayState)) { 896 trackNumberRsp = mTrackNumber; 897 } 898 899 /* track is stored in big endian format */ 900 for (int i = 0; i < TRACK_ID_SIZE; ++i) { 901 track[i] = (byte) (trackNumberRsp >> (56 - 8 * i)); 902 } 903 registerNotificationRspTrackChangeNative(mTrackChangedNT, track); 904 } 905 getPlayPosition()906 private long getPlayPosition() { 907 if (mCurrentPlayState == null) 908 return -1L; 909 910 if (mCurrentPlayState.getPosition() == PlaybackState.PLAYBACK_POSITION_UNKNOWN) 911 return -1L; 912 913 if (isPlayingState(mCurrentPlayState)) { 914 return SystemClock.elapsedRealtime() - mLastStateUpdate + mCurrentPlayState.getPosition(); 915 } 916 917 return mCurrentPlayState.getPosition(); 918 } 919 convertPlayStateToPlayStatus(PlaybackState state)920 private int convertPlayStateToPlayStatus(PlaybackState state) { 921 int playStatus = PLAYSTATUS_ERROR; 922 switch (state.getState()) { 923 case PlaybackState.STATE_PLAYING: 924 case PlaybackState.STATE_BUFFERING: 925 playStatus = PLAYSTATUS_PLAYING; 926 break; 927 928 case PlaybackState.STATE_STOPPED: 929 case PlaybackState.STATE_NONE: 930 playStatus = PLAYSTATUS_STOPPED; 931 break; 932 933 case PlaybackState.STATE_PAUSED: 934 playStatus = PLAYSTATUS_PAUSED; 935 break; 936 937 case PlaybackState.STATE_FAST_FORWARDING: 938 case PlaybackState.STATE_SKIPPING_TO_NEXT: 939 case PlaybackState.STATE_SKIPPING_TO_QUEUE_ITEM: 940 playStatus = PLAYSTATUS_FWD_SEEK; 941 break; 942 943 case PlaybackState.STATE_REWINDING: 944 case PlaybackState.STATE_SKIPPING_TO_PREVIOUS: 945 playStatus = PLAYSTATUS_REV_SEEK; 946 break; 947 948 case PlaybackState.STATE_ERROR: 949 playStatus = PLAYSTATUS_ERROR; 950 break; 951 952 } 953 return playStatus; 954 } 955 isPlayingState(PlaybackState state)956 private boolean isPlayingState(PlaybackState state) { 957 return (state.getState() == PlaybackState.STATE_PLAYING) || 958 (state.getState() == PlaybackState.STATE_BUFFERING); 959 } 960 961 /** 962 * Sends a play position notification, or schedules one to be 963 * sent later at an appropriate time. If |requested| is true, 964 * does both because this was called in reponse to a request from the 965 * TG. 966 */ sendPlayPosNotificationRsp(boolean requested)967 private void sendPlayPosNotificationRsp(boolean requested) { 968 if (!requested && mPlayPosChangedNT != NOTIFICATION_TYPE_INTERIM) { 969 if (DEBUG) Log.d(TAG, "sendPlayPosNotificationRsp: Not registered or requesting."); 970 return; 971 } 972 973 long playPositionMs = getPlayPosition(); 974 975 // mNextPosMs is set to -1 when the previous position was invalid 976 // so this will be true if the new position is valid & old was invalid. 977 // mPlayPositionMs is set to -1 when the new position is invalid, 978 // and the old mPrevPosMs is >= 0 so this is true when the new is invalid 979 // and the old was valid. 980 if (DEBUG) Log.d(TAG, "sendPlayPosNotificationRsp: (" + requested + ") " 981 + mPrevPosMs + " <=? " + playPositionMs + " <=? " + mNextPosMs); 982 if (requested || ((mLastReportedPosition != playPositionMs) && 983 (playPositionMs >= mNextPosMs) || (playPositionMs <= mPrevPosMs))) { 984 if (!requested) mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED; 985 registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)playPositionMs); 986 mLastReportedPosition = playPositionMs; 987 if (playPositionMs != PlaybackState.PLAYBACK_POSITION_UNKNOWN) { 988 mNextPosMs = playPositionMs + mPlaybackIntervalMs; 989 mPrevPosMs = playPositionMs - mPlaybackIntervalMs; 990 } else { 991 mNextPosMs = -1; 992 mPrevPosMs = -1; 993 } 994 } 995 996 mHandler.removeMessages(MESSAGE_PLAY_INTERVAL_TIMEOUT); 997 if (mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM && isPlayingState(mCurrentPlayState)) { 998 Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT); 999 long delay = mPlaybackIntervalMs; 1000 if (mNextPosMs != -1) { 1001 delay = mNextPosMs - (playPositionMs > 0 ? playPositionMs : 0); 1002 } 1003 if (DEBUG) Log.d(TAG, "PLAY_INTERVAL_TIMEOUT set for " + delay + "ms from now"); 1004 mHandler.sendMessageDelayed(msg, delay); 1005 } 1006 } 1007 1008 /** 1009 * This is called from AudioService. It will return whether this device supports abs volume. 1010 * NOT USED AT THE MOMENT. 1011 */ isAbsoluteVolumeSupported()1012 public boolean isAbsoluteVolumeSupported() { 1013 return ((mFeatures & BTRC_FEAT_ABSOLUTE_VOLUME) != 0); 1014 } 1015 1016 /** 1017 * We get this call from AudioService. This will send a message to our handler object, 1018 * requesting our handler to call setVolumeNative() 1019 */ adjustVolume(int direction)1020 public void adjustVolume(int direction) { 1021 Message msg = mHandler.obtainMessage(MESSAGE_ADJUST_VOLUME, direction, 0); 1022 mHandler.sendMessage(msg); 1023 } 1024 setAbsoluteVolume(int volume)1025 public void setAbsoluteVolume(int volume) { 1026 if (volume == mLocalVolume) { 1027 if (DEBUG) Log.v(TAG, "setAbsoluteVolume is setting same index, ignore "+volume); 1028 return; 1029 } 1030 1031 mHandler.removeMessages(MESSAGE_ADJUST_VOLUME); 1032 Message msg = mHandler.obtainMessage(MESSAGE_SET_ABSOLUTE_VOLUME, volume, 0); 1033 mHandler.sendMessage(msg); 1034 } 1035 1036 /* Called in the native layer as a btrc_callback to return the volume set on the carkit in the 1037 * case when the volume is change locally on the carkit. This notification is not called when 1038 * the volume is changed from the phone. 1039 * 1040 * This method will send a message to our handler to change the local stored volume and notify 1041 * AudioService to update the UI 1042 */ volumeChangeCallback(int volume, int ctype)1043 private void volumeChangeCallback(int volume, int ctype) { 1044 Message msg = mHandler.obtainMessage(MESSAGE_VOLUME_CHANGED, volume, ctype); 1045 mHandler.sendMessage(msg); 1046 } 1047 notifyVolumeChanged(int volume)1048 private void notifyVolumeChanged(int volume) { 1049 mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 1050 AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_BLUETOOTH_ABS_VOLUME); 1051 } 1052 convertToAudioStreamVolume(int volume)1053 private int convertToAudioStreamVolume(int volume) { 1054 // Rescale volume to match AudioSystem's volume 1055 return (int) Math.floor((double) volume*mAudioStreamMax/AVRCP_MAX_VOL); 1056 } 1057 convertToAvrcpVolume(int volume)1058 private int convertToAvrcpVolume(int volume) { 1059 return (int) Math.ceil((double) volume*AVRCP_MAX_VOL/mAudioStreamMax); 1060 } 1061 blackListCurrentDevice()1062 private void blackListCurrentDevice() { 1063 mFeatures &= ~BTRC_FEAT_ABSOLUTE_VOLUME; 1064 mAudioManager.avrcpSupportsAbsoluteVolume(mAddress, isAbsoluteVolumeSupported()); 1065 1066 SharedPreferences pref = mContext.getSharedPreferences(ABSOLUTE_VOLUME_BLACKLIST, 1067 Context.MODE_PRIVATE); 1068 SharedPreferences.Editor editor = pref.edit(); 1069 editor.putBoolean(mAddress, true); 1070 editor.commit(); 1071 } 1072 modifyRcFeatureFromBlacklist(int feature, String address)1073 private int modifyRcFeatureFromBlacklist(int feature, String address) { 1074 SharedPreferences pref = mContext.getSharedPreferences(ABSOLUTE_VOLUME_BLACKLIST, 1075 Context.MODE_PRIVATE); 1076 if (!pref.contains(address)) { 1077 return feature; 1078 } 1079 if (pref.getBoolean(address, false)) { 1080 feature &= ~BTRC_FEAT_ABSOLUTE_VOLUME; 1081 } 1082 return feature; 1083 } 1084 resetBlackList(String address)1085 public void resetBlackList(String address) { 1086 SharedPreferences pref = mContext.getSharedPreferences(ABSOLUTE_VOLUME_BLACKLIST, 1087 Context.MODE_PRIVATE); 1088 SharedPreferences.Editor editor = pref.edit(); 1089 editor.remove(address); 1090 editor.commit(); 1091 } 1092 1093 /** 1094 * This is called from A2dpStateMachine to set A2dp audio state. 1095 */ setA2dpAudioState(int state)1096 public void setA2dpAudioState(int state) { 1097 Message msg = mHandler.obtainMessage(MESSAGE_SET_A2DP_AUDIO_STATE, state, 0); 1098 mHandler.sendMessage(msg); 1099 } 1100 dump(StringBuilder sb)1101 public void dump(StringBuilder sb) { 1102 sb.append("AVRCP:\n"); 1103 ProfileService.println(sb, "mMediaAttributes: " + mMediaAttributes); 1104 ProfileService.println(sb, "mTransportControlFlags: " + mTransportControlFlags); 1105 ProfileService.println(sb, "mCurrentPlayState: " + mCurrentPlayState); 1106 ProfileService.println(sb, "mLastStateUpdate: " + mLastStateUpdate); 1107 ProfileService.println(sb, "mPlayStatusChangedNT: " + mPlayStatusChangedNT); 1108 ProfileService.println(sb, "mTrackChangedNT: " + mTrackChangedNT); 1109 ProfileService.println(sb, "mTrackNumber: " + mTrackNumber); 1110 ProfileService.println(sb, "mSongLengthMs: " + mSongLengthMs); 1111 ProfileService.println(sb, "mPlaybackIntervalMs: " + mPlaybackIntervalMs); 1112 ProfileService.println(sb, "mPlayPosChangedNT: " + mPlayPosChangedNT); 1113 ProfileService.println(sb, "mNextPosMs: " + mNextPosMs); 1114 ProfileService.println(sb, "mPrevPosMs: " + mPrevPosMs); 1115 ProfileService.println(sb, "mSkipStartTime: " + mSkipStartTime); 1116 ProfileService.println(sb, "mFeatures: " + mFeatures); 1117 ProfileService.println(sb, "mRemoteVolume: " + mRemoteVolume); 1118 ProfileService.println(sb, "mLastRemoteVolume: " + mLastRemoteVolume); 1119 ProfileService.println(sb, "mLastDirection: " + mLastDirection); 1120 ProfileService.println(sb, "mVolumeStep: " + mVolumeStep); 1121 ProfileService.println(sb, "mAudioStreamMax: " + mAudioStreamMax); 1122 ProfileService.println(sb, "mVolCmdAdjustInProgress: " + mVolCmdAdjustInProgress); 1123 ProfileService.println(sb, "mVolCmdSetInProgress: " + mVolCmdSetInProgress); 1124 ProfileService.println(sb, "mAbsVolRetryTimes: " + mAbsVolRetryTimes); 1125 ProfileService.println(sb, "mSkipAmount: " + mSkipAmount); 1126 ProfileService.println(sb, "mVolumeMapping: " + mVolumeMapping.toString()); 1127 if (mMediaController != null) 1128 ProfileService.println(sb, "mMediaSession pkg: " + mMediaController.getPackageName()); 1129 } 1130 1131 // Do not modify without updating the HAL bt_rc.h files. 1132 1133 // match up with btrc_play_status_t enum of bt_rc.h 1134 final static int PLAYSTATUS_STOPPED = 0; 1135 final static int PLAYSTATUS_PLAYING = 1; 1136 final static int PLAYSTATUS_PAUSED = 2; 1137 final static int PLAYSTATUS_FWD_SEEK = 3; 1138 final static int PLAYSTATUS_REV_SEEK = 4; 1139 final static int PLAYSTATUS_ERROR = 255; 1140 1141 // match up with btrc_media_attr_t enum of bt_rc.h 1142 final static int MEDIA_ATTR_TITLE = 1; 1143 final static int MEDIA_ATTR_ARTIST = 2; 1144 final static int MEDIA_ATTR_ALBUM = 3; 1145 final static int MEDIA_ATTR_TRACK_NUM = 4; 1146 final static int MEDIA_ATTR_NUM_TRACKS = 5; 1147 final static int MEDIA_ATTR_GENRE = 6; 1148 final static int MEDIA_ATTR_PLAYING_TIME = 7; 1149 1150 // match up with btrc_event_id_t enum of bt_rc.h 1151 final static int EVT_PLAY_STATUS_CHANGED = 1; 1152 final static int EVT_TRACK_CHANGED = 2; 1153 final static int EVT_TRACK_REACHED_END = 3; 1154 final static int EVT_TRACK_REACHED_START = 4; 1155 final static int EVT_PLAY_POS_CHANGED = 5; 1156 final static int EVT_BATT_STATUS_CHANGED = 6; 1157 final static int EVT_SYSTEM_STATUS_CHANGED = 7; 1158 final static int EVT_APP_SETTINGS_CHANGED = 8; 1159 1160 // match up with btrc_notification_type_t enum of bt_rc.h 1161 final static int NOTIFICATION_TYPE_INTERIM = 0; 1162 final static int NOTIFICATION_TYPE_CHANGED = 1; 1163 1164 // match up with BTRC_UID_SIZE of bt_rc.h 1165 final static int TRACK_ID_SIZE = 8; 1166 classInitNative()1167 private native static void classInitNative(); initNative()1168 private native void initNative(); cleanupNative()1169 private native void cleanupNative(); getPlayStatusRspNative(int playStatus, int songLen, int songPos)1170 private native boolean getPlayStatusRspNative(int playStatus, int songLen, int songPos); getElementAttrRspNative(byte numAttr, int[] attrIds, String[] textArray)1171 private native boolean getElementAttrRspNative(byte numAttr, int[] attrIds, String[] textArray); registerNotificationRspPlayStatusNative(int type, int playStatus)1172 private native boolean registerNotificationRspPlayStatusNative(int type, int playStatus); registerNotificationRspTrackChangeNative(int type, byte[] track)1173 private native boolean registerNotificationRspTrackChangeNative(int type, byte[] track); registerNotificationRspPlayPosNative(int type, int playPos)1174 private native boolean registerNotificationRspPlayPosNative(int type, int playPos); setVolumeNative(int volume)1175 private native boolean setVolumeNative(int volume); sendPassThroughCommandNative(int keyCode, int keyState)1176 private native boolean sendPassThroughCommandNative(int keyCode, int keyState); 1177 1178 } 1179