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<?>...) -- 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<ReturnType>(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