Merge "Detect extcon device type from cable name."
This commit is contained in:
committed by
Android (Google) Code Review
commit
ac334149cd
@@ -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 */
|
||||
|
||||
/** 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.
|
||||
/* 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;
|
||||
|
||||
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()
|
||||
+ " but listFiles returns null. "
|
||||
+ SELINUX_POLICIES_NEED_TO_BE_CHANGED);
|
||||
return new ArrayList<>(0); // Always return a new list.
|
||||
Slog.w(TAG,
|
||||
file + " exists " + file.exists() + " isDir " + file.isDirectory()
|
||||
+ " but listFiles returns null."
|
||||
+ SELINUX_POLICIES_NEED_TO_BE_CHANGED);
|
||||
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()));
|
||||
}
|
||||
return list;
|
||||
sExtconInfos = list.toArray(new ExtconInfo[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private final String mName;
|
||||
/**
|
||||
* Returns a new list of all external connections for the types given.
|
||||
*/
|
||||
public static List<ExtconInfo> getExtconInfoForTypes(
|
||||
@ExtconDeviceType String[] extconTypes) {
|
||||
synchronized (sLock) {
|
||||
initExtconInfos();
|
||||
}
|
||||
|
||||
public ExtconInfo(String name) {
|
||||
mName = name;
|
||||
List<ExtconInfo> extcons = new ArrayList<ExtconInfo>();
|
||||
for (ExtconInfo extcon : sExtconInfos) {
|
||||
for (String type : extconTypes) {
|
||||
if (extcon.hasCableType(type)) {
|
||||
extcons.add(extcon);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,16 +265,10 @@ 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 */
|
||||
public static boolean extconExists() {
|
||||
File extconDir = new File("/sys/class/extcon");
|
||||
|
||||
@@ -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() {
|
||||
@@ -521,15 +525,23 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
|
||||
@Override
|
||||
public Pair<Integer, Integer> parseState(ExtconInfo extconInfo, String status) {
|
||||
if (LOG) Slog.v(TAG, "status " + status);
|
||||
int []maskAndState = {0,0};
|
||||
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]);
|
||||
return Pair.create(maskAndState[0], maskAndState[1]);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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,13 +3386,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (ExtconUEventObserver.extconExists()
|
||||
&& ExtconUEventObserver.namedExtconDirExists(HdmiVideoExtconUEventObserver.NAME)) {
|
||||
HdmiVideoExtconUEventObserver observer = new HdmiVideoExtconUEventObserver();
|
||||
plugged = observer.init();
|
||||
mHDMIObserver = observer;
|
||||
} else if (localLOGV) {
|
||||
Slog.v(TAG, "Not observing HDMI plug state because HDMI was not found.");
|
||||
} 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(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.
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user