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

Android 耳机事件传递流程 在项目中,遇到问题:插入不带麦耳机,状态栏显示仍然是带麦图标。 解决此问题涉及到耳机的拔插事件传递流程,在此分析一下Android系统,耳机拔插流程源码分析。Android系统的耳机目前可以实现拍照、暂停/播放、打电话等功能,这一切的基础是耳机拔插成功,结合InputManagerService的相关知识,主要从framework层面分析耳机插拔事件的传递。一、驱动层的事件上传使用adb 命令可以 阅读详情

Android音频(7)——项目实战——耳麦插拔

7.4.3 声音路由切换实例分析 · 深入理解Android:卷1 · 看云

一、驱动程序上报耳麦拔插事件


1. 在有些Android版本中并不会在状态栏上显示耳麦图标。切换声道也不在系统中实现,而是在驱动中实现的。

2. headset headPhone lineOut
headset:既有听筒又有Mic
headPhone:只有听筒,没有Mic
lineOut: 就是输出模拟信号到音箱

驱动需要上报三种设备的拔插:headset、headPhone、lineOut。

3. 怎么上报:
(1) 输入子系统:可以上报按键事件也可以上报开关事件(EV_SW),事件类型包括headset、headPhone、lineOut。
或
(2) switch class子系统:通过uevent向用户空间发送数据,Android中有个线程专门监听这类事件。

具体使用哪一个要看Android源代码。

对于输入设备都需要指定能产生同步类事件EV_SYN. 对于按键事件,输入子系统还需要设置按键值的范围,但是对于开关类事件不需要设置。

使用switch dev子系统时,名字必须要设置为"h2w",Android系统监听/sys/class/switch/h2w这个虚拟设备。

4. 驱动Demo代码
由于耳塞的检查Codec给过来的硬件中断线没有接到Soc上所以使用软件模拟。

#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/err.h>
#include <linux/switch.h>
#include <linux/input.h>

static struct input_dev *g_virtual_input;

static ssize_t input_test_store(struct device *dev,
                     struct device_attribute *attr,
                     const char *buf, size_t count)
{
    long code;
    long val;
    char *endp;

    /* 如果字符串前面含有非数字, simple_strtol不能处理 */
    while ((*buf == ' ') || (*buf == '\t'))
        buf++;

    code = simple_strtol(buf, &endp, 0);

    /* 如果字符串前面含有非数字, simple_strtol不能处理 */
    while ((*endp == ' ') || (*endp == '\t'))
        endp++;
    val  = simple_strtol(endp, NULL, 0);

    printk("emulate to report EV_SW: 0x%x 0x%x\n", code, val);
    input_event(g_virtual_input, EV_SW, code, val);
    input_sync(g_virtual_input);

    return count;
}

static DEVICE_ATTR(test_input, S_IRUGO | S_IWUSR, NULL, input_test_store);

static int register_input_device_for_jack(void)
{
    int err;

    /* 分配input_dev */
    g_virtual_input = input_allocate_device();

    /* 设置 */
    /* 2.1 能产生哪类事件 */
    set_bit(EV_SYN, g_virtual_input->evbit);
    set_bit(EV_SW, g_virtual_input->evbit);


    /* 2.2 能产生这类事件中的哪些 */
    /* headset = 听筒 + MIC = SW_HEADPHONE_INSERT + SW_MICROPHONE_INSERT
     *    同时上报 SW_HEADPHONE_INSERT 和 SW_MICROPHONE_INSERT, 就表示headset
     *    为了简化, 对于android系统只上报SW_MICROPHONE_INSERT也表示headset
     */
    set_bit(SW_HEADPHONE_INSERT, g_virtual_input->swbit);
    set_bit(SW_MICROPHONE_INSERT, g_virtual_input->swbit);
    set_bit(SW_LINEOUT_INSERT, g_virtual_input->swbit);

    /* 2.3 这些事件的范围 */

    g_virtual_input->name = "alsa_switch"; /* 不重要 */

    /* 注册 */
    err = input_register_device(g_virtual_input);
    if (err) {
        input_free_device(g_virtual_input);
        printk("input_register_device for virtual jack err!\n");
        return err;
    }

    /*
     * sysfs文件创建方法:
     * 创建/sys/class/input/inputX/test_input文件
     *   可以执行类似下面的命令来模拟耳麦的动作:
     *       触发上报headset插入: echo 4 1 > /sys/class/input/inputX/test_input
     *       触发上报headset取下: echo 4 0 > /sys/class/input/inputX/test_input
     *       触发上报headphone插入: echo 2 1 > /sys/class/input/inputX/test_input
     *       触发上报headphonet取下: echo 2 0 > /sys/class/input/inputX/test_input
     */
    err = device_create_file(&g_virtual_input->dev, &dev_attr_test_input);
    if (err) {
        printk("device_create_file for test_input err!\n");
        input_unregister_device(g_virtual_input);
        input_free_device(g_virtual_input);
        return err;
    }

    return 0;
}

