| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.app; |
| |
| import static android.view.Display.DEFAULT_DISPLAY; |
| |
| import android.accessibilityservice.AccessibilityServiceInfo; |
| import android.accessibilityservice.IAccessibilityServiceClient; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.annotation.UserIdInt; |
| import android.companion.virtual.VirtualDeviceManager; |
| import android.compat.annotation.UnsupportedAppUsage; |
| import android.content.Context; |
| import android.graphics.Rect; |
| import android.hardware.input.InputManager; |
| import android.hardware.input.InputManagerGlobal; |
| import android.os.Binder; |
| import android.os.Build; |
| import android.os.IBinder; |
| import android.os.ParcelFileDescriptor; |
| import android.os.Process; |
| import android.os.RemoteException; |
| import android.os.ServiceManager; |
| import android.os.UserHandle; |
| import android.permission.IPermissionManager; |
| import android.util.Log; |
| import android.view.IWindowManager; |
| import android.view.InputDevice; |
| import android.view.InputEvent; |
| import android.view.KeyEvent; |
| import android.view.MotionEvent; |
| import android.view.SurfaceControl; |
| import android.view.WindowAnimationFrameStats; |
| import android.view.WindowContentFrameStats; |
| import android.view.accessibility.AccessibilityEvent; |
| import android.view.accessibility.IAccessibilityManager; |
| import android.window.ScreenCapture; |
| import android.window.ScreenCapture.CaptureArgs; |
| |
| import libcore.io.IoUtils; |
| |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.util.List; |
| |
| /** |
| * This is a remote object that is passed from the shell to an instrumentation |
| * for enabling access to privileged operations which the shell can do and the |
| * instrumentation cannot. These privileged operations are needed for implementing |
| * a {@link UiAutomation} that enables across application testing by simulating |
| * user actions and performing screen introspection. |
| * |
| * @hide |
| */ |
| public final class UiAutomationConnection extends IUiAutomationConnection.Stub { |
| |
| private static final String TAG = "UiAutomationConnection"; |
| |
| private static final int INITIAL_FROZEN_ROTATION_UNSPECIFIED = -1; |
| |
| private final IWindowManager mWindowManager = IWindowManager.Stub.asInterface( |
| ServiceManager.getService(Service.WINDOW_SERVICE)); |
| |
| private final IAccessibilityManager mAccessibilityManager = IAccessibilityManager.Stub |
| .asInterface(ServiceManager.getService(Service.ACCESSIBILITY_SERVICE)); |
| |
| private final IPermissionManager mPermissionManager = IPermissionManager.Stub |
| .asInterface(ServiceManager.getService("permissionmgr")); |
| |
| private final IActivityManager mActivityManager = IActivityManager.Stub |
| .asInterface(ServiceManager.getService("activity")); |
| |
| private final Object mLock = new Object(); |
| |
| private final Binder mToken = new Binder(); |
| |
| private int mInitialFrozenRotation = INITIAL_FROZEN_ROTATION_UNSPECIFIED; |
| |
| private IAccessibilityServiceClient mClient; |
| |
| private boolean mIsShutdown; |
| |
| private int mOwningUid; |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public UiAutomationConnection() { |
| Log.d(TAG, "Created on user " + Process.myUserHandle()); |
| } |
| |
| @Override |
| public void connect(IAccessibilityServiceClient client, int flags) { |
| if (client == null) { |
| throw new IllegalArgumentException("Client cannot be null!"); |
| } |
| synchronized (mLock) { |
| throwIfShutdownLocked(); |
| if (isConnectedLocked()) { |
| throw new IllegalStateException("Already connected."); |
| } |
| mOwningUid = Binder.getCallingUid(); |
| registerUiTestAutomationServiceLocked(client, |
| Binder.getCallingUserHandle().getIdentifier(), flags); |
| storeRotationStateLocked(); |
| } |
| } |
| |
| @Override |
| public void disconnect() { |
| synchronized (mLock) { |
| throwIfCalledByNotTrustedUidLocked(); |
| throwIfShutdownLocked(); |
| if (!isConnectedLocked()) { |
| throw new IllegalStateException("Already disconnected."); |
| } |
| mOwningUid = -1; |
| unregisterUiTestAutomationServiceLocked(); |
| restoreRotationStateLocked(); |
| } |
| } |
| |
| @Override |
| public boolean injectInputEvent(InputEvent event, boolean sync, boolean waitForAnimations) { |
| synchronized (mLock) { |
| throwIfCalledByNotTrustedUidLocked(); |
| throwIfShutdownLocked(); |
| throwIfNotConnectedLocked(); |
| } |
| |
| final boolean syncTransactionsBefore; |
| final boolean syncTransactionsAfter; |
| if (event instanceof KeyEvent) { |
| KeyEvent keyEvent = (KeyEvent) event; |
| syncTransactionsBefore = keyEvent.getAction() == KeyEvent.ACTION_DOWN; |
| syncTransactionsAfter = keyEvent.getAction() == KeyEvent.ACTION_UP; |
| } else { |
| MotionEvent motionEvent = (MotionEvent) event; |
| syncTransactionsBefore = motionEvent.getAction() == MotionEvent.ACTION_DOWN |
| || motionEvent.isFromSource(InputDevice.SOURCE_MOUSE); |
| syncTransactionsAfter = motionEvent.getAction() == MotionEvent.ACTION_UP; |
| } |
| |
| final long identity = Binder.clearCallingIdentity(); |
| try { |
| if (syncTransactionsBefore) { |
| mWindowManager.syncInputTransactions(waitForAnimations); |
| } |
| |
| final boolean result = InputManagerGlobal.getInstance().injectInputEvent(event, |
| sync ? InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH |
| : InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); |
| |
| if (syncTransactionsAfter) { |
| mWindowManager.syncInputTransactions(waitForAnimations); |
| } |
| return result; |
| } catch (RemoteException e) { |
| e.rethrowFromSystemServer(); |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| return false; |
| } |
| |
| @Override |
| public void injectInputEventToInputFilter(InputEvent event) throws RemoteException { |
| synchronized (mLock) { |
| throwIfCalledByNotTrustedUidLocked(); |
| throwIfShutdownLocked(); |
| throwIfNotConnectedLocked(); |
| } |
| mAccessibilityManager.injectInputEventToInputFilter(event); |
| } |
| |
| @Override |
| public void syncInputTransactions(boolean waitForAnimations) { |
| synchronized (mLock) { |
| throwIfCalledByNotTrustedUidLocked(); |
| throwIfShutdownLocked(); |
| throwIfNotConnectedLocked(); |
| } |
| |
| try { |
| mWindowManager.syncInputTransactions(waitForAnimations); |
| } catch (RemoteException e) { |
| } |
| } |
| |
| @Override |
| public boolean setRotation(int rotation) { |
| synchronized (mLock) { |
| throwIfCalledByNotTrustedUidLocked(); |
| throwIfShutdownLocked(); |
| throwIfNotConnectedLocked(); |
| } |
| final long identity = Binder.clearCallingIdentity(); |
| try { |
| if (rotation == UiAutomation.ROTATION_UNFREEZE) { |
| mWindowManager.thawRotation(/* caller= */ "UiAutomationConnection#setRotation"); |
| } else { |
| mWindowManager.freezeRotation(rotation, |
| /* caller= */ "UiAutomationConnection#setRotation"); |
| } |
| return true; |
| } catch (RemoteException re) { |
| /* ignore */ |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean takeScreenshot(Rect crop, ScreenCapture.ScreenCaptureListener listener) { |
| synchronized (mLock) { |
| throwIfCalledByNotTrustedUidLocked(); |
| throwIfShutdownLocked(); |
| throwIfNotConnectedLocked(); |
| } |
| |
| final long identity = Binder.clearCallingIdentity(); |
| try { |
| final CaptureArgs captureArgs = new CaptureArgs.Builder<>() |
| .setSourceCrop(crop) |
| .build(); |
| mWindowManager.captureDisplay(DEFAULT_DISPLAY, captureArgs, listener); |
| } catch (RemoteException re) { |
| re.rethrowAsRuntimeException(); |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| |
| return true; |
| } |
| |
| @Nullable |
| @Override |
| public boolean takeSurfaceControlScreenshot(@NonNull SurfaceControl surfaceControl, |
| ScreenCapture.ScreenCaptureListener listener) { |
| synchronized (mLock) { |
| throwIfCalledByNotTrustedUidLocked(); |
| throwIfShutdownLocked(); |
| throwIfNotConnectedLocked(); |
| } |
| |
| final long identity = Binder.clearCallingIdentity(); |
| try { |
| ScreenCapture.LayerCaptureArgs args = |
| new ScreenCapture.LayerCaptureArgs.Builder(surfaceControl) |
| .setChildrenOnly(false) |
| .build(); |
| int status = ScreenCapture.captureLayers(args, listener); |
| |
| if (status != 0) { |
| return false; |
| } |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public boolean clearWindowContentFrameStats(int windowId) throws RemoteException { |
| synchronized (mLock) { |
| throwIfCalledByNotTrustedUidLocked(); |
| throwIfShutdownLocked(); |
| throwIfNotConnectedLocked(); |
| } |
| int callingUserId = UserHandle.getCallingUserId(); |
| final long identity = Binder.clearCallingIdentity(); |
| try { |
| IBinder token = mAccessibilityManager.getWindowToken(windowId, callingUserId); |
| if (token == null) { |
| return false; |
| } |
| return mWindowManager.clearWindowContentFrameStats(token); |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| } |
| |
| @Override |
| public WindowContentFrameStats getWindowContentFrameStats(int windowId) throws RemoteException { |
| synchronized (mLock) { |
| throwIfCalledByNotTrustedUidLocked(); |
| throwIfShutdownLocked(); |
| throwIfNotConnectedLocked(); |
| } |
| int callingUserId = UserHandle.getCallingUserId(); |
| final long identity = Binder.clearCallingIdentity(); |
| try { |
| IBinder token = mAccessibilityManager.getWindowToken(windowId, callingUserId); |
| if (token == null) { |
| return null; |
| } |
| return mWindowManager.getWindowContentFrameStats(token); |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| } |
| |
| @Override |
| public void clearWindowAnimationFrameStats() { |
| synchronized (mLock) { |
| throwIfCalledByNotTrustedUidLocked(); |
| throwIfShutdownLocked(); |
| throwIfNotConnectedLocked(); |
| } |
| final long identity = Binder.clearCallingIdentity(); |
| try { |
| SurfaceControl.clearAnimationFrameStats(); |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| } |
| |
| @Override |
| public WindowAnimationFrameStats getWindowAnimationFrameStats() { |
| synchronized (mLock) { |
| throwIfCalledByNotTrustedUidLocked(); |
| throwIfShutdownLocked(); |
| throwIfNotConnectedLocked(); |
| } |
| final long identity = Binder.clearCallingIdentity(); |
| try { |
| WindowAnimationFrameStats stats = new WindowAnimationFrameStats(); |
| SurfaceControl.getAnimationFrameStats(stats); |
| return stats; |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| } |
| |
| /** |
| * Grants permission for the {@link Context#DEVICE_ID_DEFAULT default device} |
| */ |
| @Override |
| public void grantRuntimePermission(String packageName, String permission, int userId) |
| throws RemoteException { |
| synchronized (mLock) { |
| throwIfCalledByNotTrustedUidLocked(); |
| throwIfShutdownLocked(); |
| throwIfNotConnectedLocked(); |
| } |
| final long identity = Binder.clearCallingIdentity(); |
| try { |
| mPermissionManager.grantRuntimePermission(packageName, permission, |
| VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId); |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| } |
| |
| /** |
| * Revokes permission for the {@link Context#DEVICE_ID_DEFAULT default device} |
| */ |
| @Override |
| public void revokeRuntimePermission(String packageName, String permission, int userId) |
| throws RemoteException { |
| synchronized (mLock) { |
| throwIfCalledByNotTrustedUidLocked(); |
| throwIfShutdownLocked(); |
| throwIfNotConnectedLocked(); |
| } |
| final long identity = Binder.clearCallingIdentity(); |
| try { |
| mPermissionManager.revokeRuntimePermission(packageName, permission, |
| VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId, null); |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| } |
| |
| @Override |
| public void adoptShellPermissionIdentity(int uid, @Nullable String[] permissions) |
| throws RemoteException { |
| synchronized (mLock) { |
| throwIfCalledByNotTrustedUidLocked(); |
| throwIfShutdownLocked(); |
| throwIfNotConnectedLocked(); |
| } |
| final long identity = Binder.clearCallingIdentity(); |
| try { |
| mActivityManager.startDelegateShellPermissionIdentity(uid, permissions); |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| } |
| |
| @Override |
| public void dropShellPermissionIdentity() throws RemoteException { |
| synchronized (mLock) { |
| throwIfCalledByNotTrustedUidLocked(); |
| throwIfShutdownLocked(); |
| throwIfNotConnectedLocked(); |
| } |
| final long identity = Binder.clearCallingIdentity(); |
| try { |
| mActivityManager.stopDelegateShellPermissionIdentity(); |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| } |
| |
| @Override |
| @Nullable |
| public List<String> getAdoptedShellPermissions() throws RemoteException { |
| synchronized (mLock) { |
| throwIfCalledByNotTrustedUidLocked(); |
| throwIfShutdownLocked(); |
| throwIfNotConnectedLocked(); |
| } |
| final long identity = Binder.clearCallingIdentity(); |
| try { |
| return mActivityManager.getDelegatedShellPermissions(); |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| } |
| |
| @Override |
| public void addOverridePermissionState(int uid, String permission, int result) |
| throws RemoteException { |
| synchronized (mLock) { |
| throwIfCalledByNotTrustedUidLocked(); |
| throwIfShutdownLocked(); |
| throwIfNotConnectedLocked(); |
| } |
| final int callingUid = Binder.getCallingUid(); |
| final long identity = Binder.clearCallingIdentity(); |
| try { |
| mActivityManager.addOverridePermissionState(callingUid, uid, permission, result); |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| } |
| |
| @Override |
| public void removeOverridePermissionState(int uid, String permission) throws RemoteException { |
| synchronized (mLock) { |
| throwIfCalledByNotTrustedUidLocked(); |
| throwIfShutdownLocked(); |
| throwIfNotConnectedLocked(); |
| } |
| final int callingUid = Binder.getCallingUid(); |
| final long identity = Binder.clearCallingIdentity(); |
| try { |
| mActivityManager.removeOverridePermissionState(callingUid, uid, permission); |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| } |
| |
| @Override |
| public void clearOverridePermissionStates(int uid) throws RemoteException { |
| synchronized (mLock) { |
| throwIfCalledByNotTrustedUidLocked(); |
| throwIfShutdownLocked(); |
| throwIfNotConnectedLocked(); |
| } |
| final int callingUid = Binder.getCallingUid(); |
| final long identity = Binder.clearCallingIdentity(); |
| try { |
| mActivityManager.clearOverridePermissionStates(callingUid, uid); |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| } |
| |
| @Override |
| public void clearAllOverridePermissionStates() throws RemoteException { |
| synchronized (mLock) { |
| throwIfCalledByNotTrustedUidLocked(); |
| throwIfShutdownLocked(); |
| throwIfNotConnectedLocked(); |
| } |
| final int callingUid = Binder.getCallingUid(); |
| final long identity = Binder.clearCallingIdentity(); |
| try { |
| mActivityManager.clearAllOverridePermissionStates(callingUid); |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| } |
| |
| public class Repeater implements Runnable { |
| // Continuously read readFrom and write back to writeTo until EOF is encountered |
| private final InputStream readFrom; |
| private final OutputStream writeTo; |
| public Repeater (InputStream readFrom, OutputStream writeTo) { |
| this.readFrom = readFrom; |
| this.writeTo = writeTo; |
| } |
| @Override |
| public void run() { |
| try { |
| final byte[] buffer = new byte[8192]; |
| int readByteCount; |
| while (true) { |
| readByteCount = readFrom.read(buffer); |
| if (readByteCount < 0) { |
| break; |
| } |
| writeTo.write(buffer, 0, readByteCount); |
| writeTo.flush(); |
| } |
| } catch (IOException ignored) { |
| } finally { |
| IoUtils.closeQuietly(readFrom); |
| IoUtils.closeQuietly(writeTo); |
| } |
| } |
| } |
| |
| @Override |
| public void executeShellCommand(final String command, final ParcelFileDescriptor sink, |
| final ParcelFileDescriptor source) throws RemoteException { |
| executeShellCommandWithStderr(command, sink, source, null /* stderrSink */); |
| } |
| |
| @Override |
| public void executeShellCommandWithStderr(final String command, final ParcelFileDescriptor sink, |
| final ParcelFileDescriptor source, final ParcelFileDescriptor stderrSink) |
| throws RemoteException { |
| synchronized (mLock) { |
| throwIfCalledByNotTrustedUidLocked(); |
| throwIfShutdownLocked(); |
| throwIfNotConnectedLocked(); |
| } |
| final java.lang.Process process; |
| |
| try { |
| process = Runtime.getRuntime().exec(command); |
| } catch (IOException exc) { |
| throw new RuntimeException("Error running shell command '" + command + "'", exc); |
| } |
| |
| // Read from process and write to pipe |
| final Thread readFromProcess; |
| if (sink != null) { |
| InputStream sink_in = process.getInputStream();; |
| OutputStream sink_out = new FileOutputStream(sink.getFileDescriptor()); |
| |
| readFromProcess = new Thread(new Repeater(sink_in, sink_out)); |
| readFromProcess.start(); |
| } else { |
| readFromProcess = null; |
| } |
| |
| // Read from pipe and write to process |
| final Thread writeToProcess; |
| if (source != null) { |
| OutputStream source_out = process.getOutputStream(); |
| InputStream source_in = new FileInputStream(source.getFileDescriptor()); |
| |
| writeToProcess = new Thread(new Repeater(source_in, source_out)); |
| writeToProcess.start(); |
| } else { |
| writeToProcess = null; |
| } |
| |
| // Read from process stderr and write to pipe |
| final Thread readStderrFromProcess; |
| if (stderrSink != null) { |
| InputStream sink_in = process.getErrorStream(); |
| OutputStream sink_out = new FileOutputStream(stderrSink.getFileDescriptor()); |
| |
| readStderrFromProcess = new Thread(new Repeater(sink_in, sink_out)); |
| readStderrFromProcess.start(); |
| } else { |
| readStderrFromProcess = null; |
| } |
| |
| Thread cleanup = new Thread(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| if (writeToProcess != null) { |
| writeToProcess.join(); |
| } |
| if (readFromProcess != null) { |
| readFromProcess.join(); |
| } |
| if (readStderrFromProcess != null) { |
| readStderrFromProcess.join(); |
| } |
| } catch (InterruptedException exc) { |
| Log.e(TAG, "At least one of the threads was interrupted"); |
| } |
| IoUtils.closeQuietly(sink); |
| IoUtils.closeQuietly(source); |
| IoUtils.closeQuietly(stderrSink); |
| process.destroy(); |
| } |
| }); |
| cleanup.start(); |
| } |
| |
| @Override |
| public void shutdown() { |
| synchronized (mLock) { |
| if (isConnectedLocked()) { |
| throwIfCalledByNotTrustedUidLocked(); |
| } |
| throwIfShutdownLocked(); |
| mIsShutdown = true; |
| if (isConnectedLocked()) { |
| disconnect(); |
| } |
| } |
| } |
| |
| private void registerUiTestAutomationServiceLocked(IAccessibilityServiceClient client, |
| @UserIdInt int userId, int flags) { |
| IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface( |
| ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)); |
| final AccessibilityServiceInfo info = new AccessibilityServiceInfo(); |
| info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK; |
| info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC; |
| info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS |
| | AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS |
| | AccessibilityServiceInfo.FLAG_FORCE_DIRECT_BOOT_AWARE; |
| info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT |
| | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION |
| | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS); |
| if ((flags & UiAutomation.FLAG_NOT_ACCESSIBILITY_TOOL) == 0) { |
| info.setAccessibilityTool(true); |
| } |
| try { |
| // Calling out with a lock held is fine since if the system |
| // process is gone the client calling in will be killed. |
| manager.registerUiTestAutomationService(mToken, client, info, userId, flags); |
| mClient = client; |
| } catch (RemoteException re) { |
| throw new IllegalStateException("Error while registering UiTestAutomationService for " |
| + "user " + userId + ".", re); |
| } |
| } |
| |
| private void unregisterUiTestAutomationServiceLocked() { |
| IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface( |
| ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)); |
| try { |
| // Calling out with a lock held is fine since if the system |
| // process is gone the client calling in will be killed. |
| manager.unregisterUiTestAutomationService(mClient); |
| mClient = null; |
| } catch (RemoteException re) { |
| throw new IllegalStateException("Error while unregistering UiTestAutomationService", |
| re); |
| } |
| } |
| |
| private void storeRotationStateLocked() { |
| try { |
| if (mWindowManager.isRotationFrozen()) { |
| // Calling out with a lock held is fine since if the system |
| // process is gone the client calling in will be killed. |
| mInitialFrozenRotation = mWindowManager.getDefaultDisplayRotation(); |
| } |
| } catch (RemoteException re) { |
| /* ignore */ |
| } |
| } |
| |
| private void restoreRotationStateLocked() { |
| try { |
| if (mInitialFrozenRotation != INITIAL_FROZEN_ROTATION_UNSPECIFIED) { |
| // Calling out with a lock held is fine since if the system |
| // process is gone the client calling in will be killed. |
| mWindowManager.freezeRotation(mInitialFrozenRotation, |
| /* caller= */ "UiAutomationConnection#restoreRotationStateLocked"); |
| } else { |
| // Calling out with a lock held is fine since if the system |
| // process is gone the client calling in will be killed. |
| mWindowManager.thawRotation( |
| /* caller= */ "UiAutomationConnection#restoreRotationStateLocked"); |
| } |
| } catch (RemoteException re) { |
| /* ignore */ |
| } |
| } |
| |
| private boolean isConnectedLocked() { |
| return mClient != null; |
| } |
| |
| private void throwIfShutdownLocked() { |
| if (mIsShutdown) { |
| throw new IllegalStateException("Connection shutdown!"); |
| } |
| } |
| |
| private void throwIfNotConnectedLocked() { |
| if (!isConnectedLocked()) { |
| throw new IllegalStateException("Not connected!"); |
| } |
| } |
| |
| private void throwIfCalledByNotTrustedUidLocked() { |
| final int callingUid = Binder.getCallingUid(); |
| if (callingUid != mOwningUid && mOwningUid != Process.SYSTEM_UID |
| && callingUid != 0 /*root*/) { |
| throw new SecurityException("Calling from not trusted UID!"); |
| } |
| } |
| } |