前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android12 不依赖运行时权限使用蓝牙Sco

Android12 不依赖运行时权限使用蓝牙Sco

作者头像
一只小虾米
发布2022-10-25 16:19:28
1.1K1
发布2022-10-25 16:19:28
举报
文章被收录于专栏:Android点滴分享Android点滴分享

背景

在Android 12+上,Android添加了一个新的运行时权限BLUETOOTH_CONNECT,官方解释是

Required to be able to connect to paired Bluetooth devices. 看起来要配对蓝牙就一定需要这个权限了。可是这个权限是Dangerous,这样就得弹窗申请了,有没有可能不用这个权限,依旧可以配对蓝牙。

通过分析业界的产品,发现有的产品可以做到,这样就需要看看是如何实现的了。

分析

首先dumpsys 看看能否找到线索,分析音频蓝牙相关的调用就需要看 dumpsys audio, dumpsys media.metrics, 在机器上操作了下,发现的确使用了通话音量,并且也有连接sco的记录,这就证明了这个产品的确做到了。

比如dumpsys audio下有以下信息记录:

代码语言:javascript
复制
10-18 11:35:14:511 setCommunicationRouteForClient for pid: 20191 device: AudioDeviceAttributes: role:output type:bt_sco addr: from API: startBluetoothSco()) from u/pid:10324/20191
10-18 11:35:14:512 startBluetoothSco()) from u/pid:10324/20191
10-18 11:35:14:529 onUpdateCommunicationRoute, preferredCommunicationDevice: null eventSource: startBluetoothSco()) from u/pid:10324/20191
10-18 11:35:14:542 removePreferredDevicesForStrategySync, strategy: 15
10-18 11:35:15:309 onUpdateCommunicationRoute, preferredCommunicationDevice: AudioDeviceAttributes: role:output type:bt_sco addr:9C:FC:28:59:5E:C1 eventSource: setBluetoothScoOn(true) from u/pid:1002/13471
10-18 11:35:15:312 onUpdateCommunicationRoute, preferredCommunicationDevice: AudioDeviceAttributes: role:output type:bt_sco addr:9C:FC:28:59:5E:C1 eventSource: BtHelper.receiveBtEvent
10-18 11:35:15:313 setPreferredDevicesForStrategySync, strategy: 15 devices: [AudioDeviceAttributes: role:output type:bt_sco addr:9C:FC:28:59:5E:C1]

可是dumpsys 里看不出来调用什么接口感知到蓝牙设备了,接下来就得通过hook了,对于这种问题,应该用objection就够了。和音频相关的类主要是Audio.Media.AudioManager, 那直接hook这个类的所有方法就好了。

指令如下:

代码语言:javascript
复制
 android hooking watch class android.media.AudioManager

这时候操作下应用,就看到了一个可疑的调用registerAudioDeviceCallback

接下来继续hook这个方法

代码语言:javascript
复制
android hooking watch class_method android.media.AudioManager.registerAudioDeviceCallback --dump-args --dump-ba
cktrace --dump-return

这下子就看到了具体调用了

代码语言:javascript
复制
[usb] # (agent) [681721] Called android.media.AudioManager.registerAudioDeviceCallback(android.media.AudioDeviceCallback, android.os.Handler)
(agent) [681721] Backtrace:
        android.media.AudioManager.registerAudioDeviceCallback(Native Method)

这样就基本锁定了方向,接下来研究下这个方法,

代码语言:javascript
复制
     * Registers an {@link AudioDeviceCallback} object to receive notifications of changes
     * to the set of connected audio devices.
     * @param callback The {@link AudioDeviceCallback} object to receive connect/disconnect
     * notifications.
     * @param handler Specifies the {@link Handler} object for the thread on which to execute
     * the callback. If <code>null</code>, the {@link Handler} associated with the main
     * {@link Looper} will be used.
     */
    public void registerAudioDeviceCallback(AudioDeviceCallback callback,
            @Nullable Handler handler) {
        synchronized (mDeviceCallbacks) {
            if (callback != null && !mDeviceCallbacks.containsKey(callback)) {
                if (mDeviceCallbacks.size() == 0) {
                    if (mPortListener == null) {
                        mPortListener = new OnAmPortUpdateListener();
                    }
                    registerAudioPortUpdateListener(mPortListener);
                }
                NativeEventHandlerDelegate delegate =
                        new NativeEventHandlerDelegate(callback, handler);
                mDeviceCallbacks.put(callback, delegate);
                broadcastDeviceListChange_sync(delegate.getHandler());
            }
        }
    }

看起来这个方法会以回调形式提供所有的路由设备变化,而蓝牙对应的Flag就是TYPE_BLUETOOTH_A2DP, TYPE_BLUETOOTH_SCO,用demo 验证了下,的确不需要运行时权限也可以感知到。

到了这儿还没有结束,对于Android12, 通过广播感知sco的连接状态也需要运行时权限,看了下对比产品,没有感知sco的连接结果,而我们的产品有这块的检测,为了保持逻辑一致,也需要想办法感知到到sco的连接结果。这下就不能对比了,不过看了下开启sco的api,有如下的介绍:

代码语言:javascript
复制
 /**
     * Start bluetooth SCO audio connection.
     * <p>Requires Permission:
     *   {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
     * <p>This method can be used by applications wanting to send and received audio
     * to/from a bluetooth SCO headset while the phone is not in call.
     * <p>As the SCO connection establishment can take several seconds,
     * applications should not rely on the connection to be available when the method
     * returns but instead register to receive the intent {@link #ACTION_SCO_AUDIO_STATE_UPDATED}
     * and wait for the state to be {@link #SCO_AUDIO_STATE_CONNECTED}.
     * <p>As the ACTION_SCO_AUDIO_STATE_UPDATED intent is sticky, the application can check the SCO
     * audio state before calling startBluetoothSco() by reading the intent returned by the receiver
     * registration. If the state is already CONNECTED, no state change will be received via the
     * intent after calling startBluetoothSco(). It is however useful to call startBluetoothSco()
     * so that the connection stays active in case the current initiator stops the connection.
     * <p>Unless the connection is already active as described above, the state will always
     * transition from DISCONNECTED to CONNECTING and then either to CONNECTED if the connection
     * succeeds or back to DISCONNECTED if the connection fails (e.g no headset is connected).
     * <p>When finished with the SCO connection or if the establishment fails, the application must
     * call {@link #stopBluetoothSco()} to clear the request and turn down the bluetooth connection.
     * <p>Even if a SCO connection is established, the following restrictions apply on audio
     * output streams so that they can be routed to SCO headset:

也就是通过注册ACTION_SCO_AUDIO_STATE_UPDATED 广播,由于这个广播是粘性的,那么就可以同步感知到sco连接结果了。再在demo上验证了下,的确没问题。

安全隐私话题越来越被重视,对于产品,涉及到使用运行时权限一定需要谨慎一些。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-10-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 分析
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档