文章目录

  • Audio Jack(耳机插孔)
      • AudioDeviceInfo.TYPE_BUILTIN_SPEAKER
      • AudioDeviceInfo.TYPE_BUILTIN_EARPIECE
    • 判断耳机输出
  • Headset(耳机)在位状态
    • isWiredHeadsetOn()
    • AudioManager.ACTION_HEADSET_PLUG
    • 获取状态

Audio Jack(耳机插孔)

针对Audio Jack的设备物理组件的存在性判断,及耳机是否在位的判断。

官方文档中提及的词:

  • headset 指带有麦克风的头戴式耳机;
  • headphone 指头戴式耳机,无麦克风;
  • earphone 也指耳机,非头戴式耳机;
  • earpiece 指耳机,猜测比earhphone多个配置;

Android 6 (API 23)及以上设备测试:

  1. 判断是否支持音频输出。

    final PackageManager pm = context.getPackageManager();
    boolean hasAudioOutput = pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT);
    
  2. 再判定输出设备类型。

    AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    final AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
    

测试设备可以获取到两个值:

  • AudioDeviceInfo.TYPE_BUILTIN_EARPIECE(1)
  • AudioDeviceInfo.TYPE_BUILTIN_SPEAKER(2)

两台设备,一台有耳机孔无USB口,另一台设备有USB插口但无耳机插孔。

耳机插入 符合 AudioDeviceInfo.TYPE_BUILTIN_EARPIECE 类型设备, 扬声器 符合 AudioDeviceInfo.TYPE_BUILTIN_SPEAKER 类型设备。

AudioDeviceInfo.TYPE_BUILTIN_SPEAKER

这个设备类型很容易理解,即内置扬声器,基本设备出厂时均会集成在设备内。

AudioDeviceInfo.TYPE_BUILTIN_EARPIECE

earpiece 指耳机类型,耳机孔设备属于此范围,而 USB类型的耳机 是否属于此范围,由于无设备测试,暂且无法明确。

但从程序运行角度看,无耳机插孔的设备,也同样返回了 TYPE_BUILTIN_EARPIECE 的输出类型。以此推测,USB类型耳机也属于这个范畴。

判断耳机输出

根据判断,需要检查当前音频输出是Audio Jack还是speaker扬声器。

  1. 判断headphone是否在位(插入耳机孔判断)

    final Intent intent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_HEADSET_PLUG));
    final boolean headsetExists = intent.getIntExtra("state", 0) == 1;
    

    在匹配的Intent.ACTION_HEADSET_PLUG广播Intent中包含有参数state,表明headphone设备是否插入耳机孔。

  2. MediaRouter 判断

    final MediaRouter mr = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
    final MediaRouter.RouteInfo ri = mr.getSelectedRoute(MediaRouter.ROUTE_TYPE_LIVE_AUDIO);
    final CharSequence routeName = ri.getName(); // Headphones
    

    判断媒体路由,获取当前音频路由输出。getName() 方法返回用户可见的字符串方式,返回系统字符串资源的定义。

    <resources><!-- more string definitions --><!-- Name of the default audio route for tablets when nothingis connected to a headphone or other wired audio output jack. [CHAR LIMIT=50] --><string name="default_audio_route_name" product="tablet">Tablet</string><!-- Name of the default audio route for tablets when nothingis connected to a headphone or other wired audio output jack. [CHAR LIMIT=50] --><string name="default_audio_route_name" product="tv">TV</string><!-- Name of the default audio route when nothing is connected toa headphone or other wired audio output jack. [CHAR LIMIT=50] --><string name="default_audio_route_name" product="default">Phone</string><!-- Name of the default audio route when an audio dock is connected. [CHAR LIMIT=50] --><string name="default_audio_route_name_dock_speakers">Dock speakers</string><!-- Name of the default audio route when HDMI is connected. [CHAR LIMIT=50] --><string name="default_audio_route_name_hdmi">HDMI</string><!-- Name of the default audio route when wired headphones areconnected. [CHAR LIMIT=50] --><string name="default_audio_route_name_headphones">Headphones</string><!-- Name of the default audio route when USB is connected. [CHAR LIMIT=50] --><string name="default_audio_route_name_usb">USB</string><!-- more string definitions -->
    </resources>
    

    若不插入耳机的情况,需要看OS是何种产品类型:

    • 平板(tablet) => Tablet

    • 手机(default) => Phone

    • TV(tv) => TV

      其他的音频路由输出也有对应的系统值定义。

Headset(耳机)在位状态

在Android平台版本迭代过程中,新API不断出现,旧API会被标记Deprecated。虽然deprecated的API依然可以使用,但在不断迭代过程中,其起到的作用慢慢不太符合对应的需求,或者其原有实现被分解更加详细地实现。这里我遇到的耳机的状态判断就是其中一种情况。

isWiredHeadsetOn()

