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