Home | History | Annotate | Download | only in events
      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 
     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  */
     72 class 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  */
     92 class 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  */
    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
    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 volatile 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             // This is just an optimization to return early if there are no handlers. However, we
    814             // should still ensure that we call pre/post dispatch callbacks so that AnimatedEvents
    815             // are still cleaned up correctly if a listener has not been registered to handle them
    816             event.onPreDispatch();
    817             event.onPostDispatch();
    818             return;
    819         }
    820 
    821         // Prepare this event
    822         boolean hasPostedEvent = false;
    823         event.onPreDispatch();
    824 
    825         // We need to clone the list in case a subscriber unregisters itself during traversal
    826         // TODO: Investigate whether we can skip the object creation here
    827         eventHandlers = (ArrayList<EventHandler>) eventHandlers.clone();
    828         int eventHandlerCount = eventHandlers.size();
    829         for (int i = 0; i < eventHandlerCount; i++) {
    830             final EventHandler eventHandler = eventHandlers.get(i);
    831             if (eventHandler.subscriber.getReference() != null) {
    832                 if (event.requiresPost) {
    833                     mHandler.post(new Runnable() {
    834                         @Override
    835                         public void run() {
    836                             processEvent(eventHandler, event);
    837                         }
    838                     });
    839                     hasPostedEvent = true;
    840                 } else {
    841                     processEvent(eventHandler, event);
    842                 }
    843             }
    844         }
    845 
    846         // Clean up after this event, deferring until all subscribers have been called
    847         if (hasPostedEvent) {
    848             mHandler.post(new Runnable() {
    849                 @Override
    850                 public void run() {
    851                     event.onPostDispatch();
    852                 }
    853             });
    854         } else {
    855             event.onPostDispatch();
    856         }
    857     }
    858 
    859     /**
    860      * Processes and dispatches the given event to the given event handler, on the thread of whoever
    861      * calls this method.
    862      */
    863     private void processEvent(final EventHandler eventHandler, final Event event) {
    864         // Skip if the event was already cancelled
    865         if (event.cancelled) {
    866             if (event.trace || DEBUG_TRACE_ALL) {
    867                 logWithPid("Event dispatch cancelled");
    868             }
    869             return;
    870         }
    871 
    872         try {
    873             if (event.trace || DEBUG_TRACE_ALL) {
    874                 logWithPid(" -> " + eventHandler.toString());
    875             }
    876             Object sub = eventHandler.subscriber.getReference();
    877             if (sub != null) {
    878                 long t1 = 0;
    879                 if (DEBUG_TRACE_ALL) {
    880                     t1 = SystemClock.currentTimeMicro();
    881                 }
    882                 eventHandler.method.invoke(sub, event);
    883                 if (DEBUG_TRACE_ALL) {
    884                     long duration = (SystemClock.currentTimeMicro() - t1);
    885                     mCallDurationMicros += duration;
    886                     mCallCount++;
    887                     logWithPid(eventHandler.method.toString() + " duration: " + duration +
    888                             " microseconds, avg: " + (mCallDurationMicros / mCallCount));
    889                 }
    890             } else {
    891                 Log.e(TAG, "Failed to deliver event to null subscriber");
    892             }
    893         } catch (IllegalAccessException e) {
    894             Log.e(TAG, "Failed to invoke method", e.getCause());
    895         } catch (InvocationTargetException e) {
    896             throw new RuntimeException(e.getCause());
    897         }
    898     }
    899 
    900     /**
    901      * Re-registers the broadcast receiver for any new messages that we want to listen for.
    902      */
    903     private void registerReceiverForInterprocessEvents(Context context) {
    904         if (DEBUG_TRACE_ALL) {
    905             logWithPid("registerReceiverForInterprocessEvents()");
    906         }
    907         // Rebuild the receiver filter with the new interprocess events
    908         IntentFilter filter = new IntentFilter();
    909         for (String eventName : mInterprocessEventNameMap.keySet()) {
    910             filter.addAction(eventName);
    911             if (DEBUG_TRACE_ALL) {
    912                 logWithPid("  filter: " + eventName);
    913             }
    914         }
    915         // Re-register the receiver with the new filter
    916         if (mHasRegisteredReceiver) {
    917             context.unregisterReceiver(this);
    918         }
    919         context.registerReceiverAsUser(this, UserHandle.ALL, filter, PERMISSION_SELF, mHandler);
    920         mHasRegisteredReceiver = true;
    921     }
    922 
    923     /**
    924      * Returns whether this subscriber is currently registered.  If {@param removeFoundSubscriber}
    925      * is true, then remove the subscriber before returning.
    926      */
    927     private boolean findRegisteredSubscriber(Object subscriber, boolean removeFoundSubscriber) {
    928         for (int i = mSubscribers.size() - 1; i >= 0; i--) {
    929             Subscriber sub = mSubscribers.get(i);
    930             if (sub.getReference() == subscriber) {
    931                 if (removeFoundSubscriber) {
    932                     mSubscribers.remove(i);
    933                 }
    934                 return true;
    935             }
    936         }
    937         return false;
    938     }
    939 
    940     /**
    941      * @return whether {@param method} is a valid (normal or interprocess) event bus handler method
    942      */
    943     private boolean isValidEventBusHandlerMethod(Method method, Class<?>[] parameterTypes,
    944             MutableBoolean isInterprocessEventOut) {
    945         int modifiers = method.getModifiers();
    946         if (Modifier.isPublic(modifiers) &&
    947                 Modifier.isFinal(modifiers) &&
    948                 method.getReturnType().equals(Void.TYPE) &&
    949                 parameterTypes.length == 1) {
    950             if (EventBus.InterprocessEvent.class.isAssignableFrom(parameterTypes[0]) &&
    951                     method.getName().startsWith(INTERPROCESS_METHOD_PREFIX)) {
    952                 isInterprocessEventOut.value = true;
    953                 return true;
    954             } else if (EventBus.Event.class.isAssignableFrom(parameterTypes[0]) &&
    955                             method.getName().startsWith(METHOD_PREFIX)) {
    956                 isInterprocessEventOut.value = false;
    957                 return true;
    958             } else {
    959                 if (DEBUG_TRACE_ALL) {
    960                     if (!EventBus.Event.class.isAssignableFrom(parameterTypes[0])) {
    961                         logWithPid("  Expected method take an Event-based parameter: " + method.getName());
    962                     } else if (!method.getName().startsWith(INTERPROCESS_METHOD_PREFIX) &&
    963                             !method.getName().startsWith(METHOD_PREFIX)) {
    964                         logWithPid("  Expected method start with method prefix: " + method.getName());
    965                     }
    966                 }
    967             }
    968         } else {
    969             if (DEBUG_TRACE_ALL) {
    970                 if (!Modifier.isPublic(modifiers)) {
    971                     logWithPid("  Expected method to be public: " + method.getName());
    972                 } else if (!Modifier.isFinal(modifiers)) {
    973                     logWithPid("  Expected method to be final: " + method.getName());
    974                 } else if (!method.getReturnType().equals(Void.TYPE)) {
    975                     logWithPid("  Expected method to return null: " + method.getName());
    976                 }
    977             }
    978         }
    979         return false;
    980     }
    981 
    982     /**
    983      * Sorts the event handlers by priority and registration time.
    984      */
    985     private void sortEventHandlersByPriority(List<EventHandler> eventHandlers) {
    986         Collections.sort(eventHandlers, EVENT_HANDLER_COMPARATOR);
    987     }
    988 
    989     /**
    990      * Helper method to log the given {@param text} with the current process and user id.
    991      */
    992     private static void logWithPid(String text) {
    993         Log.d(TAG, "[" + android.os.Process.myPid() + ", u" + UserHandle.myUserId() + "] " + text);
    994     }
    995 }
    996