1 /*
2  * Copyright (C) 2015 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 package android.telecom.cts;
17 
18 import android.app.Instrumentation;
19 import android.bluetooth.BluetoothDevice;
20 import android.content.ComponentName;
21 import android.content.ContentProviderOperation;
22 import android.content.ContentResolver;
23 import android.content.Context;
24 import android.content.pm.PackageManager;
25 import android.graphics.Color;
26 import android.net.Uri;
27 import android.os.Build;
28 import android.os.Bundle;
29 import android.os.Handler;
30 import android.os.Looper;
31 import android.os.Parcel;
32 import android.os.ParcelFileDescriptor;
33 import android.os.Process;
34 import android.os.SystemClock;
35 import android.os.UserManager;
36 import android.provider.ContactsContract;
37 import android.telecom.PhoneAccount;
38 import android.telecom.PhoneAccountHandle;
39 import android.telecom.TelecomManager;
40 
41 import androidx.test.InstrumentationRegistry;
42 
43 import junit.framework.TestCase;
44 
45 import java.io.BufferedReader;
46 import java.io.FileInputStream;
47 import java.io.InputStream;
48 import java.io.InputStreamReader;
49 import java.nio.charset.StandardCharsets;
50 import java.util.ArrayList;
51 import java.util.Optional;
52 import java.util.concurrent.CountDownLatch;
53 import java.util.concurrent.TimeUnit;
54 import java.util.function.Predicate;
55 
56 public class TestUtils {
57     static final String TAG = "TelecomCTSTests";
58     static final boolean HAS_TELECOM = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
59     static final long WAIT_FOR_STATE_CHANGE_TIMEOUT_MS = 10000;
60     static final long WAIT_FOR_CALL_ADDED_TIMEOUT_S = 15;
61     static final long WAIT_FOR_STATE_CHANGE_TIMEOUT_CALLBACK = 50;
62     static final long WAIT_FOR_PHONE_STATE_LISTENER_REGISTERED_TIMEOUT_S = 15;
63     static final long WAIT_FOR_PHONE_STATE_LISTENER_CALLBACK_TIMEOUT_S = 15;
64     static final boolean HAS_BLUETOOTH = hasBluetoothFeature();
65     static final BluetoothDevice BLUETOOTH_DEVICE1 = makeBluetoothDevice("00:00:00:00:00:01");
66     static final BluetoothDevice BLUETOOTH_DEVICE2 = makeBluetoothDevice("00:00:00:00:00:02");
67 
68     // Non-final to allow modification by tests not in this package (e.g. permission-related
69     // tests in the Telecom2 test package.
70     public static String PACKAGE = "android.telecom.cts";
71     public static final String TEST_URI_SCHEME = "foobuzz";
72     public static final String COMPONENT = "android.telecom.cts.CtsConnectionService";
73     public static final String INCALL_COMPONENT = "android.telecom.cts/.MockInCallService";
74     public static final String SELF_MANAGED_COMPONENT =
75             "android.telecom.cts.CtsSelfManagedConnectionService";
76     public static final String REMOTE_COMPONENT = "android.telecom.cts.CtsRemoteConnectionService";
77     public static final String ACCOUNT_ID_1 = "xtstest_CALL_PROVIDER_ID_1";
78     public static final String ACCOUNT_ID_2 = "xtstest_CALL_PROVIDER_ID_2";
79     public static final String ACCOUNT_ID_SIM = "sim_acct";
80     public static final String ACCOUNT_ID_EMERGENCY = "xtstest_CALL_PROVIDER_EMERGENCY";
81     public static final String EXTRA_PHONE_NUMBER = "android.telecom.cts.extra.PHONE_NUMBER";
82     public static final PhoneAccountHandle TEST_PHONE_ACCOUNT_HANDLE =
83             new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), ACCOUNT_ID_1);
84     public static final PhoneAccountHandle TEST_SIM_PHONE_ACCOUNT_HANDLE =
85             new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), ACCOUNT_ID_SIM);
86     public static final PhoneAccountHandle TEST_PHONE_ACCOUNT_HANDLE_2 =
87             new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), ACCOUNT_ID_2);
88     public static final PhoneAccountHandle TEST_EMERGENCY_PHONE_ACCOUNT_HANDLE =
89             new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), ACCOUNT_ID_EMERGENCY);
90     public static final String DEFAULT_TEST_ACCOUNT_1_ID = "ctstest_DEFAULT_TEST_ID_1";
91     public static final String DEFAULT_TEST_ACCOUNT_2_ID = "ctstest_DEFAULT_TEST_ID_2";
92     public static final PhoneAccountHandle TEST_DEFAULT_PHONE_ACCOUNT_HANDLE_1 =
93             new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT),
94                     DEFAULT_TEST_ACCOUNT_1_ID);
95     public static final PhoneAccountHandle TEST_DEFAULT_PHONE_ACCOUNT_HANDLE_2 =
96             new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT),
97                     DEFAULT_TEST_ACCOUNT_2_ID);
98     public static final PhoneAccountHandle TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE =
99             new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), "handoverFrom");
100     public static final PhoneAccountHandle TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE =
101             new PhoneAccountHandle(new ComponentName(PACKAGE, SELF_MANAGED_COMPONENT),
102                     "handoverTo");
103     public static final String REMOTE_ACCOUNT_ID = "xtstest_REMOTE_CALL_PROVIDER_ID";
104     public static final String SELF_MANAGED_ACCOUNT_ID_1 = "ctstest_SELF_MANAGED_ID_1";
105     public static final PhoneAccountHandle TEST_SELF_MANAGED_HANDLE_1 =
106             new PhoneAccountHandle(new ComponentName(PACKAGE, SELF_MANAGED_COMPONENT),
107                     SELF_MANAGED_ACCOUNT_ID_1);
108     public static final String SELF_MANAGED_ACCOUNT_ID_2 = "ctstest_SELF_MANAGED_ID_2";
109     public static final PhoneAccountHandle TEST_SELF_MANAGED_HANDLE_2 =
110             new PhoneAccountHandle(new ComponentName(PACKAGE, SELF_MANAGED_COMPONENT),
111                     SELF_MANAGED_ACCOUNT_ID_2);
112     public static final String SELF_MANAGED_ACCOUNT_ID_3 = "ctstest_SELF_MANAGED_ID_3";
113     public static final PhoneAccountHandle TEST_SELF_MANAGED_HANDLE_3 =
114             new PhoneAccountHandle(new ComponentName(PACKAGE, SELF_MANAGED_COMPONENT),
115                     SELF_MANAGED_ACCOUNT_ID_3);
116     public static final String SELF_MANAGED_ACCOUNT_ID_4 = "ctstest_SELF_MANAGED_ID_4";
117     public static final PhoneAccountHandle TEST_SELF_MANAGED_HANDLE_4 =
118             new PhoneAccountHandle(new ComponentName(PACKAGE, SELF_MANAGED_COMPONENT),
119                     SELF_MANAGED_ACCOUNT_ID_4);
120 
121     public static final String ACCOUNT_LABEL = "CTSConnectionService";
122     public static final String SIM_ACCOUNT_LABEL = "CTSConnectionServiceSim";
123     public static final PhoneAccount TEST_PHONE_ACCOUNT = PhoneAccount.builder(
124             TEST_PHONE_ACCOUNT_HANDLE, ACCOUNT_LABEL)
125             .setAddress(Uri.parse("tel:555-TEST"))
126             .setSubscriptionAddress(Uri.parse("tel:555-TEST"))
127             .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER |
128                     PhoneAccount.CAPABILITY_VIDEO_CALLING |
129                     PhoneAccount.CAPABILITY_RTT |
130                     PhoneAccount.CAPABILITY_CONNECTION_MANAGER |
131                     PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS |
132                     PhoneAccount.CAPABILITY_ADHOC_CONFERENCE_CALLING)
133             .setHighlightColor(Color.RED)
134             .setShortDescription(ACCOUNT_LABEL)
135             .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
136             .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
137             .build();
138 
139     public static final PhoneAccount TEST_SIM_PHONE_ACCOUNT = PhoneAccount.builder(
140             TEST_SIM_PHONE_ACCOUNT_HANDLE, SIM_ACCOUNT_LABEL)
141             .setAddress(Uri.parse("tel:555-TEST"))
142             .setSubscriptionAddress(Uri.parse("tel:555-TEST"))
143             .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER |
144                     PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
145             .setHighlightColor(Color.RED)
146             .setShortDescription(SIM_ACCOUNT_LABEL)
147             .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
148             .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
149             .build();
150 
151     public static final PhoneAccount TEST_PHONE_ACCOUNT_2 = PhoneAccount.builder(
152             TEST_PHONE_ACCOUNT_HANDLE_2, ACCOUNT_LABEL + "2")
153             .setAddress(Uri.parse("tel:555-TEST2"))
154             .setSubscriptionAddress(Uri.parse("tel:555-TEST2"))
155             .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER |
156                     PhoneAccount.CAPABILITY_VIDEO_CALLING |
157                     PhoneAccount.CAPABILITY_RTT |
158                     PhoneAccount.CAPABILITY_CONNECTION_MANAGER)
159             .setHighlightColor(Color.BLUE)
160             .setShortDescription(ACCOUNT_LABEL)
161             .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
162             .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
163             .build();
164 
165     public static final PhoneAccount TEST_DEFAULT_PHONE_ACCOUNT_1 = PhoneAccount.builder(
166             TEST_DEFAULT_PHONE_ACCOUNT_HANDLE_1, "Default Test 1")
167             .setAddress(Uri.parse("foobuzz:testuri1"))
168             .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
169             .setHighlightColor(Color.RED)
170             .setShortDescription("Default Test 1")
171             .addSupportedUriScheme(TEST_URI_SCHEME)
172             .build();
173     public static final PhoneAccount TEST_DEFAULT_PHONE_ACCOUNT_2 = PhoneAccount.builder(
174             TEST_DEFAULT_PHONE_ACCOUNT_HANDLE_2, "Default Test 2")
175             .setAddress(Uri.parse("foobuzz:testuri2"))
176             .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
177             .setHighlightColor(Color.RED)
178             .setShortDescription("Default Test 2")
179             .addSupportedUriScheme(TEST_URI_SCHEME)
180             .build();
181     private static final Bundle SUPPORTS_HANDOVER_FROM_EXTRAS = new Bundle();
182     private static final Bundle SUPPORTS_HANDOVER_TO_EXTRAS = new Bundle();
183     static {
SUPPORTS_HANDOVER_FROM_EXTRAS.putBoolean(PhoneAccount.EXTRA_SUPPORTS_HANDOVER_FROM, true)184         SUPPORTS_HANDOVER_FROM_EXTRAS.putBoolean(PhoneAccount.EXTRA_SUPPORTS_HANDOVER_FROM, true);
SUPPORTS_HANDOVER_TO_EXTRAS.putBoolean(PhoneAccount.EXTRA_SUPPORTS_HANDOVER_TO, true)185         SUPPORTS_HANDOVER_TO_EXTRAS.putBoolean(PhoneAccount.EXTRA_SUPPORTS_HANDOVER_TO, true);
186     }
187     public static final PhoneAccount TEST_PHONE_ACCOUNT_HANDOVER_SRC = PhoneAccount.builder(
188             TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE, ACCOUNT_LABEL)
189             .setAddress(Uri.parse("tel:555-TEST"))
190             .setExtras(SUPPORTS_HANDOVER_FROM_EXTRAS)
191             .setSubscriptionAddress(Uri.parse("tel:555-TEST"))
192             .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
193             .setHighlightColor(Color.BLUE)
194             .setShortDescription(ACCOUNT_LABEL)
195             .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
196             .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
197             .build();
198     public static final PhoneAccount TEST_PHONE_ACCOUNT_HANDOVER_DEST = PhoneAccount.builder(
199             TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE, ACCOUNT_LABEL)
200             .setAddress(Uri.parse("tel:555-TEST"))
201             .setExtras(SUPPORTS_HANDOVER_TO_EXTRAS)
202             .setSubscriptionAddress(Uri.parse("tel:555-TEST"))
203             .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)
204             .setHighlightColor(Color.MAGENTA)
205             .setShortDescription(ACCOUNT_LABEL)
206             .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
207             .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
208             .build();
209     public static final String REMOTE_ACCOUNT_LABEL = "CTSRemoteConnectionService";
210     public static final String SELF_MANAGED_ACCOUNT_LABEL = "android.telecom.cts";
211     public static final PhoneAccount TEST_SELF_MANAGED_PHONE_ACCOUNT_3 = PhoneAccount.builder(
212             TEST_SELF_MANAGED_HANDLE_3, SELF_MANAGED_ACCOUNT_LABEL)
213             .setAddress(Uri.fromParts(TEST_URI_SCHEME, "test@test.com", null))
214             .setSubscriptionAddress(Uri.fromParts(TEST_URI_SCHEME, "test@test.com", null))
215             .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED |
216                     PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING |
217                     PhoneAccount.CAPABILITY_VIDEO_CALLING)
218             .setHighlightColor(Color.BLUE)
219             .setShortDescription(SELF_MANAGED_ACCOUNT_LABEL)
220             .addSupportedUriScheme(TEST_URI_SCHEME)
221             .build();
222     public static final Bundle SELF_MANAGED_ACCOUNT_1_EXTRAS;
223     static {
224         SELF_MANAGED_ACCOUNT_1_EXTRAS = new Bundle();
SELF_MANAGED_ACCOUNT_1_EXTRAS.putBoolean( PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, false)225         SELF_MANAGED_ACCOUNT_1_EXTRAS.putBoolean(
226                 PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, false);
227     }
228     public static final Bundle SELF_MANAGED_ACCOUNT_2_EXTRAS;
229     static {
230         SELF_MANAGED_ACCOUNT_2_EXTRAS = new Bundle();
SELF_MANAGED_ACCOUNT_2_EXTRAS.putBoolean(PhoneAccount.EXTRA_LOG_SELF_MANAGED_CALLS, true)231         SELF_MANAGED_ACCOUNT_2_EXTRAS.putBoolean(PhoneAccount.EXTRA_LOG_SELF_MANAGED_CALLS, true);
232     }
233     public static final Bundle SELF_MANAGED_ACCOUNT_4_EXTRAS;
234     static {
235         SELF_MANAGED_ACCOUNT_4_EXTRAS = new Bundle();
SELF_MANAGED_ACCOUNT_4_EXTRAS.putBoolean( PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, true)236         SELF_MANAGED_ACCOUNT_4_EXTRAS.putBoolean(
237                 PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, true);
238     }
239 
240     public static final PhoneAccount TEST_SELF_MANAGED_PHONE_ACCOUNT_2 = PhoneAccount.builder(
241             TEST_SELF_MANAGED_HANDLE_2, SELF_MANAGED_ACCOUNT_LABEL)
242             .setAddress(Uri.parse("sip:test@test.com"))
243             .setSubscriptionAddress(Uri.parse("sip:test@test.com"))
244             .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED |
245                     PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING |
246                     PhoneAccount.CAPABILITY_VIDEO_CALLING)
247             .setHighlightColor(Color.BLUE)
248             .setShortDescription(SELF_MANAGED_ACCOUNT_LABEL)
249             .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
250             .addSupportedUriScheme(PhoneAccount.SCHEME_SIP)
251             .setExtras(SELF_MANAGED_ACCOUNT_2_EXTRAS)
252             .build();
253     public static final PhoneAccount TEST_SELF_MANAGED_PHONE_ACCOUNT_1 = PhoneAccount.builder(
254             TEST_SELF_MANAGED_HANDLE_1, SELF_MANAGED_ACCOUNT_LABEL)
255             .setAddress(Uri.parse("sip:test@test.com"))
256             .setSubscriptionAddress(Uri.parse("sip:test@test.com"))
257             .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED |
258                     PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING |
259                     PhoneAccount.CAPABILITY_VIDEO_CALLING)
260             .setHighlightColor(Color.BLUE)
261             .setShortDescription(SELF_MANAGED_ACCOUNT_LABEL)
262             .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
263             .addSupportedUriScheme(PhoneAccount.SCHEME_SIP)
264             .setExtras(SELF_MANAGED_ACCOUNT_1_EXTRAS)
265             .build();
266     public static final PhoneAccount TEST_SELF_MANAGED_PHONE_ACCOUNT_4 = PhoneAccount.builder(
267             TEST_SELF_MANAGED_HANDLE_4, SELF_MANAGED_ACCOUNT_LABEL)
268             .setAddress(Uri.parse("sip:test@test.com"))
269             .setSubscriptionAddress(Uri.parse("sip:test@test.com"))
270             .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED |
271                     PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING |
272                     PhoneAccount.CAPABILITY_VIDEO_CALLING)
273             .setHighlightColor(Color.BLUE)
274             .setShortDescription(SELF_MANAGED_ACCOUNT_LABEL)
275             .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
276             .addSupportedUriScheme(PhoneAccount.SCHEME_SIP)
277             .setExtras(SELF_MANAGED_ACCOUNT_4_EXTRAS)
278             .build();
279 
280     /**
281      * See {@link TelecomManager#ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION}
282      */
283     public static final String ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION_STRING =
284             "ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION ";
285 
286     /**
287      * See {@link TelecomManager#ENABLE_GET_PHONE_ACCOUNT_PERMISSION_PROTECTION}
288      */
289     public static final String ENABLE_GET_PHONE_ACCOUNT_PERMISSION_PROTECTION_STRING =
290             "ENABLE_GET_PHONE_ACCOUNT_PERMISSION_PROTECTION ";
291 
292     private static final String COMMAND_SET_CALL_DIAGNOSTIC_SERVICE =
293             "telecom set-call-diagnostic-service ";
294 
295     private static final String COMMAND_SET_DEFAULT_DIALER = "telecom set-default-dialer ";
296 
297     private static final String COMMAND_GET_DEFAULT_DIALER = "telecom get-default-dialer";
298 
299     private static final String COMMAND_SET_SYSTEM_DIALER = "telecom set-system-dialer ";
300 
301     private static final String COMMAND_GET_SYSTEM_DIALER = "telecom get-system-dialer";
302 
303     private static final String COMMAND_ENABLE = "telecom set-phone-account-enabled ";
304 
305     private static final String COMMAND_SET_ACCT_SUGGESTION =
306             "telecom set-phone-acct-suggestion-component ";
307 
308     private static final String COMMAND_REGISTER_SIM = "telecom register-sim-phone-account ";
309 
310     private static final String COMMAND_SET_DEFAULT_PHONE_ACCOUNT =
311             "telecom set-user-selected-outgoing-phone-account ";
312 
313     private static final String COMMAND_WAIT_ON_HANDLERS = "telecom wait-on-handlers";
314 
315     private static final String COMMAND_ADD_TEST_EMERGENCY_NUMBER =
316             "cmd phone emergency-number-test-mode -a ";
317 
318     private static final String COMMAND_REMOVE_TEST_EMERGENCY_NUMBER =
319             "cmd phone emergency-number-test-mode -r ";
320 
321     private static final String COMMAND_CLEAR_TEST_EMERGENCY_NUMBERS =
322             "cmd phone emergency-number-test-mode -c";
323 
324     private static final String COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_NAME_FILTER =
325             "telecom set-test-emergency-phone-account-package-filter ";
326 
327     private static final String COMMAND_AM_COMPAT = "am compat ";
328 
329     public static final String MERGE_CALLER_NAME = "calls-merged";
330     public static final String SWAP_CALLER_NAME = "calls-swapped";
331 
shouldTestTelecom(Context context)332     public static boolean shouldTestTelecom(Context context) {
333         if (!HAS_TELECOM) {
334             return false;
335         }
336         final PackageManager pm = context.getPackageManager();
337         return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) &&
338                 pm.hasSystemFeature(PackageManager.FEATURE_CONNECTION_SERVICE);
339     }
340 
setCallDiagnosticService(Instrumentation instrumentation, String packageName)341     public static String setCallDiagnosticService(Instrumentation instrumentation,
342             String packageName)
343             throws Exception {
344         return executeShellCommand(instrumentation, COMMAND_SET_CALL_DIAGNOSTIC_SERVICE
345                 + packageName);
346     }
347 
setDefaultDialer(Instrumentation instrumentation, String packageName)348     public static String setDefaultDialer(Instrumentation instrumentation, String packageName)
349             throws Exception {
350         return executeShellCommand(instrumentation, COMMAND_SET_DEFAULT_DIALER + packageName);
351     }
352 
setSystemDialerOverride(Instrumentation instrumentation)353     public static String setSystemDialerOverride(Instrumentation instrumentation) throws Exception {
354         return executeShellCommand(instrumentation, COMMAND_SET_SYSTEM_DIALER + INCALL_COMPONENT);
355     }
356 
clearSystemDialerOverride( Instrumentation instrumentation)357     public static String clearSystemDialerOverride(
358             Instrumentation instrumentation) throws Exception {
359         return executeShellCommand(instrumentation, COMMAND_SET_SYSTEM_DIALER + "default");
360     }
361 
setCtsPhoneAccountSuggestionService(Instrumentation instrumentation, ComponentName componentName)362     public static String setCtsPhoneAccountSuggestionService(Instrumentation instrumentation,
363             ComponentName componentName) throws Exception {
364         return executeShellCommand(instrumentation,
365                 COMMAND_SET_ACCT_SUGGESTION
366                         + (componentName == null ? "" : componentName.flattenToString()));
367     }
368 
getDefaultDialer(Instrumentation instrumentation)369     public static String getDefaultDialer(Instrumentation instrumentation) throws Exception {
370         return executeShellCommand(instrumentation, COMMAND_GET_DEFAULT_DIALER);
371     }
372 
getSystemDialer(Instrumentation instrumentation)373     public static String getSystemDialer(Instrumentation instrumentation) throws Exception {
374         return executeShellCommand(instrumentation, COMMAND_GET_SYSTEM_DIALER);
375     }
376 
enablePhoneAccount(Instrumentation instrumentation, PhoneAccountHandle handle)377     public static void enablePhoneAccount(Instrumentation instrumentation,
378             PhoneAccountHandle handle) throws Exception {
379         final ComponentName component = handle.getComponentName();
380         final long currentUserSerial = getCurrentUserSerialNumber(instrumentation);
381         executeShellCommand(instrumentation, COMMAND_ENABLE
382                 + component.getPackageName() + "/" + component.getClassName() + " "
383                 + handle.getId() + " " + currentUserSerial);
384     }
385 
registerSimPhoneAccount(Instrumentation instrumentation, PhoneAccountHandle handle, String label, String address)386     public static void registerSimPhoneAccount(Instrumentation instrumentation,
387             PhoneAccountHandle handle, String label, String address) throws Exception {
388         final ComponentName component = handle.getComponentName();
389         final long currentUserSerial = getCurrentUserSerialNumber(instrumentation);
390         executeShellCommand(instrumentation, COMMAND_REGISTER_SIM
391                 + component.getPackageName() + "/" + component.getClassName() + " "
392                 + handle.getId() + " " + currentUserSerial + " " + label + " " + address);
393     }
394 
registerEmergencyPhoneAccount(Instrumentation instrumentation, PhoneAccountHandle handle, String label, String address)395     public static void registerEmergencyPhoneAccount(Instrumentation instrumentation,
396             PhoneAccountHandle handle, String label, String address) throws Exception {
397         final ComponentName component = handle.getComponentName();
398         final long currentUserSerial = getCurrentUserSerialNumber(instrumentation);
399         executeShellCommand(instrumentation, COMMAND_REGISTER_SIM  + "-e "
400                 + component.getPackageName() + "/" + component.getClassName() + " "
401                 + handle.getId() + " " + currentUserSerial + " " + label + " " + address);
402     }
403 
setDefaultOutgoingPhoneAccount(Instrumentation instrumentation, PhoneAccountHandle handle)404     public static void setDefaultOutgoingPhoneAccount(Instrumentation instrumentation,
405             PhoneAccountHandle handle) throws Exception {
406         if (handle != null) {
407             final ComponentName component = handle.getComponentName();
408             final long currentUserSerial = getCurrentUserSerialNumber(instrumentation);
409             executeShellCommand(instrumentation, COMMAND_SET_DEFAULT_PHONE_ACCOUNT
410                     + component.getPackageName() + "/" + component.getClassName() + " "
411                     + handle.getId() + " " + currentUserSerial);
412         } else {
413             executeShellCommand(instrumentation, COMMAND_SET_DEFAULT_PHONE_ACCOUNT);
414         }
415     }
416 
waitOnAllHandlers(Instrumentation instrumentation)417     public static void waitOnAllHandlers(Instrumentation instrumentation) {
418         try {
419             executeShellCommand(instrumentation, COMMAND_WAIT_ON_HANDLERS);
420         } catch (Throwable t) {
421             throw new RuntimeException(t);
422         }
423     }
424 
waitOnLocalMainLooper(long timeoutMs)425     public static void waitOnLocalMainLooper(long timeoutMs) {
426         Handler mainHandler = new Handler(Looper.getMainLooper());
427         final CountDownLatch lock = new CountDownLatch(1);
428         mainHandler.post(lock::countDown);
429         while (lock.getCount() > 0) {
430             try {
431                 lock.await(timeoutMs, TimeUnit.MILLISECONDS);
432             } catch (InterruptedException e) {
433                 // do nothing
434             }
435         }
436     }
437 
addTestEmergencyNumber(Instrumentation instr, String testNumber)438     public static void addTestEmergencyNumber(Instrumentation instr,
439             String testNumber) throws Exception {
440         executeShellCommand(instr, COMMAND_ADD_TEST_EMERGENCY_NUMBER + testNumber);
441     }
442 
removeTestEmergencyNumber(Instrumentation instr, String number)443     public static void removeTestEmergencyNumber(Instrumentation instr,
444             String number) throws Exception {
445         executeShellCommand(instr, COMMAND_REMOVE_TEST_EMERGENCY_NUMBER + number);
446     }
447 
clearTestEmergencyNumbers(Instrumentation instr)448     public static void clearTestEmergencyNumbers(Instrumentation instr) throws Exception {
449         executeShellCommand(instr, COMMAND_CLEAR_TEST_EMERGENCY_NUMBERS);
450     }
451 
setTestEmergencyPhoneAccountPackageFilter(Instrumentation instr, Context context)452     public static void setTestEmergencyPhoneAccountPackageFilter(Instrumentation instr,
453             Context context) throws Exception {
454         executeShellCommand(instr, COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_NAME_FILTER
455                 + context.getPackageName());
456     }
457 
clearTestEmergencyPhoneAccountPackageFilter( Instrumentation instr)458     public static void clearTestEmergencyPhoneAccountPackageFilter(
459             Instrumentation instr) throws Exception {
460         executeShellCommand(instr, COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_NAME_FILTER);
461     }
462 
enableCompatCommand(Instrumentation instr, String commandName)463     public static void enableCompatCommand(Instrumentation instr,
464             String commandName) throws Exception {
465         String cmd = COMMAND_AM_COMPAT + "enable  --no-kill " + commandName + PACKAGE;
466         executeShellCommand(instr, cmd);
467     }
468 
disableCompatCommand(Instrumentation instr, String commandName)469     public static void disableCompatCommand(Instrumentation instr,
470             String commandName) throws Exception {
471         String cmd = COMMAND_AM_COMPAT + "disable  --no-kill " + commandName + PACKAGE;
472         executeShellCommand(instr, cmd);
473     }
474 
resetCompatCommand(Instrumentation instr, String commandName)475     public static void resetCompatCommand(Instrumentation instr,
476             String commandName) throws Exception {
477         String cmd = COMMAND_AM_COMPAT + "reset  --no-kill " + commandName + PACKAGE;
478         executeShellCommand(instr, cmd);
479     }
480 
481     /**
482      * Executes the given shell command and returns the output in a string. Note that even
483      * if we don't care about the output, we have to read the stream completely to make the
484      * command execute.
485      */
executeShellCommand(Instrumentation instrumentation, String command)486     public static String executeShellCommand(Instrumentation instrumentation,
487             String command) throws Exception {
488         final ParcelFileDescriptor pfd =
489                 instrumentation.getUiAutomation().executeShellCommand(command);
490         BufferedReader br = null;
491         try (InputStream in = new FileInputStream(pfd.getFileDescriptor())) {
492             br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
493             String str = null;
494             StringBuilder out = new StringBuilder();
495             while ((str = br.readLine()) != null) {
496                 out.append(str);
497             }
498             return out.toString();
499         } finally {
500             if (br != null) {
501                 closeQuietly(br);
502             }
503             closeQuietly(pfd);
504         }
505     }
506 
closeQuietly(AutoCloseable closeable)507     private static void closeQuietly(AutoCloseable closeable) {
508         if (closeable != null) {
509             try {
510                 closeable.close();
511             } catch (RuntimeException rethrown) {
512                 throw rethrown;
513             } catch (Exception ignored) {
514             }
515         }
516     }
517 
518     /**
519      * Waits for the {@link CountDownLatch} to count down to 0 and then returns without reseting
520      * the latch.
521      * @param lock the latch that the system will wait on.
522      * @return true if the latch was released successfully, false if the latch timed out before
523      * resetting.
524      */
waitForLatchCountDown(CountDownLatch lock)525     public static boolean waitForLatchCountDown(CountDownLatch lock) {
526         if (lock == null) {
527             return false;
528         }
529 
530         boolean success;
531         try {
532             success = lock.await(5000, TimeUnit.MILLISECONDS);
533         } catch (InterruptedException ie) {
534             return false;
535         }
536 
537         return success;
538     }
539 
540     /**
541      * Waits for the {@link CountDownLatch} to count down to 0 and then returns a new reset latch.
542      * @param lock The lock that will await a countDown to 0.
543      * @return a new reset {@link CountDownLatch} if the lock successfully counted down to 0 or
544      * null if the operation timed out.
545      */
waitForLock(CountDownLatch lock)546     public static CountDownLatch waitForLock(CountDownLatch lock) {
547         boolean success = waitForLatchCountDown(lock);
548         if (success) {
549             return new CountDownLatch(1);
550         } else {
551             return null;
552         }
553     }
554 
555     /**
556      * Adds a new incoming call.
557      *
558      * @param instrumentation the Instrumentation, used for shell command execution.
559      * @param telecomManager the TelecomManager.
560      * @param handle the PhoneAccountHandle associated with the call.
561      * @param address the incoming address.
562      * @return the new self-managed incoming call.
563      */
addIncomingCall(Instrumentation instrumentation, TelecomManager telecomManager, PhoneAccountHandle handle, Uri address)564     public static void addIncomingCall(Instrumentation instrumentation,
565                                        TelecomManager telecomManager, PhoneAccountHandle handle,
566                                        Uri address) {
567 
568         // Inform telecom of new incoming self-managed connection.
569         Bundle extras = new Bundle();
570         extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, address);
571         telecomManager.addNewIncomingCall(handle, extras);
572 
573         // Wait for Telecom to finish creating the new connection.
574         try {
575             waitOnAllHandlers(instrumentation);
576         } catch (Exception e) {
577             TestCase.fail("Failed to wait on handlers");
578         }
579     }
hasBluetoothFeature()580     public static boolean hasBluetoothFeature() {
581         return InstrumentationRegistry.getContext().getPackageManager().
582                 hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
583     }
makeBluetoothDevice(String address)584     public static BluetoothDevice makeBluetoothDevice(String address) {
585         if (!HAS_BLUETOOTH) return null;
586         Parcel p1 = Parcel.obtain();
587         p1.writeString(address);
588         p1.setDataPosition(0);
589         BluetoothDevice device = BluetoothDevice.CREATOR.createFromParcel(p1);
590         p1.recycle();
591         return device;
592     }
593 
594     /**
595      * Places a new outgoing call.
596      *
597      * @param telecomManager the TelecomManager.
598      * @param handle the PhoneAccountHandle associated with the call.
599      * @param address outgoing call address.
600      * @return the new self-managed outgoing call.
601      */
placeOutgoingCall(Instrumentation instrumentation, TelecomManager telecomManager, PhoneAccountHandle handle, Uri address)602     public static void placeOutgoingCall(Instrumentation instrumentation,
603                                           TelecomManager telecomManager, PhoneAccountHandle handle,
604                                           Uri address) {
605         // Inform telecom of new incoming self-managed connection.
606         Bundle extras = new Bundle();
607         extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, handle);
608         telecomManager.placeCall(address, extras);
609 
610         // Wait for Telecom to finish creating the new connection.
611         try {
612             waitOnAllHandlers(instrumentation);
613         } catch (Exception e) {
614             TestCase.fail("Failed to wait on handlers");
615         }
616     }
617 
618     /**
619      * Waits for a new SelfManagedConnection with the given address to be added.
620      * @param address The expected address.
621      * @return The SelfManagedConnection found.
622      */
waitForAndGetConnection(Uri address)623     public static SelfManagedConnection waitForAndGetConnection(Uri address) {
624         // Wait for creation of the new connection.
625         if (!CtsSelfManagedConnectionService.waitForBinding()) {
626             TestCase.fail("Could not bind to Self-Managed ConnectionService");
627         }
628         CtsSelfManagedConnectionService connectionService =
629                 CtsSelfManagedConnectionService.getConnectionService();
630         TestCase.assertTrue(connectionService.waitForUpdate(
631                 CtsSelfManagedConnectionService.CONNECTION_CREATED_LOCK));
632 
633         Optional<SelfManagedConnection> connectionOptional = connectionService.getConnections()
634                 .stream()
635                 .filter(connection -> address.equals(connection.getAddress()))
636                 .findFirst();
637         assert(connectionOptional.isPresent());
638         return connectionOptional.get();
639     }
640 
641     /**
642      * Utility class used to track the number of times a callback was invoked, and the arguments it
643      * was invoked with. This class is prefixed Invoke rather than the more typical Call for
644      * disambiguation purposes.
645      */
646     public static final class InvokeCounter {
647         private final String mName;
648         private final Object mLock = new Object();
649         private final ArrayList<Object[]> mInvokeArgs = new ArrayList<>();
650 
651         private int mInvokeCount;
652 
InvokeCounter(String callbackName)653         public InvokeCounter(String callbackName) {
654             mName = callbackName;
655         }
656 
invoke(Object... args)657         public void invoke(Object... args) {
658             synchronized (mLock) {
659                 mInvokeCount++;
660                 mInvokeArgs.add(args);
661                 mLock.notifyAll();
662             }
663         }
664 
getArgs(int index)665         public Object[] getArgs(int index) {
666             synchronized (mLock) {
667                 return mInvokeArgs.get(index);
668             }
669         }
670 
getInvokeCount()671         public int getInvokeCount() {
672             synchronized (mLock) {
673                 return mInvokeCount;
674             }
675         }
676 
waitForCount(int count)677         public void waitForCount(int count) {
678             waitForCount(count, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
679         }
680 
waitForCount(int count, long timeoutMillis)681         public void waitForCount(int count, long timeoutMillis) {
682             waitForCount(count, timeoutMillis, null);
683         }
684 
waitForCount(long timeoutMillis)685         public void waitForCount(long timeoutMillis) {
686              synchronized (mLock) {
687              try {
688                   mLock.wait(timeoutMillis);
689              }catch (InterruptedException ex) {
690                   ex.printStackTrace();
691              }
692            }
693         }
694 
waitForCount(int count, long timeoutMillis, String message)695         public void waitForCount(int count, long timeoutMillis, String message) {
696             synchronized (mLock) {
697                 final long startTimeMillis = SystemClock.uptimeMillis();
698                 while (mInvokeCount < count) {
699                     try {
700                         final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
701                         final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis;
702                         if (remainingTimeMillis <= 0) {
703                             if (message != null) {
704                                 TestCase.fail(message);
705                             } else {
706                                 TestCase.fail(String.format("Expected %s to be called %d times.",
707                                         mName, count));
708                             }
709                         }
710                         mLock.wait(timeoutMillis);
711                     } catch (InterruptedException ie) {
712                         /* ignore */
713                     }
714                 }
715             }
716         }
717 
718         /**
719          * Waits for a predicate to return {@code true} within the specified timeout.  Uses the
720          * {@link #mLock} for this {@link InvokeCounter} to eliminate the need to perform busy-wait.
721          * @param predicate The predicate.
722          * @param timeoutMillis The timeout.
723          */
waitForPredicate(Predicate predicate, long timeoutMillis)724         public void waitForPredicate(Predicate predicate, long timeoutMillis) {
725             synchronized (mLock) {
726                 long startTimeMillis = SystemClock.uptimeMillis();
727                 long elapsedTimeMillis = 0;
728                 long remainingTimeMillis = timeoutMillis;
729                 Object foundValue = null;
730                 boolean wasFound = false;
731                 do {
732                     try {
733                         mLock.wait(timeoutMillis);
734                         foundValue = (mInvokeArgs.get(mInvokeArgs.size()-1))[0];
735                         wasFound = predicate.test(foundValue);
736                         elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
737                         remainingTimeMillis = timeoutMillis - elapsedTimeMillis;
738                     } catch (InterruptedException ie) {
739                         /* ignore */
740                     }
741                 } while (!wasFound && remainingTimeMillis > 0);
742                 if (wasFound) {
743                     return;
744                 } else if (remainingTimeMillis <= 0) {
745                     TestCase.fail("Expected value not found within time limit");
746                 }
747             }
748         }
749 
clearArgs()750         public void clearArgs() {
751             synchronized (mLock) {
752                 mInvokeArgs.clear();
753             }
754         }
755 
reset()756         public void reset() {
757             synchronized (mLock) {
758                 clearArgs();
759                 mInvokeCount = 0;
760             }
761         }
762     }
763 
getCurrentUserSerialNumber(Instrumentation instrumentation)764     private static long getCurrentUserSerialNumber(Instrumentation instrumentation) {
765         UserManager userManager =
766                 instrumentation.getContext().getSystemService(UserManager.class);
767         return userManager.getSerialNumberForUser(Process.myUserHandle());
768     }
769 
770 
771 
insertContact(ContentResolver contentResolver, String phoneNumber)772     public static Uri insertContact(ContentResolver contentResolver, String phoneNumber)
773             throws Exception {
774         ArrayList<ContentProviderOperation> ops = new ArrayList<>();
775         ops.add(ContentProviderOperation
776                 .newInsert(ContactsContract.RawContacts.CONTENT_URI)
777                 .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, "test_type")
778                 .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, "test_name")
779                 .build());
780         ops.add(ContentProviderOperation
781                 .newInsert(ContactsContract.Data.CONTENT_URI)
782                 .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
783                 .withValue(ContactsContract.Data.MIMETYPE,
784                         ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
785                 .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, "test")
786                 .build());
787         ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
788                 .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
789                 .withValue(ContactsContract.Data.MIMETYPE,
790                         ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
791                 .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phoneNumber)
792                 .withValue(ContactsContract.CommonDataKinds.Phone.TYPE,
793                         ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE)
794                 .withYieldAllowed(true)
795                 .build());
796         return contentResolver.applyBatch(ContactsContract.AUTHORITY, ops)[0].uri;
797     }
798 
deleteContact(ContentResolver contentResolver, Uri deleteUri)799     public static int deleteContact(ContentResolver contentResolver, Uri deleteUri) {
800         return contentResolver.delete(deleteUri, null, null);
801     }
802 }
803