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.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 
     48     private static native void nativeSetup();
     49     private static native String nativeWaitForNextEvent();
     50     private static native void nativeAddMatch(String match);
     51     private static native void nativeRemoveMatch(String match);
     52 
     53     @UnsupportedAppUsage
     54     public UEventObserver() {
     55     }
     56 
     57     @Override
     58     protected void finalize() throws Throwable {
     59         try {
     60             stopObserving();
     61         } finally {
     62             super.finalize();
     63         }
     64     }
     65 
     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 
     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
     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
    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
    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 
    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
    155         public String get(String key) {
    156             return mMap.get(key);
    157         }
    158 
    159         @UnsupportedAppUsage
    160         public String get(String key, String defaultValue) {
    161             String result = mMap.get(key);
    162             return (result == null ? defaultValue : result);
    163         }
    164 
    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 
    180         public UEventThread() {
    181             super("UEventObserver");
    182         }
    183 
    184         @Override
    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 
    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 
    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 */
    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