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