1 /* 2 * Copyright (C) 2020 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.server.location.listeners; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.os.Build; 22 import android.util.ArrayMap; 23 import android.util.ArraySet; 24 25 import com.android.internal.annotations.GuardedBy; 26 import com.android.internal.listeners.ListenerExecutor.ListenerOperation; 27 import com.android.internal.util.Preconditions; 28 29 import java.io.FileDescriptor; 30 import java.io.PrintWriter; 31 import java.util.AbstractMap; 32 import java.util.ArrayList; 33 import java.util.Collection; 34 import java.util.Map.Entry; 35 import java.util.Objects; 36 import java.util.function.Function; 37 import java.util.function.Predicate; 38 39 /** 40 * A base class to multiplex client listener registrations within system server. Every listener is 41 * represented by a registration object which stores all required state for a listener. Keys are 42 * used to uniquely identify every registration. Listener operations may be executed on 43 * registrations in order to invoke the represented listener. 44 * 45 * Registrations are divided into two categories, active registrations and inactive registrations, 46 * as defined by {@link #isActive(ListenerRegistration)}. If a registration's active state changes, 47 * {@link #updateRegistrations(Predicate)} must be invoked and return true for any registration 48 * whose active state may have changed. Listeners will only be invoked for active registrations. 49 * 50 * The set of active registrations is combined into a single merged registration, which is submitted 51 * to the backing service when necessary in order to register the service. The merged registration 52 * is updated whenever the set of active registration changes. 53 * 54 * Callbacks invoked for various changes will always be ordered according to this lifecycle list: 55 * 56 * <ul> 57 * <li>{@link #onRegister()}</li> 58 * <li>{@link ListenerRegistration#onRegister(Object)}</li> 59 * <li>{@link #onRegistrationAdded(Object, ListenerRegistration)}</li> 60 * <li>{@link #onRegistrationReplaced(Object, ListenerRegistration, ListenerRegistration)} (only 61 * invoked if this registration is replacing a prior registration)</li> 62 * <li>{@link #onActive()}</li> 63 * <li>{@link ListenerRegistration#onActive()}</li> 64 * <li>{@link ListenerRegistration#onInactive()}</li> 65 * <li>{@link #onInactive()}</li> 66 * <li>{@link #onRegistrationRemoved(Object, ListenerRegistration)}</li> 67 * <li>{@link ListenerRegistration#onUnregister()}</li> 68 * <li>{@link #onUnregister()}</li> 69 * </ul> 70 * 71 * Adding registrations is not allowed to be called re-entrantly (ie, while in the middle of some 72 * other operation or callback. Removal is allowed re-entrantly, however only via 73 * {@link #removeRegistration(Object, ListenerRegistration)}, not via any other removal method. This 74 * ensures re-entrant removal does not accidentally remove the incorrect registration. 75 * 76 * @param <TKey> key type 77 * @param <TListener> listener type 78 * @param <TRegistration> registration type 79 * @param <TMergedRegistration> merged registration type 80 */ 81 public abstract class ListenerMultiplexer<TKey, TListener, 82 TRegistration extends ListenerRegistration<TListener>, TMergedRegistration> { 83 84 @GuardedBy("mRegistrations") 85 private final ArrayMap<TKey, TRegistration> mRegistrations = new ArrayMap<>(); 86 87 @GuardedBy("mRegistrations") 88 private final UpdateServiceBuffer mUpdateServiceBuffer = new UpdateServiceBuffer(); 89 90 @GuardedBy("mRegistrations") 91 private final ReentrancyGuard mReentrancyGuard = new ReentrancyGuard(); 92 93 @GuardedBy("mRegistrations") 94 private int mActiveRegistrationsCount = 0; 95 96 @GuardedBy("mRegistrations") 97 private boolean mServiceRegistered = false; 98 99 @GuardedBy("mRegistrations") 100 @Nullable private TMergedRegistration mMerged; 101 102 /** 103 * Should be implemented to return a unique identifying tag that may be used for logging, etc... 104 */ getTag()105 public abstract @NonNull String getTag(); 106 107 /** 108 * Should be implemented to register with the backing service with the given merged 109 * registration, and should return true if a matching call to {@link #unregisterWithService()} 110 * is required to unregister (ie, if registration succeeds). The set of registrations passed in 111 * is the same set passed into {@link #mergeRegistrations(Collection)} to generate the merged 112 * registration. 113 * 114 * <p class="note">It may seem redundant to pass in the set of active registrations when they 115 * have already been used to generate the merged request, and indeed, for many implementations 116 * this parameter can likely simply be ignored. However, some implementations may require access 117 * to the set of registrations used to generate the merged requestion for further logic even 118 * after the merged registration has been generated. 119 * 120 * @see #mergeRegistrations(Collection) 121 * @see #reregisterWithService(Object, Object, Collection) 122 */ registerWithService(TMergedRegistration merged, @NonNull Collection<TRegistration> registrations)123 protected abstract boolean registerWithService(TMergedRegistration merged, 124 @NonNull Collection<TRegistration> registrations); 125 126 /** 127 * Invoked when the service has already been registered with some merged registration, and is 128 * now being registered with a different merged registration. The default implementation simply 129 * invokes {@link #registerWithService(Object, Collection)}. 130 * 131 * @see #registerWithService(Object, Collection) 132 */ reregisterWithService(TMergedRegistration oldMerged, TMergedRegistration newMerged, @NonNull Collection<TRegistration> registrations)133 protected boolean reregisterWithService(TMergedRegistration oldMerged, 134 TMergedRegistration newMerged, @NonNull Collection<TRegistration> registrations) { 135 return registerWithService(newMerged, registrations); 136 } 137 138 /** 139 * Should be implemented to unregister from the backing service. 140 */ unregisterWithService()141 protected abstract void unregisterWithService(); 142 143 /** 144 * Defines whether a registration is currently active or not. Only active registrations will be 145 * forwarded to {@link #registerWithService(Object, Collection)}, and listener invocations will 146 * only be delivered to active requests. If a registration's active state changes, 147 * {@link #updateRegistrations(Predicate)} must be invoked with a function that returns true for 148 * any registrations that may have changed their active state. 149 */ isActive(@onNull TRegistration registration)150 protected abstract boolean isActive(@NonNull TRegistration registration); 151 152 /** 153 * Called in order to generate a merged registration from the given set of active registrations. 154 * The list of registrations will never be empty. If the resulting merged registration is equal 155 * to the currently registered merged registration, nothing further will happen. If the merged 156 * registration differs, {@link #registerWithService(Object, Collection)} or 157 * {@link #reregisterWithService(Object, Object, Collection)} will be invoked with the new 158 * merged registration so that the backing service can be updated. 159 */ mergeRegistrations( @onNull Collection<TRegistration> registrations)160 protected abstract @Nullable TMergedRegistration mergeRegistrations( 161 @NonNull Collection<TRegistration> registrations); 162 163 /** 164 * Invoked when the multiplexer goes from having no registrations to having some registrations. 165 * This is a convenient entry point for registering listeners, etc, which only need to be 166 * present while there are any registrations. Invoked while holding the multiplexer's internal 167 * lock. 168 */ onRegister()169 protected void onRegister() {} 170 171 /** 172 * Invoked when the multiplexer goes from having some registrations to having no registrations. 173 * This is a convenient entry point for unregistering listeners, etc, which only need to be 174 * present while there are any registrations. Invoked while holding the multiplexer's internal 175 * lock. 176 */ onUnregister()177 protected void onUnregister() {} 178 179 /** 180 * Invoked when a registration is added. Invoked while holding the multiplexer's internal lock. 181 */ onRegistrationAdded(@onNull TKey key, @NonNull TRegistration registration)182 protected void onRegistrationAdded(@NonNull TKey key, @NonNull TRegistration registration) {} 183 184 /** 185 * Invoked instead of {@link #onRegistrationAdded(Object, ListenerRegistration)} if a 186 * registration is replacing an old registration. The old registration will have already been 187 * unregistered. Invoked while holding the multiplexer's internal lock. The default behavior is 188 * simply to call into {@link #onRegistrationAdded(Object, ListenerRegistration)}. 189 */ onRegistrationReplaced(@onNull TKey key, @NonNull TRegistration oldRegistration, @NonNull TRegistration newRegistration)190 protected void onRegistrationReplaced(@NonNull TKey key, @NonNull TRegistration oldRegistration, 191 @NonNull TRegistration newRegistration) { 192 onRegistrationAdded(key, newRegistration); 193 } 194 195 /** 196 * Invoked when a registration is removed. Invoked while holding the multiplexer's internal 197 * lock. 198 */ onRegistrationRemoved(@onNull TKey key, @NonNull TRegistration registration)199 protected void onRegistrationRemoved(@NonNull TKey key, @NonNull TRegistration registration) {} 200 201 /** 202 * Invoked when the multiplexer goes from having no active registrations to having some active 203 * registrations. This is a convenient entry point for registering listeners, etc, which only 204 * need to be present while there are active registrations. Invoked while holding the 205 * multiplexer's internal lock. 206 */ onActive()207 protected void onActive() {} 208 209 /** 210 * Invoked when the multiplexer goes from having some active registrations to having no active 211 * registrations. This is a convenient entry point for unregistering listeners, etc, which only 212 * need to be present while there are active registrations. Invoked while holding the 213 * multiplexer's internal lock. 214 */ onInactive()215 protected void onInactive() {} 216 217 /** 218 * Puts a new registration with the given key, replacing any previous registration under the 219 * same key. This method cannot be called to put a registration re-entrantly. 220 */ putRegistration(@onNull TKey key, @NonNull TRegistration registration)221 protected final void putRegistration(@NonNull TKey key, @NonNull TRegistration registration) { 222 replaceRegistration(key, key, registration); 223 } 224 225 /** 226 * Atomically removes the registration with the old key and adds a new registration with the 227 * given key. If there was a registration for the old key, 228 * {@link #onRegistrationReplaced(Object, ListenerRegistration, ListenerRegistration)} will be 229 * invoked for the new registration and key instead of 230 * {@link #onRegistrationAdded(Object, ListenerRegistration)}, even though they may not share 231 * the same key. The old key may be the same value as the new key, in which case this function 232 * is equivalent to {@link #putRegistration(Object, ListenerRegistration)}. This method cannot 233 * be called to add a registration re-entrantly. 234 */ replaceRegistration(@onNull TKey oldKey, @NonNull TKey key, @NonNull TRegistration registration)235 protected final void replaceRegistration(@NonNull TKey oldKey, @NonNull TKey key, 236 @NonNull TRegistration registration) { 237 Objects.requireNonNull(oldKey); 238 Objects.requireNonNull(key); 239 Objects.requireNonNull(registration); 240 241 synchronized (mRegistrations) { 242 // adding listeners reentrantly is not supported 243 Preconditions.checkState(!mReentrancyGuard.isReentrant()); 244 245 // new key may only have a prior registration if the oldKey is the same as the key 246 Preconditions.checkArgument(oldKey == key || !mRegistrations.containsKey(key)); 247 248 // since adding a registration can invoke a variety of callbacks, we need to ensure 249 // those callbacks themselves do not re-enter, as this could lead to out-of-order 250 // callbacks. further, we buffer service updates since adding a registration may 251 // involve removing a prior registration. note that try-with-resources ordering is 252 // meaningful here as well. we want to close the reentrancy guard first, as this may 253 // generate additional service updates, then close the update service buffer. 254 try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire(); 255 ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) { 256 257 boolean wasEmpty = mRegistrations.isEmpty(); 258 259 TRegistration oldRegistration = null; 260 int index = mRegistrations.indexOfKey(oldKey); 261 if (index >= 0) { 262 oldRegistration = removeRegistration(index, oldKey != key); 263 } 264 if (oldKey == key && index >= 0) { 265 mRegistrations.setValueAt(index, registration); 266 } else { 267 mRegistrations.put(key, registration); 268 } 269 270 if (wasEmpty) { 271 onRegister(); 272 } 273 registration.onRegister(key); 274 if (oldRegistration == null) { 275 onRegistrationAdded(key, registration); 276 } else { 277 onRegistrationReplaced(key, oldRegistration, registration); 278 } 279 onRegistrationActiveChanged(registration); 280 } 281 } 282 } 283 284 /** 285 * Removes the registration with the given key. This method cannot be called to remove a 286 * registration re-entrantly. 287 */ removeRegistration(@onNull Object key)288 protected final void removeRegistration(@NonNull Object key) { 289 synchronized (mRegistrations) { 290 // this method does not support removing listeners reentrantly 291 Preconditions.checkState(!mReentrancyGuard.isReentrant()); 292 293 int index = mRegistrations.indexOfKey(key); 294 if (index < 0) { 295 return; 296 } 297 298 removeRegistration(index, true); 299 } 300 } 301 302 /** 303 * Removes all registrations with keys that satisfy the given predicate. This method cannot be 304 * called to remove a registration re-entrantly. 305 */ removeRegistrationIf(@onNull Predicate<TKey> predicate)306 protected final void removeRegistrationIf(@NonNull Predicate<TKey> predicate) { 307 synchronized (mRegistrations) { 308 // this method does not support removing listeners reentrantly 309 Preconditions.checkState(!mReentrancyGuard.isReentrant()); 310 311 // since removing a registration can invoke a variety of callbacks, we need to ensure 312 // those callbacks themselves do not re-enter, as this could lead to out-of-order 313 // callbacks. further, we buffer service updates since chains of removeLater() 314 // invocations could result in multiple service updates. note that try-with-resources 315 // ordering is meaningful here as well. we want to close the reentrancy guard first, as 316 // this may generate additional service updates, then close the update service buffer. 317 try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire(); 318 ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) { 319 320 final int size = mRegistrations.size(); 321 for (int i = 0; i < size; i++) { 322 TKey key = mRegistrations.keyAt(i); 323 if (predicate.test(key)) { 324 removeRegistration(key, mRegistrations.valueAt(i)); 325 } 326 } 327 } 328 } 329 } 330 331 /** 332 * Removes the given registration with the given key. If the given key has a different 333 * registration at the time this method is called, nothing happens. This method allows for 334 * re-entrancy, and may be called to remove a registration re-entrantly. 335 */ removeRegistration(@onNull Object key, @NonNull ListenerRegistration<?> registration)336 protected final void removeRegistration(@NonNull Object key, 337 @NonNull ListenerRegistration<?> registration) { 338 synchronized (mRegistrations) { 339 int index = mRegistrations.indexOfKey(key); 340 if (index < 0) { 341 return; 342 } 343 344 TRegistration typedRegistration = mRegistrations.valueAt(index); 345 if (typedRegistration != registration) { 346 return; 347 } 348 349 if (mReentrancyGuard.isReentrant()) { 350 unregister(typedRegistration); 351 mReentrancyGuard.markForRemoval(key, typedRegistration); 352 } else { 353 removeRegistration(index, true); 354 } 355 } 356 } 357 358 @GuardedBy("mRegistrations") removeRegistration(int index, boolean removeEntry)359 private TRegistration removeRegistration(int index, boolean removeEntry) { 360 if (Build.IS_DEBUGGABLE) { 361 Preconditions.checkState(Thread.holdsLock(mRegistrations)); 362 } 363 364 TKey key = mRegistrations.keyAt(index); 365 TRegistration registration = mRegistrations.valueAt(index); 366 367 // since removing a registration can invoke a variety of callbacks, we need to ensure those 368 // callbacks themselves do not re-enter, as this could lead to out-of-order callbacks. 369 // further, we buffer service updates since chains of removeLater() invocations could result 370 // in multiple service updates. note that try-with-resources ordering is meaningful here as 371 // well. we want to close the reentrancy guard first, as this may generate additional 372 // service updates, then close the update service buffer. 373 try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire(); 374 ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) { 375 376 unregister(registration); 377 onRegistrationRemoved(key, registration); 378 registration.onUnregister(); 379 if (removeEntry) { 380 mRegistrations.removeAt(index); 381 if (mRegistrations.isEmpty()) { 382 onUnregister(); 383 } 384 } 385 } 386 387 return registration; 388 } 389 390 /** 391 * Forces a re-evalution of the merged request for all active registrations and updates service 392 * registration accordingly. 393 */ updateService()394 protected final void updateService() { 395 synchronized (mRegistrations) { 396 if (mUpdateServiceBuffer.isBuffered()) { 397 mUpdateServiceBuffer.markUpdateServiceRequired(); 398 return; 399 } 400 401 ArrayList<TRegistration> actives = new ArrayList<>(mRegistrations.size()); 402 final int size = mRegistrations.size(); 403 for (int i = 0; i < size; i++) { 404 TRegistration registration = mRegistrations.valueAt(i); 405 if (registration.isActive()) { 406 actives.add(registration); 407 } 408 } 409 410 if (actives.isEmpty()) { 411 if (mServiceRegistered) { 412 mMerged = null; 413 mServiceRegistered = false; 414 unregisterWithService(); 415 } 416 return; 417 } 418 419 TMergedRegistration merged = mergeRegistrations(actives); 420 if (!mServiceRegistered || !Objects.equals(merged, mMerged)) { 421 if (mServiceRegistered) { 422 mServiceRegistered = reregisterWithService(mMerged, merged, actives); 423 } else { 424 mServiceRegistered = registerWithService(merged, actives); 425 } 426 mMerged = mServiceRegistered ? merged : null; 427 } 428 } 429 } 430 431 /** 432 * If the service is currently registered, unregisters it and then calls 433 * {@link #updateService()} so that {@link #registerWithService(Object, Collection)} will be 434 * re-invoked. This is useful, for instance, if the backing service has crashed or otherwise 435 * lost state, and needs to be re-initialized. Because this unregisters first, this is safe to 436 * use even if there is a possibility the backing server has not crashed, or has already been 437 * reinitialized. 438 */ resetService()439 protected final void resetService() { 440 synchronized (mRegistrations) { 441 if (mServiceRegistered) { 442 mMerged = null; 443 mServiceRegistered = false; 444 unregisterWithService(); 445 updateService(); 446 } 447 } 448 } 449 450 /** 451 * Begins buffering calls to {@link #updateService()} until {@link UpdateServiceLock#close()} 452 * is called. This is useful to prevent extra work when combining multiple calls (for example, 453 * buffering {@code updateService()} until after multiple adds/removes/updates occur. 454 */ newUpdateServiceLock()455 public UpdateServiceLock newUpdateServiceLock() { 456 return new UpdateServiceLock(mUpdateServiceBuffer.acquire()); 457 } 458 459 /** 460 * Evaluates the predicate on all registrations. The predicate should return true if the active 461 * state of the registration may have changed as a result. If the active state of any 462 * registration has changed, {@link #updateService()} will automatically be invoked to handle 463 * the resulting changes. 464 */ updateRegistrations(@onNull Predicate<TRegistration> predicate)465 protected final void updateRegistrations(@NonNull Predicate<TRegistration> predicate) { 466 synchronized (mRegistrations) { 467 // since updating a registration can invoke a variety of callbacks, we need to ensure 468 // those callbacks themselves do not re-enter, as this could lead to out-of-order 469 // callbacks. note that try-with-resources ordering is meaningful here as well. we want 470 // to close the reentrancy guard first, as this may generate additional service updates, 471 // then close the update service buffer. 472 try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire(); 473 ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) { 474 475 final int size = mRegistrations.size(); 476 for (int i = 0; i < size; i++) { 477 TRegistration registration = mRegistrations.valueAt(i); 478 if (predicate.test(registration)) { 479 onRegistrationActiveChanged(registration); 480 } 481 } 482 } 483 } 484 } 485 486 /** 487 * Evaluates the predicate on a registration with the given key. The predicate should return 488 * true if the active state of the registration may have changed as a result. If the active 489 * state of the registration has changed, {@link #updateService()} will automatically be invoked 490 * to handle the resulting changes. Returns true if there is a registration with the given key 491 * (and thus the predicate was invoked), and false otherwise. 492 */ updateRegistration(@onNull Object key, @NonNull Predicate<TRegistration> predicate)493 protected final boolean updateRegistration(@NonNull Object key, 494 @NonNull Predicate<TRegistration> predicate) { 495 synchronized (mRegistrations) { 496 // since updating a registration can invoke a variety of callbacks, we need to ensure 497 // those callbacks themselves do not re-enter, as this could lead to out-of-order 498 // callbacks. note that try-with-resources ordering is meaningful here as well. we want 499 // to close the reentrancy guard first, as this may generate additional service updates, 500 // then close the update service buffer. 501 try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire(); 502 ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) { 503 504 int index = mRegistrations.indexOfKey(key); 505 if (index < 0) { 506 return false; 507 } 508 509 TRegistration registration = mRegistrations.valueAt(index); 510 if (predicate.test(registration)) { 511 onRegistrationActiveChanged(registration); 512 } 513 return true; 514 } 515 } 516 } 517 518 @GuardedBy("mRegistrations") onRegistrationActiveChanged(TRegistration registration)519 private void onRegistrationActiveChanged(TRegistration registration) { 520 if (Build.IS_DEBUGGABLE) { 521 Preconditions.checkState(Thread.holdsLock(mRegistrations)); 522 } 523 524 boolean active = registration.isRegistered() && isActive(registration); 525 boolean changed = registration.setActive(active); 526 if (changed) { 527 if (active) { 528 if (++mActiveRegistrationsCount == 1) { 529 onActive(); 530 } 531 registration.onActive(); 532 } else { 533 registration.onInactive(); 534 if (--mActiveRegistrationsCount == 0) { 535 onInactive(); 536 } 537 } 538 539 updateService(); 540 } 541 } 542 543 /** 544 * Executes the given function for all active registrations. If the function returns a non-null 545 * operation, that operation will be invoked with the associated listener. The function may not 546 * change the active state of the registration. 547 */ deliverToListeners( @onNull Function<TRegistration, ListenerOperation<TListener>> function)548 protected final void deliverToListeners( 549 @NonNull Function<TRegistration, ListenerOperation<TListener>> function) { 550 synchronized (mRegistrations) { 551 try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) { 552 final int size = mRegistrations.size(); 553 for (int i = 0; i < size; i++) { 554 TRegistration registration = mRegistrations.valueAt(i); 555 if (registration.isActive()) { 556 ListenerOperation<TListener> operation = function.apply(registration); 557 if (operation != null) { 558 registration.executeOperation(operation); 559 } 560 } 561 } 562 } 563 } 564 } 565 566 /** 567 * Executes the given operation for all active listeners. This is a convenience function 568 * equivalent to: 569 * <pre> 570 * deliverToListeners(registration -> operation); 571 * </pre> 572 */ deliverToListeners(@onNull ListenerOperation<TListener> operation)573 protected final void deliverToListeners(@NonNull ListenerOperation<TListener> operation) { 574 synchronized (mRegistrations) { 575 try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) { 576 final int size = mRegistrations.size(); 577 for (int i = 0; i < size; i++) { 578 TRegistration registration = mRegistrations.valueAt(i); 579 if (registration.isActive()) { 580 registration.executeOperation(operation); 581 } 582 } 583 } 584 } 585 } 586 unregister(TRegistration registration)587 private void unregister(TRegistration registration) { 588 registration.unregisterInternal(); 589 onRegistrationActiveChanged(registration); 590 } 591 592 /** 593 * Dumps debug information. 594 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)595 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 596 synchronized (mRegistrations) { 597 pw.print("service: "); 598 pw.print(getServiceState()); 599 pw.println(); 600 601 if (!mRegistrations.isEmpty()) { 602 pw.println("listeners:"); 603 604 final int size = mRegistrations.size(); 605 for (int i = 0; i < size; i++) { 606 TRegistration registration = mRegistrations.valueAt(i); 607 pw.print(" "); 608 pw.print(registration); 609 if (!registration.isActive()) { 610 pw.println(" (inactive)"); 611 } else { 612 pw.println(); 613 } 614 } 615 } 616 } 617 } 618 619 /** 620 * May be overridden to provide additional details on service state when dumping the manager 621 * state. Invoked while holding the multiplexer's internal lock. 622 */ getServiceState()623 protected String getServiceState() { 624 if (mServiceRegistered) { 625 if (mMerged != null) { 626 return mMerged.toString(); 627 } else { 628 return "registered"; 629 } 630 } else { 631 return "unregistered"; 632 } 633 } 634 635 /** 636 * A reference counted helper class that guards against re-entrancy, and also helps implement 637 * registration removal during reentrancy. When this class is {@link #acquire()}d, it increments 638 * the reference count. To check whether re-entrancy is occurring, clients may use 639 * {@link #isReentrant()}, and modify their behavior (such as by failing the call, or calling 640 * {@link #markForRemoval(Object, ListenerRegistration)}). When this class is {@link #close()}d, 641 * any key/registration pairs that were marked for removal prior to the close operation will 642 * then be removed - which is safe since the operation will no longer be re-entrant. 643 */ 644 private final class ReentrancyGuard implements AutoCloseable { 645 646 @GuardedBy("mRegistrations") 647 private int mGuardCount; 648 @GuardedBy("mRegistrations") 649 private @Nullable ArraySet<Entry<Object, ListenerRegistration<?>>> mScheduledRemovals; 650 ReentrancyGuard()651 ReentrancyGuard() { 652 mGuardCount = 0; 653 mScheduledRemovals = null; 654 } 655 656 @GuardedBy("mRegistrations") isReentrant()657 boolean isReentrant() { 658 if (Build.IS_DEBUGGABLE) { 659 Preconditions.checkState(Thread.holdsLock(mRegistrations)); 660 } 661 return mGuardCount != 0; 662 } 663 664 @GuardedBy("mRegistrations") markForRemoval(Object key, ListenerRegistration<?> registration)665 void markForRemoval(Object key, ListenerRegistration<?> registration) { 666 if (Build.IS_DEBUGGABLE) { 667 Preconditions.checkState(Thread.holdsLock(mRegistrations)); 668 } 669 Preconditions.checkState(isReentrant()); 670 671 if (mScheduledRemovals == null) { 672 mScheduledRemovals = new ArraySet<>(mRegistrations.size()); 673 } 674 mScheduledRemovals.add(new AbstractMap.SimpleImmutableEntry<>(key, registration)); 675 } 676 acquire()677 ReentrancyGuard acquire() { 678 ++mGuardCount; 679 return this; 680 } 681 682 @Override close()683 public void close() { 684 ArraySet<Entry<Object, ListenerRegistration<?>>> scheduledRemovals = null; 685 686 Preconditions.checkState(mGuardCount > 0); 687 if (--mGuardCount == 0) { 688 scheduledRemovals = mScheduledRemovals; 689 mScheduledRemovals = null; 690 } 691 692 if (scheduledRemovals == null) { 693 return; 694 } 695 696 try (UpdateServiceBuffer ignored = mUpdateServiceBuffer.acquire()) { 697 final int size = scheduledRemovals.size(); 698 for (int i = 0; i < size; i++) { 699 Entry<Object, ListenerRegistration<?>> entry = scheduledRemovals.valueAt(i); 700 removeRegistration(entry.getKey(), entry.getValue()); 701 } 702 } 703 } 704 } 705 706 /** 707 * A reference counted helper class that buffers class to {@link #updateService()}. Since 708 * {@link #updateService()} iterates through every registration and performs request merging 709 * work, it can often be the most expensive part of any update to the multiplexer. This means 710 * that if multiple calls to updateService() can be buffered, work will be saved. This class 711 * allows clients to begin buffering calls after {@link #acquire()}ing this class, and when 712 * {@link #close()} is called, any buffered calls to {@link #updateService()} will be combined 713 * into a single final call. Clients should acquire this class when they are doing work that 714 * could potentially result in multiple calls to updateService(), and close when they are done 715 * with that work. 716 */ 717 private final class UpdateServiceBuffer implements AutoCloseable { 718 719 // requires internal locking because close() may be exposed externally and could be called 720 // from any thread 721 722 @GuardedBy("this") 723 private int mBufferCount; 724 @GuardedBy("this") 725 private boolean mUpdateServiceRequired; 726 UpdateServiceBuffer()727 UpdateServiceBuffer() { 728 mBufferCount = 0; 729 mUpdateServiceRequired = false; 730 } 731 isBuffered()732 synchronized boolean isBuffered() { 733 return mBufferCount != 0; 734 } 735 markUpdateServiceRequired()736 synchronized void markUpdateServiceRequired() { 737 Preconditions.checkState(isBuffered()); 738 mUpdateServiceRequired = true; 739 } 740 acquire()741 synchronized UpdateServiceBuffer acquire() { 742 ++mBufferCount; 743 return this; 744 } 745 746 @Override close()747 public void close() { 748 boolean updateServiceRequired = false; 749 synchronized (this) { 750 Preconditions.checkState(mBufferCount > 0); 751 if (--mBufferCount == 0) { 752 updateServiceRequired = mUpdateServiceRequired; 753 mUpdateServiceRequired = false; 754 } 755 } 756 757 if (updateServiceRequired) { 758 updateService(); 759 } 760 } 761 } 762 763 /** 764 * Acquiring this lock will buffer all calls to {@link #updateService()} until the lock is 765 * {@link #close()}ed. This can be used to save work by acquiring the lock before multiple calls 766 * to updateService() are expected, and closing the lock after. 767 */ 768 public final class UpdateServiceLock implements AutoCloseable { 769 770 private @Nullable UpdateServiceBuffer mUpdateServiceBuffer; 771 UpdateServiceLock(UpdateServiceBuffer updateServiceBuffer)772 UpdateServiceLock(UpdateServiceBuffer updateServiceBuffer) { 773 mUpdateServiceBuffer = updateServiceBuffer; 774 } 775 776 @Override close()777 public void close() { 778 if (mUpdateServiceBuffer != null) { 779 UpdateServiceBuffer buffer = mUpdateServiceBuffer; 780 mUpdateServiceBuffer = null; 781 buffer.close(); 782 } 783 } 784 } 785 } 786