static void unregister_input_device_for_jack(void)
{
    device_remove_file(&g_virtual_input->dev, &dev_attr_test_input);

    input_unregister_device(g_virtual_input);
    input_free_device(g_virtual_input);
}


/**************************************************************************************************************/

static struct switch_dev g_virtual_switch;

static ssize_t state_test_store(struct device *dev,
                     struct device_attribute *attr,
                     const char *buf, size_t count)
{
    long val;

    val = simple_strtol(buf, NULL, 0);

    printk("emulate to report swtich state: 0x%x\n", val);
    switch_set_state(&g_virtual_switch, val);

    return count;
}

static DEVICE_ATTR(test_state, S_IRUGO | S_IWUSR, NULL, state_test_store);

static int register_switch_device_for_jack(void)
{
    int err;

    g_virtual_switch.name = "h2w"; /*名字必须是这个,Android系统中使用它判断*/
    err = switch_dev_register(&g_virtual_switch);
    if (err) {
        printk("switch_dev_register h2w err!\n");
        return err;
    }

    /* 创建/sys/class/switch/h2w/test_state文件
     *   可以执行类似下面的命令来模拟耳麦的动作:
     *       触发上报headset插入: echo 1 > /sys/class/switch/h2w/test_state
     *       触发上报headset取下: echo 0 > /sys/class/switch/h2w/test_state
     */
    err = device_create_file(g_virtual_switch.dev, &dev_attr_test_state);
    if (err) {
        printk("device_create_file test err!\n");
        switch_dev_unregister(&g_virtual_switch);
        return err;
    }

    return 0;
}

static void unregister_switch_device_for_jack(void)
{
    device_remove_file(g_virtual_switch.dev, &dev_attr_test_state);
    switch_dev_unregister(&g_virtual_switch);
}

/**************************************************************************************************************/

static int __init virtual_jack_init(void)
{
    int err;

    err = register_input_device_for_jack();
    err = register_switch_device_for_jack();

    return 0;
}

static void __exit virtual_jack_exit(void)
{
    unregister_input_device_for_jack();
    unregister_switch_device_for_jack();
}

module_init(virtual_jack_init);
module_exit(virtual_jack_exit);

MODULE_AUTHOR("weidongshan@qq.com");
MODULE_DESCRIPTION("Virutal jack driver for sound card");
MODULE_LICENSE("GPL");

/*
对应的Makefile:
KERN_DIR = /media/ubuntu/works/tiny4412/linux-3.0.86

obj-m    += virtual_jack.o

all:
    make -C $(KERN_DIR) M=`pwd` modules 

clean:
    make -C $(KERN_DIR) M=`pwd` modules clean
    rm -rf modules.order

*/

二、在状态栏显示耳麦图标

1. 优秀相关博文:

Android4.4监听耳机插入处理方法:https://blog.youkuaiyun.com/anndy_peng/article/details/30240135
直接给出的是补丁和图片①,可直接参考它来实现再状态栏上添加耳塞图标的App:
https://github.com/fire855/android_frameworks_base-mtk/commit/7661c081b037a32e273afaf70349a6a1518dab48

[整理]Android屏幕适配(不同的屏幕分辨率和尺寸),选择mic的图标大小的时候会使用到:https://blog.youkuaiyun.com/ttkatrina/article/details/50623043

2. 参考①,tiny4412上操作步骤:

a. 确定在状态栏上图标的位置
   修改 frameworks/base/core/res/res/values/config.xml
   添加一行:<item><xliff:g id="id">headset</xliff:g></item>

b. 创建图标文件
   从①中下载对应图片保存在如下位置
   frameworks/base/packages/SystemUI/res/drawable-hdpi/stat_sys_headset_with_mic.png
   frameworks/base/packages/SystemUI/res/drawable-hdpi/stat_sys_headset_without_mic.png

