Android Framework Audio Subsystem (09) headset plug-in process analysis

This series of articles Master link: Thematic sub-directory Android Framework Class Audio Subsystem


Summary and description of key points in this chapter:

This chapter mainly focuses on ➕ the plugging and unplugging of the headset in the upper left of the above mind map. This chapter mainly analyzes the process of plugging and unplugging the headset. After the headset is plugged and unplugged, how android is handled in the framework layer. At the same time, this chapter refers to the input subsystem that is relatively simple to write. For details, please refer to the topic link of the input subsystem: topic sub-category directory Android Framework input subsystem

For the plugging and unplugging of the headset, Android has two modes. You can use the input subsystem or state dev (uevent) to report the plugging and plugging operations. described as follows:

  • Input subsystem: You can report key events or switch events (EV_SW). Event types include headset, headPhone, and lineOut. For input devices, you need to specify the EV_SYN that can generate synchronization events. For key events, the input subsystem also needs to set the range of key values, but it does not need to be set for switch events.
  • Switch class subsystem: Send data to user space through uevent. There is a thread in Android that listens to such events. When using the switch dev subsystem, the name must be set to "h2w", and the Android system listens to the virtual device / sys / class / switch / h2w.

The specific operation depends on the configuration value config_useDevInputEventForAudioJack, which is defined in the config.xml file as follows:

...  
    <!-- When true use the linux /dev/input/event subsystem to detect the switch changes
         on the headphone/microphone jack. When false use the older uevent framework. -->
    <bool name="config_useDevInputEventForAudioJack">false</bool>
...

When the value is true, the input subsystem is used, and when it is false, the uevent mechanism is used. This value is defined in the following file, and the latter file will overwrite the previous one:

frameworks/base/core/res/res/values/config.xml
device/"平台厂商"/frameworks/base/core/res/res/values/config.xml

Next, we will analyze the two modes. The first half of the two modes are inconsistent, and the second half is the same. The main process is to analyze such a process: how the underlying headset plug-in reports the event layer by layer, and finally broadcasts the message through ActivityManagerService.


1 The first half of the analysis (two modes to the updateLocked method in WiredAccessoryManager)

1.1 The process of using the input subsystem (from the input subsystem to the updateLocked method in WiredAccessoryManager)

The input subsystem starts to read events and process them through InputReader. code show as below:

void InputReader::loopOnce() {
    //...
    //获取事件
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    { // acquire lock
        //...
        //处理事件
        processEventsLocked(mEventBuffer, count);
        //...
    } // release lock

    // Send out a message that the describes the changed input devices.
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
    mQueuedListener->flush();
}

The InputReader thread uses the getEvent method in EventHub to obtain the event from the driver and then uses processEventsLocked to start processing. ProcessEventsLocked is implemented as follows:

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count;) {
        int32_t type = rawEvent->type;
        size_t batchSize = 1;
        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
            //...
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {
            //...
        }
        count -= batchSize;
        rawEvent += batchSize;
    }
}

Continue to analyze the implementation of processEventsForDeviceLocked here, the code is as follows:

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
    //...
    InputDevice* device = mDevices.valueAt(deviceIndex);
    //...
    device->process(rawEvents, count);
}

Continue to analyze the InputDevice process method, the code is as follows:

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    size_t numMappers = mMappers.size();
    for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
        if (mDropUntilNextSync) {
            //...
        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
            //...
        } else {
            for (size_t i = 0; i < numMappers; i++) {
                InputMapper* mapper = mMappers[i];
                mapper->process(rawEvent);
            }
        }
    }
}

For the swicth event, the map here is SwitchInputMapper, and its process function is implemented as follows:

void SwitchInputMapper::process(const RawEvent* rawEvent) {
    switch (rawEvent->type) {
    case EV_SW:
        processSwitch(rawEvent->code, rawEvent->value);
        break;

    case EV_SYN:
        if (rawEvent->code == SYN_REPORT) {
            sync(rawEvent->when);
        }
    }
}

