# Bluetooth survey ###### tags: `AU19` `bluetooth` ## 藍牙規格 藍牙耳機規範(HSP)– 提供手機(行動電話)與耳機之間通訊所需的基本功能。 免手持裝置規範 (HFP)– 在 HSP 的基礎上增加了某些擴充套件功能,原來只用於從固定車載擴音裝置來控制行動電話人機介面規範 (HID) 藍牙立體聲音訊傳輸規範(A2DP)– 允許傳輸立體聲音訊訊號。 (相比用於 HSP 和 HFP 的單聲道加密,質量要好得 回音暨噪音消除技術(CVC) – 原理是通過耳機內置的消噪軟體及麥克風,來抑制多種類型的混響噪音。有了這個技術,周邊的環境雜音得以衰減,對方將能聽到更加清晰的聲音。 音訊/影片遠端控制設定檔(AVRCP) –用於從控制器(如立體聲耳機)向目標裝置(如裝有 Media Player 的電腦)發送命令(如前跳、暫停和播放)。 物件交換規範 (OPP) –為Object Push Profile的簡稱,在附件裝置與手機通訊的情景中,既有手機發起資料傳輸請求也有裝置側發起傳輸請求的需要。 ----------- `AVRCP` 在藍牙技術協定中包含了AVRCP這個協定。 中文:音訊/視訊遙控協定 英文:Audio/Video Remote Control Profile 它提供了搖控裝置一個標準的控制介面, 讓你可以用一支遙控器或耳機等遙控裝置, 搖控你所有的電視、電腦或是手機的音訊和視訊介面。 這個協定可以和A2DP(藍牙立體聲傳輸協定)共同使用, 一般來說最基本的用法就是控制裝置透過AVRCP傳輸控制指令, 然後目標裝置就會依指令透過A2DP傳回音訊結果。 除了音訊之外, AVRCP更可以用來傳輸要求數據的指令, 目標裝置接收到指令後, 也可以透過這個協定回傳資訊, 如果你的控制裝置有顯示面板的話, 所傳回的資訊就會顯示在面板上。 目前大多的智慧型手機, 和一些音樂手機都可以完整支援AVRCP, 因此你可以很簡單地利用藍牙耳機, 接聽電話和聆聽音樂。 `A2DP` 中文:立體聲音訊傳輸協定 英文:Advenced Audio Distribution Profile A2DP代表的意思是進階音效分配設定檔。 這是定義如何以藍牙連線, 從某個裝置串流高品質立體聲音效至另一個裝置, (例如從行動電話串流至無線耳機)的藍牙立體聲設定檔。 雖然很多產品會有用於語音通話的藍牙功能, 但要使音樂能從某個藍牙裝置串流至另一裝置, 則這兩個裝置都必須有A2DP設定檔。 如果兩個裝置都沒有這個設定檔, 仍然可以使用標準耳機或免持設定檔連線, 但這些設定檔不支援立體聲音樂。 ## 參考資料 1. https://www.jianshu.com/p/b59a5b7dca2d 2. http://cmnocsexperience.blogspot.com/2019/01/android.html 3. https://github.com/googlearchive/android-BluetoothChat 4. https://developer.android.com/guide/topics/connectivity/bluetooth?hl=zh-cn 5. http://blog.kenyang.net/2012/07/27/android-bluetooth-api 6. https://blog.csdn.net/yehui928186846/article/details/52710112 7. https://itimetraveler.github.io/2017/05/18/【Android】Audio音频输出通道切换%20-%20蓝牙、外放/ 8. https://blog.csdn.net/wc0000000/article/details/82884511 9. https://segmentfault.com/a/1190000000713535 10. https://blog.csdn.net/u012545728/article/details/99462873 ## Bluetooth 實作 藍芽耳機的兩種鏈路,`A2DP`及`SCO`。android的api表明: * A2DP:是一種單向的高品質音訊資料傳輸鏈路,通常用於播放立體聲音樂; * SCO: 則是一種雙向的音訊資料的傳輸鏈路,該鏈路只支援8K及16K單聲道的音訊資料,只能用於普通語音的傳輸,若用於播放音樂那就只能呵呵了。 連接藍牙音箱 ```cpp= public void changeToHeadset(){ mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION); mAudioManager.startBluetoothSco(); mAudioManager.setBluetoothScoOn(true); mAudioManager.setSpeakerphoneOn(false); } ``` 即使应用程序处于锁定模式,它也用于Android音乐遥控器 `RemoteControlClient`只要调用下面的方法,而您的接收器命令的行动,播放,暂停,下一个和上一个歌曲曲目。 ```cpp private void lockScreenControls() { // Use the media button APIs (if available) to register ourselves for media button // events MediaButtonHelper.registerMediaButtonEventReceiverCompat(mAudioManager, mMediaButtonReceiverComponent); // Use the remote control APIs (if available) to set the playback state if (mRemoteControlClientCompat == null) { Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON); intent.setComponent(mMediaButtonReceiverComponent); mRemoteControlClientCompat = new RemoteControlClientCompat(PendingIntent.getBroadcast(this /*context*/,0 /*requestCode, ignored*/, intent /*intent*/, 0 /*flags*/)); RemoteControlHelper.registerRemoteControlClient(mAudioManager,mRemoteControlClientCompat); } mRemoteControlClientCompat.setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING); mRemoteControlClientCompat.setTransportControlFlags( RemoteControlClient.FLAG_KEY_MEDIA_PAUSE | RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS | RemoteControlClient.FLAG_KEY_MEDIA_NEXT | RemoteControlClient.FLAG_KEY_MEDIA_STOP); //update remote controls mRemoteControlClientCompat.editMetadata(true) .putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, "NombreArtista") .putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, "Titulo Album") .putString(MediaMetadataRetriever.METADATA_KEY_TITLE, nombreCancion) //.putLong(MediaMetadataRetriever.METADATA_KEY_DURATION,playingItem.getDuration()) // TODO: fetch real item artwork .putBitmap(RemoteControlClientCompat.MetadataEditorCompat.METADATA_KEY_ARTWORK, getAlbumArt()) .apply(); } } ``` ## AVRCP example ```cpp public class A2dpTestFragment extends Fragment { private static String TAG = A2dpTestFragment.class.getSimpleName(); private MainActivity mActivity; private BluetoothA2dpSink mA2dpService; private BluetoothAvrcpController mAvrcpController; private TextView mStatusView; private TextView tvTitle; private TextView tvAlbum; private TextView tvArtist; private TextView tvDuration; private TextView tvCurrent; private SeekBar sbProgress; private Button btnStop; private Button btnConn; private Button btnDisconn; private ImageView imgPlayPause; private ImageView imgPrev; private ImageView imgNext; private ImageView imgShuffle; private ImageView imgRepeat; // private ImageButton private BluetoothDevice mDevice; private MediaMetadata mediaMetadata; private PlaybackState playbackState; private BluetoothAvrcpPlayerSettings mAvrcpPlayerSettings; private String title; private String artist; private long duration; private String album; //status control private static boolean isA2dpPlaying = false; private static boolean isA2dpConnected = false; //SETTING_EQUALIZER, SETTING_REPEAT, SETTING_SHUFFLE, SETTING_SCAN private static int[] current_avrcp_setting_value = {0, 0, 0, 0}; private SimpleDateFormat timeFormat = new SimpleDateFormat("mm:ss"); private BroadcastReceiver mA2dpReciver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Log.d(TAG, "action = " + action); if (action.equals(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED)) { int preState = intent.getIntExtra(BluetoothA2dpSink.EXTRA_PREVIOUS_STATE, 0); int state = intent.getIntExtra(BluetoothA2dpSink.EXTRA_STATE, 0); Log.d(TAG, "action = " + action + ", state " + preState + " -> " + state); if (state == BluetoothProfile.STATE_CONNECTED) { isA2dpConnected = true; } else if (state == BluetoothProfile.STATE_DISCONNECTED) { isA2dpConnected = false; } } else if (action.equals(BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED)) { int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (state == BluetoothProfile.STATE_CONNECTED) { mStatusView.setText("AVRCP_CONTROLLER connected"); mStatusView.append("\r\nAvrcp devices: \r\n"); mStatusView.append(" - " + device.getName() + " " + device.getAddress() + "\r\n"); // FIXME: mDevice = device; } if (state == BluetoothProfile.STATE_DISCONNECTED) { mStatusView.setText("AVRCP_CONTROLLER connected"); mStatusView.append("\r\nAvrcp devices: \r\n"); mStatusView.append(" - " + "no device"); imgRepeat.setAlpha(255); imgRepeat.setClickable(true); imgShuffle.setAlpha(255); imgShuffle.setClickable(true); } } else if (action.equals(BluetoothAvrcpController.ACTION_TRACK_EVENT)) { mediaMetadata = intent.getParcelableExtra(BluetoothAvrcpController.EXTRA_METADATA); playbackState = intent.getParcelableExtra(BluetoothAvrcpController.EXTRA_PLAYBACK); if (mediaMetadata != null) { Log.v(TAG, "Get MediaMetadata"); // TODO: 16-11-23 display the info of media displayMediaInfo(mediaMetadata); sbProgress.setMax((int) mediaMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION)); } // update progress bar if (playbackState != null) { Log.v(TAG, "Get PlaybackState"); Log.v(TAG, playbackState.toString()); // TODO: 16-11-23 change the progress long postion = playbackState.getPosition(); Log.v(TAG, new SimpleDateFormat("HH:mm:ss").format(postion)); sbProgress.setProgress((int) postion); tvCurrent.setText(timeFormat.format(postion)); } //update play state if (playbackState != null) { int state = playbackState.getState(); if (state == PlaybackState.STATE_PLAYING) { isA2dpPlaying = true; imgPlayPause.setImageResource(R.drawable.music_button_pause_disable); } if (state == PlaybackState.STATE_PAUSED) { isA2dpPlaying = false; imgPlayPause.setImageResource(R.drawable.music_button_play_disable); } } } else if (action.equals(BluetoothAvrcpController.ACTION_PLAYER_SETTING)) { mAvrcpPlayerSettings = intent.getParcelableExtra(BluetoothAvrcpController.EXTRA_PLAYER_SETTING); refreshAvrcpSettings(mAvrcpPlayerSettings); } } }; @Override public void onAttach(Activity activity) { super.onAttach(activity); try { mActivity = (MainActivity) activity; } catch (ClassCastException e) { throw new ClassCastException(this.getClass().getSimpleName() + " can be only attached to " + MainActivity.class.getSimpleName()); } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mDevice = mActivity.mSelectedDevice; IntentFilter it = new IntentFilter(); it.addAction(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED); it.addAction(BluetoothA2dpSink.ACTION_AUDIO_CONFIG_CHANGED); it.addAction(BluetoothA2dpSink.ACTION_PLAYING_STATE_CHANGED); it.addAction(BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED); it.addAction(BluetoothAvrcpController.ACTION_TRACK_EVENT); it.addAction(BluetoothAvrcpController.ACTION_PLAYER_SETTING); mActivity.registerReceiver(mA2dpReciver, it); } // @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_avrcp, null); if (mDevice == null) { Toast.makeText(mActivity, "Please select a device", Toast.LENGTH_SHORT).show(); return null; } else if (!BluetoothAdapter.getDefaultAdapter().isEnabled()) { Toast.makeText(mActivity, "Bluetooth is not enabled", Toast.LENGTH_SHORT).show(); return null; } findUiViewAndSetListener(view); timeFormat.setTimeZone(TimeZone.getTimeZone("GMT+00:00")); BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); bluetoothAdapter.getProfileProxy(mActivity, mA2dpServiceListener, 11); //BluetoothProfile.A2DP_SINK bluetoothAdapter.getProfileProxy(mActivity, mAvrcpServiceListener, 12); //BluetoothProfile.AVRCP_CONTROLLER mStatusView.setText("Connecting to the AVRCP_CONTROLLER service"); return view; } @Override public void onDestroy() { super.onDestroy(); mActivity.unregisterReceiver(mA2dpReciver); BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); // TODO mAvrcpController.removeCallback(); bluetoothAdapter.closeProfileProxy(11, mA2dpService); // BluetoothProfile.A2DP_SINK bluetoothAdapter.closeProfileProxy(12, mAvrcpController); //BluetoothProfile.AVRCP_CONTROLLER } private BluetoothProfile.ServiceListener mA2dpServiceListener = new BluetoothProfile.ServiceListener() { @Override public void onServiceConnected(int profile, BluetoothProfile proxy) { if (profile == 11) { // BluetoothProfile.A2DP_SINK mA2dpService = (BluetoothA2dpSink) proxy; // mA2dpService.connect(mActivity.mSelectedDevice); } //init the a2dp state // if (mA2dpService.isA2dpPlaying(mDevice)) { // isA2dpPlaying = true; // imgPlayPause.setImageResource(R.drawable.music_button_pause_disable); // } else { // isA2dpPlaying = false; // imgPlayPause.setImageResource(R.drawable.music_button_play_disable); // } } @Override public void onServiceDisconnected(int profile) { if (profile == 11) { // BluetoothProfile.A2DP_SINK mA2dpService = null; } } }; private BluetoothProfile.ServiceListener mAvrcpServiceListener = new BluetoothProfile.ServiceListener() { @Override public void onServiceConnected(int profile, BluetoothProfile proxy) { if (profile == 12) { // BluetoothProfile.AVRCP_CONTROLLER mStatusView.setText("AVRCP_CONTROLLER connected"); Log.d(TAG, "AvrcpControllerService connected"); mAvrcpController = (BluetoothAvrcpController) proxy; // TODO mAvrcpController.setCallback(new AvrcpControllerCallback()); mStatusView.append("\r\nAvrcp devices: \r\n"); List<BluetoothDevice> devices = mAvrcpController.getConnectedDevices(); if (devices.isEmpty()) { mStatusView.append(" - " + "no device"); } else { for (BluetoothDevice device : devices) mStatusView.append(" - " + device.getName() + " " + device.getAddress() + "\r\n"); } //init settings (for the selected device) if (mDevice != null) { refreshAvrcpSettings(mAvrcpController.getPlayerSettings(mDevice)); } } } @Override public void onServiceDisconnected(int profile) { if (profile == 12) { //BluetoothProfile.AVRCP_CONTROLLER mStatusView.setText("AVRCP_CONTROLLER disconnected"); Log.d(TAG, "AvrcpControllerService disconnected"); // TODO mAvrcpController.removeCallback(); mAvrcpController = null; } } }; private View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View v) { switch (v.getId()) { case R.id.play_pause_button: onPlayPauseButtonClick(); break; case R.id.prevButton: onPrevButtonClick(); break; case R.id.nextButton: onNextButtonClick(); break; case R.id.stopButton: onStopButtonClick(); break; case R.id.img_shuffle: onShuffleClick(); break; case R.id.img_repeat: onRepeatClick(); break; case R.id.btn_a2dp_conn: onA2dpConnect(); break; case R.id.btn_a2dp_disc: onA2dpDisconnnect(); break; default: break; } } }; private void findUiViewAndSetListener(View view) { mStatusView = (TextView) view.findViewById(R.id.status); tvTitle = (TextView) view.findViewById(R.id.tv_title); tvAlbum = (TextView) view.findViewById(R.id.tv_album); tvArtist = (TextView) view.findViewById(R.id.tv_artist); tvDuration = (TextView) view.findViewById(R.id.tv_duration); tvCurrent = (TextView) view.findViewById(R.id.tv_current); sbProgress = (SeekBar) view.findViewById(R.id.sb_progress); btnStop = (Button) view.findViewById(R.id.stopButton); imgPlayPause = (ImageView) view.findViewById(R.id.play_pause_button); imgPrev = (ImageView) view.findViewById(R.id.prevButton); imgNext = (ImageView) view.findViewById(R.id.nextButton); imgShuffle = (ImageView) view.findViewById(R.id.img_shuffle); imgRepeat = (ImageView) view.findViewById(R.id.img_repeat); btnConn = (Button) view.findViewById(R.id.btn_a2dp_conn); btnDisconn = (Button) view.findViewById(R.id.btn_a2dp_disc); btnStop.setOnClickListener(listener); imgPlayPause.setOnClickListener(listener); imgPrev.setOnClickListener(listener); imgNext.setOnClickListener(listener); imgShuffle.setOnClickListener(listener); imgRepeat.setOnClickListener(listener); btnConn.setOnClickListener(listener); btnDisconn.setOnClickListener(listener); } private void sendCommand(int keyCode) { if (mAvrcpController == null) return; List<BluetoothDevice> devices = mAvrcpController.getConnectedDevices(); for (BluetoothDevice device : devices) { Log.d(TAG, "send command to device: " + device.getName() + " " + device.getAddress()); mAvrcpController.sendPassThroughCmd(device, keyCode, BluetoothAvrcp.PASSTHROUGH_STATE_PRESS); mAvrcpController.sendPassThroughCmd(device, keyCode, BluetoothAvrcp.PASSTHROUGH_STATE_RELEASE); } } public void onPlayPauseButtonClick() { if (isA2dpPlaying) { sendCommand(BluetoothAvrcp.PASSTHROUGH_ID_PAUSE); } else if (!isA2dpPlaying) { sendCommand(BluetoothAvrcp.PASSTHROUGH_ID_PLAY); } } public void onStopButtonClick() { //FIXME sendCommand(BluetoothAvrcp.PASSTHROUGH_ID_STOP); // int profileState = BluetoothAdapter.getDefaultAdapter().getConnectionState(); // Log.d(TAG + "FANGCX_FANGCX", "profileSate: " + profileState); } public void onNextButtonClick() { sendCommand(BluetoothAvrcp.PASSTHROUGH_ID_FORWARD); } public void onPrevButtonClick() { sendCommand(BluetoothAvrcp.PASSTHROUGH_ID_BACKWARD); } private void displayMediaInfo(MediaMetadata metaData) { title = metaData.getString(MediaMetadata.METADATA_KEY_TITLE); artist = metaData.getString(MediaMetadata.METADATA_KEY_ARTIST); duration = metaData.getLong(MediaMetadata.METADATA_KEY_DURATION); album = metaData.getString(MediaMetadata.METADATA_KEY_ALBUM); tvTitle.setText((title == null ? "Unknown" : title)); tvArtist.setText((artist == null ? "Unknown" : artist)); tvAlbum.setText((album == null ? "Unknown" : album)); tvDuration.setText(timeFormat.format(duration)); Log.d(TAG, "duration : " + new SimpleDateFormat("HH:mm:ss").format(duration)); } public void onGetAttrsButtonClick(View view) { //for test List<BluetoothDevice> devices = mAvrcpController.getConnectedDevices(); for (BluetoothDevice device : devices) { BluetoothAvrcpPlayerSettings ps = mAvrcpController.getPlayerSettings(device); Log.d(TAG, "PlayerSetting id:" + ps.getSettings()); refreshAvrcpSettings(ps); } } //off -> shuffle -> off public void onShuffleClick() { int currentValue = current_avrcp_setting_value[2]; if (currentValue == BluetoothAvrcpPlayerSettings.STATE_OFF) { if (!setAvrcpSettings(BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE, BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK)) { //TODO : if is not supported } } if (currentValue == BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK) { if (!setAvrcpSettings(BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE, BluetoothAvrcpPlayerSettings.STATE_OFF)) { // TODO: if is note supported } } } //off -> single -> all -> off public void onRepeatClick() { int currentValue = current_avrcp_setting_value[1]; if (currentValue == BluetoothAvrcpPlayerSettings.STATE_OFF) { if (!setAvrcpSettings(BluetoothAvrcpPlayerSettings.SETTING_REPEAT, BluetoothAvrcpPlayerSettings.STATE_SINGLE_TRACK)) { // TODO: if is note supported } } if (currentValue == BluetoothAvrcpPlayerSettings.STATE_SINGLE_TRACK) { if (!setAvrcpSettings(BluetoothAvrcpPlayerSettings.SETTING_REPEAT, BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK)) { // TODO: if is note supported } } if (currentValue == BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK) { if (!setAvrcpSettings(BluetoothAvrcpPlayerSettings.SETTING_REPEAT, BluetoothAvrcpPlayerSettings.STATE_OFF)) { // TODO: if is note supported } } } private void onA2dpConnect() { if (mA2dpService != null) { Toast.makeText(mActivity, "To Conn Dev : " + mDevice.getName(), Toast.LENGTH_LONG).show(); mA2dpService.connect(mDevice); } } private void onA2dpDisconnnect() { if (mA2dpService != null) { Toast.makeText(mActivity, "To Disc Dev : " + mDevice.getName(), Toast.LENGTH_LONG).show(); mA2dpService.disconnect(mDevice); } } private boolean setAvrcpSettings(int type, int value) { BluetoothAvrcpPlayerSettings settings = new BluetoothAvrcpPlayerSettings(type); settings.addSettingValue(type, value); return mAvrcpController.setPlayerApplicationSetting(settings); } private void refreshAvrcpSettings(BluetoothAvrcpPlayerSettings ps) { if (ps == null) { Log.d(TAG, "playerSettings is null"); return; } int mSettings = ps.getSettings(); if (((byte) mSettings & BluetoothAvrcpPlayerSettings.SETTING_REPEAT) == 0) { imgRepeat.setAlpha(50); imgRepeat.setClickable(false); } if (((byte) mSettings & BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE) == 0) { imgShuffle.setAlpha(50); imgShuffle.setClickable(false); } if (((byte) mSettings & BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER) != 0) { int equalizerValue = ps.getSettingValue(BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE); Log.d(TAG, "EQUALIZER value: " + equalizerValue); switch (equalizerValue) { case BluetoothAvrcpPlayerSettings.STATE_OFF: break; case BluetoothAvrcpPlayerSettings.STATE_ON: break; default: return; } current_avrcp_setting_value[0] = equalizerValue; } if (((byte) mSettings & BluetoothAvrcpPlayerSettings.SETTING_REPEAT) != 0) { int repeatValue = ps.getSettingValue(BluetoothAvrcpPlayerSettings.SETTING_REPEAT); Log.d(TAG, "REPEAT value: " + repeatValue); switch (repeatValue) { case BluetoothAvrcpPlayerSettings.STATE_OFF: imgRepeat.setImageResource(R.drawable.avrcp_setting_repeat_off); break; case BluetoothAvrcpPlayerSettings.STATE_SINGLE_TRACK: imgRepeat.setImageResource(R.drawable.avrcp_setting_repeat_single); break; case BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK: imgRepeat.setImageResource(R.drawable.avrcp_setting_repeat_all); break; case BluetoothAvrcpPlayerSettings.STATE_GROUP: break; default: return; } current_avrcp_setting_value[1] = repeatValue; } if (((byte) mSettings & BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE) != 0) { int shuffleValue = ps.getSettingValue(BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE); Log.d(TAG, "SHUFFLE value: " + shuffleValue); switch (shuffleValue) { case BluetoothAvrcpPlayerSettings.STATE_OFF: imgShuffle.setImageResource(R.drawable.avrcp_setting_shuffle_off); break; case BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK: imgShuffle.setImageResource(R.drawable.avrcp_setting_shuffle); break; case BluetoothAvrcpPlayerSettings.STATE_GROUP: break; default: return; } current_avrcp_setting_value[2] = shuffleValue; } if (((byte) mSettings & BluetoothAvrcpPlayerSettings.SETTING_SCAN) != 0) { int scanValue = ps.getSettingValue(BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE); Log.d(TAG, "SCAN value: " + scanValue); switch (scanValue) { case BluetoothAvrcpPlayerSettings.STATE_OFF: break; case BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK: break; case BluetoothAvrcpPlayerSettings.STATE_GROUP: break; default: return; } current_avrcp_setting_value[3] = scanValue; } } } ```