Loading android/app/jni/com_android_bluetooth_a2dp.cpp +45 −33 Original line number Diff line number Diff line Loading @@ -51,8 +51,8 @@ static std::shared_timed_mutex interface_mutex; static jobject mCallbacksObj = nullptr; static std::shared_timed_mutex callbacks_mutex; static void bta2dp_connection_state_callback(btav_connection_state_t state, RawAddress* bd_addr) { static void bta2dp_connection_state_callback(RawAddress* bd_addr, btav_connection_state_t state) { ALOGI("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); Loading @@ -62,18 +62,18 @@ static void bta2dp_connection_state_callback(btav_connection_state_t state, ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for connection state"); ALOGE("%s: Fail to new jbyteArray bd addr", __func__); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, (jint)state, addr.get()); addr.get(), (jint)state); } static void bta2dp_audio_state_callback(btav_audio_state_t state, RawAddress* bd_addr) { static void bta2dp_audio_state_callback(RawAddress* bd_addr, btav_audio_state_t state) { ALOGI("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); Loading @@ -83,18 +83,18 @@ static void bta2dp_audio_state_callback(btav_audio_state_t state, ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for connection state"); ALOGE("%s: Fail to new jbyteArray bd addr", __func__); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged, (jint)state, addr.get()); addr.get(), (jint)state); } static void bta2dp_audio_config_callback( btav_a2dp_codec_config_t codec_config, RawAddress* bd_addr, btav_a2dp_codec_config_t codec_config, std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities, std::vector<btav_a2dp_codec_config_t> codecs_selectable_capabilities) { ALOGI("%s", __func__); Loading Loading @@ -146,9 +146,18 @@ static void bta2dp_audio_config_callback( sCallbackEnv->DeleteLocalRef(capObj); } sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onCodecConfigChanged, codecConfigObj, local_capabilities_array, selectable_capabilities_array); ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(RawAddress::kLength)); if (!addr.get()) { ALOGE("%s: Fail to new jbyteArray bd addr", __func__); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, RawAddress::kLength, (jbyte*)bd_addr->address); sCallbackEnv->CallVoidMethod( mCallbacksObj, method_onCodecConfigChanged, addr.get(), codecConfigObj, local_capabilities_array, selectable_capabilities_array); } static btav_source_callbacks_t sBluetoothA2dpCallbacks = { Loading Loading @@ -181,15 +190,16 @@ static void classInitNative(JNIEnv* env, jclass clazz) { jniBluetoothCodecConfigClass, "getCodecSpecific4", "()J"); method_onConnectionStateChanged = env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V"); env->GetMethodID(clazz, "onConnectionStateChanged", "([BI)V"); method_onAudioStateChanged = env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V"); env->GetMethodID(clazz, "onAudioStateChanged", "([BI)V"); method_onCodecConfigChanged = env->GetMethodID( clazz, "onCodecConfigChanged", "(Landroid/bluetooth/BluetoothCodecConfig;[Landroid/bluetooth/" "BluetoothCodecConfig;[Landroid/bluetooth/BluetoothCodecConfig;)V"); method_onCodecConfigChanged = env->GetMethodID(clazz, "onCodecConfigChanged", "([BLandroid/bluetooth/BluetoothCodecConfig;" "[Landroid/bluetooth/BluetoothCodecConfig;" "[Landroid/bluetooth/BluetoothCodecConfig;)V"); ALOGI("%s: succeeds", __func__); } Loading @@ -204,7 +214,7 @@ static std::vector<btav_a2dp_codec_config_t> prepareCodecPreferences( if (jcodecConfig == nullptr) continue; if (!env->IsInstanceOf(jcodecConfig, android_bluetooth_BluetoothCodecConfig.clazz)) { ALOGE("Invalid BluetoothCodecConfig instance"); ALOGE("%s: Invalid BluetoothCodecConfig instance", __func__); continue; } jint codecType = env->CallIntMethod( Loading Loading @@ -252,31 +262,32 @@ static void initNative(JNIEnv* env, jobject object, const bt_interface_t* btInf = getBluetoothInterface(); if (btInf == nullptr) { ALOGE("Bluetooth module is not loaded"); ALOGE("%s: Bluetooth module is not loaded", __func__); return; } if (sBluetoothA2dpInterface != nullptr) { ALOGW("Cleaning up A2DP Interface before initializing..."); ALOGW("%s: Cleaning up A2DP Interface before initializing...", __func__); sBluetoothA2dpInterface->cleanup(); sBluetoothA2dpInterface = nullptr; } if (mCallbacksObj != nullptr) { ALOGW("Cleaning up A2DP callback object"); ALOGW("%s: Cleaning up A2DP callback object", __func__); env->DeleteGlobalRef(mCallbacksObj); mCallbacksObj = nullptr; } if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) { ALOGE("Failed to allocate Global Ref for A2DP Callbacks"); ALOGE("%s: Failed to allocate Global Ref for A2DP Callbacks", __func__); return; } android_bluetooth_BluetoothCodecConfig.clazz = (jclass)env->NewGlobalRef( env->FindClass("android/bluetooth/BluetoothCodecConfig")); if (android_bluetooth_BluetoothCodecConfig.clazz == nullptr) { ALOGE("Failed to allocate Global Ref for BluetoothCodecConfig class"); ALOGE("%s: Failed to allocate Global Ref for BluetoothCodecConfig class", __func__); return; } Loading @@ -284,7 +295,7 @@ static void initNative(JNIEnv* env, jobject object, (btav_source_interface_t*)btInf->get_profile_interface( BT_PROFILE_ADVANCED_AUDIO_ID); if (sBluetoothA2dpInterface == nullptr) { ALOGE("Failed to get Bluetooth A2DP Interface"); ALOGE("%s: Failed to get Bluetooth A2DP Interface", __func__); return; } Loading @@ -294,7 +305,8 @@ static void initNative(JNIEnv* env, jobject object, bt_status_t status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks, codec_priorities); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed to initialize Bluetooth A2DP, status: %d", status); ALOGE("%s: Failed to initialize Bluetooth A2DP, status: %d", __func__, status); sBluetoothA2dpInterface = nullptr; return; } Loading @@ -306,7 +318,7 @@ static void cleanupNative(JNIEnv* env, jobject object) { const bt_interface_t* btInf = getBluetoothInterface(); if (btInf == nullptr) { ALOGE("Bluetooth module is not loaded"); ALOGE("%s: Bluetooth module is not loaded", __func__); return; } Loading Loading @@ -338,7 +350,7 @@ static jboolean connectA2dpNative(JNIEnv* env, jobject object, bt_status_t status = sBluetoothA2dpInterface->connect((RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed HF connection, status: %d", status); ALOGE("%s: Failed HF connection, status: %d", __func__, status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; Loading @@ -358,7 +370,7 @@ static jboolean disconnectA2dpNative(JNIEnv* env, jobject object, bt_status_t status = sBluetoothA2dpInterface->disconnect((RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed HF disconnection, status: %d", status); ALOGE("%s: Failed HF disconnection, status: %d", __func__, status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; Loading @@ -375,7 +387,7 @@ static jboolean setCodecConfigPreferenceNative(JNIEnv* env, jobject object, bt_status_t status = sBluetoothA2dpInterface->config_codec(codec_preferences); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed codec configuration, status: %d", status); ALOGE("%s: Failed codec configuration, status: %d", __func__, status); } return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } Loading @@ -393,8 +405,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/jni/com_android_bluetooth_a2dp_sink.cpp +8 −8 Original line number Diff line number Diff line Loading @@ -33,8 +33,8 @@ static jmethodID method_onAudioConfigChanged; static const btav_sink_interface_t* sBluetoothA2dpInterface = NULL; static jobject mCallbacksObj = NULL; static void bta2dp_connection_state_callback(btav_connection_state_t state, RawAddress* bd_addr) { static void bta2dp_connection_state_callback(RawAddress* bd_addr, btav_connection_state_t state) { ALOGI("%s", __func__); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; Loading @@ -49,11 +49,11 @@ static void bta2dp_connection_state_callback(btav_connection_state_t state, sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, (jint)state, addr.get()); addr.get(), (jint)state); } static void bta2dp_audio_state_callback(btav_audio_state_t state, RawAddress* bd_addr) { static void bta2dp_audio_state_callback(RawAddress* bd_addr, btav_audio_state_t state) { ALOGI("%s", __func__); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; Loading @@ -68,7 +68,7 @@ static void bta2dp_audio_state_callback(btav_audio_state_t state, sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged, (jint)state, addr.get()); addr.get(), (jint)state); } static void bta2dp_audio_config_callback(RawAddress* bd_addr, Loading Loading @@ -99,10 +99,10 @@ static btav_sink_callbacks_t sBluetoothA2dpCallbacks = { static void classInitNative(JNIEnv* env, jclass clazz) { method_onConnectionStateChanged = env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V"); env->GetMethodID(clazz, "onConnectionStateChanged", "([BI)V"); method_onAudioStateChanged = env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V"); env->GetMethodID(clazz, "onAudioStateChanged", "([BI)V"); method_onAudioConfigChanged = env->GetMethodID(clazz, "onAudioConfigChanged", "([BII)V"); Loading android/app/jni/permission_helpers.cc +6 −4 Original line number Diff line number Diff line Loading @@ -63,10 +63,14 @@ bool isCallerActiveUserOrManagedProfile() { uid_t callingUser = callingUid / PER_USER_RANGE; // if (!callingUid) return true; // It's a local call if ((foregroundUserId == callingUser) || (systemUiUid == callingUid) || (SYSTEM_UID == callingUid)) return true; uid_t parentUser = callingUser; sp<IServiceManager> sm = defaultServiceManager(); sp<IBinder> binder = sm->getService(String16("users")); sp<IBinder> binder = sm->getService(String16("user")); sp<IUserManager> um = interface_cast<IUserManager>(binder); if (um != NULL) { // Must use Bluetooth process identity when making call to get parent user Loading @@ -75,9 +79,7 @@ bool isCallerActiveUserOrManagedProfile() { ipcState->restoreCallingIdentity(ident); } return (foregroundUserId == callingUser) || (foregroundUserId == parentUser) || (systemUiUid == callingUid) || (SYSTEM_UID == callingUid); return foregroundUserId == parentUser; } } // namespace bluetooth Loading android/app/src/com/android/bluetooth/a2dp/A2dpNativeInterface.java 0 → 100644 +189 −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.BluetoothCodecStatus; 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(byte[] address, int state) { A2dpStackEvent event = new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); event.device = getDevice(address); event.valueInt = state; if (DBG) { Log.d(TAG, "onConnectionStateChanged: " + event); } sendMessageToService(event); } private void onAudioStateChanged(byte[] address, int state) { A2dpStackEvent event = new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); event.device = getDevice(address); event.valueInt = state; if (DBG) { Log.d(TAG, "onAudioStateChanged: " + event); } sendMessageToService(event); } private void onCodecConfigChanged(byte[] address, BluetoothCodecConfig newCodecConfig, BluetoothCodecConfig[] codecsLocalCapabilities, BluetoothCodecConfig[] codecsSelectableCapabilities) { A2dpStackEvent event = new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_CODEC_CONFIG_CHANGED); event.device = getDevice(address); event.codecStatus = new BluetoothCodecStatus(newCodecConfig, codecsLocalCapabilities, codecsSelectableCapabilities); if (DBG) { Log.d(TAG, "onCodecConfigChanged: " + event); } sendMessageToService(event); } // 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 +44 −4 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,11 @@ public class A2dpService extends ProfileService { value); } // Handle messages from native (JNI) to Java void messageFromNative(A2dpStackEvent stackEvent) { mStateMachine.sendMessage(A2dpStateMachine.STACK_EVENT, stackEvent); } // Binder object: Must be static class or memory leak may occur private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub implements IProfileServiceBinder { Loading Loading
android/app/jni/com_android_bluetooth_a2dp.cpp +45 −33 Original line number Diff line number Diff line Loading @@ -51,8 +51,8 @@ static std::shared_timed_mutex interface_mutex; static jobject mCallbacksObj = nullptr; static std::shared_timed_mutex callbacks_mutex; static void bta2dp_connection_state_callback(btav_connection_state_t state, RawAddress* bd_addr) { static void bta2dp_connection_state_callback(RawAddress* bd_addr, btav_connection_state_t state) { ALOGI("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); Loading @@ -62,18 +62,18 @@ static void bta2dp_connection_state_callback(btav_connection_state_t state, ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for connection state"); ALOGE("%s: Fail to new jbyteArray bd addr", __func__); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, (jint)state, addr.get()); addr.get(), (jint)state); } static void bta2dp_audio_state_callback(btav_audio_state_t state, RawAddress* bd_addr) { static void bta2dp_audio_state_callback(RawAddress* bd_addr, btav_audio_state_t state) { ALOGI("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); Loading @@ -83,18 +83,18 @@ static void bta2dp_audio_state_callback(btav_audio_state_t state, ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for connection state"); ALOGE("%s: Fail to new jbyteArray bd addr", __func__); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged, (jint)state, addr.get()); addr.get(), (jint)state); } static void bta2dp_audio_config_callback( btav_a2dp_codec_config_t codec_config, RawAddress* bd_addr, btav_a2dp_codec_config_t codec_config, std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities, std::vector<btav_a2dp_codec_config_t> codecs_selectable_capabilities) { ALOGI("%s", __func__); Loading Loading @@ -146,9 +146,18 @@ static void bta2dp_audio_config_callback( sCallbackEnv->DeleteLocalRef(capObj); } sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onCodecConfigChanged, codecConfigObj, local_capabilities_array, selectable_capabilities_array); ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(RawAddress::kLength)); if (!addr.get()) { ALOGE("%s: Fail to new jbyteArray bd addr", __func__); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, RawAddress::kLength, (jbyte*)bd_addr->address); sCallbackEnv->CallVoidMethod( mCallbacksObj, method_onCodecConfigChanged, addr.get(), codecConfigObj, local_capabilities_array, selectable_capabilities_array); } static btav_source_callbacks_t sBluetoothA2dpCallbacks = { Loading Loading @@ -181,15 +190,16 @@ static void classInitNative(JNIEnv* env, jclass clazz) { jniBluetoothCodecConfigClass, "getCodecSpecific4", "()J"); method_onConnectionStateChanged = env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V"); env->GetMethodID(clazz, "onConnectionStateChanged", "([BI)V"); method_onAudioStateChanged = env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V"); env->GetMethodID(clazz, "onAudioStateChanged", "([BI)V"); method_onCodecConfigChanged = env->GetMethodID( clazz, "onCodecConfigChanged", "(Landroid/bluetooth/BluetoothCodecConfig;[Landroid/bluetooth/" "BluetoothCodecConfig;[Landroid/bluetooth/BluetoothCodecConfig;)V"); method_onCodecConfigChanged = env->GetMethodID(clazz, "onCodecConfigChanged", "([BLandroid/bluetooth/BluetoothCodecConfig;" "[Landroid/bluetooth/BluetoothCodecConfig;" "[Landroid/bluetooth/BluetoothCodecConfig;)V"); ALOGI("%s: succeeds", __func__); } Loading @@ -204,7 +214,7 @@ static std::vector<btav_a2dp_codec_config_t> prepareCodecPreferences( if (jcodecConfig == nullptr) continue; if (!env->IsInstanceOf(jcodecConfig, android_bluetooth_BluetoothCodecConfig.clazz)) { ALOGE("Invalid BluetoothCodecConfig instance"); ALOGE("%s: Invalid BluetoothCodecConfig instance", __func__); continue; } jint codecType = env->CallIntMethod( Loading Loading @@ -252,31 +262,32 @@ static void initNative(JNIEnv* env, jobject object, const bt_interface_t* btInf = getBluetoothInterface(); if (btInf == nullptr) { ALOGE("Bluetooth module is not loaded"); ALOGE("%s: Bluetooth module is not loaded", __func__); return; } if (sBluetoothA2dpInterface != nullptr) { ALOGW("Cleaning up A2DP Interface before initializing..."); ALOGW("%s: Cleaning up A2DP Interface before initializing...", __func__); sBluetoothA2dpInterface->cleanup(); sBluetoothA2dpInterface = nullptr; } if (mCallbacksObj != nullptr) { ALOGW("Cleaning up A2DP callback object"); ALOGW("%s: Cleaning up A2DP callback object", __func__); env->DeleteGlobalRef(mCallbacksObj); mCallbacksObj = nullptr; } if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) { ALOGE("Failed to allocate Global Ref for A2DP Callbacks"); ALOGE("%s: Failed to allocate Global Ref for A2DP Callbacks", __func__); return; } android_bluetooth_BluetoothCodecConfig.clazz = (jclass)env->NewGlobalRef( env->FindClass("android/bluetooth/BluetoothCodecConfig")); if (android_bluetooth_BluetoothCodecConfig.clazz == nullptr) { ALOGE("Failed to allocate Global Ref for BluetoothCodecConfig class"); ALOGE("%s: Failed to allocate Global Ref for BluetoothCodecConfig class", __func__); return; } Loading @@ -284,7 +295,7 @@ static void initNative(JNIEnv* env, jobject object, (btav_source_interface_t*)btInf->get_profile_interface( BT_PROFILE_ADVANCED_AUDIO_ID); if (sBluetoothA2dpInterface == nullptr) { ALOGE("Failed to get Bluetooth A2DP Interface"); ALOGE("%s: Failed to get Bluetooth A2DP Interface", __func__); return; } Loading @@ -294,7 +305,8 @@ static void initNative(JNIEnv* env, jobject object, bt_status_t status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks, codec_priorities); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed to initialize Bluetooth A2DP, status: %d", status); ALOGE("%s: Failed to initialize Bluetooth A2DP, status: %d", __func__, status); sBluetoothA2dpInterface = nullptr; return; } Loading @@ -306,7 +318,7 @@ static void cleanupNative(JNIEnv* env, jobject object) { const bt_interface_t* btInf = getBluetoothInterface(); if (btInf == nullptr) { ALOGE("Bluetooth module is not loaded"); ALOGE("%s: Bluetooth module is not loaded", __func__); return; } Loading Loading @@ -338,7 +350,7 @@ static jboolean connectA2dpNative(JNIEnv* env, jobject object, bt_status_t status = sBluetoothA2dpInterface->connect((RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed HF connection, status: %d", status); ALOGE("%s: Failed HF connection, status: %d", __func__, status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; Loading @@ -358,7 +370,7 @@ static jboolean disconnectA2dpNative(JNIEnv* env, jobject object, bt_status_t status = sBluetoothA2dpInterface->disconnect((RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed HF disconnection, status: %d", status); ALOGE("%s: Failed HF disconnection, status: %d", __func__, status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; Loading @@ -375,7 +387,7 @@ static jboolean setCodecConfigPreferenceNative(JNIEnv* env, jobject object, bt_status_t status = sBluetoothA2dpInterface->config_codec(codec_preferences); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed codec configuration, status: %d", status); ALOGE("%s: Failed codec configuration, status: %d", __func__, status); } return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } Loading @@ -393,8 +405,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/jni/com_android_bluetooth_a2dp_sink.cpp +8 −8 Original line number Diff line number Diff line Loading @@ -33,8 +33,8 @@ static jmethodID method_onAudioConfigChanged; static const btav_sink_interface_t* sBluetoothA2dpInterface = NULL; static jobject mCallbacksObj = NULL; static void bta2dp_connection_state_callback(btav_connection_state_t state, RawAddress* bd_addr) { static void bta2dp_connection_state_callback(RawAddress* bd_addr, btav_connection_state_t state) { ALOGI("%s", __func__); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; Loading @@ -49,11 +49,11 @@ static void bta2dp_connection_state_callback(btav_connection_state_t state, sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, (jint)state, addr.get()); addr.get(), (jint)state); } static void bta2dp_audio_state_callback(btav_audio_state_t state, RawAddress* bd_addr) { static void bta2dp_audio_state_callback(RawAddress* bd_addr, btav_audio_state_t state) { ALOGI("%s", __func__); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; Loading @@ -68,7 +68,7 @@ static void bta2dp_audio_state_callback(btav_audio_state_t state, sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged, (jint)state, addr.get()); addr.get(), (jint)state); } static void bta2dp_audio_config_callback(RawAddress* bd_addr, Loading Loading @@ -99,10 +99,10 @@ static btav_sink_callbacks_t sBluetoothA2dpCallbacks = { static void classInitNative(JNIEnv* env, jclass clazz) { method_onConnectionStateChanged = env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V"); env->GetMethodID(clazz, "onConnectionStateChanged", "([BI)V"); method_onAudioStateChanged = env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V"); env->GetMethodID(clazz, "onAudioStateChanged", "([BI)V"); method_onAudioConfigChanged = env->GetMethodID(clazz, "onAudioConfigChanged", "([BII)V"); Loading
android/app/jni/permission_helpers.cc +6 −4 Original line number Diff line number Diff line Loading @@ -63,10 +63,14 @@ bool isCallerActiveUserOrManagedProfile() { uid_t callingUser = callingUid / PER_USER_RANGE; // if (!callingUid) return true; // It's a local call if ((foregroundUserId == callingUser) || (systemUiUid == callingUid) || (SYSTEM_UID == callingUid)) return true; uid_t parentUser = callingUser; sp<IServiceManager> sm = defaultServiceManager(); sp<IBinder> binder = sm->getService(String16("users")); sp<IBinder> binder = sm->getService(String16("user")); sp<IUserManager> um = interface_cast<IUserManager>(binder); if (um != NULL) { // Must use Bluetooth process identity when making call to get parent user Loading @@ -75,9 +79,7 @@ bool isCallerActiveUserOrManagedProfile() { ipcState->restoreCallingIdentity(ident); } return (foregroundUserId == callingUser) || (foregroundUserId == parentUser) || (systemUiUid == callingUid) || (SYSTEM_UID == callingUid); return foregroundUserId == parentUser; } } // namespace bluetooth Loading
android/app/src/com/android/bluetooth/a2dp/A2dpNativeInterface.java 0 → 100644 +189 −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.BluetoothCodecStatus; 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(byte[] address, int state) { A2dpStackEvent event = new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); event.device = getDevice(address); event.valueInt = state; if (DBG) { Log.d(TAG, "onConnectionStateChanged: " + event); } sendMessageToService(event); } private void onAudioStateChanged(byte[] address, int state) { A2dpStackEvent event = new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); event.device = getDevice(address); event.valueInt = state; if (DBG) { Log.d(TAG, "onAudioStateChanged: " + event); } sendMessageToService(event); } private void onCodecConfigChanged(byte[] address, BluetoothCodecConfig newCodecConfig, BluetoothCodecConfig[] codecsLocalCapabilities, BluetoothCodecConfig[] codecsSelectableCapabilities) { A2dpStackEvent event = new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_CODEC_CONFIG_CHANGED); event.device = getDevice(address); event.codecStatus = new BluetoothCodecStatus(newCodecConfig, codecsLocalCapabilities, codecsSelectableCapabilities); if (DBG) { Log.d(TAG, "onCodecConfigChanged: " + event); } sendMessageToService(event); } // 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 +44 −4 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,11 @@ public class A2dpService extends ProfileService { value); } // Handle messages from native (JNI) to Java void messageFromNative(A2dpStackEvent stackEvent) { mStateMachine.sendMessage(A2dpStateMachine.STACK_EVENT, stackEvent); } // Binder object: Must be static class or memory leak may occur private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub implements IProfileServiceBinder { Loading