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.compat.annotation.UnsupportedAppUsage; 20 import android.util.Log; 21 22 import java.util.ArrayList; 23 import java.util.HashMap; 24 25 /** 26 * UEventObserver is an abstract class that receives UEvents from the kernel.<p> 27 * 28 * Subclass UEventObserver, implementing onUEvent(UEvent event), then call 29 * startObserving() with a match string. The UEvent thread will then call your 30 * onUEvent() method when a UEvent occurs that contains your match string.<p> 31 * 32 * Call stopObserving() to stop receiving UEvents.<p> 33 * 34 * There is only one UEvent thread per process, even if that process has 35 * multiple UEventObserver subclass instances. The UEvent thread starts when 36 * the startObserving() is called for the first time in that process. Once 37 * started the UEvent thread will not stop (although it can stop notifying 38 * UEventObserver's via stopObserving()).<p> 39 * 40 * @hide 41 */ 42 public abstract class UEventObserver { 43 private static final String TAG = "UEventObserver"; 44 private static final boolean DEBUG = false; 45 46 private static UEventThread sThread; 47 nativeSetup()48 private static native void nativeSetup(); nativeWaitForNextEvent()49 private static native String nativeWaitForNextEvent(); nativeAddMatch(String match)50 private static native void nativeAddMatch(String match); nativeRemoveMatch(String match)51 private static native void nativeRemoveMatch(String match); 52 53 @UnsupportedAppUsage UEventObserver()54 public UEventObserver() { 55 } 56 57 @Override finalize()58 protected void finalize() throws Throwable { 59 try { 60 stopObserving(); 61 } finally { 62 super.finalize(); 63 } 64 } 65 getThread()66 private static UEventThread getThread() { 67 synchronized (UEventObserver.class) { 68 if (sThread == null) { 69 sThread = new UEventThread(); 70 sThread.start(); 71 } 72 return sThread; 73 } 74 } 75 peekThread()76 private static UEventThread peekThread() { 77 synchronized (UEventObserver.class) { 78 return sThread; 79 } 80 } 81 82 /** 83 * Begin observation of UEvents.<p> 84 * This method will cause the UEvent thread to start if this is the first 85 * invocation of startObserving in this process.<p> 86 * Once called, the UEvent thread will call onUEvent() when an incoming 87 * UEvent matches the specified string.<p> 88 * This method can be called multiple times to register multiple matches. 89 * Only one call to stopObserving is required even with multiple registered 90 * matches. 91 * 92 * @param match A substring of the UEvent to match. Try to be as specific 93 * as possible to avoid incurring unintended additional cost from processing 94 * irrelevant messages. Netlink messages can be moderately high bandwidth and 95 * are expensive to parse. For example, some devices may send one netlink message 96 * for each vsync period. 97 */ 98 @UnsupportedAppUsage startObserving(String match)99 public final void startObserving(String match) { 100 if (match == null || match.isEmpty()) { 101 throw new IllegalArgumentException("match substring must be non-empty"); 102 } 103 104 final UEventThread t = getThread(); 105 t.addObserver(match, this); 106 } 107 108 /** 109 * End observation of UEvents.<p> 110 * This process's UEvent thread will never call onUEvent() on this 111 * UEventObserver after this call. Repeated calls have no effect. 112 */ 113 @UnsupportedAppUsage stopObserving()114 public final void stopObserving() { 115 final UEventThread t = peekThread(); 116 if (t != null) { 117 t.removeObserver(this); 118 } 119 } 120 121 /** 122 * Subclasses of UEventObserver should override this method to handle 123 * UEvents. 124 */ 125 @UnsupportedAppUsage onUEvent(UEvent event)126 public abstract void onUEvent(UEvent event); 127 128 /** 129 * Representation of a UEvent. 130 */ 131 public static final class UEvent { 132 // collection of key=value pairs parsed from the uevent message 133 private final HashMap<String,String> mMap = new HashMap<String,String>(); 134 UEvent(String message)135 public UEvent(String message) { 136 int offset = 0; 137 int length = message.length(); 138 139 while (offset < length) { 140 int equals = message.indexOf('=', offset); 141 int at = message.indexOf('\0', offset); 142 if (at < 0) break; 143 144 if (equals > offset && equals < at) { 145 // key is before the equals sign, and value is after 146 mMap.put(message.substring(offset, equals), 147 message.substring(equals + 1, at)); 148 } 149 150 offset = at + 1; 151 } 152 } 153 154 @UnsupportedAppUsage get(String key)155 public String get(String key) { 156 return mMap.get(key); 157 } 158 159 @UnsupportedAppUsage get(String key, String defaultValue)160 public String get(String key, String defaultValue) { 161 String result = mMap.get(key); 162 return (result == null ? defaultValue : result); 163 } 164 toString()165 public String toString() { 166 return mMap.toString(); 167 } 168 } 169 170 private static final class UEventThread extends Thread { 171 /** Many to many mapping of string match to observer. 172 * Multimap would be better, but not available in android, so use 173 * an ArrayList where even elements are the String match and odd 174 * elements the corresponding UEventObserver observer */ 175 private final ArrayList<Object> mKeysAndObservers = new ArrayList<Object>(); 176 177 private final ArrayList<UEventObserver> mTempObserversToSignal = 178 new ArrayList<UEventObserver>(); 179 UEventThread()180 public UEventThread() { 181 super("UEventObserver"); 182 } 183 184 @Override run()185 public void run() { 186 nativeSetup(); 187 188 while (true) { 189 String message = nativeWaitForNextEvent(); 190 if (message != null) { 191 if (DEBUG) { 192 Log.d(TAG, message); 193 } 194 sendEvent(message); 195 } 196 } 197 } 198 sendEvent(String message)199 private void sendEvent(String message) { 200 synchronized (mKeysAndObservers) { 201 final int N = mKeysAndObservers.size(); 202 for (int i = 0; i < N; i += 2) { 203 final String key = (String)mKeysAndObservers.get(i); 204 if (message.contains(key)) { 205 final UEventObserver observer = 206 (UEventObserver)mKeysAndObservers.get(i + 1); 207 mTempObserversToSignal.add(observer); 208 } 209 } 210 } 211 212 if (!mTempObserversToSignal.isEmpty()) { 213 final UEvent event = new UEvent(message); 214 final int N = mTempObserversToSignal.size(); 215 for (int i = 0; i < N; i++) { 216 final UEventObserver observer = mTempObserversToSignal.get(i); 217 observer.onUEvent(event); 218 } 219 mTempObserversToSignal.clear(); 220 } 221 } 222 addObserver(String match, UEventObserver observer)223 public void addObserver(String match, UEventObserver observer) { 224 synchronized (mKeysAndObservers) { 225 mKeysAndObservers.add(match); 226 mKeysAndObservers.add(observer); 227 nativeAddMatch(match); 228 } 229 } 230 231 /** Removes every key/value pair where value=observer from mObservers */ removeObserver(UEventObserver observer)232 public void removeObserver(UEventObserver observer) { 233 synchronized (mKeysAndObservers) { 234 for (int i = 0; i < mKeysAndObservers.size(); ) { 235 if (mKeysAndObservers.get(i + 1) == observer) { 236 mKeysAndObservers.remove(i + 1); 237 final String match = (String)mKeysAndObservers.remove(i); 238 nativeRemoveMatch(match); 239 } else { 240 i += 2; 241 } 242 } 243 } 244 } 245 } 246 } 247