blob: b3084f50071f8498a50f977e1a83a5a5fb1edeab [file] [log] [blame]
Nick Chalko0816eb32018-11-09 11:49:26 -08001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.server;
17
18import android.annotation.Nullable;
19import android.os.UEventObserver;
20import android.util.ArrayMap;
21import android.util.Slog;
22
23import java.io.File;
24import java.io.IOException;
25import java.util.Locale;
26import java.util.Map;
27
28/**
29 * A specialized UEventObserver that receives UEvents from the kernel for devices in the {@code
30 * /sys/class/extcon}. directory
31 *
32 * <p>Subclass ExtconUEventObserver, implementing {@link #onUEvent(ExtconInfo, UEvent)}, then call
33 * startObserving() with a ExtconInfo to observe. The UEvent thread will then call your onUEvent()
34 * method when a UEvent occurs that matches the path of your ExtconInfos.
35 *
36 * <p>Call stopObserving() to stop receiving UEvents.
37 *
38 * <p>There is only one UEvent thread per process, even if that process has multiple UEventObserver
39 * subclass instances. The UEvent thread starts when the startObserving() is called for the first
40 * time in that process. Once started the UEvent thread will not stop (although it can stop
41 * notifying UEventObserver's via stopObserving()).
42 *
43 * <p>
44 *
45 * @hide
46 */
47public abstract class ExtconUEventObserver extends UEventObserver {
48 private static final String TAG = "ExtconUEventObserver";
49 private static final boolean LOG = false;
50 private final Map<String, ExtconInfo> mExtconInfos = new ArrayMap<>();
51
52 @Override
53 public final void onUEvent(UEvent event) {
54 String devPath = event.get("DEVPATH");
55 ExtconInfo info = mExtconInfos.get(devPath);
56 if (info != null) {
57 onUEvent(info, event);
58 } else {
59 Slog.w(TAG, "No match found for DEVPATH of " + event + " in " + mExtconInfos);
60 }
61 }
62
63 /**
64 * Subclasses of ExtconUEventObserver should override this method to handle UEvents.
65 *
66 * @param extconInfo that matches the {@code DEVPATH} of {@code event}
67 * @param event the event
68 */
69 protected abstract void onUEvent(ExtconInfo extconInfo, UEvent event);
70
71 /** Starts observing {@link ExtconInfo#getDevicePath()}. */
72 public void startObserving(ExtconInfo extconInfo) {
73 mExtconInfos.put(extconInfo.getDevicePath(), extconInfo);
74 if (LOG) Slog.v(TAG, "Observing " + extconInfo.getDevicePath());
75 startObserving("DEVPATH=" + extconInfo.getDevicePath());
76 }
77
78 /** An External Connection to watch. */
79 public static final class ExtconInfo {
80 private static final String TAG = "ExtconInfo";
81
82 private final String mName;
83
84 public ExtconInfo(String name) {
85 mName = name;
86 }
87
88 /** The name of the external connection */
89 public String getName() {
90 return mName;
91 }
92
93 /**
94 * The path to the device for this external connection.
95 *
96 * <p><b>NOTE</b> getting this path involves resolving a symlink.
97 *
98 * @return the device path, or null if it not found.
99 */
100 @Nullable
101 public String getDevicePath() {
102 try {
103 String extconPath = String.format(Locale.US, "/sys/class/extcon/%s", mName);
104 File devPath = new File(extconPath);
105 if (devPath.exists()) {
106 String canonicalPath = devPath.getCanonicalPath();
107 int start = canonicalPath.indexOf("/devices");
108 return canonicalPath.substring(start);
109 }
110 return null;
111 } catch (IOException e) {
112 Slog.e(TAG, "Could not get the extcon device path for " + mName, e);
113 return null;
114 }
115 }
116
117 /** The path to the state file */
118 public String getStatePath() {
119 return String.format(Locale.US, "/sys/class/extcon/%s/state", mName);
120 }
121 }
122
123 /** Does the {@link /sys/class/extcon} directory exist */
124 public static boolean extconExists() {
125 File extconDir = new File("/sys/class/extcon");
126 return extconDir.exists() && extconDir.isDirectory();
127 }
128}