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