Home | History | Annotate | Download | only in os
      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 = peekThread();
    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