• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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