blob: 212c7f4b5e79eb9f4d51e5b0669cae8ede49bd6f [file] [log] [blame]
Winsone6c90732015-09-24 16:06:29 -07001/*
2 * Copyright (C) 2014 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 com.android.systemui.recents.events;
18
19import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.os.Bundle;
24import android.os.Handler;
25import android.os.Looper;
26import android.os.SystemClock;
27import android.os.UserHandle;
28import android.util.Log;
29import android.util.MutableBoolean;
Winson Chungaaeaac12015-12-16 16:49:36 -050030import com.android.systemui.recents.misc.ReferenceCountedTrigger;
31
Winsone6c90732015-09-24 16:06:29 -070032import java.lang.ref.WeakReference;
33import java.lang.reflect.Constructor;
34import java.lang.reflect.InvocationTargetException;
35import java.lang.reflect.Method;
36import java.lang.reflect.Modifier;
37import java.util.ArrayList;
38import java.util.Collections;
39import java.util.Comparator;
40import java.util.HashMap;
41import java.util.List;
42
43/**
44 * Represents a subscriber, which implements various event bus handler methods.
45 */
46class Subscriber {
47 private WeakReference<Object> mSubscriber;
48
49 long registrationTime;
50
51 Subscriber(Object subscriber, long registrationTime) {
52 mSubscriber = new WeakReference<>(subscriber);
53 this.registrationTime = registrationTime;
54 }
55
56 public String toString(int priority) {
57 Object sub = mSubscriber.get();
58 String id = Integer.toHexString(System.identityHashCode(sub));
59 return sub.getClass().getSimpleName() + " [0x" + id + ", P" + priority + "]";
60 }
61
62 public Object getReference() {
63 return mSubscriber.get();
64 }
65}
66
67/**
68 * Represents an event handler with a priority.
69 */
70class EventHandler {
71 int priority;
72 Subscriber subscriber;
73 EventHandlerMethod method;
74
75 EventHandler(Subscriber subscriber, EventHandlerMethod method, int priority) {
76 this.subscriber = subscriber;
77 this.method = method;
78 this.priority = priority;
79 }
80
81 @Override
82 public String toString() {
83 return subscriber.toString(priority) + " " + method.toString();
84 }
85}
86
87/**
88 * Represents the low level method handling a particular event.
89 */
90class EventHandlerMethod {
91 private Method mMethod;
92 Class<? extends EventBus.Event> eventType;
93
94 EventHandlerMethod(Method method, Class<? extends EventBus.Event> eventType) {
95 mMethod = method;
96 mMethod.setAccessible(true);
97 this.eventType = eventType;
98 }
99
100 public void invoke(Object target, EventBus.Event event)
101 throws InvocationTargetException, IllegalAccessException {
102 mMethod.invoke(target, event);
103 }
104
105 @Override
106 public String toString() {
107 return mMethod.getName() + "(" + eventType.getSimpleName() + ")";
108 }
109}
110
111/**
112 * A simple in-process event bus. It is simple because we can make assumptions about the state of
113 * SystemUI and Recent's lifecycle.
114 *
115 * <p>
116 * Currently, there is a single EventBus that handles {@link EventBus.Event}s for each subscriber
117 * on the main application thread. Publishers can send() events to synchronously call subscribers
118 * of that event, or post() events to be processed in the next run of the {@link Looper}. In
119 * addition, the EventBus supports sending and handling {@link EventBus.InterprocessEvent}s
120 * (within the same package) implemented using standard {@link BroadcastReceiver} mechanism.
121 * Interprocess events must be posted using postInterprocess() to ensure that it is dispatched
122 * correctly across processes.
123 *
124 * <p>
125 * Subscribers must be registered with a particular EventBus before they will receive events, and
126 * handler methods must match a specific signature.
127 *
128 * <p>
129 * Event method signature:<ul>
130 * <li>Methods must be public final
131 * <li>Methods must return void
132 * <li>Methods must be called "onBusEvent"
133 * <li>Methods must take one parameter, of class type deriving from {@link EventBus.Event}
134 * </ul>
135 *
136 * <p>
137 * Interprocess-Event method signature:<ul>
138 * <li>Methods must be public final
139 * <li>Methods must return void
140 * <li>Methods must be called "onInterprocessBusEvent"
141 * <li>Methods must take one parameter, of class type deriving from {@link EventBus.InterprocessEvent}
142 * </ul>
143 * </p>
144 *
145 * </p>
146 * Each subscriber can be registered with a given priority (default 1), and events will be dispatch
147 * in decreasing order of priority. For subscribers with the same priority, events will be
148 * dispatched by latest registration time to earliest.
149 *
150 * <p>
151 * Interprocess events must extend {@link EventBus.InterprocessEvent}, have a constructor which
152 * takes a {@link Bundle} and implement toBundle(). This allows us to serialize events to be sent
153 * across processes.
154 *
155 * <p>
156 * Caveats:<ul>
157 * <li>The EventBus keeps a {@link WeakReference} to the publisher to prevent memory leaks, so
158 * there must be another strong reference to the publisher for it to not get garbage-collected and
159 * continue receiving events.
160 * <li>Because the event handlers are called back using reflection, the EventBus is not intended
161 * for use in tight, performance criticial loops. For most user input/system callback events, this
162 * is generally of low enough frequency to use the EventBus.
163 * <li>Because the event handlers are called back using reflection, there will often be no
164 * references to them from actual code. The proguard configuration will be need to be updated to
165 * keep these extra methods:
166 *
167 * -keepclassmembers class ** {
168 * public void onBusEvent(**);
169 * public void onInterprocessBusEvent(**);
170 * }
171 * -keepclassmembers class ** extends **.EventBus$InterprocessEvent {
172 * public <init>(android.os.Bundle);
173 * }
174 *
175 * <li>Subscriber registration can be expensive depending on the subscriber's {@link Class}. This
176 * is only done once per class type, but if possible, it is best to pre-register an instance of
177 * that class beforehand or when idle.
178 * <li>Each event should be sent once. Events may hold internal information about the current
179 * dispatch, or may be queued to be dispatched on another thread (if posted from a non-main thread),
180 * so it may be unsafe to edit, change, or re-send the event again.
181 * <li>Events should follow a pattern of public-final POD (plain old data) objects, where they are
182 * initialized by the constructor and read by each subscriber of that event. Subscribers should
183 * never alter events as they are processed, and this enforces that pattern.
184 * </ul>
185 *
186 * <p>
187 * Future optimizations:
188 * <li>throw exception/log when a subscriber loses the reference
189 * <li>trace cost per registration & invocation
190 * <li>trace cross-process invocation
191 * <li>register(subscriber, Class&lt;?&gt;...) -- pass in exact class types you want registered
192 * <li>setSubscriberEventHandlerPriority(subscriber, Class<Event>, priority)
193 * <li>allow subscribers to implement interface, ie. EventBus.Subscriber, which lets then test a
194 * message before invocation (ie. check if task id == this task id)
195 * <li>add postOnce() which automatically debounces
196 * <li>add postDelayed() which delays / postDelayedOnce() which delays and bounces
197 * <li>consolidate register() and registerInterprocess()
198 * <li>sendForResult&lt;ReturnType&gt;(Event) to send and get a result, but who will send the
199 * result?
200 * </p>
201 */
202public class EventBus extends BroadcastReceiver {
203
Winson0d14d4d2015-10-26 17:05:04 -0700204 private static final String TAG = "EventBus";
205 private static final boolean DEBUG_TRACE_ALL = false;
Winsone6c90732015-09-24 16:06:29 -0700206
207 /**
208 * An event super class that allows us to track internal event state across subscriber
209 * invocations.
210 *
211 * Events should not be edited by subscribers.
212 */
Winsone7f138c2015-10-22 16:15:21 -0700213 public static class Event implements Cloneable {
Winsone6c90732015-09-24 16:06:29 -0700214 // Indicates that this event's dispatch should be traced and logged to logcat
215 boolean trace;
216 // Indicates that this event must be posted on the EventBus's looper thread before invocation
217 boolean requiresPost;
218 // Not currently exposed, allows a subscriber to cancel further dispatch of this event
219 boolean cancelled;
220
221 // Only accessible from derived events
222 protected Event() {}
Winsone7f138c2015-10-22 16:15:21 -0700223
Winson Chungaaeaac12015-12-16 16:49:36 -0500224 /**
225 * Called by the EventBus prior to dispatching this event to any subscriber of this event.
226 */
227 void onPreDispatch() {
228 // Do nothing
229 }
230
231 /**
232 * Called by the EventBus after dispatching this event to every subscriber of this event.
233 */
234 void onPostDispatch() {
235 // Do nothing
236 }
237
Winsone7f138c2015-10-22 16:15:21 -0700238 @Override
239 protected Object clone() throws CloneNotSupportedException {
240 Event evt = (Event) super.clone();
241 // When cloning an event, reset the cancelled-dispatch state
242 evt.cancelled = false;
243 return evt;
244 }
Winsone6c90732015-09-24 16:06:29 -0700245 }
246
247 /**
Winson Chungaaeaac12015-12-16 16:49:36 -0500248 * An event that represents an animated state change, which allows subscribers to coordinate
249 * callbacks which happen after the animation has taken place.
250 *
251 * Internally, it is guaranteed that increment() and decrement() will be called before and the
252 * after the event is dispatched.
253 */
254 public static class AnimatedEvent extends Event {
255
256 private final ReferenceCountedTrigger mTrigger = new ReferenceCountedTrigger();
257
258 // Only accessible from derived events
259 protected AnimatedEvent() {}
260
261 /**
262 * Returns the reference counted trigger that coordinates the animations for this event.
263 */
264 public ReferenceCountedTrigger getAnimationTrigger() {
265 return mTrigger;
266 }
267
268 /**
269 * Adds a callback that is guaranteed to be called after the state has changed regardless of
270 * whether an actual animation took place.
271 */
272 public void addPostAnimationCallback(Runnable r) {
273 mTrigger.addLastDecrementRunnable(r);
274 }
275
276 @Override
277 void onPreDispatch() {
278 mTrigger.increment();
279 }
280
281 @Override
282 void onPostDispatch() {
283 mTrigger.decrement();
284 }
285
286 @Override
287 protected Object clone() throws CloneNotSupportedException {
288 throw new CloneNotSupportedException();
289 }
290 }
291
292 /**
Winson55003902016-01-12 12:00:37 -0800293 * An event that can be reusable, only used for situations where we want to reduce memory
294 * allocations when events are sent frequently (ie. on scroll).
295 */
296 public static class ReusableEvent extends Event {
297
298 private int mDispatchCount;
299
300 protected ReusableEvent() {}
301
302 @Override
303 void onPostDispatch() {
304 super.onPostDispatch();
305 mDispatchCount++;
306 }
307
308 @Override
309 protected Object clone() throws CloneNotSupportedException {
310 throw new CloneNotSupportedException();
311 }
312 }
313
314 /**
Winsone6c90732015-09-24 16:06:29 -0700315 * An inter-process event super class that allows us to track user state across subscriber
316 * invocations.
317 */
318 public static class InterprocessEvent extends Event {
319 private static final String EXTRA_USER = "_user";
320
321 // The user which this event originated from
322 public final int user;
323
324 // Only accessible from derived events
325 protected InterprocessEvent(int user) {
326 this.user = user;
327 }
328
329 /**
330 * Called from the event bus
331 */
332 protected InterprocessEvent(Bundle b) {
333 user = b.getInt(EXTRA_USER);
334 }
335
336 protected Bundle toBundle() {
337 Bundle b = new Bundle();
338 b.putInt(EXTRA_USER, user);
339 return b;
340 }
341 }
342
343 /**
344 * Proguard must also know, and keep, all methods matching this signature.
345 *
346 * -keepclassmembers class ** {
347 * public void onBusEvent(**);
348 * public void onInterprocessBusEvent(**);
349 * }
350 */
351 private static final String METHOD_PREFIX = "onBusEvent";
352 private static final String INTERPROCESS_METHOD_PREFIX = "onInterprocessBusEvent";
353
354 // Ensures that interprocess events can only be sent from a process holding this permission. */
355 private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
356
357 // Used for passing event data across process boundaries
358 private static final String EXTRA_INTERPROCESS_EVENT_BUNDLE = "interprocess_event_bundle";
359
360 // The default priority of all subscribers
361 private static final int DEFAULT_SUBSCRIBER_PRIORITY = 1;
362
Winsone6c90732015-09-24 16:06:29 -0700363 // Orders the handlers by priority and registration time
364 private static final Comparator<EventHandler> EVENT_HANDLER_COMPARATOR = new Comparator<EventHandler>() {
365 @Override
366 public int compare(EventHandler h1, EventHandler h2) {
367 // Rank the handlers by priority descending, followed by registration time descending.
368 // aka. the later registered
369 if (h1.priority != h2.priority) {
370 return h2.priority - h1.priority;
371 } else {
372 return Long.compare(h2.subscriber.registrationTime, h1.subscriber.registrationTime);
373 }
374 }
375 };
376
377 // Used for initializing the default bus
378 private static final Object sLock = new Object();
379 private static EventBus sDefaultBus;
380
381 // The handler to post all events
382 private Handler mHandler;
383
384 // Keep track of whether we have registered a broadcast receiver already, so that we can
385 // unregister ourselves before re-registering again with a new IntentFilter.
386 private boolean mHasRegisteredReceiver;
387
388 /**
389 * Map from event class -> event handler list. Keeps track of the actual mapping from event
390 * to subscriber method.
391 */
392 private HashMap<Class<? extends Event>, ArrayList<EventHandler>> mEventTypeMap = new HashMap<>();
393
394 /**
395 * Map from subscriber class -> event handler method lists. Used to determine upon registration
396 * of a new subscriber whether we need to read all the subscriber's methods again using
397 * reflection or whether we can just add the subscriber to the event type map.
398 */
399 private HashMap<Class<? extends Object>, ArrayList<EventHandlerMethod>> mSubscriberTypeMap = new HashMap<>();
400
401 /**
402 * Map from interprocess event name -> interprocess event class. Used for mapping the event
403 * name after receiving the broadcast, to the event type. After which a new instance is created
404 * and posted in the local process.
405 */
406 private HashMap<String, Class<? extends InterprocessEvent>> mInterprocessEventNameMap = new HashMap<>();
407
408 /**
409 * Set of all currently registered subscribers
410 */
411 private ArrayList<Subscriber> mSubscribers = new ArrayList<>();
412
413 // For tracing
414 private int mCallCount;
415 private long mCallDurationMicros;
416
417 /**
418 * Private constructor to create an event bus for a given looper.
419 */
420 private EventBus(Looper looper) {
421 mHandler = new Handler(looper);
422 }
423
424 /**
425 * @return the default event bus for the application's main thread.
426 */
427 public static EventBus getDefault() {
428 if (sDefaultBus == null)
429 synchronized (sLock) {
430 if (sDefaultBus == null) {
431 if (DEBUG_TRACE_ALL) {
432 logWithPid("New EventBus");
433 }
434 sDefaultBus = new EventBus(Looper.getMainLooper());
435 }
436 }
437 return sDefaultBus;
438 }
439
440 /**
441 * Registers a subscriber to receive events with the default priority.
442 *
443 * @param subscriber the subscriber to handle events. If this is the first instance of the
444 * subscriber's class type that has been registered, the class's methods will
445 * be scanned for appropriate event handler methods.
446 */
447 public void register(Object subscriber) {
448 registerSubscriber(subscriber, DEFAULT_SUBSCRIBER_PRIORITY, null);
449 }
450
451 /**
452 * Registers a subscriber to receive events with the given priority.
453 *
454 * @param subscriber the subscriber to handle events. If this is the first instance of the
455 * subscriber's class type that has been registered, the class's methods will
456 * be scanned for appropriate event handler methods.
457 * @param priority the priority that this subscriber will receive events relative to other
458 * subscribers
459 */
460 public void register(Object subscriber, int priority) {
461 registerSubscriber(subscriber, priority, null);
462 }
463
464 /**
465 * Explicitly registers a subscriber to receive interprocess events with the default priority.
466 *
467 * @param subscriber the subscriber to handle events. If this is the first instance of the
468 * subscriber's class type that has been registered, the class's methods will
469 * be scanned for appropriate event handler methods.
470 */
471 public void registerInterprocessAsCurrentUser(Context context, Object subscriber) {
472 registerInterprocessAsCurrentUser(context, subscriber, DEFAULT_SUBSCRIBER_PRIORITY);
473 }
474
475 /**
476 * Registers a subscriber to receive interprocess events with the given priority.
477 *
478 * @param subscriber the subscriber to handle events. If this is the first instance of the
479 * subscriber's class type that has been registered, the class's methods will
480 * be scanned for appropriate event handler methods.
481 * @param priority the priority that this subscriber will receive events relative to other
482 * subscribers
483 */
484 public void registerInterprocessAsCurrentUser(Context context, Object subscriber, int priority) {
485 if (DEBUG_TRACE_ALL) {
486 logWithPid("registerInterprocessAsCurrentUser(" + subscriber.getClass().getSimpleName() + ")");
487 }
488
489 // Register the subscriber normally, and update the broadcast receiver filter if this is
490 // a new subscriber type with interprocess events
491 MutableBoolean hasInterprocessEventsChanged = new MutableBoolean(false);
492 registerSubscriber(subscriber, priority, hasInterprocessEventsChanged);
493 if (DEBUG_TRACE_ALL) {
494 logWithPid("hasInterprocessEventsChanged: " + hasInterprocessEventsChanged.value);
495 }
496 if (hasInterprocessEventsChanged.value) {
497 registerReceiverForInterprocessEvents(context);
498 }
499 }
500
501 /**
502 * Remove all EventHandlers pointing to the specified subscriber. This does not remove the
503 * mapping of subscriber type to event handler method, in case new instances of this subscriber
504 * are registered.
505 */
506 public void unregister(Object subscriber) {
507 if (DEBUG_TRACE_ALL) {
508 logWithPid("unregister()");
509 }
510
511 // Fail immediately if we are being called from the non-main thread
512 long callingThreadId = Thread.currentThread().getId();
513 if (callingThreadId != mHandler.getLooper().getThread().getId()) {
514 throw new RuntimeException("Can not unregister() a subscriber from a non-main thread.");
515 }
516
517 // Return early if this is not a registered subscriber
518 if (!findRegisteredSubscriber(subscriber, true /* removeFoundSubscriber */)) {
519 return;
520 }
521
522 Class<?> subscriberType = subscriber.getClass();
523 ArrayList<EventHandlerMethod> subscriberMethods = mSubscriberTypeMap.get(subscriberType);
524 if (subscriberMethods != null) {
525 // For each of the event handlers the subscriber handles, remove all references of that
526 // handler
527 for (EventHandlerMethod method : subscriberMethods) {
528 ArrayList<EventHandler> eventHandlers = mEventTypeMap.get(method.eventType);
529 for (int i = eventHandlers.size() - 1; i >= 0; i--) {
530 if (eventHandlers.get(i).subscriber.getReference() == subscriber) {
531 eventHandlers.remove(i);
532 }
533 }
534 }
535 }
536 }
537
538 /**
539 * Explicit unregistration for interprocess event subscribers. This actually behaves exactly
540 * the same as unregister() since we also do not want to stop listening for specific
541 * inter-process messages in case new instances of that subscriber is registered.
542 */
543 public void unregisterInterprocess(Context context, Object subscriber) {
544 if (DEBUG_TRACE_ALL) {
545 logWithPid("unregisterInterprocess()");
546 }
547 unregister(subscriber);
548 }
549
550 /**
551 * Sends an event to the subscribers of the given event type immediately. This can only be
552 * called from the same thread as the EventBus's looper thread (for the default EventBus, this
553 * is the main application thread).
554 */
555 public void send(Event event) {
556 // Fail immediately if we are being called from the non-main thread
557 long callingThreadId = Thread.currentThread().getId();
558 if (callingThreadId != mHandler.getLooper().getThread().getId()) {
559 throw new RuntimeException("Can not send() a message from a non-main thread.");
560 }
561
562 if (DEBUG_TRACE_ALL) {
563 logWithPid("send(" + event.getClass().getSimpleName() + ")");
564 }
565
566 // Reset the event's cancelled state
567 event.requiresPost = false;
568 event.cancelled = false;
569 queueEvent(event);
570 }
571
572 /**
573 * Post a message to the subscribers of the given event type. The messages will be posted on
574 * the EventBus's looper thread (for the default EventBus, this is the main application thread).
575 */
576 public void post(Event event) {
577 if (DEBUG_TRACE_ALL) {
578 logWithPid("post(" + event.getClass().getSimpleName() + ")");
579 }
580
581 // Reset the event's cancelled state
582 event.requiresPost = true;
583 event.cancelled = false;
584 queueEvent(event);
585 }
586
Jorim Jaggidd98d412015-11-18 15:57:38 -0800587 /**
588 * If this method is called from the main thread, it will be handled directly. If this method
589 * is not called from the main thread, it will be posted onto the main thread.
590 */
591 public void sendOntoMainThread(Event event) {
592 long callingThreadId = Thread.currentThread().getId();
593 if (callingThreadId != mHandler.getLooper().getThread().getId()) {
594 post(event);
595 } else {
596 send(event);
597 }
598 }
599
Winsone6c90732015-09-24 16:06:29 -0700600 /** Prevent post()ing an InterprocessEvent */
601 @Deprecated
602 public void post(InterprocessEvent event) {
603 throw new RuntimeException("Not supported, use postInterprocess");
604 }
605
606 /** Prevent send()ing an InterprocessEvent */
607 @Deprecated
608 public void send(InterprocessEvent event) {
609 throw new RuntimeException("Not supported, use postInterprocess");
610 }
611
612 /**
613 * Posts an interprocess event.
614 */
615 public void postInterprocess(Context context, final InterprocessEvent event) {
616 if (DEBUG_TRACE_ALL) {
617 logWithPid("postInterprocess(" + event.getClass().getSimpleName() + ")");
618 }
619 String eventType = event.getClass().getName();
620 Bundle eventBundle = event.toBundle();
621 Intent intent = new Intent(eventType);
622 intent.setPackage(context.getPackageName());
623 intent.putExtra(EXTRA_INTERPROCESS_EVENT_BUNDLE, eventBundle);
624 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
625 Intent.FLAG_RECEIVER_FOREGROUND);
626 context.sendBroadcastAsUser(intent, UserHandle.ALL);
627 }
628
629 /**
630 * Receiver for interprocess events.
631 */
632 @Override
633 public void onReceive(Context context, Intent intent) {
634 if (DEBUG_TRACE_ALL) {
635 logWithPid("onReceive(" + intent.getAction() + ", user " + UserHandle.myUserId() + ")");
636 }
637
638 Bundle eventBundle = intent.getBundleExtra(EXTRA_INTERPROCESS_EVENT_BUNDLE);
639 Class<? extends InterprocessEvent> eventType = mInterprocessEventNameMap.get(intent.getAction());
640 try {
641 Constructor<? extends InterprocessEvent> ctor = eventType.getConstructor(Bundle.class);
642 send((Event) ctor.newInstance(eventBundle));
643 } catch (NoSuchMethodException|
644 InvocationTargetException|
645 InstantiationException|
646 IllegalAccessException e) {
Winsoneca4ab62015-11-04 10:50:28 -0800647 Log.e(TAG, "Failed to create InterprocessEvent", e.getCause());
Winsone6c90732015-09-24 16:06:29 -0700648 }
649 }
650
651 /**
652 * @return a dump of the current state of the EventBus
653 */
654 public String dump() {
655 StringBuilder output = new StringBuilder();
656 output.append("Registered class types:");
657 output.append("\n");
658 for (Class<?> clz : mSubscriberTypeMap.keySet()) {
659 output.append("\t");
660 output.append(clz.getSimpleName());
661 output.append("\n");
662 }
663 output.append("Event map:");
664 output.append("\n");
665 for (Class<?> clz : mEventTypeMap.keySet()) {
666 output.append("\t");
667 output.append(clz.getSimpleName());
668 output.append(" -> ");
669 output.append("\n");
670 ArrayList<EventHandler> handlers = mEventTypeMap.get(clz);
671 for (EventHandler handler : handlers) {
672 Object subscriber = handler.subscriber.getReference();
673 if (subscriber != null) {
674 String id = Integer.toHexString(System.identityHashCode(subscriber));
675 output.append("\t\t");
676 output.append(subscriber.getClass().getSimpleName());
677 output.append(" [0x" + id + ", #" + handler.priority + "]");
678 output.append("\n");
679 }
680 }
681 }
682 return output.toString();
683 }
684
685 /**
686 * Registers a new subscriber.
Winsone6c90732015-09-24 16:06:29 -0700687 */
688 private void registerSubscriber(Object subscriber, int priority,
689 MutableBoolean hasInterprocessEventsChangedOut) {
690 // Fail immediately if we are being called from the non-main thread
691 long callingThreadId = Thread.currentThread().getId();
692 if (callingThreadId != mHandler.getLooper().getThread().getId()) {
693 throw new RuntimeException("Can not register() a subscriber from a non-main thread.");
694 }
695
696 // Return immediately if this exact subscriber is already registered
697 if (findRegisteredSubscriber(subscriber, false /* removeFoundSubscriber */)) {
698 return;
699 }
700
701 long t1 = 0;
702 if (DEBUG_TRACE_ALL) {
703 t1 = SystemClock.currentTimeMicro();
704 logWithPid("registerSubscriber(" + subscriber.getClass().getSimpleName() + ")");
705 }
706 Subscriber sub = new Subscriber(subscriber, SystemClock.uptimeMillis());
707 Class<?> subscriberType = subscriber.getClass();
708 ArrayList<EventHandlerMethod> subscriberMethods = mSubscriberTypeMap.get(subscriberType);
709 if (subscriberMethods != null) {
710 if (DEBUG_TRACE_ALL) {
711 logWithPid("Subscriber class type already registered");
712 }
713
714 // If we've parsed this subscriber type before, just add to the set for all the known
715 // events
716 for (EventHandlerMethod method : subscriberMethods) {
717 ArrayList<EventHandler> eventTypeHandlers = mEventTypeMap.get(method.eventType);
718 eventTypeHandlers.add(new EventHandler(sub, method, priority));
719 sortEventHandlersByPriority(eventTypeHandlers);
720 }
721 mSubscribers.add(sub);
722 return;
723 } else {
724 if (DEBUG_TRACE_ALL) {
725 logWithPid("Subscriber class type requires registration");
726 }
727
728 // If we are parsing this type from scratch, ensure we add it to the subscriber type
729 // map, and pull out he handler methods below
730 subscriberMethods = new ArrayList<>();
731 mSubscriberTypeMap.put(subscriberType, subscriberMethods);
732 mSubscribers.add(sub);
733 }
734
735 // Find all the valid event bus handler methods of the subscriber
736 MutableBoolean isInterprocessEvent = new MutableBoolean(false);
Winson2536c7e2015-10-01 15:49:31 -0700737 Method[] methods = subscriberType.getDeclaredMethods();
Winsone6c90732015-09-24 16:06:29 -0700738 for (Method m : methods) {
739 Class<?>[] parameterTypes = m.getParameterTypes();
740 isInterprocessEvent.value = false;
741 if (isValidEventBusHandlerMethod(m, parameterTypes, isInterprocessEvent)) {
742 Class<? extends Event> eventType = (Class<? extends Event>) parameterTypes[0];
743 ArrayList<EventHandler> eventTypeHandlers = mEventTypeMap.get(eventType);
744 if (eventTypeHandlers == null) {
745 eventTypeHandlers = new ArrayList<>();
746 mEventTypeMap.put(eventType, eventTypeHandlers);
747 }
748 if (isInterprocessEvent.value) {
749 try {
750 // Enforce that the event must have a Bundle constructor
751 eventType.getConstructor(Bundle.class);
752
753 mInterprocessEventNameMap.put(eventType.getName(),
754 (Class<? extends InterprocessEvent>) eventType);
755 if (hasInterprocessEventsChangedOut != null) {
756 hasInterprocessEventsChangedOut.value = true;
757 }
758 } catch (NoSuchMethodException e) {
759 throw new RuntimeException("Expected InterprocessEvent to have a Bundle constructor");
760 }
761 }
762 EventHandlerMethod method = new EventHandlerMethod(m, eventType);
763 EventHandler handler = new EventHandler(sub, method, priority);
764 eventTypeHandlers.add(handler);
765 subscriberMethods.add(method);
766 sortEventHandlersByPriority(eventTypeHandlers);
767
768 if (DEBUG_TRACE_ALL) {
769 logWithPid(" * Method: " + m.getName() +
770 " event: " + parameterTypes[0].getSimpleName() +
771 " interprocess? " + isInterprocessEvent.value);
772 }
773 }
774 }
775 if (DEBUG_TRACE_ALL) {
776 logWithPid("Registered " + subscriber.getClass().getSimpleName() + " in " +
777 (SystemClock.currentTimeMicro() - t1) + " microseconds");
778 }
779 }
780
781 /**
782 * Adds a new message.
783 */
784 private void queueEvent(final Event event) {
785 ArrayList<EventHandler> eventHandlers = mEventTypeMap.get(event.getClass());
786 if (eventHandlers == null) {
787 return;
788 }
Winson Chungaaeaac12015-12-16 16:49:36 -0500789
790 // Prepare this event
791 boolean hasPostedEvent = false;
792 event.onPreDispatch();
793
Winsone6c90732015-09-24 16:06:29 -0700794 // We need to clone the list in case a subscriber unregisters itself during traversal
Winson55003902016-01-12 12:00:37 -0800795 // TODO: Investigate whether we can skip the object creation here
Winsone6c90732015-09-24 16:06:29 -0700796 eventHandlers = (ArrayList<EventHandler>) eventHandlers.clone();
Winson55003902016-01-12 12:00:37 -0800797 int eventHandlerCount = eventHandlers.size();
798 for (int i = 0; i < eventHandlerCount; i++) {
799 final EventHandler eventHandler = eventHandlers.get(i);
Winsone6c90732015-09-24 16:06:29 -0700800 if (eventHandler.subscriber.getReference() != null) {
801 if (event.requiresPost) {
802 mHandler.post(new Runnable() {
803 @Override
804 public void run() {
805 processEvent(eventHandler, event);
806 }
807 });
Winson Chungaaeaac12015-12-16 16:49:36 -0500808 hasPostedEvent = true;
Winsone6c90732015-09-24 16:06:29 -0700809 } else {
810 processEvent(eventHandler, event);
811 }
812 }
813 }
Winson Chungaaeaac12015-12-16 16:49:36 -0500814
815 // Clean up after this event, deferring until all subscribers have been called
816 if (hasPostedEvent) {
817 mHandler.post(new Runnable() {
818 @Override
819 public void run() {
820 event.onPostDispatch();
821 }
822 });
823 } else {
824 event.onPostDispatch();
825 }
Winsone6c90732015-09-24 16:06:29 -0700826 }
827
828 /**
829 * Processes and dispatches the given event to the given event handler, on the thread of whoever
830 * calls this method.
831 */
832 private void processEvent(final EventHandler eventHandler, final Event event) {
833 // Skip if the event was already cancelled
834 if (event.cancelled) {
835 if (event.trace || DEBUG_TRACE_ALL) {
836 logWithPid("Event dispatch cancelled");
837 }
838 return;
839 }
840
841 try {
842 if (event.trace || DEBUG_TRACE_ALL) {
843 logWithPid(" -> " + eventHandler.toString());
844 }
845 Object sub = eventHandler.subscriber.getReference();
846 if (sub != null) {
847 long t1 = 0;
848 if (DEBUG_TRACE_ALL) {
849 t1 = SystemClock.currentTimeMicro();
850 }
851 eventHandler.method.invoke(sub, event);
852 if (DEBUG_TRACE_ALL) {
853 long duration = (SystemClock.currentTimeMicro() - t1);
854 mCallDurationMicros += duration;
855 mCallCount++;
856 logWithPid(eventHandler.method.toString() + " duration: " + duration +
857 " microseconds, avg: " + (mCallDurationMicros / mCallCount));
858 }
859 } else {
860 Log.e(TAG, "Failed to deliver event to null subscriber");
861 }
862 } catch (IllegalAccessException e) {
Winsoneca4ab62015-11-04 10:50:28 -0800863 Log.e(TAG, "Failed to invoke method", e.getCause());
Winsone6c90732015-09-24 16:06:29 -0700864 } catch (InvocationTargetException e) {
Winson5b7dd532015-12-01 16:02:12 -0800865 throw new RuntimeException(e.getCause());
Winsone6c90732015-09-24 16:06:29 -0700866 }
867 }
868
869 /**
870 * Re-registers the broadcast receiver for any new messages that we want to listen for.
871 */
872 private void registerReceiverForInterprocessEvents(Context context) {
873 if (DEBUG_TRACE_ALL) {
874 logWithPid("registerReceiverForInterprocessEvents()");
875 }
876 // Rebuild the receiver filter with the new interprocess events
877 IntentFilter filter = new IntentFilter();
878 for (String eventName : mInterprocessEventNameMap.keySet()) {
879 filter.addAction(eventName);
880 if (DEBUG_TRACE_ALL) {
881 logWithPid(" filter: " + eventName);
882 }
883 }
884 // Re-register the receiver with the new filter
885 if (mHasRegisteredReceiver) {
886 context.unregisterReceiver(this);
887 }
888 context.registerReceiverAsUser(this, UserHandle.ALL, filter, PERMISSION_SELF, mHandler);
889 mHasRegisteredReceiver = true;
890 }
891
892 /**
893 * Returns whether this subscriber is currently registered. If {@param removeFoundSubscriber}
894 * is true, then remove the subscriber before returning.
895 */
896 private boolean findRegisteredSubscriber(Object subscriber, boolean removeFoundSubscriber) {
897 for (int i = mSubscribers.size() - 1; i >= 0; i--) {
898 Subscriber sub = mSubscribers.get(i);
899 if (sub.getReference() == subscriber) {
900 if (removeFoundSubscriber) {
901 mSubscribers.remove(i);
902 }
903 return true;
904 }
905 }
906 return false;
907 }
908
909 /**
910 * @return whether {@param method} is a valid (normal or interprocess) event bus handler method
911 */
912 private boolean isValidEventBusHandlerMethod(Method method, Class<?>[] parameterTypes,
913 MutableBoolean isInterprocessEventOut) {
914 int modifiers = method.getModifiers();
915 if (Modifier.isPublic(modifiers) &&
916 Modifier.isFinal(modifiers) &&
917 method.getReturnType().equals(Void.TYPE) &&
918 parameterTypes.length == 1) {
919 if (EventBus.InterprocessEvent.class.isAssignableFrom(parameterTypes[0]) &&
920 method.getName().startsWith(INTERPROCESS_METHOD_PREFIX)) {
921 isInterprocessEventOut.value = true;
922 return true;
923 } else if (EventBus.Event.class.isAssignableFrom(parameterTypes[0]) &&
924 method.getName().startsWith(METHOD_PREFIX)) {
925 isInterprocessEventOut.value = false;
926 return true;
927 } else {
928 if (DEBUG_TRACE_ALL) {
929 if (!EventBus.Event.class.isAssignableFrom(parameterTypes[0])) {
930 logWithPid(" Expected method take an Event-based parameter: " + method.getName());
931 } else if (!method.getName().startsWith(INTERPROCESS_METHOD_PREFIX) &&
932 !method.getName().startsWith(METHOD_PREFIX)) {
933 logWithPid(" Expected method start with method prefix: " + method.getName());
934 }
935 }
936 }
937 } else {
938 if (DEBUG_TRACE_ALL) {
939 if (!Modifier.isPublic(modifiers)) {
940 logWithPid(" Expected method to be public: " + method.getName());
941 } else if (!Modifier.isFinal(modifiers)) {
942 logWithPid(" Expected method to be final: " + method.getName());
943 } else if (!method.getReturnType().equals(Void.TYPE)) {
944 logWithPid(" Expected method to return null: " + method.getName());
945 }
946 }
947 }
948 return false;
949 }
950
951 /**
952 * Sorts the event handlers by priority and registration time.
953 */
954 private void sortEventHandlersByPriority(List<EventHandler> eventHandlers) {
955 Collections.sort(eventHandlers, EVENT_HANDLER_COMPARATOR);
956 }
957
958 /**
959 * Helper method to log the given {@param text} with the current process and user id.
960 */
961 private static void logWithPid(String text) {
962 Log.d(TAG, "[" + android.os.Process.myPid() + ", u" + UserHandle.myUserId() + "] " + text);
963 }
964}