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 java.util.ArrayList; 20 import java.util.HashMap; 21 22 /** 23 * UEventObserver is an abstract class that receives UEvent's from the kernel.<p> 24 * 25 * Subclass UEventObserver, implementing onUEvent(UEvent event), then call 26 * startObserving() with a match string. The UEvent thread will then call your 27 * onUEvent() method when a UEvent occurs that contains your match string.<p> 28 * 29 * Call stopObserving() to stop receiving UEvent's.<p> 30 * 31 * There is only one UEvent thread per process, even if that process has 32 * multiple UEventObserver subclass instances. The UEvent thread starts when 33 * the startObserving() is called for the first time in that process. Once 34 * started the UEvent thread will not stop (although it can stop notifying 35 * UEventObserver's via stopObserving()).<p> 36 * 37 * @hide 38 */ 39 public abstract class UEventObserver { 40 private static final String TAG = UEventObserver.class.getSimpleName(); 41 42 /** 43 * Representation of a UEvent. 44 */ 45 static public class UEvent { 46 // collection of key=value pairs parsed from the uevent message 47 public HashMap<String,String> mMap = new HashMap<String,String>(); 48 49 public UEvent(String message) { 50 int offset = 0; 51 int length = message.length(); 52 53 while (offset < length) { 54 int equals = message.indexOf('=', offset); 55 int at = message.indexOf(0, offset); 56 if (at < 0) break; 57 58 if (equals > offset && equals < at) { 59 // key is before the equals sign, and value is after 60 mMap.put(message.substring(offset, equals), 61 message.substring(equals + 1, at)); 62 } 63 64 offset = at + 1; 65 } 66 } 67 68 public String get(String key) { 69 return mMap.get(key); 70 } 71 72 public String get(String key, String defaultValue) { 73 String result = mMap.get(key); 74 return (result == null ? defaultValue : result); 75 } 76 77 public String toString() { 78 return mMap.toString(); 79 } 80 } 81 82 private static UEventThread sThread; 83 private static boolean sThreadStarted = false; 84 85 private static class UEventThread extends Thread { 86 /** Many to many mapping of string match to observer. 87 * Multimap would be better, but not available in android, so use 88 * an ArrayList where even elements are the String match and odd 89 * elements the corresponding UEventObserver observer */ 90 private ArrayList<Object> mObservers = new ArrayList<Object>(); 91 92 UEventThread() { 93 super("UEventObserver"); 94 } 95 96 public void run() { 97 native_setup(); 98 99 byte[] buffer = new byte[1024]; 100 int len; 101 while (true) { 102 len = next_event(buffer); 103 if (len > 0) { 104 String bufferStr = new String(buffer, 0, len); // easier to search a String 105 synchronized (mObservers) { 106 for (int i = 0; i < mObservers.size(); i += 2) { 107 if (bufferStr.indexOf((String)mObservers.get(i)) != -1) { 108 ((UEventObserver)mObservers.get(i+1)) 109 .onUEvent(new UEvent(bufferStr)); 110 } 111 } 112 } 113 } 114 } 115 } 116 public void addObserver(String match, UEventObserver observer) { 117 synchronized(mObservers) { 118 mObservers.add(match); 119 mObservers.add(observer); 120 } 121 } 122 /** Removes every key/value pair where value=observer from mObservers */ 123 public void removeObserver(UEventObserver observer) { 124 synchronized(mObservers) { 125 boolean found = true; 126 while (found) { 127 found = false; 128 for (int i = 0; i < mObservers.size(); i += 2) { 129 if (mObservers.get(i+1) == observer) { 130 mObservers.remove(i+1); 131 mObservers.remove(i); 132 found = true; 133 break; 134 } 135 } 136 } 137 } 138 } 139 } 140 141 private static native void native_setup(); 142 private static native int next_event(byte[] buffer); 143 144 private static final synchronized void ensureThreadStarted() { 145 if (sThreadStarted == false) { 146 sThread = new UEventThread(); 147 sThread.start(); 148 sThreadStarted = true; 149 } 150 } 151 152 /** 153 * Begin observation of UEvent's.<p> 154 * This method will cause the UEvent thread to start if this is the first 155 * invocation of startObserving in this process.<p> 156 * Once called, the UEvent thread will call onUEvent() when an incoming 157 * UEvent matches the specified string.<p> 158 * This method can be called multiple times to register multiple matches. 159 * Only one call to stopObserving is required even with multiple registered 160 * matches. 161 * @param match A substring of the UEvent to match. Use "" to match all 162 * UEvent's 163 */ 164 public final synchronized void startObserving(String match) { 165 ensureThreadStarted(); 166 sThread.addObserver(match, this); 167 } 168 169 /** 170 * End observation of UEvent's.<p> 171 * This process's UEvent thread will never call onUEvent() on this 172 * UEventObserver after this call. Repeated calls have no effect. 173 */ 174 public final synchronized void stopObserving() { 175 sThread.removeObserver(this); 176 } 177 178 /** 179 * Subclasses of UEventObserver should override this method to handle 180 * UEvents. 181 */ 182 public abstract void onUEvent(UEvent event); 183 184 protected void finalize() throws Throwable { 185 try { 186 stopObserving(); 187 } finally { 188 super.finalize(); 189 } 190 } 191 } 192