Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 12e325e6 authored by Terry Cheong's avatar Terry Cheong Committed by Android (Google) Code Review
Browse files

Merge changes Id934ff95,Ib1fd68a6,If7ac3aeb into main

* changes:
  Add WiredAccessoryManagerUnitTest
  Extract logic in notifyWiredAccessoryChanged to make it testable
  Treat SND_JACK_AVOUT as HDMI audio jack
parents c13f76aa fb7c2cf8
Loading
Loading
Loading
Loading
+69 −40
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import static com.android.server.input.InputManagerService.SW_HEADPHONE_INSERT;
import static com.android.server.input.InputManagerService.SW_HEADPHONE_INSERT_BIT;
import static com.android.server.input.InputManagerService.SW_LINEOUT_INSERT;
import static com.android.server.input.InputManagerService.SW_LINEOUT_INSERT_BIT;
import static com.android.server.input.InputManagerService.SW_VIDEOOUT_INSERT;
import static com.android.server.input.InputManagerService.SW_VIDEOOUT_INSERT_BIT;
import static com.android.server.input.InputManagerService.SW_MICROPHONE_INSERT;
import static com.android.server.input.InputManagerService.SW_MICROPHONE_INSERT_BIT;

@@ -66,6 +68,9 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
    private static final int SUPPORTED_HEADSETS = (BIT_HEADSET | BIT_HEADSET_NO_MIC |
            BIT_USB_HEADSET_ANLG | BIT_USB_HEADSET_DGTL |
            BIT_HDMI_AUDIO | BIT_LINEOUT);
    private static final int SW_HEADSET_INSERT_BITS =
            SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT;
    private static final int SW_AVOUT_INSERT_BITS = SW_LINEOUT_INSERT_BIT | SW_VIDEOOUT_INSERT_BIT;

    private static final String NAME_H2W = "h2w";
    private static final String NAME_USB_AUDIO = "usb_audio";
@@ -82,8 +87,6 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {

    private int mHeadsetState;

    private int mSwitchValues;

    private final WiredAccessoryObserver mObserver;
    private final WiredAccessoryExtconObserver mExtconObserver;
    private final InputManagerService mInputManager;
@@ -118,13 +121,27 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
            if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_LINEOUT_INSERT) == 1) {
                switchValues |= SW_LINEOUT_INSERT_BIT;
            }
            if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_VIDEOOUT_INSERT) == 1) {
                switchValues |= SW_VIDEOOUT_INSERT_BIT;
            }
            // Making our best guess here.
            int headphone = switchValues & SW_HEADSET_INSERT_BITS;
            if (headphone != 0) {
                notifyWiredAccessoryChanged(
                            0,
                    switchValues,
                    SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT,
                            headphone,
                            headphone,
                            true /*isSynchronous*/);
            }

            int lineout_or_hdmi = switchValues & SW_AVOUT_INSERT_BITS;
            if (lineout_or_hdmi != 0) {
                notifyWiredAccessoryChanged(
                            0,
                            lineout_or_hdmi,
                            lineout_or_hdmi,
                            true /*isSynchronous*/);
            }
        }

        if (ExtconUEventObserver.extconExists()) {
            if (mUseDevInputEventForAudioJack) {
@@ -137,6 +154,46 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
        }
    }

    static int calculateHeadsetState(int headsetState, int switchValues, int switchMask) {
        // Assumptions:
        // 1. Events will only be plug 1 device or unplug 1 device.
        //    It would not have plug and unplug in the same time.
        // 2. events for LINEOUT devices won't have a mask that
        //    is SW_AVOUT_INSERT_BITS
        int newHeadsetState = headsetState;
        if ((switchMask & SW_HEADSET_INSERT_BITS) != 0) {
            int clearMask = BIT_HEADSET | BIT_HEADSET_NO_MIC;

            newHeadsetState = newHeadsetState & ~clearMask;
            int device = switch(switchValues & SW_HEADSET_INSERT_BITS) {
                case SW_HEADPHONE_INSERT_BIT -> BIT_HEADSET_NO_MIC;
                case SW_MICROPHONE_INSERT_BIT -> BIT_HEADSET;
                case SW_HEADSET_INSERT_BITS -> BIT_HEADSET;
                default -> 0;
            };
            newHeadsetState = newHeadsetState | device;
        }

        if ((switchMask & SW_AVOUT_INSERT_BITS) != 0) {
            int clearMask = switch(switchMask & SW_AVOUT_INSERT_BITS) {
                case SW_LINEOUT_INSERT_BIT -> BIT_LINEOUT;
                case SW_AVOUT_INSERT_BITS -> BIT_HDMI_AUDIO;
                default -> 0;
            };

            newHeadsetState = newHeadsetState & ~clearMask;
            int device = switch(switchValues & SW_AVOUT_INSERT_BITS) {
                case SW_LINEOUT_INSERT_BIT -> BIT_LINEOUT;
                case SW_AVOUT_INSERT_BITS -> BIT_HDMI_AUDIO;
                default -> 0;
            };

            newHeadsetState = newHeadsetState | device;
        }

        return newHeadsetState;
    }

    @Override
    public void notifyWiredAccessoryChanged(
            long whenNanos, int switchValues, int switchMask) {
@@ -152,40 +209,12 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
        }

        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;
            // Extracting the logic for unit test
            int newHeadsetState = calculateHeadsetState(mHeadsetState, switchValues, switchMask);

                default:
                    headset = 0;
                    break;
            updateLocked(NAME_H2W, newHeadsetState, isSynchronous);
        }

            updateLocked(
                    NAME_H2W,
                    (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) | headset,
                    isSynchronous);
        }
    }

    @Override
