Android 9.0 10蓝牙音乐获取歌手、歌曲等信息

Android 9.0中蓝牙音乐其实实现很简单,蓝牙远程服务处理在Bluetooth中。
修改代码路径:

android/packages/apps/Bluetooth./jni/com_android_bluetooth_avrcp_controller.cpp
android/packages/apps/Bluetooth/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java

1、在com_android_bluetooth_avrcp_controller.cpp中classInitNative方法中会会初始化一些方法,其中当播放音乐发生变化时候,会通过onTrackChanged方法回调给java层

static void classInitNative(JNIEnv* env, jclass clazz) {
  method_handletrackchanged =
      env->GetMethodID(clazz, "onTrackChanged", "([BB[I[Ljava/lang/String;)V");
}

2、在com_android_bluetooth_avrcp_controller.cpp中命名空间中会定义一些回调java层的方法

namespace android {
static jmethodID method_handlePassthroughRsp;
static jmethodID method_onConnectionStateChanged;
static jmethodID method_getRcFeatures;
static jmethodID method_setplayerappsettingrsp;
static jmethodID method_handleplayerappsetting;
static jmethodID method_handleplayerappsettingchanged;
static jmethodID method_handleSetAbsVolume;
static jmethodID method_handleRegisterNotificationAbsVol;
static jmethodID method_handletrackchanged;
static jmethodID method_handleplaypositionchanged;
static jmethodID method_handleplaystatuschanged;
static jmethodID method_handleGetFolderItemsRsp;
static jmethodID method_handleGetPlayerItemsRsp;
static jmethodID method_handleGroupNavigationRsp;
static jmethodID method_createFromNativeMediaItem;
static jmethodID method_createFromNativeFolderItem;
static jmethodID method_createFromNativePlayerItem;
static jmethodID method_handleChangeFolderRsp;
static jmethodID method_handleSetBrowsedPlayerRsp;
static jmethodID method_handleSetAddressedPlayerRsp;

3、在com_android_bluetooth_avrcp_controller.cpp中找到method_handletrackchanged方法实现的地方,该方法最后是通过该方法回调btavrcp_track_changed_callback

static void btavrcp_track_changed_callback(const RawAddress& bd_addr,
                                           uint8_t num_attr,
                                           btrc_element_attr_val_t* p_attrs) {
  /*
   * byteArray will be formatted like this: id,len,string
   * Assuming text feild to be null terminated.
   */
  ALOGI("%s", __func__);
  CallbackEnv sCallbackEnv(__func__);
  if (!sCallbackEnv.valid()) return;

  ScopedLocalRef<jbyteArray> addr(
      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
  if (!addr.get()) {
    ALOGE("Fail to get new array ");
    return;
  }

  ScopedLocalRef<jintArray> attribIds(sCallbackEnv.get(),
                                      sCallbackEnv->NewIntArray(num_attr));
  if (!attribIds.get()) {
    ALOGE(" failed to set new array for attribIds");
    return;
  }
  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
                                   (jbyte*)&bd_addr.address);

  jclass strclazz = sCallbackEnv->FindClass("java/lang/String");
  ScopedLocalRef<jobjectArray> stringArray(
      sCallbackEnv.get(),
      sCallbackEnv->NewObjectArray((jint)num_attr, strclazz, 0));
  if (!stringArray.get()) {
    ALOGE(" failed to get String array");
    return;
  }

  for (jint i = 0; i < num_attr; i++) {
    ScopedLocalRef<jstring> str(
        sCallbackEnv.get(),
        sCallbackEnv->NewStringUTF((char*)(p_attrs[i].text)));
    if (!str.get()) {
      ALOGE("Unable to get str");
      return;
    }
    sCallbackEnv->SetIntArrayRegion(attribIds.get(), i, 1,
                                    (jint*)&(p_attrs[i].attr_id));
    sCallbackEnv->SetObjectArrayElement(stringArray.get(), i, str.get());
  }

  sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handletrackchanged,
                               addr.get(), (jbyte)(num_attr), attribIds.get(),
                               stringArray.get());
}

4、在com_android_bluetooth_avrcp_controller.cpp中sBluetoothAvrcpCallbacks方法会会定义一些回调给jiava层的方法

