Home | History | Annotate | Download | only in cts
      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.Context;
     22 import android.content.pm.PackageManager;
     23 import android.graphics.Color;
     24 import android.net.Uri;
     25 import android.os.Build;
     26 import android.os.Bundle;
     27 import android.os.Handler;
     28 import android.os.Looper;
     29 import android.os.Parcel;
     30 import android.os.ParcelFileDescriptor;
     31 import android.os.Process;
     32 import android.os.SystemClock;
     33 import android.os.UserManager;
     34 import android.telecom.PhoneAccount;
     35 import android.telecom.PhoneAccountHandle;
     36 import android.telecom.TelecomManager;
     37 
     38 import androidx.test.InstrumentationRegistry;
     39 
     40 import junit.framework.TestCase;
     41 
     42 import java.io.BufferedReader;
     43 import java.io.FileInputStream;
     44 import java.io.InputStream;
     45 import java.io.InputStreamReader;
     46 import java.nio.charset.StandardCharsets;
     47 import java.util.ArrayList;
     48 import java.util.Optional;
     49 import java.util.concurrent.CountDownLatch;
     50 import java.util.concurrent.TimeUnit;
     51 import java.util.function.Predicate;
     52 
     53 public class TestUtils {
     54     static final String TAG = "TelecomCTSTests";
     55     static final boolean HAS_TELECOM = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
     56     static final long WAIT_FOR_STATE_CHANGE_TIMEOUT_MS = 10000;
     57     static final long WAIT_FOR_CALL_ADDED_TIMEOUT_S = 15;
     58     static final long WAIT_FOR_STATE_CHANGE_TIMEOUT_CALLBACK = 50;
     59     static final long WAIT_FOR_PHONE_STATE_LISTENER_REGISTERED_TIMEOUT_S = 15;
     60     static final long WAIT_FOR_PHONE_STATE_LISTENER_CALLBACK_TIMEOUT_S = 15;
     61     static final boolean HAS_BLUETOOTH = hasBluetoothFeature();
     62     static final BluetoothDevice BLUETOOTH_DEVICE1 = makeBluetoothDevice("00:00:00:00:00:01");
     63     static final BluetoothDevice BLUETOOTH_DEVICE2 = makeBluetoothDevice("00:00:00:00:00:02");
     64 
     65     // Non-final to allow modification by tests not in this package (e.g. permission-related
     66     // tests in the Telecom2 test package.
     67     public static String PACKAGE = "android.telecom.cts";
     68     public static final String TEST_URI_SCHEME = "foobuzz";
     69     public static final String COMPONENT = "android.telecom.cts.CtsConnectionService";
     70     public static final String SELF_MANAGED_COMPONENT =
     71             "android.telecom.cts.CtsSelfManagedConnectionService";
     72     public static final String REMOTE_COMPONENT = "android.telecom.cts.CtsRemoteConnectionService";
     73     public static final String ACCOUNT_ID_1 = "xtstest_CALL_PROVIDER_ID_1";
     74     public static final String ACCOUNT_ID_2 = "xtstest_CALL_PROVIDER_ID_2";
     75     public static final String EXTRA_PHONE_NUMBER = "android.telecom.cts.extra.PHONE_NUMBER";
     76     public static final PhoneAccountHandle TEST_PHONE_ACCOUNT_HANDLE =
     77             new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), ACCOUNT_ID_1);
     78     public static final PhoneAccountHandle TEST_PHONE_ACCOUNT_HANDLE_2 =
     79             new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), ACCOUNT_ID_2);
     80     public static final String DEFAULT_TEST_ACCOUNT_1_ID = "ctstest_DEFAULT_TEST_ID_1";
     81     public static final String DEFAULT_TEST_ACCOUNT_2_ID = "ctstest_DEFAULT_TEST_ID_2";
     82     public static final PhoneAccountHandle TEST_DEFAULT_PHONE_ACCOUNT_HANDLE_1 =
     83             new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT),
     84                     DEFAULT_TEST_ACCOUNT_1_ID);
     85     public static final PhoneAccountHandle TEST_DEFAULT_PHONE_ACCOUNT_HANDLE_2 =
     86             new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT),
     87                     DEFAULT_TEST_ACCOUNT_2_ID);
     88     public static final PhoneAccountHandle TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE =
     89             new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), "handoverFrom");
     90     public static final PhoneAccountHandle TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE =
     91             new PhoneAccountHandle(new ComponentName(PACKAGE, SELF_MANAGED_COMPONENT),
     92                     "handoverTo");
     93     public static final String REMOTE_ACCOUNT_ID = "xtstest_REMOTE_CALL_PROVIDER_ID";
     94     public static final String SELF_MANAGED_ACCOUNT_ID_1 = "ctstest_SELF_MANAGED_ID_1";
     95     public static final PhoneAccountHandle TEST_SELF_MANAGED_HANDLE_1 =
     96             new PhoneAccountHandle(new ComponentName(PACKAGE, SELF_MANAGED_COMPONENT),
     97                     SELF_MANAGED_ACCOUNT_ID_1);
     98     public static final String SELF_MANAGED_ACCOUNT_ID_2 = "ctstest_SELF_MANAGED_ID_2";
     99     public static final PhoneAccountHandle TEST_SELF_MANAGED_HANDLE_2 =
    100             new PhoneAccountHandle(new ComponentName(PACKAGE, SELF_MANAGED_COMPONENT),
    101                     SELF_MANAGED_ACCOUNT_ID_2);
    102     public static final String SELF_MANAGED_ACCOUNT_ID_3 = "ctstest_SELF_MANAGED_ID_3";
    103     public static final PhoneAccountHandle TEST_SELF_MANAGED_HANDLE_3 =
    104             new PhoneAccountHandle(new ComponentName(PACKAGE, SELF_MANAGED_COMPONENT),
    105                     SELF_MANAGED_ACCOUNT_ID_3);
    106 
    107     public static final String ACCOUNT_LABEL = "CTSConnectionService";
    108     public static final PhoneAccount TEST_PHONE_ACCOUNT = PhoneAccount.builder(
    109             TEST_PHONE_ACCOUNT_HANDLE, ACCOUNT_LABEL)
    110             .setAddress(Uri.parse("tel:555-TEST"))
    111             .setSubscriptionAddress(Uri.parse("tel:555-TEST"))
    112             .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER |
    113                     PhoneAccount.CAPABILITY_VIDEO_CALLING |
    114                     PhoneAccount.CAPABILITY_RTT |
    115                     PhoneAccount.CAPABILITY_CONNECTION_MANAGER)
    116             .setHighlightColor(Color.RED)
    117             .setShortDescription(ACCOUNT_LABEL)
    118             .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
    119             .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
    120             .build();
    121 
    122     public static final PhoneAccount TEST_PHONE_ACCOUNT_2 = PhoneAccount.builder(
    123             TEST_PHONE_ACCOUNT_HANDLE_2, ACCOUNT_LABEL + "2")
    124             .setAddress(Uri.parse("tel:555-TEST2"))
    125             .setSubscriptionAddress(Uri.parse("tel:555-TEST2"))
    126             .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER |
    127                     PhoneAccount.CAPABILITY_VIDEO_CALLING |
    128                     PhoneAccount.CAPABILITY_RTT |
    129                     PhoneAccount.CAPABILITY_CONNECTION_MANAGER)
    130             .setHighlightColor(Color.BLUE)
    131             .setShortDescription(ACCOUNT_LABEL)
    132             .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
    133             .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
    134             .build();
    135 
    136     public static final PhoneAccount TEST_DEFAULT_PHONE_ACCOUNT_1 = PhoneAccount.builder(
    137             TEST_DEFAULT_PHONE_ACCOUNT_HANDLE_1, "Default Test 1")
    138             .setAddress(Uri.parse("foobuzz:testuri1"))
    139             .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
    140             .setHighlightColor(Color.RED)
    141             .setShortDescription("Default Test 1")
    142             .addSupportedUriScheme(TEST_URI_SCHEME)
    143             .build();
    144     public static final PhoneAccount TEST_DEFAULT_PHONE_ACCOUNT_2 = PhoneAccount.builder(
    145             TEST_DEFAULT_PHONE_ACCOUNT_HANDLE_2, "Default Test 2")
    146             .setAddress(Uri.parse("foobuzz:testuri2"))
    147             .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
    148             .setHighlightColor(Color.RED)
    149             .setShortDescription("Default Test 2")
    150             .addSupportedUriScheme(TEST_URI_SCHEME)
    151             .build();
    152     private static final Bundle SUPPORTS_HANDOVER_FROM_EXTRAS = new Bundle();
    153     private static final Bundle SUPPORTS_HANDOVER_TO_EXTRAS = new Bundle();
    154     static {
    155         SUPPORTS_HANDOVER_FROM_EXTRAS.putBoolean(PhoneAccount.EXTRA_SUPPORTS_HANDOVER_FROM, true);
    156         SUPPORTS_HANDOVER_TO_EXTRAS.putBoolean(PhoneAccount.EXTRA_SUPPORTS_HANDOVER_TO, true);
    157     }
    158     public static final PhoneAccount TEST_PHONE_ACCOUNT_HANDOVER_SRC = PhoneAccount.builder(
    159             TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE, ACCOUNT_LABEL)
    160             .setAddress(Uri.parse("tel:555-TEST"))
    161             .setExtras(SUPPORTS_HANDOVER_FROM_EXTRAS)
    162             .setSubscriptionAddress(Uri.parse("tel:555-TEST"))
    163             .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
    164             .setHighlightColor(Color.BLUE)
    165             .setShortDescription(ACCOUNT_LABEL)
    166             .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
    167             .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
    168             .build();
    169     public static final PhoneAccount TEST_PHONE_ACCOUNT_HANDOVER_DEST = PhoneAccount.builder(
    170             TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE, ACCOUNT_LABEL)
    171             .setAddress(Uri.parse("tel:555-TEST"))
    172             .setExtras(SUPPORTS_HANDOVER_TO_EXTRAS)
    173             .setSubscriptionAddress(Uri.parse("tel:555-TEST"))
    174             .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)
    175             .setHighlightColor(Color.MAGENTA)
    176             .setShortDescription(ACCOUNT_LABEL)
    177             .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
    178             .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
    179             .build();
    180     public static final String REMOTE_ACCOUNT_LABEL = "CTSRemoteConnectionService";
    181     public static final String SELF_MANAGED_ACCOUNT_LABEL = "android.telecom.cts";
    182     public static final PhoneAccount TEST_SELF_MANAGED_PHONE_ACCOUNT_3 = PhoneAccount.builder(
    183             TEST_SELF_MANAGED_HANDLE_3, SELF_MANAGED_ACCOUNT_LABEL)
    184             .setAddress(Uri.fromParts(TEST_URI_SCHEME, "test (at) test.com", null))
    185             .setSubscriptionAddress(Uri.fromParts(TEST_URI_SCHEME, "test (at) test.com", null))
    186             .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED |
    187                     PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING |
    188                     PhoneAccount.CAPABILITY_VIDEO_CALLING)
    189             .setHighlightColor(Color.BLUE)
    190             .setShortDescription(SELF_MANAGED_ACCOUNT_LABEL)
    191             .addSupportedUriScheme(TEST_URI_SCHEME)
    192             .build();
    193     public static final Bundle SELF_MANAGED_ACCOUNT_2_EXTRAS;
    194     static {
    195         SELF_MANAGED_ACCOUNT_2_EXTRAS = new Bundle();
    196         SELF_MANAGED_ACCOUNT_2_EXTRAS.putBoolean(PhoneAccount.EXTRA_LOG_SELF_MANAGED_CALLS, true);
    197     }
    198 
    199     public static final PhoneAccount TEST_SELF_MANAGED_PHONE_ACCOUNT_2 = PhoneAccount.builder(
    200             TEST_SELF_MANAGED_HANDLE_2, SELF_MANAGED_ACCOUNT_LABEL)
    201             .setAddress(Uri.parse("sip:test (at) test.com"))
    202             .setSubscriptionAddress(Uri.parse("sip:test (at) test.com"))
    203             .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED |
    204                     PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING |
    205                     PhoneAccount.CAPABILITY_VIDEO_CALLING)
    206             .setHighlightColor(Color.BLUE)
    207             .setShortDescription(SELF_MANAGED_ACCOUNT_LABEL)
    208             .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
    209             .addSupportedUriScheme(PhoneAccount.SCHEME_SIP)
    210             .setExtras(SELF_MANAGED_ACCOUNT_2_EXTRAS)
    211             .build();
    212     public static final PhoneAccount TEST_SELF_MANAGED_PHONE_ACCOUNT_1 = PhoneAccount.builder(
    213             TEST_SELF_MANAGED_HANDLE_1, SELF_MANAGED_ACCOUNT_LABEL)
    214             .setAddress(Uri.parse("sip:test (at) test.com"))
    215             .setSubscriptionAddress(Uri.parse("sip:test (at) test.com"))
    216             .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED |
    217                     PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING |
    218                     PhoneAccount.CAPABILITY_VIDEO_CALLING)
    219             .setHighlightColor(Color.BLUE)
    220             .setShortDescription(SELF_MANAGED_ACCOUNT_LABEL)
    221             .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
    222             .addSupportedUriScheme(PhoneAccount.SCHEME_SIP)
    223             .build();
    224 
    225     private static final String COMMAND_SET_DEFAULT_DIALER = "telecom set-default-dialer ";
    226 
    227     private static final String COMMAND_GET_DEFAULT_DIALER = "telecom get-default-dialer";
    228 
    229     private static final String COMMAND_GET_SYSTEM_DIALER = "telecom get-system-dialer";
    230 
    231     private static final String COMMAND_ENABLE = "telecom set-phone-account-enabled ";
    232 
    233     private static final String COMMAND_SET_ACCT_SUGGESTION =
    234             "telecom set-phone-acct-suggestion-component ";
    235 
    236     private static final String COMMAND_REGISTER_SIM = "telecom register-sim-phone-account ";
    237 
    238     private static final String COMMAND_SET_DEFAULT_PHONE_ACCOUNT =
    239             "telecom set-user-selected-outgoing-phone-account ";
    240 
    241     private static final String COMMAND_WAIT_ON_HANDLERS = "telecom wait-on-handlers";
    242 
    243     public static final String MERGE_CALLER_NAME = "calls-merged";
    244     public static final String SWAP_CALLER_NAME = "calls-swapped";
    245 
    246     public static boolean shouldTestTelecom(Context context) {
    247         if (!HAS_TELECOM) {
    248             return false;
    249         }
    250         final PackageManager pm = context.getPackageManager();
    251         return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) &&
    252                 pm.hasSystemFeature(PackageManager.FEATURE_CONNECTION_SERVICE);
    253     }
    254 
    255     public static String setDefaultDialer(Instrumentation instrumentation, String packageName)
    256             throws Exception {
    257         return executeShellCommand(instrumentation, COMMAND_SET_DEFAULT_DIALER + packageName);
    258     }
    259 
    260     public static String setCtsPhoneAccountSuggestionService(Instrumentation instrumentation,
    261             ComponentName componentName) throws Exception {
    262         return executeShellCommand(instrumentation,
    263                 COMMAND_SET_ACCT_SUGGESTION
    264                         + (componentName == null ? "" : componentName.flattenToString()));
    265     }
    266 
    267     public static String getDefaultDialer(Instrumentation instrumentation) throws Exception {
    268         return executeShellCommand(instrumentation, COMMAND_GET_DEFAULT_DIALER);
    269     }
    270 
    271     public static String getSystemDialer(Instrumentation instrumentation) throws Exception {
    272         return executeShellCommand(instrumentation, COMMAND_GET_SYSTEM_DIALER);
    273     }
    274 
    275     public static void enablePhoneAccount(Instrumentation instrumentation,
    276             PhoneAccountHandle handle) throws Exception {
    277         final ComponentName component = handle.getComponentName();
    278         final long currentUserSerial = getCurrentUserSerialNumber(instrumentation);
    279         executeShellCommand(instrumentation, COMMAND_ENABLE
    280                 + component.getPackageName() + "/" + component.getClassName() + " "
    281                 + handle.getId() + " " + currentUserSerial);
    282     }
    283 
    284     public static void registerSimPhoneAccount(Instrumentation instrumentation,
    285             PhoneAccountHandle handle, String label, String address) throws Exception {
    286         final ComponentName component = handle.getComponentName();
    287         final long currentUserSerial = getCurrentUserSerialNumber(instrumentation);
    288         executeShellCommand(instrumentation, COMMAND_REGISTER_SIM
    289                 + component.getPackageName() + "/" + component.getClassName() + " "
    290                 + handle.getId() + " " + currentUserSerial + " " + label + " " + address);
    291     }
    292 
    293     public static void setDefaultOutgoingPhoneAccount(Instrumentation instrumentation,
    294             PhoneAccountHandle handle) throws Exception {
    295         if (handle != null) {
    296             final ComponentName component = handle.getComponentName();
    297             final long currentUserSerial = getCurrentUserSerialNumber(instrumentation);
    298             executeShellCommand(instrumentation, COMMAND_SET_DEFAULT_PHONE_ACCOUNT
    299                     + component.getPackageName() + "/" + component.getClassName() + " "
    300                     + handle.getId() + " " + currentUserSerial);
    301         } else {
    302             executeShellCommand(instrumentation, COMMAND_SET_DEFAULT_PHONE_ACCOUNT);
    303         }
    304     }
    305 
    306     public static void waitOnAllHandlers(Instrumentation instrumentation) throws Exception {
    307         executeShellCommand(instrumentation, COMMAND_WAIT_ON_HANDLERS);
    308     }
    309 
    310     public static void waitOnLocalMainLooper(long timeoutMs) {
    311         Handler mainHandler = new Handler(Looper.getMainLooper());
    312         final CountDownLatch lock = new CountDownLatch(1);
    313         mainHandler.post(lock::countDown);
    314         while (lock.getCount() > 0) {
    315             try {
    316                 lock.await(timeoutMs, TimeUnit.MILLISECONDS);
    317             } catch (InterruptedException e) {
    318                 // do nothing
    319             }
    320         }
    321     }
    322 
    323     /**
    324      * Executes the given shell command and returns the output in a string. Note that even
    325      * if we don't care about the output, we have to read the stream completely to make the
    326      * command execute.
    327      */
    328     public static String executeShellCommand(Instrumentation instrumentation,
    329             String command) throws Exception {
    330         final ParcelFileDescriptor pfd =
    331                 instrumentation.getUiAutomation().executeShellCommand(command);
    332         BufferedReader br = null;
    333         try (InputStream in = new FileInputStream(pfd.getFileDescriptor())) {
    334             br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
    335             String str = null;
    336             StringBuilder out = new StringBuilder();
    337             while ((str = br.readLine()) != null) {
    338                 out.append(str);
    339             }
    340             return out.toString();
    341         } finally {
    342             if (br != null) {
    343                 closeQuietly(br);
    344             }
    345             closeQuietly(pfd);
    346         }
    347     }
    348 
    349     private static void closeQuietly(AutoCloseable closeable) {
    350         if (closeable != null) {
    351             try {
    352                 closeable.close();
    353             } catch (RuntimeException rethrown) {
    354                 throw rethrown;
    355             } catch (Exception ignored) {
    356             }
    357         }
    358     }
    359 
    360     /**
    361      * Waits for the {@link CountDownLatch} to count down to 0 and then returns without reseting
    362      * the latch.
    363      * @param lock the latch that the system will wait on.
    364      * @return true if the latch was released successfully, false if the latch timed out before
    365      * resetting.
    366      */
    367     public static boolean waitForLatchCountDown(CountDownLatch lock) {
    368         if (lock == null) {
    369             return false;
    370         }
    371 
    372         boolean success;
    373         try {
    374             success = lock.await(5000, TimeUnit.MILLISECONDS);
    375         } catch (InterruptedException ie) {
    376             return false;
    377         }
    378 
    379         return success;
    380     }
    381 
    382     /**
    383      * Waits for the {@link CountDownLatch} to count down to 0 and then returns a new reset latch.
    384      * @param lock The lock that will await a countDown to 0.
    385      * @return a new reset {@link CountDownLatch} if the lock successfully counted down to 0 or
    386      * null if the operation timed out.
    387      */
    388     public static CountDownLatch waitForLock(CountDownLatch lock) {
    389         boolean success = waitForLatchCountDown(lock);
    390         if (success) {
    391             return new CountDownLatch(1);
    392         } else {
    393             return null;
    394         }
    395     }
    396 
    397     /**
    398      * Adds a new incoming call.
    399      *
    400      * @param instrumentation the Instrumentation, used for shell command execution.
    401      * @param telecomManager the TelecomManager.
    402      * @param handle the PhoneAccountHandle associated with the call.
    403      * @param address the incoming address.
    404      * @return the new self-managed incoming call.
    405      */
    406     public static void addIncomingCall(Instrumentation instrumentation,
    407                                        TelecomManager telecomManager, PhoneAccountHandle handle,
    408                                        Uri address) {
    409 
    410         // Inform telecom of new incoming self-managed connection.
    411         Bundle extras = new Bundle();
    412         extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, address);
    413         telecomManager.addNewIncomingCall(handle, extras);
    414 
    415         // Wait for Telecom to finish creating the new connection.
    416         try {
    417             waitOnAllHandlers(instrumentation);
    418         } catch (Exception e) {
    419             TestCase.fail("Failed to wait on handlers");
    420         }
    421     }
    422     public static boolean hasBluetoothFeature() {
    423         return InstrumentationRegistry.getContext().getPackageManager().
    424                 hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
    425     }
    426     public static BluetoothDevice makeBluetoothDevice(String address) {
    427         if (!HAS_BLUETOOTH) return null;
    428         Parcel p1 = Parcel.obtain();
    429         p1.writeString(address);
    430         p1.setDataPosition(0);
    431         BluetoothDevice device = BluetoothDevice.CREATOR.createFromParcel(p1);
    432         p1.recycle();
    433         return device;
    434     }
    435 
    436     /**
    437      * Places a new outgoing call.
    438      *
    439      * @param telecomManager the TelecomManager.
    440      * @param handle the PhoneAccountHandle associated with the call.
    441      * @param address outgoing call address.
    442      * @return the new self-managed outgoing call.
    443      */
    444     public static void placeOutgoingCall(Instrumentation instrumentation,
    445                                           TelecomManager telecomManager, PhoneAccountHandle handle,
    446                                           Uri address) {
    447         // Inform telecom of new incoming self-managed connection.
    448         Bundle extras = new Bundle();
    449         extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, handle);
    450         telecomManager.placeCall(address, extras);
    451 
    452         // Wait for Telecom to finish creating the new connection.
    453         try {
    454             waitOnAllHandlers(instrumentation);
    455         } catch (Exception e) {
    456             TestCase.fail("Failed to wait on handlers");
    457         }
    458     }
    459 
    460     /**
    461      * Waits for a new SelfManagedConnection with the given address to be added.
    462      * @param address The expected address.
    463      * @return The SelfManagedConnection found.
    464      */
    465     public static SelfManagedConnection waitForAndGetConnection(Uri address) {
    466         // Wait for creation of the new connection.
    467         if (!CtsSelfManagedConnectionService.waitForBinding()) {
    468             TestCase.fail("Could not bind to Self-Managed ConnectionService");
    469         }
    470         CtsSelfManagedConnectionService connectionService =
    471                 CtsSelfManagedConnectionService.getConnectionService();
    472         TestCase.assertTrue(connectionService.waitForUpdate(
    473                 CtsSelfManagedConnectionService.CONNECTION_CREATED_LOCK));
    474 
    475         Optional<SelfManagedConnection> connectionOptional = connectionService.getConnections()
    476                 .stream()
    477                 .filter(connection -> address.equals(connection.getAddress()))
    478                 .findFirst();
    479         assert(connectionOptional.isPresent());
    480         return connectionOptional.get();
    481     }
    482 
    483     /**
    484      * Utility class used to track the number of times a callback was invoked, and the arguments it
    485      * was invoked with. This class is prefixed Invoke rather than the more typical Call for
    486      * disambiguation purposes.
    487      */
    488     public static final class InvokeCounter {
    489         private final String mName;
    490         private final Object mLock = new Object();
    491         private final ArrayList<Object[]> mInvokeArgs = new ArrayList<>();
    492 
    493         private int mInvokeCount;
    494 
    495         public InvokeCounter(String callbackName) {
    496             mName = callbackName;
    497         }
    498 
    499         public void invoke(Object... args) {
    500             synchronized (mLock) {
    501                 mInvokeCount++;
    502                 mInvokeArgs.add(args);
    503                 mLock.notifyAll();
    504             }
    505         }
    506 
    507         public Object[] getArgs(int index) {
    508             synchronized (mLock) {
    509                 return mInvokeArgs.get(index);
    510             }
    511         }
    512 
    513         public int getInvokeCount() {
    514             synchronized (mLock) {
    515                 return mInvokeCount;
    516             }
    517         }
    518 
    519         public void waitForCount(int count) {
    520             waitForCount(count, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
    521         }
    522 
    523         public void waitForCount(int count, long timeoutMillis) {
    524             waitForCount(count, timeoutMillis, null);
    525         }
    526 
    527         public void waitForCount(long timeoutMillis) {
    528              synchronized (mLock) {
    529              try {
    530                   mLock.wait(timeoutMillis);
    531              }catch (InterruptedException ex) {
    532                   ex.printStackTrace();
    533              }
    534            }
    535         }
    536 
    537         public void waitForCount(int count, long timeoutMillis, String message) {
    538             synchronized (mLock) {
    539                 final long startTimeMillis = SystemClock.uptimeMillis();
    540                 while (mInvokeCount < count) {
    541                     try {
    542                         final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
    543                         final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis;
    544                         if (remainingTimeMillis <= 0) {
    545                             if (message != null) {
    546                                 TestCase.fail(message);
    547                             } else {
    548                                 TestCase.fail(String.format("Expected %s to be called %d times.",
    549                                         mName, count));
    550                             }
    551                         }
    552                         mLock.wait(timeoutMillis);
    553                     } catch (InterruptedException ie) {
    554                         /* ignore */
    555                     }
    556                 }
    557             }
    558         }
    559 
    560         /**
    561          * Waits for a predicate to return {@code true} within the specified timeout.  Uses the
    562          * {@link #mLock} for this {@link InvokeCounter} to eliminate the need to perform busy-wait.
    563          * @param predicate The predicate.
    564          * @param timeoutMillis The timeout.
    565          */
    566         public void waitForPredicate(Predicate predicate, long timeoutMillis) {
    567             synchronized (mLock) {
    568                 long startTimeMillis = SystemClock.uptimeMillis();
    569                 long elapsedTimeMillis = 0;
    570                 long remainingTimeMillis = timeoutMillis;
    571                 Object foundValue = null;
    572                 boolean wasFound = false;
    573                 do {
    574                     try {
    575                         mLock.wait(timeoutMillis);
    576                         foundValue = (mInvokeArgs.get(mInvokeArgs.size()-1))[0];
    577                         wasFound = predicate.test(foundValue);
    578                         elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
    579                         remainingTimeMillis = timeoutMillis - elapsedTimeMillis;
    580                     } catch (InterruptedException ie) {
    581                         /* ignore */
    582                     }
    583                 } while (!wasFound && remainingTimeMillis > 0);
    584                 if (wasFound) {
    585                     return;
    586                 } else if (remainingTimeMillis <= 0) {
    587                     TestCase.fail("Expected value not found within time limit");
    588                 }
    589             }
    590         }
    591 
    592         public void clearArgs() {
    593             synchronized (mLock) {
    594                 mInvokeArgs.clear();
    595             }
    596         }
    597     }
    598 
    599     private static long getCurrentUserSerialNumber(Instrumentation instrumentation) {
    600         UserManager userManager =
    601                 instrumentation.getContext().getSystemService(UserManager.class);
    602         return userManager.getSerialNumberForUser(Process.myUserHandle());
    603     }
    604 }
    605