c. 修改源码, 当接收到消息时显示/清除图标 : 
   frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
   参考①中的修改。
   
d. 编译源文件
d.1 mmm frameworks/base/core/res    // 编译config.xml
    得到  out/target/product/tiny4412/system/framework/framework-res.apk
d.2 mmm frameworks/base/packages/SystemUI   // 编译图标文件, 编译源码
    得到 out/target/product/tiny4412/system/priv-app/SystemUI/SystemUI.apk

e. 替换单板上的文件
   adb push到 /system/framework/framework-res.apk
   adb push到 /system/priv-app/SystemUI/SystemUI.apk 

f. 测试
   使用5.1所编译得到的zImage启动开发板,
   执行以下命令:
     触发上报headset插入:   echo 4 1 > /sys/class/input/input0/test_input  #4=SW_MICROPHONE_INSERT,1=PlugIn
     触发上报headset取下:   echo 4 0 > /sys/class/input/input0/test_input  
     触发上报headphone插入: echo 2 1 > /sys/class/input/input0/test_input  #2=SW_HEADPHONE_INSERT,1=PlugIn
     触发上报headphone取下: echo 2 0 > /sys/class/input/input0/test_input  
     触发上报lineout插入:   echo 6 1 > /sys/class/input/input0/test_input  #6=SW_LINEOUT_INSERT,1=PlugIn
     触发上报lineout取下:   echo 6 0 > /sys/class/input/input0/test_input  

测试结果:input的测试成功。但是uevent的失败,原因见下文

三、耳麦拔插事件调用流程分析

1. Android系统使用input子系统还是使用Switch class(uevent)上报拔插操作,取决于 config_useDevInputEventForAudioJack 配置值,
该值为true时使用input子系统, 为false时使用uevent机制, 该值在下述文件中定义, 后一个文件会覆盖前一个文件,目前在4412的配置文件
中选择的是使用输入子系统的。
frameworks/base/core/res/res/values/config.xml
device/friendly-arm/tiny4412/overlay/frameworks/base/core/res/res/values/config.xml

mUseDevInputEventForAudioJack的赋值:
InputManagerService(Context context) //InputManagerService.java
    mUseDevInputEventForAudioJack = context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);

2. 输入子系统对耳麦插拔事件上报流程

InputReader::loopOnce() //InputReader.cpp
    mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    processEventsLocked(mEventBuffer, count);
        InputReader::processEventsForDeviceLocked
            device->process(rawEvents, count); //输入子系统中为每一个/dev/input/eventX都创建一个InputDevice
                mapper->process(rawEvent); //对于开关事件,这里的InputMapper mapper是SwitchInputMapper
                    SwitchInputMapper::process(const RawEvent* rawEvent)
                        case EV_SW: processSwitch(rawEvent->code, rawEvent->value); //只是简单记录下来按键值
                        case EV_SYN: sync(rawEvent->when); //收到sync(type=EV_SYN,code=SYN_REPORT)后处理
                            //获取监听器,调用监听器的notifySwitch,这里的监听器是InputDispatch
                            getListener()->notifySwitch(&args);

InputDispatcher::notifySwitch(const NotifySwitchArgs* args) //InputDispatcher.cpp
    mPolicy->notifySwitch(args->eventTime, args->switchValues, args->switchMask, policyFlags);
    //上面调用的就是com_android_server_input_InputManagerService.cpp中的下面这个函数
    NativeInputManager::notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask, uint32_t policyFlags) //com_android_server_input_InputManagerService.cpp
        //调用java的同名函数notifySwitch,位于InputManagerService.java中
        env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifySwitch, when, switchValues, switchMask);
            notifySwitch(long whenNanos, int switchValues, int switchMask) //InputManagerService.java
                //调用到mWiredAccessoryCallbacks中的notifyWiredAccessoryChanged
                if (mUseDevInputEventForAudioJack) //这个就是来自上面的config.xml文件配置,为true才会选择输入子系统
                    mWiredAccessoryCallbacks.notifyWiredAccessoryChanged(whenNanos, switchValues, switchMask);

mWiredAccessoryCallbacks是何时注册的:
startOtherServices() //SystemServer.java
    //WiredAccessoryManager作为一个callback传给了inputManager
    inputManager.setWiredAccessoryCallbacks(new WiredAccessoryManager(context, inputManager));
        mWiredAccessoryCallbacks = callbacks;

