1 /*
2  * Copyright (C) 2011 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.telephony.cts;
18 
19 import android.app.Instrumentation;
20 import android.os.ParcelFileDescriptor;
21 import android.telecom.TelecomManager;
22 import android.telephony.TelephonyManager;
23 
24 import java.io.BufferedReader;
25 import java.io.FileInputStream;
26 import java.io.InputStream;
27 import java.io.InputStreamReader;
28 import java.lang.reflect.Field;
29 import java.nio.charset.StandardCharsets;
30 import java.util.function.BooleanSupplier;
31 
32 public class TelephonyUtils {
33 
34     /**
35      * See {@link TelecomManager#ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION}
36      */
37     public static final String ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION_STRING =
38             "ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION";
39 
40     private static final String COMMAND_ADD_TEST_EMERGENCY_NUMBER =
41             "cmd phone emergency-number-test-mode -a ";
42 
43     private static final String COMMAND_REMOVE_TEST_EMERGENCY_NUMBER =
44             "cmd phone emergency-number-test-mode -r ";
45 
46     private static final String COMMAND_END_BLOCK_SUPPRESSION = "cmd phone end-block-suppression";
47 
48     private static final String COMMAND_FLUSH_TELEPHONY_METRICS =
49             "/system/bin/dumpsys activity service TelephonyDebugService --metricsproto";
50 
51     private static final String COMMAND_AM_COMPAT = "am compat ";
52 
53     public static final String CTS_APP_PACKAGE = "android.telephony.cts";
54     public static final String CTS_APP_PACKAGE2 = "android.telephony2.cts";
55 
56     private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
57             'A', 'B', 'C', 'D', 'E', 'F' };
58 
addTestEmergencyNumber(Instrumentation instr, String testNumber)59     public static void addTestEmergencyNumber(Instrumentation instr, String testNumber)
60             throws Exception {
61         executeShellCommand(instr, COMMAND_ADD_TEST_EMERGENCY_NUMBER + testNumber);
62     }
63 
removeTestEmergencyNumber(Instrumentation instr, String testNumber)64     public static void removeTestEmergencyNumber(Instrumentation instr, String testNumber)
65             throws Exception {
66         executeShellCommand(instr, COMMAND_REMOVE_TEST_EMERGENCY_NUMBER + testNumber);
67     }
68 
endBlockSuppression(Instrumentation instr)69     public static void endBlockSuppression(Instrumentation instr) throws Exception {
70         executeShellCommand(instr, COMMAND_END_BLOCK_SUPPRESSION);
71     }
72 
flushTelephonyMetrics(Instrumentation instr)73     public static void flushTelephonyMetrics(Instrumentation instr) throws Exception {
74         executeShellCommand(instr, COMMAND_FLUSH_TELEPHONY_METRICS);
75     }
76 
enableCompatCommand(Instrumentation instr, String pkgName, String commandName)77     public static void enableCompatCommand(Instrumentation instr, String pkgName,
78             String commandName) throws Exception {
79         executeShellCommand(instr, COMMAND_AM_COMPAT + "enable  --no-kill " + commandName + " "
80                 + pkgName);
81     }
82 
disableCompatCommand(Instrumentation instr, String pkgName, String commandName)83     public static void disableCompatCommand(Instrumentation instr, String pkgName,
84             String commandName) throws Exception {
85         executeShellCommand(instr, COMMAND_AM_COMPAT + "disable  --no-kill " + commandName + " "
86                 + pkgName);
87     }
88 
resetCompatCommand(Instrumentation instr, String pkgName, String commandName)89     public static void resetCompatCommand(Instrumentation instr, String pkgName,
90             String commandName) throws Exception {
91         executeShellCommand(instr, COMMAND_AM_COMPAT + "reset  --no-kill " + commandName + " "
92                 + pkgName);
93     }
94 
isSkt(TelephonyManager telephonyManager)95     public static boolean isSkt(TelephonyManager telephonyManager) {
96         return isOperator(telephonyManager, "45005");
97     }
98 
isKt(TelephonyManager telephonyManager)99     public static boolean isKt(TelephonyManager telephonyManager) {
100         return isOperator(telephonyManager, "45002")
101                 || isOperator(telephonyManager, "45004")
102                 || isOperator(telephonyManager, "45008");
103     }
104 
isOperator(TelephonyManager telephonyManager, String operator)105     private static boolean isOperator(TelephonyManager telephonyManager, String operator) {
106         String simOperator = telephonyManager.getSimOperator();
107         return simOperator != null && simOperator.equals(operator);
108     }
109 
parseErrorCodeToString(int errorCode, Class<?> containingClass, String prefix)110     public static String parseErrorCodeToString(int errorCode,
111             Class<?> containingClass, String prefix) {
112         for (Field field : containingClass.getDeclaredFields()) {
113             if (field.getName().startsWith(prefix)) {
114                 if (field.getType() == Integer.TYPE) {
115                     field.setAccessible(true);
116                     try {
117                         if (field.getInt(null) == errorCode) {
118                             return field.getName();
119                         }
120                     } catch (IllegalAccessException e) {
121                         continue;
122                     }
123                 }
124             }
125         }
126         return String.format("??%d??", errorCode);
127     }
128 
129     /**
130      * Executes the given shell command and returns the output in a string. Note that even
131      * if we don't care about the output, we have to read the stream completely to make the
132      * command execute.
133      */
executeShellCommand(Instrumentation instrumentation, String command)134     public static String executeShellCommand(Instrumentation instrumentation,
135             String command) throws Exception {
136         final ParcelFileDescriptor pfd =
137                 instrumentation.getUiAutomation().executeShellCommand(command);
138         BufferedReader br = null;
139         try (InputStream in = new FileInputStream(pfd.getFileDescriptor())) {
140             br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
141             String str = null;
142             StringBuilder out = new StringBuilder();
143             while ((str = br.readLine()) != null) {
144                 out.append(str);
145             }
146             return out.toString();
147         } finally {
148             if (br != null) {
149                 closeQuietly(br);
150             }
151             closeQuietly(pfd);
152         }
153     }
154 
closeQuietly(AutoCloseable closeable)155     private static void closeQuietly(AutoCloseable closeable) {
156         if (closeable != null) {
157             try {
158                 closeable.close();
159             } catch (RuntimeException rethrown) {
160                 throw rethrown;
161             } catch (Exception ignored) {
162             }
163         }
164     }
165 
pollUntilTrue(BooleanSupplier s, int times, int timeoutMs)166     public static boolean pollUntilTrue(BooleanSupplier s, int times, int timeoutMs) {
167         boolean successful = false;
168         for (int i = 0; i < times; i++) {
169             successful = s.getAsBoolean();
170             if (successful) break;
171             try {
172                 Thread.sleep(timeoutMs);
173             } catch (InterruptedException e) { }
174         }
175         return successful;
176     }
177 
toHexString(byte[] array)178     public static String toHexString(byte[] array) {
179         int length = array.length;
180         char[] buf = new char[length * 2];
181 
182         int bufIndex = 0;
183         for (byte b : array) {
184             buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F];
185             buf[bufIndex++] = HEX_DIGITS[b & 0x0F];
186         }
187 
188         return new String(buf);
189     }
190 
toByte(char c)191     private static int toByte(char c) {
192         if (c >= '0' && c <= '9') return (c - '0');
193         if (c >= 'A' && c <= 'F') return (c - 'A' + 10);
194         if (c >= 'a' && c <= 'f') return (c - 'a' + 10);
195 
196         throw new RuntimeException("Invalid hex char '" + c + "'");
197     }
198 
hexStringToByteArray(String hexString)199     public static byte[] hexStringToByteArray(String hexString) {
200         int length = hexString.length();
201         byte[] buffer = new byte[length / 2];
202 
203         for (int i = 0; i < length; i += 2) {
204             buffer[i / 2] =
205                     (byte) ((toByte(hexString.charAt(i)) << 4) | toByte(hexString.charAt(i + 1)));
206         }
207 
208         return buffer;
209     }
210 }
211