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