+7 −2
Original line number Diff line number Diff line
@@ -424,6 +424,9 @@ public class InputManagerService extends IInputManager.Stub
    /** Switch code: Headphone/Microphone Jack.  When set, something is inserted. */
    public static final int SW_JACK_PHYSICAL_INSERT = 0x07;

    /** Switch code: Video Jack.  When set, something is inserted. */
    public static final int SW_VIDEOOUT_INSERT = 0x08;

    /** Switch code: Camera lens cover. When set the lens is covered. */
    public static final int SW_CAMERA_LENS_COVER = 0x09;

@@ -437,8 +440,10 @@ public class InputManagerService extends IInputManager.Stub
    public static final int SW_MICROPHONE_INSERT_BIT = 1 << SW_MICROPHONE_INSERT;
    public static final int SW_LINEOUT_INSERT_BIT = 1 << SW_LINEOUT_INSERT;
    public static final int SW_JACK_PHYSICAL_INSERT_BIT = 1 << SW_JACK_PHYSICAL_INSERT;
    public static final int SW_JACK_BITS =
            SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_JACK_PHYSICAL_INSERT_BIT | SW_LINEOUT_INSERT_BIT;
    public static final int SW_VIDEOOUT_INSERT_BIT = 1 << SW_VIDEOOUT_INSERT;
    public static final int SW_JACK_BITS = SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT
                                           | SW_JACK_PHYSICAL_INSERT_BIT | SW_LINEOUT_INSERT_BIT
                                           | SW_VIDEOOUT_INSERT_BIT;
    public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER;
    public static final int SW_MUTE_DEVICE_BIT = 1 << SW_MUTE_DEVICE;

+213 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server;

import static com.android.server.input.InputManagerService.SW_HEADPHONE_INSERT_BIT;
import static com.android.server.input.InputManagerService.SW_LINEOUT_INSERT_BIT;
import static com.android.server.input.InputManagerService.SW_MICROPHONE_INSERT_BIT;
import static com.android.server.input.InputManagerService.SW_VIDEOOUT_INSERT_BIT;

import static org.junit.Assert.assertEquals;

import androidx.test.runner.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
public class WiredAccessoryManagerUnitTest {
    private static final String TAG = "WiredAccessoryManUnitTest";

    // The values defined here must match the ones in WiredAccessoryManager.
    // See L60-L65 in WiredAccessoryManager.java
    private static final int BIT_HEADSET = (1 << 0);
    private static final int BIT_HEADSET_NO_MIC = (1 << 1);
    private static final int BIT_USB_HEADSET_ANLG = (1 << 2);
    private static final int BIT_USB_HEADSET_DGTL = (1 << 3);
    private static final int BIT_HDMI_AUDIO = (1 << 4);
    private static final int BIT_LINEOUT = (1 << 5);
    private static final int SW_HEADSET_INSERT_BITS =
            SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT;
    private static final int SW_AVOUT_INSERT_BITS = SW_LINEOUT_INSERT_BIT | SW_VIDEOOUT_INSERT_BIT;

