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
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;
30
31import com.android.systemui.recents.misc.ReferenceCountedTrigger;
32
33import java.io.PrintWriter;
34import java.lang.ref.WeakReference;
35import java.lang.reflect.Constructor;
36import java.lang.reflect.InvocationTargetException;
37import java.lang.reflect.Method;
38import java.lang.reflect.Modifier;
39import java.util.ArrayList;
40import java.util.Collections;
41import java.util.Comparator;
42import java.util.HashMap;
43import java.util.List;
44
45/**
46 * Represents a subscriber, which implements various event bus handler methods.
47 */
48class Subscriber {
49    private WeakReference<Object> mSubscriber;
50
51    long registrationTime;
52
53    Subscriber(Object subscriber, long registrationTime) {
54        mSubscriber = new WeakReference<>(subscriber);
55        this.registrationTime = registrationTime;
56    }
57
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
64    public Object getReference() {
65        return mSubscriber.get();
66    }
67}
68
69/**
70 * Represents an event handler with a priority.
71 */
72class EventHandler {
73    int priority;
74    Subscriber subscriber;
75    EventHandlerMethod method;
76
77    EventHandler(Subscriber subscriber, EventHandlerMethod method, int priority) {
78        this.subscriber = subscriber;
79        this.method = method;
80        this.priority = priority;
81    }
82
83    @Override
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 */
92class EventHandlerMethod {
93    private Method mMethod;
94    Class<? extends EventBus.Event> eventType;
95
96    EventHandlerMethod(Method method, Class<? extends EventBus.Event> eventType) {
97        mMethod = method;
98        mMethod.setAccessible(true);
99        this.eventType = eventType;
100    }
101
102    public void invoke(Object target, EventBus.Event event)
103            throws InvocationTargetException, IllegalAccessException {
104        mMethod.invoke(target, event);
105    }
106
107    @Override
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 */
204public 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
224        protected Event() {}
225
226        /**
227         * Called by the EventBus prior to dispatching this event to any subscriber of this event.
228         */
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         */
236        void onPostDispatch() {
237            // Do nothing
238        }
239
240        @Override
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
261        protected AnimatedEvent() {}
262
263        /**
264         * Returns the reference counted trigger that coordinates the animations for this event.
265         */
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         */
274        public void addPostAnimationCallback(Runnable r) {
275            mTrigger.addLastDecrementRunnable(r);
276        }
277
278        @Override
279        void onPreDispatch() {
280            mTrigger.increment();
281        }
282
283        @Override
284        void onPostDispatch() {
285            mTrigger.decrement();
286        }
287
288        @Override
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
302        protected ReusableEvent() {}
303
304        @Override
305        void onPostDispatch() {
306            super.onPostDispatch();
307            mDispatchCount++;
308        }
309
310        @Override
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
327        protected InterprocessEvent(int user) {
328            this.user = user;
329        }
330
331        /**
332         * Called from the event bus
333         */
334        protected InterprocessEvent(Bundle b) {
335            user = b.getInt(EXTRA_USER);
336        }
337
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
604    public void post(InterprocessEvent event) {
605        throw new RuntimeException("Not supported, use postInterprocess");
606    }
607
608    /** Prevent send()ing an InterprocessEvent */
609    @Deprecated
610    public void send(InterprocessEvent event) {
611        throw new RuntimeException("Not supported, use postInterprocess");
612    }
613
614    /**
615     * Posts an interprocess event.
616     */
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
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     */
656    public void dump(String prefix, PrintWriter writer) {
657        writer.println(dumpInternal(prefix));
658    }
659
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
987    private static void logWithPid(String text) {
988        Log.d(TAG, "[" + android.os.Process.myPid() + ", u" + UserHandle.myUserId() + "] " + text);
989    }
990}
991