因此上面调用的就是
WiredAccessoryManager.notifyWiredAccessoryChanged
mSwitchValues = (mSwitchValues & ~switchMask) | switchValues;
    //根据上报的值确定变量headset的取值,显示那个图标由变量headset决定的
    switch (mSwitchValues & (SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT)) {
        case 0: headset = 0; break;
        case SW_HEADPHONE_INSERT_BIT: headset = BIT_HEADSET_NO_MIC; break;
        case SW_LINEOUT_INSERT_BIT: headset = BIT_LINEOUT; break;
        case SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT: headset = BIT_HEADSET; break;
        case SW_MICROPHONE_INSERT_BIT: headset = BIT_HEADSET; break;
        default: headset = 0; break;
    }
    //WiredAccessoryManager的函数,这个函数将会进入Audio系统,uevent分支最终也是调用这个函数。
    updateLocked(NAME_H2W, (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) | headset);

3. Switch class使用uevent对耳麦插拔事件上报流程

有一个UeventThread线程,循环读取socket的uevent事件并且发送uevent.

(1)初始化:
WiredAccessoryManager //WiredAccessoryManager.java
    //创建一个观察者
    mObserver = new WiredAccessoryObserver();
        //WiredAccessoryObserver构造函数调用
        WiredAccessoryObserver() 
            //创建一个观察者事件的列表
            mUEventInfo = makeObservedUEventList();
                //当上面的config.xml中配置这个变量为false时就使用uevent机制
                if (!mUseDevInputEventForAudioJack) {
                    //用于监听名为"h2w"这个Switch class驱动中创建的虚拟文件
                    //shell@tiny4412:/sys # find ./ -name h2w                                        
                    //./devices/virtual/switch/h2w
                    //./class/switch/h2w
                    //这里只监听这三个事件,所以驱动程序通过uevent上报事件也只能上报这三个事件之一
                    uei = new UEventInfo("h2w", BIT_HEADSET, BIT_HEADSET_NO_MIC, BIT_LINEOUT);
        
        
init() //WiredAccessoryManager.java
    startObserving("DEVPATH="+uei.getDevPath());    
        //往UEventThread里面添加观察者
        UEventThread t = getThread();
        //这里的this就是子类WiredAccessoryObserver
        t.addObserver(match, this);    //①


(2)从native代码中获取uevent事件
run()
    //本地初始化
    nativeSetup();
        uevent_init() //android_os_UEventObserver.cpp
            s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); //hardware/uevent.c
            setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));
            bind(s, (struct sockaddr *) &addr, sizeof(addr))
    //等待下一个事件
    nativeWaitForNextEvent(); //UEventObserver.java
        uevent_next_event(char* buffer, int buffer_length) //hardware/uevent.c
            poll(&fds, 1, -1);
            recv(fd, buffer, buffer_length, 0);
    //读取到数据后发送
    sendEvent(message);
        observer.onUEvent(event);
            //取出上面①中设置的观察者
            UEventObserver observer = mTempObserversToSignal.get(i);
            observer.onUEvent(UEventObserver.UEvent event) //WiredAccessoryManager.java
                String devPath = event.get("DEVPATH");
                String name = event.get("SWITCH_NAME");
                int state = Integer.parseInt(event.get("SWITCH_STATE"));
                updateStateLocked(devPath, name, state);
                    //WiredAccessoryManager的函数,两条分支(input和Switch class)在这里汇合了
                    updateLocked(String newName, int newState)

4. uevent实现和input实现在这里汇合位置

updateLocked(String newName, int newState) //WiredAccessoryManager.java
    //获得message,然后发送message
    Message msg = mHandler.obtainMessage(MSG_NEW_DEVICE_STATE, headsetState, mHeadsetState, newName);
    mHandler.sendMessage(msg);