Continue to analyze processSwitch, the code is as follows:

void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) {
    if (switchCode >= 0 && switchCode < 32) {
        if (switchValue) {
            mSwitchValues |= 1 << switchCode;
        } else {
            mSwitchValues &= ~(1 << switchCode);
        }
        mUpdatedSwitchMask |= 1 << switchCode;
    }
}

Here is mainly to store the state through the value of mSwitchValues, we continue to analyze the implementation of sync, the code is as follows:

void SwitchInputMapper::sync(nsecs_t when) {
    if (mUpdatedSwitchMask) {
        uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask;
        NotifySwitchArgs args(when, 0, updatedSwitchValues, mUpdatedSwitchMask);
        getListener()->notifySwitch(&args);
        mUpdatedSwitchMask = 0;
    }
}

The getListener ()-> notifySwitch (& args) here is actually the notifySwitch method of InputDispatcher. The code implementation is as follows:

void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) {
    uint32_t policyFlags = args->policyFlags;
    policyFlags |= POLICY_FLAG_TRUSTED;
    mPolicy->notifySwitch(args->eventTime,
            args->switchValues, args->switchMask, policyFlags);
}

The mPolicy-> notifySwitch here will eventually call the notifySwitch method in the InputManagerService of the Java layer after the callback of JNI. The code is as follows:

// Native callback.
private void notifySwitch(long whenNanos, int switchValues, int switchMask) {
    if ((switchMask & SW_LID_BIT) != 0) {
        final boolean lidOpen = ((switchValues & SW_LID_BIT) == 0);
        mWindowManagerCallbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
    }

    if ((switchMask & SW_CAMERA_LENS_COVER_BIT) != 0) {
        final boolean lensCovered = ((switchValues & SW_CAMERA_LENS_COVER_BIT) != 0);
        mWindowManagerCallbacks.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
    }
    //mUseDevInputEventForAudioJack在这里被配置成true,会走该分支
    if (mUseDevInputEventForAudioJack && (switchMask & SW_JACK_BITS) != 0) {
        mWiredAccessoryCallbacks.notifyWiredAccessoryChanged(whenNanos, switchValues,
                switchMask);
    }
}

Continue to analyze notifyWiredAccessoryChanged here, the code implementation is as follows:

@Override
public void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask) {
    synchronized (mLock) {
        int headset;
        mSwitchValues = (mSwitchValues & ~switchMask) | switchValues;
        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;
        }
        updateLocked(NAME_H2W,
            (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) | headset);
    }
}

The updateLocked method is called here. The following analysis will continue in the second half of the analysis.

1.2 The process of using uevent mechanism (from uevent to updateLocked method in WiredAccessoryManager)

The creation and use of WiredAccessoryManager in startOtherServices in systemserver are as follows:

private void startOtherServices() {
    final Context context = mSystemContext;
	//...
        if (!disableMedia) {
            try {
                //关键点1
                inputManager.setWiredAccessoryCallbacks(
                        new WiredAccessoryManager(context, inputManager));
            } catch (Throwable e) {
                reportWtf("starting WiredAccessoryManager", e);
            }
        }
	//...
	final InputManagerService inputManagerF = inputManager;
	//...
	try {
		//关键点2
		if (inputManagerF != null) inputManagerF.systemRunning();
	} catch (Throwable e) {
		reportWtf("Notifying InputManagerService running", e);
	}
	//...
}

@ 1 WiredAccessoryManager created

The constructor code of WiredAccessoryManager is implemented as follows:

public WiredAccessoryManager(Context context, InputManagerService inputManager) {
    //这里涉及三个服务:电源子系统、输入输出子系统、音频子系统
    PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
    mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WiredAccessoryManager");
    mWakeLock.setReferenceCounted(false);
    mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
    mInputManager = inputManager;
    mUseDevInputEventForAudioJack =
            context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
    mObserver = new WiredAccessoryObserver();
}