在API 15(含)后被标记为deprecated的isWiredHeadsetOn()这个方法,一般就被使用来判断是否有耳机设备连接到当前Android设备。

    /*** Checks whether a wired headset is connected or not.* <p>This is not a valid indication that audio playback is* actually over the wired headset as audio routing depends on other conditions.** @return true if a wired headset is connected.*         false if otherwise* @deprecated Use {@link AudioManager#getDevices(int)} instead to list available audio devices.*/public boolean isWiredHeadsetOn() {if (AudioSystem.getDeviceConnectionState(DEVICE_OUT_WIRED_HEADSET,"")== AudioSystem.DEVICE_STATE_UNAVAILABLE &&AudioSystem.getDeviceConnectionState(DEVICE_OUT_WIRED_HEADPHONE,"")== AudioSystem.DEVICE_STATE_UNAVAILABLE &&AudioSystem.getDeviceConnectionState(DEVICE_OUT_USB_HEADSET, "")== AudioSystem.DEVICE_STATE_UNAVAILABLE) {return false;} else {return true;}}

其源码实现中判断了WIRED_HEADSET,WIRED_HEADPHONE,USB_HEADSET三种有线设备的状态。一般情况下均可以使用此方法进行是否连接了外部的音频设备,因为Android设备外设接口类型可用作支持的音频的只有耳机孔及USB。但在一种场景下,此方法无法具体判断是何种外接设备在位——是耳机在耳机孔内亦或USB设备连接上。

AudioManager.ACTION_HEADSET_PLUG

此值定义是广播动作(action),用以接收有线的插拔状态广播。在文档说明中,此action起作用只能使用Context#registerReceiver(BroadcastReceiver, IntentFilter)方式,在manifest进行注册的方式将无法接收到此广播消息。

另外在Intent中包含有其他消息数据:

  • state —— 0 表示耳机不在位,1表示在位;
  • name —— 耳机类型;
  • microphone —— 1 表示耳机有麦克风,0 表示没有;

这些值均比较容易理解,由于测试设备并非手机,并无phone模块,因此不知是否会影响到name,microphone的值。在我的测试场景中name值null,microphone也未准确获取到(由于OS是定制的,可能是下边修改的,如果知道确切原因的请在评论中告知我)。

获取状态

一般Broadcast的action动作接收均是通过定义BroadcastReceiver后接收ACTION_HEADSET_PLUG的广播,后在回调方法内进行相关处理。若仔细看Context#registerReceiver(BroadcastReceiver, IntentFilter)的签名,可以看到起返回是一个Intent对象。AudioManager.ACTION_HEADSET_PLUG广播的定义有对应的数据说明。

    /*** Broadcast Action: Wired Headset plugged in or unplugged.** You <em>cannot</em> receive this through components declared* in manifests, only by explicitly registering for it with* {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)* Context.registerReceiver()}.** <p>The intent will have the following extra values:* <ul>*   <li><em>state</em> - 0 for unplugged, 1 for plugged. </li>*   <li><em>name</em> - Headset type, human readable string </li>*   <li><em>microphone</em> - 1 if headset has a microphone, 0 otherwise </li>* </ul>* </ul>*/@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)public static final String ACTION_HEADSET_PLUG ="android.intent.action.HEADSET_PLUG";

