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