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