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 17package android.app; 18 19import android.accessibilityservice.AccessibilityServiceInfo; 20import android.accessibilityservice.IAccessibilityServiceClient; 21import android.content.Context; 22import android.graphics.Bitmap; 23import android.hardware.input.InputManager; 24import android.os.Binder; 25import android.os.Process; 26import android.os.RemoteException; 27import android.os.ServiceManager; 28import android.view.IWindowManager; 29import android.view.InputEvent; 30import android.view.SurfaceControl; 31import android.view.accessibility.AccessibilityEvent; 32import android.view.accessibility.IAccessibilityManager; 33 34/** 35 * This is a remote object that is passed from the shell to an instrumentation 36 * for enabling access to privileged operations which the shell can do and the 37 * instrumentation cannot. These privileged operations are needed for implementing 38 * a {@link UiAutomation} that enables across application testing by simulating 39 * user actions and performing screen introspection. 40 * 41 * @hide 42 */ 43public final class UiAutomationConnection extends IUiAutomationConnection.Stub { 44 45 private static final int INITIAL_FROZEN_ROTATION_UNSPECIFIED = -1; 46 47 private final IWindowManager mWindowManager = IWindowManager.Stub.asInterface( 48 ServiceManager.getService(Service.WINDOW_SERVICE)); 49 50 private final Object mLock = new Object(); 51 52 private final Binder mToken = new Binder(); 53 54 private int mInitialFrozenRotation = INITIAL_FROZEN_ROTATION_UNSPECIFIED; 55 56 private IAccessibilityServiceClient mClient; 57 58 private boolean mIsShutdown; 59 60 private int mOwningUid; 61 62 public void connect(IAccessibilityServiceClient client) { 63 if (client == null) { 64 throw new IllegalArgumentException("Client cannot be null!"); 65 } 66 synchronized (mLock) { 67 throwIfShutdownLocked(); 68 if (isConnectedLocked()) { 69 throw new IllegalStateException("Already connected."); 70 } 71 mOwningUid = Binder.getCallingUid(); 72 registerUiTestAutomationServiceLocked(client); 73 storeRotationStateLocked(); 74 } 75 } 76 77 @Override 78 public void disconnect() { 79 synchronized (mLock) { 80 throwIfCalledByNotTrustedUidLocked(); 81 throwIfShutdownLocked(); 82 if (!isConnectedLocked()) { 83 throw new IllegalStateException("Already disconnected."); 84 } 85 mOwningUid = -1; 86 unregisterUiTestAutomationServiceLocked(); 87 restoreRotationStateLocked(); 88 } 89 } 90 91 @Override 92 public boolean injectInputEvent(InputEvent event, boolean sync) { 93 synchronized (mLock) { 94 throwIfCalledByNotTrustedUidLocked(); 95 throwIfShutdownLocked(); 96 throwIfNotConnectedLocked(); 97 } 98 final int mode = (sync) ? InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH 99 : InputManager.INJECT_INPUT_EVENT_MODE_ASYNC; 100 final long identity = Binder.clearCallingIdentity(); 101 try { 102 return InputManager.getInstance().injectInputEvent(event, mode); 103 } finally { 104 Binder.restoreCallingIdentity(identity); 105 } 106 } 107 108 @Override 109 public boolean setRotation(int rotation) { 110 synchronized (mLock) { 111 throwIfCalledByNotTrustedUidLocked(); 112 throwIfShutdownLocked(); 113 throwIfNotConnectedLocked(); 114 } 115 final long identity = Binder.clearCallingIdentity(); 116 try { 117 if (rotation == UiAutomation.ROTATION_UNFREEZE) { 118 mWindowManager.thawRotation(); 119 } else { 120 mWindowManager.freezeRotation(rotation); 121 } 122 return true; 123 } catch (RemoteException re) { 124 /* ignore */ 125 } finally { 126 Binder.restoreCallingIdentity(identity); 127 } 128 return false; 129 } 130 131 @Override 132 public Bitmap takeScreenshot(int width, int height) { 133 synchronized (mLock) { 134 throwIfCalledByNotTrustedUidLocked(); 135 throwIfShutdownLocked(); 136 throwIfNotConnectedLocked(); 137 } 138 final long identity = Binder.clearCallingIdentity(); 139 try { 140 return SurfaceControl.screenshot(width, height); 141 } finally { 142 Binder.restoreCallingIdentity(identity); 143 } 144 } 145 146 @Override 147 public void shutdown() { 148 synchronized (mLock) { 149 throwIfCalledByNotTrustedUidLocked(); 150 throwIfShutdownLocked(); 151 mIsShutdown = true; 152 if (isConnectedLocked()) { 153 disconnect(); 154 } 155 } 156 } 157 158 private void registerUiTestAutomationServiceLocked(IAccessibilityServiceClient client) { 159 IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface( 160 ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)); 161 AccessibilityServiceInfo info = new AccessibilityServiceInfo(); 162 info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK; 163 info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC; 164 info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS 165 | AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS; 166 info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT 167 | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION 168 | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY 169 | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS); 170 try { 171 // Calling out with a lock held is fine since if the system 172 // process is gone the client calling in will be killed. 173 manager.registerUiTestAutomationService(mToken, client, info); 174 mClient = client; 175 } catch (RemoteException re) { 176 throw new IllegalStateException("Error while registering UiTestAutomationService.", re); 177 } 178 } 179 180 private void unregisterUiTestAutomationServiceLocked() { 181 IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface( 182 ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)); 183 try { 184 // Calling out with a lock held is fine since if the system 185 // process is gone the client calling in will be killed. 186 manager.unregisterUiTestAutomationService(mClient); 187 mClient = null; 188 } catch (RemoteException re) { 189 throw new IllegalStateException("Error while unregistering UiTestAutomationService", 190 re); 191 } 192 } 193 194 private void storeRotationStateLocked() { 195 try { 196 if (mWindowManager.isRotationFrozen()) { 197 // Calling out with a lock held is fine since if the system 198 // process is gone the client calling in will be killed. 199 mInitialFrozenRotation = mWindowManager.getRotation(); 200 } 201 } catch (RemoteException re) { 202 /* ignore */ 203 } 204 } 205 206 private void restoreRotationStateLocked() { 207 try { 208 if (mInitialFrozenRotation != INITIAL_FROZEN_ROTATION_UNSPECIFIED) { 209 // Calling out with a lock held is fine since if the system 210 // process is gone the client calling in will be killed. 211 mWindowManager.freezeRotation(mInitialFrozenRotation); 212 } else { 213 // Calling out with a lock held is fine since if the system 214 // process is gone the client calling in will be killed. 215 mWindowManager.thawRotation(); 216 } 217 } catch (RemoteException re) { 218 /* ignore */ 219 } 220 } 221 222 private boolean isConnectedLocked() { 223 return mClient != null; 224 } 225 226 private void throwIfShutdownLocked() { 227 if (mIsShutdown) { 228 throw new IllegalStateException("Connection shutdown!"); 229 } 230 } 231 232 private void throwIfNotConnectedLocked() { 233 if (!isConnectedLocked()) { 234 throw new IllegalStateException("Not connected!"); 235 } 236 } 237 238 private void throwIfCalledByNotTrustedUidLocked() { 239 final int callingUid = Binder.getCallingUid(); 240 if (callingUid != mOwningUid && mOwningUid != Process.SYSTEM_UID 241 && callingUid != 0 /*root*/) { 242 throw new SecurityException("Calling from not trusted UID!"); 243 } 244 } 245} 246