CallLogManagerTest.java revision c7dbd2c2c6bf5e07d5de389f2512454e577c2153
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
19
20import android.content.ComponentName;
21import android.content.ContentProvider;
22import android.content.ContentValues;
23import android.content.Context;
24import android.content.IContentProvider;
25import android.content.pm.UserInfo;
26import android.location.Country;
27import android.location.CountryDetector;
28import android.location.CountryListener;
29import android.net.Uri;
30import android.os.Looper;
31import android.os.PersistableBundle;
32import android.os.UserHandle;
33import android.os.UserManager;
34import android.platform.test.annotations.Postsubmit;
35import android.provider.CallLog;
36import android.provider.CallLog.Calls;
37import android.support.test.filters.FlakyTest;
38import android.telecom.DisconnectCause;
39import android.telecom.PhoneAccount;
40import android.telecom.PhoneAccountHandle;
41import android.telecom.VideoProfile;
42import android.telephony.CarrierConfigManager;
43import android.telephony.PhoneNumberUtils;
44import android.test.suitebuilder.annotation.MediumTest;
45import android.test.suitebuilder.annotation.SmallTest;
46
47import com.android.server.telecom.Call;
48import com.android.server.telecom.CallLogManager;
49import com.android.server.telecom.CallState;
50import com.android.server.telecom.MissedCallNotifier;
51import com.android.server.telecom.PhoneAccountRegistrar;
52import com.android.server.telecom.R;
53import com.android.server.telecom.TelephonyUtil;
54
55import static org.mockito.Matchers.any;
56import static org.mockito.Matchers.anyString;
57import static org.mockito.Matchers.eq;
58import static org.mockito.Mockito.doAnswer;
59import static org.mockito.Mockito.mock;
60import static org.mockito.Mockito.never;
61import static org.mockito.Mockito.timeout;
62import static org.mockito.Mockito.verify;
63import static org.mockito.Mockito.when;
64
65import org.mockito.ArgumentCaptor;
66import org.mockito.Mock;
67import org.mockito.invocation.InvocationOnMock;
68import org.mockito.stubbing.Answer;
69
70import java.util.Arrays;
71import java.util.Locale;
72
73public class CallLogManagerTest extends TelecomTestCase {
74
75    private CallLogManager mCallLogManager;
76    private IContentProvider mContentProvider;
77    private PhoneAccountHandle mDefaultAccountHandle;
78    private PhoneAccountHandle mOtherUserAccountHandle;
79    private PhoneAccountHandle mManagedProfileAccountHandle;
80
81    private static final Uri TEL_PHONEHANDLE = Uri.parse("tel:5555551234");
82
83    private static final PhoneAccountHandle EMERGENCY_ACCT_HANDLE = TelephonyUtil
84            .getDefaultEmergencyPhoneAccount()
85            .getAccountHandle();
86
87    private static final int NO_VIDEO_STATE = VideoProfile.STATE_AUDIO_ONLY;
88    private static final int BIDIRECTIONAL_VIDEO_STATE = VideoProfile.STATE_BIDIRECTIONAL;
89    private static final String POST_DIAL_STRING = ";12345";
90    private static final String VIA_NUMBER_STRING = "5555555678";
91    private static final String TEST_PHONE_ACCOUNT_ID= "testPhoneAccountId";
92
93    private static final int TEST_TIMEOUT_MILLIS = 200;
94    private static final int CURRENT_USER_ID = 0;
95    private static final int OTHER_USER_ID = 10;
96    private static final int MANAGED_USER_ID = 11;
97
98    private static final String TEST_ISO = "KR";
99    private static final String TEST_ISO_2 = "JP";
100
101    @Mock PhoneAccountRegistrar mMockPhoneAccountRegistrar;
102
103    @Mock
104    MissedCallNotifier mMissedCallNotifier;
105
106    @Override
107    public void setUp() throws Exception {
108        super.setUp();
109        mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
110        mCallLogManager = new CallLogManager(mContext, mMockPhoneAccountRegistrar,
111                mMissedCallNotifier);
112        mContentProvider =
113                mContext.getContentResolver().acquireProvider("0@call_log");
114        mDefaultAccountHandle = new PhoneAccountHandle(
115                new ComponentName("com.android.server.telecom.tests", "CallLogManagerTest"),
116                TEST_PHONE_ACCOUNT_ID,
117                UserHandle.of(CURRENT_USER_ID)
118        );
119
120        mOtherUserAccountHandle = new PhoneAccountHandle(
121                new ComponentName("com.android.server.telecom.tests", "CallLogManagerTest"),
122                TEST_PHONE_ACCOUNT_ID,
123                UserHandle.of(OTHER_USER_ID)
124        );
125
126        mManagedProfileAccountHandle = new PhoneAccountHandle(
127                new ComponentName("com.android.server.telecom.tests", "CallLogManagerTest"),
128                TEST_PHONE_ACCOUNT_ID,
129                UserHandle.of(MANAGED_USER_ID)
130        );
131
132        UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
133        UserInfo userInfo = new UserInfo(CURRENT_USER_ID, "test", 0);
134        UserInfo otherUserInfo = new UserInfo(OTHER_USER_ID, "test2", 0);
135        UserInfo managedProfileUserInfo = new UserInfo(MANAGED_USER_ID, "test3",
136                UserInfo.FLAG_MANAGED_PROFILE);
137
138        doAnswer(new Answer<Uri>() {
139            @Override
140            public Uri answer(InvocationOnMock invocation) throws Throwable {
141                return (Uri) invocation.getArguments()[1];
142            }
143        }).when(mContentProvider).insert(anyString(), any(Uri.class), any(ContentValues.class));
144
145        when(userManager.isUserRunning(any(UserHandle.class))).thenReturn(true);
146        when(userManager.isUserUnlocked(any(UserHandle.class))).thenReturn(true);
147        when(userManager.hasUserRestriction(any(String.class), any(UserHandle.class)))
148                .thenReturn(false);
149        when(userManager.getUsers(any(Boolean.class)))
150                .thenReturn(Arrays.asList(userInfo, otherUserInfo, managedProfileUserInfo));
151        when(userManager.getUserInfo(eq(CURRENT_USER_ID))).thenReturn(userInfo);
152        when(userManager.getUserInfo(eq(OTHER_USER_ID))).thenReturn(otherUserInfo);
153        when(userManager.getUserInfo(eq(MANAGED_USER_ID))).thenReturn(managedProfileUserInfo);
154    }
155
156    @MediumTest
157    public void testDontLogCancelledCall() {
158        Call fakeCall = makeFakeCall(
159                DisconnectCause.CANCELED,
160                false, // isConference
161                false, // isIncoming
162                1L, // creationTimeMillis
163                1000L, // ageMillis
164                TEL_PHONEHANDLE, // callHandle
165                mDefaultAccountHandle, // phoneAccountHandle
166                NO_VIDEO_STATE, // callVideoState
167                POST_DIAL_STRING, // postDialDigits
168                VIA_NUMBER_STRING, // viaNumber
169                UserHandle.of(CURRENT_USER_ID)
170        );
171        mCallLogManager.onCallStateChanged(fakeCall, CallState.DIALING, CallState.DISCONNECTED);
172        verifyNoInsertion();
173        mCallLogManager.onCallStateChanged(fakeCall, CallState.DIALING, CallState.ABORTED);
174        verifyNoInsertion();
175    }
176
177    @MediumTest
178    public void testDontLogChoosingAccountCall() {
179        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
180                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
181        Call fakeCall = makeFakeCall(
182                DisconnectCause.OTHER, // disconnectCauseCode
183                false, // isConference
184                false, // isIncoming
185                1L, // creationTimeMillis
186                1000L, // ageMillis
187                TEL_PHONEHANDLE, // callHandle
188                mDefaultAccountHandle, // phoneAccountHandle
189                NO_VIDEO_STATE, // callVideoState
190                POST_DIAL_STRING, // postDialDigits
191                VIA_NUMBER_STRING, // viaNumber
192                UserHandle.of(CURRENT_USER_ID)
193        );
194        mCallLogManager.onCallStateChanged(fakeCall, CallState.SELECT_PHONE_ACCOUNT,
195                CallState.DISCONNECTED);
196        verifyNoInsertion();
197    }
198
199    @MediumTest
200    public void testDontLogCallsFromEmergencyAccount() {
201        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
202                .thenReturn(makeFakePhoneAccount(EMERGENCY_ACCT_HANDLE, 0));
203        CarrierConfigManager mockCarrierConfigManager =
204                (CarrierConfigManager) mComponentContextFixture.getTestDouble()
205                        .getApplicationContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
206        PersistableBundle bundle = new PersistableBundle();
207        bundle.putBoolean(CarrierConfigManager.KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL, false);
208        when(mockCarrierConfigManager.getConfig()).thenReturn(bundle);
209
210        Call fakeCall = makeFakeCall(
211                DisconnectCause.OTHER, // disconnectCauseCode
212                false, // isConference
213                false, // isIncoming
214                1L, // creationTimeMillis
215                1000L, // ageMillis
216                TEL_PHONEHANDLE, // callHandle
217                EMERGENCY_ACCT_HANDLE, // phoneAccountHandle
218                NO_VIDEO_STATE, // callVideoState
219                POST_DIAL_STRING, // postDialDigits
220                VIA_NUMBER_STRING, // viaNumber
221                UserHandle.of(CURRENT_USER_ID)
222        );
223        mCallLogManager.onCallStateChanged(fakeCall, CallState.ACTIVE, CallState.DISCONNECTED);
224        verifyNoInsertion();
225    }
226
227    @MediumTest
228    public void testLogCallDirectionOutgoing() {
229        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
230                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
231        Call fakeOutgoingCall = makeFakeCall(
232                DisconnectCause.OTHER, // disconnectCauseCode
233                false, // isConference
234                false, // isIncoming
235                1L, // creationTimeMillis
236                1000L, // ageMillis
237                TEL_PHONEHANDLE, // callHandle
238                mDefaultAccountHandle, // phoneAccountHandle
239                NO_VIDEO_STATE, // callVideoState
240                POST_DIAL_STRING, // postDialDigits
241                VIA_NUMBER_STRING, // viaNumber
242                UserHandle.of(CURRENT_USER_ID)
243        );
244        mCallLogManager.onCallStateChanged(fakeOutgoingCall, CallState.ACTIVE,
245                CallState.DISCONNECTED);
246        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
247        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
248                Integer.valueOf(CallLog.Calls.OUTGOING_TYPE));
249    }
250
251    @MediumTest
252    public void testLogCallDirectionIncoming() {
253        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
254                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
255        Call fakeIncomingCall = makeFakeCall(
256                DisconnectCause.OTHER, // disconnectCauseCode
257                false, // isConference
258                true, // isIncoming
259                1L, // creationTimeMillis
260                1000L, // ageMillis
261                TEL_PHONEHANDLE, // callHandle
262                mDefaultAccountHandle, // phoneAccountHandle
263                NO_VIDEO_STATE, // callVideoState
264                POST_DIAL_STRING, // postDialDigits
265                VIA_NUMBER_STRING, // viaNumber
266                null
267        );
268        mCallLogManager.onCallStateChanged(fakeIncomingCall, CallState.ACTIVE,
269                CallState.DISCONNECTED);
270        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
271        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
272                Integer.valueOf(CallLog.Calls.INCOMING_TYPE));
273    }
274
275    @MediumTest
276    public void testLogCallDirectionMissed() {
277        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
278                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
279        Call fakeMissedCall = makeFakeCall(
280                DisconnectCause.MISSED, // disconnectCauseCode
281                false, // isConference
282                true, // isIncoming
283                1L, // creationTimeMillis
284                1000L, // ageMillis
285                TEL_PHONEHANDLE, // callHandle
286                mDefaultAccountHandle, // phoneAccountHandle
287                NO_VIDEO_STATE, // callVideoState
288                POST_DIAL_STRING, // postDialDigits
289                VIA_NUMBER_STRING, // viaNumber
290                null
291        );
292
293        mCallLogManager.onCallStateChanged(fakeMissedCall, CallState.ACTIVE,
294                CallState.DISCONNECTED);
295        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
296        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
297                Integer.valueOf(CallLog.Calls.MISSED_TYPE));
298        // Timeout needed because showMissedCallNotification is called from onPostExecute.
299        verify(mMissedCallNotifier, timeout(TEST_TIMEOUT_MILLIS))
300                .showMissedCallNotification(any(MissedCallNotifier.CallInfo.class));
301    }
302
303    @MediumTest
304    public void testLogCallDirectionRejected() {
305        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
306                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
307        Call fakeMissedCall = makeFakeCall(
308                DisconnectCause.REJECTED, // disconnectCauseCode
309                false, // isConference
310                true, // isIncoming
311                1L, // creationTimeMillis
312                1000L, // ageMillis
313                TEL_PHONEHANDLE, // callHandle
314                mDefaultAccountHandle, // phoneAccountHandle
315                NO_VIDEO_STATE, // callVideoState
316                POST_DIAL_STRING, // postDialDigits
317                VIA_NUMBER_STRING, // viaNumber
318                null
319        );
320
321        mCallLogManager.onCallStateChanged(fakeMissedCall, CallState.ACTIVE,
322                CallState.DISCONNECTED);
323        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
324        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
325                Integer.valueOf(Calls.REJECTED_TYPE));
326    }
327
328    @MediumTest
329    public void testCreationTimeAndAge() {
330        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
331                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
332        long currentTime = System.currentTimeMillis();
333        long duration = 1000L;
334        Call fakeCall = makeFakeCall(
335                DisconnectCause.OTHER, // disconnectCauseCode
336                false, // isConference
337                false, // isIncoming
338                currentTime, // creationTimeMillis
339                duration, // ageMillis
340                TEL_PHONEHANDLE, // callHandle
341                mDefaultAccountHandle, // phoneAccountHandle
342                NO_VIDEO_STATE, // callVideoState
343                POST_DIAL_STRING, // postDialDigits
344                VIA_NUMBER_STRING, // viaNumber
345                UserHandle.of(CURRENT_USER_ID)
346        );
347        mCallLogManager.onCallStateChanged(fakeCall, CallState.ACTIVE, CallState.DISCONNECTED);
348        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
349        assertEquals(insertedValues.getAsLong(CallLog.Calls.DATE),
350                Long.valueOf(currentTime));
351        assertEquals(insertedValues.getAsLong(CallLog.Calls.DURATION),
352                Long.valueOf(duration / 1000));
353    }
354
355    @MediumTest
356    public void testLogPhoneAccountId() {
357        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
358                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
359        Call fakeCall = makeFakeCall(
360                DisconnectCause.OTHER, // disconnectCauseCode
361                false, // isConference
362                true, // isIncoming
363                1L, // creationTimeMillis
364                1000L, // ageMillis
365                TEL_PHONEHANDLE, // callHandle
366                mDefaultAccountHandle, // phoneAccountHandle
367                NO_VIDEO_STATE, // callVideoState
368                POST_DIAL_STRING, // postDialDigits
369                VIA_NUMBER_STRING, // viaNumber
370                UserHandle.of(CURRENT_USER_ID)
371        );
372        mCallLogManager.onCallStateChanged(fakeCall, CallState.ACTIVE, CallState.DISCONNECTED);
373        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
374        assertEquals(insertedValues.getAsString(CallLog.Calls.PHONE_ACCOUNT_ID),
375                TEST_PHONE_ACCOUNT_ID);
376    }
377
378    @MediumTest
379    public void testLogCorrectPhoneNumber() {
380        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
381                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
382        Call fakeCall = makeFakeCall(
383                DisconnectCause.OTHER, // disconnectCauseCode
384                false, // isConference
385                true, // isIncoming
386                1L, // creationTimeMillis
387                1000L, // ageMillis
388                TEL_PHONEHANDLE, // callHandle
389                mDefaultAccountHandle, // phoneAccountHandle
390                NO_VIDEO_STATE, // callVideoState
391                POST_DIAL_STRING, // postDialDigits
392                VIA_NUMBER_STRING, // viaNumber
393                UserHandle.of(CURRENT_USER_ID)
394        );
395        mCallLogManager.onCallStateChanged(fakeCall, CallState.ACTIVE, CallState.DISCONNECTED);
396        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
397        assertEquals(insertedValues.getAsString(CallLog.Calls.NUMBER),
398                TEL_PHONEHANDLE.getSchemeSpecificPart());
399        assertEquals(insertedValues.getAsString(CallLog.Calls.POST_DIAL_DIGITS), POST_DIAL_STRING);
400        String expectedNumber = PhoneNumberUtils.formatNumber(VIA_NUMBER_STRING, "US");
401        assertEquals(insertedValues.getAsString(Calls.VIA_NUMBER), expectedNumber);
402    }
403
404    @MediumTest
405    public void testLogCallVideoFeatures() {
406        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
407                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
408        Call fakeVideoCall = makeFakeCall(
409                DisconnectCause.OTHER, // disconnectCauseCode
410                false, // isConference
411                true, // isIncoming
412                1L, // creationTimeMillis
413                1000L, // ageMillis
414                TEL_PHONEHANDLE, // callHandle
415                mDefaultAccountHandle, // phoneAccountHandle
416                BIDIRECTIONAL_VIDEO_STATE, // callVideoState
417                POST_DIAL_STRING, // postDialDigits
418                VIA_NUMBER_STRING, // viaNumber
419                UserHandle.of(CURRENT_USER_ID)
420        );
421        mCallLogManager.onCallStateChanged(fakeVideoCall, CallState.ACTIVE, CallState.DISCONNECTED);
422        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
423        assertTrue((insertedValues.getAsInteger(CallLog.Calls.FEATURES)
424                & CallLog.Calls.FEATURES_VIDEO) == CallLog.Calls.FEATURES_VIDEO);
425    }
426
427    @MediumTest
428    @FlakyTest
429    @Postsubmit
430    public void testLogCallDirectionOutgoingWithMultiUserCapability() {
431        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
432                .thenReturn(makeFakePhoneAccount(mOtherUserAccountHandle,
433                        PhoneAccount.CAPABILITY_MULTI_USER));
434        Call fakeOutgoingCall = makeFakeCall(
435                DisconnectCause.OTHER, // disconnectCauseCode
436                false, // isConference
437                false, // isIncoming
438                1L, // creationTimeMillis
439                1000L, // ageMillis
440                TEL_PHONEHANDLE, // callHandle
441                mDefaultAccountHandle, // phoneAccountHandle
442                NO_VIDEO_STATE, // callVideoState
443                POST_DIAL_STRING, // postDialDigits
444                VIA_NUMBER_STRING, // viaNumber
445                UserHandle.of(CURRENT_USER_ID)
446        );
447        mCallLogManager.onCallStateChanged(fakeOutgoingCall, CallState.ACTIVE,
448                CallState.DISCONNECTED);
449
450        // Outgoing call placed through a phone account with multi user capability is inserted to
451        // all users except managed profile.
452        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
453        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
454                Integer.valueOf(CallLog.Calls.OUTGOING_TYPE));
455        insertedValues = verifyInsertionWithCapture(OTHER_USER_ID);
456        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
457                Integer.valueOf(CallLog.Calls.OUTGOING_TYPE));
458        verifyNoInsertionInUser(MANAGED_USER_ID);
459    }
460
461    @MediumTest
462    public void testLogCallDirectionIncomingWithMultiUserCapability() {
463        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
464                .thenReturn(makeFakePhoneAccount(mOtherUserAccountHandle,
465                        PhoneAccount.CAPABILITY_MULTI_USER));
466        Call fakeIncomingCall = makeFakeCall(
467                DisconnectCause.OTHER, // disconnectCauseCode
468                false, // isConference
469                true, // isIncoming
470                1L, // creationTimeMillis
471                1000L, // ageMillis
472                TEL_PHONEHANDLE, // callHandle
473                mDefaultAccountHandle, // phoneAccountHandle
474                NO_VIDEO_STATE, // callVideoState
475                POST_DIAL_STRING, // postDialDigits
476                VIA_NUMBER_STRING, // viaNumber
477                null
478        );
479        mCallLogManager.onCallStateChanged(fakeIncomingCall, CallState.ACTIVE,
480                CallState.DISCONNECTED);
481
482        // Incoming call using a phone account with multi user capability is inserted to all users
483        // except managed profile.
484        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
485        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
486                Integer.valueOf(CallLog.Calls.INCOMING_TYPE));
487        insertedValues = verifyInsertionWithCapture(OTHER_USER_ID);
488        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
489                Integer.valueOf(CallLog.Calls.INCOMING_TYPE));
490        verifyNoInsertionInUser(MANAGED_USER_ID);
491    }
492
493    @MediumTest
494    public void testLogCallDirectionOutgoingWithMultiUserCapabilityFromManagedProfile() {
495        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
496                .thenReturn(makeFakePhoneAccount(mManagedProfileAccountHandle,
497                        PhoneAccount.CAPABILITY_MULTI_USER));
498        Call fakeOutgoingCall = makeFakeCall(
499                DisconnectCause.OTHER, // disconnectCauseCode
500                false, // isConference
501                false, // isIncoming
502                1L, // creationTimeMillis
503                1000L, // ageMillis
504                TEL_PHONEHANDLE, // callHandle
505                mManagedProfileAccountHandle, // phoneAccountHandle
506                NO_VIDEO_STATE, // callVideoState
507                POST_DIAL_STRING, // postDialDigits
508                VIA_NUMBER_STRING, // viaNumber
509                UserHandle.of(MANAGED_USER_ID)
510        );
511        mCallLogManager.onCallStateChanged(fakeOutgoingCall, CallState.ACTIVE,
512                CallState.DISCONNECTED);
513
514        // Outgoing call placed through work dialer should be inserted to managed profile only.
515        verifyNoInsertionInUser(CURRENT_USER_ID);
516        verifyNoInsertionInUser(OTHER_USER_ID);
517        ContentValues insertedValues = verifyInsertionWithCapture(MANAGED_USER_ID);
518        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
519                Integer.valueOf(CallLog.Calls.OUTGOING_TYPE));
520    }
521
522    @MediumTest
523    @FlakyTest
524    @Postsubmit
525    public void testLogCallDirectionOutgoingFromManagedProfile() {
526        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
527                .thenReturn(makeFakePhoneAccount(mManagedProfileAccountHandle, 0));
528        Call fakeOutgoingCall = makeFakeCall(
529                DisconnectCause.OTHER, // disconnectCauseCode
530                false, // isConference
531                false, // isIncoming
532                1L, // creationTimeMillis
533                1000L, // ageMillis
534                TEL_PHONEHANDLE, // callHandle
535                mManagedProfileAccountHandle, // phoneAccountHandle
536                NO_VIDEO_STATE, // callVideoState
537                POST_DIAL_STRING, // postDialDigits
538                VIA_NUMBER_STRING, // viaNumber
539                UserHandle.of(MANAGED_USER_ID)
540        );
541        mCallLogManager.onCallStateChanged(fakeOutgoingCall, CallState.ACTIVE,
542                CallState.DISCONNECTED);
543
544        // Outgoing call using phone account in managed profile should be inserted to managed
545        // profile only.
546        verifyNoInsertionInUser(CURRENT_USER_ID);
547        verifyNoInsertionInUser(OTHER_USER_ID);
548        ContentValues insertedValues = verifyInsertionWithCapture(MANAGED_USER_ID);
549        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
550                Integer.valueOf(CallLog.Calls.OUTGOING_TYPE));
551    }
552
553    @MediumTest
554    public void testLogCallDirectionIngoingFromManagedProfile() {
555        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
556                .thenReturn(makeFakePhoneAccount(mManagedProfileAccountHandle, 0));
557        Call fakeOutgoingCall = makeFakeCall(
558                DisconnectCause.OTHER, // disconnectCauseCode
559                false, // isConference
560                true, // isIncoming
561                1L, // creationTimeMillis
562                1000L, // ageMillis
563                TEL_PHONEHANDLE, // callHandle
564                mManagedProfileAccountHandle, // phoneAccountHandle
565                NO_VIDEO_STATE, // callVideoState
566                POST_DIAL_STRING, // postDialDigits
567                VIA_NUMBER_STRING, // viaNumber
568                null
569        );
570        mCallLogManager.onCallStateChanged(fakeOutgoingCall, CallState.ACTIVE,
571                CallState.DISCONNECTED);
572
573        // Incoming call using phone account in managed profile should be inserted to managed
574        // profile only.
575        verifyNoInsertionInUser(CURRENT_USER_ID);
576        verifyNoInsertionInUser(OTHER_USER_ID);
577        ContentValues insertedValues = verifyInsertionWithCapture(MANAGED_USER_ID);
578        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
579                Integer.valueOf(Calls.INCOMING_TYPE));
580    }
581
582    /**
583     * Ensure call data usage is persisted to the call log when present in the call.
584     */
585    @MediumTest
586    public void testLogCallDataUsageSet() {
587        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
588                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
589        Call fakeVideoCall = makeFakeCall(
590                DisconnectCause.OTHER, // disconnectCauseCode
591                false, // isConference
592                true, // isIncoming
593                1L, // creationTimeMillis
594                1000L, // ageMillis
595                TEL_PHONEHANDLE, // callHandle
596                mDefaultAccountHandle, // phoneAccountHandle
597                BIDIRECTIONAL_VIDEO_STATE, // callVideoState
598                POST_DIAL_STRING, // postDialDigits
599                VIA_NUMBER_STRING, // viaNumber
600                UserHandle.of(CURRENT_USER_ID), // initiatingUser
601                1000 // callDataUsage
602        );
603        mCallLogManager.onCallStateChanged(fakeVideoCall, CallState.ACTIVE, CallState.DISCONNECTED);
604        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
605        assertEquals(Long.valueOf(1000), insertedValues.getAsLong(CallLog.Calls.DATA_USAGE));
606    }
607
608    /**
609     * Ensures call data usage is null in the call log when not set on the call.
610     */
611    @MediumTest
612    public void testLogCallDataUsageNotSet() {
613        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
614                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
615        Call fakeVideoCall = makeFakeCall(
616                DisconnectCause.OTHER, // disconnectCauseCode
617                false, // isConference
618                true, // isIncoming
619                1L, // creationTimeMillis
620                1000L, // ageMillis
621                TEL_PHONEHANDLE, // callHandle
622                mDefaultAccountHandle, // phoneAccountHandle
623                BIDIRECTIONAL_VIDEO_STATE, // callVideoState
624                POST_DIAL_STRING, // postDialDigits
625                VIA_NUMBER_STRING, // viaNumber
626                UserHandle.of(CURRENT_USER_ID), // initiatingUser
627                Call.DATA_USAGE_NOT_SET // callDataUsage
628        );
629        mCallLogManager.onCallStateChanged(fakeVideoCall, CallState.ACTIVE, CallState.DISCONNECTED);
630        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
631        assertNull(insertedValues.getAsLong(CallLog.Calls.DATA_USAGE));
632    }
633
634    @SmallTest
635    public void testCountryIso_setCache() {
636        Country testCountry = new Country(TEST_ISO, Country.COUNTRY_SOURCE_LOCALE);
637        CountryDetector mockDetector = (CountryDetector) mContext.getSystemService(
638                Context.COUNTRY_DETECTOR);
639        when(mockDetector.detectCountry()).thenReturn(testCountry);
640
641        String resultIso = mCallLogManager.getCountryIso();
642
643        verifyCountryIso(mockDetector, resultIso);
644    }
645
646    @SmallTest
647    public void testCountryIso_newCountryDetected() {
648        Country testCountry = new Country(TEST_ISO, Country.COUNTRY_SOURCE_LOCALE);
649        Country testCountry2 = new Country(TEST_ISO_2, Country.COUNTRY_SOURCE_LOCALE);
650        CountryDetector mockDetector = (CountryDetector) mContext.getSystemService(
651                Context.COUNTRY_DETECTOR);
652        when(mockDetector.detectCountry()).thenReturn(testCountry);
653        // Put TEST_ISO in the Cache
654        String resultIso = mCallLogManager.getCountryIso();
655        ArgumentCaptor<CountryListener> captor = verifyCountryIso(mockDetector, resultIso);
656
657        // Change ISO to TEST_ISO_2
658        CountryListener listener = captor.getValue();
659        listener.onCountryDetected(testCountry2);
660
661        String resultIso2 = mCallLogManager.getCountryIso();
662        assertEquals(TEST_ISO_2, resultIso2);
663    }
664
665    private ArgumentCaptor<CountryListener> verifyCountryIso(CountryDetector mockDetector,
666            String resultIso) {
667        ArgumentCaptor<CountryListener> captor = ArgumentCaptor.forClass(CountryListener.class);
668        verify(mockDetector).addCountryListener(captor.capture(), any(Looper.class));
669        assertEquals(TEST_ISO, resultIso);
670        return captor;
671    }
672
673    private void verifyNoInsertion() {
674        try {
675            Thread.sleep(TEST_TIMEOUT_MILLIS);
676            verify(mContentProvider, never()).insert(any(String.class),
677                    any(Uri.class), any(ContentValues.class));
678        } catch (android.os.RemoteException e) {
679            fail("Remote exception occurred during test execution");
680        } catch (InterruptedException e) {
681            e.printStackTrace();
682        }
683    }
684
685
686    private void verifyNoInsertionInUser(int userId) {
687        try {
688            Uri uri = ContentProvider.maybeAddUserId(CallLog.Calls.CONTENT_URI, userId);
689            Thread.sleep(TEST_TIMEOUT_MILLIS);
690            verify(getContentProviderForUser(userId), never())
691                    .insert(any(String.class), eq(uri), any(ContentValues.class));
692        } catch (android.os.RemoteException e) {
693            fail("Remote exception occurred during test execution");
694        } catch (InterruptedException e) {
695            e.printStackTrace();
696        }
697    }
698
699    private ContentValues verifyInsertionWithCapture(int userId) {
700        ArgumentCaptor<ContentValues> captor = ArgumentCaptor.forClass(ContentValues.class);
701        try {
702            Uri uri = ContentProvider.maybeAddUserId(CallLog.Calls.CONTENT_URI, userId);
703            verify(getContentProviderForUser(userId), timeout(TEST_TIMEOUT_MILLIS).atLeastOnce())
704                    .insert(any(String.class), eq(uri), captor.capture());
705        } catch (android.os.RemoteException e) {
706            fail("Remote exception occurred during test execution");
707        }
708
709        return captor.getValue();
710    }
711
712    private IContentProvider getContentProviderForUser(int userId) {
713        return mContext.getContentResolver().acquireProvider(userId + "@call_log");
714    }
715
716    private Call makeFakeCall(int disconnectCauseCode, boolean isConference, boolean isIncoming,
717            long creationTimeMillis, long ageMillis, Uri callHandle,
718            PhoneAccountHandle phoneAccountHandle, int callVideoState,
719            String postDialDigits, String viaNumber, UserHandle initiatingUser) {
720        return makeFakeCall(disconnectCauseCode, isConference, isIncoming, creationTimeMillis,
721                ageMillis, callHandle, phoneAccountHandle, callVideoState, postDialDigits,
722                viaNumber, initiatingUser, Call.DATA_USAGE_NOT_SET);
723    }
724
725    private Call makeFakeCall(int disconnectCauseCode, boolean isConference, boolean isIncoming,
726            long creationTimeMillis, long ageMillis, Uri callHandle,
727            PhoneAccountHandle phoneAccountHandle, int callVideoState,
728            String postDialDigits, String viaNumber, UserHandle initiatingUser,
729            long callDataUsage) {
730        Call fakeCall = mock(Call.class);
731        when(fakeCall.getDisconnectCause()).thenReturn(
732                new DisconnectCause(disconnectCauseCode));
733        when(fakeCall.isConference()).thenReturn(isConference);
734        when(fakeCall.isIncoming()).thenReturn(isIncoming);
735        when(fakeCall.getCreationTimeMillis()).thenReturn(creationTimeMillis);
736        when(fakeCall.getAgeMillis()).thenReturn(ageMillis);
737        when(fakeCall.getOriginalHandle()).thenReturn(callHandle);
738        when(fakeCall.getTargetPhoneAccount()).thenReturn(phoneAccountHandle);
739        when(fakeCall.getVideoStateHistory()).thenReturn(callVideoState);
740        when(fakeCall.getPostDialDigits()).thenReturn(postDialDigits);
741        when(fakeCall.getViaNumber()).thenReturn(viaNumber);
742        when(fakeCall.getInitiatingUser()).thenReturn(initiatingUser);
743        when(fakeCall.getCallDataUsage()).thenReturn(callDataUsage);
744        when(fakeCall.isEmergencyCall()).thenReturn(
745                phoneAccountHandle.equals(EMERGENCY_ACCT_HANDLE));
746        return fakeCall;
747    }
748
749    private PhoneAccount makeFakePhoneAccount(PhoneAccountHandle phoneAccountHandle,
750            int capabilities) {
751        return PhoneAccount.builder(phoneAccountHandle, "testing")
752                .setCapabilities(capabilities).build();
753    }
754}
755