UEventObserver.java revision 7014b26c3e1429a6b841696ac7d84589158b0aaf
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
17package android.os;
18
19import android.util.Log;
20
21import java.util.ArrayList;
22import 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*/
41public 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 = getThread();
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