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.car; 18 19 import static java.util.Map.entry; 20 21 import android.annotation.NonNull; 22 import android.car.input.CarInputManager; 23 import android.car.input.ICarInputCallback; 24 import android.car.input.RotaryEvent; 25 import android.content.Context; 26 import android.os.Binder; 27 import android.os.IBinder; 28 import android.os.RemoteException; 29 import android.util.ArrayMap; 30 import android.util.Log; 31 import android.util.SparseArray; 32 import android.view.KeyEvent; 33 34 import com.android.internal.annotations.GuardedBy; 35 import com.android.internal.util.ArrayUtils; 36 import com.android.internal.util.Preconditions; 37 38 39 import java.io.PrintWriter; 40 import java.util.ArrayList; 41 import java.util.Arrays; 42 import java.util.HashMap; 43 import java.util.LinkedList; 44 import java.util.List; 45 import java.util.Map; 46 import java.util.Objects; 47 import java.util.Set; 48 49 /** 50 * Manages input capture request from clients 51 */ 52 public class InputCaptureClientController { 53 private static final boolean DBG_STACK = false; 54 private static final boolean DBG_DISPATCH = false; 55 private static final boolean DBG_CALLS = false; 56 57 private static final String TAG = CarLog.TAG_INPUT; 58 /** 59 * This table decides which input key goes into which input type. Not mapped here means it is 60 * not supported for capturing. Rotary events are treated separately and this is only for 61 * key events. 62 */ 63 private static final Map<Integer, Integer> KEY_EVENT_TO_INPUT_TYPE = Map.ofEntries( 64 entry(KeyEvent.KEYCODE_DPAD_CENTER, CarInputManager.INPUT_TYPE_DPAD_KEYS), 65 entry(KeyEvent.KEYCODE_DPAD_DOWN, CarInputManager.INPUT_TYPE_DPAD_KEYS), 66 entry(KeyEvent.KEYCODE_DPAD_UP, CarInputManager.INPUT_TYPE_DPAD_KEYS), 67 entry(KeyEvent.KEYCODE_DPAD_LEFT, CarInputManager.INPUT_TYPE_DPAD_KEYS), 68 entry(KeyEvent.KEYCODE_DPAD_RIGHT, CarInputManager.INPUT_TYPE_DPAD_KEYS), 69 entry(KeyEvent.KEYCODE_DPAD_DOWN_LEFT, CarInputManager.INPUT_TYPE_DPAD_KEYS), 70 entry(KeyEvent.KEYCODE_DPAD_DOWN_RIGHT, CarInputManager.INPUT_TYPE_DPAD_KEYS), 71 entry(KeyEvent.KEYCODE_DPAD_UP_LEFT, CarInputManager.INPUT_TYPE_DPAD_KEYS), 72 entry(KeyEvent.KEYCODE_DPAD_UP_RIGHT, CarInputManager.INPUT_TYPE_DPAD_KEYS), 73 entry(KeyEvent.KEYCODE_NAVIGATE_IN, CarInputManager.INPUT_TYPE_NAVIGATE_KEYS), 74 entry(KeyEvent.KEYCODE_NAVIGATE_OUT, CarInputManager.INPUT_TYPE_NAVIGATE_KEYS), 75 entry(KeyEvent.KEYCODE_NAVIGATE_NEXT, CarInputManager.INPUT_TYPE_NAVIGATE_KEYS), 76 entry(KeyEvent.KEYCODE_NAVIGATE_PREVIOUS, CarInputManager.INPUT_TYPE_NAVIGATE_KEYS), 77 entry(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN, 78 CarInputManager.INPUT_TYPE_SYSTEM_NAVIGATE_KEYS), 79 entry(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP, 80 CarInputManager.INPUT_TYPE_SYSTEM_NAVIGATE_KEYS), 81 entry(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT, 82 CarInputManager.INPUT_TYPE_SYSTEM_NAVIGATE_KEYS), 83 entry(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT, 84 CarInputManager.INPUT_TYPE_SYSTEM_NAVIGATE_KEYS) 85 ); 86 87 private static final Set<Integer> VALID_INPUT_TYPES = Set.of( 88 CarInputManager.INPUT_TYPE_ALL_INPUTS, 89 CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION, 90 CarInputManager.INPUT_TYPE_DPAD_KEYS, 91 CarInputManager.INPUT_TYPE_NAVIGATE_KEYS, 92 CarInputManager.INPUT_TYPE_SYSTEM_NAVIGATE_KEYS 93 ); 94 95 private static final Set<Integer> VALID_ROTARY_TYPES = Set.of( 96 CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION 97 ); 98 99 // TODO(b/150818155) Need to migrate cluster code to use this to enable it. 100 private static final List<Integer> SUPPORTED_DISPLAY_TYPES = List.of( 101 CarInputManager.TARGET_DISPLAY_TYPE_MAIN 102 ); 103 104 private static final int[] EMPTY_INPUT_TYPES = new int[0]; 105 106 private final class ClientInfoForDisplay implements IBinder.DeathRecipient { 107 private final int mUid; 108 private final int mPid; 109 private final ICarInputCallback mCallback; 110 private final int mTargetDisplayType; 111 private final int[] mInputTypes; 112 private final int mFlags; 113 private final ArrayList<Integer> mGrantedTypes; 114 ClientInfoForDisplay(int uid, int pid, @NonNull ICarInputCallback callback, int targetDisplayType, int[] inputTypes, int flags)115 private ClientInfoForDisplay(int uid, int pid, @NonNull ICarInputCallback callback, 116 int targetDisplayType, int[] inputTypes, int flags) { 117 mUid = uid; 118 mPid = pid; 119 mCallback = callback; 120 mTargetDisplayType = targetDisplayType; 121 mInputTypes = inputTypes; 122 mFlags = flags; 123 mGrantedTypes = new ArrayList<>(inputTypes.length); 124 } 125 linkToDeath()126 private void linkToDeath() throws RemoteException { 127 mCallback.asBinder().linkToDeath(this, 0); 128 } 129 unlinkToDeath()130 private void unlinkToDeath() { 131 mCallback.asBinder().unlinkToDeath(this, 0); 132 } 133 134 @Override binderDied()135 public void binderDied() { 136 onClientDeath(this); 137 } 138 139 @Override toString()140 public String toString() { 141 return new StringBuilder(128) 142 .append("Client{") 143 .append("uid:") 144 .append(mUid) 145 .append(",pid:") 146 .append(mPid) 147 .append(",callback:") 148 .append(mCallback) 149 .append(",inputTypes:") 150 .append(mInputTypes) 151 .append(",flags:") 152 .append(Integer.toHexString(mFlags)) 153 .append(",grantedTypes:") 154 .append(mGrantedTypes) 155 .append("}") 156 .toString(); 157 } 158 } 159 160 private static final class ClientsToDispatch { 161 // The same client can be added multiple times. Keeping only the last addition is ok. 162 private final ArrayMap<ICarInputCallback, int[]> mClientsToDispatch = 163 new ArrayMap<>(); 164 private final int mDisplayType; 165 ClientsToDispatch(int displayType)166 private ClientsToDispatch(int displayType) { 167 mDisplayType = displayType; 168 } 169 add(ClientInfoForDisplay client)170 private void add(ClientInfoForDisplay client) { 171 int[] inputTypesToDispatch; 172 if (client.mGrantedTypes.isEmpty()) { 173 inputTypesToDispatch = EMPTY_INPUT_TYPES; 174 } else { 175 inputTypesToDispatch = ArrayUtils.convertToIntArray(client.mGrantedTypes); 176 } 177 mClientsToDispatch.put(client.mCallback, inputTypesToDispatch); 178 } 179 } 180 181 private final Context mContext; 182 183 private final Object mLock = new Object(); 184 185 /** 186 * key: display type, for quick discovery of client 187 * LinkedList is for implementing stack. First entry is the top. 188 */ 189 @GuardedBy("mLock") 190 private final SparseArray<LinkedList<ClientInfoForDisplay>> mFullDisplayEventCapturers = 191 new SparseArray<>(2); 192 193 /** 194 * key: display type -> inputType, for quick discovery of client 195 * LinkedList is for implementing stack. First entry is the top. 196 */ 197 @GuardedBy("mLock") 198 private final SparseArray<SparseArray<LinkedList<ClientInfoForDisplay>>> 199 mPerInputTypeCapturers = new SparseArray<>(2); 200 201 @GuardedBy("mLock") 202 /** key: display type -> client binder */ 203 private final SparseArray<HashMap<IBinder, ClientInfoForDisplay>> mAllClients = 204 new SparseArray<>(1); 205 206 @GuardedBy("mLock") 207 /** Keeps events to dispatch together. FIFO, last one added to last */ 208 private final LinkedList<ClientsToDispatch> mClientDispatchQueue = 209 new LinkedList<>(); 210 211 /** Accessed from dispatch thread only */ 212 private final ArrayList<KeyEvent> mKeyEventDispatchScratchList = new ArrayList<>(1); 213 214 /** Accessed from dispatch thread only */ 215 private final ArrayList<RotaryEvent> mRotaryEventDispatchScratchList = new ArrayList<>(1); 216 217 @GuardedBy("mLock") 218 private int mNumKeyEventsDispatched; 219 @GuardedBy("mLock") 220 private int mNumRotaryEventsDispatched; 221 InputCaptureClientController(Context context)222 public InputCaptureClientController(Context context) { 223 mContext = context; 224 } 225 226 /** 227 * See 228 * {@link CarInputManager#requestInputEventCapture(CarInputManager.CarInputCaptureCallback, 229 * int, int[], int)}. 230 */ requestInputEventCapture(ICarInputCallback callback, int targetDisplayType, int[] inputTypes, int requestFlags)231 public int requestInputEventCapture(ICarInputCallback callback, int targetDisplayType, 232 int[] inputTypes, int requestFlags) { 233 ICarImpl.assertPermission(mContext, android.Manifest.permission.MONITOR_INPUT); 234 235 Preconditions.checkArgument(SUPPORTED_DISPLAY_TYPES.contains(targetDisplayType), 236 "Display not supported yet:" + targetDisplayType); 237 238 boolean isRequestingAllEvents = 239 (requestFlags & CarInputManager.CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY) != 0; 240 if (isRequestingAllEvents) { 241 ICarImpl.assertCallingFromSystemProcessOrSelf(); 242 if (inputTypes.length != 1 || inputTypes[0] != CarInputManager.INPUT_TYPE_ALL_INPUTS) { 243 throw new IllegalArgumentException("Input type should be INPUT_TYPE_ALL_INPUTS" 244 + " for CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY"); 245 } 246 } 247 if (targetDisplayType != CarInputManager.TARGET_DISPLAY_TYPE_CLUSTER 248 && targetDisplayType != CarInputManager.TARGET_DISPLAY_TYPE_MAIN) { 249 throw new IllegalArgumentException("Unrecognized display type:" + targetDisplayType); 250 } 251 if (inputTypes == null) { 252 throw new IllegalArgumentException("inputTypes cannot be null"); 253 } 254 assertInputTypeValid(inputTypes); 255 Arrays.sort(inputTypes); 256 IBinder clientBinder = callback.asBinder(); 257 boolean allowsDelayedGrant = 258 (requestFlags & CarInputManager.CAPTURE_REQ_FLAGS_ALLOW_DELAYED_GRANT) != 0; 259 int ret = CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED; 260 if (DBG_CALLS) { 261 Log.i(TAG, 262 "requestInputEventCapture callback:" + callback 263 + ", display:" + targetDisplayType 264 + ", inputTypes:" + Arrays.toString(inputTypes) 265 + ", flags:" + requestFlags); 266 } 267 ClientsToDispatch clientsToDispatch = new ClientsToDispatch(targetDisplayType); 268 synchronized (mLock) { 269 HashMap<IBinder, ClientInfoForDisplay> allClientsForDisplay = mAllClients.get( 270 targetDisplayType); 271 if (allClientsForDisplay == null) { 272 allClientsForDisplay = new HashMap<IBinder, ClientInfoForDisplay>(); 273 mAllClients.put(targetDisplayType, allClientsForDisplay); 274 } 275 ClientInfoForDisplay oldClientInfo = allClientsForDisplay.remove(clientBinder); 276 277 LinkedList<ClientInfoForDisplay> fullCapturersStack = mFullDisplayEventCapturers.get( 278 targetDisplayType); 279 if (fullCapturersStack == null) { 280 fullCapturersStack = new LinkedList<ClientInfoForDisplay>(); 281 mFullDisplayEventCapturers.put(targetDisplayType, fullCapturersStack); 282 } 283 284 if (!isRequestingAllEvents && fullCapturersStack.size() > 0 285 && fullCapturersStack.getFirst() != oldClientInfo && !allowsDelayedGrant) { 286 // full capturing active. return failed if not delayed granting. 287 return CarInputManager.INPUT_CAPTURE_RESPONSE_FAILED; 288 } 289 // Now we need to register client anyway, so do death monitoring from here. 290 ClientInfoForDisplay newClient = new ClientInfoForDisplay(Binder.getCallingUid(), 291 Binder.getCallingPid(), callback, targetDisplayType, 292 inputTypes, requestFlags); 293 try { 294 newClient.linkToDeath(); 295 } catch (RemoteException e) { 296 // client died 297 Log.i(TAG, "requestInputEventCapture, cannot linkToDeath to client, pid:" 298 + Binder.getCallingUid()); 299 return CarInputManager.INPUT_CAPTURE_RESPONSE_FAILED; 300 } 301 302 SparseArray<LinkedList<ClientInfoForDisplay>> perInputStacks = 303 mPerInputTypeCapturers.get(targetDisplayType); 304 if (perInputStacks == null) { 305 perInputStacks = new SparseArray<LinkedList<ClientInfoForDisplay>>(); 306 mPerInputTypeCapturers.put(targetDisplayType, perInputStacks); 307 } 308 309 if (isRequestingAllEvents) { 310 if (!fullCapturersStack.isEmpty()) { 311 ClientInfoForDisplay oldCapturer = fullCapturersStack.getFirst(); 312 if (oldCapturer != oldClientInfo) { 313 oldCapturer.mGrantedTypes.clear(); 314 clientsToDispatch.add(oldCapturer); 315 } 316 fullCapturersStack.remove(oldClientInfo); 317 } else { // All per input type top stack client should be notified. 318 for (int i = 0; i < perInputStacks.size(); i++) { 319 LinkedList<ClientInfoForDisplay> perTypeStack = perInputStacks.valueAt(i); 320 if (!perTypeStack.isEmpty()) { 321 ClientInfoForDisplay topClient = perTypeStack.getFirst(); 322 if (topClient != oldClientInfo) { 323 topClient.mGrantedTypes.clear(); 324 clientsToDispatch.add(topClient); 325 } 326 // Even if the client was on top, the one in back does not need 327 // update. 328 perTypeStack.remove(oldClientInfo); 329 } 330 } 331 } 332 fullCapturersStack.addFirst(newClient); 333 334 } else { 335 boolean hadFullCapture = false; 336 boolean fullCaptureActive = false; 337 if (fullCapturersStack.size() > 0) { 338 if (fullCapturersStack.getFirst() == oldClientInfo) { 339 fullCapturersStack.remove(oldClientInfo); 340 // Now we need to check if there is other client in fullCapturersStack 341 if (fullCapturersStack.size() > 0) { 342 fullCaptureActive = true; 343 ret = CarInputManager.INPUT_CAPTURE_RESPONSE_DELAYED; 344 ClientInfoForDisplay topClient = fullCapturersStack.getFirst(); 345 topClient.mGrantedTypes.clear(); 346 topClient.mGrantedTypes.add(CarInputManager.INPUT_TYPE_ALL_INPUTS); 347 clientsToDispatch.add(topClient); 348 } else { 349 hadFullCapture = true; 350 } 351 } else { 352 // other client doing full capturing and it should have DELAYED_GRANT flag. 353 fullCaptureActive = true; 354 ret = CarInputManager.INPUT_CAPTURE_RESPONSE_DELAYED; 355 } 356 } 357 for (int i = 0; i < perInputStacks.size(); i++) { 358 LinkedList<ClientInfoForDisplay> perInputStack = perInputStacks.valueAt(i); 359 perInputStack.remove(oldClientInfo); 360 } 361 // Now go through per input stack 362 for (int inputType : inputTypes) { 363 LinkedList<ClientInfoForDisplay> perInputStack = perInputStacks.get( 364 inputType); 365 if (perInputStack == null) { 366 perInputStack = new LinkedList<ClientInfoForDisplay>(); 367 perInputStacks.put(inputType, perInputStack); 368 } 369 if (perInputStack.size() > 0) { 370 ClientInfoForDisplay oldTopClient = perInputStack.getFirst(); 371 if (oldTopClient.mGrantedTypes.remove(Integer.valueOf(inputType))) { 372 clientsToDispatch.add(oldTopClient); 373 } 374 } 375 if (!fullCaptureActive) { 376 newClient.mGrantedTypes.add(inputType); 377 } 378 perInputStack.addFirst(newClient); 379 } 380 if (!fullCaptureActive && hadFullCapture) { 381 for (int i = 0; i < perInputStacks.size(); i++) { 382 int inputType = perInputStacks.keyAt(i); 383 LinkedList<ClientInfoForDisplay> perInputStack = perInputStacks.valueAt( 384 i); 385 if (perInputStack.size() > 0) { 386 ClientInfoForDisplay topStackClient = perInputStack.getFirst(); 387 if (topStackClient == newClient) { 388 continue; 389 } 390 if (!topStackClient.mGrantedTypes.contains(inputType)) { 391 topStackClient.mGrantedTypes.add(inputType); 392 clientsToDispatch.add(topStackClient); 393 } 394 } 395 } 396 } 397 } 398 allClientsForDisplay.put(clientBinder, newClient); 399 dispatchClientCallbackLocked(clientsToDispatch); 400 } 401 return ret; 402 } 403 404 /** 405 * See {@link CarInputManager#releaseInputEventCapture(int)}. 406 */ releaseInputEventCapture(ICarInputCallback callback, int targetDisplayType)407 public void releaseInputEventCapture(ICarInputCallback callback, int targetDisplayType) { 408 Objects.requireNonNull(callback); 409 Preconditions.checkArgument(SUPPORTED_DISPLAY_TYPES.contains(targetDisplayType), 410 "Display not supported yet:" + targetDisplayType); 411 412 if (DBG_CALLS) { 413 Log.i(TAG, "releaseInputEventCapture callback:" + callback 414 + ", display:" + targetDisplayType); 415 } 416 ClientsToDispatch clientsToDispatch = new ClientsToDispatch(targetDisplayType); 417 synchronized (mLock) { 418 HashMap<IBinder, ClientInfoForDisplay> allClientsForDisplay = mAllClients.get( 419 targetDisplayType); 420 ClientInfoForDisplay clientInfo = allClientsForDisplay.remove(callback.asBinder()); 421 if (clientInfo == null) { 422 Log.w(TAG, "Cannot find client for releaseInputEventCapture:" + callback); 423 return; 424 } 425 clientInfo.unlinkToDeath(); 426 LinkedList<ClientInfoForDisplay> fullCapturersStack = mFullDisplayEventCapturers.get( 427 targetDisplayType); 428 boolean fullCaptureActive = false; 429 if (fullCapturersStack.size() > 0) { 430 if (fullCapturersStack.getFirst() == clientInfo) { 431 fullCapturersStack.remove(clientInfo); 432 if (fullCapturersStack.size() > 0) { 433 ClientInfoForDisplay newStopStackClient = fullCapturersStack.getFirst(); 434 newStopStackClient.mGrantedTypes.clear(); 435 newStopStackClient.mGrantedTypes.add(CarInputManager.INPUT_TYPE_ALL_INPUTS); 436 clientsToDispatch.add(newStopStackClient); 437 fullCaptureActive = true; 438 } 439 } else { // no notification as other client is in top of the stack 440 fullCaptureActive = true; 441 } 442 fullCapturersStack.remove(clientInfo); 443 } 444 SparseArray<LinkedList<ClientInfoForDisplay>> perInputStacks = 445 mPerInputTypeCapturers.get(targetDisplayType); 446 if (DBG_STACK) { 447 Log.i(TAG, "releaseInputEventCapture, fullCaptureActive:" 448 + fullCaptureActive + ", perInputStacks:" + perInputStacks); 449 } 450 if (perInputStacks != null) { 451 for (int i = 0; i < perInputStacks.size(); i++) { 452 int inputType = perInputStacks.keyAt(i); 453 LinkedList<ClientInfoForDisplay> perInputStack = perInputStacks.valueAt(i); 454 if (perInputStack.size() > 0) { 455 if (perInputStack.getFirst() == clientInfo) { 456 perInputStack.removeFirst(); 457 if (perInputStack.size() > 0) { 458 ClientInfoForDisplay newTopClient = perInputStack.getFirst(); 459 if (!fullCaptureActive) { 460 newTopClient.mGrantedTypes.add(inputType); 461 clientsToDispatch.add(newTopClient); 462 } 463 } 464 } else { // something else on top. 465 if (!fullCaptureActive) { 466 ClientInfoForDisplay topClient = perInputStack.getFirst(); 467 if (!topClient.mGrantedTypes.contains(inputType)) { 468 topClient.mGrantedTypes.add(inputType); 469 clientsToDispatch.add(topClient); 470 } 471 } 472 perInputStack.remove(clientInfo); 473 } 474 } 475 } 476 } 477 dispatchClientCallbackLocked(clientsToDispatch); 478 } 479 } 480 481 /** 482 * Dispatches the given {@code KeyEvent} to a capturing client if there is one. 483 * 484 * @param displayType Should be a display type defined in {@code CarInputManager} such as 485 * {@link CarInputManager#TARGET_DISPLAY_TYPE_MAIN}. 486 * @param event 487 * @return true if the event was consumed. 488 */ onKeyEvent(int displayType, KeyEvent event)489 public boolean onKeyEvent(int displayType, KeyEvent event) { 490 if (!SUPPORTED_DISPLAY_TYPES.contains(displayType)) { 491 return false; 492 } 493 Integer inputType = KEY_EVENT_TO_INPUT_TYPE.get(event.getKeyCode()); 494 if (inputType == null) { // not supported key 495 return false; 496 } 497 ICarInputCallback callback; 498 synchronized (mLock) { 499 callback = getClientForInputTypeLocked(displayType, inputType); 500 if (callback == null) { 501 return false; 502 } 503 mNumKeyEventsDispatched++; 504 } 505 506 dispatchKeyEvent(displayType, event, callback); 507 return true; 508 } 509 510 /** 511 * Dispatches the given {@code RotaryEvent} to a capturing client if there is one. 512 * 513 * @param displayType Should be a display type defined in {@code CarInputManager} such as 514 * {@link CarInputManager#TARGET_DISPLAY_TYPE_MAIN}. 515 * @param event 516 * @return true if the event was consumed. 517 */ onRotaryEvent(int displayType, RotaryEvent event)518 public boolean onRotaryEvent(int displayType, RotaryEvent event) { 519 if (!SUPPORTED_DISPLAY_TYPES.contains(displayType)) { 520 Log.w(TAG, "onRotaryEvent for not supported display:" + displayType); 521 return false; 522 } 523 int inputType = event.getInputType(); 524 if (!VALID_ROTARY_TYPES.contains(inputType)) { 525 Log.w(TAG, "onRotaryEvent for not supported input type:" + inputType); 526 return false; 527 } 528 529 ICarInputCallback callback; 530 synchronized (mLock) { 531 callback = getClientForInputTypeLocked(displayType, inputType); 532 if (callback == null) { 533 if (DBG_DISPATCH) { 534 Log.i(TAG, "onRotaryEvent no client for input type:" + inputType); 535 } 536 return false; 537 } 538 mNumRotaryEventsDispatched++; 539 } 540 541 dispatchRotaryEvent(displayType, event, callback); 542 return true; 543 } 544 getClientForInputTypeLocked(int targetDisplayType, int inputType)545 ICarInputCallback getClientForInputTypeLocked(int targetDisplayType, int inputType) { 546 LinkedList<ClientInfoForDisplay> fullCapturersStack = mFullDisplayEventCapturers.get( 547 targetDisplayType); 548 if (fullCapturersStack != null && fullCapturersStack.size() > 0) { 549 return fullCapturersStack.getFirst().mCallback; 550 } 551 552 SparseArray<LinkedList<ClientInfoForDisplay>> perInputStacks = 553 mPerInputTypeCapturers.get(targetDisplayType); 554 if (perInputStacks == null) { 555 return null; 556 } 557 558 LinkedList<ClientInfoForDisplay> perInputStack = perInputStacks.get(inputType); 559 if (perInputStack != null && perInputStack.size() > 0) { 560 return perInputStack.getFirst().mCallback; 561 } 562 563 return null; 564 } 565 onClientDeath(ClientInfoForDisplay client)566 private void onClientDeath(ClientInfoForDisplay client) { 567 releaseInputEventCapture(client.mCallback, client.mTargetDisplayType); 568 } 569 570 /** dump for debugging */ dump(PrintWriter writer)571 public void dump(PrintWriter writer) { 572 writer.println("**InputCaptureClientController**"); 573 synchronized (mLock) { 574 for (int display: SUPPORTED_DISPLAY_TYPES) { 575 writer.println("***Display:" + display); 576 577 HashMap<IBinder, ClientInfoForDisplay> allClientsForDisplay = mAllClients.get( 578 display); 579 if (allClientsForDisplay != null) { 580 writer.println("****All clients:"); 581 for (ClientInfoForDisplay client: allClientsForDisplay.values()) { 582 writer.println(client); 583 } 584 } 585 586 LinkedList<ClientInfoForDisplay> fullCapturersStack = 587 mFullDisplayEventCapturers.get(display); 588 if (fullCapturersStack != null) { 589 writer.println("****Full capture stack"); 590 for (ClientInfoForDisplay client: fullCapturersStack) { 591 writer.println(client); 592 } 593 } 594 SparseArray<LinkedList<ClientInfoForDisplay>> perInputStacks = 595 mPerInputTypeCapturers.get(display); 596 if (perInputStacks != null) { 597 for (int i = 0; i < perInputStacks.size(); i++) { 598 int inputType = perInputStacks.keyAt(i); 599 LinkedList<ClientInfoForDisplay> perInputStack = perInputStacks.valueAt(i); 600 if (perInputStack.size() > 0) { 601 writer.println("**** Per Input stack, input type:" + inputType); 602 for (ClientInfoForDisplay client: perInputStack) { 603 writer.println(client); 604 } 605 } 606 } 607 } 608 } 609 writer.println("mNumKeyEventsDispatched:" + mNumKeyEventsDispatched 610 + ",mNumRotaryEventsDispatched:" + mNumRotaryEventsDispatched); 611 } 612 } 613 dispatchClientCallbackLocked(ClientsToDispatch clientsToDispatch)614 private void dispatchClientCallbackLocked(ClientsToDispatch clientsToDispatch) { 615 if (clientsToDispatch.mClientsToDispatch.isEmpty()) { 616 return; 617 } 618 if (DBG_DISPATCH) { 619 Log.i(TAG, "dispatchClientCallbackLocked, number of clients:" 620 + clientsToDispatch.mClientsToDispatch.size()); 621 } 622 mClientDispatchQueue.add(clientsToDispatch); 623 CarServiceUtils.runOnMain(() -> { 624 ClientsToDispatch clients; 625 synchronized (mLock) { 626 if (mClientDispatchQueue.isEmpty()) { 627 return; 628 } 629 clients = mClientDispatchQueue.pop(); 630 } 631 632 if (DBG_DISPATCH) { 633 Log.i(TAG, "dispatching to clients, num of clients:" 634 + clients.mClientsToDispatch.size() 635 + ", display:" + clients.mDisplayType); 636 } 637 for (int i = 0; i < clients.mClientsToDispatch.size(); i++) { 638 ICarInputCallback callback = clients.mClientsToDispatch.keyAt(i); 639 int[] inputTypes = clients.mClientsToDispatch.valueAt(i); 640 Arrays.sort(inputTypes); 641 if (DBG_DISPATCH) { 642 Log.i(TAG, "dispatching to client, callback:" 643 + callback + ", inputTypes:" + Arrays.toString(inputTypes)); 644 } 645 try { 646 callback.onCaptureStateChanged(clients.mDisplayType, inputTypes); 647 } catch (RemoteException e) { 648 // Ignore. Let death handler deal with it. 649 } 650 } 651 }); 652 } 653 dispatchKeyEvent(int targetDisplayType, KeyEvent event, ICarInputCallback callback)654 private void dispatchKeyEvent(int targetDisplayType, KeyEvent event, 655 ICarInputCallback callback) { 656 CarServiceUtils.runOnMain(() -> { 657 mKeyEventDispatchScratchList.clear(); 658 mKeyEventDispatchScratchList.add(event); 659 try { 660 callback.onKeyEvents(targetDisplayType, mKeyEventDispatchScratchList); 661 } catch (RemoteException e) { 662 // Ignore. Let death handler deal with it. 663 } 664 }); 665 } 666 dispatchRotaryEvent(int targetDisplayType, RotaryEvent event, ICarInputCallback callback)667 private void dispatchRotaryEvent(int targetDisplayType, RotaryEvent event, 668 ICarInputCallback callback) { 669 if (DBG_DISPATCH) { 670 Log.i(TAG, "dispatchRotaryEvent:" + event); 671 } 672 CarServiceUtils.runOnMain(() -> { 673 mRotaryEventDispatchScratchList.clear(); 674 mRotaryEventDispatchScratchList.add(event); 675 try { 676 callback.onRotaryEvents(targetDisplayType, mRotaryEventDispatchScratchList); 677 } catch (RemoteException e) { 678 // Ignore. Let death handler deal with it. 679 } 680 }); 681 } 682 assertInputTypeValid(int[] inputTypes)683 private static void assertInputTypeValid(int[] inputTypes) { 684 for (int inputType : inputTypes) { 685 if (!VALID_INPUT_TYPES.contains(inputType)) { 686 throw new IllegalArgumentException("Invalid input type:" + inputType 687 + ", inputTypes:" + Arrays.toString(inputTypes)); 688 } 689 } 690 } 691 } 692