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