UEventObserver.java revision 008b1762a8d5c908281a832ff90817ade6c7f9f6
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 java.util.ArrayList;
20import 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*/
39public abstract class UEventObserver {
40    private static UEventThread sThread;
41
42    private static native void native_setup();
43    private static native int next_event(byte[] buffer);
44
45    public UEventObserver() {
46    }
47
48    protected void finalize() throws Throwable {
49        try {
50            stopObserving();
51        } finally {
52            super.finalize();
53        }
54    }
55
56    private static UEventThread getThread() {
57        synchronized (UEventObserver.class) {
58            if (sThread == null) {
59                sThread = new UEventThread();
60                sThread.start();
61            }
62            return sThread;
63        }
64    }
65
66    private static UEventThread peekThread() {
67        synchronized (UEventObserver.class) {
68            return sThread;
69        }
70    }
71
72    /**
73     * Begin observation of UEvent's.<p>
74     * This method will cause the UEvent thread to start if this is the first
75     * invocation of startObserving in this process.<p>
76     * Once called, the UEvent thread will call onUEvent() when an incoming
77     * UEvent matches the specified string.<p>
78     * This method can be called multiple times to register multiple matches.
79     * Only one call to stopObserving is required even with multiple registered
80     * matches.
81     * @param match A substring of the UEvent to match. Use "" to match all
82     *              UEvent's
83     */
84    public final void startObserving(String match) {
85        final UEventThread t = getThread();
86        t.addObserver(match, this);
87    }
88
89    /**
90     * End observation of UEvent's.<p>
91     * This process's UEvent thread will never call onUEvent() on this
92     * UEventObserver after this call. Repeated calls have no effect.
93     */
94    public final void stopObserving() {
95        final UEventThread t = getThread();
96        if (t != null) {
97            t.removeObserver(this);
98        }
99    }
100
101    /**
102     * Subclasses of UEventObserver should override this method to handle
103     * UEvents.
104     */
105    public abstract void onUEvent(UEvent event);
106
107    /**
108     * Representation of a UEvent.
109     */
110    public static final class UEvent {
111        // collection of key=value pairs parsed from the uevent message
112        private final HashMap<String,String> mMap = new HashMap<String,String>();
113
114        public UEvent(String message) {
115            int offset = 0;
116            int length = message.length();
117
118            while (offset < length) {
119                int equals = message.indexOf('=', offset);
120                int at = message.indexOf(0, offset);
121                if (at < 0) break;
122
123                if (equals > offset && equals < at) {
124                    // key is before the equals sign, and value is after
125                    mMap.put(message.substring(offset, equals),
126                            message.substring(equals + 1, at));
127                }
128
129                offset = at + 1;
130            }
131        }
132
133        public String get(String key) {
134            return mMap.get(key);
135        }
136
137        public String get(String key, String defaultValue) {
138            String result = mMap.get(key);
139            return (result == null ? defaultValue : result);
140        }
141
142        public String toString() {
143            return mMap.toString();
144        }
145    }
146
147    private static final class UEventThread extends Thread {
148        /** Many to many mapping of string match to observer.
149         *  Multimap would be better, but not available in android, so use
150         *  an ArrayList where even elements are the String match and odd
151         *  elements the corresponding UEventObserver observer */
152        private final ArrayList<Object> mKeysAndObservers = new ArrayList<Object>();
153
154        private final ArrayList<UEventObserver> mTempObserversToSignal =
155                new ArrayList<UEventObserver>();
156
157        public UEventThread() {
158            super("UEventObserver");
159        }
160
161        public void run() {
162            native_setup();
163
164            byte[] buffer = new byte[1024];
165            int len;
166            while (true) {
167                len = next_event(buffer);
168                if (len > 0) {
169                    sendEvent(new String(buffer, 0, len));
170                }
171            }
172        }
173
174        private void sendEvent(String message) {
175            synchronized (mKeysAndObservers) {
176                final int N = mKeysAndObservers.size();
177                for (int i = 0; i < N; i += 2) {
178                    final String key = (String)mKeysAndObservers.get(i);
179                    if (message.indexOf(key) != -1) {
180                        final UEventObserver observer =
181                                (UEventObserver)mKeysAndObservers.get(i + 1);
182                        mTempObserversToSignal.add(observer);
183                    }
184                }
185            }
186
187            if (!mTempObserversToSignal.isEmpty()) {
188                final UEvent event = new UEvent(message);
189                final int N = mTempObserversToSignal.size();
190                for (int i = 0; i < N; i++) {
191                    final UEventObserver observer = mTempObserversToSignal.get(i);
192                    observer.onUEvent(event);
193                }
194                mTempObserversToSignal.clear();
195            }
196        }
197
198        public void addObserver(String match, UEventObserver observer) {
199            synchronized (mKeysAndObservers) {
200                mKeysAndObservers.add(match);
201                mKeysAndObservers.add(observer);
202            }
203        }
204
205        /** Removes every key/value pair where value=observer from mObservers */
206        public void removeObserver(UEventObserver observer) {
207            synchronized (mKeysAndObservers) {
208                for (int i = 0; i < mKeysAndObservers.size(); ) {
209                    if (mKeysAndObservers.get(i + 1) == observer) {
210                        mKeysAndObservers.remove(i + 1);
211                        mKeysAndObservers.remove(i);
212                    } else {
213                        i += 2;
214                    }
215                }
216            }
217        }
218    }
219}
220