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