可以看到源码文档注释中的intent中携带的数据key,及类型。因此可以使用如下的代码获取当前的audio的headset状态。

        final Intent intent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_HEADSET_PLUG));boolean headsetExists = false;try {Objects.requireNonNull(intent, "headset device does not exist!");headsetExists = intent.getIntExtra("state", 0) == 1;final String audioType = intent.getStringExtra("name");final boolean hasMicrophone = intent.getIntExtra("", 0) == 1;Log.d(TAG, String.format("headset exists? %b, audio type: %s, microphone exists? %b",headsetExists, audioType, hasMicrophone));} catch (NullPointerException e) { // headset does not existLog.i(TAG, "headset unplugged.");headsetExists = false;}

在调用registerReceiver(BroadcastReceiver, IntentFilter)时第一个参数出入null,这样告诉Android不接收ACTION_HEADSET_PLUG的动作,但需要匹配动作的sticky Intent返回。这里有个requireNonNull()方法的调用对返回的Intent进行判null操作,原因在于首次系统启动,且headset不在位的情况下,获取到的intent为null。因此我们多了一种获取headset是否在位的方式,且可以不使用deprecated的API,看着代码可读性会好很多。

PS: 这里的headset不仅是耳机,还包含了支持USB的音频输出设备。大多数情况下是可以满足使用的。

Android音频输出设备判断 Headset(耳机)在位状态查询相关推荐

  1. android音频系统(7):通话过程中的音频输出设备切换

    前言:由于通话比较特殊,Android对于通话过程中音频输出设备的切换做了特殊处理,它在上层也是通过切换音频播放状态来完成切换操作的,android用CallAudioState来封装通话过程中的音频 ...

  2. 【Android 高性能音频】OboeTester 音频性能测试应用 ( Oboe 输出测试参数 | API 选择 | 音频输出设备选择 | 采样率 | 通道 | 采样格式 | 播放偏好 )

    文章目录 一.Oboe 输出测试参数面板 二.Oboe 输出测试参数 API 及 设备选择 三.Oboe 输出测试参数 音频参数 四.Oboe 输出测试参数 播放偏好 五.Oboe 输出测试参数 ( ...

  3. Android之判断设备网络连接状态,并判断连接方式

    在Android开发过程中,对于一个需要连接网络的Android设备,对设备的网络状态检测是很有必要的!有很多的App都需要连接网络.判断设备是否已经连接网络,并且在连接网络的状态下判断是wifi无线 ...

  4. android type c 耳机检测,USB Type-C 的新音频标准将帮助 Android 设备去掉 3.5mm 耳机孔...

    原标题:USB Type-C 的新音频标准将帮助 Android 设备去掉 3.5mm 耳机孔 苹果往往能够带领硬件行业的一个潮流,虽然在刚开始的时候,充满争议.但随着 iPhone 7 正式去除 3 ...

  5. Android音频子系统(十四)------耳机杂音问题解析

    你好!这里是风筝的博客, 欢迎和我一起交流. 背景介绍: [前提条件]OPPO的模拟有线耳机 [操作步骤]打开全民K歌进行任意一首音乐K歌的时候 [实际结果]耳机里面有滋滋的杂音 [期望结果]耳机里面 ...

  6. Android音频框架之一 详解audioPolicy流程及HAL驱动加载与配置

    前言 此音频架构梳理笔记.主要是因工作上需要在 Android8.1 以上版本中,增加 snd-aloop 虚拟声卡做前期准备工作, 本篇文章提纲挈领的把音频框架主线梳理清晰,通过这篇文章能够清晰如下 ...

  7. Android 音频设备管理

    Android 音频设备管理 一.简介 在即使语音或者实时视频通话中,时常需要提供以下功能: 手动切换听筒或者扬声器: 连接蓝牙时,音频需转到蓝牙: 连接有线耳机时,音频需转到有线耳机: Androi ...

  8. Linux/Android 音频驱动从概念到 APP

    这里写自定义目录标题 前言 硬件介绍 Codec 通用结构 ADC 框图 DAC 框图 常用数字接口 其他相关术语 Codec 实际结构 硬件原理图 芯片手册框图 软硬件对应示例 Codec 硬件逻辑 ...

  9. 【转载】Android音频(7)——项目实战——耳麦插拔

    Android音频(7)--项目实战--耳麦插拔 7.4.3 声音路由切换实例分析 · 深入理解Android:卷1 · 看云 一.驱动程序上报耳麦拔插事件 1. 在有些Android版本中并不会在状 ...

最新文章

  1. Selenium的延迟等待
  2. Redis【第二篇】集群搭建
  3. Window Function--the function of window function
  4. 《Altium Designer 14电路设计与仿真从入门到精通》——1.4 Altium电路板总体设计流程...
  5. 蓝桥杯java第七届决赛第四题--路径之谜
  6. 深度优先遍历和广度优先遍历_利用广度优先搜索解LeetCode第515题:在每个树行中找最大值...
  7. RabbitMQ 入门:1. Message Broker(消息代理)
  8. C、C++函数集 说明
  9. 【OpenCV 例程200篇】25. 图像的平移
  10. 闭合导线坐标计算表_测量员人员必备:8套工程测量公式计算表,输入参数自动得出结果...
  11. jq跨域代理_jQuery中的跨域问题
  12. 1月至今 微信共对超十万个确认存在欺诈行为的帐号进行了阶梯式处理
  13. [bzoj4832][Lydsy1704月赛]抵制克苏恩
  14. 计算机上什么盘放大型游戏好,大型游戏可以直接装到移动硬盘里玩吗?
  15. 史上最全!数学参考书大评测及常见问题
  16. 用jquery编写简易计算器
  17. wso2 esb 配置mysql_WSO2企业服务总线(WSO2 ESB)介绍
  18. JS代码获取当前项目名称
  19. 从放弃本专业到直播编程,这女孩都经历了些什么?
  20. 一个web站点的欢迎页面

热门文章

  1. MUP EXEcryptor v2.2.6 with target_PowerArchiver 2007
  2. 如何占用计算机内存,怎么减少电脑内存被占用
  3. 炎炎夏日,漂流去哪漂?评论情感分析告诉你
  4. C#学习 仿windows计算器 完结
  5. 小米路由器青春版中继模式后登陆
  6. 代码质量管理平台SonarQube安装使用Centos7 三 Maven处理提交至远程仓库
  7. (在线)实时图片加密
  8. matlab组织的培训讲义,MATLAB与Simulink简介培训讲义.ppt
  9. 360百科词条怎么创建(教你100%通过审核方法)
  10. matlab布尔代数运算法则,布尔代数法则.pdf