• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 com.android.compatibility.common.util;
18 
19 import android.text.TextUtils;
20 import android.util.AndroidRuntimeException;
21 import android.util.Log;
22 import android.view.View;
23 
24 import androidx.annotation.NonNull;
25 import androidx.test.InstrumentationRegistry;
26 
27 import java.util.concurrent.CountDownLatch;
28 import java.util.concurrent.TimeUnit;
29 import java.util.concurrent.TimeoutException;
30 import java.util.concurrent.atomic.AtomicReference;
31 
32 /**
33  * Provides Shell-based utilities such as running a command.
34  */
35 public final class ShellUtils {
36 
37     private static final String TAG = "ShellHelper";
38 
39     private static final UserHelper sUserHelper = new UserHelper();
40 
41     /**
42      * Runs a Shell command with a timeout, returning a trimmed response.
43      */
44     @NonNull
runShellCommandWithTimeout(@onNull String command, long timeoutInSecond)45     public static String runShellCommandWithTimeout(@NonNull String command, long timeoutInSecond)
46             throws TimeoutException {
47         AtomicReference<Exception> exception = new AtomicReference<>(null);
48         AtomicReference<String> result = new AtomicReference<>(null);
49 
50         CountDownLatch latch = new CountDownLatch(1);
51 
52         new Thread(() -> {
53             try {
54                 result.set(runShellCommand(command));
55             } catch (Exception e) {
56                 exception.set(e);
57             } finally {
58                 latch.countDown();
59             }
60         }).start();
61 
62         try {
63             if (!latch.await(timeoutInSecond, TimeUnit.SECONDS)) {
64                 throw new TimeoutException("Command: '" + command + "' could not run in "
65                         + timeoutInSecond + " seconds");
66             }
67         } catch (InterruptedException e) {
68             Thread.currentThread().interrupt();
69         }
70 
71         if (exception.get() != null) {
72             throw new AndroidRuntimeException(exception.get());
73         }
74 
75         return result.get();
76     }
77 
78     /**
79      * Runs a Shell command, returning a trimmed response.
80      */
81     @NonNull
runShellCommand(@onNull String template, Object...args)82     public static String runShellCommand(@NonNull String template, Object...args) {
83         final String command = String.format(template, args);
84         Log.d(TAG, "runShellCommand(): " + command);
85         try {
86             final String result = SystemUtil
87                     .runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
88             return TextUtils.isEmpty(result) ? "" : result.trim();
89         } catch (Exception e) {
90             throw new RuntimeException("Command '" + command + "' failed: ", e);
91         }
92     }
93 
94     /**
95      * Tap on the view center, it may change window focus.
96      */
tap(View view)97     public static void tap(View view) {
98         final int[] xy = new int[2];
99         view.getLocationOnScreen(xy);
100         final int viewWidth = view.getWidth();
101         final int viewHeight = view.getHeight();
102         final int x = (int) (xy[0] + (viewWidth / 2.0f));
103         final int y = (int) (xy[1] + (viewHeight / 2.0f));
104 
105         runShellCommand("%s tap %d %d", sUserHelper.getInputCmd("touchscreen"), x, y);
106     }
107 
ShellUtils()108     private ShellUtils() {
109         throw new UnsupportedOperationException("contain static methods only");
110     }
111 
112     /**
113      * Simulates input of key event.
114      *
115      * @param keyCode key event to fire.
116      */
sendKeyEvent(String keyCode)117     public static void sendKeyEvent(String keyCode) {
118         runShellCommand("%s %s", sUserHelper.getInputCmd("keyevent"), keyCode);
119     }
120 
121     /**
122      * Allows an app to draw overlaid windows.
123      */
setOverlayPermissions(@onNull String packageName, boolean allowed)124     public static void setOverlayPermissions(@NonNull String packageName, boolean allowed) {
125         final String action = allowed ? "allow" : "ignore";
126         runShellCommand("%s %s SYSTEM_ALERT_WINDOW %s",
127                 sUserHelper.getAppopsCmd("set"), packageName, action);
128     }
129 }
130