The WiredAccessoryObserver here inherits the UEventObserver class, UEventObserver is empty, and the constructor code of WiredAccessoryObserver is as follows:

public WiredAccessoryObserver() {
    mUEventInfo = makeObservedUEventList();
}

Here the code of makeObservedUEventList is implemented as follows:

private List<UEventInfo> makeObservedUEventList() {
    List<UEventInfo> retVal = new ArrayList<UEventInfo>();
    UEventInfo uei;

    //这里只监听这三个UEventInfo对应的事件,驱动程序通过uevent上报事件也只能上报这三个事件之一
    // Monitor h2w
    if (!mUseDevInputEventForAudioJack) {
                    //用于监听名为"h2w"这个Switch class驱动中创建的虚拟文件                                     
                    //./devices/virtual/switch/h2w
                    //./class/switch/h2w
        uei = new UEventInfo(NAME_H2W, BIT_HEADSET, BIT_HEADSET_NO_MIC, BIT_LINEOUT);
        if (uei.checkSwitchExists()) {
            retVal.add(uei);
        }
    }

    // Monitor USB
    uei = new UEventInfo(NAME_USB_AUDIO, BIT_USB_HEADSET_ANLG, BIT_USB_HEADSET_DGTL, 0);
    if (uei.checkSwitchExists()) {
        retVal.add(uei);
    } 
    uei = new UEventInfo(NAME_HDMI_AUDIO, BIT_HDMI_AUDIO, 0, 0);
    if (uei.checkSwitchExists()) {
        retVal.add(uei);
    } else {
        uei = new UEventInfo(NAME_HDMI, BIT_HDMI_AUDIO, 0, 0);
        if (uei.checkSwitchExists()) {
            retVal.add(uei);
        }
    }
    return retVal;
}

@ 2 inputManagerF's systemRunning () function analysis

The inputManagerF.systemRunning () code is implemented as follows:

public void systemRunning() {
    //...
    if (mWiredAccessoryCallbacks != null) {
        mWiredAccessoryCallbacks.systemReady();
    }
}

Here the systemReady code is implemented as follows:

@Override
public void systemReady() {
    synchronized (mLock) {
        mWakeLock.acquire();
        Message msg = mHandler.obtainMessage(MSG_SYSTEM_READY, 0, 0, null);
        mHandler.sendMessage(msg);
    }
}

The handler code to process the MSG_SYSTEM_READY message is as follows:

private final Handler mHandler = new Handler(Looper.myLooper(), null, true) {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_NEW_DEVICE_STATE:
                setDevicesState(msg.arg1, msg.arg2, (String)msg.obj);
                mWakeLock.release();
                break;
            case MSG_SYSTEM_READY:
                onSystemReady();
                mWakeLock.release();
                break;
        }
    }
};

Here will call onSystemReady, the code implementation is as follows:

private void onSystemReady() {
    if (mUseDevInputEventForAudioJack) {
        int switchValues = 0;
        if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_HEADPHONE_INSERT) == 1) {
            switchValues |= SW_HEADPHONE_INSERT_BIT;
        }
        if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_MICROPHONE_INSERT) == 1) {
            switchValues |= SW_MICROPHONE_INSERT_BIT;
        }
        if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_LINEOUT_INSERT) == 1) {
            switchValues |= SW_LINEOUT_INSERT_BIT;
        }
        notifyWiredAccessoryChanged(0, switchValues,
                SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT);
    }
    mObserver.init();
}

Continue to analyze mObserver's init () method, the code is implemented as follows:

void init() {
    //...
    for (int i = 0; i < mUEventInfo.size(); ++i) {
        UEventInfo uei = mUEventInfo.get(i);
        startObserving("DEVPATH="+uei.getDevPath());
    }
}

Continue to analyze startObserving here, the code implementation is as follows:

public final void startObserving(String match) {
    final UEventThread t = getThread();
    t.addObserver(match, this);
}