    @Test
    public void plugHeadphoneTest() {
        // Use mHeadsetState variable to represents the internal state.
        // WiredAccessoryManager.mHeadsetState
        int mHeadsetState = 0;
        // Headphone Plug Event:
        int switchValue = SW_HEADPHONE_INSERT_BIT;
        int switchMask = SW_HEADPHONE_INSERT_BIT;
        mHeadsetState =
                WiredAccessoryManager.calculateHeadsetState(mHeadsetState, switchValue, switchMask);
        assertEquals("Expect headphone plugged in", BIT_HEADSET_NO_MIC, mHeadsetState);
        // Headphone Unplug Event:
        switchValue = 0;
        switchMask = SW_HEADPHONE_INSERT_BIT;
        mHeadsetState =
                WiredAccessoryManager.calculateHeadsetState(mHeadsetState, switchValue, switchMask);
        assertEquals("Expect headphone unplugged", 0, mHeadsetState);
        // Check cases when value != mask
        switchValue = SW_HEADPHONE_INSERT_BIT;
        switchMask = SW_HEADSET_INSERT_BITS;
        mHeadsetState =
                WiredAccessoryManager.calculateHeadsetState(mHeadsetState, switchValue, switchMask);
        assertEquals("Expect headphone plugged in", BIT_HEADSET_NO_MIC, mHeadsetState);
        // Unplug
        switchValue = 0;
        switchMask = SW_HEADSET_INSERT_BITS;
        mHeadsetState =
                WiredAccessoryManager.calculateHeadsetState(mHeadsetState, switchValue, switchMask);
        assertEquals("Expect headphone unplugged", 0, mHeadsetState);
    }

    @Test
    public void plugHeadphoneAndHdmiTest() {
        // Use mHeadsetState variable to represents the internal state.
        // WiredAccessoryManager.mHeadsetState
        int mHeadsetState = 0;
        // Headphone Plug Event:
        // value = SW_HEADPHONE_INSERT_BIT, mask = SW_HEADPHONE_INSERT_BIT
        // HDMI Plug Event:
        // value = SW_AVOUT_INSERT_BITS, mask = SW_AVOUT_INSERT_BITS

        // Plug headphone
        int switchValue = SW_HEADPHONE_INSERT_BIT;
        int switchMask = SW_HEADPHONE_INSERT_BIT;
        mHeadsetState =
                WiredAccessoryManager.calculateHeadsetState(mHeadsetState, switchValue, switchMask);
        assertEquals("Expect headphone plugged in", BIT_HEADSET_NO_MIC, mHeadsetState);

        // Plug HDMI
        switchValue = SW_AVOUT_INSERT_BITS;
        switchMask = SW_AVOUT_INSERT_BITS;
        mHeadsetState =
                WiredAccessoryManager.calculateHeadsetState(mHeadsetState, switchValue, switchMask);
        assertEquals(
                "Expect headphone and HDMI plugged in",
                BIT_HEADSET_NO_MIC | BIT_HDMI_AUDIO,
                mHeadsetState);

        switchValue = 0;
        switchMask = SW_HEADPHONE_INSERT_BIT;
        mHeadsetState =
                WiredAccessoryManager.calculateHeadsetState(mHeadsetState, switchValue, switchMask);
        assertEquals("Expect headphone unplugged", BIT_HDMI_AUDIO, mHeadsetState);

        switchValue = 0;
        switchMask = SW_AVOUT_INSERT_BITS;
        mHeadsetState =
                WiredAccessoryManager.calculateHeadsetState(mHeadsetState, switchValue, switchMask);
        assertEquals("Expect HDMI unplugged", 0, mHeadsetState);

        switchValue = SW_AVOUT_INSERT_BITS;
        switchMask = SW_AVOUT_INSERT_BITS;
        mHeadsetState =
                WiredAccessoryManager.calculateHeadsetState(mHeadsetState, switchValue, switchMask);
        assertEquals("Expect HDMI plugged in", BIT_HDMI_AUDIO, mHeadsetState);

        // Plug headphone
        switchValue = SW_HEADPHONE_INSERT_BIT;
        switchMask = SW_HEADPHONE_INSERT_BIT;
        mHeadsetState =
                WiredAccessoryManager.calculateHeadsetState(mHeadsetState, switchValue, switchMask);
        assertEquals(
                "Expect both headphone and HDMI plugged in",
                BIT_HDMI_AUDIO | BIT_HEADSET_NO_MIC,
                mHeadsetState);

        switchValue = 0;
        switchMask = SW_AVOUT_INSERT_BITS;
        mHeadsetState =
                WiredAccessoryManager.calculateHeadsetState(mHeadsetState, switchValue, switchMask);
        assertEquals("Expect HDMI unplugged", BIT_HEADSET_NO_MIC, mHeadsetState);

        switchValue = 0;
        switchMask = SW_HEADPHONE_INSERT_BIT;
        mHeadsetState =
                WiredAccessoryManager.calculateHeadsetState(mHeadsetState, switchValue, switchMask);
        assertEquals("Expect headphone unplugged", 0, mHeadsetState);
    }

