├── LICENSE ├── README.md ├── okbluetooth ├── .gitignore ├── build.gradle ├── okbluetooth.iml ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── aidl │ └── android │ │ ├── bluetooth │ │ ├── IBluetoothHeadset.aidl │ │ ├── IBluetoothManager.aidl │ │ ├── IBluetoothProfileServiceConnection.aidl │ │ └── IBluetoothStateChangeCallback.aidl │ │ ├── media │ │ └── IAudioService.aidl │ │ └── os │ │ └── IPermissionController.aidl │ ├── assets │ └── test.mp3 │ ├── java │ ├── android │ │ ├── bluetooth │ │ │ ├── BluetoothA2dp.java │ │ │ ├── BluetoothHeadset.java │ │ │ ├── BluetoothProfile.java │ │ │ └── BluetoothUuid.java │ │ ├── media │ │ │ ├── AudioGain.java │ │ │ ├── AudioGainConfig.java │ │ │ ├── AudioHandle.java │ │ │ ├── AudioPatch.java │ │ │ ├── AudioPort.java │ │ │ ├── AudioPortConfig.java │ │ │ └── AudioSystem.java │ │ └── os │ │ │ ├── IServiceManager.java │ │ │ ├── ServiceManager.java │ │ │ └── SystemProperties.java │ └── com │ │ └── devyok │ │ └── bluetooth │ │ ├── AudioDevice.java │ │ ├── AudioDeviceSelector.java │ │ ├── AudioService.java │ │ ├── Configuration.java │ │ ├── ConnectionHelper.java │ │ ├── OkBluetooth.java │ │ ├── TelephonyService.java │ │ ├── a2dp │ │ ├── A2dpProfileService.java │ │ └── BluetoothA2dpProfileService.java │ │ ├── base │ │ ├── BaseBluetoothStateChangedListener.java │ │ ├── BluetoothAdapterService.java │ │ ├── BluetoothAdapterStateListener.java │ │ ├── BluetoothAndroidThread.java │ │ ├── BluetoothException.java │ │ ├── BluetoothProfileService.java │ │ ├── BluetoothProfileServiceTemplate.java │ │ ├── BluetoothRuntimeException.java │ │ ├── BluetoothService.java │ │ ├── BluetoothServiceLifecycle.java │ │ ├── Executor.java │ │ ├── StateInformation.java │ │ └── TaskQueue.java │ │ ├── connection │ │ ├── AbstractBluetoothConnection.java │ │ ├── BluetoothConnection.java │ │ ├── BluetoothConnectionException.java │ │ ├── BluetoothConnectionImpl.java │ │ ├── BluetoothConnectionStateListener.java │ │ ├── BluetoothConnectionTimeoutException.java │ │ ├── BluetoothDeviceConnectionService.java │ │ ├── Connection.java │ │ ├── DefaultRetryPolicy.java │ │ └── RetryPolicy.java │ │ ├── debug │ │ ├── DebugHelper.java │ │ └── DebugUIConsoleActivity.java │ │ ├── hfp │ │ ├── BluetoothHeadsetProfileService.java │ │ ├── HFPConnection.java │ │ ├── HFPConnectionImpl.java │ │ └── HeadsetProfileService.java │ │ ├── message │ │ ├── BluetoothMessage.java │ │ ├── BluetoothMessageDispatcher.java │ │ ├── BluetoothMessageHandler.java │ │ └── BluetoothMessageReceiver.java │ │ ├── sco │ │ └── BluetoothSCOService.java │ │ ├── spp │ │ ├── AbstractSPPConnection.java │ │ ├── DefaultSPPMessageParser.java │ │ ├── SPPBluetoothMessageParser.java │ │ ├── SPPConnectionInsecurePolicy.java │ │ ├── SPPConnectionSecurePolicy.java │ │ ├── SPPMessageParser.java │ │ ├── SPPMessageReceiver.java │ │ └── SppConnectionLoopChannelPolicy.java │ │ └── utils │ │ └── BluetoothUtils.java │ └── res │ ├── layout │ ├── okbt_activity_debug_ui_console.xml │ ├── okbt_audio_device_list.xml │ └── okbt_audio_device_list_item.xml │ └── values │ ├── okbt_colors.xml │ └── okbt_strings.xml └── okbluetooth_demo ├── .gitignore ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── devyok │ │ └── bluetooth │ │ └── demo │ │ ├── DemoApplication.java │ │ ├── MainActivity.java │ │ └── OkBluetoothAdapter.java │ └── res │ ├── drawable │ └── ic_launcher_background.xml │ ├── layout │ └── activity_main.xml │ └── values │ ├── colors.xml │ └── strings.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── test.jpg /README.md: -------------------------------------------------------------------------------- 1 | [![license](http://img.shields.io/badge/license-Apache2.0-brightgreen.svg?style=flat)](https://github.com/devyok/ServiceManager/blob/master/LICENSE) 2 | [![Release Version](https://img.shields.io/badge/release-1.0.0-brightgreen.svg)](https://jcenter.bintray.com/com/devyok/web/hybridmessenger-core/1.0.0/) 3 | 4 | # OkBluetooth 5 | Android蓝牙音频及消息通信框架 6 | 7 | ## 作用 ## 8 | - OkBluetooth主要帮助应用与蓝牙设备之间建立可靠的HFP或SPP连接,并同时建立稳定的SCO音频连接; 9 | - OkBluetooth会根据当前手机终端所连接的音频输出设备(蓝牙耳机/有线耳机/扬声器等)的状态来选择音频设备放音; 10 | - 可以使用OkBluetooth提供的接口,搭建自己的音频及消息的通信组件; 11 | 12 | ## 例子 ## 13 | - [OkBluetooth-Demo](https://github.com/devyok/OkBluetooth/tree/master/okbluetooth_demo) 14 | 15 | 在demo中参考OkBluetoothAdapter#onAppReady方法中是如何使用OkBluetooth接口的。 16 | 17 | - [查看所有蓝牙相关接口](https://github.com/devyok/OkBluetooth/blob/master/okbluetooth/src/main/java/com/devyok/bluetooth/OkBluetooth.java) 18 | 19 | ## License ## 20 | ServiceManager is released under the [Apache 2.0 license](https://github.com/devyok/OkBluetooth/blob/master/LICENSE). 21 | 22 | Copyright (C) 2018 DengWei. -------------------------------------------------------------------------------- /okbluetooth/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /okbluetooth/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 26 5 | 6 | 7 | 8 | defaultConfig { 9 | minSdkVersion 19 10 | targetSdkVersion 26 11 | versionCode 10000 12 | versionName "1.0.0" 13 | 14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 15 | 16 | } 17 | 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | 25 | } 26 | 27 | dependencies { 28 | implementation fileTree(dir: 'libs', include: ['*.jar']) 29 | } 30 | -------------------------------------------------------------------------------- /okbluetooth/okbluetooth.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /okbluetooth/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /okbluetooth/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /okbluetooth/src/main/aidl/android/bluetooth/IBluetoothHeadset.aidl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 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 android.bluetooth; 18 | 19 | import android.bluetooth.BluetoothDevice; 20 | 21 | /** 22 | * API for Bluetooth Headset service 23 | * 24 | * {@hide} 25 | */ 26 | interface IBluetoothHeadset { 27 | // Public API 28 | boolean connect(in BluetoothDevice device); 29 | boolean disconnect(in BluetoothDevice device); 30 | List getConnectedDevices(); 31 | List getDevicesMatchingConnectionStates(in int[] states); 32 | int getConnectionState(in BluetoothDevice device); 33 | boolean setPriority(in BluetoothDevice device, int priority); 34 | int getPriority(in BluetoothDevice device); 35 | boolean startVoiceRecognition(in BluetoothDevice device); 36 | boolean stopVoiceRecognition(in BluetoothDevice device); 37 | boolean isAudioConnected(in BluetoothDevice device); 38 | boolean sendVendorSpecificResultCode(in BluetoothDevice device, 39 | in String command, 40 | in String arg); 41 | 42 | // APIs that can be made public in future 43 | int getBatteryUsageHint(in BluetoothDevice device); 44 | 45 | // Internal functions, not be made public 46 | boolean acceptIncomingConnect(in BluetoothDevice device); 47 | boolean rejectIncomingConnect(in BluetoothDevice device); 48 | int getAudioState(in BluetoothDevice device); 49 | 50 | boolean isAudioOn(); 51 | boolean connectAudio(); 52 | boolean disconnectAudio(); 53 | boolean startScoUsingVirtualVoiceCall(in BluetoothDevice device); 54 | boolean stopScoUsingVirtualVoiceCall(in BluetoothDevice device); 55 | void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type); 56 | void clccResponse(int index, int direction, int status, int mode, boolean mpty, 57 | String number, int type); 58 | boolean enableWBS(); 59 | boolean disableWBS(); 60 | } 61 | -------------------------------------------------------------------------------- /okbluetooth/src/main/aidl/android/bluetooth/IBluetoothManager.aidl: -------------------------------------------------------------------------------- 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 android.bluetooth; 18 | 19 | import android.bluetooth.IBluetoothProfileServiceConnection; 20 | import android.bluetooth.IBluetoothStateChangeCallback; 21 | 22 | /** 23 | * System private API for talking with the Bluetooth service. 24 | * 25 | * {@hide} 26 | */ 27 | interface IBluetoothManager 28 | { 29 | void registerStateChangeCallback(in IBluetoothStateChangeCallback callback); 30 | void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback); 31 | boolean isEnabled(); 32 | boolean enable(); 33 | boolean enableNoAutoConnect(); 34 | boolean disable(boolean persist); 35 | 36 | boolean bindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy); 37 | void unbindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy); 38 | 39 | String getAddress(); 40 | String getName(); 41 | 42 | boolean isBleScanAlwaysAvailable(); 43 | int updateBleAppCount(IBinder b, boolean enable); 44 | boolean isBleAppPresent(); 45 | } 46 | -------------------------------------------------------------------------------- /okbluetooth/src/main/aidl/android/bluetooth/IBluetoothProfileServiceConnection.aidl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 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 android.bluetooth; 18 | 19 | import android.content.ComponentName; 20 | import android.os.IBinder; 21 | 22 | /** 23 | * Callback for bluetooth profile connections. 24 | * 25 | * {@hide} 26 | */ 27 | interface IBluetoothProfileServiceConnection { 28 | void onServiceConnected(in ComponentName comp, in IBinder service); 29 | void onServiceDisconnected(in ComponentName comp); 30 | } 31 | -------------------------------------------------------------------------------- /okbluetooth/src/main/aidl/android/bluetooth/IBluetoothStateChangeCallback.aidl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011, 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 android.bluetooth; 18 | 19 | /** 20 | * System private API for Bluetooth state change callback. 21 | * 22 | * {@hide} 23 | */ 24 | interface IBluetoothStateChangeCallback 25 | { 26 | void onBluetoothStateChange(boolean on); 27 | } 28 | -------------------------------------------------------------------------------- /okbluetooth/src/main/aidl/android/media/IAudioService.aidl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 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 android.media; 18 | 19 | 20 | /** 21 | * {@hide} 22 | */ 23 | interface IAudioService { 24 | 25 | void setBluetoothA2dpOn(boolean on); 26 | 27 | boolean isBluetoothA2dpOn(); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /okbluetooth/src/main/aidl/android/os/IPermissionController.aidl: -------------------------------------------------------------------------------- 1 | /* //device/java/android/android/os/IPowerManager.aidl 2 | ** 3 | ** Copyright 2007, The Android Open Source Project 4 | ** 5 | ** Licensed under the Apache License, Version 2.0 (the "License"); 6 | ** you may not use this file except in compliance with the License. 7 | ** You may obtain a copy of the License at 8 | ** 9 | ** http://www.apache.org/licenses/LICENSE-2.0 10 | ** 11 | ** Unless required by applicable law or agreed to in writing, software 12 | ** distributed under the License is distributed on an "AS IS" BASIS, 13 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | ** See the License for the specific language governing permissions and 15 | ** limitations under the License. 16 | */ 17 | 18 | package android.os; 19 | 20 | /** @hide */ 21 | interface IPermissionController { 22 | boolean checkPermission(String permission, int pid, int uid); 23 | String[] getPackagesForUid(int uid); 24 | boolean isRuntimePermission(String permission); 25 | } 26 | -------------------------------------------------------------------------------- /okbluetooth/src/main/assets/test.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devyok/OkBluetooth/49be79259c5e935f9eb8ad1f47f1c5dc6ba0a74d/okbluetooth/src/main/assets/test.mp3 -------------------------------------------------------------------------------- /okbluetooth/src/main/java/android/bluetooth/BluetoothProfile.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010-2014 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 | 18 | package android.bluetooth; 19 | 20 | import java.util.List; 21 | 22 | /** 23 | * Public APIs for the Bluetooth Profiles. 24 | * 25 | *

Clients should call {@link BluetoothAdapter#getProfileProxy}, 26 | * to get the Profile Proxy. Each public profile implements this 27 | * interface. 28 | */ 29 | public interface BluetoothProfile { 30 | 31 | /** 32 | * Extra for the connection state intents of the individual profiles. 33 | * 34 | * This extra represents the current connection state of the profile of the 35 | * Bluetooth device. 36 | */ 37 | public static final String EXTRA_STATE = "android.bluetooth.profile.extra.STATE"; 38 | 39 | /** 40 | * Extra for the connection state intents of the individual profiles. 41 | * 42 | * This extra represents the previous connection state of the profile of the 43 | * Bluetooth device. 44 | */ 45 | public static final String EXTRA_PREVIOUS_STATE = 46 | "android.bluetooth.profile.extra.PREVIOUS_STATE"; 47 | 48 | /** The profile is in disconnected state */ 49 | public static final int STATE_DISCONNECTED = 0; 50 | /** The profile is in connecting state */ 51 | public static final int STATE_CONNECTING = 1; 52 | /** The profile is in connected state */ 53 | public static final int STATE_CONNECTED = 2; 54 | /** The profile is in disconnecting state */ 55 | public static final int STATE_DISCONNECTING = 3; 56 | 57 | /** 58 | * Headset and Handsfree profile 59 | */ 60 | public static final int HEADSET = 1; 61 | 62 | /** 63 | * A2DP profile. 64 | */ 65 | public static final int A2DP = 2; 66 | 67 | /** 68 | * Health Profile 69 | */ 70 | public static final int HEALTH = 3; 71 | 72 | /** 73 | * Input Device Profile 74 | * @hide 75 | */ 76 | public static final int INPUT_DEVICE = 4; 77 | 78 | /** 79 | * PAN Profile 80 | * @hide 81 | */ 82 | public static final int PAN = 5; 83 | 84 | /** 85 | * PBAP 86 | * @hide 87 | */ 88 | public static final int PBAP = 6; 89 | 90 | /** 91 | * GATT 92 | */ 93 | static public final int GATT = 7; 94 | 95 | /** 96 | * GATT_SERVER 97 | */ 98 | static public final int GATT_SERVER = 8; 99 | 100 | /** 101 | * MAP Profile 102 | * @hide 103 | */ 104 | public static final int MAP = 9; 105 | 106 | /* 107 | * SAP Profile 108 | * @hide 109 | */ 110 | public static final int SAP = 10; 111 | 112 | /** 113 | * A2DP Sink Profile 114 | * @hide 115 | */ 116 | public static final int A2DP_SINK = 11; 117 | 118 | /** 119 | * AVRCP Controller Profile 120 | * @hide 121 | */ 122 | public static final int AVRCP_CONTROLLER = 12; 123 | 124 | /** 125 | * Headset Client - HFP HF Role 126 | * @hide 127 | */ 128 | public static final int HEADSET_CLIENT = 16; 129 | 130 | /** 131 | * Default priority for devices that we try to auto-connect to and 132 | * and allow incoming connections for the profile 133 | * @hide 134 | **/ 135 | public static final int PRIORITY_AUTO_CONNECT = 1000; 136 | 137 | /** 138 | * Default priority for devices that allow incoming 139 | * and outgoing connections for the profile 140 | * @hide 141 | **/ 142 | public static final int PRIORITY_ON = 100; 143 | 144 | /** 145 | * Default priority for devices that does not allow incoming 146 | * connections and outgoing connections for the profile. 147 | * @hide 148 | **/ 149 | public static final int PRIORITY_OFF = 0; 150 | 151 | /** 152 | * Default priority when not set or when the device is unpaired 153 | * @hide 154 | * */ 155 | public static final int PRIORITY_UNDEFINED = -1; 156 | 157 | /** 158 | * Get connected devices for this specific profile. 159 | * 160 | *

Return the set of devices which are in state {@link #STATE_CONNECTED} 161 | * 162 | *

Requires {@link android.Manifest.permission#BLUETOOTH} permission. 163 | * 164 | * @return List of devices. The list will be empty on error. 165 | */ 166 | public List getConnectedDevices(); 167 | 168 | /** 169 | * Get a list of devices that match any of the given connection 170 | * states. 171 | * 172 | *

If none of the devices match any of the given states, 173 | * an empty list will be returned. 174 | * 175 | *

Requires {@link android.Manifest.permission#BLUETOOTH} permission. 176 | * 177 | * @param states Array of states. States can be one of 178 | * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, 179 | * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, 180 | * @return List of devices. The list will be empty on error. 181 | */ 182 | public List getDevicesMatchingConnectionStates(int[] states); 183 | 184 | /** 185 | * Get the current connection state of the profile 186 | * 187 | *

Requires {@link android.Manifest.permission#BLUETOOTH} permission. 188 | * 189 | * @param device Remote bluetooth device. 190 | * @return State of the profile connection. One of 191 | * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, 192 | * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} 193 | */ 194 | public int getConnectionState(BluetoothDevice device); 195 | 196 | /** 197 | * An interface for notifying BluetoothProfile IPC clients when they have 198 | * been connected or disconnected to the service. 199 | */ 200 | public interface ServiceListener { 201 | /** 202 | * Called to notify the client when the proxy object has been 203 | * connected to the service. 204 | * @param profile - One of {@link #HEALTH}, {@link #HEADSET} or 205 | * {@link #A2DP} 206 | * @param proxy - One of {@link BluetoothHealth}, {@link BluetoothHeadset} or 207 | * {@link BluetoothA2dp} 208 | */ 209 | public void onServiceConnected(int profile, BluetoothProfile proxy); 210 | 211 | /** 212 | * Called to notify the client that this proxy object has been 213 | * disconnected from the service. 214 | * @param profile - One of {@link #HEALTH}, {@link #HEADSET} or 215 | * {@link #A2DP} 216 | */ 217 | public void onServiceDisconnected(int profile); 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/android/media/AudioGain.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 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 android.media; 18 | 19 | /** 20 | * The AudioGain describes a gain controller. Gain controllers are exposed by 21 | * audio ports when the gain is configurable at this port's input or output. 22 | * Gain values are expressed in millibels. 23 | * A gain controller has the following attributes: 24 | * - mode: defines modes of operation or features 25 | * MODE_JOINT: all channel gains are controlled simultaneously 26 | * MODE_CHANNELS: each channel gain is controlled individually 27 | * MODE_RAMP: ramps can be applied when gain changes 28 | * - channel mask: indicates for which channels the gain can be controlled 29 | * - min value: minimum gain value in millibel 30 | * - max value: maximum gain value in millibel 31 | * - default value: gain value after reset in millibel 32 | * - step value: granularity of gain control in millibel 33 | * - min ramp duration: minimum ramp duration in milliseconds 34 | * - max ramp duration: maximum ramp duration in milliseconds 35 | * 36 | * This object is always created by the framework and read only by applications. 37 | * Applications get a list of AudioGainDescriptors from AudioPortDescriptor.gains() and can build a 38 | * valid gain configuration from AudioGain.buildConfig() 39 | * @hide 40 | */ 41 | public class AudioGain { 42 | 43 | /** 44 | * Bit of AudioGain.mode() field indicating that 45 | * all channel gains are controlled simultaneously 46 | */ 47 | public static final int MODE_JOINT = 1; 48 | /** 49 | * Bit of AudioGain.mode() field indicating that 50 | * each channel gain is controlled individually 51 | */ 52 | public static final int MODE_CHANNELS = 2; 53 | /** 54 | * Bit of AudioGain.mode() field indicating that 55 | * ramps can be applied when gain changes. The type of ramp (linear, log etc...) is 56 | * implementation specific. 57 | */ 58 | public static final int MODE_RAMP = 4; 59 | 60 | private final int mIndex; 61 | private final int mMode; 62 | private final int mChannelMask; 63 | private final int mMinValue; 64 | private final int mMaxValue; 65 | private final int mDefaultValue; 66 | private final int mStepValue; 67 | private final int mRampDurationMinMs; 68 | private final int mRampDurationMaxMs; 69 | 70 | // The channel mask passed to the constructor is as specified in AudioFormat 71 | // (e.g. AudioFormat.CHANNEL_OUT_STEREO) 72 | AudioGain(int index, int mode, int channelMask, 73 | int minValue, int maxValue, int defaultValue, int stepValue, 74 | int rampDurationMinMs, int rampDurationMaxMs) { 75 | mIndex = index; 76 | mMode = mode; 77 | mChannelMask = channelMask; 78 | mMinValue = minValue; 79 | mMaxValue = maxValue; 80 | mDefaultValue = defaultValue; 81 | mStepValue = stepValue; 82 | mRampDurationMinMs = rampDurationMinMs; 83 | mRampDurationMaxMs = rampDurationMaxMs; 84 | } 85 | 86 | /** 87 | * Bit field indicating supported modes of operation 88 | */ 89 | public int mode() { 90 | return mMode; 91 | } 92 | 93 | /** 94 | * Indicates for which channels the gain can be controlled 95 | * (e.g. AudioFormat.CHANNEL_OUT_STEREO) 96 | */ 97 | public int channelMask() { 98 | return mChannelMask; 99 | } 100 | 101 | /** 102 | * Minimum gain value in millibel 103 | */ 104 | public int minValue() { 105 | return mMinValue; 106 | } 107 | 108 | /** 109 | * Maximum gain value in millibel 110 | */ 111 | public int maxValue() { 112 | return mMaxValue; 113 | } 114 | 115 | /** 116 | * Default gain value in millibel 117 | */ 118 | public int defaultValue() { 119 | return mDefaultValue; 120 | } 121 | 122 | /** 123 | * Granularity of gain control in millibel 124 | */ 125 | public int stepValue() { 126 | return mStepValue; 127 | } 128 | 129 | /** 130 | * Minimum ramp duration in milliseconds 131 | * 0 if MODE_RAMP not set 132 | */ 133 | public int rampDurationMinMs() { 134 | return mRampDurationMinMs; 135 | } 136 | 137 | /** 138 | * Maximum ramp duration in milliseconds 139 | * 0 if MODE_RAMP not set 140 | */ 141 | public int rampDurationMaxMs() { 142 | return mRampDurationMaxMs; 143 | } 144 | 145 | /** 146 | * Build a valid gain configuration for this gain controller for use by 147 | * AudioPortDescriptor.setGain() 148 | * @param mode: desired mode of operation 149 | * @param channelMask: channels of which the gain should be modified. 150 | * @param values: gain values for each channels. 151 | * @param rampDurationMs: ramp duration if mode MODE_RAMP is set. 152 | * ignored if MODE_JOINT. 153 | */ 154 | public AudioGainConfig buildConfig(int mode, int channelMask, 155 | int[] values, int rampDurationMs) { 156 | //TODO: check params here 157 | return new AudioGainConfig(mIndex, this, mode, channelMask, values, rampDurationMs); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/android/media/AudioGainConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 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 android.media; 18 | 19 | /** 20 | * The AudioGainConfig is used by APIs setting or getting values on a given gain 21 | * controller. It contains a valid configuration (value, channels...) for a gain controller 22 | * exposed by an audio port. 23 | * @see AudioGain 24 | * @see AudioPort 25 | * @hide 26 | */ 27 | public class AudioGainConfig { 28 | AudioGain mGain; 29 | private final int mIndex; 30 | private final int mMode; 31 | private final int mChannelMask; 32 | private final int mValues[]; 33 | private final int mRampDurationMs; 34 | 35 | AudioGainConfig(int index, AudioGain gain, int mode, int channelMask, 36 | int[] values, int rampDurationMs) { 37 | mIndex = index; 38 | mGain = gain; 39 | mMode = mode; 40 | mChannelMask = channelMask; 41 | mValues = values; 42 | mRampDurationMs = rampDurationMs; 43 | } 44 | 45 | /** 46 | * get the index of the parent gain. 47 | * frameworks use only. 48 | */ 49 | int index() { 50 | return mIndex; 51 | } 52 | 53 | /** 54 | * Bit field indicating requested modes of operation. See {@link AudioGain#MODE_JOINT}, 55 | * {@link AudioGain#MODE_CHANNELS}, {@link AudioGain#MODE_RAMP} 56 | */ 57 | public int mode() { 58 | return mMode; 59 | } 60 | 61 | /** 62 | * Indicates for which channels the gain is set. 63 | * See {@link AudioFormat#CHANNEL_OUT_STEREO}, {@link AudioFormat#CHANNEL_OUT_MONO} ... 64 | */ 65 | public int channelMask() { 66 | return mChannelMask; 67 | } 68 | 69 | /** 70 | * Gain values for each channel in the order of bits set in 71 | * channelMask() from LSB to MSB 72 | */ 73 | public int[] values() { 74 | return mValues; 75 | } 76 | 77 | /** 78 | * Ramp duration in milliseconds. N/A if mode() does not 79 | * specify MODE_RAMP. 80 | */ 81 | public int rampDurationMs() { 82 | return mRampDurationMs; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/android/media/AudioHandle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 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 android.media; 18 | 19 | /** 20 | * The AudioHandle is used by the audio framework implementation to 21 | * uniquely identify a particular component of the routing topology 22 | * (AudioPort or AudioPatch) 23 | * It is not visible or used at the API. 24 | */ 25 | class AudioHandle { 26 | private final int mId; 27 | 28 | AudioHandle(int id) { 29 | mId = id; 30 | } 31 | 32 | int id() { 33 | return mId; 34 | } 35 | 36 | @Override 37 | public boolean equals(Object o) { 38 | if (o == null || !(o instanceof AudioHandle)) { 39 | return false; 40 | } 41 | AudioHandle ah = (AudioHandle)o; 42 | return mId == ah.id(); 43 | } 44 | 45 | @Override 46 | public int hashCode() { 47 | return mId; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return Integer.toString(mId); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/android/media/AudioPatch.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 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 android.media; 18 | 19 | 20 | /** 21 | * An AudioPatch describes a connection between audio sources and audio sinks. 22 | * An audio source can be an output mix (playback AudioBus) or an input device (microphone). 23 | * An audio sink can be an output device (speaker) or an input mix (capture AudioBus). 24 | * An AudioPatch is created by AudioManager.createAudioPatch() and released by 25 | * AudioManager.releaseAudioPatch() 26 | * It contains the list of source and sink AudioPortConfig showing audio port configurations 27 | * being connected. 28 | * @hide 29 | */ 30 | public class AudioPatch { 31 | 32 | private final AudioHandle mHandle; 33 | private final AudioPortConfig[] mSources; 34 | private final AudioPortConfig[] mSinks; 35 | 36 | AudioPatch(AudioHandle patchHandle, AudioPortConfig[] sources, AudioPortConfig[] sinks) { 37 | mHandle = patchHandle; 38 | mSources = sources; 39 | mSinks = sinks; 40 | } 41 | 42 | /** 43 | * Retrieve the list of sources of this audio patch. 44 | */ 45 | public AudioPortConfig[] sources() { 46 | return mSources; 47 | } 48 | 49 | /** 50 | * Retreive the list of sinks of this audio patch. 51 | */ 52 | public AudioPortConfig[] sinks() { 53 | return mSinks; 54 | } 55 | 56 | @Override 57 | public String toString() { 58 | StringBuilder s = new StringBuilder(); 59 | s.append("mHandle: "); 60 | s.append(mHandle.toString()); 61 | 62 | s.append(" mSources: {"); 63 | for (AudioPortConfig source : mSources) { 64 | s.append(source.toString()); 65 | s.append(", "); 66 | } 67 | s.append("} mSinks: {"); 68 | for (AudioPortConfig sink : mSinks) { 69 | s.append(sink.toString()); 70 | s.append(", "); 71 | } 72 | s.append("}"); 73 | 74 | return s.toString(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/android/media/AudioPort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 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 android.media; 18 | 19 | /** 20 | * An audio port is a node of the audio framework or hardware that can be connected to or 21 | * disconnect from another audio node to create a specific audio routing configuration. 22 | * Examples of audio ports are an output device (speaker) or an output mix (see AudioMixPort). 23 | * All attributes that are relevant for applications to make routing selection are decribed 24 | * in an AudioPort, in particular: 25 | * - possible channel mask configurations. 26 | * - audio format (PCM 16bit, PCM 24bit...) 27 | * - gain: a port can be associated with one or more gain controllers (see AudioGain). 28 | * 29 | * This object is always created by the framework and read only by applications. 30 | * A list of all audio port descriptors currently available for applications to control 31 | * is obtained by AudioManager.listAudioPorts(). 32 | * An application can obtain an AudioPortConfig for a valid configuration of this port 33 | * by calling AudioPort.buildConfig() and use this configuration 34 | * to create a connection between audio sinks and sources with AudioManager.connectAudioPatch() 35 | * 36 | * @hide 37 | */ 38 | public class AudioPort { 39 | private static final String TAG = "AudioPort"; 40 | 41 | /** 42 | * For use by the audio framework. 43 | */ 44 | public static final int ROLE_NONE = 0; 45 | /** 46 | * The audio port is a source (produces audio) 47 | */ 48 | public static final int ROLE_SOURCE = 1; 49 | /** 50 | * The audio port is a sink (consumes audio) 51 | */ 52 | public static final int ROLE_SINK = 2; 53 | 54 | /** 55 | * audio port type for use by audio framework implementation 56 | */ 57 | public static final int TYPE_NONE = 0; 58 | /** 59 | */ 60 | public static final int TYPE_DEVICE = 1; 61 | /** 62 | */ 63 | public static final int TYPE_SUBMIX = 2; 64 | /** 65 | */ 66 | public static final int TYPE_SESSION = 3; 67 | 68 | 69 | AudioHandle mHandle; 70 | protected final int mRole; 71 | private final String mName; 72 | private final int[] mSamplingRates; 73 | private final int[] mChannelMasks; 74 | private final int[] mChannelIndexMasks; 75 | private final int[] mFormats; 76 | private final AudioGain[] mGains; 77 | private AudioPortConfig mActiveConfig; 78 | 79 | AudioPort(AudioHandle handle, int role, String name, 80 | int[] samplingRates, int[] channelMasks, int[] channelIndexMasks, 81 | int[] formats, AudioGain[] gains) { 82 | 83 | mHandle = handle; 84 | mRole = role; 85 | mName = name; 86 | mSamplingRates = samplingRates; 87 | mChannelMasks = channelMasks; 88 | mChannelIndexMasks = channelIndexMasks; 89 | mFormats = formats; 90 | mGains = gains; 91 | } 92 | 93 | AudioHandle handle() { 94 | return mHandle; 95 | } 96 | 97 | /** 98 | * Get the system unique device ID. 99 | */ 100 | public int id() { 101 | return mHandle.id(); 102 | } 103 | 104 | 105 | /** 106 | * Get the audio port role 107 | */ 108 | public int role() { 109 | return mRole; 110 | } 111 | 112 | /** 113 | * Get the human-readable name of this port. Perhaps an internal 114 | * designation or an physical device. 115 | */ 116 | public String name() { 117 | return mName; 118 | } 119 | 120 | /** 121 | * Get the list of supported sampling rates 122 | * Empty array if sampling rate is not relevant for this audio port 123 | */ 124 | public int[] samplingRates() { 125 | return mSamplingRates; 126 | } 127 | 128 | /** 129 | * Get the list of supported channel mask configurations 130 | * (e.g AudioFormat.CHANNEL_OUT_STEREO) 131 | * Empty array if channel mask is not relevant for this audio port 132 | */ 133 | public int[] channelMasks() { 134 | return mChannelMasks; 135 | } 136 | 137 | /** 138 | * Get the list of supported channel index mask configurations 139 | * (e.g 0x0003 means 2 channel, 0x000F means 4 channel....) 140 | * Empty array if channel index mask is not relevant for this audio port 141 | */ 142 | public int[] channelIndexMasks() { 143 | return mChannelIndexMasks; 144 | } 145 | 146 | /** 147 | * Get the list of supported audio format configurations 148 | * (e.g AudioFormat.ENCODING_PCM_16BIT) 149 | * Empty array if format is not relevant for this audio port 150 | */ 151 | public int[] formats() { 152 | return mFormats; 153 | } 154 | 155 | /** 156 | * Get the list of gain descriptors 157 | * Empty array if this port does not have gain control 158 | */ 159 | public AudioGain[] gains() { 160 | return mGains; 161 | } 162 | 163 | /** 164 | * Get the gain descriptor at a given index 165 | */ 166 | AudioGain gain(int index) { 167 | if (index < 0 || index >= mGains.length) { 168 | return null; 169 | } 170 | return mGains[index]; 171 | } 172 | 173 | /** 174 | * Build a specific configuration of this audio port for use by methods 175 | * like AudioManager.connectAudioPatch(). 176 | * @param channelMask The desired channel mask. AudioFormat.CHANNEL_OUT_DEFAULT if no change 177 | * from active configuration requested. 178 | * @param format The desired audio format. AudioFormat.ENCODING_DEFAULT if no change 179 | * from active configuration requested. 180 | * @param gain The desired gain. null if no gain changed requested. 181 | */ 182 | public AudioPortConfig buildConfig(int samplingRate, int channelMask, int format, 183 | AudioGainConfig gain) { 184 | return new AudioPortConfig(this, samplingRate, channelMask, format, gain); 185 | } 186 | 187 | /** 188 | * Get currently active configuration of this audio port. 189 | */ 190 | public AudioPortConfig activeConfig() { 191 | return mActiveConfig; 192 | } 193 | 194 | @Override 195 | public boolean equals(Object o) { 196 | if (o == null || !(o instanceof AudioPort)) { 197 | return false; 198 | } 199 | AudioPort ap = (AudioPort)o; 200 | return mHandle.equals(ap.handle()); 201 | } 202 | 203 | @Override 204 | public int hashCode() { 205 | return mHandle.hashCode(); 206 | } 207 | 208 | @Override 209 | public String toString() { 210 | String role = Integer.toString(mRole); 211 | switch (mRole) { 212 | case ROLE_NONE: 213 | role = "NONE"; 214 | break; 215 | case ROLE_SOURCE: 216 | role = "SOURCE"; 217 | break; 218 | case ROLE_SINK: 219 | role = "SINK"; 220 | break; 221 | } 222 | return "{mHandle: " + mHandle 223 | + ", mRole: " + role 224 | + "}"; 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/android/media/AudioPortConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 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 android.media; 18 | 19 | /** 20 | * An AudioPortConfig contains a possible configuration of an audio port chosen 21 | * among all possible attributes described by an AudioPort. 22 | * An AudioPortConfig is created by AudioPort.buildConfiguration(). 23 | * AudioPorts are used to specify the sources and sinks of a patch created 24 | * with AudioManager.connectAudioPatch(). 25 | * Several specialized versions of AudioPortConfig exist to handle different categories of 26 | * audio ports and their specific attributes: 27 | * - AudioDevicePortConfig for input (e.g micropohone) and output devices (e.g speaker) 28 | * - AudioMixPortConfig for input or output streams of the audio framework. 29 | * @hide 30 | */ 31 | 32 | public class AudioPortConfig { 33 | final AudioPort mPort; 34 | private final int mSamplingRate; 35 | private final int mChannelMask; 36 | private final int mFormat; 37 | private final AudioGainConfig mGain; 38 | 39 | // mConfigMask indicates which fields in this configuration should be 40 | // taken into account. Used with AudioSystem.setAudioPortConfig() 41 | // framework use only. 42 | static final int SAMPLE_RATE = 0x1; 43 | static final int CHANNEL_MASK = 0x2; 44 | static final int FORMAT = 0x4; 45 | static final int GAIN = 0x8; 46 | int mConfigMask; 47 | 48 | AudioPortConfig(AudioPort port, int samplingRate, int channelMask, int format, 49 | AudioGainConfig gain) { 50 | mPort = port; 51 | mSamplingRate = samplingRate; 52 | mChannelMask = channelMask; 53 | mFormat = format; 54 | mGain = gain; 55 | mConfigMask = 0; 56 | } 57 | 58 | /** 59 | * Returns the audio port this AudioPortConfig is issued from. 60 | */ 61 | public AudioPort port() { 62 | return mPort; 63 | } 64 | 65 | /** 66 | * Sampling rate configured for this AudioPortConfig. 67 | */ 68 | public int samplingRate() { 69 | return mSamplingRate; 70 | } 71 | 72 | /** 73 | * Channel mask configuration (e.g AudioFormat.CHANNEL_CONFIGURATION_STEREO). 74 | */ 75 | public int channelMask() { 76 | return mChannelMask; 77 | } 78 | 79 | /** 80 | * Audio format configuration (e.g AudioFormat.ENCODING_PCM_16BIT). 81 | */ 82 | public int format() { 83 | return mFormat; 84 | } 85 | 86 | /** 87 | * The gain configuration if this port supports gain control, null otherwise 88 | * @see AudioGainConfig. 89 | */ 90 | public AudioGainConfig gain() { 91 | return mGain; 92 | } 93 | 94 | @Override 95 | public String toString() { 96 | return "{mPort:" + mPort 97 | + ", mSamplingRate:" + mSamplingRate 98 | + ", mChannelMask: " + mChannelMask 99 | + ", mFormat:" + mFormat 100 | + ", mGain:" + mGain 101 | + "}"; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/android/os/IServiceManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006 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 android.os; 18 | 19 | /** 20 | * Basic interface for finding and publishing system services. 21 | * 22 | * An implementation of this interface is usually published as the 23 | * global context object, which can be retrieved via 24 | * BinderNative.getContextObject(). An easy way to retrieve this 25 | * is with the static method BnServiceManager.getDefault(). 26 | * 27 | * @hide 28 | */ 29 | public interface IServiceManager extends IInterface 30 | { 31 | /** 32 | * Retrieve an existing service called @a name from the 33 | * service manager. Blocks for a few seconds waiting for it to be 34 | * published if it does not already exist. 35 | */ 36 | public IBinder getService(String name) throws RemoteException; 37 | 38 | /** 39 | * Retrieve an existing service called @a name from the 40 | * service manager. Non-blocking. 41 | */ 42 | public IBinder checkService(String name) throws RemoteException; 43 | 44 | /** 45 | * Place a new @a service called @a name into the service 46 | * manager. 47 | */ 48 | public void addService(String name, IBinder service, boolean allowIsolated) 49 | throws RemoteException; 50 | 51 | /** 52 | * Return a list of all currently running services. 53 | */ 54 | public String[] listServices() throws RemoteException; 55 | 56 | /** 57 | * Assign a permission controller to the service manager. After set, this 58 | * interface is checked before any services are added. 59 | */ 60 | public void setPermissionController(IPermissionController controller) 61 | throws RemoteException; 62 | 63 | static final String descriptor = "android.os.IServiceManager"; 64 | 65 | int GET_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION; 66 | int CHECK_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+1; 67 | int ADD_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+2; 68 | int LIST_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+3; 69 | int CHECK_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+4; 70 | int SET_PERMISSION_CONTROLLER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+5; 71 | } 72 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/android/os/ServiceManager.java: -------------------------------------------------------------------------------- 1 | package android.os; 2 | /* 3 | * Copyright (C) 2007 The Android Open Source Project 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 19 | 20 | 21 | import java.util.HashMap; 22 | import java.util.Map; 23 | 24 | /** @hide */ 25 | public final class ServiceManager { 26 | private static final String TAG = "ServiceManager"; 27 | 28 | private static IServiceManager sServiceManager; 29 | private static HashMap sCache = new HashMap(); 30 | 31 | private static IServiceManager getIServiceManager() { 32 | throw new RuntimeException("Stub!!!"); 33 | } 34 | 35 | /** 36 | * Returns a reference to a service with the given name. 37 | * 38 | * @param name the name of the service to get 39 | * @return a reference to the service, or null if the service doesn't exist 40 | */ 41 | public static IBinder getService(String name) { 42 | throw new RuntimeException("Stub!!!"); 43 | } 44 | 45 | /** 46 | * Place a new @a service called @a name into the service 47 | * manager. 48 | * 49 | * @param name the name of the new service 50 | * @param service the service object 51 | */ 52 | public static void addService(String name, IBinder service) { 53 | throw new RuntimeException("Stub!!!"); 54 | } 55 | 56 | /** 57 | * Place a new @a service called @a name into the service 58 | * manager. 59 | * 60 | * @param name the name of the new service 61 | * @param service the service object 62 | * @param allowIsolated set to true to allow isolated sandboxed processes 63 | * to access this service 64 | */ 65 | public static void addService(String name, IBinder service, boolean allowIsolated) { 66 | throw new RuntimeException("Stub!!!"); 67 | } 68 | 69 | /** 70 | * Retrieve an existing service called @a name from the 71 | * service manager. Non-blocking. 72 | */ 73 | public static IBinder checkService(String name) { 74 | throw new RuntimeException("Stub!!!"); 75 | } 76 | 77 | /** 78 | * Return a list of all currently running services. 79 | */ 80 | public static String[] listServices() throws RemoteException { 81 | throw new RuntimeException("Stub!!!"); 82 | } 83 | 84 | /** 85 | * This is only intended to be called when the process is first being brought 86 | * up and bound by the activity manager. There is only one thread in the process 87 | * at that time, so no locking is done. 88 | * 89 | * @param cache the cache of service references 90 | * @hide 91 | */ 92 | public static void initServiceCache(Map cache) { 93 | throw new RuntimeException("Stub!!!"); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/android/os/SystemProperties.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006 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 android.os; 18 | 19 | import java.util.ArrayList; 20 | 21 | 22 | /** 23 | * Gives access to the system properties store. The system properties 24 | * store contains a list of string key-value pairs. 25 | * 26 | * {@hide} 27 | */ 28 | public class SystemProperties 29 | { 30 | public static final int PROP_NAME_MAX = 31; 31 | public static final int PROP_VALUE_MAX = 91; 32 | 33 | private static final ArrayList sChangeCallbacks = new ArrayList(); 34 | 35 | private static native String native_get(String key); 36 | private static native String native_get(String key, String def); 37 | private static native int native_get_int(String key, int def); 38 | private static native long native_get_long(String key, long def); 39 | private static native boolean native_get_boolean(String key, boolean def); 40 | private static native void native_set(String key, String def); 41 | private static native void native_add_change_callback(); 42 | 43 | /** 44 | * Get the value for the given key. 45 | * @return an empty string if the key isn't found 46 | * @throws IllegalArgumentException if the key exceeds 32 characters 47 | */ 48 | public static String get(String key) { 49 | if (key.length() > PROP_NAME_MAX) { 50 | throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); 51 | } 52 | return native_get(key); 53 | } 54 | 55 | /** 56 | * Get the value for the given key. 57 | * @return if the key isn't found, return def if it isn't null, or an empty string otherwise 58 | * @throws IllegalArgumentException if the key exceeds 32 characters 59 | */ 60 | public static String get(String key, String def) { 61 | if (key.length() > PROP_NAME_MAX) { 62 | throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); 63 | } 64 | return native_get(key, def); 65 | } 66 | 67 | /** 68 | * Get the value for the given key, and return as an integer. 69 | * @param key the key to lookup 70 | * @param def a default value to return 71 | * @return the key parsed as an integer, or def if the key isn't found or 72 | * cannot be parsed 73 | * @throws IllegalArgumentException if the key exceeds 32 characters 74 | */ 75 | public static int getInt(String key, int def) { 76 | if (key.length() > PROP_NAME_MAX) { 77 | throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); 78 | } 79 | return native_get_int(key, def); 80 | } 81 | 82 | /** 83 | * Get the value for the given key, and return as a long. 84 | * @param key the key to lookup 85 | * @param def a default value to return 86 | * @return the key parsed as a long, or def if the key isn't found or 87 | * cannot be parsed 88 | * @throws IllegalArgumentException if the key exceeds 32 characters 89 | */ 90 | public static long getLong(String key, long def) { 91 | if (key.length() > PROP_NAME_MAX) { 92 | throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); 93 | } 94 | return native_get_long(key, def); 95 | } 96 | 97 | /** 98 | * Get the value for the given key, returned as a boolean. 99 | * Values 'n', 'no', '0', 'false' or 'off' are considered false. 100 | * Values 'y', 'yes', '1', 'true' or 'on' are considered true. 101 | * (case sensitive). 102 | * If the key does not exist, or has any other value, then the default 103 | * result is returned. 104 | * @param key the key to lookup 105 | * @param def a default value to return 106 | * @return the key parsed as a boolean, or def if the key isn't found or is 107 | * not able to be parsed as a boolean. 108 | * @throws IllegalArgumentException if the key exceeds 32 characters 109 | */ 110 | public static boolean getBoolean(String key, boolean def) { 111 | if (key.length() > PROP_NAME_MAX) { 112 | throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); 113 | } 114 | return native_get_boolean(key, def); 115 | } 116 | 117 | /** 118 | * Set the value for the given key. 119 | * @throws IllegalArgumentException if the key exceeds 32 characters 120 | * @throws IllegalArgumentException if the value exceeds 92 characters 121 | */ 122 | public static void set(String key, String val) { 123 | if (key.length() > PROP_NAME_MAX) { 124 | throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); 125 | } 126 | if (val != null && val.length() > PROP_VALUE_MAX) { 127 | throw new IllegalArgumentException("val.length > " + 128 | PROP_VALUE_MAX); 129 | } 130 | native_set(key, val); 131 | } 132 | 133 | public static void addChangeCallback(Runnable callback) { 134 | synchronized (sChangeCallbacks) { 135 | if (sChangeCallbacks.size() == 0) { 136 | native_add_change_callback(); 137 | } 138 | sChangeCallbacks.add(callback); 139 | } 140 | } 141 | 142 | static void callChangeCallbacks() { 143 | synchronized (sChangeCallbacks) { 144 | //Log.i("foo", "Calling " + sChangeCallbacks.size() + " change callbacks!"); 145 | if (sChangeCallbacks.size() == 0) { 146 | return; 147 | } 148 | ArrayList callbacks = new ArrayList(sChangeCallbacks); 149 | for (int i=0; i audioDeivceList = new ArrayList<>(); 53 | 54 | //sco 55 | final String bluetooth = context.getString(R.string.okbt_audiodevice_bluetooth); 56 | //听筒 57 | final String earphone = context.getString(R.string.okbt_audiodevice_earphone); 58 | //扬声器 59 | final String speaker = context.getString(R.string.okbt_audiodevice_speaker); 60 | 61 | if(OkBluetooth.isBluetoothEnable() && OkBluetooth.HFP.hasConnectedDevice()){ 62 | audioDeivceList.add(bluetooth); 63 | } 64 | 65 | audioDeivceList.add(earphone); 66 | audioDeivceList.add(speaker); 67 | 68 | listView.setAdapter(new BaseAdapter() { 69 | 70 | @Override 71 | public View getView(int position, View convertView, ViewGroup parent) { 72 | 73 | String item = audioDeivceList.get(position); 74 | 75 | View view = LayoutInflater.from(context).inflate(R.layout.okbt_audio_device_list_item, null); 76 | 77 | TextView textView = (TextView) view.findViewById(R.id.okbt_audiodevice_name); 78 | textView.setText(item); 79 | 80 | return view; 81 | } 82 | 83 | @Override 84 | public long getItemId(int position) { 85 | return 0; 86 | } 87 | 88 | @Override 89 | public Object getItem(int position) { 90 | return audioDeivceList.get(position); 91 | } 92 | 93 | @Override 94 | public int getCount() { 95 | return audioDeivceList.size(); 96 | } 97 | }); 98 | 99 | listView.setOnItemClickListener(new OnItemClickListener() { 100 | 101 | @Override 102 | public void onItemClick(AdapterView arg0, View arg1, int pos, 103 | long arg3) { 104 | 105 | String selected = audioDeivceList.get(pos); 106 | 107 | if(bluetooth.equals(selected)) { 108 | 109 | selectedListener.onSelected(AudioDevice.SCO); 110 | 111 | } else if(earphone.equals(selected)) { 112 | 113 | selectedListener.onSelected(AudioDevice.WIREDHEADSET); 114 | 115 | } else if(speaker.equals(selected)) { 116 | 117 | selectedListener.onSelected(AudioDevice.SPEAKER); 118 | 119 | } else { 120 | 121 | selectedListener.onSelected(AudioDevice.SBP); 122 | 123 | } 124 | 125 | dialog.dismiss(); 126 | 127 | } 128 | 129 | }); 130 | 131 | dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE); 132 | dialog.setContentView(view); 133 | dialog.show(); 134 | 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/AudioService.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth; 2 | 3 | import android.content.Context; 4 | import android.media.AudioManager; 5 | import android.media.AudioSystem; 6 | import android.media.IAudioService; 7 | import android.os.RemoteException; 8 | import android.os.ServiceManager; 9 | import android.util.Log; 10 | 11 | import com.devyok.bluetooth.OkBluetooth.Interceptor; 12 | import com.devyok.bluetooth.base.BluetoothException; 13 | import com.devyok.bluetooth.base.BluetoothRuntimeException; 14 | import com.devyok.bluetooth.base.BluetoothService; 15 | 16 | import java.lang.reflect.InvocationTargetException; 17 | import java.lang.reflect.Method; 18 | /** 19 | * @author wei.deng 20 | */ 21 | final class AudioService extends BluetoothService{ 22 | 23 | private static final String TAG = OkBluetooth.TAG; 24 | 25 | private AudioManager mAudioManager; 26 | private IAudioService mRealAudioService; 27 | private AudioModeModifier mAudioModeModifier = AudioModeModifier.DEFAULT; 28 | 29 | /** 30 | * 实现音频模式的修改 31 | */ 32 | public interface AudioModeModifier { 33 | public static final AudioModeModifier DEFAULT = new AudioModeModifier() { 34 | @Override 35 | public void modify(int audioMode) { 36 | 37 | int current = OkBluetooth.getAudioMode(); 38 | 39 | if(current != audioMode){ 40 | OkBluetooth.setAudioMode(audioMode); 41 | } 42 | 43 | } 44 | }; 45 | public void modify(int audioMode); 46 | } 47 | 48 | @Override 49 | public boolean init() throws BluetoothException { 50 | 51 | super.init(); 52 | 53 | mAudioManager = (AudioManager) OkBluetooth.getContext().getSystemService(Context.AUDIO_SERVICE); 54 | mRealAudioService = IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE)); 55 | 56 | return true; 57 | } 58 | 59 | @Override 60 | public boolean destory() { 61 | super.destory(); 62 | 63 | mAudioManager = null; 64 | mRealAudioService = null; 65 | 66 | return true; 67 | } 68 | 69 | public boolean connectAudio(AudioDevice audioDevice){ 70 | 71 | if(audioDevice == null) throw new BluetoothRuntimeException("audioDevice null"); 72 | 73 | Interceptor interceptor = OkBluetooth.getInterceptor(); 74 | 75 | if(interceptor.beforeConnect(audioDevice)) { 76 | Log.i(TAG, "[AudioService] connectAudio "+audioDevice + " intercepted"); 77 | return false; 78 | } 79 | 80 | if(AudioDevice.SBP == audioDevice){ 81 | 82 | } else if(AudioDevice.SPEAKER == audioDevice){ 83 | 84 | tryConnectSpeaker(); 85 | 86 | } else if(AudioDevice.SCO == audioDevice){ 87 | 88 | tryConnectSco(); 89 | 90 | } else if(AudioDevice.WIREDHEADSET == audioDevice){ 91 | 92 | tryConnectWiredHeadset(); 93 | 94 | } else if(AudioDevice.A2DP == audioDevice){ 95 | 96 | tryConnectSpeaker(); 97 | 98 | } 99 | 100 | return true; 101 | } 102 | 103 | public boolean isBluetoothA2dpOn(){ 104 | try { 105 | IAudioService audioService = mRealAudioService; 106 | boolean result = audioService.isBluetoothA2dpOn(); 107 | return result; 108 | } catch (RemoteException e) { 109 | e.printStackTrace(); 110 | Log.i(TAG, "[AudioService] isBluetoothA2dpOn on a2dp exception = " + e.getMessage()); 111 | } 112 | 113 | return false; 114 | } 115 | 116 | public boolean isWiredHeadsetOn(){ 117 | return mAudioManager.isWiredHeadsetOn(); 118 | } 119 | 120 | public void setBluetoothA2dpOn(boolean on){ 121 | try { 122 | IAudioService audioService = mRealAudioService; 123 | boolean result = audioService.isBluetoothA2dpOn(); 124 | Log.i(TAG, "[AudioService] setBluetoothA2dpOn pre state = "+ result); 125 | audioService.setBluetoothA2dpOn(on); 126 | } catch (RemoteException e) { 127 | e.printStackTrace(); 128 | Log.i(TAG, "[AudioService] setBluetoothA2dpOn on a2dp exception = " + e.getMessage()); 129 | } 130 | } 131 | 132 | /** 133 | * 尝试连接SCO 134 | */ 135 | void tryConnectSco() { 136 | boolean isPhoneCalling = OkBluetooth.isPhoneCalling(); 137 | boolean isAudioConnected = OkBluetooth.HFP.isAudioConnected(); 138 | boolean isBluetoothScoOn = isBluetoothScoOn(); 139 | boolean isBluetoothA2dpOn = isBluetoothA2dpOn(); 140 | boolean isSpeakerphoneOn = isSpeakerphoneOn(); 141 | 142 | Log.i(TAG, "[AudioService] tryConnectSco isAudioConnected = " + isAudioConnected + " , isBluetoothScoOn = " + isBluetoothScoOn + " , isBluetoothA2dpOn = " + isBluetoothA2dpOn + " , isSpeakerphoneOn = " + isSpeakerphoneOn); 143 | if(!isAudioConnected) { 144 | if(!isPhoneCalling) { 145 | mAudioModeModifier.modify(AudioManager.MODE_IN_COMMUNICATION); 146 | } 147 | setSpeakerphoneOn(false); 148 | setBluetoothA2dpOn(false); 149 | setBluetoothScoOn(false); 150 | if(isPhoneCalling) { 151 | OkBluetooth.HFP.connectAudio(); 152 | } else { 153 | OkBluetooth.startSco(); 154 | } 155 | } else { 156 | 157 | if(isBluetoothA2dpOn){ 158 | setBluetoothA2dpOn(false); 159 | } 160 | if(isSpeakerphoneOn){ 161 | setSpeakerphoneOn(false); 162 | } 163 | if(!isBluetoothScoOn) { 164 | setBluetoothScoOn(true); 165 | } 166 | 167 | } 168 | 169 | } 170 | 171 | /** 172 | * 尝试连接有线耳机 173 | */ 174 | void tryConnectWiredHeadset() { 175 | 176 | boolean isPhoneCalling = OkBluetooth.isPhoneCalling(); 177 | 178 | boolean isAudioConnected = OkBluetooth.HFP.isAudioConnected(); 179 | 180 | boolean isBluetoothScoOn = isBluetoothScoOn(); 181 | boolean isBluetoothA2dpOn = isBluetoothA2dpOn(); 182 | boolean isSpeakerphoneOn = isSpeakerphoneOn(); 183 | if(!isPhoneCalling){ 184 | mAudioModeModifier.modify(AudioManager.MODE_IN_COMMUNICATION); 185 | } 186 | Log.i(TAG, "[AudioService] tryConnectWiredHeadset isAudioConnected = " + isAudioConnected + " , isBluetoothScoOn = " + isBluetoothScoOn + " , isBluetoothA2dpOn = " + isBluetoothA2dpOn + " , isSpeakerphoneOn = " + isSpeakerphoneOn); 187 | 188 | if(isAudioConnected){ 189 | outofBluetoothScoEnv(); 190 | setSpeakerphoneOn(false); 191 | } else { 192 | if(OkBluetooth.isBluetoothA2dpOn()){ 193 | setBluetoothA2dpOn(false); 194 | } 195 | if(OkBluetooth.isBluetoothScoOn()){ 196 | setBluetoothScoOn(false); 197 | } 198 | if(OkBluetooth.isSpeakerphoneOn()){ 199 | setSpeakerphoneOn(false); 200 | } 201 | } 202 | } 203 | 204 | void tryConnectSpeaker() { 205 | boolean isPhoneCalling = OkBluetooth.isPhoneCalling(); 206 | boolean isAudioConnected = OkBluetooth.HFP.isAudioConnected(); 207 | boolean isBluetoothScoOn = isBluetoothScoOn(); 208 | boolean isBluetoothA2dpOn = isBluetoothA2dpOn(); 209 | boolean isSpeakerphoneOn = isSpeakerphoneOn(); 210 | if(!isPhoneCalling){ 211 | mAudioModeModifier.modify(AudioManager.MODE_NORMAL); 212 | } 213 | Log.i(TAG, "[AudioService] tryConnectSpeaker isAudioConnected = " + isAudioConnected + " , isBluetoothScoOn = " + isBluetoothScoOn + " , isBluetoothA2dpOn = " + isBluetoothA2dpOn + " , isSpeakerphoneOn = " + isSpeakerphoneOn); 214 | if(isAudioConnected) { 215 | outofBluetoothScoEnv(); 216 | } 217 | if(OkBluetooth.isBluetoothScoOn()){ 218 | setBluetoothScoOn(false); 219 | } 220 | if(!isSpeakerphoneOn) { 221 | setSpeakerphoneOn(true); 222 | } 223 | 224 | } 225 | 226 | /** 227 | * 退出SCO 228 | * @return 229 | */ 230 | private boolean outofBluetoothScoEnv(){ 231 | setBluetoothA2dpOn(false); 232 | boolean result = OkBluetooth.HFP.disconnectAudio(); 233 | setBluetoothScoOn(false); 234 | return result; 235 | } 236 | 237 | public void setAudioMode(int mode) { 238 | mAudioManager.setMode(mode); 239 | } 240 | 241 | public int getAudioMode(){ 242 | return mAudioManager.getMode(); 243 | } 244 | 245 | public void setScoStreamVolumn(int volumnIndex,int flags){ 246 | mAudioManager.setStreamVolume(AudioSystem.STREAM_BLUETOOTH_SCO, (volumnIndex), flags); 247 | } 248 | 249 | public void setStreamVolumn(int streamType,int volumnIndex,int flags){ 250 | mAudioManager.setStreamVolume(streamType, (volumnIndex), flags); 251 | } 252 | 253 | public int getCurrentStreamVolumn(int streamType){ 254 | return mAudioManager.getStreamVolume(streamType); 255 | } 256 | 257 | public int getMaxStreamVolumn(int streamType){ 258 | return mAudioManager.getStreamMaxVolume(streamType); 259 | } 260 | 261 | public int getScoStreamVolumn(){ 262 | return mAudioManager.getStreamVolume(AudioSystem.STREAM_BLUETOOTH_SCO); 263 | } 264 | 265 | public int getScoMaxStreamVolumn(){ 266 | return mAudioManager.getStreamMaxVolume(AudioSystem.STREAM_BLUETOOTH_SCO); 267 | } 268 | 269 | public void setSpeakerphoneOn(boolean on){ 270 | mAudioManager.setSpeakerphoneOn(on); 271 | } 272 | 273 | public void setBluetoothScoOn(boolean on){ 274 | mAudioManager.setBluetoothScoOn(on); 275 | } 276 | 277 | public boolean isBluetoothScoOn(){ 278 | return mAudioManager.isBluetoothScoOn(); 279 | } 280 | 281 | public boolean isSpeakerphoneOn(){ 282 | return mAudioManager.isSpeakerphoneOn(); 283 | } 284 | 285 | public void requestAudioFocusForCall(int streamType){ 286 | AudioManager audioManager = mAudioManager; 287 | 288 | try { 289 | Method method = audioManager.getClass().getDeclaredMethod("requestAudioFocusForCall", new Class[]{int.class,int.class}); 290 | 291 | method.invoke(audioManager, streamType,AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); 292 | 293 | } catch (NoSuchMethodException e) { 294 | e.printStackTrace(); 295 | } catch (SecurityException e) { 296 | e.printStackTrace(); 297 | } catch (IllegalAccessException e) { 298 | // TODO Auto-generated catch block 299 | e.printStackTrace(); 300 | } catch (IllegalArgumentException e) { 301 | // TODO Auto-generated catch block 302 | e.printStackTrace(); 303 | } catch (InvocationTargetException e) { 304 | // TODO Auto-generated catch block 305 | e.printStackTrace(); 306 | } 307 | } 308 | 309 | public void setAudioModeModifier(AudioModeModifier modifier) { 310 | this.mAudioModeModifier = (modifier != null ? modifier : AudioModeModifier.DEFAULT); 311 | } 312 | 313 | } 314 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/Configuration.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth; 2 | 3 | import com.devyok.bluetooth.OkBluetooth.ConnectionMode; 4 | import com.devyok.bluetooth.message.BluetoothMessageDispatcher; 5 | import com.devyok.bluetooth.message.BluetoothMessageHandler; 6 | import com.devyok.bluetooth.utils.BluetoothUtils; 7 | 8 | /** 9 | * @author deng.wei 10 | */ 11 | public final class Configuration { 12 | 13 | public static final Configuration DEFAULT = new Builder() 14 | .setDebug(true) 15 | .setSupport(true) 16 | .setConnectionMode(ConnectionMode.BLUETOOTH_WIREDHEADSET_SPEAKER) 17 | .build(); 18 | 19 | private boolean debug; 20 | private boolean isSupport; 21 | private boolean debugThread; 22 | private BluetoothMessageHandler messageDispatcher; 23 | private boolean isSupportScoDaemon = true; 24 | private int companyid = BluetoothUtils.UNKNOW; 25 | private ConnectionMode connectionMode = ConnectionMode.BLUETOOTH_WIREDHEADSET_SPEAKER; 26 | private int forceTypes; 27 | private Configuration(){} 28 | 29 | public boolean isDebugThread() { 30 | return debugThread; 31 | } 32 | 33 | public int getForceTypes(){ 34 | return forceTypes; 35 | } 36 | 37 | public ConnectionMode getConnectionMode(){ 38 | return connectionMode; 39 | } 40 | 41 | public int getCompanyId(){ 42 | return this.companyid; 43 | } 44 | 45 | public boolean isSupportScoDaemon(){ 46 | return isSupportScoDaemon; 47 | } 48 | 49 | public boolean isSupport() { 50 | return isSupport; 51 | } 52 | 53 | public boolean isDebug() { 54 | return debug; 55 | } 56 | 57 | public BluetoothMessageHandler getDispatcher(){ 58 | return (BluetoothMessageHandler) this.messageDispatcher; 59 | } 60 | 61 | @Override 62 | public String toString(){ 63 | StringBuffer buffer = new StringBuffer(); 64 | 65 | buffer.append("Configuration { "); 66 | buffer.append("debug").append("=").append(debug).append(",") 67 | .append("isSupport").append("=").append(isSupport).append(",") 68 | .append("debugThread").append("=").append(debugThread).append(",") 69 | .append("messageDispatcher").append("=").append(messageDispatcher).append(",") 70 | .append("isSupportScoDaemon").append("=").append(isSupportScoDaemon).append(",") 71 | .append("companyid").append("=").append(companyid).append(",") 72 | .append("connectionMode").append("=").append(connectionMode).append(",") 73 | .append("forceTypes").append("=").append(forceTypes).append(",") 74 | .append("hasForcePhoneIdle").append("=").append((forceTypes & OkBluetooth.FORCE_TYPE_PHONE_INCALL_TO_IDLE)!=0 ? "Y" : "N").append(",") 75 | .append("hasForcePhoneIncall").append("=").append((forceTypes & OkBluetooth.FORCE_TYPE_PHONE_INCALL)!=0 ? "Y" : "N").append(",") 76 | .append("hasForcePhoneRing").append("=").append((forceTypes & OkBluetooth.FORCE_TYPE_PHONE_RING)!=0 ? "Y" : "N"); 77 | buffer.append(" }"); 78 | 79 | return buffer.toString(); 80 | } 81 | 82 | public static class Builder { 83 | 84 | private boolean debug; 85 | private boolean isSupportBMS; 86 | private boolean debugThread; 87 | private int companyid; 88 | private BluetoothMessageHandler messageDispatcher; 89 | private ConnectionMode connectionMode; 90 | private int forceTypes; 91 | 92 | public Builder(){ 93 | } 94 | 95 | public Builder setForceTypes(int forceTypes){ 96 | this.forceTypes = forceTypes; 97 | return this; 98 | } 99 | 100 | public Builder setCompanyId(int companyid) { 101 | this.companyid = companyid; 102 | return this; 103 | } 104 | 105 | public Builder setConnectionMode(ConnectionMode mode) { 106 | this.connectionMode = mode; 107 | return this; 108 | } 109 | 110 | public Builder setDebug(boolean debug) { 111 | this.debug = debug; 112 | return this; 113 | } 114 | 115 | public Builder setSupport(boolean isSupport) { 116 | this.isSupportBMS = isSupport; 117 | return this; 118 | } 119 | 120 | public Builder setDebugThread(boolean debugThread) { 121 | this.debugThread = debugThread; 122 | return this; 123 | } 124 | 125 | public Builder setMessageDispatcher(BluetoothMessageHandler dispatch){ 126 | messageDispatcher = dispatch; 127 | return this; 128 | } 129 | 130 | public Configuration build(){ 131 | Configuration configuration = new Configuration(); 132 | 133 | configuration.debug = this.debug; 134 | configuration.debugThread = this.debugThread; 135 | configuration.isSupport = this.isSupportBMS; 136 | configuration.messageDispatcher = this.messageDispatcher; 137 | configuration.companyid = this.companyid; 138 | configuration.connectionMode = this.connectionMode; 139 | configuration.forceTypes = this.forceTypes; 140 | 141 | if(configuration.messageDispatcher == null){ 142 | configuration.messageDispatcher = BluetoothMessageDispatcher.getDispatcher(); 143 | } 144 | 145 | return configuration; 146 | } 147 | 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/TelephonyService.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth; 2 | 3 | import android.content.Context; 4 | import android.telephony.PhoneStateListener; 5 | import android.telephony.TelephonyManager; 6 | import android.util.Log; 7 | 8 | import com.devyok.bluetooth.ConnectionHelper.Event; 9 | import com.devyok.bluetooth.base.BluetoothException; 10 | import com.devyok.bluetooth.base.BluetoothService; 11 | /** 12 | * 13 | * @author wei.deng 14 | */ 15 | final class TelephonyService extends BluetoothService{ 16 | 17 | private static final String TAG = OkBluetooth.TAG; 18 | 19 | private boolean isListenPhoneState = true; 20 | 21 | /** 22 | * 负责监听系统电话状态,当系统电话挂断,则尝试提交恢复音频等连接的请求 23 | */ 24 | private volatile PhoneStateListenerImpl phoneStateListenerImpl; 25 | private TelephonyManager mTelephonyManager; 26 | 27 | public TelephonyService(){ 28 | this(true); 29 | } 30 | 31 | public TelephonyService(boolean isListenPhoneState){ 32 | this.isListenPhoneState = isListenPhoneState; 33 | } 34 | 35 | @Override 36 | public boolean init() throws BluetoothException { 37 | super.init(); 38 | mTelephonyManager = (TelephonyManager) OkBluetooth.getContext().getSystemService(Context.TELEPHONY_SERVICE); 39 | listenPhoneState(); 40 | 41 | return true; 42 | } 43 | 44 | @Override 45 | public boolean destory() { 46 | super.destory(); 47 | unlistenPhoneState(); 48 | return true; 49 | } 50 | 51 | private void listenPhoneState() { 52 | if(isListenPhoneState && phoneStateListenerImpl == null){ 53 | phoneStateListenerImpl = new PhoneStateListenerImpl(); 54 | mTelephonyManager.listen(phoneStateListenerImpl,PhoneStateListener.LISTEN_CALL_STATE); 55 | } 56 | } 57 | 58 | private void unlistenPhoneState() { 59 | if(isListenPhoneState && phoneStateListenerImpl != null){ 60 | mTelephonyManager.listen(phoneStateListenerImpl,PhoneStateListener.LISTEN_NONE); 61 | phoneStateListenerImpl = null; 62 | } 63 | } 64 | 65 | public boolean isPhoneCalling() { 66 | int callState = mTelephonyManager.getCallState(); 67 | if(callState == TelephonyManager.CALL_STATE_OFFHOOK || callState == TelephonyManager.CALL_STATE_RINGING){ 68 | return true; 69 | } 70 | return false; 71 | } 72 | 73 | class PhoneStateListenerImpl extends PhoneStateListener { 74 | 75 | volatile boolean called = false; 76 | 77 | @Override 78 | public void onCallStateChanged(int state, String incomingNumber) { 79 | 80 | switch (state) { 81 | case TelephonyManager.CALL_STATE_IDLE: 82 | Log.i(TAG, "[TelephonyService] call idle"); 83 | try { 84 | if(called && OkBluetooth.hasForcePhoneIdle()){ 85 | OkBluetooth.tryRecoveryAudioConnection(Event.PHONECALL_INCALL_TO_IDLE); 86 | } 87 | } finally { 88 | called = false; 89 | } 90 | 91 | break; 92 | case TelephonyManager.CALL_STATE_RINGING: 93 | case TelephonyManager.CALL_STATE_OFFHOOK: 94 | Log.i(TAG, "[TelephonyService] system phone calling"); 95 | called = true; 96 | break; 97 | } 98 | } 99 | } 100 | 101 | 102 | } 103 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/a2dp/A2dpProfileService.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.a2dp; 2 | 3 | import android.bluetooth.BluetoothA2dp; 4 | import android.bluetooth.BluetoothDevice; 5 | import android.bluetooth.BluetoothProfile; 6 | import android.bluetooth.BluetoothUuid; 7 | import android.os.ParcelUuid; 8 | 9 | import com.devyok.bluetooth.base.BluetoothProfileService; 10 | import com.devyok.bluetooth.base.BluetoothProfileServiceTemplate; 11 | import com.devyok.bluetooth.base.BluetoothRuntimeException; 12 | /** 13 | * @author wei.deng 14 | */ 15 | public class A2dpProfileService extends BluetoothProfileServiceTemplate implements BluetoothA2dpProfileService{ 16 | //copy from 5.0 settings 17 | public static final ParcelUuid[] SINK_UUIDS = { 18 | BluetoothUuid.AudioSink, 19 | BluetoothUuid.AdvAudioDist, 20 | }; 21 | 22 | public static final int PROFILE = BluetoothProfile.A2DP; 23 | 24 | public A2dpProfileService() { 25 | super(PROFILE); 26 | } 27 | 28 | public A2dpProfileService(BluetoothProfileService decorater) { 29 | super(PROFILE,decorater); 30 | } 31 | 32 | public A2dpProfileService(int profileType) { 33 | super(profileType); 34 | throw new BluetoothRuntimeException("not support"); 35 | } 36 | 37 | @Override 38 | public boolean isA2dpPlaying(final BluetoothDevice device) { 39 | 40 | if(realService == null) return false; 41 | 42 | return ((BluetoothA2dp)realService).isA2dpPlaying(device); 43 | 44 | } 45 | 46 | @Override 47 | protected ConnectionStateListenerArgs make() { 48 | return new ConnectionStateListenerArgs() { 49 | 50 | @Override 51 | public String extraPreState() { 52 | return BluetoothA2dp.EXTRA_PREVIOUS_STATE; 53 | } 54 | 55 | @Override 56 | public String extraNewState() { 57 | return BluetoothA2dp.EXTRA_STATE; 58 | } 59 | 60 | @Override 61 | public String extraDevice() { 62 | return BluetoothDevice.EXTRA_DEVICE; 63 | } 64 | 65 | @Override 66 | public String action() { 67 | return BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED; 68 | } 69 | }; 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/a2dp/BluetoothA2dpProfileService.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.a2dp; 2 | 3 | import android.bluetooth.BluetoothDevice; 4 | 5 | import com.devyok.bluetooth.base.BluetoothProfileService; 6 | /** 7 | * 8 | * @author wei.deng 9 | * 10 | */ 11 | public interface BluetoothA2dpProfileService extends BluetoothProfileService{ 12 | 13 | public boolean isA2dpPlaying(BluetoothDevice device); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/base/BaseBluetoothStateChangedListener.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.base; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.IntentFilter; 7 | 8 | import com.devyok.bluetooth.OkBluetooth; 9 | /** 10 | * @author wei.deng 11 | */ 12 | public abstract class BaseBluetoothStateChangedListener extends BroadcastReceiver{ 13 | 14 | private volatile boolean mCalled; 15 | 16 | public BaseBluetoothStateChangedListener(){ 17 | } 18 | 19 | public abstract boolean onChanged(StateInformation information); 20 | public abstract String[] actions(); 21 | 22 | void check(){ 23 | 24 | } 25 | 26 | public boolean isStarted() { 27 | return mCalled; 28 | } 29 | 30 | public void startListener() throws BluetoothException { 31 | 32 | check(); 33 | String[] actions = actions(); 34 | 35 | if(actions == null || actions.length == 0){ 36 | throw new BluetoothException("listener actions is empty"); 37 | } 38 | 39 | synchronized (this) { 40 | IntentFilter stateFilter = new IntentFilter(); 41 | for(int i = 0;i < actions.length;i++){ 42 | stateFilter.addAction(actions[i]); 43 | } 44 | OkBluetooth.getContext().registerReceiver(this, stateFilter); 45 | mCalled = true; 46 | } 47 | } 48 | 49 | public void stopListener() { 50 | 51 | check(); 52 | synchronized (this) { 53 | if (isStarted()) { 54 | try { 55 | OkBluetooth.getContext().unregisterReceiver(this); 56 | } finally { 57 | recycle(); 58 | } 59 | } 60 | } 61 | } 62 | 63 | @Override 64 | public void onReceive(Context context, Intent intent) { 65 | 66 | StateInformation information = StateInformation.toInformation(intent); 67 | 68 | onChanged(information); 69 | 70 | } 71 | 72 | public void recycle(){ 73 | mCalled = false; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/base/BluetoothAdapterService.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.base; 2 | 3 | import android.bluetooth.BluetoothA2dp; 4 | import android.bluetooth.BluetoothAdapter; 5 | import android.bluetooth.BluetoothDevice; 6 | import android.bluetooth.BluetoothHeadset; 7 | import android.bluetooth.BluetoothProfile; 8 | import android.bluetooth.BluetoothProfile.ServiceListener; 9 | import android.os.Build; 10 | import android.util.Log; 11 | 12 | import com.devyok.bluetooth.OkBluetooth; 13 | import com.devyok.bluetooth.OkBluetooth.Callback2; 14 | import com.devyok.bluetooth.utils.BluetoothUtils; 15 | 16 | import java.lang.reflect.InvocationTargetException; 17 | import java.lang.reflect.Method; 18 | import java.util.List; 19 | import java.util.Set; 20 | /** 21 | * @author deng.wei 22 | */ 23 | public class BluetoothAdapterService extends BluetoothService{ 24 | 25 | private static String TAG = BluetoothAdapterService.class.getSimpleName(); 26 | 27 | private BluetoothAdapterStateListener mAdapterStateListener = BluetoothAdapterStateListener.EMPTY; 28 | private final BluetoothAdapterStateListenerImpl mAdapterStateListenerImpl = new BluetoothAdapterStateListenerImpl(); 29 | 30 | public BluetoothAdapterService(){ 31 | } 32 | 33 | public int getProfileConnectionState(int profile) { 34 | return BluetoothAdapter.getDefaultAdapter().getProfileConnectionState(profile); 35 | } 36 | 37 | @Override 38 | public boolean init() throws BluetoothException { 39 | super.init(); 40 | mAdapterStateListenerImpl.startListener(); 41 | return true; 42 | } 43 | 44 | @Override 45 | public boolean destory() { 46 | super.destory(); 47 | mAdapterStateListenerImpl.stopListener(); 48 | mAdapterStateListener = null; 49 | return true; 50 | } 51 | 52 | public int getConnectionState() { 53 | 54 | BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 55 | 56 | try { 57 | Method m = adapter.getClass().getDeclaredMethod("getConnectionState", null); 58 | 59 | try { 60 | Object result = m.invoke(adapter, null); 61 | 62 | if(result!=null){ 63 | return Integer.valueOf(result.toString()); 64 | } 65 | 66 | } catch (IllegalAccessException | IllegalArgumentException 67 | | InvocationTargetException e) { 68 | e.printStackTrace(); 69 | } 70 | 71 | } catch (NoSuchMethodException | SecurityException e) { 72 | e.printStackTrace(); 73 | } 74 | 75 | return BluetoothAdapter.STATE_DISCONNECTED; 76 | } 77 | 78 | public Set getBondedDevices(){ 79 | return BluetoothAdapter.getDefaultAdapter().getBondedDevices(); 80 | } 81 | 82 | public void setBluetoothAdapterStateListener(BluetoothAdapterStateListener lis){ 83 | this.mAdapterStateListener = (lis!=null ? lis : BluetoothAdapterStateListener.EMPTY); 84 | } 85 | 86 | /** 87 | * 打开蓝牙 88 | */ 89 | public void enable(){ 90 | BluetoothAdapter.getDefaultAdapter().enable(); 91 | } 92 | 93 | public void closeBluetoothProfile(int profile,BluetoothProfile bluetoothProfile) { 94 | if(bluetoothProfile==null) return ; 95 | BluetoothAdapter.getDefaultAdapter().closeProfileProxy(profile, bluetoothProfile); 96 | } 97 | 98 | public void closeBluetoothProfile(BluetoothProfile bluetoothProfile) { 99 | if(bluetoothProfile==null) return ; 100 | if (bluetoothProfile instanceof BluetoothA2dp) { 101 | closeBluetoothProfile(BluetoothProfile.A2DP,bluetoothProfile); 102 | } else if (bluetoothProfile instanceof BluetoothHeadset) { 103 | closeBluetoothProfile(BluetoothProfile.HEADSET,bluetoothProfile); 104 | } 105 | } 106 | 107 | public boolean isEnable(){ 108 | return BluetoothAdapter.getDefaultAdapter().isEnabled(); 109 | } 110 | 111 | public boolean disable(){ 112 | return BluetoothAdapter.getDefaultAdapter().disable(); 113 | } 114 | 115 | public boolean getProfileService(final int profileParam,final ServiceListener serviceListener){ 116 | 117 | boolean result = BluetoothAdapter.getDefaultAdapter().getProfileProxy(OkBluetooth.getContext(), new ServiceListener() { 118 | 119 | @Override 120 | public void onServiceDisconnected(int profile) { 121 | 122 | if(serviceListener!=null){ 123 | serviceListener.onServiceDisconnected(profile); 124 | } 125 | 126 | } 127 | 128 | public void onServiceConnected(int profile, BluetoothProfile proxy) { 129 | 130 | if(serviceListener!=null){ 131 | serviceListener.onServiceConnected(profile, proxy); 132 | } 133 | 134 | } 135 | }, profileParam); 136 | 137 | return result; 138 | } 139 | 140 | public void getConnectedBluetoothDevice(final int profileParam, 141 | final Callback2> serviceConnectedCallback){ 142 | 143 | if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 144 | 145 | boolean result = getProfileService(profileParam, new ServiceListener() { 146 | 147 | void handleCallback(BluetoothProfile profile,List list) { 148 | if(serviceConnectedCallback!=null){ 149 | serviceConnectedCallback.callback(profile, list); 150 | } 151 | } 152 | 153 | @Override 154 | public void onServiceDisconnected(int profile) { 155 | 156 | Log.i(TAG, "[devybt connect] getConnectedBluetoothDevice BluetoothProfile("+BluetoothUtils.getProfileString(profile)+") onServiceDisconnected()"); 157 | 158 | if(serviceConnectedCallback!=null){ 159 | serviceConnectedCallback.callback(null, null); 160 | } 161 | 162 | } 163 | 164 | public void onServiceConnected(int profile, BluetoothProfile proxy) { 165 | try { 166 | switch (profile) { 167 | case BluetoothProfile.A2DP: 168 | case BluetoothProfile.HEADSET: 169 | 170 | Log.i(TAG, "[devybt connect] getConnectedBluetoothDevice BluetoothProfile("+BluetoothUtils.getProfileString(profile)+") onServiceConnected()"); 171 | 172 | List connectedDevices = proxy.getConnectedDevices(); 173 | 174 | Log.i(TAG, "[devybt connect] getConnectedBluetoothDevice BluetoothProfile("+BluetoothUtils.getProfileString(profile)+") connectedDevices(size = "+connectedDevices.size()+")"); 175 | 176 | handleCallback(proxy,connectedDevices); 177 | 178 | break; 179 | case BluetoothProfile.HEALTH: 180 | Log.i(TAG, "[devybt connect] BluetoothProfile.HEALTH onServiceConnected()"); 181 | handleCallback(null,null); 182 | break; 183 | default: 184 | Log.i(TAG, "[devybt connect] BluetoothProfile.default onServiceConnected()"); 185 | handleCallback(null,null); 186 | break; 187 | } 188 | 189 | } catch(Exception e){ 190 | handleCallback(null,null); 191 | } 192 | 193 | } 194 | }); 195 | 196 | 197 | if(!result){ //get error , notify 198 | if(serviceConnectedCallback!=null){ 199 | serviceConnectedCallback.callback(null, null); 200 | } 201 | } 202 | 203 | Log.i(TAG, "[devybt connect] BluetoothProfile.getProfileProxy() result = " + result); 204 | 205 | } else { 206 | if(serviceConnectedCallback!=null){ 207 | serviceConnectedCallback.callback(null, null); 208 | } 209 | } 210 | 211 | } 212 | 213 | private class BluetoothAdapterStateListenerImpl extends BaseBluetoothStateChangedListener { 214 | 215 | public BluetoothAdapterStateListenerImpl() { 216 | } 217 | 218 | @Override 219 | public boolean onChanged(StateInformation information) { 220 | 221 | if(OkBluetooth.isDebugable()) { 222 | BluetoothUtils.dumpBluetoothSystemSwitchStateInfos(TAG, information.getIntent()); 223 | } 224 | 225 | int currentState = information.getCurrentState(); 226 | 227 | switch (currentState) { 228 | case BluetoothAdapter.STATE_TURNING_ON: 229 | mAdapterStateListener.onOpening(information); 230 | break; 231 | case BluetoothAdapter.STATE_ON: 232 | mAdapterStateListener.onOpened(information); 233 | break; 234 | case BluetoothAdapter.STATE_TURNING_OFF: 235 | mAdapterStateListener.onCloseing(information); 236 | break; 237 | case BluetoothAdapter.STATE_OFF: 238 | mAdapterStateListener.onClosed(information); 239 | break; 240 | default: 241 | break; 242 | } 243 | 244 | return true; 245 | } 246 | 247 | @Override 248 | public String[] actions() { 249 | return new String[]{BluetoothAdapter.ACTION_STATE_CHANGED}; 250 | } 251 | 252 | } 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | } 261 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/base/BluetoothAdapterStateListener.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.base; 2 | 3 | /** 4 | * @author deng.wei 5 | */ 6 | public abstract class BluetoothAdapterStateListener{ 7 | 8 | public static final BluetoothAdapterStateListener EMPTY = new BluetoothAdapterStateListener() {}; 9 | 10 | /** 11 | * 正在开启 12 | */ 13 | public void onOpening(StateInformation stateInformation){} 14 | /** 15 | * 已开启 16 | */ 17 | public void onOpened(StateInformation stateInformation){} 18 | /** 19 | * 关闭中 20 | */ 21 | public void onCloseing(StateInformation stateInformation){} 22 | /** 23 | * 已关闭 24 | */ 25 | public void onClosed(StateInformation stateInformation){} 26 | 27 | } 28 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/base/BluetoothAndroidThread.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.base; 2 | 3 | 4 | import android.os.Handler; 5 | import android.os.HandlerThread; 6 | 7 | /** 8 | * @author deng.wei 9 | */ 10 | public final class BluetoothAndroidThread extends HandlerThread implements Executor{ 11 | private static BluetoothAndroidThread sInstance; 12 | private static Handler sHandler; 13 | 14 | private BluetoothAndroidThread() { 15 | super("bt.runtime.base", android.os.Process.THREAD_PRIORITY_DEFAULT); 16 | } 17 | 18 | private static void ensureThreadLocked() { 19 | if (sInstance == null) { 20 | sInstance = new BluetoothAndroidThread(); 21 | sInstance.start(); 22 | sHandler = new Handler(sInstance.getLooper()); 23 | } 24 | } 25 | 26 | public static BluetoothAndroidThread get() { 27 | synchronized (BluetoothAndroidThread.class) { 28 | ensureThreadLocked(); 29 | return sInstance; 30 | } 31 | } 32 | 33 | static Handler getHandler() { 34 | synchronized (BluetoothAndroidThread.class) { 35 | ensureThreadLocked(); 36 | return sHandler; 37 | } 38 | } 39 | 40 | public boolean execute(Runnable runnable) { 41 | if(runnable!=null){ 42 | synchronized (BluetoothAndroidThread.class) { 43 | getHandler().post(runnable); 44 | } 45 | return true; 46 | } 47 | return false; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/base/BluetoothException.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.base; 2 | /** 3 | * 4 | * @author wei.deng 5 | * 6 | */ 7 | public class BluetoothException extends Exception{ 8 | 9 | /** 10 | * 11 | */ 12 | private static final long serialVersionUID = 1L; 13 | 14 | public BluetoothException(String message, Throwable cause) { 15 | super(message, cause); 16 | } 17 | 18 | public BluetoothException(String message) { 19 | super(message); 20 | } 21 | 22 | public BluetoothException(Throwable cause) { 23 | super(cause); 24 | } 25 | 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/base/BluetoothProfileService.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.base; 2 | 3 | import java.util.List; 4 | 5 | import android.bluetooth.BluetoothDevice; 6 | import android.bluetooth.BluetoothProfile; 7 | /** 8 | * @author wei.deng 9 | */ 10 | public interface BluetoothProfileService extends BluetoothServiceLifecycle{ 11 | 12 | public interface BluetoothProfileServiceStateListener { 13 | public static final BluetoothProfileServiceStateListener EMPTY = new BluetoothProfileServiceStateListener(){ 14 | @Override 15 | public void onServiceReady(int profile,BluetoothProfileService service) { 16 | } 17 | }; 18 | public void onServiceReady(int profile, BluetoothProfileService service); 19 | } 20 | 21 | public interface BluetoothProfileConnectionStateChangedListener { 22 | 23 | public static final BluetoothProfileConnectionStateChangedListener EMPTY = new BluetoothProfileConnectionStateChangedListener() { 24 | @Override 25 | public void onDisconnected(int profile, int newState, int preState, 26 | BluetoothDevice device) { 27 | } 28 | @Override 29 | public void onConnected(int profile, int newState, int preState, 30 | BluetoothDevice device) { 31 | } 32 | }; 33 | 34 | public void onConnected(int profile, int newState, int preState, BluetoothDevice device); 35 | public void onDisconnected(int profile, int newState, int preState, BluetoothDevice device); 36 | } 37 | 38 | public void registerProfileConnectionStateChangedListener(BluetoothProfileConnectionStateChangedListener lis); 39 | public void unregisterProfileConnectionStateChangedListener(BluetoothProfileConnectionStateChangedListener lis); 40 | 41 | public void registerBluetoothProfileServiceListener(BluetoothProfileServiceStateListener lis); 42 | public void unregisterBluetoothProfileServiceListener(BluetoothProfileServiceStateListener lis); 43 | 44 | /** 45 | * DISCONNECTED��ordinal��{@link BluetoothProfile#STATE_DISCONNECTED}��ֵ�Ƕ�Ӧ��,����˳���ܱ�,�������������� 46 | */ 47 | public enum ProfileConnectionState { 48 | DISCONNECTED, 49 | CONNECTING, 50 | CONNECTED, 51 | DISCONNECTING, 52 | CONNECTED_NO_PHONE, 53 | CONNECTED_NO_MEDIA, 54 | CONNECTED_NO_PHONE_AND_MEIDA; 55 | 56 | public static ProfileConnectionState toState(int state) { 57 | 58 | ProfileConnectionState[] states = ProfileConnectionState.values(); 59 | 60 | for(ProfileConnectionState item : states){ 61 | if(item.ordinal() == state){ 62 | return item; 63 | } 64 | } 65 | 66 | return ProfileConnectionState.DISCONNECTED; 67 | } 68 | 69 | public static boolean isConnected(ProfileConnectionState state) { 70 | 71 | switch (state) { 72 | case CONNECTED: 73 | case CONNECTED_NO_PHONE: 74 | case CONNECTED_NO_MEDIA: 75 | case CONNECTED_NO_PHONE_AND_MEIDA: 76 | return true; 77 | } 78 | 79 | return false; 80 | } 81 | } 82 | 83 | 84 | public ProfileConnectionState getConnectionState(final BluetoothDevice device); 85 | 86 | public ProfileConnectionState getCurrentConnectionState(); 87 | 88 | public List getConnectedBluetoothDeviceList(); 89 | 90 | 91 | public List getConnectedBluetoothDeviceList(final String deviceName); 92 | /** 93 | * ��android4.2�Ļ��������Ͽ��ɹ�֮�󣬻Ὣ���ȼ�����ΪON����ô�����������������Զ����� 94 | * ����5.0�Ļ������� 95 | * 96 | */ 97 | public boolean disconnect(final BluetoothDevice device) ; 98 | /** 99 | * android�汾���� 4.4֮��,����a2dpʧ�ܣ��׳�Ȩ���쳣��android.permission.WRITE_SECURE_SETTINGS(ϵͳ��) 100 | * �Ͽ�û���� 101 | */ 102 | public boolean connect(final BluetoothDevice device) ; 103 | 104 | public int getPriority(final BluetoothDevice device); 105 | /** 106 | * android�汾���� 4.4֮��,setPriorityʧ�ܣ��׳�Ȩ���쳣��android.permission.WRITE_SECURE_SETTINGS(ϵͳ��) 107 | * @param priority 108 | * @param device 109 | * @return 110 | */ 111 | public boolean setPriority(final int priority, final BluetoothDevice device); 112 | 113 | public static final BluetoothProfileService EMPTY = new BluetoothProfileService() { 114 | 115 | @Override 116 | public void registerProfileConnectionStateChangedListener( 117 | BluetoothProfileConnectionStateChangedListener lis) { 118 | // TODO Auto-generated method stub 119 | 120 | } 121 | 122 | @Override 123 | public void unregisterProfileConnectionStateChangedListener( 124 | BluetoothProfileConnectionStateChangedListener lis) { 125 | // TODO Auto-generated method stub 126 | 127 | } 128 | 129 | @Override 130 | public ProfileConnectionState getConnectionState(BluetoothDevice device) { 131 | // TODO Auto-generated method stub 132 | return null; 133 | } 134 | 135 | @Override 136 | public ProfileConnectionState getCurrentConnectionState() { 137 | // TODO Auto-generated method stub 138 | return null; 139 | } 140 | 141 | @Override 142 | public List getConnectedBluetoothDeviceList() { 143 | // TODO Auto-generated method stub 144 | return null; 145 | } 146 | 147 | @Override 148 | public List getConnectedBluetoothDeviceList( 149 | String deviceName) { 150 | // TODO Auto-generated method stub 151 | return null; 152 | } 153 | 154 | @Override 155 | public boolean disconnect(BluetoothDevice device) { 156 | // TODO Auto-generated method stub 157 | return false; 158 | } 159 | 160 | @Override 161 | public boolean connect(BluetoothDevice device) { 162 | // TODO Auto-generated method stub 163 | return false; 164 | } 165 | 166 | @Override 167 | public int getPriority(BluetoothDevice device) { 168 | // TODO Auto-generated method stub 169 | return 0; 170 | } 171 | 172 | @Override 173 | public boolean setPriority(int priority, BluetoothDevice device) { 174 | // TODO Auto-generated method stub 175 | return false; 176 | } 177 | 178 | @Override 179 | public void registerBluetoothProfileServiceListener( 180 | BluetoothProfileServiceStateListener lis) { 181 | } 182 | 183 | @Override 184 | public void unregisterBluetoothProfileServiceListener( 185 | BluetoothProfileServiceStateListener lis) { 186 | } 187 | 188 | @Override 189 | public boolean init() throws BluetoothException { 190 | return false; 191 | } 192 | 193 | @Override 194 | public boolean destory() { 195 | return false; 196 | }}; 197 | 198 | } 199 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/base/BluetoothRuntimeException.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.base; 2 | /** 3 | * @author deng.wei 4 | */ 5 | public class BluetoothRuntimeException extends RuntimeException{ 6 | 7 | /** 8 | * 9 | */ 10 | private static final long serialVersionUID = 1L; 11 | 12 | public BluetoothRuntimeException(String detailMessage, Throwable throwable) { 13 | super(detailMessage, throwable); 14 | // TODO Auto-generated constructor stub 15 | } 16 | 17 | public BluetoothRuntimeException(String detailMessage) { 18 | super(detailMessage); 19 | // TODO Auto-generated constructor stub 20 | } 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/base/BluetoothService.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.base; 2 | /** 3 | * @author deng.wei 4 | */ 5 | //adapter 6 | public abstract class BluetoothService implements BluetoothServiceLifecycle{ 7 | 8 | public boolean init() throws BluetoothException{ 9 | 10 | return false; 11 | } 12 | 13 | public boolean destory(){ 14 | return false; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/base/BluetoothServiceLifecycle.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.base; 2 | /** 3 | * @author wei.deng 4 | */ 5 | public interface BluetoothServiceLifecycle { 6 | 7 | public boolean init() throws BluetoothException; 8 | 9 | public boolean destory(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/base/Executor.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.base; 2 | /** 3 | * @author deng.wei 4 | */ 5 | public interface Executor { 6 | public boolean execute(Runnable task); 7 | } 8 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/base/StateInformation.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.base; 2 | 3 | import android.bluetooth.BluetoothAdapter; 4 | import android.bluetooth.BluetoothDevice; 5 | import android.content.Intent; 6 | 7 | import com.devyok.bluetooth.utils.BluetoothUtils; 8 | /** 9 | * @author wei.deng 10 | */ 11 | public final class StateInformation { 12 | 13 | static final StateInformation EMPTY = new StateInformation(); 14 | 15 | private int currentState = BluetoothUtils.UNKNOW; 16 | private int previousState = BluetoothUtils.UNKNOW; 17 | private BluetoothDevice device; 18 | private String broadcastAction = BluetoothUtils.EMPTY_STRING; 19 | private Intent intent; 20 | 21 | private StateInformation(){ 22 | } 23 | 24 | public boolean isUnknow(){ 25 | return (currentState == BluetoothUtils.UNKNOW || previousState == BluetoothUtils.UNKNOW); 26 | } 27 | 28 | public boolean isEmpty(){ 29 | return (this == EMPTY) || (isUnknow()); 30 | } 31 | 32 | public static StateInformation toInformation(Intent intent){ 33 | 34 | if(intent == null) return StateInformation.EMPTY; 35 | 36 | int currentState = BluetoothUtils.UNKNOW; 37 | if(intent.hasExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE)){ 38 | currentState = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, BluetoothUtils.UNKNOW); 39 | } 40 | 41 | int previousState = BluetoothUtils.UNKNOW; 42 | 43 | if(intent.hasExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE)) { 44 | previousState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE, BluetoothUtils.UNKNOW); 45 | } 46 | 47 | BluetoothDevice device = null; 48 | if(intent.hasExtra(BluetoothDevice.EXTRA_DEVICE)) { 49 | device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 50 | } 51 | 52 | String broadcastAction = intent.getAction(); 53 | return obtain(currentState, previousState, device, broadcastAction,intent); 54 | } 55 | 56 | public static StateInformation obtain(int currentState,int previousState,BluetoothDevice device,String broadcastAction,Intent intent){ 57 | 58 | StateInformation information = new StateInformation(); 59 | 60 | information.currentState = currentState; 61 | information.previousState = previousState; 62 | information.device = device; 63 | information.broadcastAction = broadcastAction; 64 | information.intent = intent; 65 | 66 | return information; 67 | } 68 | 69 | public int getCurrentState() { 70 | return currentState; 71 | } 72 | 73 | public BluetoothDevice getDevice() { 74 | return device; 75 | } 76 | 77 | public String getBroadcastAction() { 78 | return broadcastAction; 79 | } 80 | 81 | public int getPreviousState() { 82 | return previousState; 83 | } 84 | 85 | public Intent getIntent() { 86 | return intent; 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/base/TaskQueue.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.base; 2 | 3 | import java.util.Iterator; 4 | import java.util.Map; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | 7 | import android.os.Handler; 8 | import android.os.Handler.Callback; 9 | import android.os.HandlerThread; 10 | /** 11 | * @author wei.deng 12 | */ 13 | public final class TaskQueue extends HandlerThread{ 14 | private Handler mHandler; 15 | 16 | final ConcurrentHashMap taskMapping = new ConcurrentHashMap(); 17 | 18 | public TaskQueue(String name) { 19 | this(name,null); 20 | } 21 | 22 | public TaskQueue(String name,Callback callback) { 23 | super(name, android.os.Process.THREAD_PRIORITY_DEFAULT); 24 | ensureThreadLocked(callback); 25 | } 26 | 27 | void ensureThreadLocked(Callback callback) { 28 | start(); 29 | mHandler = new Handler(getLooper(),callback); 30 | } 31 | 32 | void remove(TaskWrapper taskWrapper){ 33 | getHandler().removeCallbacks(taskWrapper); 34 | if(taskWrapper.taskImpl!=null){ 35 | taskMapping.remove(taskWrapper.taskImpl); 36 | taskWrapper.taskImpl = null; 37 | } 38 | } 39 | 40 | private Handler getHandler() { 41 | return mHandler; 42 | } 43 | 44 | public boolean contains(Runnable task){ 45 | return taskMapping.contains(task); 46 | } 47 | 48 | public void removeTasks(Runnable ...tasks){ 49 | 50 | for (int i = 0; i < tasks.length; i++) { 51 | Runnable task = tasks[i]; 52 | if(task == null) continue; 53 | TaskWrapper taskWrapper = taskMapping.remove(task); 54 | if(taskWrapper!=null){ 55 | taskWrapper.taskImpl = null; 56 | getHandler().removeCallbacks(taskWrapper); 57 | } 58 | } 59 | 60 | } 61 | 62 | public void removeAllTasks(){ 63 | 64 | for(Iterator> iter = taskMapping.entrySet().iterator();iter.hasNext();){ 65 | 66 | Map.Entry item = iter.next(); 67 | 68 | TaskWrapper taskWrapper = item.getValue(); 69 | taskWrapper.taskImpl = null; 70 | getHandler().removeCallbacks(taskWrapper); 71 | 72 | } 73 | 74 | taskMapping.clear(); 75 | 76 | } 77 | 78 | public void submitTask(Runnable task){ 79 | TaskWrapper taskWrapper = TaskWrapper.create(task, this); 80 | taskMapping.put(task, taskWrapper); 81 | getHandler().post(taskWrapper); 82 | } 83 | 84 | public void submitTask(Runnable task,long delayMillis){ 85 | TaskWrapper taskWrapper = TaskWrapper.create(task, this); 86 | taskMapping.put(task, taskWrapper); 87 | getHandler().postDelayed(taskWrapper, delayMillis); 88 | } 89 | 90 | final static class TaskWrapper implements Runnable { 91 | 92 | Runnable taskImpl; 93 | TaskQueue taskQueue; 94 | 95 | private TaskWrapper(Runnable runnable,TaskQueue queue){ 96 | taskImpl = runnable; 97 | this.taskQueue = queue; 98 | } 99 | 100 | public static TaskWrapper create(Runnable runnable,TaskQueue queue){ 101 | TaskWrapper taskWrapper = new TaskWrapper(runnable,queue); 102 | return taskWrapper; 103 | } 104 | 105 | @Override 106 | public void run() { 107 | 108 | try { 109 | if(taskImpl!=null){ 110 | taskImpl.run(); 111 | } 112 | } finally { 113 | taskQueue.remove(this); 114 | } 115 | 116 | } 117 | } 118 | 119 | public int getTaskCount() { 120 | return taskMapping.size(); 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/connection/AbstractBluetoothConnection.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.connection; 2 | 3 | import java.io.Closeable; 4 | import java.util.UUID; 5 | 6 | import android.bluetooth.BluetoothDevice; 7 | import android.util.Log; 8 | 9 | import com.devyok.bluetooth.base.BluetoothException; 10 | import com.devyok.bluetooth.connection.BluetoothConnection.State; 11 | import com.devyok.bluetooth.utils.BluetoothUtils; 12 | /** 13 | * @author wei.deng 14 | */ 15 | public abstract class AbstractBluetoothConnection implements Connection { 16 | 17 | static final String TAG = AbstractBluetoothConnection.class.getSimpleName(); 18 | 19 | protected BluetoothDevice connectedDevice; 20 | protected UUID sppuuid; 21 | protected volatile boolean isCancel; 22 | protected volatile State currentState = State.INIT; 23 | protected TransactCore transactCore; 24 | protected long connectionTimeout = 6*1000; 25 | 26 | public AbstractBluetoothConnection(UUID sppuuid,BluetoothDevice connectedDevice){ 27 | this.sppuuid = sppuuid; 28 | this.connectedDevice = connectedDevice; 29 | } 30 | 31 | @Override 32 | public void connect() throws BluetoothConnectionException, 33 | BluetoothConnectionTimeoutException, BluetoothException { 34 | connect(this.sppuuid, this.connectedDevice); 35 | } 36 | 37 | @Override 38 | public UUID getUuid() { 39 | return sppuuid; 40 | } 41 | 42 | @Override 43 | public BluetoothDevice getBluetoothDevice() { 44 | return connectedDevice; 45 | } 46 | 47 | @Override 48 | public long getTimeout() { 49 | return connectionTimeout; 50 | } 51 | 52 | public void setTimeout(long timeout) { 53 | this.connectionTimeout = timeout; 54 | } 55 | 56 | @Override 57 | public void reset() { 58 | isCancel = false; 59 | closeTransactCore(); 60 | } 61 | 62 | @Override 63 | public void disconnect() { 64 | closeTransactCore(); 65 | } 66 | 67 | @Override 68 | public State getState() { 69 | return this.currentState; 70 | } 71 | 72 | @Override 73 | public TransactCore getCore() { 74 | return this.transactCore; 75 | } 76 | 77 | @Override 78 | public void cancel() { 79 | Log.i(TAG, "[devybt sppconnection] cancel{"+this.getClass().getSimpleName()+"} enter , transactCore = " + transactCore); 80 | isCancel = true; 81 | closeTransactCore(); 82 | } 83 | 84 | protected boolean isCancel(){ 85 | return isCancel; 86 | } 87 | 88 | protected void closeTransactCore(){ 89 | try { 90 | if(this.transactCore instanceof Closeable){ 91 | Closeable closeable = (Closeable) this.transactCore; 92 | BluetoothUtils.close(closeable); 93 | } 94 | } finally { 95 | this.transactCore = null; 96 | currentState = State.CLOSED; 97 | } 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/connection/BluetoothConnection.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.connection; 2 | 3 | import android.bluetooth.BluetoothDevice; 4 | import android.os.ParcelUuid; 5 | 6 | import com.devyok.bluetooth.a2dp.A2dpProfileService; 7 | import com.devyok.bluetooth.base.BluetoothException; 8 | import com.devyok.bluetooth.hfp.HFPConnection; 9 | import com.devyok.bluetooth.hfp.HeadsetProfileService; 10 | import com.devyok.bluetooth.utils.BluetoothUtils; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.UUID; 15 | /** 16 | * @author wei.deng 17 | */ 18 | public abstract class BluetoothConnection{ 19 | 20 | public static final UUID DEFAULT_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); 21 | 22 | /** 23 | * 连接状态 24 | * @author wei.deng 25 | */ 26 | public enum State { 27 | UNKNOW, 28 | INIT, 29 | CONNECTED, 30 | LISTENING, 31 | CLOSED, 32 | } 33 | 34 | /** 35 | * 连接监听器 36 | * @author wei.deng 37 | */ 38 | public static abstract class BluetoothConnectionListener { 39 | public void onConnected(Connection connection) { 40 | } 41 | 42 | public void onDisconnected(Connection connection) { 43 | } 44 | } 45 | /** 46 | * 支持的协议 47 | * @author wei.deng 48 | * 49 | */ 50 | public static class Protocol { 51 | private String name; 52 | 53 | public static final Protocol A2DP = new Protocol("a2dp"); 54 | 55 | public static final Protocol HFP = new Protocol("hfp"); 56 | 57 | public static final Protocol SPP = new Protocol("spp"); 58 | 59 | public static final Protocol[] ALL = new Protocol[]{A2DP,HFP,SPP}; 60 | 61 | public Protocol(String name){ 62 | this.name = name; 63 | } 64 | 65 | public String getName() { 66 | return name; 67 | } 68 | 69 | public static Protocol getProtocol(ParcelUuid uuid){ 70 | throw new RuntimeException("stub"); 71 | } 72 | 73 | public static Protocol getProtocol(int profile){ 74 | if(profile == HeadsetProfileService.PROFILE) { 75 | return Protocol.HFP; 76 | } else if(profile == A2dpProfileService.PROFILE) { 77 | return Protocol.A2DP; 78 | } 79 | return Protocol.SPP; 80 | } 81 | 82 | @Override 83 | public int hashCode() { 84 | return name.hashCode(); 85 | } 86 | 87 | @Override 88 | public boolean equals(Object obj) { 89 | Protocol p1 = (Protocol) obj; 90 | return p1.getName().equals(this.name); 91 | } 92 | 93 | } 94 | 95 | /** 96 | * sppconnection builder 97 | * @author wei.deng 98 | */ 99 | public static class Builder { 100 | private BluetoothDevice connectedDevice; 101 | private UUID serviceUUID; 102 | private RetryPolicy connectionRetryPolicy; 103 | private BluetoothConnectionListener connectListener; 104 | private long connectionTimeout; 105 | private int companyid = BluetoothUtils.UNKNOW; 106 | final List connectPolicyList = new ArrayList(); 107 | 108 | public Builder(){ 109 | } 110 | 111 | public Builder setConnectedDevice(BluetoothDevice connectedDevice) { 112 | this.connectedDevice = connectedDevice; 113 | return this; 114 | } 115 | 116 | public Builder setCompanyid(int companyid) { 117 | this.companyid = companyid; 118 | return this; 119 | } 120 | 121 | public Builder setConnectionUUID(UUID sppUUID) { 122 | this.serviceUUID = sppUUID; 123 | return this; 124 | } 125 | 126 | public Builder setConnectionRetryPolicy(RetryPolicy connectionRetryPolicy) { 127 | this.connectionRetryPolicy = connectionRetryPolicy; 128 | return this; 129 | } 130 | 131 | public Builder addDefaultConnectionRetryPolicy(){ 132 | this.connectionRetryPolicy = new DefaultRetryPolicy(); 133 | return this; 134 | } 135 | 136 | public Builder setConnectionListener(BluetoothConnectionListener lis) { 137 | this.connectListener = lis; 138 | return this; 139 | } 140 | 141 | public Builder addConnectionPolicy(Connection connectPolicy) { 142 | if(!this.connectPolicyList.contains(connectPolicy)) { 143 | this.connectPolicyList.add(connectPolicy); 144 | } 145 | return this; 146 | } 147 | 148 | public Builder setConnectionTimeout(long timeout){ 149 | this.connectionTimeout = timeout; 150 | return this; 151 | } 152 | 153 | public Connection build() throws BluetoothException{ 154 | if(connectPolicyList.size()>0){ 155 | for (Connection connection : connectPolicyList) { 156 | if(connection instanceof AbstractBluetoothConnection) { 157 | AbstractBluetoothConnection abstractBluetoothConnection = (AbstractBluetoothConnection) connection; 158 | abstractBluetoothConnection.connectedDevice = this.connectedDevice; 159 | abstractBluetoothConnection.sppuuid = this.serviceUUID; 160 | abstractBluetoothConnection.setTimeout(connectionTimeout); 161 | 162 | if(abstractBluetoothConnection instanceof HFPConnection){ 163 | HFPConnection hfpConnection = (HFPConnection) abstractBluetoothConnection; 164 | hfpConnection.setCompanyid(this.companyid); 165 | } 166 | 167 | } 168 | } 169 | } 170 | return BluetoothConnectionImpl.open(this.serviceUUID, this.connectedDevice, this.connectionRetryPolicy, this.connectListener,this.connectPolicyList.toArray(new Connection[]{})); 171 | } 172 | 173 | } 174 | 175 | } 176 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/connection/BluetoothConnectionException.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.connection; 2 | 3 | import com.devyok.bluetooth.base.BluetoothException; 4 | 5 | /** 6 | * @author wei.deng 7 | */ 8 | public class BluetoothConnectionException extends BluetoothException { 9 | 10 | public BluetoothConnectionException(String message) { 11 | super(message); 12 | } 13 | 14 | public BluetoothConnectionException(String message, Throwable cause) { 15 | super(message, cause); 16 | } 17 | 18 | public BluetoothConnectionException(Throwable cause) { 19 | super(cause); 20 | } 21 | 22 | private static final long serialVersionUID = 1L; 23 | 24 | } -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/connection/BluetoothConnectionImpl.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.connection; 2 | 3 | import android.bluetooth.BluetoothDevice; 4 | import android.util.Log; 5 | 6 | import com.devyok.bluetooth.base.BluetoothException; 7 | import com.devyok.bluetooth.connection.BluetoothConnection.BluetoothConnectionListener; 8 | import com.devyok.bluetooth.connection.BluetoothConnection.State; 9 | import com.devyok.bluetooth.utils.BluetoothUtils; 10 | 11 | import java.io.Closeable; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.UUID; 15 | import java.util.concurrent.Callable; 16 | import java.util.concurrent.ExecutionException; 17 | import java.util.concurrent.ExecutorService; 18 | import java.util.concurrent.Executors; 19 | import java.util.concurrent.FutureTask; 20 | import java.util.concurrent.TimeUnit; 21 | import java.util.concurrent.TimeoutException; 22 | /** 23 | * 负责与蓝牙设备建立连接 24 | * @author wei.deng 25 | */ 26 | @SuppressWarnings("rawtypes") 27 | final class BluetoothConnectionImpl extends AbstractBluetoothConnection { 28 | 29 | private static final String TAG = BluetoothConnectionImpl.class.getSimpleName(); 30 | 31 | final RetryPolicy retryPolicy; 32 | final BluetoothConnectionListener connectionListener; 33 | final List sppConnectPolicyList; 34 | Connection currentConnectPolicy = null; 35 | static final ExecutorService btConnectionExecutorService = Executors.newCachedThreadPool(BluetoothUtils.createThreadFactory("bt.runtime.connection")); 36 | 37 | private BluetoothConnectionImpl(UUID sppuuid,BluetoothDevice connectedDevice){ 38 | this(sppuuid,connectedDevice,null,null,null); 39 | } 40 | 41 | private BluetoothConnectionImpl(UUID sppid,BluetoothDevice connectedDevice,Connection[] connectPolicies,RetryPolicy retryPolicy,BluetoothConnectionListener connectionListener){ 42 | super(sppid,connectedDevice); 43 | sppConnectPolicyList = new ArrayList(); 44 | if(connectPolicies!=null){ 45 | for(int i = 0;i < connectPolicies.length;i++){ 46 | sppConnectPolicyList.add(connectPolicies[i]); 47 | } 48 | } 49 | this.retryPolicy = retryPolicy; 50 | this.connectionListener = connectionListener; 51 | } 52 | 53 | static BluetoothConnectionImpl open(BluetoothDevice connectedDevice) throws BluetoothException { 54 | return open(BluetoothConnection.DEFAULT_UUID,connectedDevice,new DefaultRetryPolicy(),null,null); 55 | } 56 | 57 | static BluetoothConnectionImpl open(UUID sppuuid,BluetoothDevice connectedDevice) throws BluetoothException { 58 | return open(sppuuid,connectedDevice,new DefaultRetryPolicy(),null,null); 59 | } 60 | 61 | static BluetoothConnectionImpl open(UUID sppuuid,BluetoothDevice connectedDevice,RetryPolicy policy) throws BluetoothException { 62 | return open(sppuuid,connectedDevice,policy,null,null); 63 | } 64 | 65 | static BluetoothConnectionImpl open(UUID sppuuid,BluetoothDevice connectedDevice,RetryPolicy policy,BluetoothConnectionListener lis) throws BluetoothException { 66 | return open(sppuuid,connectedDevice,policy,lis,null); 67 | } 68 | 69 | /** 70 | * 开启一个SPP连接 71 | * @param sppuuid 连接蓝牙设备时需要的ID 72 | * @param connectedDevice 已连接的蓝牙设备 73 | * @param policy 当SPP连接失败后,尝试重连的策略 74 | * @return 75 | * @throws BluetoothException 76 | */ 77 | static BluetoothConnectionImpl open(UUID sppuuid,BluetoothDevice connectedDevice,RetryPolicy policy,BluetoothConnectionListener sppConnectListener,Connection[] sppConnectPolicyArray) throws BluetoothException { 78 | 79 | if(sppuuid == null){ 80 | throw new BluetoothException("sppuuid == null"); 81 | } 82 | 83 | Log.i(TAG, "[devybt btconnection] open sppuuid = " + sppuuid); 84 | 85 | if(connectedDevice == null){ 86 | throw new BluetoothException("connectedDevice == null"); 87 | } 88 | 89 | BluetoothConnectionImpl sppConnection = new BluetoothConnectionImpl(sppuuid,connectedDevice,sppConnectPolicyArray,policy,sppConnectListener); 90 | return sppConnection; 91 | } 92 | 93 | 94 | /** 95 | * 连接蓝牙设备 96 | * @throws BluetoothConnectionException 97 | * @throws BluetoothException 98 | */ 99 | public void connect() throws BluetoothConnectionException , BluetoothConnectionTimeoutException , BluetoothException{ 100 | 101 | try { 102 | Connection connection = currentConnectPolicy; 103 | if(connection!=null){ 104 | connection.connect(); 105 | } else { 106 | connection = tryConnect(); 107 | } 108 | 109 | if(connection!=null && connection.isConnected()){ 110 | this.currentConnectPolicy = connection; 111 | try { 112 | resetRetryPolicy(); 113 | } finally { 114 | connectedNotifier(); 115 | } 116 | } 117 | } catch (BluetoothConnectionException e) { 118 | if(!retry(e)){ 119 | throw e; 120 | } 121 | } catch(TimeoutException e){ 122 | if(!retry(e)){ 123 | throw new BluetoothConnectionTimeoutException("spp connect timeout("+getTotalTimeout()+")" , e); 124 | } 125 | } 126 | catch(Exception e){ 127 | throw new BluetoothException(e); 128 | } 129 | } 130 | 131 | boolean retry(Exception e) throws BluetoothConnectionException, BluetoothException{ 132 | if(retryPolicy!=null){ 133 | 134 | retryPolicy.retry(sppuuid, connectedDevice, e); 135 | return true; 136 | } 137 | return false; 138 | } 139 | 140 | void resetRetryPolicy(){ 141 | if(retryPolicy!=null){ 142 | retryPolicy.reset(); 143 | } 144 | } 145 | 146 | void connectedNotifier(){ 147 | if(connectionListener!=null){ 148 | connectionListener.onConnected(this); 149 | } 150 | } 151 | 152 | void disconnetedNotifier() { 153 | if(connectionListener!=null){ 154 | connectionListener.onDisconnected(this); 155 | } 156 | } 157 | 158 | @Override 159 | public long getTimeout() { 160 | return getTotalTimeout(); 161 | } 162 | 163 | @Override 164 | public void cancel() { 165 | super.cancel(); 166 | if(currentConnectPolicy!=null){ 167 | currentConnectPolicy.cancel(); 168 | } 169 | } 170 | 171 | @Override 172 | public void connect(UUID sppuuid, BluetoothDevice connectedDevice) throws BluetoothConnectionException, BluetoothConnectionTimeoutException, BluetoothException { 173 | this.sppuuid = sppuuid; 174 | this.connectedDevice = connectedDevice; 175 | connect(); 176 | } 177 | 178 | /** 179 | * 将连接成功的调整到最前面,避免在重连时可以快速连接 180 | * @param bestPolicyIndex 181 | */ 182 | void adjustBestConnectPolicy(int bestPolicyIndex){ 183 | Log.i(TAG, "[devybt btconnection] adjustBestConnectPolicy bestPolicyIndex = " + bestPolicyIndex); 184 | if(bestPolicyIndex == 0) return ; 185 | Connection bestPolicy = sppConnectPolicyList.remove(bestPolicyIndex); 186 | sppConnectPolicyList.add(0, bestPolicy); 187 | } 188 | 189 | /** 190 | * 尝试获取所有的连接策略,进行循环连接. 191 | * @return 192 | * @throws BluetoothConnectionException 193 | * @throws BluetoothConnectionTimeoutException 194 | * @throws TimeoutException 195 | */ 196 | Connection tryConnect() throws BluetoothConnectionException, TimeoutException{ 197 | 198 | long totalTimeout = getTotalTimeout(); 199 | 200 | Log.i(TAG, "[devybt btconnection] start try connect policy , totalTimeout = " + totalTimeout); 201 | FutureTask> futureTask = 202 | new FutureTask>(new Callable>() { 203 | 204 | @Override 205 | public CallResult call() { 206 | 207 | int size = sppConnectPolicyList.size(); 208 | Log.i(TAG, "[devybt btconnection] try connect policy size = " + size); 209 | 210 | BluetoothConnectionException excp = null; 211 | for (int i = 0; i < size; i++) { 212 | final Connection policy = sppConnectPolicyList.get(i); 213 | Log.i(TAG, "[devybt btconnection] try connect policy = " + policy); 214 | policy.reset(); 215 | currentConnectPolicy = policy; 216 | try { 217 | policy.connect(sppuuid, connectedDevice); 218 | if(policy.isConnected()) { 219 | adjustBestConnectPolicy(i); 220 | return CallResult.obtain(policy,CallResult.SUCCESS); 221 | } 222 | } catch (BluetoothConnectionException e) { 223 | excp = e; 224 | } catch (BluetoothConnectionTimeoutException e) { 225 | e.printStackTrace(); 226 | } catch (BluetoothException e) { 227 | e.printStackTrace(); 228 | } 229 | } 230 | 231 | return CallResult.obtain(null, CallResult.EXCEPTION,excp); 232 | } 233 | }); 234 | 235 | btConnectionExecutorService.execute(futureTask); 236 | 237 | CallResult callResult = null; 238 | try { 239 | callResult = futureTask.get(totalTimeout,TimeUnit.MILLISECONDS); 240 | 241 | if(callResult.isSuccess()){ 242 | 243 | Connection connect = callResult.result; 244 | 245 | Log.i(TAG, "[devybt btconnection] try connect futureTask result = " + connect); 246 | 247 | if(connect!=null && connect.isConnected()) { 248 | return connect; 249 | } 250 | } else { 251 | if(callResult.exception!=null && callResult.exception instanceof BluetoothConnectionException){ 252 | throw (BluetoothConnectionException)callResult.exception; 253 | } else { 254 | throw new BluetoothConnectionException(callResult.exception); 255 | } 256 | } 257 | } catch (InterruptedException e) { 258 | e.printStackTrace(); 259 | } catch (ExecutionException e) { 260 | e.printStackTrace(); 261 | } catch (TimeoutException e) { 262 | //当出现超时之后,有一种情况是socket阻塞在connect上长时间不返回,所以需要调用policy#cancel关闭连接 263 | //关闭之后connect会抛出IOException,这样就打断阻塞了 264 | if(currentConnectPolicy!=null){ 265 | currentConnectPolicy.cancel(); 266 | } 267 | throw e; 268 | } finally { 269 | currentConnectPolicy = null; 270 | } 271 | 272 | return null; 273 | } 274 | 275 | private long getTotalTimeout() { 276 | 277 | int size = sppConnectPolicyList.size(); 278 | long total = 0L; 279 | for (int i = 0; i < size; i++) { 280 | final Connection policy = sppConnectPolicyList.get(i); 281 | total += policy.getTimeout(); 282 | } 283 | 284 | return total == 0L ? 6*1000 : total; 285 | } 286 | 287 | @Override 288 | public boolean isConnected() { 289 | return currentConnectPolicy!=null ? currentConnectPolicy.isConnected() : false; 290 | } 291 | 292 | @Override 293 | public State getState() { 294 | return currentConnectPolicy!=null ? currentConnectPolicy.getState() : State.INIT; 295 | } 296 | 297 | @Override 298 | public void disconnect() { 299 | try { 300 | if(currentConnectPolicy!=null){ 301 | currentConnectPolicy.disconnect(); 302 | 303 | disconnetedNotifier(); 304 | 305 | } 306 | } finally { 307 | super.disconnect(); 308 | } 309 | } 310 | 311 | static class CallResult { 312 | 313 | public static final int SUCCESS = 0x00; 314 | public static final int EXCEPTION = 0x01; 315 | 316 | Result result; 317 | int status; 318 | Throwable exception; 319 | 320 | public static CallResult obtain(Result result,int status){ 321 | return obtain(result,status,null); 322 | } 323 | 324 | public static CallResult obtain(Result result,int status,Throwable exception){ 325 | CallResult callResult = new CallResult(); 326 | callResult.result = result; 327 | callResult.status = status; 328 | callResult.exception = exception; 329 | return callResult; 330 | } 331 | 332 | public boolean isSuccess(){ 333 | return (status == SUCCESS); 334 | } 335 | 336 | } 337 | 338 | } 339 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/connection/BluetoothConnectionStateListener.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.connection; 2 | 3 | 4 | import com.devyok.bluetooth.base.StateInformation; 5 | /** 6 | * @author deng.wei 7 | */ 8 | public abstract class BluetoothConnectionStateListener { 9 | 10 | public static final BluetoothConnectionStateListener EMTPY = new BluetoothConnectionStateListener() { 11 | }; 12 | 13 | public void onConnecting(StateInformation stateInformation) { 14 | } 15 | 16 | public void onConnected(StateInformation stateInformation) { 17 | } 18 | 19 | public void onDisconnected(StateInformation stateInformation) { 20 | } 21 | 22 | public void onDisconnecting(StateInformation stateInformation) { 23 | } 24 | 25 | public void onError(StateInformation stateInformation) { 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/connection/BluetoothConnectionTimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.connection; 2 | 3 | import com.devyok.bluetooth.base.BluetoothException; 4 | 5 | 6 | /** 7 | * @author wei.deng 8 | */ 9 | public class BluetoothConnectionTimeoutException extends BluetoothException { 10 | 11 | private static final long serialVersionUID = 1L; 12 | public BluetoothConnectionTimeoutException(String message) { 13 | super(message); 14 | } 15 | 16 | public BluetoothConnectionTimeoutException(String message, Throwable cause) { 17 | super(message, cause); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/connection/BluetoothDeviceConnectionService.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.connection; 2 | 3 | import android.bluetooth.BluetoothAdapter; 4 | import android.util.Log; 5 | 6 | import com.devyok.bluetooth.OkBluetooth; 7 | import com.devyok.bluetooth.base.BaseBluetoothStateChangedListener; 8 | import com.devyok.bluetooth.base.BluetoothException; 9 | import com.devyok.bluetooth.base.BluetoothService; 10 | import com.devyok.bluetooth.base.StateInformation; 11 | import com.devyok.bluetooth.utils.BluetoothUtils; 12 | /** 13 | * @author deng.wei 14 | */ 15 | public class BluetoothDeviceConnectionService extends BluetoothService{ 16 | 17 | private static String TAG = BluetoothDeviceConnectionService.class.getSimpleName(); 18 | 19 | private BluetoothConnectionStateListener mConnectionStateListener = BluetoothConnectionStateListener.EMTPY; 20 | private final BluetoothDeviceConnectionStateListenerImpl stateListenerImpl = new BluetoothDeviceConnectionStateListenerImpl(); 21 | 22 | public BluetoothDeviceConnectionService(){ 23 | } 24 | 25 | public void setConnectionStateListener(BluetoothConnectionStateListener lis){ 26 | this.mConnectionStateListener = lis; 27 | } 28 | 29 | @Override 30 | public boolean init() throws BluetoothException { 31 | // TODO Auto-generated method stub 32 | super.init(); 33 | stateListenerImpl.startListener(); 34 | return true; 35 | 36 | } 37 | 38 | @Override 39 | public boolean destory() { 40 | super.destory(); 41 | stateListenerImpl.stopListener(); 42 | mConnectionStateListener = null; 43 | return true; 44 | } 45 | 46 | class BluetoothDeviceConnectionStateListenerImpl extends BaseBluetoothStateChangedListener { 47 | 48 | public BluetoothDeviceConnectionStateListenerImpl(){ 49 | } 50 | 51 | @Override 52 | public boolean onChanged(StateInformation stateInformation){ 53 | 54 | if(OkBluetooth.isDebugable()){ 55 | 56 | int connectionState = OkBluetooth.getConnectionState(); 57 | String getConnectionStateString = BluetoothUtils.getConnectionStateString(connectionState); 58 | 59 | Log.i(TAG, "[devybt connect] getConnectionStateString = " + getConnectionStateString); 60 | 61 | BluetoothUtils.dumpBluetoothConnectionInfos(TAG, stateInformation.getIntent()); 62 | } 63 | 64 | int currentState = stateInformation.getCurrentState(); 65 | 66 | switch (currentState) { 67 | case BluetoothAdapter.STATE_CONNECTING: 68 | mConnectionStateListener.onConnecting(stateInformation); 69 | break; 70 | case BluetoothAdapter.STATE_CONNECTED: 71 | mConnectionStateListener.onConnected(stateInformation); 72 | break; 73 | case BluetoothAdapter.STATE_DISCONNECTING: 74 | mConnectionStateListener.onDisconnecting(stateInformation); 75 | break; 76 | case BluetoothAdapter.STATE_DISCONNECTED: 77 | mConnectionStateListener.onDisconnected(stateInformation); 78 | break; 79 | 80 | default: 81 | break; 82 | } 83 | 84 | return true; 85 | } 86 | 87 | @Override 88 | public String[] actions(){ 89 | return new String[]{BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED}; 90 | } 91 | 92 | } 93 | 94 | 95 | } 96 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/connection/Connection.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.connection; 2 | 3 | import android.bluetooth.BluetoothDevice; 4 | 5 | import com.devyok.bluetooth.base.BluetoothException; 6 | import com.devyok.bluetooth.connection.BluetoothConnection.State; 7 | 8 | import java.io.Closeable; 9 | import java.io.IOException; 10 | import java.util.UUID; 11 | 12 | /** 13 | * 蓝牙连接建立的策略 14 | * @author wei.deng 15 | */ 16 | public interface Connection { 17 | 18 | public long getTimeout(); 19 | public void connect() throws BluetoothConnectionException , BluetoothConnectionTimeoutException , BluetoothException; 20 | public void connect(UUID sppuuid,BluetoothDevice connectedDevice) throws BluetoothConnectionException , BluetoothConnectionTimeoutException , BluetoothException; 21 | public void disconnect(); 22 | public boolean isConnected(); 23 | public void cancel(); 24 | public void reset(); 25 | public TransactCore getCore(); 26 | public UUID getUuid(); 27 | public BluetoothDevice getBluetoothDevice(); 28 | public State getState(); 29 | 30 | public static Connection EMPTY = new Connection() { 31 | 32 | @Override 33 | public long getTimeout() { 34 | return -1; 35 | } 36 | 37 | @Override 38 | public void connect() throws BluetoothConnectionException, 39 | BluetoothConnectionTimeoutException, BluetoothException { 40 | throw new BluetoothConnectionException("empty"); 41 | } 42 | 43 | @Override 44 | public void connect(UUID sppuuid, BluetoothDevice connectedDevice) 45 | throws BluetoothConnectionException, 46 | BluetoothConnectionTimeoutException, BluetoothException { 47 | throw new BluetoothConnectionException("empty"); 48 | } 49 | 50 | @Override 51 | public void disconnect() { 52 | } 53 | 54 | @Override 55 | public boolean isConnected() { 56 | return false; 57 | } 58 | 59 | @Override 60 | public void cancel() { 61 | } 62 | 63 | @Override 64 | public void reset() { 65 | } 66 | 67 | @Override 68 | public Closeable getCore() { 69 | return new Closeable() { 70 | 71 | @Override 72 | public void close() throws IOException { 73 | } 74 | }; 75 | } 76 | 77 | @Override 78 | public UUID getUuid() { 79 | return UUID.randomUUID(); 80 | } 81 | 82 | @Override 83 | public BluetoothDevice getBluetoothDevice() { 84 | return null; 85 | } 86 | 87 | @Override 88 | public State getState() { 89 | return State.UNKNOW; 90 | } 91 | }; 92 | 93 | } 94 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/connection/DefaultRetryPolicy.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.connection; 2 | 3 | import android.bluetooth.BluetoothDevice; 4 | import android.util.Log; 5 | 6 | import com.devyok.bluetooth.base.BluetoothException; 7 | 8 | import java.util.UUID; 9 | 10 | 11 | /** 12 | * 默认的重连实现 13 | * @author wei.deng 14 | * 15 | */ 16 | public final class DefaultRetryPolicy implements RetryPolicy{ 17 | 18 | static final String TAG = DefaultRetryPolicy.class.getSimpleName(); 19 | 20 | static final int DEFAULT_RETRY_COUNT = 3; 21 | 22 | private int maxRetryCount = DEFAULT_RETRY_COUNT; 23 | 24 | private int currentRetryCount = 0; 25 | 26 | static int[] TIMEOUT = new int[]{100,500,1000}; 27 | 28 | public DefaultRetryPolicy(){ 29 | this(DEFAULT_RETRY_COUNT); 30 | } 31 | 32 | public DefaultRetryPolicy(int maxCount){ 33 | maxRetryCount = maxCount; 34 | } 35 | 36 | @Override 37 | public int getCurrentRetryCount() { 38 | return currentRetryCount; 39 | } 40 | 41 | @Override 42 | public void retry(UUID sppuuid,BluetoothDevice connectedDevice,Exception error) throws BluetoothConnectionException,BluetoothException { 43 | 44 | if(currentRetryCount>=maxRetryCount){ 45 | throw new BluetoothConnectionException("spp retry connect fail",error); 46 | } 47 | 48 | try { 49 | Thread.sleep(TIMEOUT[getCurrentRetryCount()]); 50 | } catch (InterruptedException e1) { 51 | e1.printStackTrace(); 52 | } 53 | 54 | currentRetryCount++; 55 | Log.i(TAG, "[devybt sppconnection] retryconnect count = " + currentRetryCount); 56 | 57 | BluetoothConnectionImpl sppConnection = BluetoothConnectionImpl.open(sppuuid,connectedDevice,null); 58 | try { 59 | sppConnection.connect(); 60 | } catch (BluetoothConnectionException e) { 61 | Log.i(TAG, "[devybt sppconnection] retryconnect BluetoothConnectionException"); 62 | 63 | retry(sppuuid, connectedDevice, error); 64 | 65 | } catch(BluetoothConnectionTimeoutException e){ 66 | Log.i(TAG, "[devybt sppconnection] retryconnect BluetoothConnectionTimeoutException"); 67 | 68 | retry(sppuuid, connectedDevice, error); 69 | 70 | } catch (BluetoothException e) { 71 | throw e; 72 | } 73 | 74 | } 75 | 76 | @Override 77 | public void reset() { 78 | currentRetryCount = 0; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/connection/RetryPolicy.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.connection; 2 | 3 | import android.bluetooth.BluetoothDevice; 4 | 5 | import com.devyok.bluetooth.base.BluetoothException; 6 | 7 | import java.util.UUID; 8 | 9 | 10 | /** 11 | * 重连策略 12 | * 默认实现:{@link DefaultRetryPolicy} 13 | * @author wei.deng 14 | */ 15 | public interface RetryPolicy { 16 | public void reset(); 17 | public int getCurrentRetryCount(); 18 | public void retry(UUID sppuuid,BluetoothDevice connectedDevice,Exception e) throws BluetoothConnectionException,BluetoothException; 19 | } 20 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/debug/DebugHelper.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.debug; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | 7 | import com.devyok.bluetooth.OkBluetooth; 8 | import com.devyok.bluetooth.utils.BluetoothUtils; 9 | 10 | /** 11 | * 仅运行在adb环境下 12 | * 13 | * 输出系统信息:adb shell am broadcast -a com.devyok.DEBUG_SYSTEM_BLUETOOTH_INFO 14 | * 弹出控制界面:adb shell am broadcast -a com.devyok.DEBUG_UI_CONSOLE 15 | * 输出LOG :adb logcat -v time debugBluetooth:I *:S 16 | * 系统社设置 :adb shell am start com.android.settings/com.android.settings.Settings 17 | * @author wei.deng 18 | */ 19 | public class DebugHelper extends BroadcastReceiver { 20 | 21 | public static final String ACTION_DEBUG_SYSTEM_INFO = "com.devyok.DEBUG_SYSTEM_BLUETOOTH_INFO"; 22 | 23 | public static final String ACTION_DEBUG_ACTIVITY = "com.devyok.DEBUG_UI_CONSOLE"; 24 | 25 | public static final String TAG = "debugBluetooth"; 26 | 27 | @Override 28 | public void onReceive(Context context, Intent intent) { 29 | 30 | if(intent == null) return ; 31 | 32 | if(!OkBluetooth.isReady()) { 33 | OkBluetooth.init(context); 34 | } 35 | 36 | 37 | String action = intent.getAction(); 38 | 39 | if(ACTION_DEBUG_SYSTEM_INFO.equals(action)) { 40 | 41 | BluetoothUtils.dumpBluetoothAllSystemInfos(TAG); 42 | 43 | } else if(ACTION_DEBUG_ACTIVITY.equals(action)){ 44 | 45 | Intent impl = new Intent(OkBluetooth.getContext(),DebugUIConsoleActivity.class); 46 | impl.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 47 | OkBluetooth.getContext().startActivity(impl); 48 | 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/hfp/BluetoothHeadsetProfileService.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.hfp; 2 | 3 | import android.bluetooth.BluetoothDevice; 4 | 5 | import com.devyok.bluetooth.base.BluetoothProfileService; 6 | /** 7 | * @author wei.deng 8 | */ 9 | public interface BluetoothHeadsetProfileService extends BluetoothProfileService { 10 | 11 | public interface BluetoothHeadsetAudioStateListener { 12 | public void onAudioConnected(BluetoothDevice bluetoothDevice, BluetoothHeadsetProfileService service); 13 | public void onAudioDisconnected(BluetoothDevice bluetoothDevice, BluetoothHeadsetProfileService service); 14 | public void onAudioConnecting(BluetoothDevice bluetoothDevice, BluetoothHeadsetProfileService service); 15 | } 16 | 17 | public void registerAudioStateChangedListener(BluetoothHeadsetAudioStateListener lis); 18 | public void unregisterAudioStateChangedListener(BluetoothHeadsetAudioStateListener lis); 19 | 20 | public boolean isAudioConnected(final BluetoothDevice device); 21 | 22 | public boolean disconnectAudio(); 23 | 24 | public boolean connectAudio(); 25 | 26 | public int getAudioState(final BluetoothDevice device); 27 | 28 | public boolean isAudioOn(); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/hfp/HFPConnection.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.hfp; 2 | 3 | import android.bluetooth.BluetoothDevice; 4 | 5 | import com.devyok.bluetooth.base.BluetoothException; 6 | import com.devyok.bluetooth.connection.AbstractBluetoothConnection; 7 | import com.devyok.bluetooth.connection.BluetoothConnection.State; 8 | import com.devyok.bluetooth.connection.BluetoothConnectionException; 9 | import com.devyok.bluetooth.connection.BluetoothConnectionTimeoutException; 10 | import com.devyok.bluetooth.utils.BluetoothUtils; 11 | 12 | import java.util.UUID; 13 | /** 14 | * @author wei.deng 15 | */ 16 | public final class HFPConnection extends AbstractBluetoothConnection{ 17 | 18 | private int companyid; 19 | volatile boolean isConnected = false; 20 | 21 | public HFPConnection(){ 22 | this(BluetoothUtils.UNKNOW); 23 | } 24 | 25 | public HFPConnection(int companyid) { 26 | this(null,null,companyid); 27 | } 28 | 29 | public boolean checkCompanyId(){ 30 | if(companyid == BluetoothUtils.UNKNOW){ 31 | return false; 32 | } 33 | return true; 34 | } 35 | 36 | public void setCompanyid(int companyid) { 37 | this.companyid = companyid; 38 | } 39 | 40 | public HFPConnection(UUID sppuuid, BluetoothDevice connectedDevice) { 41 | this(sppuuid,connectedDevice,-1); 42 | } 43 | 44 | public HFPConnection(UUID sppuuid, BluetoothDevice connectedDevice,int companyid) { 45 | super(sppuuid, connectedDevice); 46 | this.companyid = companyid; 47 | } 48 | 49 | @Override 50 | public void connect(UUID sppuuid, BluetoothDevice connectedDevice) throws BluetoothConnectionException, 51 | BluetoothConnectionTimeoutException, BluetoothException { 52 | try { 53 | HFPConnectionImpl impl = HFPConnectionImpl.connect(companyid); 54 | this.transactCore = impl; 55 | isConnected = true; 56 | this.currentState = State.CONNECTED; 57 | } catch(Exception e){ 58 | isConnected = false; 59 | throw e; 60 | } 61 | } 62 | 63 | @Override 64 | public void disconnect() { 65 | try { 66 | super.disconnect(); 67 | } finally { 68 | isConnected = false; 69 | } 70 | } 71 | 72 | @Override 73 | public boolean isConnected() { 74 | return isConnected; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/hfp/HFPConnectionImpl.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.hfp; 2 | 3 | import java.io.Closeable; 4 | import java.io.IOException; 5 | 6 | import android.bluetooth.BluetoothDevice; 7 | import android.bluetooth.BluetoothHeadset; 8 | import android.content.BroadcastReceiver; 9 | import android.content.Context; 10 | import android.content.Intent; 11 | import android.content.IntentFilter; 12 | import android.os.Parcelable; 13 | import android.util.Log; 14 | 15 | import com.devyok.bluetooth.OkBluetooth; 16 | import com.devyok.bluetooth.base.BluetoothException; 17 | import com.devyok.bluetooth.connection.BluetoothConnection.Protocol; 18 | import com.devyok.bluetooth.message.BluetoothMessage; 19 | import com.devyok.bluetooth.message.BluetoothMessageDispatcher; 20 | import com.devyok.bluetooth.utils.BluetoothUtils; 21 | /** 22 | * @author wei.deng 23 | */ 24 | class HFPConnectionImpl extends BroadcastReceiver implements Closeable{ 25 | 26 | static String TAG = HFPConnectionImpl.class.getSimpleName(); 27 | 28 | private HFPConnectionImpl(){ 29 | } 30 | 31 | public static HFPConnectionImpl connect(int companyid) throws BluetoothException { 32 | 33 | try { 34 | HFPConnectionImpl connectionImpl = new HFPConnectionImpl(); 35 | 36 | IntentFilter intentFilter = new IntentFilter(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT); 37 | 38 | if(companyid<0){ 39 | intentFilter.addCategory("android.bluetooth.headset.intent.category.companyid"); 40 | } else { 41 | intentFilter.addCategory("android.bluetooth.headset.intent.category.companyid"+"." + companyid); 42 | } 43 | 44 | OkBluetooth.getContext().registerReceiver(connectionImpl, intentFilter); 45 | 46 | return connectionImpl; 47 | } catch(Exception e){ 48 | throw new BluetoothException("connect fail", e); 49 | } 50 | } 51 | 52 | @Override 53 | public void close() throws IOException { 54 | try { 55 | OkBluetooth.getContext().unregisterReceiver(this); 56 | } catch(Exception e){ 57 | throw new IOException("unregisterReceiver fail",e); 58 | } 59 | } 60 | 61 | @Override 62 | public void onReceive(Context context, Intent intent) { 63 | 64 | if(intent == null){ 65 | return ; 66 | } 67 | 68 | String cmd = intent.getStringExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD); 69 | Object[] cmdArgs = (Object[]) intent.getExtras().get(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS); 70 | int cmdType = intent.getIntExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE, -1); 71 | Parcelable device = (Parcelable)intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 72 | String cmdTypeString = BluetoothUtils.getHeadsetEventTypeString(cmdType); 73 | 74 | String cmdArgsString = BluetoothUtils.toString(cmdArgs); 75 | 76 | BluetoothMessage bluetoothMessage = BluetoothMessage.obtain(cmdArgsString,Protocol.HFP); 77 | 78 | if(device!=null){ 79 | bluetoothMessage.setBluetoothDevice((BluetoothDevice) device); 80 | } 81 | 82 | BluetoothMessageDispatcher.dispatch(bluetoothMessage); 83 | 84 | Log.i(TAG, "[devybt sppconnection] cmd = " + cmd + ", args = " + cmdArgsString + " , cmdTypeString = " + cmdTypeString); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/hfp/HeadsetProfileService.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.hfp; 2 | 3 | import android.bluetooth.BluetoothDevice; 4 | import android.bluetooth.BluetoothHeadset; 5 | import android.bluetooth.BluetoothProfile; 6 | import android.bluetooth.BluetoothUuid; 7 | import android.content.BroadcastReceiver; 8 | import android.content.Context; 9 | import android.content.Intent; 10 | import android.content.IntentFilter; 11 | import android.os.ParcelUuid; 12 | import android.util.Log; 13 | 14 | import com.devyok.bluetooth.OkBluetooth; 15 | import com.devyok.bluetooth.base.BluetoothProfileService; 16 | import com.devyok.bluetooth.base.BluetoothProfileServiceTemplate; 17 | import com.devyok.bluetooth.base.BluetoothRuntimeException; 18 | import com.devyok.bluetooth.utils.BluetoothUtils; 19 | 20 | /** 21 | * @author wei.deng 22 | */ 23 | public class HeadsetProfileService extends BluetoothProfileServiceTemplate implements BluetoothHeadsetProfileService{ 24 | 25 | public static final ParcelUuid[] UUIDS = { 26 | BluetoothUuid.HSP, 27 | BluetoothUuid.Handsfree, 28 | }; 29 | 30 | public static final int PROFILE = BluetoothProfile.HEADSET; 31 | 32 | private BluetoothHeadsetProfileService.BluetoothHeadsetAudioStateListener audioStateListener; 33 | private BroadcastReceiver audioStateReceiverImpl; 34 | 35 | public HeadsetProfileService() { 36 | super(PROFILE); 37 | } 38 | 39 | public HeadsetProfileService(BluetoothProfileService decorater) { 40 | super(PROFILE,decorater); 41 | } 42 | 43 | public HeadsetProfileService(int profileType) { 44 | super(profileType); 45 | throw new BluetoothRuntimeException("not support"); 46 | } 47 | 48 | /** 49 | * 如果sco已经连接上,则callback返回true 50 | * @param device 51 | */ 52 | @Override 53 | public boolean isAudioConnected(final BluetoothDevice device) { 54 | 55 | if(realService == null) return false; 56 | 57 | return ((BluetoothHeadset) realService).isAudioConnected(device); 58 | 59 | } 60 | 61 | @Override 62 | public boolean disconnectAudio() { 63 | if(realService == null) return false; 64 | 65 | return ((BluetoothHeadset) realService).disconnectAudio(); 66 | } 67 | 68 | @Override 69 | public boolean connectAudio() { 70 | 71 | if(realService == null) return false; 72 | 73 | return ((BluetoothHeadset) realService).connectAudio(); 74 | 75 | } 76 | 77 | @Override 78 | public boolean isAudioOn(){ 79 | if(realService == null) return false; 80 | return ((BluetoothHeadset) realService).isAudioOn(); 81 | } 82 | 83 | @Override 84 | public int getAudioState(final BluetoothDevice device){ 85 | if(realService == null) return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 86 | return ((BluetoothHeadset) realService).getAudioState(device); 87 | } 88 | 89 | @Override 90 | protected void onRealServiceConnected() { 91 | super.onRealServiceConnected(); 92 | 93 | if(audioStateReceiverImpl == null){ 94 | OkBluetooth.getContext().registerReceiver(audioStateReceiverImpl = new BroadcastReceiver() { 95 | 96 | @Override 97 | public void onReceive(Context context, Intent intent) { 98 | 99 | int newState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, -1); 100 | int previousState = intent.getIntExtra(BluetoothHeadset.EXTRA_PREVIOUS_STATE, -1); 101 | 102 | Log.i(TAG, "[devybt connect] hfp audio new state = " + BluetoothUtils.getScoStateStringFromHeadsetProfile(newState) + " , pre state = " + BluetoothUtils.getScoStateStringFromHeadsetProfile(previousState)); 103 | 104 | BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 105 | 106 | BluetoothUtils.dumpBluetoothDevice(TAG, device); 107 | 108 | if(audioStateListener!=null){ 109 | 110 | if(newState == BluetoothHeadset.STATE_AUDIO_CONNECTED) { 111 | audioStateListener.onAudioConnected(device,HeadsetProfileService.this); 112 | } else if(newState == BluetoothHeadset.STATE_AUDIO_DISCONNECTED){ 113 | audioStateListener.onAudioDisconnected(device, HeadsetProfileService.this); 114 | } else if(newState == BluetoothHeadset.STATE_AUDIO_CONNECTING) { 115 | audioStateListener.onAudioConnecting(device, HeadsetProfileService.this); 116 | } 117 | 118 | } 119 | 120 | } 121 | }, new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)); 122 | } 123 | } 124 | 125 | @Override 126 | protected void onRealServiceDisconnected() { 127 | super.onRealServiceDisconnected(); 128 | if(audioStateReceiverImpl!=null){ 129 | OkBluetooth.getContext().unregisterReceiver(audioStateReceiverImpl); 130 | audioStateReceiverImpl = null; 131 | } 132 | } 133 | 134 | @Override 135 | protected ConnectionStateListenerArgs make() { 136 | return new ConnectionStateListenerArgs() { 137 | 138 | @Override 139 | public String extraPreState() { 140 | return BluetoothHeadset.EXTRA_PREVIOUS_STATE; 141 | } 142 | 143 | @Override 144 | public String extraNewState() { 145 | return BluetoothHeadset.EXTRA_STATE; 146 | } 147 | 148 | @Override 149 | public String extraDevice() { 150 | return BluetoothDevice.EXTRA_DEVICE; 151 | } 152 | 153 | @Override 154 | public String action() { 155 | return BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED; 156 | } 157 | }; 158 | } 159 | 160 | @Override 161 | public void registerAudioStateChangedListener( 162 | BluetoothHeadsetAudioStateListener lis) { 163 | audioStateListener = lis; 164 | } 165 | 166 | @Override 167 | public void unregisterAudioStateChangedListener( 168 | BluetoothHeadsetAudioStateListener lis) { 169 | audioStateListener = null; 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/message/BluetoothMessage.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.message; 2 | 3 | import java.util.UUID; 4 | 5 | import com.devyok.bluetooth.connection.BluetoothConnection.Protocol; 6 | 7 | import android.bluetooth.BluetoothDevice; 8 | 9 | /** 10 | * @author wei.deng 11 | */ 12 | public class BluetoothMessage { 13 | private String id; 14 | 15 | private long time; 16 | private DataType dataBody; 17 | private Protocol protocol; 18 | private BluetoothDevice bluetoothDevice; 19 | 20 | public BluetoothMessage(){ 21 | this.id = UUID.randomUUID().toString(); 22 | } 23 | 24 | public static BluetoothMessage obtain(DataType data,Protocol protocol){ 25 | return obtain(System.currentTimeMillis(), data,protocol); 26 | } 27 | 28 | public static BluetoothMessage obtain(long time,DataType data,Protocol protocol){ 29 | BluetoothMessage message = new BluetoothMessage(); 30 | message.time = time; 31 | message.dataBody = data; 32 | message.protocol = protocol; 33 | return message; 34 | } 35 | 36 | public BluetoothDevice getBluetoothDevice() { 37 | return bluetoothDevice; 38 | } 39 | 40 | public void setBluetoothDevice(BluetoothDevice bluetoothDevice) { 41 | this.bluetoothDevice = bluetoothDevice; 42 | } 43 | 44 | public long getTime(){ 45 | return time; 46 | } 47 | 48 | public DataType getBodyData(){ 49 | return dataBody; 50 | } 51 | 52 | public Protocol getProtocol(){ 53 | return protocol; 54 | } 55 | 56 | @Override 57 | public int hashCode() { 58 | return this.id.hashCode(); 59 | } 60 | 61 | @Override 62 | public boolean equals(Object obj) { 63 | 64 | BluetoothMessage msg = (BluetoothMessage) obj; 65 | 66 | if(this.id.equals(msg.id)){ 67 | return true; 68 | } 69 | 70 | return false; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/message/BluetoothMessageDispatcher.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.message; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | import java.util.concurrent.ExecutorService; 8 | import java.util.concurrent.Executors; 9 | 10 | import com.devyok.bluetooth.OkBluetooth; 11 | import com.devyok.bluetooth.connection.BluetoothConnection.Protocol; 12 | import com.devyok.bluetooth.utils.BluetoothUtils; 13 | /** 14 | * @author wei.deng 15 | */ 16 | public final class BluetoothMessageDispatcher implements BluetoothMessageHandler{ 17 | 18 | final ConcurrentHashMap>> receivers = new ConcurrentHashMap>>(); 19 | 20 | static ExecutorService dispatcherThreadPool = Executors.newSingleThreadExecutor(BluetoothUtils.createThreadFactory("bt.runtime.message-dispatcher")); 21 | 22 | static BluetoothMessageHandler instance; 23 | 24 | public static BluetoothMessageHandler getDispatcher(){ 25 | if(instance == null){ 26 | instance = new BluetoothMessageDispatcher(); 27 | } 28 | return (BluetoothMessageHandler) instance; 29 | } 30 | 31 | public void registerBluetoothMessageReceiver(Protocol protocol,BluetoothMessageReceiver receiver) { 32 | 33 | List> list = receivers.get(protocol); 34 | 35 | if(list == null){ 36 | list = Collections.synchronizedList(new ArrayList>()); 37 | } 38 | 39 | if(!list.contains(receiver)){ 40 | list.add(receiver); 41 | } 42 | 43 | receivers.put(protocol, list); 44 | } 45 | 46 | public void unregisterBluetoothMessageReceiver(Protocol protocol,BluetoothMessageReceiver receiver) { 47 | List> list = receivers.get(protocol); 48 | list.remove(receiver); 49 | } 50 | 51 | public void clear(Protocol protocol) { 52 | receivers.remove(protocol); 53 | } 54 | 55 | public void clearAll() { 56 | receivers.clear(); 57 | } 58 | 59 | class HandleTask implements Runnable { 60 | 61 | BluetoothMessage message; 62 | 63 | public HandleTask(BluetoothMessage message){ 64 | this.message = message; 65 | } 66 | 67 | @Override 68 | public void run() { 69 | 70 | List> list = receivers.get(message.getProtocol()); 71 | 72 | if(list!=null){ 73 | int size = list.size(); 74 | 75 | for(int i = 0;i < size;i++){ 76 | BluetoothMessageReceiver receive = list.get(i); 77 | if(receive!=null){ 78 | receive.onReceive(message); 79 | } 80 | } 81 | } 82 | 83 | } 84 | } 85 | 86 | public void handle(BluetoothMessage message) { 87 | dispatcherThreadPool.submit(new HandleTask(message)); 88 | } 89 | 90 | public static void dispatch(BluetoothMessage message){ 91 | BluetoothMessageHandler dispatcher = OkBluetooth.getConfiguration().getDispatcher(); 92 | dispatcher.handle(message); 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/message/BluetoothMessageHandler.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.message; 2 | /** 3 | * @author wei.deng 4 | */ 5 | public interface BluetoothMessageHandler { 6 | 7 | public void handle(BluetoothMessage bluetoothMessage); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/message/BluetoothMessageReceiver.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.message; 2 | /** 3 | * @author wei.deng 4 | */ 5 | public abstract class BluetoothMessageReceiver { 6 | 7 | public abstract boolean onReceive(BluetoothMessage message); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/sco/BluetoothSCOService.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.sco; 2 | 3 | import android.content.Context; 4 | import android.media.AudioManager; 5 | import android.util.Log; 6 | 7 | import com.devyok.bluetooth.OkBluetooth; 8 | import com.devyok.bluetooth.base.BaseBluetoothStateChangedListener; 9 | import com.devyok.bluetooth.base.BluetoothException; 10 | import com.devyok.bluetooth.base.BluetoothService; 11 | import com.devyok.bluetooth.base.StateInformation; 12 | import com.devyok.bluetooth.connection.BluetoothConnectionStateListener; 13 | import com.devyok.bluetooth.utils.BluetoothUtils; 14 | /** 15 | * @author deng.wei 16 | */ 17 | public class BluetoothSCOService extends BluetoothService{ 18 | 19 | private static final String TAG = BluetoothSCOService.class.getSimpleName(); 20 | 21 | private BluetoothConnectionStateListener mScoStateListener = BluetoothConnectionStateListener.EMTPY; 22 | private final ScoStateListenerImpl stateListenerImpl = new ScoStateListenerImpl(); 23 | 24 | public BluetoothSCOService(){ 25 | } 26 | 27 | @Override 28 | public boolean init() throws BluetoothException { 29 | super.init(); 30 | stateListenerImpl.startListener(); 31 | return true; 32 | } 33 | 34 | @Override 35 | public boolean destory() { 36 | super.destory(); 37 | stateListenerImpl.stopListener(); 38 | mScoStateListener = null; 39 | return true; 40 | } 41 | 42 | public void setConnectionStateListener(BluetoothConnectionStateListener lis){ 43 | this.mScoStateListener = (lis!=null ? lis : BluetoothConnectionStateListener.EMTPY); 44 | } 45 | 46 | public boolean isConnectedSco(){ 47 | AudioManager am = getAudioManager(); 48 | 49 | boolean isBluetoothScoOn = am.isBluetoothScoOn(); 50 | boolean isSpeakphoneOn = am.isSpeakerphoneOn(); 51 | int audioMode = am.getMode(); 52 | 53 | if(OkBluetooth.isBluetoothEnable() && isBluetoothScoOn && !isSpeakphoneOn && AudioManager.MODE_IN_COMMUNICATION == audioMode) { 54 | return true; 55 | } 56 | 57 | return false; 58 | } 59 | 60 | public void startSco(){ 61 | AudioManager am = getAudioManager(); 62 | boolean isBluetoothScoOn = am.isBluetoothScoOn(); 63 | 64 | Log.i(TAG, "[devybt sco] startTryConnectSco startSco enter , isBluetoothScoOn = " + isBluetoothScoOn); 65 | if(!am.isBluetoothScoOn()) { 66 | am.setBluetoothScoOn(true); 67 | am.startBluetoothSco(); 68 | } 69 | Log.i(TAG, "[devybt sco] startTryConnectSco startSco exit"); 70 | } 71 | 72 | public void stopSco(){ 73 | AudioManager am = getAudioManager(); 74 | am.stopBluetoothSco(); 75 | am.setBluetoothScoOn(false); 76 | } 77 | 78 | AudioManager getAudioManager(){ 79 | return (AudioManager) OkBluetooth.getContext().getSystemService(Context.AUDIO_SERVICE); 80 | } 81 | 82 | 83 | private class ScoStateListenerImpl extends BaseBluetoothStateChangedListener { 84 | 85 | public ScoStateListenerImpl(){ 86 | } 87 | 88 | @Override 89 | public boolean onChanged(StateInformation information) { 90 | 91 | if(OkBluetooth.isDebugable()){ 92 | BluetoothUtils.dumpBluetoothScoStateInfos(TAG, information.getIntent()); 93 | } 94 | 95 | int currentState = information.getCurrentState(); 96 | 97 | switch (currentState) { 98 | case AudioManager.SCO_AUDIO_STATE_CONNECTING: 99 | mScoStateListener.onConnecting(information); 100 | break; 101 | case AudioManager.SCO_AUDIO_STATE_CONNECTED: 102 | mScoStateListener.onConnected(information); 103 | break; 104 | case AudioManager.SCO_AUDIO_STATE_DISCONNECTED: 105 | mScoStateListener.onDisconnected(information); 106 | break; 107 | case AudioManager.SCO_AUDIO_STATE_ERROR: 108 | mScoStateListener.onError(information); 109 | break; 110 | default: 111 | break; 112 | } 113 | 114 | return true; 115 | } 116 | 117 | 118 | @Override 119 | public String[] actions() { 120 | return new String[]{AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED}; 121 | } 122 | 123 | 124 | } 125 | 126 | 127 | } 128 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/spp/AbstractSPPConnection.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.spp; 2 | 3 | import java.util.UUID; 4 | 5 | import android.bluetooth.BluetoothDevice; 6 | import android.bluetooth.BluetoothSocket; 7 | 8 | import com.devyok.bluetooth.OkBluetooth; 9 | import com.devyok.bluetooth.connection.AbstractBluetoothConnection; 10 | import com.devyok.bluetooth.connection.BluetoothConnection.State; 11 | import com.devyok.bluetooth.utils.BluetoothUtils; 12 | /** 13 | * @author wei.deng 14 | */ 15 | public abstract class AbstractSPPConnection extends AbstractBluetoothConnection { 16 | 17 | static final String TAG = AbstractSPPConnection.class.getSimpleName(); 18 | 19 | protected SPPMessageReceiver messageReceiver; 20 | 21 | public AbstractSPPConnection(UUID sppuuid,BluetoothDevice connectedDevice){ 22 | super(sppuuid,connectedDevice); 23 | } 24 | 25 | @Override 26 | public boolean isConnected(){ 27 | return transactCore!=null ? transactCore.isConnected() : false; 28 | } 29 | 30 | @Override 31 | public State getState(){ 32 | return BluetoothUtils.getBluetoothSocketState(this.transactCore); 33 | } 34 | 35 | public void onConnected(){ 36 | 37 | if(messageReceiver!=null){ 38 | messageReceiver.stopReceiver(); 39 | } 40 | 41 | SPPBluetoothMessageParser parser = OkBluetooth.getSppMessageParser(); 42 | messageReceiver = new SPPMessageReceiver(this.transactCore,parser == null ? new DefaultSPPMessageParser() : parser,this.getBluetoothDevice()); 43 | messageReceiver.start(); 44 | } 45 | 46 | @Override 47 | protected void closeTransactCore() { 48 | super.closeTransactCore(); 49 | 50 | if(messageReceiver!=null){ 51 | messageReceiver.closeStream(); 52 | messageReceiver = null; 53 | } 54 | 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/spp/DefaultSPPMessageParser.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.spp; 2 | 3 | import com.devyok.bluetooth.connection.BluetoothConnection.Protocol; 4 | import com.devyok.bluetooth.message.BluetoothMessage; 5 | 6 | import android.bluetooth.BluetoothDevice; 7 | /** 8 | * @author wei.deng 9 | */ 10 | public class DefaultSPPMessageParser implements SPPBluetoothMessageParser { 11 | 12 | final StringBuilder readMessage = new StringBuilder(); 13 | 14 | @Override 15 | public BluetoothMessage[] parse(byte[] buffer, int readCount,Protocol protocol,BluetoothDevice device) { 16 | String readed = new String(buffer, 0, readCount); 17 | readMessage.append(readed); 18 | if (readed.contains("\n")) { 19 | BluetoothMessage message = BluetoothMessage.obtain(new String(readMessage.toString()), protocol); 20 | readMessage.setLength(0); 21 | return new BluetoothMessage[]{message}; 22 | } 23 | return null; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/spp/SPPBluetoothMessageParser.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.spp; 2 | 3 | import com.devyok.bluetooth.connection.BluetoothConnection.Protocol; 4 | import com.devyok.bluetooth.message.BluetoothMessage; 5 | 6 | import android.bluetooth.BluetoothDevice; 7 | /** 8 | * @author wei.deng 9 | */ 10 | public interface SPPBluetoothMessageParser { 11 | 12 | public static final SPPBluetoothMessageParser DEFAULT = new SPPBluetoothMessageParser() { 13 | 14 | final StringBuilder readMessage = new StringBuilder(); 15 | 16 | @Override 17 | public BluetoothMessage[] parse(byte[] buffer, int readCount,Protocol protocol,BluetoothDevice device) { 18 | String readed = new String(buffer, 0, readCount); 19 | readMessage.append(readed); 20 | if (readed.contains("\n")) { 21 | BluetoothMessage message = BluetoothMessage.obtain(new String(readMessage.toString()), protocol); 22 | readMessage.setLength(0); 23 | return new BluetoothMessage[]{message}; 24 | } 25 | return null; 26 | } 27 | 28 | }; 29 | 30 | public BluetoothMessage[] parse(byte[] buffer, int readCount, Protocol protocol, BluetoothDevice device); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/spp/SPPConnectionInsecurePolicy.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.spp; 2 | 3 | import android.bluetooth.BluetoothDevice; 4 | import android.bluetooth.BluetoothSocket; 5 | import android.os.Build; 6 | import android.util.Log; 7 | 8 | import com.devyok.bluetooth.connection.BluetoothConnectionException; 9 | 10 | import java.io.IOException; 11 | import java.lang.reflect.Method; 12 | import java.util.UUID; 13 | /** 14 | * @author wei.deng 15 | */ 16 | public class SPPConnectionInsecurePolicy extends AbstractSPPConnection { 17 | 18 | public SPPConnectionInsecurePolicy(){ 19 | this(null,null); 20 | } 21 | 22 | public SPPConnectionInsecurePolicy(UUID sppuuid, 23 | BluetoothDevice connectedDevice) { 24 | super(sppuuid, connectedDevice); 25 | } 26 | 27 | static final String TAG = SPPConnectionInsecurePolicy.class.getSimpleName(); 28 | 29 | @Override 30 | public void connect(UUID sppuuid,BluetoothDevice connectedDevice) throws BluetoothConnectionException{ 31 | 32 | Log.i(TAG, "[devybt sppconnection] SPPConnectionDefaultPolicy start try connect , isCancel("+isCancel()+") , sppuuid = " + sppuuid); 33 | 34 | if (Build.VERSION.SDK_INT >= 10) { //不安全 35 | Class cls = BluetoothDevice.class; 36 | try { 37 | Method m = cls.getMethod("createInsecureRfcommSocketToServiceRecord",new Class[] { UUID.class }); 38 | transactCore = (BluetoothSocket) m.invoke(connectedDevice,new Object[] { sppuuid }); 39 | } catch (Exception e) { 40 | Log.i(TAG, "[devybt sppconnection] createInsecureRfcommSocketToServiceRecord Exception"); 41 | throw new BluetoothConnectionException("createInsecureRfcommSocketToServiceRecord exception",e); 42 | } 43 | } else { 44 | throw new BluetoothConnectionException("sdk not support insecure connection"); 45 | } 46 | 47 | try { 48 | if(!isCancel() && transactCore != null){ 49 | transactCore.connect(); 50 | } 51 | } catch (IOException e) { 52 | closeTransactCore(); 53 | Log.i(TAG, "[devybt sppconnection] try connect IOException"); 54 | throw new BluetoothConnectionException("spp connect exception" , e); 55 | } 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/spp/SPPConnectionSecurePolicy.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.spp; 2 | 3 | import java.io.IOException; 4 | import java.util.UUID; 5 | 6 | import android.bluetooth.BluetoothDevice; 7 | import android.util.Log; 8 | 9 | import com.devyok.bluetooth.connection.BluetoothConnectionException; 10 | /** 11 | * @author wei.deng 12 | */ 13 | public class SPPConnectionSecurePolicy extends AbstractSPPConnection { 14 | 15 | static int exeCount = 0; 16 | 17 | public SPPConnectionSecurePolicy(){ 18 | this(null,null); 19 | } 20 | 21 | public SPPConnectionSecurePolicy(UUID sppuuid, BluetoothDevice connectedDevice) { 22 | super(sppuuid, connectedDevice); 23 | } 24 | 25 | static final String TAG = SPPConnectionSecurePolicy.class.getSimpleName(); 26 | 27 | @Override 28 | public void connect(UUID sppuuid,BluetoothDevice connectedDevice) throws BluetoothConnectionException{ 29 | 30 | Log.i(TAG, "[devybt sppconnection] SPPConnectionSecurePolicy start try connect , isCancel("+isCancel()+") , sppuuid = " + sppuuid); 31 | 32 | try { 33 | transactCore = connectedDevice.createRfcommSocketToServiceRecord(sppuuid); 34 | } catch (IOException e) { 35 | Log.i(TAG, "[devybt sppconnection] createRfcommSocketToServiceRecord IOException"); 36 | throw new BluetoothConnectionException("createRfcommSocketToServiceRecord exception", e); 37 | } 38 | 39 | // test code 40 | // if(exeCount % 2 == 0){ 41 | // try { 42 | // Thread.sleep(24*1000); 43 | // } catch (InterruptedException e1) { 44 | // e1.printStackTrace(); 45 | // } 46 | // } 47 | // 48 | // exeCount++; 49 | 50 | try { 51 | if(!isCancel() && transactCore != null){ 52 | transactCore.connect(); 53 | if(transactCore.isConnected()){ 54 | onConnected(); 55 | } 56 | } 57 | } catch (IOException e) { 58 | closeTransactCore(); 59 | Log.i(TAG, "[devybt sppconnection] try connect IOException"); 60 | throw new BluetoothConnectionException("spp connect exception" , e); 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/spp/SPPMessageParser.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.spp; 2 | 3 | import com.devyok.bluetooth.connection.BluetoothConnection.Protocol; 4 | import com.devyok.bluetooth.message.BluetoothMessage; 5 | 6 | import android.bluetooth.BluetoothDevice; 7 | /** 8 | * @author wei.deng 9 | */ 10 | public interface SPPMessageParser { 11 | 12 | public BluetoothMessage[] parse(byte[] buffer, int readCount, Protocol protocol, BluetoothDevice device); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/spp/SPPMessageReceiver.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.spp; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | 7 | import com.devyok.bluetooth.connection.BluetoothConnection.Protocol; 8 | import com.devyok.bluetooth.message.BluetoothMessage; 9 | import com.devyok.bluetooth.message.BluetoothMessageDispatcher; 10 | import com.devyok.bluetooth.utils.BluetoothUtils; 11 | 12 | import android.bluetooth.BluetoothDevice; 13 | import android.bluetooth.BluetoothSocket; 14 | import android.util.Log; 15 | /** 16 | * @author wei.deng 17 | */ 18 | public class SPPMessageReceiver extends Thread{ 19 | 20 | private static final String TAG = SPPMessageReceiver.class.getSimpleName(); 21 | 22 | private InputStream mInStream; 23 | private OutputStream mOutStream; 24 | private volatile boolean isStopped = false; 25 | private SPPBluetoothMessageParser messageParser; 26 | private BluetoothDevice bluetoothDevice; 27 | 28 | public SPPMessageReceiver(BluetoothSocket socket,SPPBluetoothMessageParser parser,BluetoothDevice bluetoothDevice) { 29 | Log.i(TAG, "create SPPMessageReader"); 30 | 31 | this.messageParser = parser; 32 | this.bluetoothDevice = bluetoothDevice; 33 | InputStream tmpIn = null; 34 | OutputStream tmpOut = null; 35 | 36 | try { 37 | tmpIn = socket.getInputStream(); 38 | tmpOut = socket.getOutputStream(); 39 | } catch (IOException e) { 40 | Log.e(TAG, "temp sockets not created", e); 41 | } 42 | 43 | mInStream = tmpIn; 44 | mOutStream = tmpOut; 45 | } 46 | 47 | public void run() { 48 | Log.i(TAG, "SPPMessageReceiver run"); 49 | byte[] buffer = new byte[512]; 50 | int bytes; 51 | 52 | while (!isStopped()) { 53 | try { 54 | bytes = mInStream.read(buffer); 55 | 56 | BluetoothMessage[] messages = messageParser.parse(buffer, bytes,Protocol.SPP,this.bluetoothDevice); 57 | 58 | if(messages!=null){ 59 | 60 | for (int i = 0; i < messages.length; i++) { 61 | BluetoothMessage message = messages[i]; 62 | message.setBluetoothDevice(this.bluetoothDevice); 63 | BluetoothMessageDispatcher.dispatch(message); 64 | } 65 | 66 | } 67 | 68 | } catch (IOException e) { 69 | Log.e(TAG, "disconnected", e); 70 | closeStream(); 71 | break; 72 | } 73 | } 74 | Log.i(TAG, "SPPMessageReceiver run completed"); 75 | } 76 | 77 | public void startReceiver() { 78 | isStopped = false; 79 | super.start(); 80 | } 81 | 82 | public void stopReceiver(){ 83 | isStopped = true; 84 | closeStream(); 85 | } 86 | 87 | public boolean isStopped(){ 88 | return isStopped; 89 | } 90 | 91 | public void closeStream() { 92 | try { 93 | BluetoothUtils.close(mInStream); 94 | BluetoothUtils.close(mOutStream); 95 | } finally { 96 | mInStream = null; 97 | mOutStream = null; 98 | } 99 | 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /okbluetooth/src/main/java/com/devyok/bluetooth/spp/SppConnectionLoopChannelPolicy.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.spp; 2 | 3 | import java.io.IOException; 4 | import java.lang.reflect.Method; 5 | import java.util.UUID; 6 | 7 | import android.bluetooth.BluetoothDevice; 8 | import android.bluetooth.BluetoothSocket; 9 | import android.util.Log; 10 | 11 | import com.devyok.bluetooth.connection.BluetoothConnectionException; 12 | /** 13 | * @author wei.deng 14 | */ 15 | public class SppConnectionLoopChannelPolicy extends AbstractSPPConnection { 16 | 17 | static final String TAG = SppConnectionLoopChannelPolicy.class.getSimpleName(); 18 | 19 | int maxChannel = 3; 20 | 21 | public SppConnectionLoopChannelPolicy(UUID sppuuid,BluetoothDevice connectedDevice,int maxChannel){ 22 | super(sppuuid,connectedDevice); 23 | this.maxChannel = maxChannel; 24 | } 25 | 26 | public SppConnectionLoopChannelPolicy(){ 27 | this(null,null,10); 28 | } 29 | 30 | public SppConnectionLoopChannelPolicy(UUID sppuuid,BluetoothDevice connectedDevice){ 31 | super(sppuuid,connectedDevice); 32 | } 33 | 34 | @Override 35 | public void connect(UUID sppuuid,BluetoothDevice connectedDevice) throws BluetoothConnectionException { 36 | BluetoothConnectionException excp = null; 37 | 38 | for (int channel = 1; channel < maxChannel; channel++) { 39 | try { 40 | 41 | if(isCancel()){ 42 | break; 43 | } 44 | 45 | Method m = connectedDevice.getClass().getMethod("createRfcommSocket", new Class[] { int.class }); 46 | transactCore = (BluetoothSocket) m.invoke(connectedDevice, channel); 47 | 48 | if(transactCore != null){ 49 | Log.i(TAG, "[devybt sppconnection] LoopChannelSppConnectPolicy start try connect channel("+channel+") , isCancel("+isCancel()+")"); 50 | 51 | transactCore.connect(); 52 | 53 | Log.i(TAG,"[devybt sppconnection] connect channel("+channel+") result" + (transactCore!=null && transactCore.isConnected() ? "success" : "fail")); 54 | 55 | } else { 56 | Log.i(TAG,"[devybt sppconnection] connect channel("+channel+") bluetoothSocket(null)"); 57 | } 58 | 59 | } catch (IOException e) { 60 | Log.i(TAG, "[devybt sppconnection] IOException try connect channel("+channel+") fail"); 61 | if(excp == null) { 62 | excp = new BluetoothConnectionException("try connect channel fail",e); 63 | } 64 | 65 | closeTransactCore(); 66 | 67 | } catch (Exception e){ 68 | Log.i(TAG, "[devybt sppconnection] Exception try connect channel("+channel+") fail"); 69 | throw new BluetoothConnectionException("try connect channel exception",e); 70 | } 71 | } 72 | throw excp == null ? new BluetoothConnectionException("LoopChannelSppConnectPolicy fail") : excp; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /okbluetooth/src/main/res/layout/okbt_audio_device_list.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /okbluetooth/src/main/res/layout/okbt_audio_device_list_item.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 19 | 20 | -------------------------------------------------------------------------------- /okbluetooth/src/main/res/values/okbt_colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #2C3E50 5 | 6 | -------------------------------------------------------------------------------- /okbluetooth/src/main/res/values/okbt_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 扬声器 5 | 听筒 6 | 蓝牙设备 7 | 8 | 9 | -------------------------------------------------------------------------------- /okbluetooth_demo/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /okbluetooth_demo/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /okbluetooth_demo/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | defaultConfig { 6 | applicationId "com.devyok.bluetooth.demo" 7 | minSdkVersion 19 8 | targetSdkVersion 23 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(dir: 'libs', include: ['*.jar']) 23 | compile project(path: ':okbluetooth') 24 | } 25 | -------------------------------------------------------------------------------- /okbluetooth_demo/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /okbluetooth_demo/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /okbluetooth_demo/app/src/main/java/com/devyok/bluetooth/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.demo; 2 | 3 | import android.app.Application; 4 | 5 | public class DemoApplication extends Application { 6 | 7 | @Override 8 | public void onCreate() { 9 | super.onCreate(); 10 | 11 | OkBluetoothAdapter.onAppReady(this); 12 | 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /okbluetooth_demo/app/src/main/java/com/devyok/bluetooth/demo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.demo; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | import android.view.View.OnClickListener; 8 | 9 | import com.devyok.bluetooth.OkBluetooth; 10 | import com.devyok.bluetooth.debug.DebugUIConsoleActivity; 11 | 12 | public class MainActivity extends Activity implements OnClickListener{ 13 | static final String TAG = MainActivity.class.getSimpleName(); 14 | 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | setContentView(R.layout.activity_main); 19 | 20 | this.findViewById(R.id.start_debug_ui_console).setOnClickListener(this); 21 | 22 | } 23 | 24 | @Override 25 | public void onClick(View v) { 26 | int id = v.getId(); 27 | if(R.id.start_debug_ui_console == id){ 28 | 29 | Intent impl = new Intent(OkBluetooth.getContext(),DebugUIConsoleActivity.class); 30 | 31 | impl.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 32 | OkBluetooth.getContext().startActivity(impl); 33 | 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /okbluetooth_demo/app/src/main/java/com/devyok/bluetooth/demo/OkBluetoothAdapter.java: -------------------------------------------------------------------------------- 1 | package com.devyok.bluetooth.demo; 2 | 3 | import java.util.ArrayList; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | import android.bluetooth.BluetoothDevice; 9 | import android.content.BroadcastReceiver; 10 | import android.content.Context; 11 | import android.content.Intent; 12 | import android.content.IntentFilter; 13 | import android.media.AudioManager; 14 | import android.os.Build; 15 | import android.util.Log; 16 | 17 | import com.devyok.bluetooth.AudioDevice; 18 | import com.devyok.bluetooth.Configuration; 19 | import com.devyok.bluetooth.OkBluetooth; 20 | import com.devyok.bluetooth.OkBluetooth.BluetoothProtocolConnectionStateListener; 21 | import com.devyok.bluetooth.OkBluetooth.ConnectionMode; 22 | import com.devyok.bluetooth.OkBluetooth.Interceptor; 23 | import com.devyok.bluetooth.connection.BluetoothConnection.Protocol; 24 | import com.devyok.bluetooth.message.BluetoothMessage; 25 | import com.devyok.bluetooth.message.BluetoothMessageReceiver; 26 | import com.devyok.bluetooth.spp.SPPBluetoothMessageParser; 27 | import com.devyok.bluetooth.utils.BluetoothUtils; 28 | 29 | public final class OkBluetoothAdapter { 30 | 31 | private static final String TAG = OkBluetoothAdapter.class.getSimpleName(); 32 | 33 | private static final ConcurrentHashMap gInterceptors = new ConcurrentHashMap<>(); 34 | 35 | static { 36 | gInterceptors.put("phoneModel", new DemoInterceptor()); 37 | } 38 | 39 | public static void onAppReady(Context context){ 40 | 41 | Log.i(TAG,"device mode = " + Build.MODEL); 42 | 43 | Configuration configuration = new Configuration.Builder() 44 | .setDebug(true) 45 | .setSupport(true) 46 | .setCompanyId(85) 47 | .setConnectionMode(ConnectionMode.BLUETOOTH_WIREDHEADSET_SPEAKER) 48 | .setForceTypes(OkBluetooth.FORCE_TYPE_PHONE_INCALL | 49 | OkBluetooth.FORCE_TYPE_PHONE_RING | 50 | OkBluetooth.FORCE_TYPE_PHONE_INCALL_TO_IDLE) 51 | .build(); 52 | 53 | 54 | OkBluetooth.init(context.getApplicationContext(), configuration); 55 | 56 | Interceptor interceptor = gInterceptors.get(Build.MODEL); 57 | 58 | OkBluetooth.setInterceptor(interceptor); 59 | OkBluetooth.registerBluetoothProtocolConnectionStateListener(new BluetoothProtocolConnectionStateListener() { 60 | 61 | @Override 62 | public void onDisconnected(Protocol protocol, BluetoothDevice device) { 63 | super.onDisconnected(protocol, device); 64 | Log.i("btMessage", "onDisconnected protocol = " + protocol.getName() + " , device = " + device.getName()); 65 | } 66 | 67 | @Override 68 | public void onConnected(Protocol protocol, BluetoothDevice device) { 69 | super.onConnected(protocol, device); 70 | Log.i("btMessage", "onConnected protocol = " + protocol.getName() + " , device = " + device.getName()); 71 | } 72 | 73 | }); 74 | 75 | OkBluetooth.registerBluetoothMessageParser(new SavoxSppMessageParser()); 76 | 77 | OkBluetooth.registerBluetoothMessageReceiver(new BluetoothMessageReceiver() { 78 | 79 | @Override 80 | public boolean onReceive(BluetoothMessage message) { 81 | String bodyData = message.getBodyData(); 82 | 83 | Log.i("btMessage", "body data = " + bodyData); 84 | 85 | return false; 86 | } 87 | 88 | },new Protocol[]{Protocol.SPP,Protocol.HFP}); 89 | 90 | context.registerReceiver(new BroadcastReceiver() { 91 | 92 | @Override 93 | public void onReceive(Context context, Intent intent) { 94 | 95 | int streamType = intent.getIntExtra("android.media.EXTRA_VOLUME_STREAM_TYPE", -1); 96 | 97 | Log.i("volumnTrace", "volumn changed stream type = " + BluetoothUtils.getAudioStreamTypeString(streamType)); 98 | 99 | } 100 | }, new IntentFilter("android.media.VOLUME_CHANGED_ACTION")); 101 | 102 | } 103 | 104 | static class SavoxSppMessageParser implements SPPBluetoothMessageParser { 105 | 106 | final Pattern pattern = Pattern.compile("\\W?\\w+=\\w{1}"); 107 | 108 | @Override 109 | public BluetoothMessage[] parse(byte[] buffer,int readCount,Protocol protocol,BluetoothDevice device) { 110 | String data = new String(buffer, 0, readCount); 111 | Log.i("btMessage", "receiver data = " + data); 112 | Matcher matcher = pattern.matcher(data); 113 | ArrayList> result = new ArrayList<>(); 114 | while(matcher.find()){ 115 | result.add(BluetoothMessage.obtain(matcher.group(), protocol)); 116 | } 117 | 118 | return result.toArray(new BluetoothMessage[]{}); 119 | } 120 | 121 | } 122 | 123 | static class DemoInterceptor extends Interceptor { 124 | 125 | @Override 126 | public boolean beforeConnect(AudioDevice audioDevice) { 127 | 128 | if(AudioDevice.SCO == audioDevice) { 129 | // handleConnectSco(); 130 | // return true; 131 | } 132 | 133 | return super.beforeConnect(audioDevice); 134 | } 135 | 136 | private static void handleConnectSco(){ 137 | boolean isAudioConnected = OkBluetooth.HFP.isAudioConnected(); 138 | boolean isBluetoothScoOn = OkBluetooth.isBluetoothScoOn(); 139 | boolean isBluetoothA2dpOn = OkBluetooth.isBluetoothA2dpOn(); 140 | boolean isSpeakerphoneOn = OkBluetooth.isSpeakerphoneOn(); 141 | 142 | Log.i(TAG, "intercept tryBuildAudioConnection(SCO) isAudioConnected = " + isAudioConnected + " , isBluetoothScoOn = " + isBluetoothScoOn + " , isBluetoothA2dpOn = " + isBluetoothA2dpOn + " , isSpeakerphoneOn = " + isSpeakerphoneOn); 143 | if(!isAudioConnected) { 144 | syncSetMode(AudioManager.MODE_IN_CALL); 145 | 146 | OkBluetooth.setSpeakerphoneOn(false); 147 | OkBluetooth.setBluetoothA2dpOn(false); 148 | OkBluetooth.setBluetoothScoOn(false); 149 | syncConnectAudio(); 150 | OkBluetooth.setScoStreamVolumn(OkBluetooth.getScoMaxStreamVolumn(), AudioManager.FLAG_PLAY_SOUND); 151 | } else { 152 | 153 | if(isBluetoothA2dpOn){ 154 | OkBluetooth.setBluetoothA2dpOn(false); 155 | } 156 | if(isSpeakerphoneOn){ 157 | OkBluetooth.setSpeakerphoneOn(false); 158 | } 159 | if(!isBluetoothScoOn) { 160 | OkBluetooth.setBluetoothScoOn(true); 161 | } 162 | 163 | } 164 | } 165 | 166 | private static void syncSetMode(int mode){ 167 | Log.i(TAG, "syncSetMode("+BluetoothUtils.getAudioModeString(mode)+")"); 168 | Intent intent = new Intent("com.zed3.action.SET_SYSTEM_AUDIO_MODE"); 169 | intent.putExtra("com.zed3.extra.AUDIO_MODE", mode); 170 | OkBluetooth.getContext().sendBroadcast(intent); 171 | 172 | int newmode = OkBluetooth.getAudioMode(); 173 | int max = 5; 174 | int cur = 0; 175 | while(newmode != mode) { 176 | if(cur >= max){ 177 | break; 178 | } 179 | try { 180 | Thread.sleep(300); 181 | } catch (InterruptedException e) { 182 | e.printStackTrace(); 183 | } 184 | 185 | newmode = OkBluetooth.getAudioMode(); 186 | 187 | Log.i(TAG, "loop item wait new mode("+BluetoothUtils.getAudioModeString(newmode)+")"); 188 | ++cur; 189 | } 190 | 191 | } 192 | 193 | private static void syncConnectAudio(){ 194 | OkBluetooth.HFP.connectAudio(); 195 | 196 | int max = 5; 197 | int cur = 0; 198 | 199 | boolean isAudioConnected = OkBluetooth.HFP.isAudioConnected(); 200 | 201 | Log.i(TAG, "syncConnectAudio("+isAudioConnected+")"); 202 | 203 | while(!isAudioConnected) { 204 | if(cur >= max){ 205 | break; 206 | } 207 | try { 208 | Thread.sleep(300); 209 | } catch (InterruptedException e) { 210 | e.printStackTrace(); 211 | } 212 | 213 | isAudioConnected = OkBluetooth.HFP.isAudioConnected(); 214 | 215 | Log.i(TAG, "loop item wait new audio state("+isAudioConnected+")"); 216 | ++cur; 217 | } 218 | 219 | } 220 | 221 | } 222 | 223 | 224 | 225 | 226 | } 227 | -------------------------------------------------------------------------------- /okbluetooth_demo/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | 16 | 21 | 26 | 31 | 36 | 41 | 46 | 51 | 56 | 61 | 66 | 71 | 76 | 81 | 86 | 91 | 96 | 101 | 106 | 111 | 116 | 121 | 126 | 131 | 136 | 141 | 146 | 151 | 156 | 161 | 166 | 171 | 172 | -------------------------------------------------------------------------------- /okbluetooth_demo/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 |