Loading android/app/jni/com_android_bluetooth_a2dp.cpp +3 −3 Original line number Diff line number Diff line Loading @@ -393,8 +393,8 @@ static JNINativeMethod sMethods[] = { }; int register_com_android_bluetooth_a2dp(JNIEnv* env) { return jniRegisterNativeMethods(env, "com/android/bluetooth/a2dp/A2dpStateMachine", sMethods, NELEM(sMethods)); return jniRegisterNativeMethods( env, "com/android/bluetooth/a2dp/A2dpNativeInterface", sMethods, NELEM(sMethods)); } } android/app/src/com/android/bluetooth/a2dp/A2dpNativeInterface.java 0 → 100644 +190 −0 Original line number Diff line number Diff line /* * Copyright 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Defines the native inteface that is used by state machine/service to * send or receive messages from the native stack. This file is registered * for the native methods in the corresponding JNI C++ file. */ package com.android.bluetooth.a2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothCodecConfig; import android.bluetooth.BluetoothDevice; import android.support.annotation.VisibleForTesting; import android.util.Log; import com.android.bluetooth.Utils; import com.android.internal.annotations.GuardedBy; /** * A2DP Native Interface to/from JNI. */ public class A2dpNativeInterface { private static final String TAG = "A2dpNativeInterface"; private static final boolean DBG = true; private BluetoothAdapter mAdapter; @GuardedBy("INSTANCE_LOCK") private static A2dpNativeInterface sInstance; private static final Object INSTANCE_LOCK = new Object(); static { classInitNative(); } @VisibleForTesting private A2dpNativeInterface() { mAdapter = BluetoothAdapter.getDefaultAdapter(); if (mAdapter == null) { Log.wtf(TAG, "No Bluetooth Adapter Available"); } } /** * Get singleton instance. */ public static A2dpNativeInterface getInstance() { synchronized (INSTANCE_LOCK) { if (sInstance == null) { sInstance = new A2dpNativeInterface(); } return sInstance; } } /** * Initializes the native interface. * * @param codecConfigPriorities an array with the codec configuration * priorities to configure. */ public void init(BluetoothCodecConfig[] codecConfigPriorities) { initNative(codecConfigPriorities); } /** * Cleanup the native interface. */ public void cleanup() { cleanupNative(); } /** * Initiates A2DP connection to a remote device. * * @param device the remote device * @return true on success, otherwise false. */ public boolean connectA2dp(BluetoothDevice device) { return connectA2dpNative(getByteAddress(device)); } /** * Disconnects A2DP from a remote device. * * @param device the remote device * @return true on success, otherwise false. */ public boolean disconnectA2dp(BluetoothDevice device) { return disconnectA2dpNative(getByteAddress(device)); } /** * Sets the codec configuration preferences. * * @param codecConfigArray an array with the codec configurations to * configure. * @return true on success, otherwise false. */ public boolean setCodecConfigPreference(BluetoothCodecConfig[] codecConfigArray) { return setCodecConfigPreferenceNative(codecConfigArray); } private BluetoothDevice getDevice(byte[] address) { return mAdapter.getRemoteDevice(address); } private byte[] getByteAddress(BluetoothDevice device) { return Utils.getBytesFromAddress(device.getAddress()); } private void sendMessageToService(A2dpStackEvent event) { A2dpService service = A2dpService.getA2dpService(); if (service != null) { service.messageFromNative(event); } else { Log.w(TAG, "Event ignored, service not available: " + event); } } // Callbacks from the native stack back into the Java framework. // All callbacks are routed via the Service which will disambiguate which // state machine the message should be routed to. private void onConnectionStateChanged(int state, byte[] address) { A2dpStackEvent event = new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); event.valueInt = state; event.device = getDevice(address); if (DBG) { Log.d(TAG, "onConnectionStateChanged: " + event); } sendMessageToService(event); } private void onAudioStateChanged(int state, byte[] address) { A2dpStackEvent event = new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); event.valueInt = state; event.device = getDevice(address); if (DBG) { Log.d(TAG, "onAudioStateChanged: " + event); } sendMessageToService(event); } private void onCodecConfigChanged(BluetoothCodecConfig newCodecConfig, BluetoothCodecConfig[] codecsLocalCapabilities, BluetoothCodecConfig[] codecsSelectableCapabilities) { if (DBG) { Log.d(TAG, "onCodecConfigChanged: " + newCodecConfig); } // TODO: We need to use A2dpStackEvent instead of specialized service calls. A2dpService service = A2dpService.getA2dpService(); if (service != null) { service.onCodecConfigChangedFromNative(newCodecConfig, codecsLocalCapabilities, codecsSelectableCapabilities); } else { Log.w(TAG, "onCodecConfigChanged ignored: service not available"); } } // Native methods that call into the JNI interface private static native void classInitNative(); private native void initNative(BluetoothCodecConfig[] codecConfigPriorities); private native void cleanupNative(); private native boolean connectA2dpNative(byte[] address); private native boolean disconnectA2dpNative(byte[] address); private native boolean setCodecConfigPreferenceNative(BluetoothCodecConfig[] codecConfigArray); } android/app/src/com/android/bluetooth/a2dp/A2dpService.java +55 −3 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.HandlerThread; import android.os.ParcelUuid; import android.provider.Settings; import android.util.Log; Loading @@ -44,14 +45,20 @@ import java.util.Objects; * @hide */ public class A2dpService extends ProfileService { private static final boolean DBG = false; private static final boolean DBG = true; private static final String TAG = "A2dpService"; private HandlerThread mStateMachinesThread = null; private A2dpStateMachine mStateMachine; private Avrcp mAvrcp; private A2dpNativeInterface mA2dpNativeInterface = null; private BroadcastReceiver mConnectionStateChangedReceiver = null; public A2dpService() { mA2dpNativeInterface = A2dpNativeInterface.getInstance(); } private class CodecSupportReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Loading Loading @@ -113,8 +120,16 @@ public class A2dpService extends ProfileService { @Override protected boolean start() { if (DBG) { Log.d(TAG, "start()"); } mStateMachinesThread = new HandlerThread("A2dpService.StateMachines"); mStateMachinesThread.start(); mAvrcp = Avrcp.make(this); mStateMachine = A2dpStateMachine.make(this, this); mStateMachine = A2dpStateMachine.make(this, this, mA2dpNativeInterface, mStateMachinesThread.getLooper()); setA2dpService(this); if (mConnectionStateChangedReceiver == null) { IntentFilter filter = new IntentFilter(); Loading @@ -127,17 +142,29 @@ public class A2dpService extends ProfileService { @Override protected boolean stop() { if (DBG) { Log.d(TAG, "stop()"); } if (mStateMachine != null) { mStateMachine.doQuit(); } if (mAvrcp != null) { mAvrcp.doQuit(); } if (mStateMachinesThread != null) { mStateMachinesThread.quit(); mStateMachinesThread = null; } return true; } @Override protected boolean cleanup() { if (DBG) { Log.d(TAG, "cleanup()"); } if (mConnectionStateChangedReceiver != null) { unregisterReceiver(mConnectionStateChangedReceiver); mConnectionStateChangedReceiver = null; Loading Loading @@ -176,7 +203,7 @@ public class A2dpService extends ProfileService { private static synchronized void setA2dpService(A2dpService instance) { if (instance != null && instance.isAvailable()) { if (DBG) { Log.d(TAG, "setA2dpService(): set to: " + sA2dpService); Log.d(TAG, "setA2dpService(): set to: " + instance); } sA2dpService = instance; } else { Loading @@ -195,6 +222,10 @@ public class A2dpService extends ProfileService { } public boolean connect(BluetoothDevice device) { if (DBG) { Log.d(TAG, "connect(): " + device); } enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) { Loading @@ -218,6 +249,10 @@ public class A2dpService extends ProfileService { } boolean disconnect(BluetoothDevice device) { if (DBG) { Log.d(TAG, "disconnect(): " + device); } enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); int connectionState = mStateMachine.getConnectionState(device); if (connectionState != BluetoothProfile.STATE_CONNECTED Loading Loading @@ -362,6 +397,23 @@ public class A2dpService extends ProfileService { value); } // Handle messages from native (JNI) to Java void messageFromNative(A2dpStackEvent stackEvent) { if (DBG) { Log.d(TAG, "messageFromNative(): " + stackEvent); } mStateMachine.sendMessage(A2dpStateMachine.STACK_EVENT, stackEvent); } // TODO: This method should go away and should be replaced with // the messageFromNative(A2dpStackEvent) mechanism void onCodecConfigChangedFromNative(BluetoothCodecConfig newCodecConfig, BluetoothCodecConfig[] codecsLocalCapabilities, BluetoothCodecConfig[] codecsSelectableCapabilities) { mStateMachine.onCodecConfigChanged(newCodecConfig, codecsLocalCapabilities, codecsSelectableCapabilities); } //Binder object: Must be static class or memory leak may occur private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub implements IProfileServiceBinder { Loading android/app/src/com/android/bluetooth/a2dp/A2dpStackEvent.java 0 → 100644 +62 −0 Original line number Diff line number Diff line /* * Copyright 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.bluetooth.a2dp; import android.bluetooth.BluetoothDevice; /** * Stack event sent via a callback from JNI to Java, or generated * internally by the A2DP State Machine. */ public class A2dpStackEvent { // Event types for STACK_EVENT message (coming from native) private static final int EVENT_TYPE_NONE = 0; public static final int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; public static final int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; public int type = EVENT_TYPE_NONE; public int valueInt = 0; public BluetoothDevice device = null; A2dpStackEvent(int type) { this.type = type; } @Override public String toString() { // event dump StringBuilder result = new StringBuilder(); result.append("A2dpStackEvent {type:" + eventTypeToString(type)); result.append(", value1:" + valueInt); result.append(", device:" + device + "}"); return result.toString(); } // for debugging only private static String eventTypeToString(int type) { switch (type) { case EVENT_TYPE_NONE: return "EVENT_TYPE_NONE"; case EVENT_TYPE_CONNECTION_STATE_CHANGED: return "EVENT_TYPE_CONNECTION_STATE_CHANGED"; case EVENT_TYPE_AUDIO_STATE_CHANGED: return "EVENT_TYPE_AUDIO_STATE_CHANGED"; default: return "EVENT_TYPE_UNKNOWN:" + type; } } } android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java +176 −191 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
android/app/jni/com_android_bluetooth_a2dp.cpp +3 −3 Original line number Diff line number Diff line Loading @@ -393,8 +393,8 @@ static JNINativeMethod sMethods[] = { }; int register_com_android_bluetooth_a2dp(JNIEnv* env) { return jniRegisterNativeMethods(env, "com/android/bluetooth/a2dp/A2dpStateMachine", sMethods, NELEM(sMethods)); return jniRegisterNativeMethods( env, "com/android/bluetooth/a2dp/A2dpNativeInterface", sMethods, NELEM(sMethods)); } }
android/app/src/com/android/bluetooth/a2dp/A2dpNativeInterface.java 0 → 100644 +190 −0 Original line number Diff line number Diff line /* * Copyright 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Defines the native inteface that is used by state machine/service to * send or receive messages from the native stack. This file is registered * for the native methods in the corresponding JNI C++ file. */ package com.android.bluetooth.a2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothCodecConfig; import android.bluetooth.BluetoothDevice; import android.support.annotation.VisibleForTesting; import android.util.Log; import com.android.bluetooth.Utils; import com.android.internal.annotations.GuardedBy; /** * A2DP Native Interface to/from JNI. */ public class A2dpNativeInterface { private static final String TAG = "A2dpNativeInterface"; private static final boolean DBG = true; private BluetoothAdapter mAdapter; @GuardedBy("INSTANCE_LOCK") private static A2dpNativeInterface sInstance; private static final Object INSTANCE_LOCK = new Object(); static { classInitNative(); } @VisibleForTesting private A2dpNativeInterface() { mAdapter = BluetoothAdapter.getDefaultAdapter(); if (mAdapter == null) { Log.wtf(TAG, "No Bluetooth Adapter Available"); } } /** * Get singleton instance. */ public static A2dpNativeInterface getInstance() { synchronized (INSTANCE_LOCK) { if (sInstance == null) { sInstance = new A2dpNativeInterface(); } return sInstance; } } /** * Initializes the native interface. * * @param codecConfigPriorities an array with the codec configuration * priorities to configure. */ public void init(BluetoothCodecConfig[] codecConfigPriorities) { initNative(codecConfigPriorities); } /** * Cleanup the native interface. */ public void cleanup() { cleanupNative(); } /** * Initiates A2DP connection to a remote device. * * @param device the remote device * @return true on success, otherwise false. */ public boolean connectA2dp(BluetoothDevice device) { return connectA2dpNative(getByteAddress(device)); } /** * Disconnects A2DP from a remote device. * * @param device the remote device * @return true on success, otherwise false. */ public boolean disconnectA2dp(BluetoothDevice device) { return disconnectA2dpNative(getByteAddress(device)); } /** * Sets the codec configuration preferences. * * @param codecConfigArray an array with the codec configurations to * configure. * @return true on success, otherwise false. */ public boolean setCodecConfigPreference(BluetoothCodecConfig[] codecConfigArray) { return setCodecConfigPreferenceNative(codecConfigArray); } private BluetoothDevice getDevice(byte[] address) { return mAdapter.getRemoteDevice(address); } private byte[] getByteAddress(BluetoothDevice device) { return Utils.getBytesFromAddress(device.getAddress()); } private void sendMessageToService(A2dpStackEvent event) { A2dpService service = A2dpService.getA2dpService(); if (service != null) { service.messageFromNative(event); } else { Log.w(TAG, "Event ignored, service not available: " + event); } } // Callbacks from the native stack back into the Java framework. // All callbacks are routed via the Service which will disambiguate which // state machine the message should be routed to. private void onConnectionStateChanged(int state, byte[] address) { A2dpStackEvent event = new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); event.valueInt = state; event.device = getDevice(address); if (DBG) { Log.d(TAG, "onConnectionStateChanged: " + event); } sendMessageToService(event); } private void onAudioStateChanged(int state, byte[] address) { A2dpStackEvent event = new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); event.valueInt = state; event.device = getDevice(address); if (DBG) { Log.d(TAG, "onAudioStateChanged: " + event); } sendMessageToService(event); } private void onCodecConfigChanged(BluetoothCodecConfig newCodecConfig, BluetoothCodecConfig[] codecsLocalCapabilities, BluetoothCodecConfig[] codecsSelectableCapabilities) { if (DBG) { Log.d(TAG, "onCodecConfigChanged: " + newCodecConfig); } // TODO: We need to use A2dpStackEvent instead of specialized service calls. A2dpService service = A2dpService.getA2dpService(); if (service != null) { service.onCodecConfigChangedFromNative(newCodecConfig, codecsLocalCapabilities, codecsSelectableCapabilities); } else { Log.w(TAG, "onCodecConfigChanged ignored: service not available"); } } // Native methods that call into the JNI interface private static native void classInitNative(); private native void initNative(BluetoothCodecConfig[] codecConfigPriorities); private native void cleanupNative(); private native boolean connectA2dpNative(byte[] address); private native boolean disconnectA2dpNative(byte[] address); private native boolean setCodecConfigPreferenceNative(BluetoothCodecConfig[] codecConfigArray); }
android/app/src/com/android/bluetooth/a2dp/A2dpService.java +55 −3 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.HandlerThread; import android.os.ParcelUuid; import android.provider.Settings; import android.util.Log; Loading @@ -44,14 +45,20 @@ import java.util.Objects; * @hide */ public class A2dpService extends ProfileService { private static final boolean DBG = false; private static final boolean DBG = true; private static final String TAG = "A2dpService"; private HandlerThread mStateMachinesThread = null; private A2dpStateMachine mStateMachine; private Avrcp mAvrcp; private A2dpNativeInterface mA2dpNativeInterface = null; private BroadcastReceiver mConnectionStateChangedReceiver = null; public A2dpService() { mA2dpNativeInterface = A2dpNativeInterface.getInstance(); } private class CodecSupportReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Loading Loading @@ -113,8 +120,16 @@ public class A2dpService extends ProfileService { @Override protected boolean start() { if (DBG) { Log.d(TAG, "start()"); } mStateMachinesThread = new HandlerThread("A2dpService.StateMachines"); mStateMachinesThread.start(); mAvrcp = Avrcp.make(this); mStateMachine = A2dpStateMachine.make(this, this); mStateMachine = A2dpStateMachine.make(this, this, mA2dpNativeInterface, mStateMachinesThread.getLooper()); setA2dpService(this); if (mConnectionStateChangedReceiver == null) { IntentFilter filter = new IntentFilter(); Loading @@ -127,17 +142,29 @@ public class A2dpService extends ProfileService { @Override protected boolean stop() { if (DBG) { Log.d(TAG, "stop()"); } if (mStateMachine != null) { mStateMachine.doQuit(); } if (mAvrcp != null) { mAvrcp.doQuit(); } if (mStateMachinesThread != null) { mStateMachinesThread.quit(); mStateMachinesThread = null; } return true; } @Override protected boolean cleanup() { if (DBG) { Log.d(TAG, "cleanup()"); } if (mConnectionStateChangedReceiver != null) { unregisterReceiver(mConnectionStateChangedReceiver); mConnectionStateChangedReceiver = null; Loading Loading @@ -176,7 +203,7 @@ public class A2dpService extends ProfileService { private static synchronized void setA2dpService(A2dpService instance) { if (instance != null && instance.isAvailable()) { if (DBG) { Log.d(TAG, "setA2dpService(): set to: " + sA2dpService); Log.d(TAG, "setA2dpService(): set to: " + instance); } sA2dpService = instance; } else { Loading @@ -195,6 +222,10 @@ public class A2dpService extends ProfileService { } public boolean connect(BluetoothDevice device) { if (DBG) { Log.d(TAG, "connect(): " + device); } enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) { Loading @@ -218,6 +249,10 @@ public class A2dpService extends ProfileService { } boolean disconnect(BluetoothDevice device) { if (DBG) { Log.d(TAG, "disconnect(): " + device); } enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); int connectionState = mStateMachine.getConnectionState(device); if (connectionState != BluetoothProfile.STATE_CONNECTED Loading Loading @@ -362,6 +397,23 @@ public class A2dpService extends ProfileService { value); } // Handle messages from native (JNI) to Java void messageFromNative(A2dpStackEvent stackEvent) { if (DBG) { Log.d(TAG, "messageFromNative(): " + stackEvent); } mStateMachine.sendMessage(A2dpStateMachine.STACK_EVENT, stackEvent); } // TODO: This method should go away and should be replaced with // the messageFromNative(A2dpStackEvent) mechanism void onCodecConfigChangedFromNative(BluetoothCodecConfig newCodecConfig, BluetoothCodecConfig[] codecsLocalCapabilities, BluetoothCodecConfig[] codecsSelectableCapabilities) { mStateMachine.onCodecConfigChanged(newCodecConfig, codecsLocalCapabilities, codecsSelectableCapabilities); } //Binder object: Must be static class or memory leak may occur private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub implements IProfileServiceBinder { Loading
android/app/src/com/android/bluetooth/a2dp/A2dpStackEvent.java 0 → 100644 +62 −0 Original line number Diff line number Diff line /* * Copyright 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.bluetooth.a2dp; import android.bluetooth.BluetoothDevice; /** * Stack event sent via a callback from JNI to Java, or generated * internally by the A2DP State Machine. */ public class A2dpStackEvent { // Event types for STACK_EVENT message (coming from native) private static final int EVENT_TYPE_NONE = 0; public static final int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; public static final int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; public int type = EVENT_TYPE_NONE; public int valueInt = 0; public BluetoothDevice device = null; A2dpStackEvent(int type) { this.type = type; } @Override public String toString() { // event dump StringBuilder result = new StringBuilder(); result.append("A2dpStackEvent {type:" + eventTypeToString(type)); result.append(", value1:" + valueInt); result.append(", device:" + device + "}"); return result.toString(); } // for debugging only private static String eventTypeToString(int type) { switch (type) { case EVENT_TYPE_NONE: return "EVENT_TYPE_NONE"; case EVENT_TYPE_CONNECTION_STATE_CHANGED: return "EVENT_TYPE_CONNECTION_STATE_CHANGED"; case EVENT_TYPE_AUDIO_STATE_CHANGED: return "EVENT_TYPE_AUDIO_STATE_CHANGED"; default: return "EVENT_TYPE_UNKNOWN:" + type; } } }
android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java +176 −191 File changed.Preview size limit exceeded, changes collapsed. Show changes