2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 import android.util.Log;
21 import java.util.ArrayList;
22 import java.util.HashMap;
25 * UEventObserver is an abstract class that receives UEvents from the kernel.<p>
27 * Subclass UEventObserver, implementing onUEvent(UEvent event), then call
28 * startObserving() with a match string. The UEvent thread will then call your
29 * onUEvent() method when a UEvent occurs that contains your match string.<p>
31 * Call stopObserving() to stop receiving UEvents.<p>
33 * There is only one UEvent thread per process, even if that process has
34 * multiple UEventObserver subclass instances. The UEvent thread starts when
35 * the startObserving() is called for the first time in that process. Once
36 * started the UEvent thread will not stop (although it can stop notifying
37 * UEventObserver's via stopObserving()).<p>
41 public abstract class UEventObserver {
42 private static final String TAG = "UEventObserver";
43 private static final boolean DEBUG = false;
45 private static UEventThread sThread;
47 private static native void nativeSetup();
48 private static native String nativeWaitForNextEvent();
49 private static native void nativeAddMatch(String match);
50 private static native void nativeRemoveMatch(String match);
52 public UEventObserver() {
56 protected void finalize() throws Throwable {
64 private static UEventThread getThread() {
65 synchronized (UEventObserver.class) {
66 if (sThread == null) {
67 sThread = new UEventThread();
74 private static UEventThread peekThread() {
75 synchronized (UEventObserver.class) {
81 * Begin observation of UEvents.<p>
82 * This method will cause the UEvent thread to start if this is the first
83 * invocation of startObserving in this process.<p>
84 * Once called, the UEvent thread will call onUEvent() when an incoming
85 * UEvent matches the specified string.<p>
86 * This method can be called multiple times to register multiple matches.
87 * Only one call to stopObserving is required even with multiple registered
90 * @param match A substring of the UEvent to match. Try to be as specific
91 * as possible to avoid incurring unintended additional cost from processing
92 * irrelevant messages. Netlink messages can be moderately high bandwidth and
93 * are expensive to parse. For example, some devices may send one netlink message
94 * for each vsync period.
96 public final void startObserving(String match) {
97 if (match == null || match.isEmpty()) {
98 throw new IllegalArgumentException("match substring must be non-empty");
101 final UEventThread t = getThread();
102 t.addObserver(match, this);
106 * End observation of UEvents.<p>
107 * This process's UEvent thread will never call onUEvent() on this
108 * UEventObserver after this call. Repeated calls have no effect.
110 public final void stopObserving() {
111 final UEventThread t = getThread();
113 t.removeObserver(this);
118 * Subclasses of UEventObserver should override this method to handle
121 public abstract void onUEvent(UEvent event);
124 * Representation of a UEvent.
126 public static final class UEvent {
127 // collection of key=value pairs parsed from the uevent message
128 private final HashMap<String,String> mMap = new HashMap<String,String>();
130 public UEvent(String message) {
132 int length = message.length();
134 while (offset < length) {
135 int equals = message.indexOf('=', offset);
136 int at = message.indexOf('\0', offset);
139 if (equals > offset && equals < at) {
140 // key is before the equals sign, and value is after
141 mMap.put(message.substring(offset, equals),
142 message.substring(equals + 1, at));
149 public String get(String key) {
150 return mMap.get(key);
153 public String get(String key, String defaultValue) {
154 String result = mMap.get(key);
155 return (result == null ? defaultValue : result);
158 public String toString() {
159 return mMap.toString();
163 private static final class UEventThread extends Thread {
164 /** Many to many mapping of string match to observer.
165 * Multimap would be better, but not available in android, so use
166 * an ArrayList where even elements are the String match and odd
167 * elements the corresponding UEventObserver observer */
168 private final ArrayList<Object> mKeysAndObservers = new ArrayList<Object>();
170 private final ArrayList<UEventObserver> mTempObserversToSignal =
171 new ArrayList<UEventObserver>();
173 public UEventThread() {
174 super("UEventObserver");
182 String message = nativeWaitForNextEvent();
183 if (message != null) {
192 private void sendEvent(String message) {
193 synchronized (mKeysAndObservers) {
194 final int N = mKeysAndObservers.size();
195 for (int i = 0; i < N; i += 2) {
196 final String key = (String)mKeysAndObservers.get(i);
197 if (message.contains(key)) {
198 final UEventObserver observer =
199 (UEventObserver)mKeysAndObservers.get(i + 1);
200 mTempObserversToSignal.add(observer);
205 if (!mTempObserversToSignal.isEmpty()) {
206 final UEvent event = new UEvent(message);
207 final int N = mTempObserversToSignal.size();
208 for (int i = 0; i < N; i++) {
209 final UEventObserver observer = mTempObserversToSignal.get(i);
210 observer.onUEvent(event);
212 mTempObserversToSignal.clear();
216 public void addObserver(String match, UEventObserver observer) {
217 synchronized (mKeysAndObservers) {
218 mKeysAndObservers.add(match);
219 mKeysAndObservers.add(observer);
220 nativeAddMatch(match);
224 /** Removes every key/value pair where value=observer from mObservers */
225 public void removeObserver(UEventObserver observer) {
226 synchronized (mKeysAndObservers) {
227 for (int i = 0; i < mKeysAndObservers.size(); ) {
228 if (mKeysAndObservers.get(i + 1) == observer) {
229 mKeysAndObservers.remove(i + 1);
230 final String match = (String)mKeysAndObservers.remove(i);
231 nativeRemoveMatch(match);