static btrc_ctrl_callbacks_t sBluetoothAvrcpCallbacks = {
    sizeof(sBluetoothAvrcpCallbacks),
    btavrcp_passthrough_response_callback,
    btavrcp_groupnavigation_response_callback,
    btavrcp_connection_state_callback,
    btavrcp_get_rcfeatures_callback,
    btavrcp_setplayerapplicationsetting_rsp_callback,
    btavrcp_playerapplicationsetting_callback,
    btavrcp_playerapplicationsetting_changed_callback,
    btavrcp_set_abs_vol_cmd_callback,
    btavrcp_register_notification_absvol_callback,
    btavrcp_track_changed_callback,
    btavrcp_play_position_changed_callback,
    btavrcp_play_status_changed_callback,
    btavrcp_get_folder_items_callback,
    btavrcp_change_path_callback,
    btavrcp_set_browsed_player_callback,
    btavrcp_set_addressed_player_callback};

5、在com_android_bluetooth_avrcp_controller.cpp中initNative方法会初始化一些回调函数

static void initNative(JNIEnv* env, jobject object) {
  jclass tmpMediaItem =
      env->FindClass("android/media/browse/MediaBrowser$MediaItem");
  class_MediaBrowser_MediaItem = (jclass)env->NewGlobalRef(tmpMediaItem);

  jclass tmpBtPlayer =
      env->FindClass("com/android/bluetooth/avrcpcontroller/AvrcpPlayer");
  class_AvrcpPlayer = (jclass)env->NewGlobalRef(tmpBtPlayer);

  const bt_interface_t* btInf = getBluetoothInterface();
  if (btInf == NULL) {
    ALOGE("Bluetooth module is not loaded");
    return;
  }

  if (sBluetoothAvrcpInterface != NULL) {
    ALOGW("Cleaning up Avrcp Interface before initializing...");
    sBluetoothAvrcpInterface->cleanup();
    sBluetoothAvrcpInterface = NULL;
  }

  if (sCallbacksObj != NULL) {
    ALOGW("Cleaning up Avrcp callback object");
    env->DeleteGlobalRef(sCallbacksObj);
    sCallbacksObj = NULL;
  }

  sBluetoothAvrcpInterface =
      (btrc_ctrl_interface_t*)btInf->get_profile_interface(
          BT_PROFILE_AV_RC_CTRL_ID);
  if (sBluetoothAvrcpInterface == NULL) {
    ALOGE("Failed to get Bluetooth Avrcp Controller Interface");
    return;
  }

  bt_status_t status =
      sBluetoothAvrcpInterface->init(&sBluetoothAvrcpCallbacks);//这里就是初始化一些回调函数
  if (status != BT_STATUS_SUCCESS) {
    ALOGE("Failed to initialize Bluetooth Avrcp Controller, status: %d",
          status);
    sBluetoothAvrcpInterface = NULL;
    return;
  }

  sCallbacksObj = env->NewGlobalRef(object);
}

6、AvrcpControllerService.java中找到onTrackChanged方法,该方法就是获取蓝牙音乐的一些信息

   private synchronized void onTrackChanged(byte[] address, byte numAttributes, int[] attributes,
            String[] attribVals) {
        if (DBG) {
            Log.d(TAG, "onTrackChanged");
        }
        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
        if (device != null && !device.equals(mConnectedDevice)) {
            Log.e(TAG, "onTrackChanged device not found " + address);
            return;
        }
        //该方法就是把蓝牙音乐的信息传递给java层
     	getElementAttrRsp(attributes,attribVals,numAttributes);
        List<Integer> attrList = new ArrayList<>();
        for (int attr : attributes) {
            
            attrList.add(attr);
        }
        List<String> attrValList = Arrays.asList(attribVals);
       
        TrackInfo trackInfo = new TrackInfo(attrList, attrValList);
        if (VDBG) {
            Log.d(TAG, "onTrackChanged " + trackInfo);
        }
      
        Message msg = mAvrcpCtSm.obtainMessage(
                AvrcpControllerStateMachine.MESSAGE_PROCESS_TRACK_CHANGED, trackInfo);
        mAvrcpCtSm.sendMessage(msg);
    }

7、AvrcpControllerService.java中找到onTrackChanged方法,然后在该方法中添加一个方法getElementAttrRsp(),这样就可以把音乐信息传递给java层

 private void getElementAttrRsp(int[] attr_id,String[] textArray,byte num_attr){
        String artist = null;
        String trackTitle = null;
        String album = null;
        for (int i = 0; i < num_attr; i++){
            switch (attr_id[i]) {
                case JNI_MEDIA_ATTR_ID_TITLE:
                    trackTitle = textArray[i];
                    if (trackTitle == null){
                        trackTitle = "Unknown";
                    }
                    break;
                case JNI_MEDIA_ATTR_ID_ARTIST:
                    artist = textArray[i];
                    if (artist == null){
                        artist = "Unknown";
                    }
                    break;
                case JNI_MEDIA_ATTR_ID_ALBUM:
                    album = textArray[i];
                    if (album == null){
                        album = "Unknown";
                    }
                    break;
            }
        }
        Intent intent = new Intent("com.android.getelementattrrsp");
        intent.putExtra("artist", artist);
        intent.putExtra("trackTitle",trackTitle);
        intent.putExtra("album",album);
        Log.d(TAG,"getElementAttrRsp,artist: " + artist + ",trackTitle: " + trackTitle + ",album: " + album);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
    }

