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