| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.os; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| |
| /** |
| * UEventObserver is an abstract class that receives UEvent's from the kernel.<p> |
| * |
| * Subclass UEventObserver, implementing onUEvent(UEvent event), then call |
| * startObserving() with a match string. The UEvent thread will then call your |
| * onUEvent() method when a UEvent occurs that contains your match string.<p> |
| * |
| * Call stopObserving() to stop receiving UEvent's.<p> |
| * |
| * There is only one UEvent thread per process, even if that process has |
| * multiple UEventObserver subclass instances. The UEvent thread starts when |
| * the startObserving() is called for the first time in that process. Once |
| * started the UEvent thread will not stop (although it can stop notifying |
| * UEventObserver's via stopObserving()).<p> |
| * |
| * @hide |
| */ |
| public abstract class UEventObserver { |
| private static final String TAG = UEventObserver.class.getSimpleName(); |
| |
| /** |
| * Representation of a UEvent. |
| */ |
| static public class UEvent { |
| // collection of key=value pairs parsed from the uevent message |
| public HashMap<String,String> mMap = new HashMap<String,String>(); |
| |
| public UEvent(String message) { |
| int offset = 0; |
| int length = message.length(); |
| |
| while (offset < length) { |
| int equals = message.indexOf('=', offset); |
| int at = message.indexOf(0, offset); |
| if (at < 0) break; |
| |
| if (equals > offset && equals < at) { |
| // key is before the equals sign, and value is after |
| mMap.put(message.substring(offset, equals), |
| message.substring(equals + 1, at)); |
| } |
| |
| offset = at + 1; |
| } |
| } |
| |
| public String get(String key) { |
| return mMap.get(key); |
| } |
| |
| public String get(String key, String defaultValue) { |
| String result = mMap.get(key); |
| return (result == null ? defaultValue : result); |
| } |
| |
| public String toString() { |
| return mMap.toString(); |
| } |
| } |
| |
| private static UEventThread sThread; |
| private static boolean sThreadStarted = false; |
| |
| private static class UEventThread extends Thread { |
| /** Many to many mapping of string match to observer. |
| * Multimap would be better, but not available in android, so use |
| * an ArrayList where even elements are the String match and odd |
| * elements the corresponding UEventObserver observer */ |
| private ArrayList<Object> mObservers = new ArrayList<Object>(); |
| |
| UEventThread() { |
| super("UEventObserver"); |
| } |
| |
| public void run() { |
| native_setup(); |
| |
| byte[] buffer = new byte[1024]; |
| int len; |
| while (true) { |
| len = next_event(buffer); |
| if (len > 0) { |
| String bufferStr = new String(buffer, 0, len); // easier to search a String |
| synchronized (mObservers) { |
| for (int i = 0; i < mObservers.size(); i += 2) { |
| if (bufferStr.indexOf((String)mObservers.get(i)) != -1) { |
| ((UEventObserver)mObservers.get(i+1)) |
| .onUEvent(new UEvent(bufferStr)); |
| } |
| } |
| } |
| } |
| } |
| } |
| public void addObserver(String match, UEventObserver observer) { |
| synchronized(mObservers) { |
| mObservers.add(match); |
| mObservers.add(observer); |
| } |
| } |
| /** Removes every key/value pair where value=observer from mObservers */ |
| public void removeObserver(UEventObserver observer) { |
| synchronized(mObservers) { |
| boolean found = true; |
| while (found) { |
| found = false; |
| for (int i = 0; i < mObservers.size(); i += 2) { |
| if (mObservers.get(i+1) == observer) { |
| mObservers.remove(i+1); |
| mObservers.remove(i); |
| found = true; |
| break; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| private static native void native_setup(); |
| private static native int next_event(byte[] buffer); |
| |
| private static final synchronized void ensureThreadStarted() { |
| if (sThreadStarted == false) { |
| sThread = new UEventThread(); |
| sThread.start(); |
| sThreadStarted = true; |
| } |
| } |
| |
| /** |
| * Begin observation of UEvent's.<p> |
| * This method will cause the UEvent thread to start if this is the first |
| * invocation of startObserving in this process.<p> |
| * Once called, the UEvent thread will call onUEvent() when an incoming |
| * UEvent matches the specified string.<p> |
| * This method can be called multiple times to register multiple matches. |
| * Only one call to stopObserving is required even with multiple registered |
| * matches. |
| * @param match A substring of the UEvent to match. Use "" to match all |
| * UEvent's |
| */ |
| public final synchronized void startObserving(String match) { |
| ensureThreadStarted(); |
| sThread.addObserver(match, this); |
| } |
| |
| /** |
| * End observation of UEvent's.<p> |
| * This process's UEvent thread will never call onUEvent() on this |
| * UEventObserver after this call. Repeated calls have no effect. |
| */ |
| public final synchronized void stopObserving() { |
| sThread.removeObserver(this); |
| } |
| |
| /** |
| * Subclasses of UEventObserver should override this method to handle |
| * UEvents. |
| */ |
| public abstract void onUEvent(UEvent event); |
| |
| protected void finalize() throws Throwable { |
| try { |
| stopObserving(); |
| } finally { |
| super.finalize(); |
| } |
| } |
| } |