Loading android/app/jni/com_android_bluetooth_avrcp_target.cpp +64 −12 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ #include <base/bind.h> #include <map> #include <mutex> #include <shared_mutex> #include <vector> #include "android_runtime/AndroidRuntime.h" Loading @@ -33,6 +35,8 @@ namespace android { static MediaCallbacks* mServiceCallbacks; static ServiceInterface* sServiceInterface; static jobject mJavaInterface; static std::shared_timed_mutex interface_mutex; static std::shared_timed_mutex callbacks_mutex; // Forward Declarations static void sendMediaKeyEvent(int, int); Loading Loading @@ -204,6 +208,8 @@ static void classInitNative(JNIEnv* env, jclass clazz) { static void initNative(JNIEnv* env, jobject object) { ALOGD("%s", __func__); std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex); std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex); mJavaInterface = env->NewGlobalRef(object); sServiceInterface = getBluetoothInterface()->get_avrcp_service(); Loading @@ -214,7 +220,12 @@ static void sendMediaUpdateNative(JNIEnv* env, jobject object, jboolean metadata, jboolean state, jboolean queue) { ALOGD("%s", __func__); CHECK(mServiceCallbacks); std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex); if (mServiceCallbacks == nullptr) { ALOGW("%s: Service not loaded.", __func__); return; } mServiceCallbacks->SendMediaUpdate(metadata == JNI_TRUE, state == JNI_TRUE, queue == JNI_TRUE); } Loading @@ -223,13 +234,21 @@ static void sendFolderUpdateNative(JNIEnv* env, jobject object, jboolean available_players, jboolean addressed_player, jboolean uids) { ALOGD("%s", __func__); CHECK(mServiceCallbacks); std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex); if (mServiceCallbacks == nullptr) { ALOGW("%s: Service not loaded.", __func__); return; } mServiceCallbacks->SendFolderUpdate(available_players == JNI_TRUE, addressed_player == JNI_TRUE, uids == JNI_TRUE); } static void cleanupNative(JNIEnv* env, jobject object) { std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex); std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex); sServiceInterface->Cleanup(); env->DeleteGlobalRef(mJavaInterface); mJavaInterface = nullptr; Loading @@ -239,7 +258,11 @@ static void cleanupNative(JNIEnv* env, jobject object) { jboolean connectDeviceNative(JNIEnv* env, jobject object, jstring address) { ALOGD("%s", __func__); if (!sServiceInterface) return JNI_FALSE; std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex); if (mServiceCallbacks == nullptr) { ALOGW("%s: Service not loaded.", __func__); return JNI_FALSE; } const char* tmp_addr = env->GetStringUTFChars(address, 0); RawAddress bdaddr; Loading @@ -254,7 +277,11 @@ jboolean connectDeviceNative(JNIEnv* env, jobject object, jstring address) { jboolean disconnectDeviceNative(JNIEnv* env, jobject object, jstring address) { ALOGD("%s", __func__); if (!sServiceInterface) return JNI_FALSE; std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex); if (mServiceCallbacks == nullptr) { ALOGW("%s: Service not loaded.", __func__); return JNI_FALSE; } const char* tmp_addr = env->GetStringUTFChars(address, 0); RawAddress bdaddr; Loading @@ -269,8 +296,9 @@ jboolean disconnectDeviceNative(JNIEnv* env, jobject object, jstring address) { static void sendMediaKeyEvent(int key, int state) { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!sCallbackEnv.valid() || !mJavaInterface) return; sCallbackEnv->CallVoidMethod(mJavaInterface, method_sendMediaKeyEvent, key, state); } Loading Loading @@ -393,7 +421,10 @@ static FolderInfo getFolderInfoFromJavaObj(JNIEnv* env, jobject folder) { static SongInfo getSongInfo() { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mJavaInterface) return SongInfo(); jobject metadata = sCallbackEnv->CallObjectMethod(mJavaInterface, method_getCurrentSongInfo); return getSongInfoFromJavaObj(sCallbackEnv.get(), metadata); Loading @@ -401,11 +432,11 @@ static SongInfo getSongInfo() { static PlayStatus getCurrentPlayStatus() { ALOGD("%s", __func__); PlayStatus status; std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return status; if (!sCallbackEnv.valid() || !mJavaInterface) return PlayStatus(); PlayStatus status; jobject playStatus = sCallbackEnv->CallObjectMethod(mJavaInterface, method_getPlaybackStatus); jclass class_playStatus = sCallbackEnv->GetObjectClass(playStatus); Loading @@ -425,8 +456,9 @@ static PlayStatus getCurrentPlayStatus() { static std::string getCurrentMediaId() { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return ""; if (!sCallbackEnv.valid() || !mJavaInterface) return ""; jstring media_id = (jstring)sCallbackEnv->CallObjectMethod( mJavaInterface, method_getCurrentMediaId); Loading @@ -440,8 +472,9 @@ static std::string getCurrentMediaId() { static std::vector<SongInfo> getNowPlayingList() { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return std::vector<SongInfo>(); if (!sCallbackEnv.valid() || !mJavaInterface) return std::vector<SongInfo>(); jobject song_list = sCallbackEnv->CallObjectMethod(mJavaInterface, method_getNowPlayingList); Loading @@ -468,9 +501,9 @@ static std::vector<SongInfo> getNowPlayingList() { static uint16_t getCurrentPlayerId() { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return 0u; if (!sCallbackEnv.valid() || !mJavaInterface) return 0u; jint id = sCallbackEnv->CallIntMethod(mJavaInterface, method_getCurrentPlayerId); Loading @@ -480,7 +513,10 @@ static uint16_t getCurrentPlayerId() { static std::vector<MediaPlayerInfo> getMediaPlayerList() { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mJavaInterface) return std::vector<MediaPlayerInfo>(); jobject player_list = (jobject)sCallbackEnv->CallObjectMethod( mJavaInterface, method_getMediaPlayerList); Loading Loading @@ -535,7 +571,9 @@ SetBrowsedPlayerCb set_browsed_player_cb; static void setBrowsedPlayer(uint16_t player_id, SetBrowsedPlayerCb cb) { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mJavaInterface) return; set_browsed_player_cb = cb; sCallbackEnv->CallVoidMethod(mJavaInterface, method_setBrowsedPlayer, Loading Loading @@ -631,7 +669,9 @@ static void getFolderItemsResponseNative(JNIEnv* env, jobject object, static void getFolderItems(uint16_t player_id, std::string media_id, GetFolderItemsCb cb) { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mJavaInterface) return; // TODO (apanicke): Fix a potential media_id collision if two media players // use the same media_id scheme or two devices browse the same content. Loading @@ -645,7 +685,9 @@ static void getFolderItems(uint16_t player_id, std::string media_id, static void playItem(uint16_t player_id, bool now_playing, std::string media_id) { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mJavaInterface) return; jstring j_media_id = sCallbackEnv->NewStringUTF(media_id.c_str()); sCallbackEnv->CallVoidMethod(mJavaInterface, method_playItem, player_id, Loading @@ -654,7 +696,9 @@ static void playItem(uint16_t player_id, bool now_playing, static void setActiveDevice(const RawAddress& address) { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mJavaInterface) return; jstring j_bdaddr = sCallbackEnv->NewStringUTF(address.ToString().c_str()); sCallbackEnv->CallVoidMethod(mJavaInterface, method_setActiveDevice, Loading @@ -663,7 +707,9 @@ static void setActiveDevice(const RawAddress& address) { static void volumeDeviceConnected(const RawAddress& address) { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mJavaInterface) return; jstring j_bdaddr = sCallbackEnv->NewStringUTF(address.ToString().c_str()); sCallbackEnv->CallVoidMethod(mJavaInterface, method_volumeDeviceConnected, Loading @@ -677,7 +723,9 @@ static void volumeDeviceConnected( const RawAddress& address, ::bluetooth::avrcp::VolumeInterface::VolumeChangedCb cb) { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mJavaInterface) return; volumeCallbackMap.emplace(address, cb); Loading @@ -688,7 +736,9 @@ static void volumeDeviceConnected( static void volumeDeviceDisconnected(const RawAddress& address) { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mJavaInterface) return; volumeCallbackMap.erase(address); Loading @@ -706,7 +756,9 @@ static void sendVolumeChangedNative(JNIEnv* env, jobject object, jint volume) { static void setVolume(int8_t volume) { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mJavaInterface) return; sCallbackEnv->CallVoidMethod(mJavaInterface, method_setVolume, volume); } Loading android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java +10 −0 Original line number Diff line number Diff line Loading @@ -81,6 +81,12 @@ public class HeadsetClientService extends ProfileService { // Setup the JNI service NativeInterface.initializeNative(); mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); if (mAudioManager == null) { Log.e(TAG, "AudioManager service doesn't exist?"); } else { // start AudioManager in a known state mAudioManager.setParameters("hfp_enable=false"); } mSmFactory = new HeadsetClientStateMachineFactory(); mStateMachineMap.clear(); Loading Loading @@ -923,4 +929,8 @@ public class HeadsetClientService extends ProfileService { protected void setSMFactory(HeadsetClientStateMachineFactory factory) { mSmFactory = factory; } AudioManager getAudioManager() { return mAudioManager; } } android/app/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java +54 −23 Original line number Diff line number Diff line Loading @@ -39,8 +39,9 @@ import android.bluetooth.BluetoothHeadsetClient; import android.bluetooth.BluetoothHeadsetClientCall; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; import android.content.Context; import android.content.Intent; import android.media.AudioAttributes; import android.media.AudioFocusRequest; import android.media.AudioManager; import android.os.Bundle; import android.os.Looper; Loading Loading @@ -156,7 +157,6 @@ public class HeadsetClientStateMachine extends StateMachine { // indicator private Pair<Integer, Object> mPendingAction; private static AudioManager sAudioManager; private int mAudioState; private boolean mAudioWbs; private int mVoiceRecognitionActive; Loading @@ -169,6 +169,11 @@ public class HeadsetClientStateMachine extends StateMachine { private int mPeerFeatures; private int mChldFeatures; // This is returned when requesting focus from AudioManager private AudioFocusRequest mAudioFocusRequest; private AudioManager mAudioManager; // Accessor for the states, useful for reusing the state machines public IState getDisconnectedState() { return mDisconnected; Loading Loading @@ -690,13 +695,9 @@ public class HeadsetClientStateMachine extends StateMachine { HeadsetClientStateMachine(HeadsetClientService context, Looper looper) { super(TAG, looper); mService = context; mAudioManager = mService.getAudioManager(); mAdapter = BluetoothAdapter.getDefaultAdapter(); if (sAudioManager == null) { sAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); // Initialize hfp_enable into a known state. routeHfpAudio(false); } mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; mAudioWbs = false; mVoiceRecognitionActive = HeadsetClientHalConstants.VR_STATE_STOPPED; Loading @@ -706,8 +707,8 @@ public class HeadsetClientStateMachine extends StateMachine { mIndicatorNetworkSignal = 0; mIndicatorBatteryLevel = 0; sMaxAmVcVol = sAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL); sMinAmVcVol = sAudioManager.getStreamMinVolume(AudioManager.STREAM_VOICE_CALL); sMaxAmVcVol = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL); sMinAmVcVol = mAudioManager.getStreamMinVolume(AudioManager.STREAM_VOICE_CALL); mOperatorName = null; mSubscriberInfo = null; Loading Loading @@ -740,26 +741,53 @@ public class HeadsetClientStateMachine extends StateMachine { return hfcsm; } static synchronized void routeHfpAudio(boolean enable) { synchronized void routeHfpAudio(boolean enable) { if (mAudioManager == null) { Log.e(TAG, "AudioManager is null!"); return; } if (DBG) { Log.d(TAG, "hfp_enable=" + enable); } if (enable && !sAudioIsRouted) { sAudioManager.setParameters("hfp_enable=true"); mAudioManager.setParameters("hfp_enable=true"); } else if (!enable) { sAudioManager.setParameters("hfp_enable=false"); mAudioManager.setParameters("hfp_enable=false"); } sAudioIsRouted = enable; } private AudioFocusRequest requestAudioFocus() { AudioAttributes streamAttributes = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) .build(); AudioFocusRequest focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT) .setAudioAttributes(streamAttributes) .build(); int focusRequestStatus = mAudioManager.requestAudioFocus(focusRequest); if (DBG) { String s = (focusRequestStatus == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) ? "AudioFocus granted" : "AudioFocus NOT granted"; Log.d(TAG, "AudioManager requestAudioFocus returned: " + s); } return focusRequest; } public void doQuit() { Log.d(TAG, "doQuit"); if (sAudioManager != null) { routeHfpAudio(false); } returnAudioFocusIfNecessary(); quitNow(); } private void returnAudioFocusIfNecessary() { if (mAudioFocusRequest == null) return; mAudioManager.abandonAudioFocusRequest(mAudioFocusRequest); mAudioFocusRequest = null; } static int hfToAmVol(int hfVol) { int amRange = sMaxAmVcVol - sMinAmVcVol; int hfRange = MAX_HFP_SCO_VOICE_CALL_VOLUME - MIN_HFP_SCO_VOICE_CALL_VOLUME; Loading Loading @@ -1025,13 +1053,13 @@ public class HeadsetClientStateMachine extends StateMachine { } } int amVol = sAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); int amVol = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); deferMessage( obtainMessage(HeadsetClientStateMachine.SET_SPEAKER_VOLUME, amVol, 0)); // Mic is either in ON state (full volume) or OFF state. There is no way in // Android to change the MIC volume. deferMessage(obtainMessage(HeadsetClientStateMachine.SET_MIC_VOLUME, sAudioManager.isMicrophoneMute() ? 0 : 15, 0)); mAudioManager.isMicrophoneMute() ? 0 : 15, 0)); // query subscriber info deferMessage(obtainMessage(HeadsetClientStateMachine.SUBSCRIBER_INFO)); transitionTo(mConnected); Loading Loading @@ -1361,11 +1389,11 @@ public class HeadsetClientStateMachine extends StateMachine { if (event.valueInt == HeadsetClientHalConstants.VOLUME_TYPE_SPK) { mCommandedSpeakerVolume = hfToAmVol(event.valueInt2); Log.d(TAG, "AM volume set to " + mCommandedSpeakerVolume); sAudioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, mAudioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, +mCommandedSpeakerVolume, AudioManager.FLAG_SHOW_UI); } else if (event.valueInt == HeadsetClientHalConstants.VOLUME_TYPE_MIC) { sAudioManager.setMicrophoneMute(event.valueInt2 == 0); mAudioManager.setMicrophoneMute(event.valueInt2 == 0); } break; case StackEvent.EVENT_TYPE_CMD_RESULT: Loading Loading @@ -1495,7 +1523,7 @@ public class HeadsetClientStateMachine extends StateMachine { // We need to set the volume after switching into HFP mode as some Audio HALs // reset the volume to a known-default on mode switch. final int amVol = sAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); final int amVol = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); final int hfVol = amToHfVol(amVol); if (DBG) { Loading @@ -1505,18 +1533,19 @@ public class HeadsetClientStateMachine extends StateMachine { if (DBG) { Log.d(TAG, "Setting sampling rate as 16000"); } sAudioManager.setParameters("hfp_set_sampling_rate=16000"); mAudioManager.setParameters("hfp_set_sampling_rate=16000"); } else { if (DBG) { Log.d(TAG, "Setting sampling rate as 8000"); } sAudioManager.setParameters("hfp_set_sampling_rate=8000"); mAudioManager.setParameters("hfp_set_sampling_rate=8000"); } if (DBG) { Log.d(TAG, "hf_volume " + hfVol); } routeHfpAudio(true); sAudioManager.setParameters("hfp_volume=" + hfVol); mAudioFocusRequest = requestAudioFocus(); mAudioManager.setParameters("hfp_volume=" + hfVol); transitionTo(mAudioOn); break; Loading Loading @@ -1590,6 +1619,7 @@ public class HeadsetClientStateMachine extends StateMachine { */ if (NativeInterface.disconnectAudioNative(getByteAddress(mCurrentDevice))) { routeHfpAudio(false); returnAudioFocusIfNecessary(); } break; Loading Loading @@ -1661,6 +1691,7 @@ public class HeadsetClientStateMachine extends StateMachine { // is not much we can do here since dropping the call without user consent // even if the audio connection snapped may not be a good idea. routeHfpAudio(false); returnAudioFocusIfNecessary(); transitionTo(mConnected); break; Loading Loading
android/app/jni/com_android_bluetooth_avrcp_target.cpp +64 −12 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ #include <base/bind.h> #include <map> #include <mutex> #include <shared_mutex> #include <vector> #include "android_runtime/AndroidRuntime.h" Loading @@ -33,6 +35,8 @@ namespace android { static MediaCallbacks* mServiceCallbacks; static ServiceInterface* sServiceInterface; static jobject mJavaInterface; static std::shared_timed_mutex interface_mutex; static std::shared_timed_mutex callbacks_mutex; // Forward Declarations static void sendMediaKeyEvent(int, int); Loading Loading @@ -204,6 +208,8 @@ static void classInitNative(JNIEnv* env, jclass clazz) { static void initNative(JNIEnv* env, jobject object) { ALOGD("%s", __func__); std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex); std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex); mJavaInterface = env->NewGlobalRef(object); sServiceInterface = getBluetoothInterface()->get_avrcp_service(); Loading @@ -214,7 +220,12 @@ static void sendMediaUpdateNative(JNIEnv* env, jobject object, jboolean metadata, jboolean state, jboolean queue) { ALOGD("%s", __func__); CHECK(mServiceCallbacks); std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex); if (mServiceCallbacks == nullptr) { ALOGW("%s: Service not loaded.", __func__); return; } mServiceCallbacks->SendMediaUpdate(metadata == JNI_TRUE, state == JNI_TRUE, queue == JNI_TRUE); } Loading @@ -223,13 +234,21 @@ static void sendFolderUpdateNative(JNIEnv* env, jobject object, jboolean available_players, jboolean addressed_player, jboolean uids) { ALOGD("%s", __func__); CHECK(mServiceCallbacks); std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex); if (mServiceCallbacks == nullptr) { ALOGW("%s: Service not loaded.", __func__); return; } mServiceCallbacks->SendFolderUpdate(available_players == JNI_TRUE, addressed_player == JNI_TRUE, uids == JNI_TRUE); } static void cleanupNative(JNIEnv* env, jobject object) { std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex); std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex); sServiceInterface->Cleanup(); env->DeleteGlobalRef(mJavaInterface); mJavaInterface = nullptr; Loading @@ -239,7 +258,11 @@ static void cleanupNative(JNIEnv* env, jobject object) { jboolean connectDeviceNative(JNIEnv* env, jobject object, jstring address) { ALOGD("%s", __func__); if (!sServiceInterface) return JNI_FALSE; std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex); if (mServiceCallbacks == nullptr) { ALOGW("%s: Service not loaded.", __func__); return JNI_FALSE; } const char* tmp_addr = env->GetStringUTFChars(address, 0); RawAddress bdaddr; Loading @@ -254,7 +277,11 @@ jboolean connectDeviceNative(JNIEnv* env, jobject object, jstring address) { jboolean disconnectDeviceNative(JNIEnv* env, jobject object, jstring address) { ALOGD("%s", __func__); if (!sServiceInterface) return JNI_FALSE; std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex); if (mServiceCallbacks == nullptr) { ALOGW("%s: Service not loaded.", __func__); return JNI_FALSE; } const char* tmp_addr = env->GetStringUTFChars(address, 0); RawAddress bdaddr; Loading @@ -269,8 +296,9 @@ jboolean disconnectDeviceNative(JNIEnv* env, jobject object, jstring address) { static void sendMediaKeyEvent(int key, int state) { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!sCallbackEnv.valid() || !mJavaInterface) return; sCallbackEnv->CallVoidMethod(mJavaInterface, method_sendMediaKeyEvent, key, state); } Loading Loading @@ -393,7 +421,10 @@ static FolderInfo getFolderInfoFromJavaObj(JNIEnv* env, jobject folder) { static SongInfo getSongInfo() { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mJavaInterface) return SongInfo(); jobject metadata = sCallbackEnv->CallObjectMethod(mJavaInterface, method_getCurrentSongInfo); return getSongInfoFromJavaObj(sCallbackEnv.get(), metadata); Loading @@ -401,11 +432,11 @@ static SongInfo getSongInfo() { static PlayStatus getCurrentPlayStatus() { ALOGD("%s", __func__); PlayStatus status; std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return status; if (!sCallbackEnv.valid() || !mJavaInterface) return PlayStatus(); PlayStatus status; jobject playStatus = sCallbackEnv->CallObjectMethod(mJavaInterface, method_getPlaybackStatus); jclass class_playStatus = sCallbackEnv->GetObjectClass(playStatus); Loading @@ -425,8 +456,9 @@ static PlayStatus getCurrentPlayStatus() { static std::string getCurrentMediaId() { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return ""; if (!sCallbackEnv.valid() || !mJavaInterface) return ""; jstring media_id = (jstring)sCallbackEnv->CallObjectMethod( mJavaInterface, method_getCurrentMediaId); Loading @@ -440,8 +472,9 @@ static std::string getCurrentMediaId() { static std::vector<SongInfo> getNowPlayingList() { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return std::vector<SongInfo>(); if (!sCallbackEnv.valid() || !mJavaInterface) return std::vector<SongInfo>(); jobject song_list = sCallbackEnv->CallObjectMethod(mJavaInterface, method_getNowPlayingList); Loading @@ -468,9 +501,9 @@ static std::vector<SongInfo> getNowPlayingList() { static uint16_t getCurrentPlayerId() { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return 0u; if (!sCallbackEnv.valid() || !mJavaInterface) return 0u; jint id = sCallbackEnv->CallIntMethod(mJavaInterface, method_getCurrentPlayerId); Loading @@ -480,7 +513,10 @@ static uint16_t getCurrentPlayerId() { static std::vector<MediaPlayerInfo> getMediaPlayerList() { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mJavaInterface) return std::vector<MediaPlayerInfo>(); jobject player_list = (jobject)sCallbackEnv->CallObjectMethod( mJavaInterface, method_getMediaPlayerList); Loading Loading @@ -535,7 +571,9 @@ SetBrowsedPlayerCb set_browsed_player_cb; static void setBrowsedPlayer(uint16_t player_id, SetBrowsedPlayerCb cb) { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mJavaInterface) return; set_browsed_player_cb = cb; sCallbackEnv->CallVoidMethod(mJavaInterface, method_setBrowsedPlayer, Loading Loading @@ -631,7 +669,9 @@ static void getFolderItemsResponseNative(JNIEnv* env, jobject object, static void getFolderItems(uint16_t player_id, std::string media_id, GetFolderItemsCb cb) { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mJavaInterface) return; // TODO (apanicke): Fix a potential media_id collision if two media players // use the same media_id scheme or two devices browse the same content. Loading @@ -645,7 +685,9 @@ static void getFolderItems(uint16_t player_id, std::string media_id, static void playItem(uint16_t player_id, bool now_playing, std::string media_id) { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mJavaInterface) return; jstring j_media_id = sCallbackEnv->NewStringUTF(media_id.c_str()); sCallbackEnv->CallVoidMethod(mJavaInterface, method_playItem, player_id, Loading @@ -654,7 +696,9 @@ static void playItem(uint16_t player_id, bool now_playing, static void setActiveDevice(const RawAddress& address) { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mJavaInterface) return; jstring j_bdaddr = sCallbackEnv->NewStringUTF(address.ToString().c_str()); sCallbackEnv->CallVoidMethod(mJavaInterface, method_setActiveDevice, Loading @@ -663,7 +707,9 @@ static void setActiveDevice(const RawAddress& address) { static void volumeDeviceConnected(const RawAddress& address) { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mJavaInterface) return; jstring j_bdaddr = sCallbackEnv->NewStringUTF(address.ToString().c_str()); sCallbackEnv->CallVoidMethod(mJavaInterface, method_volumeDeviceConnected, Loading @@ -677,7 +723,9 @@ static void volumeDeviceConnected( const RawAddress& address, ::bluetooth::avrcp::VolumeInterface::VolumeChangedCb cb) { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mJavaInterface) return; volumeCallbackMap.emplace(address, cb); Loading @@ -688,7 +736,9 @@ static void volumeDeviceConnected( static void volumeDeviceDisconnected(const RawAddress& address) { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mJavaInterface) return; volumeCallbackMap.erase(address); Loading @@ -706,7 +756,9 @@ static void sendVolumeChangedNative(JNIEnv* env, jobject object, jint volume) { static void setVolume(int8_t volume) { ALOGD("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mJavaInterface) return; sCallbackEnv->CallVoidMethod(mJavaInterface, method_setVolume, volume); } Loading
android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java +10 −0 Original line number Diff line number Diff line Loading @@ -81,6 +81,12 @@ public class HeadsetClientService extends ProfileService { // Setup the JNI service NativeInterface.initializeNative(); mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); if (mAudioManager == null) { Log.e(TAG, "AudioManager service doesn't exist?"); } else { // start AudioManager in a known state mAudioManager.setParameters("hfp_enable=false"); } mSmFactory = new HeadsetClientStateMachineFactory(); mStateMachineMap.clear(); Loading Loading @@ -923,4 +929,8 @@ public class HeadsetClientService extends ProfileService { protected void setSMFactory(HeadsetClientStateMachineFactory factory) { mSmFactory = factory; } AudioManager getAudioManager() { return mAudioManager; } }
android/app/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java +54 −23 Original line number Diff line number Diff line Loading @@ -39,8 +39,9 @@ import android.bluetooth.BluetoothHeadsetClient; import android.bluetooth.BluetoothHeadsetClientCall; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; import android.content.Context; import android.content.Intent; import android.media.AudioAttributes; import android.media.AudioFocusRequest; import android.media.AudioManager; import android.os.Bundle; import android.os.Looper; Loading Loading @@ -156,7 +157,6 @@ public class HeadsetClientStateMachine extends StateMachine { // indicator private Pair<Integer, Object> mPendingAction; private static AudioManager sAudioManager; private int mAudioState; private boolean mAudioWbs; private int mVoiceRecognitionActive; Loading @@ -169,6 +169,11 @@ public class HeadsetClientStateMachine extends StateMachine { private int mPeerFeatures; private int mChldFeatures; // This is returned when requesting focus from AudioManager private AudioFocusRequest mAudioFocusRequest; private AudioManager mAudioManager; // Accessor for the states, useful for reusing the state machines public IState getDisconnectedState() { return mDisconnected; Loading Loading @@ -690,13 +695,9 @@ public class HeadsetClientStateMachine extends StateMachine { HeadsetClientStateMachine(HeadsetClientService context, Looper looper) { super(TAG, looper); mService = context; mAudioManager = mService.getAudioManager(); mAdapter = BluetoothAdapter.getDefaultAdapter(); if (sAudioManager == null) { sAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); // Initialize hfp_enable into a known state. routeHfpAudio(false); } mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; mAudioWbs = false; mVoiceRecognitionActive = HeadsetClientHalConstants.VR_STATE_STOPPED; Loading @@ -706,8 +707,8 @@ public class HeadsetClientStateMachine extends StateMachine { mIndicatorNetworkSignal = 0; mIndicatorBatteryLevel = 0; sMaxAmVcVol = sAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL); sMinAmVcVol = sAudioManager.getStreamMinVolume(AudioManager.STREAM_VOICE_CALL); sMaxAmVcVol = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL); sMinAmVcVol = mAudioManager.getStreamMinVolume(AudioManager.STREAM_VOICE_CALL); mOperatorName = null; mSubscriberInfo = null; Loading Loading @@ -740,26 +741,53 @@ public class HeadsetClientStateMachine extends StateMachine { return hfcsm; } static synchronized void routeHfpAudio(boolean enable) { synchronized void routeHfpAudio(boolean enable) { if (mAudioManager == null) { Log.e(TAG, "AudioManager is null!"); return; } if (DBG) { Log.d(TAG, "hfp_enable=" + enable); } if (enable && !sAudioIsRouted) { sAudioManager.setParameters("hfp_enable=true"); mAudioManager.setParameters("hfp_enable=true"); } else if (!enable) { sAudioManager.setParameters("hfp_enable=false"); mAudioManager.setParameters("hfp_enable=false"); } sAudioIsRouted = enable; } private AudioFocusRequest requestAudioFocus() { AudioAttributes streamAttributes = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) .build(); AudioFocusRequest focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT) .setAudioAttributes(streamAttributes) .build(); int focusRequestStatus = mAudioManager.requestAudioFocus(focusRequest); if (DBG) { String s = (focusRequestStatus == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) ? "AudioFocus granted" : "AudioFocus NOT granted"; Log.d(TAG, "AudioManager requestAudioFocus returned: " + s); } return focusRequest; } public void doQuit() { Log.d(TAG, "doQuit"); if (sAudioManager != null) { routeHfpAudio(false); } returnAudioFocusIfNecessary(); quitNow(); } private void returnAudioFocusIfNecessary() { if (mAudioFocusRequest == null) return; mAudioManager.abandonAudioFocusRequest(mAudioFocusRequest); mAudioFocusRequest = null; } static int hfToAmVol(int hfVol) { int amRange = sMaxAmVcVol - sMinAmVcVol; int hfRange = MAX_HFP_SCO_VOICE_CALL_VOLUME - MIN_HFP_SCO_VOICE_CALL_VOLUME; Loading Loading @@ -1025,13 +1053,13 @@ public class HeadsetClientStateMachine extends StateMachine { } } int amVol = sAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); int amVol = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); deferMessage( obtainMessage(HeadsetClientStateMachine.SET_SPEAKER_VOLUME, amVol, 0)); // Mic is either in ON state (full volume) or OFF state. There is no way in // Android to change the MIC volume. deferMessage(obtainMessage(HeadsetClientStateMachine.SET_MIC_VOLUME, sAudioManager.isMicrophoneMute() ? 0 : 15, 0)); mAudioManager.isMicrophoneMute() ? 0 : 15, 0)); // query subscriber info deferMessage(obtainMessage(HeadsetClientStateMachine.SUBSCRIBER_INFO)); transitionTo(mConnected); Loading Loading @@ -1361,11 +1389,11 @@ public class HeadsetClientStateMachine extends StateMachine { if (event.valueInt == HeadsetClientHalConstants.VOLUME_TYPE_SPK) { mCommandedSpeakerVolume = hfToAmVol(event.valueInt2); Log.d(TAG, "AM volume set to " + mCommandedSpeakerVolume); sAudioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, mAudioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, +mCommandedSpeakerVolume, AudioManager.FLAG_SHOW_UI); } else if (event.valueInt == HeadsetClientHalConstants.VOLUME_TYPE_MIC) { sAudioManager.setMicrophoneMute(event.valueInt2 == 0); mAudioManager.setMicrophoneMute(event.valueInt2 == 0); } break; case StackEvent.EVENT_TYPE_CMD_RESULT: Loading Loading @@ -1495,7 +1523,7 @@ public class HeadsetClientStateMachine extends StateMachine { // We need to set the volume after switching into HFP mode as some Audio HALs // reset the volume to a known-default on mode switch. final int amVol = sAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); final int amVol = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); final int hfVol = amToHfVol(amVol); if (DBG) { Loading @@ -1505,18 +1533,19 @@ public class HeadsetClientStateMachine extends StateMachine { if (DBG) { Log.d(TAG, "Setting sampling rate as 16000"); } sAudioManager.setParameters("hfp_set_sampling_rate=16000"); mAudioManager.setParameters("hfp_set_sampling_rate=16000"); } else { if (DBG) { Log.d(TAG, "Setting sampling rate as 8000"); } sAudioManager.setParameters("hfp_set_sampling_rate=8000"); mAudioManager.setParameters("hfp_set_sampling_rate=8000"); } if (DBG) { Log.d(TAG, "hf_volume " + hfVol); } routeHfpAudio(true); sAudioManager.setParameters("hfp_volume=" + hfVol); mAudioFocusRequest = requestAudioFocus(); mAudioManager.setParameters("hfp_volume=" + hfVol); transitionTo(mAudioOn); break; Loading Loading @@ -1590,6 +1619,7 @@ public class HeadsetClientStateMachine extends StateMachine { */ if (NativeInterface.disconnectAudioNative(getByteAddress(mCurrentDevice))) { routeHfpAudio(false); returnAudioFocusIfNecessary(); } break; Loading Loading @@ -1661,6 +1691,7 @@ public class HeadsetClientStateMachine extends StateMachine { // is not much we can do here since dropping the call without user consent // even if the audio connection snapped may not be a good idea. routeHfpAudio(false); returnAudioFocusIfNecessary(); transitionTo(mConnected); break; Loading