1 /* 2 * Copyright (C) 2013 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 android.app; 18 19 import android.accessibilityservice.AccessibilityServiceInfo; 20 import android.accessibilityservice.IAccessibilityServiceClient; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.UserIdInt; 24 import android.companion.virtual.VirtualDeviceManager; 25 import android.compat.annotation.UnsupportedAppUsage; 26 import android.content.Context; 27 import android.graphics.Rect; 28 import android.hardware.input.InputManager; 29 import android.hardware.input.InputManagerGlobal; 30 import android.os.Binder; 31 import android.os.Build; 32 import android.os.IBinder; 33 import android.os.ParcelFileDescriptor; 34 import android.os.Process; 35 import android.os.RemoteException; 36 import android.os.ServiceManager; 37 import android.os.UserHandle; 38 import android.permission.IPermissionManager; 39 import android.util.Log; 40 import android.view.IWindowManager; 41 import android.view.InputDevice; 42 import android.view.InputEvent; 43 import android.view.KeyEvent; 44 import android.view.MotionEvent; 45 import android.view.SurfaceControl; 46 import android.view.WindowAnimationFrameStats; 47 import android.view.WindowContentFrameStats; 48 import android.view.accessibility.AccessibilityEvent; 49 import android.view.accessibility.IAccessibilityManager; 50 import android.window.ScreenCapture; 51 import android.window.ScreenCapture.CaptureArgs; 52 53 import libcore.io.IoUtils; 54 55 import java.io.FileInputStream; 56 import java.io.FileOutputStream; 57 import java.io.IOException; 58 import java.io.InputStream; 59 import java.io.OutputStream; 60 import java.util.List; 61 62 /** 63 * This is a remote object that is passed from the shell to an instrumentation 64 * for enabling access to privileged operations which the shell can do and the 65 * instrumentation cannot. These privileged operations are needed for implementing 66 * a {@link UiAutomation} that enables across application testing by simulating 67 * user actions and performing screen introspection. 68 * 69 * @hide 70 */ 71 public final class UiAutomationConnection extends IUiAutomationConnection.Stub { 72 73 private static final String TAG = "UiAutomationConnection"; 74 75 private static final int INITIAL_FROZEN_ROTATION_UNSPECIFIED = -1; 76 77 private final IWindowManager mWindowManager = IWindowManager.Stub.asInterface( 78 ServiceManager.getService(Service.WINDOW_SERVICE)); 79 80 private final IAccessibilityManager mAccessibilityManager = IAccessibilityManager.Stub 81 .asInterface(ServiceManager.getService(Service.ACCESSIBILITY_SERVICE)); 82 83 private final IPermissionManager mPermissionManager = IPermissionManager.Stub 84 .asInterface(ServiceManager.getService("permissionmgr")); 85 86 private final IActivityManager mActivityManager = IActivityManager.Stub 87 .asInterface(ServiceManager.getService("activity")); 88 89 private final Object mLock = new Object(); 90 91 private final Binder mToken = new Binder(); 92 93 private int mInitialFrozenRotation = INITIAL_FROZEN_ROTATION_UNSPECIFIED; 94 95 private IAccessibilityServiceClient mClient; 96 97 private boolean mIsShutdown; 98 99 private int mOwningUid; 100 101 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) UiAutomationConnection()102 public UiAutomationConnection() { 103 Log.d(TAG, "Created on user " + Process.myUserHandle()); 104 } 105 106 @Override connect(IAccessibilityServiceClient client, int flags)107 public void connect(IAccessibilityServiceClient client, int flags) { 108 if (client == null) { 109 throw new IllegalArgumentException("Client cannot be null!"); 110 } 111 synchronized (mLock) { 112 throwIfShutdownLocked(); 113 if (isConnectedLocked()) { 114 throw new IllegalStateException("Already connected."); 115 } 116 mOwningUid = Binder.getCallingUid(); 117 registerUiTestAutomationServiceLocked(client, 118 Binder.getCallingUserHandle().getIdentifier(), flags); 119 storeRotationStateLocked(); 120 } 121 } 122 123 @Override disconnect()124 public void disconnect() { 125 synchronized (mLock) { 126 throwIfCalledByNotTrustedUidLocked(); 127 throwIfShutdownLocked(); 128 if (!isConnectedLocked()) { 129 throw new IllegalStateException("Already disconnected."); 130 } 131 mOwningUid = -1; 132 unregisterUiTestAutomationServiceLocked(); 133 restoreRotationStateLocked(); 134 } 135 } 136 137 @Override injectInputEvent(InputEvent event, boolean sync, boolean waitForAnimations)138 public boolean injectInputEvent(InputEvent event, boolean sync, boolean waitForAnimations) { 139 synchronized (mLock) { 140 throwIfCalledByNotTrustedUidLocked(); 141 throwIfShutdownLocked(); 142 throwIfNotConnectedLocked(); 143 } 144 145 final boolean syncTransactionsBefore; 146 final boolean syncTransactionsAfter; 147 if (event instanceof KeyEvent) { 148 KeyEvent keyEvent = (KeyEvent) event; 149 syncTransactionsBefore = keyEvent.getAction() == KeyEvent.ACTION_DOWN; 150 syncTransactionsAfter = keyEvent.getAction() == KeyEvent.ACTION_UP; 151 } else { 152 MotionEvent motionEvent = (MotionEvent) event; 153 syncTransactionsBefore = motionEvent.getAction() == MotionEvent.ACTION_DOWN 154 || motionEvent.isFromSource(InputDevice.SOURCE_MOUSE); 155 syncTransactionsAfter = motionEvent.getAction() == MotionEvent.ACTION_UP; 156 } 157 158 final long identity = Binder.clearCallingIdentity(); 159 try { 160 if (syncTransactionsBefore) { 161 mWindowManager.syncInputTransactions(waitForAnimations); 162 } 163 164 final boolean result = InputManagerGlobal.getInstance().injectInputEvent(event, 165 sync ? InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH 166 : InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); 167 168 if (syncTransactionsAfter) { 169 mWindowManager.syncInputTransactions(waitForAnimations); 170 } 171 return result; 172 } catch (RemoteException e) { 173 e.rethrowFromSystemServer(); 174 } finally { 175 Binder.restoreCallingIdentity(identity); 176 } 177 return false; 178 } 179 180 @Override injectInputEventToInputFilter(InputEvent event)181 public void injectInputEventToInputFilter(InputEvent event) throws RemoteException { 182 synchronized (mLock) { 183 throwIfCalledByNotTrustedUidLocked(); 184 throwIfShutdownLocked(); 185 throwIfNotConnectedLocked(); 186 } 187 mAccessibilityManager.injectInputEventToInputFilter(event); 188 } 189 190 @Override syncInputTransactions(boolean waitForAnimations)191 public void syncInputTransactions(boolean waitForAnimations) { 192 synchronized (mLock) { 193 throwIfCalledByNotTrustedUidLocked(); 194 throwIfShutdownLocked(); 195 throwIfNotConnectedLocked(); 196 } 197 198 try { 199 mWindowManager.syncInputTransactions(waitForAnimations); 200 } catch (RemoteException e) { 201 } 202 } 203 204 @Override setRotation(int rotation)205 public boolean setRotation(int rotation) { 206 synchronized (mLock) { 207 throwIfCalledByNotTrustedUidLocked(); 208 throwIfShutdownLocked(); 209 throwIfNotConnectedLocked(); 210 } 211 final long identity = Binder.clearCallingIdentity(); 212 try { 213 if (rotation == UiAutomation.ROTATION_UNFREEZE) { 214 mWindowManager.thawRotation(/* caller= */ "UiAutomationConnection#setRotation"); 215 } else { 216 mWindowManager.freezeRotation(rotation, 217 /* caller= */ "UiAutomationConnection#setRotation"); 218 } 219 return true; 220 } catch (RemoteException re) { 221 /* ignore */ 222 } finally { 223 Binder.restoreCallingIdentity(identity); 224 } 225 return false; 226 } 227 228 @Override takeScreenshot(Rect crop, ScreenCapture.ScreenCaptureListener listener, int displayId)229 public boolean takeScreenshot(Rect crop, ScreenCapture.ScreenCaptureListener listener, 230 int displayId) { 231 synchronized (mLock) { 232 throwIfCalledByNotTrustedUidLocked(); 233 throwIfShutdownLocked(); 234 throwIfNotConnectedLocked(); 235 } 236 237 final long identity = Binder.clearCallingIdentity(); 238 try { 239 final CaptureArgs captureArgs = new CaptureArgs.Builder<>() 240 .setSourceCrop(crop) 241 .build(); 242 mWindowManager.captureDisplay(displayId, captureArgs, listener); 243 } catch (RemoteException re) { 244 re.rethrowAsRuntimeException(); 245 } finally { 246 Binder.restoreCallingIdentity(identity); 247 } 248 249 return true; 250 } 251 252 @Nullable 253 @Override takeSurfaceControlScreenshot(@onNull SurfaceControl surfaceControl, ScreenCapture.ScreenCaptureListener listener)254 public boolean takeSurfaceControlScreenshot(@NonNull SurfaceControl surfaceControl, 255 ScreenCapture.ScreenCaptureListener listener) { 256 synchronized (mLock) { 257 throwIfCalledByNotTrustedUidLocked(); 258 throwIfShutdownLocked(); 259 throwIfNotConnectedLocked(); 260 } 261 262 final long identity = Binder.clearCallingIdentity(); 263 try { 264 ScreenCapture.LayerCaptureArgs args = 265 new ScreenCapture.LayerCaptureArgs.Builder(surfaceControl) 266 .setChildrenOnly(false) 267 .build(); 268 int status = ScreenCapture.captureLayers(args, listener); 269 270 if (status != 0) { 271 return false; 272 } 273 } finally { 274 Binder.restoreCallingIdentity(identity); 275 } 276 277 return true; 278 } 279 280 @Override clearWindowContentFrameStats(int windowId)281 public boolean clearWindowContentFrameStats(int windowId) throws RemoteException { 282 synchronized (mLock) { 283 throwIfCalledByNotTrustedUidLocked(); 284 throwIfShutdownLocked(); 285 throwIfNotConnectedLocked(); 286 } 287 int callingUserId = UserHandle.getCallingUserId(); 288 final long identity = Binder.clearCallingIdentity(); 289 try { 290 IBinder token = mAccessibilityManager.getWindowToken(windowId, callingUserId); 291 if (token == null) { 292 return false; 293 } 294 return mWindowManager.clearWindowContentFrameStats(token); 295 } finally { 296 Binder.restoreCallingIdentity(identity); 297 } 298 } 299 300 @Override getWindowContentFrameStats(int windowId)301 public WindowContentFrameStats getWindowContentFrameStats(int windowId) throws RemoteException { 302 synchronized (mLock) { 303 throwIfCalledByNotTrustedUidLocked(); 304 throwIfShutdownLocked(); 305 throwIfNotConnectedLocked(); 306 } 307 int callingUserId = UserHandle.getCallingUserId(); 308 final long identity = Binder.clearCallingIdentity(); 309 try { 310 IBinder token = mAccessibilityManager.getWindowToken(windowId, callingUserId); 311 if (token == null) { 312 return null; 313 } 314 return mWindowManager.getWindowContentFrameStats(token); 315 } finally { 316 Binder.restoreCallingIdentity(identity); 317 } 318 } 319 320 @Override clearWindowAnimationFrameStats()321 public void clearWindowAnimationFrameStats() { 322 synchronized (mLock) { 323 throwIfCalledByNotTrustedUidLocked(); 324 throwIfShutdownLocked(); 325 throwIfNotConnectedLocked(); 326 } 327 final long identity = Binder.clearCallingIdentity(); 328 try { 329 SurfaceControl.clearAnimationFrameStats(); 330 } finally { 331 Binder.restoreCallingIdentity(identity); 332 } 333 } 334 335 @Override getWindowAnimationFrameStats()336 public WindowAnimationFrameStats getWindowAnimationFrameStats() { 337 synchronized (mLock) { 338 throwIfCalledByNotTrustedUidLocked(); 339 throwIfShutdownLocked(); 340 throwIfNotConnectedLocked(); 341 } 342 final long identity = Binder.clearCallingIdentity(); 343 try { 344 WindowAnimationFrameStats stats = new WindowAnimationFrameStats(); 345 SurfaceControl.getAnimationFrameStats(stats); 346 return stats; 347 } finally { 348 Binder.restoreCallingIdentity(identity); 349 } 350 } 351 352 /** 353 * Grants permission for the {@link Context#DEVICE_ID_DEFAULT default device} 354 */ 355 @Override grantRuntimePermission(String packageName, String permission, int userId)356 public void grantRuntimePermission(String packageName, String permission, int userId) 357 throws RemoteException { 358 synchronized (mLock) { 359 throwIfCalledByNotTrustedUidLocked(); 360 throwIfShutdownLocked(); 361 throwIfNotConnectedLocked(); 362 } 363 final long identity = Binder.clearCallingIdentity(); 364 try { 365 mPermissionManager.grantRuntimePermission(packageName, permission, 366 VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId); 367 } finally { 368 Binder.restoreCallingIdentity(identity); 369 } 370 } 371 372 /** 373 * Revokes permission for the {@link Context#DEVICE_ID_DEFAULT default device} 374 */ 375 @Override revokeRuntimePermission(String packageName, String permission, int userId)376 public void revokeRuntimePermission(String packageName, String permission, int userId) 377 throws RemoteException { 378 synchronized (mLock) { 379 throwIfCalledByNotTrustedUidLocked(); 380 throwIfShutdownLocked(); 381 throwIfNotConnectedLocked(); 382 } 383 final long identity = Binder.clearCallingIdentity(); 384 try { 385 mPermissionManager.revokeRuntimePermission(packageName, permission, 386 VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId, null); 387 } finally { 388 Binder.restoreCallingIdentity(identity); 389 } 390 } 391 392 @Override adoptShellPermissionIdentity(int uid, @Nullable String[] permissions)393 public void adoptShellPermissionIdentity(int uid, @Nullable String[] permissions) 394 throws RemoteException { 395 synchronized (mLock) { 396 throwIfCalledByNotTrustedUidLocked(); 397 throwIfShutdownLocked(); 398 throwIfNotConnectedLocked(); 399 } 400 final long identity = Binder.clearCallingIdentity(); 401 try { 402 mActivityManager.startDelegateShellPermissionIdentity(uid, permissions); 403 } finally { 404 Binder.restoreCallingIdentity(identity); 405 } 406 } 407 408 @Override dropShellPermissionIdentity()409 public void dropShellPermissionIdentity() throws RemoteException { 410 synchronized (mLock) { 411 throwIfCalledByNotTrustedUidLocked(); 412 throwIfShutdownLocked(); 413 throwIfNotConnectedLocked(); 414 } 415 final long identity = Binder.clearCallingIdentity(); 416 try { 417 mActivityManager.stopDelegateShellPermissionIdentity(); 418 } finally { 419 Binder.restoreCallingIdentity(identity); 420 } 421 } 422 423 @Override 424 @Nullable getAdoptedShellPermissions()425 public List<String> getAdoptedShellPermissions() throws RemoteException { 426 synchronized (mLock) { 427 throwIfCalledByNotTrustedUidLocked(); 428 throwIfShutdownLocked(); 429 throwIfNotConnectedLocked(); 430 } 431 final long identity = Binder.clearCallingIdentity(); 432 try { 433 return mActivityManager.getDelegatedShellPermissions(); 434 } finally { 435 Binder.restoreCallingIdentity(identity); 436 } 437 } 438 439 @Override addOverridePermissionState(int uid, String permission, int result)440 public void addOverridePermissionState(int uid, String permission, int result) 441 throws RemoteException { 442 synchronized (mLock) { 443 throwIfCalledByNotTrustedUidLocked(); 444 throwIfShutdownLocked(); 445 throwIfNotConnectedLocked(); 446 } 447 final int callingUid = Binder.getCallingUid(); 448 final long identity = Binder.clearCallingIdentity(); 449 try { 450 mActivityManager.addOverridePermissionState(callingUid, uid, permission, result); 451 } finally { 452 Binder.restoreCallingIdentity(identity); 453 } 454 } 455 456 @Override removeOverridePermissionState(int uid, String permission)457 public void removeOverridePermissionState(int uid, String permission) throws RemoteException { 458 synchronized (mLock) { 459 throwIfCalledByNotTrustedUidLocked(); 460 throwIfShutdownLocked(); 461 throwIfNotConnectedLocked(); 462 } 463 final int callingUid = Binder.getCallingUid(); 464 final long identity = Binder.clearCallingIdentity(); 465 try { 466 mActivityManager.removeOverridePermissionState(callingUid, uid, permission); 467 } finally { 468 Binder.restoreCallingIdentity(identity); 469 } 470 } 471 472 @Override clearOverridePermissionStates(int uid)473 public void clearOverridePermissionStates(int uid) throws RemoteException { 474 synchronized (mLock) { 475 throwIfCalledByNotTrustedUidLocked(); 476 throwIfShutdownLocked(); 477 throwIfNotConnectedLocked(); 478 } 479 final int callingUid = Binder.getCallingUid(); 480 final long identity = Binder.clearCallingIdentity(); 481 try { 482 mActivityManager.clearOverridePermissionStates(callingUid, uid); 483 } finally { 484 Binder.restoreCallingIdentity(identity); 485 } 486 } 487 488 @Override clearAllOverridePermissionStates()489 public void clearAllOverridePermissionStates() throws RemoteException { 490 synchronized (mLock) { 491 throwIfCalledByNotTrustedUidLocked(); 492 throwIfShutdownLocked(); 493 throwIfNotConnectedLocked(); 494 } 495 final int callingUid = Binder.getCallingUid(); 496 final long identity = Binder.clearCallingIdentity(); 497 try { 498 mActivityManager.clearAllOverridePermissionStates(callingUid); 499 } finally { 500 Binder.restoreCallingIdentity(identity); 501 } 502 } 503 504 public class Repeater implements Runnable { 505 // Continuously read readFrom and write back to writeTo until EOF is encountered 506 private final InputStream readFrom; 507 private final OutputStream writeTo; Repeater(InputStream readFrom, OutputStream writeTo)508 public Repeater (InputStream readFrom, OutputStream writeTo) { 509 this.readFrom = readFrom; 510 this.writeTo = writeTo; 511 } 512 @Override run()513 public void run() { 514 try { 515 final byte[] buffer = new byte[8192]; 516 int readByteCount; 517 while (true) { 518 readByteCount = readFrom.read(buffer); 519 if (readByteCount < 0) { 520 break; 521 } 522 writeTo.write(buffer, 0, readByteCount); 523 writeTo.flush(); 524 } 525 } catch (IOException ignored) { 526 } finally { 527 IoUtils.closeQuietly(readFrom); 528 IoUtils.closeQuietly(writeTo); 529 } 530 } 531 } 532 533 @Override executeShellCommand(final String command, final ParcelFileDescriptor sink, final ParcelFileDescriptor source)534 public void executeShellCommand(final String command, final ParcelFileDescriptor sink, 535 final ParcelFileDescriptor source) throws RemoteException { 536 executeShellCommandWithStderr(command, sink, source, null /* stderrSink */); 537 } 538 539 @Override executeShellCommandWithStderr(final String command, final ParcelFileDescriptor sink, final ParcelFileDescriptor source, final ParcelFileDescriptor stderrSink)540 public void executeShellCommandWithStderr(final String command, final ParcelFileDescriptor sink, 541 final ParcelFileDescriptor source, final ParcelFileDescriptor stderrSink) 542 throws RemoteException { 543 synchronized (mLock) { 544 throwIfCalledByNotTrustedUidLocked(); 545 throwIfShutdownLocked(); 546 throwIfNotConnectedLocked(); 547 } 548 final java.lang.Process process; 549 550 try { 551 process = Runtime.getRuntime().exec(command); 552 } catch (IOException ex) { 553 // Make sure the passed FDs are closed. 554 IoUtils.closeQuietly(sink); 555 IoUtils.closeQuietly(source); 556 IoUtils.closeQuietly(stderrSink); 557 // No to need to wrap in RuntimeException. Only to keep the old behavior. 558 // This is just logged and not propagated to the remote caller anyway. 559 throw new RuntimeException("Error running shell command '" + command + "'", ex); 560 } catch (IllegalArgumentException | NullPointerException | SecurityException ex) { 561 // Make sure the passed FDs are closed. 562 IoUtils.closeQuietly(sink); 563 IoUtils.closeQuietly(source); 564 IoUtils.closeQuietly(stderrSink); 565 // Rethrow the exception. This will be propagated to the remote caller. 566 throw ex; 567 } 568 handleExecuteShellCommandProcess(process, sink, source, stderrSink); 569 } 570 handleExecuteShellCommandProcess(final java.lang.Process process, final ParcelFileDescriptor sink, final ParcelFileDescriptor source, final ParcelFileDescriptor stderrSink)571 private void handleExecuteShellCommandProcess(final java.lang.Process process, 572 final ParcelFileDescriptor sink, final ParcelFileDescriptor source, 573 final ParcelFileDescriptor stderrSink) { 574 // Read from process and write to pipe 575 final Thread readFromProcess; 576 if (sink != null) { 577 InputStream sink_in = process.getInputStream();; 578 OutputStream sink_out = new FileOutputStream(sink.getFileDescriptor()); 579 580 readFromProcess = new Thread(new Repeater(sink_in, sink_out)); 581 readFromProcess.start(); 582 } else { 583 readFromProcess = null; 584 } 585 586 // Read from pipe and write to process 587 final Thread writeToProcess; 588 if (source != null) { 589 OutputStream source_out = process.getOutputStream(); 590 InputStream source_in = new FileInputStream(source.getFileDescriptor()); 591 592 writeToProcess = new Thread(new Repeater(source_in, source_out)); 593 writeToProcess.start(); 594 } else { 595 writeToProcess = null; 596 } 597 598 // Read from process stderr and write to pipe 599 final Thread readStderrFromProcess; 600 if (stderrSink != null) { 601 InputStream sink_in = process.getErrorStream(); 602 OutputStream sink_out = new FileOutputStream(stderrSink.getFileDescriptor()); 603 604 readStderrFromProcess = new Thread(new Repeater(sink_in, sink_out)); 605 readStderrFromProcess.start(); 606 } else { 607 readStderrFromProcess = null; 608 } 609 610 Thread cleanup = new Thread(new Runnable() { 611 @Override 612 public void run() { 613 try { 614 if (writeToProcess != null) { 615 writeToProcess.join(); 616 } 617 if (readFromProcess != null) { 618 readFromProcess.join(); 619 } 620 if (readStderrFromProcess != null) { 621 readStderrFromProcess.join(); 622 } 623 } catch (InterruptedException exc) { 624 Log.e(TAG, "At least one of the threads was interrupted"); 625 } 626 IoUtils.closeQuietly(sink); 627 IoUtils.closeQuietly(source); 628 IoUtils.closeQuietly(stderrSink); 629 process.destroy(); 630 } 631 }); 632 cleanup.start(); 633 } 634 635 @Override executeShellCommandArrayWithStderr(final String[] command, final ParcelFileDescriptor sink, final ParcelFileDescriptor source, final ParcelFileDescriptor stderrSink)636 public void executeShellCommandArrayWithStderr(final String[] command, 637 final ParcelFileDescriptor sink, final ParcelFileDescriptor source, 638 final ParcelFileDescriptor stderrSink) throws RemoteException { 639 synchronized (mLock) { 640 throwIfCalledByNotTrustedUidLocked(); 641 throwIfShutdownLocked(); 642 throwIfNotConnectedLocked(); 643 } 644 final java.lang.Process process; 645 646 try { 647 process = Runtime.getRuntime().exec(command); 648 } catch (IOException exc) { 649 throw new RuntimeException( 650 "Error running shell command '" + String.join(" ", command) + "'", exc); 651 } 652 handleExecuteShellCommandProcess(process, sink, source, stderrSink); 653 } 654 655 @Override shutdown()656 public void shutdown() { 657 synchronized (mLock) { 658 if (isConnectedLocked()) { 659 throwIfCalledByNotTrustedUidLocked(); 660 } 661 throwIfShutdownLocked(); 662 mIsShutdown = true; 663 if (isConnectedLocked()) { 664 disconnect(); 665 } 666 } 667 } 668 registerUiTestAutomationServiceLocked(IAccessibilityServiceClient client, @UserIdInt int userId, int flags)669 private void registerUiTestAutomationServiceLocked(IAccessibilityServiceClient client, 670 @UserIdInt int userId, int flags) { 671 IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface( 672 ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)); 673 final AccessibilityServiceInfo info = new AccessibilityServiceInfo(); 674 info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK; 675 info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC; 676 info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS 677 | AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS 678 | AccessibilityServiceInfo.FLAG_FORCE_DIRECT_BOOT_AWARE; 679 info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT 680 | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION 681 | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS); 682 if ((flags & UiAutomation.FLAG_NOT_ACCESSIBILITY_TOOL) == 0) { 683 info.setAccessibilityTool(true); 684 } 685 try { 686 // Calling out with a lock held is fine since if the system 687 // process is gone the client calling in will be killed. 688 manager.registerUiTestAutomationService(mToken, client, info, userId, flags); 689 mClient = client; 690 } catch (RemoteException re) { 691 throw new IllegalStateException("Error while registering UiTestAutomationService for " 692 + "user " + userId + ".", re); 693 } 694 } 695 unregisterUiTestAutomationServiceLocked()696 private void unregisterUiTestAutomationServiceLocked() { 697 IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface( 698 ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)); 699 try { 700 // Calling out with a lock held is fine since if the system 701 // process is gone the client calling in will be killed. 702 manager.unregisterUiTestAutomationService(mClient); 703 mClient = null; 704 } catch (RemoteException re) { 705 throw new IllegalStateException("Error while unregistering UiTestAutomationService", 706 re); 707 } 708 } 709 storeRotationStateLocked()710 private void storeRotationStateLocked() { 711 try { 712 if (mWindowManager.isRotationFrozen()) { 713 // Calling out with a lock held is fine since if the system 714 // process is gone the client calling in will be killed. 715 mInitialFrozenRotation = mWindowManager.getDefaultDisplayRotation(); 716 } 717 } catch (RemoteException re) { 718 /* ignore */ 719 } 720 } 721 restoreRotationStateLocked()722 private void restoreRotationStateLocked() { 723 try { 724 if (mInitialFrozenRotation != INITIAL_FROZEN_ROTATION_UNSPECIFIED) { 725 // Calling out with a lock held is fine since if the system 726 // process is gone the client calling in will be killed. 727 mWindowManager.freezeRotation(mInitialFrozenRotation, 728 /* caller= */ "UiAutomationConnection#restoreRotationStateLocked"); 729 } else { 730 // Calling out with a lock held is fine since if the system 731 // process is gone the client calling in will be killed. 732 mWindowManager.thawRotation( 733 /* caller= */ "UiAutomationConnection#restoreRotationStateLocked"); 734 } 735 } catch (RemoteException re) { 736 /* ignore */ 737 } 738 } 739 isConnectedLocked()740 private boolean isConnectedLocked() { 741 return mClient != null; 742 } 743 throwIfShutdownLocked()744 private void throwIfShutdownLocked() { 745 if (mIsShutdown) { 746 throw new IllegalStateException("Connection shutdown!"); 747 } 748 } 749 throwIfNotConnectedLocked()750 private void throwIfNotConnectedLocked() { 751 if (!isConnectedLocked()) { 752 throw new IllegalStateException("Not connected!"); 753 } 754 } 755 throwIfCalledByNotTrustedUidLocked()756 private void throwIfCalledByNotTrustedUidLocked() { 757 final int callingUid = Binder.getCallingUid(); 758 if (callingUid != mOwningUid && mOwningUid != Process.SYSTEM_UID 759 && callingUid != 0 /*root*/) { 760 throw new SecurityException("Calling from not trusted UID!"); 761 } 762 } 763 } 764