• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 com.googlecode.android_scripting.facade;
18 
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Queue;
22 import java.util.Set;
23 import java.util.concurrent.ConcurrentLinkedQueue;
24 import java.util.concurrent.CopyOnWriteArrayList;
25 import java.util.concurrent.TimeUnit;
26 
27 import org.json.JSONException;
28 
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.IntentFilter;
32 import android.os.Bundle;
33 
34 import com.google.common.collect.ArrayListMultimap;
35 import com.google.common.collect.Lists;
36 import com.google.common.collect.Multimap;
37 import com.google.common.collect.Multimaps;
38 import com.googlecode.android_scripting.Log;
39 import com.googlecode.android_scripting.event.Event;
40 import com.googlecode.android_scripting.event.EventObserver;
41 import com.googlecode.android_scripting.event.EventServer;
42 import com.googlecode.android_scripting.future.FutureResult;
43 import com.googlecode.android_scripting.jsonrpc.JsonBuilder;
44 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
45 import com.googlecode.android_scripting.rpc.Rpc;
46 import com.googlecode.android_scripting.rpc.RpcDefault;
47 import com.googlecode.android_scripting.rpc.RpcDeprecated;
48 import com.googlecode.android_scripting.rpc.RpcName;
49 import com.googlecode.android_scripting.rpc.RpcOptional;
50 import com.googlecode.android_scripting.rpc.RpcParameter;
51 
52 /**
53  * Manage the event queue. <br>
54  * <br>
55  * <b>Usage Notes:</b><br>
56  * EventFacade APIs interact with the Event Queue (a data buffer containing up to 1024 event
57  * entries).<br>
58  * Events are automatically entered into the Event Queue following API calls such as startSensing()
59  * and startLocating().<br>
60  * The Event Facade provides control over how events are entered into (and removed from) the Event
61  * Queue.<br>
62  * The Event Queue provides a useful means of recording background events (such as sensor data) when
63  * the phone is busy with foreground activities.
64  *
65  */
66 public class EventFacade extends RpcReceiver {
67     /**
68      * The maximum length of the event queue. Old events will be discarded when this limit is
69      * exceeded.
70      */
71     private static final int MAX_QUEUE_SIZE = 1024;
72     private final Queue<Event> mEventQueue = new ConcurrentLinkedQueue<Event>();
73     private final CopyOnWriteArrayList<EventObserver> mGlobalEventObservers =
74             new CopyOnWriteArrayList<EventObserver>();
75     private final Multimap<String, EventObserver> mNamedEventObservers = Multimaps
76             .synchronizedListMultimap(ArrayListMultimap.<String, EventObserver> create());
77     private EventServer mEventServer = null;
78     private final HashMap<String, BroadcastListener> mBroadcastListeners =
79             new HashMap<String, BroadcastListener>();
80     private final Context mContext;
81     private boolean bEventServerRunning;
82 
EventFacade(FacadeManager manager)83     public EventFacade(FacadeManager manager) {
84         super(manager);
85         mContext = manager.getService().getApplicationContext();
86         Log.v("Creating new EventFacade Instance()");
87         bEventServerRunning = false;
88     }
89 
90     /**
91      * Example (python): droid.eventClearBuffer()
92      */
93     @Rpc(description = "Clears all events from the event buffer.")
eventClearBuffer()94     public void eventClearBuffer() {
95         mEventQueue.clear();
96     }
97 
98     /**
99      * Registers a listener for a new broadcast signal
100      */
101     @Rpc(description = "Registers a listener for a new broadcast signal")
eventRegisterForBroadcast( @pcParametername = "category") String category, @RpcParameter(name = "enqueue", description = "Should this events be added to the event queue or only dispatched") @RpcDefault(value = "true") Boolean enqueue)102     public boolean eventRegisterForBroadcast(
103             @RpcParameter(name = "category") String category,
104             @RpcParameter(name = "enqueue",
105                     description = "Should this events be added to the event queue or only dispatched") @RpcDefault(value = "true") Boolean enqueue) {
106         if (mBroadcastListeners.containsKey(category)) {
107             return false;
108         }
109 
110         BroadcastListener b = new BroadcastListener(this, enqueue.booleanValue());
111         IntentFilter c = new IntentFilter(category);
112         mContext.registerReceiver(b, c);
113         mBroadcastListeners.put(category, b);
114 
115         return true;
116     }
117 
118     @Rpc(description = "Stop listening for a broadcast signal")
eventUnregisterForBroadcast( @pcParametername = "category") String category)119     public void eventUnregisterForBroadcast(
120             @RpcParameter(name = "category") String category) {
121         if (!mBroadcastListeners.containsKey(category)) {
122             return;
123         }
124 
125         mContext.unregisterReceiver(mBroadcastListeners.get(category));
126         mBroadcastListeners.remove(category);
127     }
128 
129     @Rpc(description = "Lists all the broadcast signals we are listening for")
eventGetBrodcastCategories()130     public Set<String> eventGetBrodcastCategories() {
131         return mBroadcastListeners.keySet();
132     }
133 
134     /**
135      * Actual data returned in the map will depend on the type of event.
136      *
137      * <pre>
138      * Example (python):
139      *     import android, time
140      *     droid = android.Android()
141      *     droid.startSensing()
142      *     time.sleep(1)
143      *     droid.eventClearBuffer()
144      *     time.sleep(1)
145      *     e = eventPoll(1).result
146      *     event_entry_number = 0
147      *     x = e[event_entry_ number]['data']['xforce']
148      * </pre>
149      *
150      * e has the format:<br>
151      * [{u'data': {u'accuracy': 0, u'pitch': -0.48766891956329345, u'xmag': -5.6875, u'azimuth':
152      * 0.3312483489513397, u'zforce': 8.3492730000000002, u'yforce': 4.5628165999999997, u'time':
153      * 1297072704.813, u'ymag': -11.125, u'zmag': -42.375, u'roll': -0.059393649548292161,
154      * u'xforce': 0.42223078000000003}, u'name': u'sensors', u'time': 1297072704813000L}]<br>
155      * x has the string value of the x force data (0.42223078000000003) at the time of the event
156      * entry. </pre>
157      */
158 
159     @Rpc(description = "Returns and removes the oldest n events (i.e. location or sensor update, etc.) from the event buffer.",
160             returns = "A List of Maps of event properties.")
eventPoll( @pcParametername = "number_of_events") @pcDefault"1") Integer number_of_events)161     public List<Event> eventPoll(
162             @RpcParameter(name = "number_of_events") @RpcDefault("1") Integer number_of_events) {
163         List<Event> events = Lists.newArrayList();
164         for (int i = 0; i < number_of_events; i++) {
165             Event event = mEventQueue.poll();
166             if (event == null) {
167                 break;
168             }
169             events.add(event);
170         }
171         return events;
172     }
173 
174     @Rpc(description = "Blocks until an event with the supplied name occurs. Event is removed from the buffer if removeEvent is True.",
175             returns = "Map of event properties.")
eventWaitFor( @pcParametername = "eventName") final String eventName, @RpcParameter(name = "removeEvent") final Boolean removeEvent, @RpcParameter(name = "timeout", description = "the maximum time to wait (in ms)") @RpcOptional Integer timeout)176     public Event eventWaitFor(
177             @RpcParameter(name = "eventName")
178             final String eventName,
179             @RpcParameter(name = "removeEvent")
180             final Boolean removeEvent,
181             @RpcParameter(name = "timeout", description = "the maximum time to wait (in ms)") @RpcOptional Integer timeout)
182             throws InterruptedException {
183         Event result = null;
184         final FutureResult<Event> futureEvent;
185         synchronized (mEventQueue) { // First check to make sure it isn't already there
186             for (Event event : mEventQueue) {
187                 if (event.getName().equals(eventName)) {
188                     result = event;
189                     if (removeEvent)
190                         mEventQueue.remove(event);
191                     return result;
192                 }
193             }
194             futureEvent = new FutureResult<Event>();
195             addNamedEventObserver(eventName, new EventObserver() {
196                 @Override
197                 public void onEventReceived(Event event) {
198                     if (event.getName().equals(eventName)) {
199                         synchronized (futureEvent) {
200                             if (!futureEvent.isDone()) {
201                                 futureEvent.set(event);
202                                 // TODO: Remove log.
203                                 Log.v(String.format("Removing observer (%s) got event  (%s)",
204                                         this,
205                                         event));
206                                 removeEventObserver(this);
207                             }
208                             if (removeEvent)
209                                 mEventQueue.remove(event);
210                         }
211                     }
212                 }
213             });
214         }
215         if (futureEvent != null) {
216             if (timeout != null) {
217                 result = futureEvent.get(timeout, TimeUnit.MILLISECONDS);
218             } else {
219                 result = futureEvent.get();
220             }
221         }
222         return result;
223     }
224 
225     @Rpc(description = "Blocks until an event occurs. The returned event is removed from the buffer.",
226             returns = "Map of event properties.")
eventWait( @pcParametername = "timeout", description = "the maximum time to wait") @pcOptional Integer timeout)227     public Event eventWait(
228             @RpcParameter(name = "timeout", description = "the maximum time to wait") @RpcOptional Integer timeout)
229             throws InterruptedException {
230         Event result = null;
231         final FutureResult<Event> futureEvent = new FutureResult<Event>();
232         EventObserver observer;
233         synchronized (mEventQueue) { // Anything in queue?
234             if (mEventQueue.size() > 0) {
235                 return mEventQueue.poll(); // return it.
236             }
237             observer = new EventObserver() {
238                 @Override
239                 public void onEventReceived(Event event) { // set up observer for any events.
240                     synchronized (futureEvent) {
241                         if (!futureEvent.isDone()) {
242                             futureEvent.set(event);
243                             // TODO: Remove log.
244                             Log.v(String.format("onEventReceived for event (%s)", event));
245                         }
246                     }
247                 }
248             };
249             addGlobalEventObserver(observer);
250         }
251         if (timeout != null) {
252             result = futureEvent.get(timeout, TimeUnit.MILLISECONDS);
253         } else {
254             result = futureEvent.get();
255         }
256         if (result != null) {
257             mEventQueue.remove(result);
258         }
259         // TODO: Remove log.
260         Log.v(String.format("Removing observer (%s) got event  (%s)", observer, result));
261         if (observer != null) {
262             removeEventObserver(observer); // Make quite sure this goes away.
263         }
264         return result;
265     }
266 
267     /**
268      * <pre>
269      * Example:
270      *   import android
271      *   from datetime import datetime
272      *   droid = android.Android()
273      *   t = datetime.now()
274      *   droid.eventPost('Some Event', t)
275      * </pre>
276      */
277     @Rpc(description = "Post an event to the event queue.")
eventPost( @pcParametername = "name", description = "Name of event") String name, @RpcParameter(name = "data", description = "Data contained in event.") String data, @RpcParameter(name = "enqueue", description = "Set to False if you don't want your events to be added to the event queue, just dispatched.") @RpcOptional @RpcDefault("false") Boolean enqueue)278     public void eventPost(
279             @RpcParameter(name = "name", description = "Name of event") String name,
280             @RpcParameter(name = "data", description = "Data contained in event.") String data,
281             @RpcParameter(name = "enqueue",
282                     description = "Set to False if you don't want your events to be added to the event queue, just dispatched.") @RpcOptional @RpcDefault("false") Boolean enqueue) {
283         postEvent(name, data, enqueue.booleanValue());
284     }
285 
286     /**
287      * Post an event and queue it
288      */
postEvent(String name, Object data)289     public void postEvent(String name, Object data) {
290         postEvent(name, data, true);
291     }
292 
293     /**
294      * Posts an event with to the event queue.
295      */
postEvent(String name, Object data, boolean enqueue)296     public void postEvent(String name, Object data, boolean enqueue) {
297         Event event = new Event(name, data);
298         if (enqueue != false) {
299             synchronized (mEventQueue) {
300                 while (mEventQueue.size() >= MAX_QUEUE_SIZE) {
301                     mEventQueue.remove();
302                 }
303                 mEventQueue.add(event);
304             }
305             Log.v(String.format("postEvent(%s)", name));
306         }
307         synchronized (mNamedEventObservers) {
308             for (EventObserver observer : mNamedEventObservers.get(name)) {
309                 observer.onEventReceived(event);
310             }
311         }
312         synchronized (mGlobalEventObservers) {
313             // TODO: Remove log.
314             Log.v(String.format("mGlobalEventObservers size (%s)", mGlobalEventObservers.size()));
315             for (EventObserver observer : mGlobalEventObservers) {
316                 observer.onEventReceived(event);
317             }
318         }
319     }
320 
321     @RpcDeprecated(value = "eventPost", release = "r4")
322     @Rpc(description = "Post an event to the event queue.")
323     @RpcName(name = "postEvent")
rpcPostEvent( @pcParametername = "name") String name, @RpcParameter(name = "data") String data)324     public void rpcPostEvent(
325             @RpcParameter(name = "name") String name,
326             @RpcParameter(name = "data") String data) {
327         postEvent(name, data);
328     }
329 
330     @RpcDeprecated(value = "eventPoll", release = "r4")
331     @Rpc(description = "Returns and removes the oldest event (i.e. location or sensor update, etc.) from the event buffer.",
332             returns = "Map of event properties.")
receiveEvent()333     public Event receiveEvent() {
334         return mEventQueue.poll();
335     }
336 
337     @RpcDeprecated(value = "eventWaitFor", release = "r4")
338     @Rpc(description = "Blocks until an event with the supplied name occurs. Event is removed from the buffer if removeEvent is True.",
339             returns = "Map of event properties.")
waitForEvent( @pcParametername = "eventName") final String eventName, @RpcOptional final Boolean removeEvent, @RpcParameter(name = "timeout", description = "the maximum time to wait") @RpcOptional Integer timeout)340     public Event waitForEvent(
341             @RpcParameter(name = "eventName")
342             final String eventName,
343             @RpcOptional
344             final Boolean removeEvent,
345             @RpcParameter(name = "timeout", description = "the maximum time to wait") @RpcOptional Integer timeout)
346             throws InterruptedException {
347         return eventWaitFor(eventName, removeEvent, timeout);
348     }
349 
350     @Rpc(description = "Opens up a socket where you can read for events posted")
startEventDispatcher( @pcParametername = "port", description = "Port to use") @pcDefault"0") @pcOptional) Integer port)351     public int startEventDispatcher(
352             @RpcParameter(name = "port", description = "Port to use") @RpcDefault("0") @RpcOptional() Integer port) {
353         if (mEventServer == null) {
354             if (port == null) {
355                 port = 0;
356             }
357             mEventServer = new EventServer(port);
358             addGlobalEventObserver(mEventServer);
359             bEventServerRunning = true;
360         }
361         return mEventServer.getAddress().getPort();
362     }
363 
364     @Rpc(description = "sl4a session is shutting down, send terminate event to client.")
closeSl4aSession()365     public void closeSl4aSession() {
366         eventClearBuffer();
367         postEvent("EventDispatcherShutdown", null);
368     }
369 
370     @Rpc(description = "Stops the event server, you can't read in the port anymore")
stopEventDispatcher()371     public void stopEventDispatcher() throws RuntimeException {
372         if (bEventServerRunning == true) {
373             if (mEventServer == null) {
374                 throw new RuntimeException("Not running");
375             }
376             bEventServerRunning = false;
377             mEventServer.shutdown();
378             Log.v(String.format("stopEventDispatcher   (%s)", mEventServer));
379             removeEventObserver(mEventServer);
380             mEventServer = null;
381         }
382         return;
383     }
384 
385     @Override
shutdown()386     public void shutdown() {
387 
388         try {
389             stopEventDispatcher();
390         } catch (Exception e) {
391             Log.e("Exception tearing down event dispatcher", e);
392         }
393         mGlobalEventObservers.clear();
394         mEventQueue.clear();
395     }
396 
addNamedEventObserver(String eventName, EventObserver observer)397     public void addNamedEventObserver(String eventName, EventObserver observer) {
398         mNamedEventObservers.put(eventName, observer);
399     }
400 
addGlobalEventObserver(EventObserver observer)401     public void addGlobalEventObserver(EventObserver observer) {
402         mGlobalEventObservers.add(observer);
403     }
404 
removeEventObserver(EventObserver observer)405     public void removeEventObserver(EventObserver observer) {
406         mNamedEventObservers.removeAll(observer);
407         mGlobalEventObservers.remove(observer);
408     }
409 
410     public class BroadcastListener extends android.content.BroadcastReceiver {
411         private EventFacade mParent;
412         private boolean mEnQueue;
413 
BroadcastListener(EventFacade parent, boolean enqueue)414         public BroadcastListener(EventFacade parent, boolean enqueue) {
415             mParent = parent;
416             mEnQueue = enqueue;
417         }
418 
419         @Override
onReceive(Context context, Intent intent)420         public void onReceive(Context context, Intent intent) {
421             Bundle data;
422             if (intent.getExtras() != null) {
423                 data = (Bundle) intent.getExtras().clone();
424             } else {
425                 data = new Bundle();
426             }
427             data.putString("action", intent.getAction());
428             try {
429                 mParent.eventPost("sl4a", JsonBuilder.build(data).toString(), mEnQueue);
430             } catch (JSONException e) {
431                 e.printStackTrace();
432             }
433         }
434 
435     }
436 }
437