日志:

08-09 18:30:20.791  2576  2814 D wang  : onTrackChanged  attrValList 7
08-09 18:30:20.792  2576  2814 D wang  : onTrackChanged  attrValList [热爱105°C的你, 阿肆, 热爱105°C的你, 7, 100, , 195370]
08-09 18:30:20.821  2576  2814 D wang  : onTrackChanged  attrValList 7
08-09 18:30:20.821  2576  2814 D wang  : onTrackChanged  attrValList [热爱105°C的你, 阿肆, 热爱105°C的你, 7, 100, , 195370]
08-09 18:30:20.881  2576  2814 D wang  : onTrackChanged  attrValList 7
08-09 18:30:20.881  2576  2814 D wang  : onTrackChanged  attrValList [热爱105°C的你, 阿肆, 热爱105°C的你, 7, 100, , 195370]
  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ble audio补丁原理是利用hidraw节点捕捉协议栈发送的语音数据,目前Android Blueroid将ble语音数据和按键信息通过hid发送出去,通过建立hidraw节点,可以从中捕捉到语音数据。目前通过ble hal实现从hidraw中读取遥控器语音数据,在Android框架层上就通过配置文件将ble hal导入到音频框架中,并通过绑定Android原生已有的耳麦设备来完成audio音频策略选择,通过apk检测ble连接状态,通知audio服务耳麦设备的状态就可以使得录音通路切换至ble hal,实现从ble获取录音数据功能。 打补丁前最好使用干净的环境,不要有别家方案ble补丁,否则可能会有不兼容问题。 补丁如若不能使用首先检查节点是否存在和其权限,正常节点权限如下: ls -l /dev/hidraw* crw-rw---- 1 system audio 241, 0 2018-12-18 13:42 /dev/hidraw0 audio用户组有读写权限。 2、如果selinux模式为Enforcing,可以通过logcat搜索avc关键字。有如下类似提示则为异常,提示进程没有权限,检查sepolicy是否设置正常: avc: denied { read } for name="/" dev="tmpfs" ino=6145 scontext=u:r:mediaserver:s0 tcontext=u:object_r:device:s0 tclass=dir permissive=0 //Android 5.0和6.0版本,audio hal被mediaserver进程加载 avc: denied { read } for name="/" dev="tmpfs" ino=8125 scontext=u:r:audioserver:s0 tcontext=u:object_r:device:s0 tclass=dir permissive=0 //Android 7.0版本,audio hal被audioserver进程加载 avc: denied { read } for name="hidraw" dev="sysfs" ino=16332 scontext=u:r:hal_audio_default:s0 tcontext=u:object_r:sysfs:s0 tclass=dir permissive=0 //Android 8.0和9.0版本,audio hal被android[email protected]进程加载 3、检查audio的配置,打上patch后,首先确认小机上文件是否有修改到,目前文件可能位于/vendor/etc或/system/etc目录下,其中/vendor/etc下的配置文件是优先解析的。确保文件无误后,通过dumpsys media.audio_policy查看ble hal是否正常加载。 以下是相关说明: AudioPolicyManager: 0xf20c5200 Command Thread: 0xf20af140 Tones Thread: 0xf20af020 ... - Available input devices: Device 1: - id: 3 - type: AUDIO_DEVICE_IN_BUILTIN_MIC - Profiles: Profile 0: - format: AUDIO_FORMAT_PCM_16_BIT - sampling rates:8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 - channel masks:0x000c, 0x0010 Device 2: - id: 20 - type: AUDIO_DEVICE_IN_WIRED_HEADSET //对应的数值是0x80000010 - name: RemoteDM1204 - Profiles: Available input devices指示当前可用设备,目前ble hal是和AUDIO_DEVICE_IN_WIRED_HEADSET设备绑定,如果需要录音走ble hal,AUDIO_DEVICE_IN_WIRED_HEADSET设备必须出现在可用设备中,如果没有,就可能是补丁中hidaudio.apk的问题。 HW Modules dump: ... - H

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值