Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 121e0c07 authored by Svetoslav's avatar Svetoslav
Browse files

Adding shell command execution to UiAutomation.

The UiAUtomation APIs are an extension object on instrumentation and
is not null only if the instrumentation was started from the shell.
The UiAutomation APIs allow performing privileged operations for testing.
Since UiAutomation tests are just regular instrumentation tests they
cannot take advantage of the rich set of command line utilities on the
platform. This change adds a capability to UiAutomation to execute
shell commands.

bug:12927198

Change-Id: Ifb764c8e89943654f3e35d7a0c65b0b730db672f
parent 201f9b8d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -4838,6 +4838,7 @@ package android.app {
    method public void clearWindowAnimationFrameStats();
    method public boolean clearWindowContentFrameStats(int);
    method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException;
    method public android.os.ParcelFileDescriptor executeShellCommand(java.lang.String);
    method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
    method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
    method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
+1 −0
Original line number Diff line number Diff line
@@ -43,4 +43,5 @@ interface IUiAutomationConnection {
    WindowContentFrameStats getWindowContentFrameStats(int windowId);
    void clearWindowAnimationFrameStats();
    WindowAnimationFrameStats getWindowAnimationFrameStats();
    void executeShellCommand(String command, in ParcelFileDescriptor fd);
}
+41 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.graphics.Canvas;
import android.graphics.Point;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
@@ -40,7 +41,9 @@ import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import libcore.io.IoUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeoutException;
@@ -840,6 +843,44 @@ public final class UiAutomation {
        return null;
    }

    /**
     * Executes a shell command. This method returs a file descriptor that points
     * to the standard output stream. The command execution is similar to running
     * "adb shell <command>" from a host connected to the device.
     * <p>
     * <strong>Note:</strong> It is your responsibility to close the retunred file
     * descriptor once you are done reading.
     * </p>
     *
     * @param command The command to execute.
     * @return A file descriptor to the standard output stream.
     */
    public ParcelFileDescriptor executeShellCommand(String command) {
        synchronized (mLock) {
            throwIfNotConnectedLocked();
        }

        ParcelFileDescriptor source = null;
        ParcelFileDescriptor sink = null;

        try {
            ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
            source = pipe[0];
            sink = pipe[1];

            // Calling out without a lock held.
            mUiAutomationConnection.executeShellCommand(command, sink);
        } catch (IOException ioe) {
            Log.e(LOG_TAG, "Error executing shell command!", ioe);
        } catch (RemoteException re) {
            Log.e(LOG_TAG, "Error executing shell command!", re);
        } finally {
            IoUtils.closeQuietly(sink);
        }

        return source;
    }

    private static float getDegreesForRotation(int value) {
        switch (value) {
            case Surface.ROTATION_90: {
+44 −2
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.graphics.Bitmap;
import android.hardware.input.InputManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -33,6 +34,12 @@ import android.view.WindowAnimationFrameStats;
import android.view.WindowContentFrameStats;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.IAccessibilityManager;
import libcore.io.IoUtils;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * This is a remote object that is passed from the shell to an instrumentation
@@ -50,8 +57,8 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
    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 IAccessibilityManager mAccessibilityManager = IAccessibilityManager.Stub
            .asInterface(ServiceManager.getService(Service.ACCESSIBILITY_SERVICE));

    private final Object mLock = new Object();

@@ -219,6 +226,41 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
        }
    }

    @Override
    public void executeShellCommand(String command, ParcelFileDescriptor sink)
            throws RemoteException {
        synchronized (mLock) {
            throwIfCalledByNotTrustedUidLocked();
            throwIfShutdownLocked();
            throwIfNotConnectedLocked();
        }

        InputStream in = null;
        OutputStream out = null;

        try {
            java.lang.Process process = Runtime.getRuntime().exec(command);

            in = process.getInputStream();
            out = new FileOutputStream(sink.getFileDescriptor());

            final byte[] buffer = new byte[8192];
            while (true) {
                final int readByteCount = in.read(buffer);
                if (readByteCount < 0) {
                    break;
                }
                out.write(buffer, 0, readByteCount);
            }
        } catch (IOException ioe) {
            throw new RuntimeException("Error running shell command", ioe);
        } finally {
            IoUtils.closeQuietly(in);
            IoUtils.closeQuietly(out);
            IoUtils.closeQuietly(sink);
        }
    }

    @Override
    public void shutdown() {
        synchronized (mLock) {