1/*
2 * Copyright (C) 2017 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
17package com.android.internal.telephony.ims;
18
19import static junit.framework.Assert.assertEquals;
20import static junit.framework.Assert.assertFalse;
21import static junit.framework.Assert.assertTrue;
22
23import static org.mockito.Matchers.any;
24import static org.mockito.Matchers.anyInt;
25import static org.mockito.Matchers.eq;
26import static org.mockito.Mockito.never;
27import static org.mockito.Mockito.times;
28import static org.mockito.Mockito.verify;
29import static org.mockito.Mockito.when;
30
31import android.content.ComponentName;
32import android.content.Context;
33import android.content.Intent;
34import android.content.ServiceConnection;
35import android.os.Handler;
36import android.os.IBinder;
37import android.os.Looper;
38import android.os.RemoteException;
39import android.support.test.filters.FlakyTest;
40import android.support.test.runner.AndroidJUnit4;
41import android.telephony.ims.ImsService;
42import android.telephony.ims.stub.ImsFeatureConfiguration;
43
44import com.android.ims.internal.IImsServiceFeatureCallback;
45
46import org.junit.After;
47import org.junit.Before;
48import org.junit.Ignore;
49import org.junit.Test;
50import org.junit.runner.RunWith;
51import org.mockito.ArgumentCaptor;
52import org.mockito.Mock;
53import org.mockito.Spy;
54
55import java.util.HashSet;
56
57/**
58 * Unit tests for ImsServiceController
59 */
60@RunWith(AndroidJUnit4.class)
61@Ignore
62public class ImsServiceControllerTest extends ImsTestBase {
63
64    private static final ImsServiceController.RebindRetry REBIND_RETRY =
65            new ImsServiceController.RebindRetry() {
66        @Override
67        public long getStartDelay() {
68            return 50;
69        }
70
71        @Override
72        public long getMaximumDelay() {
73            return 1000;
74        }
75    };
76
77    @Spy TestImsServiceControllerAdapter mMockServiceControllerBinder;
78    @Mock ImsServiceController.ImsServiceControllerCallbacks mMockCallbacks;
79    @Mock IImsServiceFeatureCallback mMockProxyCallbacks;
80    @Mock Context mMockContext;
81    private final ComponentName mTestComponentName = new ComponentName("TestPkg",
82            "ImsServiceControllerTest");
83    private final Handler mHandler = new Handler(Looper.getMainLooper());
84    private ImsServiceController mTestImsServiceController;
85
86    @Before
87    @Override
88    public void setUp() throws Exception {
89        super.setUp();
90        mTestImsServiceController = new ImsServiceController(mMockContext, mTestComponentName,
91                mMockCallbacks, mHandler, REBIND_RETRY);
92        mTestImsServiceController.addImsServiceFeatureCallback(mMockProxyCallbacks);
93        when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(true);
94    }
95
96
97    @After
98    @Override
99    public void tearDown() throws Exception {
100        mTestImsServiceController = null;
101        super.tearDown();
102    }
103
104    /**
105     * Tests that Context.bindService is called with the correct parameters when we call bind.
106     */
107    @FlakyTest
108    @Test
109    public void testBindService() {
110        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
111        // Slot 1, MMTel
112        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
113        // Slot 1, RCS
114        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
115        ArgumentCaptor<Intent> intentCaptor =
116                ArgumentCaptor.forClass(Intent.class);
117
118        assertTrue(mTestImsServiceController.bind(testFeatures));
119
120        int expectedFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
121                | Context.BIND_IMPORTANT;
122        verify(mMockContext).bindService(intentCaptor.capture(), any(), eq(expectedFlags));
123        Intent testIntent = intentCaptor.getValue();
124        assertEquals(ImsService.SERVICE_INTERFACE, testIntent.getAction());
125        assertEquals(mTestComponentName, testIntent.getComponent());
126    }
127
128    /**
129     * Verify that if bind is called multiple times, we only call bindService once.
130     */
131    @FlakyTest
132    @Test
133    public void testBindFailureWhenBound() {
134        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
135        // Slot 1, MMTel
136        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
137        bindAndConnectService(testFeatures);
138
139        // already bound, should return false
140        assertFalse(mTestImsServiceController.bind(testFeatures));
141
142        verify(mMockContext, times(1)).bindService(any(), any(), anyInt());
143    }
144
145    /**
146     * Tests ImsServiceController callbacks are properly called when an ImsService is bound and
147     * connected.
148     */
149    @FlakyTest
150    @Test
151    public void testBindServiceAndConnected() throws RemoteException {
152        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
153        // Slot 1, MMTel
154        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
155        // Slot 1, RCS
156        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
157
158        bindAndConnectService(testFeatures);
159
160        IBinder binder = mMockServiceControllerBinder.getBinder().asBinder();
161        verify(binder).linkToDeath(any(), anyInt());
162        verify(mMockServiceControllerBinder).createMMTelFeature(eq(1));
163        verify(mMockServiceControllerBinder).createRcsFeature(eq(1));
164        verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
165                eq(mTestImsServiceController));
166        verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(2),
167                eq(mTestImsServiceController));
168        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
169        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(2));
170        assertEquals(mMockServiceControllerBinder.getBinder(),
171                mTestImsServiceController.getImsServiceControllerBinder());
172    }
173
174    /**
175     * Tests Emergency MMTEL ImsServiceController callbacks are properly called when an ImsService
176     * is bound and connected.
177     */
178    @FlakyTest
179    @Test
180    public void testBindEmergencyMmTel() throws RemoteException {
181        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
182        // Slot 1, Emergency MMTel
183        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 0));
184        // Slot 1, MmTel
185        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
186
187        bindAndConnectService(testFeatures);
188
189        IBinder binder = mMockServiceControllerBinder.getBinder().asBinder();
190        verify(binder).linkToDeath(any(), anyInt());
191        verify(mMockServiceControllerBinder).createMMTelFeature(eq(1));
192        // We do not want this callback to happen for emergency MMTEL
193        verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(1), eq(0),
194                eq(mTestImsServiceController));
195        verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
196                eq(mTestImsServiceController));
197        // Make sure this callback happens, which will notify the framework of emergency calling
198        // availability.
199        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(0));
200        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
201        assertEquals(mMockServiceControllerBinder.getBinder(),
202                mTestImsServiceController.getImsServiceControllerBinder());
203    }
204
205    /**
206     * Tests that if a callback is added after the ImsServiceController is already bound, we get a
207     * imsFeatureCreated callback.
208     */
209    @FlakyTest
210    @Test
211    public void testCallbacksHappenWhenAddedAfterBind() throws RemoteException {
212        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
213        // Slot 1, Emergency MMTel
214        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 0));
215        // Slot 1, MmTel
216        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
217        mTestImsServiceController.removeImsServiceFeatureCallbacks();
218
219        bindAndConnectService(testFeatures);
220        // add the callback after bind
221        mTestImsServiceController.addImsServiceFeatureCallback(mMockProxyCallbacks);
222
223        // Make sure this callback happens for Emergency MMTEL and MMTEL
224        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(0));
225        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
226        assertEquals(mMockServiceControllerBinder.getBinder(),
227                mTestImsServiceController.getImsServiceControllerBinder());
228    }
229
230    /**
231     * Tests ImsServiceController callbacks are properly called when an ImsService is bound and
232     * connected.
233     */
234    @FlakyTest
235    @Test
236    public void testBindServiceAndConnectedDisconnected() throws RemoteException {
237        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
238        // Slot 1, MMTel
239        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
240        // Slot 1, RCS
241        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
242        ServiceConnection conn = bindAndConnectService(testFeatures);
243
244        conn.onServiceDisconnected(mTestComponentName);
245
246        IBinder binder = mMockServiceControllerBinder.getBinder().asBinder();
247        verify(binder).unlinkToDeath(any(), anyInt());
248        // binder already disconnected, removeImsFeatures shouldn't be called.
249        verify(mMockServiceControllerBinder, never()).removeImsFeature(anyInt(), anyInt());
250        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(1), eq(1),
251                eq(mTestImsServiceController));
252        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(1), eq(2),
253                eq(mTestImsServiceController));
254        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(1), eq(1));
255        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(1), eq(2));
256    }
257
258    /**
259     * Tests ImsServiceController callbacks are properly called when an ImsService is bound and
260     * connected.
261     */
262    @FlakyTest
263    @Test
264    public void testBindServiceBindUnbind() throws RemoteException {
265        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
266        // Slot 1, MMTel
267        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
268        // Slot 1, RCS
269        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
270        ServiceConnection conn = bindAndConnectService(testFeatures);
271
272        mTestImsServiceController.unbind();
273
274        verify(mMockContext).unbindService(eq(conn));
275        IBinder binder = mMockServiceControllerBinder.getBinder().asBinder();
276        verify(binder).unlinkToDeath(any(), anyInt());
277        verify(mMockServiceControllerBinder).removeImsFeature(eq(1), eq(1));
278        verify(mMockServiceControllerBinder).removeImsFeature(eq(1), eq(2));
279        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(1), eq(1),
280                eq(mTestImsServiceController));
281        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(1), eq(2),
282                eq(mTestImsServiceController));
283        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(1), eq(1));
284        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(1), eq(2));
285    }
286
287    /**
288     * Ensures that imsServiceFeatureRemoved is called when the binder dies in another process.
289     */
290    @FlakyTest
291    @Test
292    public void testBindServiceAndBinderDied() throws RemoteException {
293        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
294        // Slot 1, MMTel
295        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
296        // Slot 1, RCS
297        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
298        bindAndConnectService(testFeatures);
299        ArgumentCaptor<IBinder.DeathRecipient> deathCaptor =
300                ArgumentCaptor.forClass(IBinder.DeathRecipient.class);
301        IBinder binder = mMockServiceControllerBinder.getBinder().asBinder();
302        verify(binder).linkToDeath(deathCaptor.capture(), anyInt());
303
304        deathCaptor.getValue().binderDied();
305
306        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(1), eq(1),
307                eq(mTestImsServiceController));
308        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(1), eq(2),
309                eq(mTestImsServiceController));
310        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(1), eq(1));
311        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(1), eq(2));
312    }
313
314    /**
315     * Ensures ImsService and ImsResolver are notified when a feature is added.
316     */
317    @FlakyTest
318    @Test
319    public void testBindServiceAndAddFeature() throws RemoteException {
320        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
321        // Slot 1, MMTel
322        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
323        bindAndConnectService(testFeatures);
324        verify(mMockServiceControllerBinder).createMMTelFeature(eq(1));
325        verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
326                eq(mTestImsServiceController));
327        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
328        // Create a new list with an additional item
329        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithAddition = new HashSet<>(
330                testFeatures);
331        testFeaturesWithAddition.add(new ImsFeatureConfiguration.FeatureSlotPair(2, 1));
332
333        mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithAddition);
334
335        verify(mMockServiceControllerBinder).createMMTelFeature(eq(2));
336        verify(mMockCallbacks).imsServiceFeatureCreated(eq(2), eq(1),
337                eq(mTestImsServiceController));
338        verify(mMockProxyCallbacks).imsFeatureCreated(eq(2), eq(1));
339    }
340
341    /**
342     * Ensures ImsService and ImsResolver are notified when a feature is added.
343     */
344    @FlakyTest
345    @Test
346    public void testBindServiceAndRemoveFeature() throws RemoteException {
347        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
348        // Slot 1, MMTel
349        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
350        // Slot 2, MMTel
351        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(2, 1));
352        bindAndConnectService(testFeatures);
353        verify(mMockServiceControllerBinder).createMMTelFeature(eq(1));
354        verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
355                eq(mTestImsServiceController));
356        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
357        verify(mMockServiceControllerBinder).createMMTelFeature(eq(2));
358        verify(mMockCallbacks).imsServiceFeatureCreated(eq(2), eq(1),
359                eq(mTestImsServiceController));
360        verify(mMockProxyCallbacks).imsFeatureCreated(eq(2), eq(1));
361        // Create a new list with one less item
362        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithSubtraction =
363                new HashSet<>(testFeatures);
364        testFeaturesWithSubtraction.remove(new ImsFeatureConfiguration.FeatureSlotPair(2, 1));
365
366        mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithSubtraction);
367
368        verify(mMockServiceControllerBinder).removeImsFeature(eq(2), eq(1));
369        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(2), eq(1),
370                eq(mTestImsServiceController));
371        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(2), eq(1));
372    }
373
374    /**
375     * Ensures ImsService and ImsResolver are notified when all features are removed.
376     */
377    @FlakyTest
378    @Test
379    public void testBindServiceAndRemoveAllFeatures() throws RemoteException {
380        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
381        // slot 1, MMTel
382        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
383        // slot 2, MMTel
384        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(2, 1));
385        bindAndConnectService(testFeatures);
386        verify(mMockServiceControllerBinder).createMMTelFeature(eq(1));
387        verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
388                eq(mTestImsServiceController));
389        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
390        verify(mMockServiceControllerBinder).createMMTelFeature(eq(2));
391        verify(mMockCallbacks).imsServiceFeatureCreated(eq(2), eq(1),
392                eq(mTestImsServiceController));
393        verify(mMockProxyCallbacks).imsFeatureCreated(eq(2), eq(1));
394
395        // Create a new empty list
396        mTestImsServiceController.changeImsServiceFeatures(new HashSet<>());
397
398        verify(mMockServiceControllerBinder).removeImsFeature(eq(1), eq(1));
399        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(1), eq(1),
400                eq(mTestImsServiceController));
401        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(1), eq(1));
402        verify(mMockServiceControllerBinder).removeImsFeature(eq(2), eq(1));
403        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(2), eq(1),
404                eq(mTestImsServiceController));
405        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(2), eq(1));
406    }
407
408    /**
409     * Verifies that nothing is notified of a feature change if the service is not bound.
410     */
411    @FlakyTest
412    @Test
413    public void testBindUnbindServiceAndAddFeature() throws RemoteException {
414        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
415        // Slot 1, MMTel
416        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
417        bindAndConnectService(testFeatures);
418        mTestImsServiceController.unbind();
419        // Create a new list with an additional item
420        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithAddition = new HashSet<>(
421                testFeatures);
422        // Try to create an RCS feature
423        testFeaturesWithAddition.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
424
425        mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithAddition);
426
427        verify(mMockServiceControllerBinder, never()).createRcsFeature(eq(1));
428        verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(1), eq(2),
429                eq(mTestImsServiceController));
430        verify(mMockProxyCallbacks, never()).imsFeatureCreated(eq(1), eq(2));
431    }
432
433    /**
434     * Verifies that the ImsServiceController automatically tries to bind again after an untimely
435     * binder death.
436     */
437    @FlakyTest
438    @Test
439    public void testAutoBindAfterBinderDied() throws RemoteException {
440        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
441        // Slot 1, MMTel
442        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
443        // Slot 1, RCS
444        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
445        bindAndConnectService(testFeatures);
446
447        getDeathRecipient().binderDied();
448
449        long delay = mTestImsServiceController.getRebindDelay();
450        waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
451        // The service should autobind after rebind event occurs
452        verify(mMockContext, times(2)).bindService(any(), any(), anyInt());
453    }
454
455    /**
456     * Ensure that bindService has only been called once before automatic rebind occurs.
457     */
458    @FlakyTest
459    @Test
460    public void testNoAutoBindBeforeTimeout() throws RemoteException {
461        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
462        // Slot 1, MMTel
463        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
464        // Slot 1, RCS
465        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
466        bindAndConnectService(testFeatures);
467
468        getDeathRecipient().binderDied();
469
470        // Be sure that there are no binds before the RETRY_TIMEOUT expires
471        verify(mMockContext, times(1)).bindService(any(), any(), anyInt());
472    }
473
474    /**
475     * Ensure that calling unbind stops automatic rebind of the ImsService from occuring.
476     */
477    @FlakyTest
478    @Test
479    public void testUnbindCauseAutoBindCancelAfterBinderDied() throws RemoteException {
480        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
481        // Slot 1, MMTel
482        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
483        // Slot 1, RCS
484        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
485        bindAndConnectService(testFeatures);
486
487        getDeathRecipient().binderDied();
488        mTestImsServiceController.unbind();
489
490        long delay = mTestImsServiceController.getRebindDelay();
491        waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
492
493        // Unbind should stop the autobind from occurring.
494        verify(mMockContext, times(1)).bindService(any(), any(), anyInt());
495    }
496
497    /**
498     * Ensure that calling bind causes the automatic rebinding to be cancelled or not cause another
499     * call to bindService.
500     */
501    @FlakyTest
502    @Test
503    public void testBindCauseAutoBindCancelAfterBinderDied() throws RemoteException {
504        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
505        // Slot 1, MMTel
506        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
507        // Slot 1, RCS
508        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
509        bindAndConnectService(testFeatures);
510        getDeathRecipient().binderDied();
511        mTestImsServiceController.bind(testFeatures);
512
513        long delay = mTestImsServiceController.getRebindDelay();
514        waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
515        // Should only see two binds, not three from the auto rebind that occurs.
516        verify(mMockContext, times(2)).bindService(any(), any(), anyInt());
517    }
518
519    private ServiceConnection bindAndConnectService(
520            HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures) {
521        ArgumentCaptor<ServiceConnection> serviceCaptor =
522                ArgumentCaptor.forClass(ServiceConnection.class);
523        assertTrue(mTestImsServiceController.bind(testFeatures));
524        verify(mMockContext).bindService(any(), serviceCaptor.capture(), anyInt());
525        serviceCaptor.getValue().onServiceConnected(mTestComponentName,
526                mMockServiceControllerBinder.getBinder().asBinder());
527        return serviceCaptor.getValue();
528    }
529
530    private IBinder.DeathRecipient getDeathRecipient() throws RemoteException {
531        ArgumentCaptor<IBinder.DeathRecipient> deathCaptor =
532                ArgumentCaptor.forClass(IBinder.DeathRecipient.class);
533        IBinder binder = mMockServiceControllerBinder.getBinder().asBinder();
534        verify(binder).linkToDeath(deathCaptor.capture(), anyInt());
535        return deathCaptor.getValue();
536    }
537}
538