    @Test
    public void plugHeadsetAndLineoutTest() {
        // Use mHeadsetState variable to represents the internal state.
        // WiredAccessoryManager.mHeadsetState
        int mHeadsetState = 0;

        // Headset Plug Event:
        // value = SW_HEADPHONE_INSERT_BIT, mask = SW_HEADPHONE_INSERT_BIT
        // Lineout Plug Event:
        // value = SW_LINEOUT_INSERT_BIT, mask = SW_LINEOUT_INSERT_BIT

        // Plug headphone
        int switchValue = SW_HEADPHONE_INSERT_BIT;
        int switchMask = SW_HEADPHONE_INSERT_BIT;
        mHeadsetState =
                WiredAccessoryManager.calculateHeadsetState(mHeadsetState, switchValue, switchMask);
        assertEquals("Expect headphone plugged in", BIT_HEADSET_NO_MIC, mHeadsetState);

        // Plug Lineout
        switchValue = SW_LINEOUT_INSERT_BIT;
        switchMask = SW_LINEOUT_INSERT_BIT;
        int newHeadsetState =
                WiredAccessoryManager.calculateHeadsetState(mHeadsetState, switchValue, switchMask);
        assertEquals(
                "Expect headphone and lineout plugged in",
                BIT_HEADSET_NO_MIC | BIT_LINEOUT,
                newHeadsetState);
        // updateLocked will reject newHeadsetState
        // so we do not update mHeadsetState here.

        switchValue = 0;
        switchMask = SW_HEADPHONE_INSERT_BIT;
        mHeadsetState =
                WiredAccessoryManager.calculateHeadsetState(mHeadsetState, switchValue, switchMask);
        assertEquals("Expect headphone unplugged", 0, mHeadsetState);

        switchValue = 0;
        switchMask = SW_LINEOUT_INSERT_BIT;
        mHeadsetState =
                WiredAccessoryManager.calculateHeadsetState(mHeadsetState, switchValue, switchMask);
        assertEquals("Expect LINEOUT unplugged", 0, mHeadsetState);
    }

    @Test
    public void plugLineoutAndHdmiTest() {
        int mHeadsetState = 0;

        // Plug HDMI
        int switchValue = SW_AVOUT_INSERT_BITS;
        int switchMask = SW_AVOUT_INSERT_BITS;
        mHeadsetState =
                WiredAccessoryManager.calculateHeadsetState(mHeadsetState, switchValue, switchMask);
        assertEquals("Expect HDMI plugged in", BIT_HDMI_AUDIO, mHeadsetState);

        // Plug lineout
        switchValue = SW_LINEOUT_INSERT_BIT;
        switchMask = SW_LINEOUT_INSERT_BIT;
        mHeadsetState =
                WiredAccessoryManager.calculateHeadsetState(mHeadsetState, switchValue, switchMask);
        assertEquals("Expect LINEOUT plugged in", BIT_LINEOUT | BIT_HDMI_AUDIO, mHeadsetState);

        switchValue = 0;
        switchMask = SW_LINEOUT_INSERT_BIT;
        mHeadsetState =
                WiredAccessoryManager.calculateHeadsetState(mHeadsetState, switchValue, switchMask);
        assertEquals("Expect LINEOUT unplugged", BIT_HDMI_AUDIO, mHeadsetState);
    }
}