上面发送消息会触发消息的handler被调用:
handleMessage(Message msg) {
case MSG_NEW_DEVICE_STATE:
    setDevicesState(msg.arg1, msg.arg2, (String)msg.obj);
        setDeviceStateLocked(curHeadset, headsetState, prevHeadsetState, headsetName);
            //终于进入了Audio系统了,设置有线设备的连接状态
            mAudioManager.setWiredDeviceConnectionState(inDevice, state, headsetName);
                IAudioService service = getService(); //AudioManager.java
                service.setWiredDeviceConnectionState(device, state, name);
                    //指定一个延时时间后又把消息放到一个消息队列中
                    delay = checkSendBecomingNoisyIntent(device, state);
                    queueMsgUnderWakeLock(mAudioHandler, MSG_SET_WIRED_DEVICE_CONNECTION_STATE, delay);
                        //会触发消息的handler被调用
                        handleMessage(Message msg) 
                             onSetWiredDeviceConnectionState(msg.arg1, msg.arg2, (String)msg.obj);
                                sendDeviceConnectionIntent(device, state, name);
                                    //构建一个Intent结构,然后向应用程序广播它,注册对这个Intent感兴趣的App就会收到它
                                    Intent intent = new Intent();
                                    intent.putExtra("state", state);
                                    intent.putExtra("name", name);    
                                    intent.setAction(Intent.ACTION_HEADSET_PLUG);                        
                                    ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);

5. App注册感兴趣的Intent:

//构造函数中添加
PhoneStatusBarPolicy(Context context, CastController cast) //PhoneStatusBarPolicy.java
    filter.addAction(Intent.ACTION_HEADSET_PLUG);

收到消息后onReceive被调用,updateHeadset是处理函数
onReceive(Context context, Intent intent)
if (action.equals(Intent.ACTION_HEADSET_PLUG))
    updateHeadset(intent);

在状态栏中设置head set图标的状态
private final void updateHeadset(Intent intent) {
    final String action = intent.getAction();
    final int state = intent.getIntExtra("state", 4);
    final int mic = intent.getIntExtra("microphone", 4);

    switch (state) {
        case 0: //拔出
            mService.setIconVisibility("headset", false);
    break;
        case 1: //插入
            if (mic == 1) { //耳机上有mic显示这张图标
                mService.setIcon("headset", R.drawable.stat_sys_headset_with_mic, 0, null);
            } else {         //耳机上没有mic显示这张图标
                mService.setIcon("headset", R.drawable.stat_sys_headset_without_mic, 0, null);
            }
            mService.setIconVisibility("headset", true);
            break;
    }
}

6. native函数有些是在framework中,有些是在system中,有些是在hardware中,有些是在external中。

四、切换声音通道流程

1. 切换声音通道流程
a.在驱动程序中切换:
比如: 插上耳麦发生中断, 在中断处理程序中设置声卡让声音从耳机中输出
b.把输出通道的选择权交给android系统
目前这是主流的做法,驱动应该提供切换的能力,但是不应该替App进行决策。此处讲解此种方法。


3. 有个查看Android源码非常方便的网站:androidxref.com/5.0.0_r2/
输入audio_policy.conf,选择右边的"select all", 点search

一个完整复杂的audio_policy.conf的例子:http://androidxref.com/5.0.0_r2/xref/device/asus/grouper/audio_policy.conf

4. App只指定stream type, 从哪个声卡播放出去那是AudioFlinger的内容。

5. output支持哪些device在策略配置文件/system/etc/audio_policy.conf(/etc下也有?)中就指定了。

6. 对应关系:
一个output对应一个播放线程,也对应一个声卡,可能对应多个device; 一个device对应一个播放设备,比如喇叭或者耳机.

7. 怎样切换通道:

主要涉及的文件是AudioPolicyManager.cpp,参考0006_handleDeviceConnection UML

一旦插上USB声卡,就会创建一个对应的播放线程

驱动程序上报音频拔插事件, 该事件为某个device插入或拔出, Android系统需要切换声音通道。

过程为(核心文件为frameworks/av/services/audiopolicy/AudioPolicyManager.cpp): (核心函数为 setDeviceConnectionState)
a. checkOutputsForDevice
针对该device, 打开新的output, 创建新的playbackthread.
方法:
从audio_policy.conf中确定"本该有多少个output"可以支持它, mOutputs表示"已经打开的output", 两者对比即可确定"尚未打开的output"

b. checkOutputForAllStrategies / checkOutputForStrategy
对所有的strategy分组声音, 判断是否需要迁移到新的output, 如果需要则迁移对应Track到新的output,方法: 
b.1 判断是否需要迁移
对于该strategy, 得到它的oldDevice, 进而得到它的outputs (srcOutputs);
对于该strategy, 得到它的newDevice, 进而得到它的outputs (dstOutputs);
如果这2个srcOutputs、dstOutputs不相同, 表示需要迁移