Continue to look at the implementation of getThread, the code is as follows:

private static UEventThread getThread() {
    synchronized (UEventObserver.class) {
        if (sThread == null) {
            sThread = new UEventThread();
            sThread.start();
        }
        return sThread;
    }
}

When start is executed here, the run method of UEventThread will be called, and the code implementation is as follows:

public void run() {
    nativeSetup();
    while (true) {
        //...
        //等待事件,native层实现,从系统底层接收消息
        String message = nativeWaitForNextEvent();
        if (message != null) {
            //发送消息
            sendEvent(message);
        }
    }
}

Sending messages is implemented as follows:

private void sendEvent(String message) {
    //...
    if (!mTempObserversToSignal.isEmpty()) {
        final UEvent event = new UEvent(message);
        final int N = mTempObserversToSignal.size();
        for (int i = 0; i < N; i++) {
            final UEventObserver observer = mTempObserversToSignal.get(i);
            observer.onUEvent(event);
        }
        mTempObserversToSignal.clear();
    }
}

Continue to analyze the implementation of the observer's onUEvent (...) method, which is empty. The implementation of the subclass WiredAccessoryObserver will be called here.

@Override
public void onUEvent(UEventObserver.UEvent event) {
    try {
        String devPath = event.get("DEVPATH");
        String name = event.get("SWITCH_NAME");
        int state = Integer.parseInt(event.get("SWITCH_STATE"));
        synchronized (mLock) {
            updateStateLocked(devPath, name, state);
        }
    } catch (NumberFormatException e) {
        Slog.e(TAG, "Could not parse switch state from event " + event);
    }
}

Continue to analyze updateStateLocked, the code implementation is as follows:

private void updateStateLocked(String devPath, String name, int state) {
    for (int i = 0; i < mUEventInfo.size(); ++i) {
        UEventInfo uei = mUEventInfo.get(i);
        if (devPath.equals(uei.getDevPath())) {
            updateLocked(name, uei.computeNewHeadsetState(mHeadsetState, state));
            return;
        }
    }
}

The updateLocked method is called here. The following analysis will continue in the second half of the analysis.


2 The second half of the analysis (from the updateLocked method in WiredAccessoryManager to the upper ActivityManagerService to send broadcasts)

WiredAccessoryManager's updateLocked code is implemented as follows:

private void updateLocked(String newName, int newState) {
    //...
    mWakeLock.acquire();
	//获得并发送message
    Message msg = mHandler.obtainMessage(MSG_NEW_DEVICE_STATE, headsetState,
            mHeadsetState, newName);
    mHandler.sendMessage(msg);
    mHeadsetState = headsetState;
}

The handler here handles the message MSG_NEW_DEVICE_STATE as follows:

private final Handler mHandler = new Handler(Looper.myLooper(), null, true) {
@Override
public void handleMessage(Message msg) {
    switch (msg.what) {
        case MSG_NEW_DEVICE_STATE:
            setDevicesState(msg.arg1, msg.arg2, (String)msg.obj);
            mWakeLock.release();
            break;
        case MSG_SYSTEM_READY:
            onSystemReady();
            mWakeLock.release();
            break;
    }
}

Focus on the implementation of setDevicesState here, the code is as follows:

private void setDevicesState(
        int headsetState, int prevHeadsetState, String headsetName) {
    synchronized (mLock) {
        int allHeadsets = SUPPORTED_HEADSETS;
        for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) {
            if ((curHeadset & allHeadsets) != 0) {
                setDeviceStateLocked(curHeadset, headsetState, prevHeadsetState, headsetName);
                allHeadsets &= ~curHeadset;
            }
        }
    }
}

Here is concerned about the implementation of setDeviceStateLocked, the code is as follows:

private void setDeviceStateLocked(int headset,
        int headsetState, int prevHeadsetState, String headsetName) {
    if ((headsetState & headset) != (prevHeadsetState & headset)) {
        int outDevice = 0;
        int inDevice = 0;
        int state;

        if ((headsetState & headset) != 0) {
            state = 1;
        } else {
            state = 0;
        }

        if (headset == BIT_HEADSET) {
            outDevice = AudioManager.DEVICE_OUT_WIRED_HEADSET;
            inDevice = AudioManager.DEVICE_IN_WIRED_HEADSET;
        } else if (headset == BIT_HEADSET_NO_MIC){
            outDevice = AudioManager.DEVICE_OUT_WIRED_HEADPHONE;
        } else if (headset == BIT_LINEOUT){
            outDevice = AudioManager.DEVICE_OUT_LINE;
        } else if (headset == BIT_USB_HEADSET_ANLG) {
            outDevice = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET;
        } else if (headset == BIT_USB_HEADSET_DGTL) {
            outDevice = AudioManager.DEVICE_OUT_DGTL_DOCK_HEADSET;
        } else if (headset == BIT_HDMI_AUDIO) {
            outDevice = AudioManager.DEVICE_OUT_HDMI;
        } else {
            Slog.e(TAG, "setDeviceState() invalid headset type: "+headset);
            return;
        }
        if (outDevice != 0) {
          //进入了Audio系统,设置有线设备的连接状态
          mAudioManager.setWiredDeviceConnectionState(outDevice, state, headsetName);
        }
        if (inDevice != 0) {
          mAudioManager.setWiredDeviceConnectionState(inDevice, state, headsetName);
        }
    }
}

Here pay attention to the implementation of the setWiredDeviceConnectionState method of mAudioManager, the code is as follows:

public void setWiredDeviceConnectionState(int device, int state, String name) {
    IAudioService service = getService();
    try {
        service.setWiredDeviceConnectionState(device, state, name);
    } catch (RemoteException e) {
        Log.e(TAG, "Dead object in setWiredDeviceConnectionState "+e);
    }
}

Continue to analyze setWiredDeviceConnectionState, the code implementation is as follows:

public void setWiredDeviceConnectionState(int device, int state, String name) {
    synchronized (mConnectedDevices) {
        int delay = checkSendBecomingNoisyIntent(device, state);
        queueMsgUnderWakeLock(mAudioHandler,
                MSG_SET_WIRED_DEVICE_CONNECTION_STATE,device,state,name,delay);
    }
}

The queueMsgUnderWakeLock code here is implemented as follows:

private void queueMsgUnderWakeLock(Handler handler, int msg,
        int arg1, int arg2, Object obj, int delay) {
    final long ident = Binder.clearCallingIdentity();
    mAudioEventWakeLock.acquire();
    Binder.restoreCallingIdentity(ident);
    sendMsg(handler, msg, SENDMSG_QUEUE, arg1, arg2, obj, delay);
}

The handler processes the MSG_SET_WIRED_DEVICE_CONNECTION_STATE message, the code is as follows:

@Override
public void handleMessage(Message msg) {
    switch (msg.what) {
        case MSG_SET_DEVICE_VOLUME:
            setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1);
            break;
        //...
        case MSG_SET_WIRED_DEVICE_CONNECTION_STATE:
            onSetWiredDeviceConnectionState(msg.arg1, msg.arg2, (String)msg.obj);
            mAudioEventWakeLock.release();
            break;
        //...
    }
}

The code implementation of onSetWiredDeviceConnectionState is as follows:

private void onSetWiredDeviceConnectionState(int device, int state, String name)
{
    synchronized (mConnectedDevices) {
        //...
        //关键点1:声道切换入口
        handleDeviceConnection((state == 1), device, (isUsb ? name : ""));
        if (state != 0) {
            //...
            if ((device & mSafeMediaVolumeDevices) != 0) {
                sendMsg(mAudioHandler,MSG_CHECK_MUSIC_ACTIVE,SENDMSG_REPLACE,0,0,null,MUSIC_ACTIVE_POLL_PERIOD_MS);
            }
            //...
        } else {
            //...
        }
        if (!isUsb && (device != AudioSystem.DEVICE_IN_WIRED_HEADSET)) {
            //关键点2:通过AMS上报intent
            sendDeviceConnectionIntent(device, state, name);
        }
    }
}

