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 static android.view.Display.DEFAULT_DISPLAY;
20 
21 import android.accessibilityservice.AccessibilityServiceInfo;
22 import android.accessibilityservice.IAccessibilityServiceClient;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.UserIdInt;
26 import android.companion.virtual.VirtualDeviceManager;
27 import android.compat.annotation.UnsupportedAppUsage;
28 import android.content.Context;
29 import android.graphics.Rect;
30 import android.hardware.input.InputManager;
31 import android.hardware.input.InputManagerGlobal;
32 import android.os.Binder;
33 import android.os.Build;
34 import android.os.IBinder;
35 import android.os.ParcelFileDescriptor;
36 import android.os.Process;
37 import android.os.RemoteException;
38 import android.os.ServiceManager;
39 import android.os.UserHandle;
40 import android.permission.IPermissionManager;
41 import android.util.Log;
42 import android.view.IWindowManager;
43 import android.view.InputDevice;
44 import android.view.InputEvent;
45 import android.view.KeyEvent;
46 import android.view.MotionEvent;
47 import android.view.SurfaceControl;
48 import android.view.WindowAnimationFrameStats;
49 import android.view.WindowContentFrameStats;
50 import android.view.accessibility.AccessibilityEvent;
51 import android.view.accessibility.IAccessibilityManager;
52 import android.window.ScreenCapture;
53 import android.window.ScreenCapture.CaptureArgs;
54 
55 import libcore.io.IoUtils;
56 
57 import java.io.FileInputStream;
58 import java.io.FileOutputStream;
59 import java.io.IOException;
60 import java.io.InputStream;
61 import java.io.OutputStream;
62 import 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  */
73 public 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)
UiAutomationConnection()104     public UiAutomationConnection() {
105         Log.d(TAG, "Created on user " + Process.myUserHandle());
106     }
107 
108     @Override
connect(IAccessibilityServiceClient client, int flags)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
disconnect()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
injectInputEvent(InputEvent event, boolean sync, boolean waitForAnimations)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
injectInputEventToInputFilter(InputEvent event)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
syncInputTransactions(boolean waitForAnimations)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
setRotation(int rotation)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
takeScreenshot(Rect crop, ScreenCapture.ScreenCaptureListener listener)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
takeSurfaceControlScreenshot(@onNull SurfaceControl surfaceControl, ScreenCapture.ScreenCaptureListener listener)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
clearWindowContentFrameStats(int windowId)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
getWindowContentFrameStats(int windowId)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
clearWindowAnimationFrameStats()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
getWindowAnimationFrameStats()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
grantRuntimePermission(String packageName, String permission, int userId)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
revokeRuntimePermission(String packageName, String permission, int userId)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
adoptShellPermissionIdentity(int uid, @Nullable String[] permissions)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
dropShellPermissionIdentity()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
getAdoptedShellPermissions()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
addOverridePermissionState(int uid, String permission, int result)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
removeOverridePermissionState(int uid, String permission)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
clearOverridePermissionStates(int uid)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
clearAllOverridePermissionStates()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;
Repeater(InputStream readFrom, OutputStream writeTo)509         public Repeater (InputStream readFrom, OutputStream writeTo) {
510             this.readFrom = readFrom;
511             this.writeTo = writeTo;
512         }
513         @Override
run()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
executeShellCommand(final String command, final ParcelFileDescriptor sink, final ParcelFileDescriptor source)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
executeShellCommandWithStderr(final String command, final ParcelFileDescriptor sink, final ParcelFileDescriptor source, final ParcelFileDescriptor stderrSink)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
shutdown()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 
registerUiTestAutomationServiceLocked(IAccessibilityServiceClient client, @UserIdInt int userId, int flags)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 
unregisterUiTestAutomationServiceLocked()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 
storeRotationStateLocked()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 
restoreRotationStateLocked()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 
isConnectedLocked()703     private boolean isConnectedLocked() {
704         return mClient != null;
705     }
706 
throwIfShutdownLocked()707     private void throwIfShutdownLocked() {
708         if (mIsShutdown) {
709             throw new IllegalStateException("Connection shutdown!");
710         }
711     }
712 
throwIfNotConnectedLocked()713     private void throwIfNotConnectedLocked() {
714         if (!isConnectedLocked()) {
715             throw new IllegalStateException("Not connected!");
716         }
717     }
718 
throwIfCalledByNotTrustedUidLocked()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 }
727