Loading services/core/java/com/android/server/ExtconUEventObserver.java +137 −33 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 */ Loading @@ -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(); Loading @@ -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 */ Loading services/core/java/com/android/server/WiredAccessoryManager.java +20 −8 Original line number Diff line number Diff line Loading @@ -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() { Loading Loading @@ -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]); } Loading services/core/java/com/android/server/policy/PhoneWindowManager.java +22 −16 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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. Loading Loading @@ -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; } Loading Loading
services/core/java/com/android/server/ExtconUEventObserver.java +137 −33 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 */ Loading @@ -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(); Loading @@ -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 */ Loading
services/core/java/com/android/server/WiredAccessoryManager.java +20 −8 Original line number Diff line number Diff line Loading @@ -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() { Loading Loading @@ -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]); } Loading
services/core/java/com/android/server/policy/PhoneWindowManager.java +22 −16 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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. Loading Loading @@ -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; } Loading