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