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

Commit ac334149 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Detect extcon device type from cable name."

parents 19071521 12b5dedf
Loading
Loading
Loading
Loading
+137 −33
Original line number Diff line number Diff line
@@ -16,17 +16,19 @@
package com.android.server;

import android.annotation.Nullable;
import android.annotation.StringDef;
import android.os.FileUtils;
import android.os.UEventObserver;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * A specialized UEventObserver that receives UEvents from the kernel for devices in the {@code
@@ -87,41 +89,149 @@ public abstract class ExtconUEventObserver extends UEventObserver {

    /** An External Connection to watch. */
    public static final class ExtconInfo {
        private static final String TAG = "ExtconInfo";
        /* Copied from drivers/extcon/extcon.c */

        /* USB external connector */
        public static final String EXTCON_USB = "USB";
        public static final String EXTCON_USB_HOST = "USB-HOST";

        /* Charger external connector */
        public static final String EXTCON_TA = "TA";
        public static final String EXTCON_FAST_CHARGER = "FAST-CHARGER";
        public static final String EXTCON_SLOW_CHARGER = "SLOW-CHARGER";
        public static final String EXTCON_CHARGE_DOWNSTREAM = "CHARGE-DOWNSTREAM";

        /* Audio/Video external connector */
        public static final String EXTCON_LINE_IN = "LINE-IN";
        public static final String EXTCON_LINE_OUT = "LINE-OUT";
        public static final String EXTCON_MICROPHONE = "MICROPHONE";
        public static final String EXTCON_HEADPHONE = "HEADPHONE";

        public static final String EXTCON_HDMI = "HDMI";
        public static final String EXTCON_MHL = "MHL";
        public static final String EXTCON_DVI = "DVI";
        public static final String EXTCON_VGA = "VGA";
        public static final String EXTCON_SPDIF_IN = "SPDIF-IN";
        public static final String EXTCON_SPDIF_OUT = "SPDIF-OUT";
        public static final String EXTCON_VIDEO_IN = "VIDEO-IN";
        public static final String EXTCON_VIDEO_OUT = "VIDEO-OUT";

        /* Etc external connector */
        public static final String EXTCON_DOCK = "DOCK";
        public static final String EXTCON_JIG = "JIG";
        public static final String EXTCON_MECHANICAL = "MECHANICAL";

        @StringDef({
                EXTCON_USB,
                EXTCON_USB_HOST,
                EXTCON_TA,
                EXTCON_FAST_CHARGER,
                EXTCON_SLOW_CHARGER,
                EXTCON_CHARGE_DOWNSTREAM,
                EXTCON_LINE_IN,
                EXTCON_LINE_OUT,
                EXTCON_MICROPHONE,
                EXTCON_HEADPHONE,
                EXTCON_HDMI,
                EXTCON_MHL,
                EXTCON_DVI,
                EXTCON_VGA,
                EXTCON_SPDIF_IN,
                EXTCON_SPDIF_OUT,
                EXTCON_VIDEO_IN,
                EXTCON_VIDEO_OUT,
                EXTCON_DOCK,
                EXTCON_JIG,
                EXTCON_MECHANICAL,
        })

        public @interface ExtconDeviceType {}

        private static final Object sLock = new Object();
        private static ExtconInfo[] sExtconInfos = null;

        /** Returns a new list of all external connections whose name matches {@code regex}. */
        public static List<ExtconInfo> getExtconInfos(@Nullable String regex) {
            if (!extconExists()) {
                return new ArrayList<>(0);  // Always return a new list.
        private final String mName;
        private final @ExtconDeviceType HashSet<String> mDeviceTypes = new HashSet<>();

        @GuardedBy("sLock")
        private static void initExtconInfos() {
            if (sExtconInfos != null) {
                return;
            }
            Pattern p = regex == null ? null : Pattern.compile(regex);

            File file = new File("/sys/class/extcon");
            File[] files = file.listFiles();
            if (files == null) {
                Slog.wtf(TAG, file + " exists " + file.exists() + " isDir " + file.isDirectory()
                Slog.w(TAG,
                        file + " exists " + file.exists() + " isDir " + file.isDirectory()
                                + " but listFiles returns null."
                                + SELINUX_POLICIES_NEED_TO_BE_CHANGED);
                return new ArrayList<>(0);  // Always return a new list.
                sExtconInfos = new ExtconInfo[0];
            } else {
                ArrayList list = new ArrayList(files.length);
                List<ExtconInfo> list = new ArrayList<>(files.length);
                for (File f : files) {
                    String name = f.getName();
                    if (p == null || p.matcher(name).matches()) {
                        ExtconInfo uei = new ExtconInfo(name);
                        list.add(uei);
                        if (LOG) Slog.d(TAG, name + " matches " + regex);
                    } else {
                        if (LOG) Slog.d(TAG, name + " does not match " + regex);
                    list.add(new ExtconInfo(f.getName()));
                }
                sExtconInfos = list.toArray(new ExtconInfo[0]);
            }
                return list;
        }

        /**
         * Returns a new list of all external connections for the types given.
         */
        public static List<ExtconInfo> getExtconInfoForTypes(
                @ExtconDeviceType String[] extconTypes) {
            synchronized (sLock) {
                initExtconInfos();
            }

        private final String mName;
            List<ExtconInfo> extcons = new ArrayList<ExtconInfo>();
            for (ExtconInfo extcon : sExtconInfos) {
                for (String type : extconTypes) {
                    if (extcon.hasCableType(type)) {
                        extcons.add(extcon);
                        break;
                    }
                }
            }

        public ExtconInfo(String name) {
            mName = name;
            return extcons;
        }

        /** True if the given type is supported */
        public boolean hasCableType(@ExtconDeviceType String type) {
            return mDeviceTypes.contains(type);
        }

        private ExtconInfo(String extconName) {
            mName = extconName;

            // Retrieve device types from /sys/class/extcon/extcon[X]/cable.[Y]/name
            File[] cableDirs = FileUtils.listFilesOrEmpty(new File("/sys/class/extcon", mName),
                    (dir, cable) -> cable.startsWith("cable."));
            if (cableDirs.length == 0) {
                Slog.d(TAG,
                        "Unable to list cables in /sys/class/extcon/" + mName + ". "
                                + SELINUX_POLICIES_NEED_TO_BE_CHANGED);
            }

            for (File cableDir : cableDirs) {
                String cableCanonicalPath = null;
                try {
                    cableCanonicalPath = cableDir.getCanonicalPath();
                    String name = FileUtils.readTextFile(new File(cableDir, "name"), 0, null);
                    name = name.replace("\n", "").replace("\r", "");
                    if (LOG) {
                        Slog.v(TAG, "Add extcon cable " + cableCanonicalPath);
                    }
                    mDeviceTypes.add(name);
                } catch (IOException ex) {
                    Slog.w(TAG,
                            "Unable to read " + cableCanonicalPath + "/name. "
                                    + SELINUX_POLICIES_NEED_TO_BE_CHANGED,
                            ex);
                }
            }
        }

        /** The name of the external connection */
@@ -139,7 +249,7 @@ public abstract class ExtconUEventObserver extends UEventObserver {
        @Nullable
        public String getDevicePath() {
            try {
                String extconPath = String.format(Locale.US, "/sys/class/extcon/%s", mName);
                String extconPath = TextUtils.formatSimple("/sys/class/extcon/%s", mName);
                File devPath = new File(extconPath);
                if (devPath.exists()) {
                    String canonicalPath = devPath.getCanonicalPath();
@@ -155,14 +265,8 @@ public abstract class ExtconUEventObserver extends UEventObserver {

        /** The path to the state file */
        public String getStatePath() {
            return String.format(Locale.US, "/sys/class/extcon/%s/state", mName);
        }
            return TextUtils.formatSimple("/sys/class/extcon/%s/state", mName);
        }

    /** Does the {@code /sys/class/extcon/<name>} directory exist */
    public static boolean namedExtconDirExists(String name) {
        File extconDir = new File("/sys/class/extcon/" + name);
        return extconDir.exists() && extconDir.isDirectory();
    }

    /** Does the {@code /sys/class/extcon} directory exist */
+20 −8
Original line number Diff line number Diff line
@@ -490,8 +490,12 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
        private final List<ExtconInfo> mExtconInfos;

        WiredAccessoryExtconObserver() {
            mExtconInfos = ExtconInfo.getExtconInfos(".*audio.*");

            mExtconInfos = ExtconInfo.getExtconInfoForTypes(new String[] {
                    ExtconInfo.EXTCON_HEADPHONE,
                    ExtconInfo.EXTCON_MICROPHONE,
                    ExtconInfo.EXTCON_HDMI,
                    ExtconInfo.EXTCON_LINE_OUT,
            });
        }

        private void init() {
@@ -524,10 +528,18 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
            int[] maskAndState = {0, 0};
            // extcon event state changes from kernel4.9
            // new state will be like STATE=MICROPHONE=1\nHEADPHONE=0
            updateBit(maskAndState, BIT_HEADSET_NO_MIC, status, "HEADPHONE") ;
            updateBit(maskAndState, BIT_HEADSET, status,"MICROPHONE") ;
            updateBit(maskAndState, BIT_HDMI_AUDIO, status,"HDMI") ;
            updateBit(maskAndState, BIT_LINEOUT, status,"LINE-OUT") ;
            if (extconInfo.hasCableType(ExtconInfo.EXTCON_HEADPHONE)) {
                updateBit(maskAndState, BIT_HEADSET_NO_MIC, status, ExtconInfo.EXTCON_HEADPHONE);
            }
            if (extconInfo.hasCableType(ExtconInfo.EXTCON_MICROPHONE)) {
                updateBit(maskAndState, BIT_HEADSET, status, ExtconInfo.EXTCON_MICROPHONE);
            }
            if (extconInfo.hasCableType(ExtconInfo.EXTCON_HDMI)) {
                updateBit(maskAndState, BIT_HDMI_AUDIO, status, ExtconInfo.EXTCON_HDMI);
            }
            if (extconInfo.hasCableType(ExtconInfo.EXTCON_LINE_OUT)) {
                updateBit(maskAndState, BIT_LINEOUT, status, ExtconInfo.EXTCON_LINE_OUT);
            }
            if (LOG) Slog.v(TAG, "mask " + maskAndState[0] + " state " + maskAndState[1]);
            return Pair.create(maskAndState[0], maskAndState[1]);
        }
+22 −16
Original line number Diff line number Diff line
@@ -230,6 +230,7 @@ import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.List;

/**
 * WindowManagerPolicy implementation for the Android phone UI.  This
@@ -3385,14 +3386,19 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                    }
                }
            }
        } else if (ExtconUEventObserver.extconExists()
                && ExtconUEventObserver.namedExtconDirExists(HdmiVideoExtconUEventObserver.NAME)) {
        } else {
            final List<ExtconUEventObserver.ExtconInfo> extcons =
                    ExtconUEventObserver.ExtconInfo.getExtconInfoForTypes(
                            new String[] {ExtconUEventObserver.ExtconInfo.EXTCON_HDMI});
            if (!extcons.isEmpty()) {
                // TODO: handle more than one HDMI
                HdmiVideoExtconUEventObserver observer = new HdmiVideoExtconUEventObserver();
            plugged = observer.init();
                plugged = observer.init(extcons.get(0));
                mHDMIObserver = observer;
            } else if (localLOGV) {
                Slog.v(TAG, "Not observing HDMI plug state because HDMI was not found.");
            }
        }

        // This dance forces the code in setHdmiPlugged to run.
        // Always do this so the sticky intent is stuck (to false) if there is no hdmi.
@@ -5604,23 +5610,23 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    private class HdmiVideoExtconUEventObserver extends ExtconStateObserver<Boolean> {
        private static final String HDMI_EXIST = "HDMI=1";
        private static final String NAME = "hdmi";
        private final ExtconInfo mHdmi = new ExtconInfo(NAME);

        private boolean init() {
        private boolean init(ExtconInfo hdmi) {
            boolean plugged = false;
            try {
                plugged = parseStateFromFile(mHdmi);
                plugged = parseStateFromFile(hdmi);
            } catch (FileNotFoundException e) {
                Slog.w(TAG, mHdmi.getStatePath()
                        + " not found while attempting to determine initial state", e);
                Slog.w(TAG,
                        hdmi.getStatePath()
                                + " not found while attempting to determine initial state",
                        e);
            } catch (IOException e) {
                Slog.e(
                        TAG,
                        "Error reading " + mHdmi.getStatePath()
                Slog.e(TAG,
                        "Error reading " + hdmi.getStatePath()
                                + " while attempting to determine initial state",
                        e);
            }
            startObserving(mHdmi);
            startObserving(hdmi);
            return plugged;
        }