blob: 5c80ca6559aeed5eeb78dacf7eaec1bff7c5e3ce [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 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 */
16
17package android.os;
18
Jeff Brown9cf36b72012-10-10 17:17:58 -070019import android.util.Log;
20
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import java.util.ArrayList;
22import java.util.HashMap;
23
24/**
Valter Strods7014b262014-08-19 23:42:18 +030025 * UEventObserver is an abstract class that receives UEvents from the kernel.<p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026 *
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>
30 *
Valter Strods7014b262014-08-19 23:42:18 +030031 * Call stopObserving() to stop receiving UEvents.<p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032 *
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>
38 *
39 * @hide
40*/
41public abstract class UEventObserver {
Jeff Brown9cf36b72012-10-10 17:17:58 -070042 private static final String TAG = "UEventObserver";
43 private static final boolean DEBUG = false;
44
Jeff Brown008b1762012-08-20 20:15:34 -070045 private static UEventThread sThread;
46
Jeff Brown9cf36b72012-10-10 17:17:58 -070047 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);
Jeff Brown008b1762012-08-20 20:15:34 -070051
52 public UEventObserver() {
53 }
54
Jeff Brown9cf36b72012-10-10 17:17:58 -070055 @Override
Jeff Brown008b1762012-08-20 20:15:34 -070056 protected void finalize() throws Throwable {
57 try {
58 stopObserving();
59 } finally {
60 super.finalize();
61 }
62 }
63
64 private static UEventThread getThread() {
65 synchronized (UEventObserver.class) {
66 if (sThread == null) {
67 sThread = new UEventThread();
68 sThread.start();
69 }
70 return sThread;
71 }
72 }
73
74 private static UEventThread peekThread() {
75 synchronized (UEventObserver.class) {
76 return sThread;
77 }
78 }
79
80 /**
Valter Strods7014b262014-08-19 23:42:18 +030081 * Begin observation of UEvents.<p>
Jeff Brown008b1762012-08-20 20:15:34 -070082 * 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
88 * matches.
Jeff Brown9cf36b72012-10-10 17:17:58 -070089 *
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.
Jeff Brown008b1762012-08-20 20:15:34 -070095 */
96 public final void startObserving(String match) {
Jeff Brown9cf36b72012-10-10 17:17:58 -070097 if (match == null || match.isEmpty()) {
98 throw new IllegalArgumentException("match substring must be non-empty");
99 }
100
Jeff Brown008b1762012-08-20 20:15:34 -0700101 final UEventThread t = getThread();
102 t.addObserver(match, this);
103 }
104
105 /**
Valter Strods7014b262014-08-19 23:42:18 +0300106 * End observation of UEvents.<p>
Jeff Brown008b1762012-08-20 20:15:34 -0700107 * This process's UEvent thread will never call onUEvent() on this
108 * UEventObserver after this call. Repeated calls have no effect.
109 */
110 public final void stopObserving() {
111 final UEventThread t = getThread();
112 if (t != null) {
113 t.removeObserver(this);
114 }
115 }
116
117 /**
118 * Subclasses of UEventObserver should override this method to handle
119 * UEvents.
120 */
121 public abstract void onUEvent(UEvent event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122
123 /**
124 * Representation of a UEvent.
125 */
Jeff Brown008b1762012-08-20 20:15:34 -0700126 public static final class UEvent {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 // collection of key=value pairs parsed from the uevent message
Jeff Brown008b1762012-08-20 20:15:34 -0700128 private final HashMap<String,String> mMap = new HashMap<String,String>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129
130 public UEvent(String message) {
131 int offset = 0;
132 int length = message.length();
133
134 while (offset < length) {
135 int equals = message.indexOf('=', offset);
Jeff Brown9cf36b72012-10-10 17:17:58 -0700136 int at = message.indexOf('\0', offset);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 if (at < 0) break;
138
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));
143 }
144
145 offset = at + 1;
146 }
147 }
148
149 public String get(String key) {
150 return mMap.get(key);
151 }
152
153 public String get(String key, String defaultValue) {
154 String result = mMap.get(key);
155 return (result == null ? defaultValue : result);
156 }
157
158 public String toString() {
159 return mMap.toString();
160 }
161 }
162
Jeff Brown008b1762012-08-20 20:15:34 -0700163 private static final class UEventThread extends Thread {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 /** 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 */
Jeff Brown008b1762012-08-20 20:15:34 -0700168 private final ArrayList<Object> mKeysAndObservers = new ArrayList<Object>();
169
170 private final ArrayList<UEventObserver> mTempObserversToSignal =
171 new ArrayList<UEventObserver>();
172
173 public UEventThread() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 super("UEventObserver");
175 }
Jeff Brown008b1762012-08-20 20:15:34 -0700176
Jeff Brown9cf36b72012-10-10 17:17:58 -0700177 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 public void run() {
Jeff Brown9cf36b72012-10-10 17:17:58 -0700179 nativeSetup();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 while (true) {
Jeff Brown9cf36b72012-10-10 17:17:58 -0700182 String message = nativeWaitForNextEvent();
183 if (message != null) {
184 if (DEBUG) {
185 Log.d(TAG, message);
186 }
187 sendEvent(message);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 }
189 }
190 }
Jeff Brown008b1762012-08-20 20:15:34 -0700191
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);
Jeff Brown9cf36b72012-10-10 17:17:58 -0700197 if (message.contains(key)) {
Jeff Brown008b1762012-08-20 20:15:34 -0700198 final UEventObserver observer =
199 (UEventObserver)mKeysAndObservers.get(i + 1);
200 mTempObserversToSignal.add(observer);
201 }
202 }
203 }
204
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);
211 }
212 mTempObserversToSignal.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213 }
214 }
Jeff Brown008b1762012-08-20 20:15:34 -0700215
216 public void addObserver(String match, UEventObserver observer) {
217 synchronized (mKeysAndObservers) {
218 mKeysAndObservers.add(match);
219 mKeysAndObservers.add(observer);
Jeff Brown9cf36b72012-10-10 17:17:58 -0700220 nativeAddMatch(match);
Jeff Brown008b1762012-08-20 20:15:34 -0700221 }
222 }
223
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 /** Removes every key/value pair where value=observer from mObservers */
225 public void removeObserver(UEventObserver observer) {
Jeff Brown008b1762012-08-20 20:15:34 -0700226 synchronized (mKeysAndObservers) {
227 for (int i = 0; i < mKeysAndObservers.size(); ) {
228 if (mKeysAndObservers.get(i + 1) == observer) {
229 mKeysAndObservers.remove(i + 1);
Jeff Brown9cf36b72012-10-10 17:17:58 -0700230 final String match = (String)mKeysAndObservers.remove(i);
231 nativeRemoveMatch(match);
Jeff Brown008b1762012-08-20 20:15:34 -0700232 } else {
233 i += 2;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234 }
235 }
236 }
237 }
238 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800239}