From here we have two key points to pay attention to, the channel switch entry handleDeviceConnection and the sendDeviceConnectionIntent that reports the intent to AMS. 

@ 1 Continue to analyze handleDeviceConnection, the code implementation is as follows:

private boolean handleDeviceConnection(boolean connected, int device, String params) {
    synchronized (mConnectedDevices) {
        boolean isConnected = (mConnectedDevices.containsKey(device) &&
                (params.isEmpty() || mConnectedDevices.get(device).equals(params)));

        if (isConnected && !connected) {
            AudioSystem.setDeviceConnectionState(device,
                                          AudioSystem.DEVICE_STATE_UNAVAILABLE,
                                          mConnectedDevices.get(device));
             mConnectedDevices.remove(device);
             return true;
        } else if (!isConnected && connected) {
             AudioSystem.setDeviceConnectionState(device,
                                                  AudioSystem.DEVICE_STATE_AVAILABLE,
                                                  params);
             mConnectedDevices.put(new Integer(device), params);
             return true;
        }
    }
    return false;
}

The setDeviceConnectionState method of AudioSystem is called here, and its JNI mapping relationship is:

{"setDeviceConnectionState", "(IILjava/lang/String;)I", (void *)android_media_AudioSystem_setDeviceConnectionState}

Continue to analyze the implementation of android_media_AudioSystem_setDeviceConnectionState, the code is as follows:

static jint
android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobject thiz, jint device, jint state, jstring device_address)
{
    const char *c_address = env->GetStringUTFChars(device_address, NULL);
    int status = check_AudioSystem_Command(AudioSystem::setDeviceConnectionState(static_cast <audio_devices_t>(device),
                                          static_cast <audio_policy_dev_state_t>(state),
                                          c_address));
    env->ReleaseStringUTFChars(device_address, c_address);
    return (jint) status;
}

Here we focus on analyzing the setDeviceConnectionState of the AudioSystem in the Native layer. The code implementation is as follows:

status_t AudioSystem::setDeviceConnectionState(audio_devices_t device,
                                               audio_policy_dev_state_t state,
                                               const char *device_address)
{
    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
    const char *address = "";
    //...
    if (device_address != NULL) {
        address = device_address;
    }
    return aps->setDeviceConnectionState(device, state, address);
}

The setDeviceConnectionState method of AudioPolicyManager is returned here. The code implementation is as follows:

status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device,
                                                          audio_policy_dev_state_t state,
                                                  const char *device_address)
{
    return setDeviceConnectionStateInt(device, state, device_address);
}

Here we only need to know that the handleDeviceConnection method of the AudioService in the Java layer can finally be directly called to the setDeviceConnectionStateInt of the AudioPolicyManager in the Native layer. Later, we will specifically interpret the setDeviceConnectionStateInt part of the code in detail.

@ 2 Continue to analyze sendDeviceConnectionIntent, the code implementation is as follows:

private void sendDeviceConnectionIntent(int device, int state, String name)
{
    //构建一个Intent结构,然后向应用程序广播它,注册对这个Intent感兴趣的App就会收到它
    Intent intent = new Intent();
    intent.putExtra("state", state);
    intent.putExtra("name", name);
    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
    //...
    //intent其他处理
    //...
    try {
        
        ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
    } finally {
        //...
    }
}

At this point, by building an Intent structure and then broadcasting it to the application, the App that is interested in the Intent will receive it and the control will be given to the APP. Later, the intent will be received and processed in the form of a case.

 

Published 289 original articles · praised 47 · 30,000+ views

Guess you like

Origin blog.csdn.net/vviccc/article/details/105394043