TelecomSystemTest.java revision 0a4b95fc7731943fdd1a9b295daae45eb46b28d0
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
17package com.android.server.telecom.tests;
18
19import static org.mockito.Matchers.any;
20import static org.mockito.Matchers.anyBoolean;
21import static org.mockito.Matchers.anyInt;
22import static org.mockito.Matchers.anyString;
23import static org.mockito.Matchers.eq;
24import static org.mockito.Mockito.mock;
25import static org.mockito.Mockito.timeout;
26import static org.mockito.Mockito.verify;
27import static org.mockito.Mockito.when;
28
29import android.content.BroadcastReceiver;
30import android.content.ComponentName;
31import android.content.Context;
32import android.content.Intent;
33import android.net.Uri;
34import android.os.Bundle;
35import android.os.Handler;
36import android.os.UserHandle;
37import android.telecom.Call;
38import android.telecom.ConnectionRequest;
39import android.telecom.DisconnectCause;
40import android.telecom.ParcelableCall;
41import android.telecom.PhoneAccount;
42import android.telecom.PhoneAccountHandle;
43import android.telecom.TelecomManager;
44import android.telephony.TelephonyManager;
45
46import com.android.internal.telecom.IInCallAdapter;
47import com.android.server.telecom.CallsManager;
48import com.android.server.telecom.HeadsetMediaButton;
49import com.android.server.telecom.HeadsetMediaButtonFactory;
50import com.android.server.telecom.InCallWakeLockController;
51import com.android.server.telecom.InCallWakeLockControllerFactory;
52import com.android.server.telecom.Log;
53import com.android.server.telecom.MissedCallNotifier;
54import com.android.server.telecom.ProximitySensorManager;
55import com.android.server.telecom.ProximitySensorManagerFactory;
56import com.android.server.telecom.TelecomSystem;
57
58import org.mockito.ArgumentCaptor;
59import org.mockito.Mock;
60
61import java.util.concurrent.BrokenBarrierException;
62import java.util.concurrent.CountDownLatch;
63import java.util.concurrent.CyclicBarrier;
64
65public class TelecomSystemTest extends TelecomTestCase {
66
67    static final int TEST_TIMEOUT = 1000;  // milliseconds
68
69    @Mock MissedCallNotifier mMissedCallNotifier;
70    @Mock HeadsetMediaButton mHeadsetMediaButton;
71    @Mock ProximitySensorManager mProximitySensorManager;
72    @Mock InCallWakeLockController mInCallWakeLockController;
73
74    final ComponentName mInCallServiceComponentNameX =
75            new ComponentName(
76                    "incall-service-package-X",
77                    "incall-service-class-X");
78    final ComponentName mInCallServiceComponentNameY =
79            new ComponentName(
80                    "incall-service-package-Y",
81                    "incall-service-class-Y");
82
83    InCallServiceFixture mInCallServiceFixtureX;
84    InCallServiceFixture mInCallServiceFixtureY;
85
86    final ComponentName mConnectionServiceComponentNameA =
87            new ComponentName(
88                    "connection-service-package-A",
89                    "connection-service-class-A");
90    final ComponentName mConnectionServiceComponentNameB =
91            new ComponentName(
92                    "connection-service-package-B",
93                    "connection-service-class-B");
94
95    final PhoneAccount mPhoneAccountA0 =
96            PhoneAccount.builder(
97                    new PhoneAccountHandle(
98                            mConnectionServiceComponentNameA,
99                            "id A 0"),
100                    "Phone account service A ID 0")
101                    .addSupportedUriScheme("tel")
102                    .setCapabilities(
103                            PhoneAccount.CAPABILITY_CALL_PROVIDER |
104                            PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
105                    .build();
106    final PhoneAccount mPhoneAccountA1 =
107            PhoneAccount.builder(
108                    new PhoneAccountHandle(
109                            mConnectionServiceComponentNameA,
110                            "id A 1"),
111                    "Phone account service A ID 1")
112                    .addSupportedUriScheme("tel")
113                    .setCapabilities(
114                            PhoneAccount.CAPABILITY_CALL_PROVIDER |
115                            PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
116                    .build();
117    final PhoneAccount mPhoneAccountB0 =
118            PhoneAccount.builder(
119                    new PhoneAccountHandle(
120                            mConnectionServiceComponentNameA,
121                            "id B 0"),
122                    "Phone account service B ID 0")
123                    .addSupportedUriScheme("tel")
124                    .setCapabilities(
125                            PhoneAccount.CAPABILITY_CALL_PROVIDER |
126                            PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
127                    .build();
128
129    ConnectionServiceFixture mConnectionServiceFixtureA;
130    ConnectionServiceFixture mConnectionServiceFixtureB;
131
132    CallerInfoAsyncQueryFactoryFixture mCallerInfoAsyncQueryFactoryFixture;
133
134    TelecomSystem mTelecomSystem;
135
136    @Override
137    public void setUp() throws Exception {
138        super.setUp();
139
140        // First set up information about the In-Call services in the mock Context, since
141        // Telecom will search for these as soon as it is instantiated
142        setupInCallServices();
143
144        // Next, create the TelecomSystem, our system under test
145        setupTelecomSystem();
146
147        // Finally, register the ConnectionServices with the PhoneAccountRegistrar of the
148        // now-running TelecomSystem
149        setupConnectionServices();
150    }
151
152    @Override
153    public void tearDown() throws Exception {
154        mTelecomSystem = null;
155        super.tearDown();
156    }
157
158    private void setupTelecomSystem() throws Exception {
159        HeadsetMediaButtonFactory headsetMediaButtonFactory =
160                mock(HeadsetMediaButtonFactory.class);
161        ProximitySensorManagerFactory proximitySensorManagerFactory =
162                mock(ProximitySensorManagerFactory.class);
163        InCallWakeLockControllerFactory inCallWakeLockControllerFactory =
164                mock(InCallWakeLockControllerFactory.class);
165
166        mCallerInfoAsyncQueryFactoryFixture = new CallerInfoAsyncQueryFactoryFixture();
167
168        when(headsetMediaButtonFactory.create(
169                any(Context.class),
170                any(CallsManager.class),
171                any(TelecomSystem.SyncRoot.class)))
172                .thenReturn(mHeadsetMediaButton);
173        when(proximitySensorManagerFactory.create(
174                any(Context.class),
175                any(CallsManager.class)))
176                .thenReturn(mProximitySensorManager);
177        when(inCallWakeLockControllerFactory.create(
178                any(Context.class),
179                any(CallsManager.class)))
180                .thenReturn(mInCallWakeLockController);
181
182        mTelecomSystem = new TelecomSystem(
183                mComponentContextFixture.getTestDouble(),
184                mMissedCallNotifier,
185                mCallerInfoAsyncQueryFactoryFixture.getTestDouble(),
186                headsetMediaButtonFactory,
187                proximitySensorManagerFactory,
188                inCallWakeLockControllerFactory);
189
190        verify(headsetMediaButtonFactory).create(
191                eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
192                any(CallsManager.class),
193                any(TelecomSystem.SyncRoot.class));
194        verify(proximitySensorManagerFactory).create(
195                eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
196                any(CallsManager.class));
197        verify(inCallWakeLockControllerFactory).create(
198                eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
199                any(CallsManager.class));
200    }
201
202    private void setupConnectionServices() throws Exception {
203        mConnectionServiceFixtureA = new ConnectionServiceFixture();
204        mConnectionServiceFixtureB = new ConnectionServiceFixture();
205
206        mComponentContextFixture.addConnectionService(
207                mConnectionServiceComponentNameA,
208                mConnectionServiceFixtureA.getTestDouble());
209        mComponentContextFixture.addConnectionService(
210                mConnectionServiceComponentNameB,
211                mConnectionServiceFixtureB.getTestDouble());
212
213        mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA0);
214        mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA1);
215        mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountB0);
216
217        mTelecomSystem.getPhoneAccountRegistrar().setUserSelectedOutgoingPhoneAccount(
218                mPhoneAccountA0.getAccountHandle());
219    }
220
221    private void setupInCallServices() throws Exception {
222        mComponentContextFixture.putResource(
223                com.android.server.telecom.R.string.ui_default_package,
224                mInCallServiceComponentNameX.getPackageName());
225        mComponentContextFixture.putResource(
226                com.android.server.telecom.R.string.incall_default_class,
227                mInCallServiceComponentNameX.getClassName());
228
229        mInCallServiceFixtureX = new InCallServiceFixture();
230        mInCallServiceFixtureY = new InCallServiceFixture();
231
232        mComponentContextFixture.addInCallService(
233                mInCallServiceComponentNameX,
234                mInCallServiceFixtureX.getTestDouble());
235        mComponentContextFixture.addInCallService(
236                mInCallServiceComponentNameY,
237                mInCallServiceFixtureY.getTestDouble());
238    }
239
240    private String startOutgoingPhoneCall(
241            String number,
242            PhoneAccountHandle phoneAccountHandle,
243            ConnectionServiceFixture connectionServiceFixture) throws Exception {
244        Intent actionCallIntent = new Intent();
245        actionCallIntent.setData(Uri.parse("tel:" + number));
246        actionCallIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
247        actionCallIntent.setAction(Intent.ACTION_CALL);
248        if (phoneAccountHandle != null) {
249            actionCallIntent.putExtra(
250                    TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
251                    phoneAccountHandle);
252        }
253
254        mTelecomSystem.getCallIntentProcessor().processIntent(actionCallIntent);
255
256        ArgumentCaptor<Intent> newOutgoingCallIntent =
257                ArgumentCaptor.forClass(Intent.class);
258        ArgumentCaptor<BroadcastReceiver> newOutgoingCallReceiver =
259                ArgumentCaptor.forClass(BroadcastReceiver.class);
260
261        verify(mComponentContextFixture.getTestDouble().getApplicationContext())
262                .sendOrderedBroadcastAsUser(
263                        newOutgoingCallIntent.capture(),
264                        any(UserHandle.class),
265                        anyString(),
266                        anyInt(),
267                        newOutgoingCallReceiver.capture(),
268                        any(Handler.class),
269                        anyInt(),
270                        anyString(),
271                        any(Bundle.class));
272
273        assertNotNull(mInCallServiceFixtureX.mInCallAdapter);
274        assertNotNull(mInCallServiceFixtureY.mInCallAdapter);
275
276        // Pass on the new outgoing call Intent
277        // Set a dummy PendingResult so the BroadcastReceiver agrees to accept onReceive()
278        newOutgoingCallReceiver.getValue().setPendingResult(
279                new BroadcastReceiver.PendingResult(0, "", null, 0, true, false, null, 0, 0));
280        newOutgoingCallReceiver.getValue().setResultData(
281                newOutgoingCallIntent.getValue().getStringExtra(Intent.EXTRA_PHONE_NUMBER));
282        newOutgoingCallReceiver.getValue().onReceive(
283                mComponentContextFixture.getTestDouble(),
284                newOutgoingCallIntent.getValue());
285
286        verify(connectionServiceFixture.getTestDouble()).createConnection(
287                eq(phoneAccountHandle),
288                anyString(),
289                any(ConnectionRequest.class),
290                anyBoolean(),
291                anyBoolean());
292
293        String id = connectionServiceFixture.mLatestConnectionId;
294
295        connectionServiceFixture.sendHandleCreateConnectionComplete(id);
296
297        return id;
298    }
299
300    private String startIncomingPhoneCall(
301            String number,
302            PhoneAccountHandle phoneAccountHandle,
303            ConnectionServiceFixture connectionServiceFixture) throws Exception {
304        Bundle extras = new Bundle();
305        extras.putParcelable(
306                TelephonyManager.EXTRA_INCOMING_NUMBER,
307                Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null));
308        mTelecomSystem.getTelecomServiceImpl().getBinder()
309                .addNewIncomingCall(phoneAccountHandle, extras);
310
311        verify(connectionServiceFixture.getTestDouble()).createConnection(
312                any(PhoneAccountHandle.class),
313                anyString(),
314                any(ConnectionRequest.class),
315                eq(true),
316                eq(false));
317
318        String id = connectionServiceFixture.mLatestConnectionId;
319
320        connectionServiceFixture.sendHandleCreateConnectionComplete(id);
321        connectionServiceFixture.sendSetRinging(id);
322
323        // For the case of incoming calls, Telecom connecting the InCall services and adding the
324        // Call is triggered by the async completion of the CallerInfoAsyncQuery. Once the Call
325        // is added, future interactions as triggered by the ConnectionService, through the various
326        // test fixtures, will be synchronous.
327
328        verify(
329                mInCallServiceFixtureX.getTestDouble(),
330                timeout(TEST_TIMEOUT))
331                .setInCallAdapter(
332                        any(IInCallAdapter.class));
333        verify(
334                mInCallServiceFixtureY.getTestDouble(),
335                timeout(TEST_TIMEOUT))
336                .setInCallAdapter(
337                        any(IInCallAdapter.class));
338
339        assertNotNull(mInCallServiceFixtureX.mInCallAdapter);
340        assertNotNull(mInCallServiceFixtureY.mInCallAdapter);
341
342        verify(
343                mInCallServiceFixtureX.getTestDouble(),
344                timeout(TEST_TIMEOUT))
345                .addCall(
346                        any(ParcelableCall.class));
347        verify(
348                mInCallServiceFixtureY.getTestDouble(),
349                timeout(TEST_TIMEOUT))
350                .addCall(
351                        any(ParcelableCall.class));
352
353        return id;
354    }
355
356    private void rapidFire(Runnable... tasks) {
357        final CyclicBarrier barrier = new CyclicBarrier(tasks.length);
358        final CountDownLatch latch = new CountDownLatch(tasks.length);
359        for (int i = 0; i < tasks.length; i++) {
360            final Runnable task = tasks[i];
361            new Thread(new Runnable() {
362                @Override
363                public void run() {
364                    try {
365                        barrier.await();
366                        task.run();
367                    } catch (InterruptedException | BrokenBarrierException e){
368                        Log.e(TelecomSystemTest.this, e, "Unexpectedly interrupted");
369                    } finally {
370                        latch.countDown();
371                    }
372                }
373            }).start();
374        }
375        try {
376            latch.await();
377        } catch (InterruptedException e) {
378            Log.e(TelecomSystemTest.this, e, "Unexpectedly interrupted");
379        }
380    }
381
382    // A simple outgoing call, verifying that the appropriate connection service is contacted,
383    // the proper lifecycle is followed, and both In-Call Services are updated correctly.
384    public void testSingleOutgoingCall() throws Exception {
385        String connectionId = startOutgoingPhoneCall(
386                "650-555-1212",
387                mPhoneAccountA0.getAccountHandle(),
388                mConnectionServiceFixtureA);
389
390        assertEquals(1, mConnectionServiceFixtureA.mConnectionServiceAdapters.size());
391        assertEquals(1, mConnectionServiceFixtureA.mConnectionById.size());
392
393        mConnectionServiceFixtureA.sendSetDialing(connectionId);
394
395        assertEquals(1, mInCallServiceFixtureX.mCallById.size());
396        String callId = mInCallServiceFixtureX.mLatestCallId;
397
398        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(callId).getState());
399        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(callId).getState());
400
401        mConnectionServiceFixtureA.sendSetActive(connectionId);
402
403        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(callId).getState());
404        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(callId).getState());
405
406        mInCallServiceFixtureX.mInCallAdapter.disconnectCall(callId);;
407        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(callId).getState());
408        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(callId).getState());
409
410        mConnectionServiceFixtureA.sendSetDisconnected(connectionId, DisconnectCause.LOCAL);
411        assertEquals(Call.STATE_DISCONNECTED, mInCallServiceFixtureX.getCall(callId).getState());
412        assertEquals(Call.STATE_DISCONNECTED, mInCallServiceFixtureY.getCall(callId).getState());
413    }
414
415    // A simple incoming call, similar in scope to the previous test
416    public void testSingleIncomingCall() throws Exception {
417        String connectionId = startIncomingPhoneCall(
418                "650-555-1212",
419                mPhoneAccountA0.getAccountHandle(),
420                mConnectionServiceFixtureA);
421
422        assertEquals(1, mConnectionServiceFixtureA.mConnectionServiceAdapters.size());
423        assertEquals(1, mConnectionServiceFixtureA.mConnectionById.size());
424
425        assertEquals(1, mInCallServiceFixtureX.mCallById.size());
426        String callId = mInCallServiceFixtureX.mLatestCallId;
427
428        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(callId).getState());
429        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(callId).getState());
430
431        mConnectionServiceFixtureA.sendSetActive(connectionId);
432        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(callId).getState());
433        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(callId).getState());
434
435        mInCallServiceFixtureX.mInCallAdapter.disconnectCall(callId);;
436        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(callId).getState());
437        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(callId).getState());
438
439        mConnectionServiceFixtureA.sendSetDisconnected(connectionId, DisconnectCause.LOCAL);
440        assertEquals(Call.STATE_DISCONNECTED, mInCallServiceFixtureX.getCall(callId).getState());
441        assertEquals(Call.STATE_DISCONNECTED, mInCallServiceFixtureY.getCall(callId).getState());
442    }
443
444    public void testDeadlockOnOutgoingCall() throws Exception {
445        for (int i = 0; i < 100; i++) {
446            TelecomSystemTest test = new TelecomSystemTest();
447            test.setContext(getContext());
448            test.setTestContext(getTestContext());
449            test.setName(getName());
450            test.setUp();
451            test.do_testDeadlockOnOutgoingCall();
452            test.tearDown();
453        }
454    }
455
456    public void do_testDeadlockOnOutgoingCall() throws Exception {
457        final String connectionId = startOutgoingPhoneCall(
458                "650-555-1212",
459                mPhoneAccountA0.getAccountHandle(),
460                mConnectionServiceFixtureA);
461        rapidFire(
462                new Runnable() {
463                    @Override
464                    public void run() {
465                        while (mCallerInfoAsyncQueryFactoryFixture.mRequests.size() > 0) {
466                            mCallerInfoAsyncQueryFactoryFixture.mRequests.remove(0).reply();
467                        }
468                    }
469                },
470                new Runnable() {
471                    @Override
472                    public void run() {
473                        try {
474                            mConnectionServiceFixtureA.sendSetActive(connectionId);
475                        } catch (Exception e) {
476                            Log.e(this, e, "");
477                        }
478                    }
479                });
480    }
481}
482