1 /* 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 17 package android.os; 18 19 import android.util.Log; 20 21 import java.util.ArrayList; 22 import java.util.HashMap; 23 24 /** 25 * UEventObserver is an abstract class that receives UEvents from the kernel.<p> 26 * 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 * 31 * Call stopObserving() to stop receiving UEvents.<p> 32 * 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 */ 41 public abstract class UEventObserver { 42 private static final String TAG = "UEventObserver"; 43 private static final boolean DEBUG = false; 44 45 private static UEventThread sThread; 46 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); 51 52 public UEventObserver() { 53 } 54 55 @Override 56 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 /** 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 88 * matches. 89 * 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. 95 */ 96 public final void startObserving(String match) { 97 if (match == null || match.isEmpty()) { 98 throw new IllegalArgumentException("match substring must be non-empty"); 99 } 100 101 final UEventThread t = getThread(); 102 t.addObserver(match, this); 103 } 104 105 /** 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. 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); 122 123 /** 124 * Representation of a UEvent. 125 */ 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>(); 129 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); 136 int at = message.indexOf('\0', offset); 137 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 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>(); 169 170 private final ArrayList<UEventObserver> mTempObserversToSignal = 171 new ArrayList<UEventObserver>(); 172 173 public UEventThread() { 174 super("UEventObserver"); 175 } 176 177 @Override 178 public void run() { 179 nativeSetup(); 180 181 while (true) { 182 String message = nativeWaitForNextEvent(); 183 if (message != null) { 184 if (DEBUG) { 185 Log.d(TAG, message); 186 } 187 sendEvent(message); 188 } 189 } 190 } 191 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); 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(); 213 } 214 } 215 216 public void addObserver(String match, UEventObserver observer) { 217 synchronized (mKeysAndObservers) { 218 mKeysAndObservers.add(match); 219 mKeysAndObservers.add(observer); 220 nativeAddMatch(match); 221 } 222 } 223 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); 232 } else { 233 i += 2; 234 } 235 } 236 } 237 } 238 } 239 } 240