b.2 如果迁移:
把对应的Track设置为invalidate状态即可, App写AudioTrack时发现它是invalidate状态, 就会重新创建新的Track
    audio_devices_t oldDevice = getDeviceForStrategy(strategy, true /*fromCache*/);
    audio_devices_t newDevice = getDeviceForStrategy(strategy, false /*fromCache*/);
    SortedVector<audio_io_handle_t> srcOutputs = getOutputsForDevice(oldDevice, mPreviousOutputs);
    SortedVector<audio_io_handle_t> dstOutputs = getOutputsForDevice(newDevice, mOutputs);

c. getNewOutputDevice/setOutputDevice
这要操作HAL

8. output的参数信息会构成一个profile结构描述

audio_hw_modules {
  primary {
    outputs {
      primary {    //这些参数信息会构造成一个profile
Android驱动——audio输入输出插拔检测 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 例如:第一章 Python 机器学习入门之pandas的使用 文章目录前言一、audio插拔检测原理图二、驱动相关代码三、插拔事件处理并广播四、测试总结 前言 实现耳机输入输出的插拔检测功能:修改插拔驱动代码,驱动中上报inputevent事件,通过framework处理事件并发出广播; 平台:amlogic soc:a311d 一、audio插拔检测原理图 输入检测gpio口(microphone): 输出检测gpio口(he 阅读详情

相关推荐

android耳机插拔的监听

必须动态注册,否则无效监听 一、主要监听事件 1.监听有线耳机 Intent.ACTION_HEADSET_PLUG android.intent.action.HEADSET_PLUG 2,监听蓝牙耳机 BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED 二、代码实现/** 注册receiver */ public class HeadS

ITxiaobing123的博客 6039

sendStickyBroadcast与Sticky Intent

什么是Sticky Intent呢?我们在最后一次调用sendStickyBroadcast函数来发送某个Action类型的广播时,系统会把代表这个广播的Intent保存下来,这样,后来调用registerReceiver来注册相同Action类型的广播接收器,就会得到这个最后发出的广播。这就是为什么叫做Sticky Intent了,这个最后发出的广播虽然被处理完了,但是仍然被粘住在Activit

Daideshu的博客 871

Android音频子系统-耳机插拔

带着以下几个问题,我们着手分析:Android系统在播放音乐的时候,如果插入耳机,系统会发生什么变化?会产生哪些逻辑?音频是如何从扬声器切换到耳机的?插入3.5mm的耳机孔和usb耳机又有什么不同?

不定时记录分享一些有用没用的东西 2464

Android四大组件——BroadcastReceiver普通广播、有序广播、拦截广播、本地广播、Sticky广播、系统广播

前言 又是一篇基础总结性的文章来啦,个人强迫症犯了,非得把博客的四大组件模块给补齐了,总结了一下BoradcastReceiver的各种使用,废话不多说,开车啦。博主建议自己敲一遍代码来理解广播,因为里面有很多细节的东西需要注意,在学习中博主也犯过一些低级错误,不然以后到大项目中,不牢牢掌握好基础,会浪费很多时间在这上面。或者认真阅读本篇文章内容 BroadcastReceiver的简介

点击置顶文章查看博客目录(全站式导航) 1万+

全面分析总结解决Could not transfer artifact org.apache.maven.plugins等此类问题的方法

全面分析总结解决Could not transfer artifact org.apache.maven.plugins,Could not transfer artifact xxx from/to xxx等此类问题的方法

ccjjjjdff的博客 1万+

BMS开发避坑指南:如何用ADBMS6815实现0.03%精度的电池采样?

本文深入解析了如何利用ADBMS6815等高精度AFE芯片,在车规级BMS开发中实现0.03%级别的电池电压采样精度。文章从芯片选型、硬件设计、软件配置、算法优化到系统验证,提供了一套完整的实战指南,帮助工程师系统性地提升BMS测量精度,从而保障电池安全、延长寿命并增强产品竞争力。

weixin_28314575的博客 134

Android音频子系统(四)------耳机拔插流程

你好!这里是风筝的博客, 欢迎和我一起交流。 耳机上报主要有两种方式: 1.UEvent 2.InputEvent 两种模式的切换可以使用配置项来完成,Android 系统默认是使用UEvent的方式。 不过一般厂商会在自己的xml文件里进行配置。 例如我这板子配置: device/softwinner/ceres-spen/overlay/frameworks/base/core/res/res/values/config.xml:140: <bool name="config_useDev

风筝 4776

PLC编程标准之争:IEC61131与IEC61499核心区别与选型指南

在工业自动化领域,PLC编程长期依赖IEC61131标准,它定义了梯形图、结构化文本等语言,通过周期扫描机制保证确定性执行。然而,随着工业4.0和分布式控制场景的兴起,IEC61499以事件驱动模型打破传统范式,让功能块能跨设备协同,实现更灵活的柔性产线和模块化系统。理解两者在程序结构、数据交互、部署方式和实时性上的差异,是选型与工程落地的关键。本文从基础概念出发,剖析执行模型与架构演进,结合一线调试经验,帮助工程师在传统PLC与下一代标准之间做出理性决策。

weixin_27014595的博客 267

耳机插入检测

可以看到拔插使用了switch_dev_register函数去把自己注册在switch这个子类中,并且根据headset->sdev.name = "h2w"指定了名字是h2w,最后在系统中在/sys/class/switch/h2w下,而h2w的state就是耳机插入状态值,如下是分别是拔插卡耳机的log和state值。使用uevent,上报同步extcon事件,会更新/sys/class/extcon/xxx/state节点的值;不过文件系统下的extcon节点是可以根据插拔改变state值的。

lkdom 2059

人脸情绪识别(使用深度学习和OpenCV)

基于opencv与深度学习的人脸情绪识别系统

mx0390304的博客 8251

UNet 改进(22):结合Transformer结构

保留了UNet的经典编码器-解码器结构在瓶颈层使用Transformer替代传统的卷积层采用跳跃连接保持空间细节信息使用双卷积块(DoubleConv)作为基础构建模块。

2401_82355416的博客 774

android 耳机连接状态查询

近期有个要监听耳机的需求,本以为挺简单,三五行调用个系统方法就能解决了。结果发现还有点复杂,考虑到这块妥妥是一个模块的东西,自己四处查了一些资料后,去伪存真,最终总算整理出了可以直接使用的内容。记录下来,以资来者。耳机样式分为有线和无线两种,而且需要动态监控当前耳机状态,所以要对察插入时间进行监听,有线耳机的接入是有广播的,而无线耳机就没有了,需要做个简单的轮询处理。

kiba_zwei的博客 2757

MIPI RFFE 信号完整性与硬件设计

MIPI RFFE信号完整性设计关键点 摘要:MIPI RFFE作为射频前端控制总线,其信号完整性设计需平衡速率、驱动能力与抗干扰性。核心挑战在于确保接收端在52Mb/s速率下保持足够的电压和时序裕量。关键设计考量包括: 驱动能力优化:过强导致过冲/振铃和射频杂散,过弱则恶化时序裕量,需与总线负载匹配。 波形质量指标:严格控制上升/下降时间(兼顾EMC和时序要求),抑制过冲/振铃幅度,管理VIO电源纹波和耦合噪声。

sinat_15150363的博客 237

linux耳机插拔检测,Android应用开发之耳机插拔处理两种方式

本文将带你了解Android应用开发[RK3288][Android6.0] 耳机插拔处理两种方式,希望本文对大家学Android有所帮助。[RK3288][Android6.0] 耳机插拔处理两种方式。Platform: RockchipOS: Android 6.0Kernel: 3.10.92系统对耳机插拔处理的方式有两种,一种是UEvent直接上报,另一种是通过Input模块来处理...

weixin_32760125的博客 506

YOLOv改进 | YOLOv11更换主干Backbone之MobileNetV2(轻量化主干网络结构--高效轻量移动模型)

本文提出将YOLOv11的主干网络替换为轻量化的MobileNetV2,以提升模型在移动端设备的部署效率。MobileNetV2通过倒残差结构和线性瓶颈层,显著减少了计算量和参数量(降至1M~3M),同时保持多尺度特征提取能力。实验表明,改进后的模型在移动设备上可实现20-30 FPS的实时检测,适用于手机APP、无人机和工业监控等场景。核心代码展示了倒残差块的实现,通过深度可分离卷积和通道扩展/压缩实现高效特征提取。

走向CTO的路上... 1461

ABAP 大规模数据并行处理实战,六套 Parallel Processing Framework 如何选,性能调优真正该盯住什么

如果今天真的面对那份执行 14 个小时的生产报表,我不会马上把原来的 Loop 改成 50 个aRFC。我会先用 SAT、ST12、SQL Monitor 和 HANA 侧工具确认时间到底花在哪里。如果 90% 时间其实消耗在一条低效 SQL 上,Parallel Processing 只是让 50 个 Worker 同时执行那条低效 SQL,数据库会比原来更痛苦。应该先把串行逻辑本身优化到合理水平。该做 Set-based Processing 的地方先交给 HANA。该避免SELECT。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 149

关于Android粘性广播StickyBroadcast的使用及注意事项

粘性广播介绍 粘性广播所发送的广播会在系统中滞留一段时间,当注册了符合的广播接收器的时候,广播接收器会自动接收广播消息。 粘性广播的发送 /** * 发送粘性广播 * */ Intent i = new Intent(); i.setAction("StickyBroadcast"); i.putE...

Alrey的博客 2788

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

四十一、Android Notification通知详解

根据activity的生命周期,在activity不显示时,会执行onStop函数(比如按下home键),所以你在onStop函数(按退出键除外)里面把notification放在通知栏里,再此显示时,把notification从通知栏里去掉。或者,只要程序在运行就一直显示通知栏图标。          下面对Notification类中的一些常量,字段,方法简单介绍一下:常量:DEFAULT...

weixin_34358092的博客 203

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

耳机插入时间

adb shell 查看 P55Nxt:/dev/input # getevent add device 1: /dev/input/event8 name: "xxx-skua-snd-card Headset Jack" add device 2: /dev/input/event7 name: "xxx-skua-snd-card Button Jack" add...

拒绝背八股文 414

Android4.×耳机插拔检测

Android4.2耳机插拔检测实现方法   目录 1. 耳机检测的硬件原理 2 1.1 两种机制的切换 2 1.2 Android耳机插拨检测流程 3 2 InputEvent 3 2.1 Framework层对InputEvent的处理机制 3 2.2 Kernel层的处理机制 4 3. UEvent 5 3.1 Switch 基本原理  5 3.1.1 Switch的实

fengying765的专栏 1万+

Android:有线耳机拔出或蓝牙耳机断开连接,暂停播放

当我们正在“专注学习”的过程中,如果不小心拔掉了耳机线或者断开蓝牙耳机的连接,突然之间我们APP里正在播放的声音就会打扰到周围的人,如果声音还挺大的话,是不是有些尴尬?所以呢,现在需要实现这么一个功能:在有线耳机拔掉或者蓝牙耳机断开连接时,立刻停止播放。

指尖流连敲击梦想,代码行行承载远方! 4198

headset 和 headphone 区别

Android代码里有headset 和 headphone , 意思好像都是耳机,又感觉有点不同,从代码看, headset是指有mic的的耳机,headphone 是没有mic的耳机。 后期的Android版本,插入耳机是否有mic, 状态栏图标不一样。4.4版本以前的,默认是一样的图标。...

caz28的专栏 6827

08.音频系统:第005课_项目实战1_耳麦拔插:第001节_驱动程序上报耳麦拔插事件

在接下来的几个小节中,会讲解几个音频的项目,现在想讲解第一个项目:对 耳麦拔插事件的处理,一起分为四个小节左右: 5.1 驱动程序上报耳麦拔插事件 5.2 在状态栏显示耳麦图标 5.3 耳麦拔插事件调用流程分析 5.4 切换声音通道流程 在讲解驱动程序之前,先讲解几个概念: 如上,对于耳机,有很多类型,带麦克的耳机术语叫做headset,不带麦克的术语叫做headphone: 上面是一个and......

江南才尽江南山,年少无知年少狂! 1622

Android实现检测耳机插入和拔出

在Android下实现检测耳机插入和拔出,也即建立一个Broadcast Receiver,监听“android.intent.action.HEADSET_PLUG”广播 但直接在AndroidManifest.xml中添加一个标签是无效的,如: 你会发现Receiver的on

sunmanzth的专栏 1万+

Android--耳机插拔检测(framework篇)

Android 耳机插拔检测

qq_42364999的博客 1483

Android10 音频系统之耳麦插拔

硬件插上耳麦发生中断, 在中断处理程序中设置声卡让声音从耳机中输出,驱动程序上报音频拔插事件,该事件为某个device插入或拔出,接下来把输出通道的选择权交给android系统,由Android系统进行声音通道的切换操作

yang_0323的博客 1055
上一篇: MJPEG摄像头解码失败
下一篇: audio
seiyaaa
博客等级 码龄16年 151粉丝 45原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值