CallLogManagerTest.java revision 8755e29fd54403b404b4940d3dccebf83a8acbcf
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        // Timeout needed because showMissedCallNotification is called from onPostExecute.
289        verify(mMissedCallNotifier, timeout(TEST_TIMEOUT_MILLIS))
290                .showMissedCallNotification(any(MissedCallNotifier.CallInfo.class));
291    }
292
293    @MediumTest
294    public void testLogCallDirectionRejected() {
295        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
296                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
297        Call fakeMissedCall = makeFakeCall(
298                DisconnectCause.REJECTED, // disconnectCauseCode
299                false, // isConference
300                true, // isIncoming
301                1L, // creationTimeMillis
302                1000L, // ageMillis
303                TEL_PHONEHANDLE, // callHandle
304                mDefaultAccountHandle, // phoneAccountHandle
305                NO_VIDEO_STATE, // callVideoState
306                POST_DIAL_STRING, // postDialDigits
307                VIA_NUMBER_STRING, // viaNumber
308                null
309        );
310
311        mCallLogManager.onCallStateChanged(fakeMissedCall, CallState.ACTIVE,
312                CallState.DISCONNECTED);
313        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
314        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
315                Integer.valueOf(Calls.REJECTED_TYPE));
316    }
317
318    @MediumTest
319    public void testCreationTimeAndAge() {
320        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
321                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
322        long currentTime = System.currentTimeMillis();
323        long duration = 1000L;
324        Call fakeCall = makeFakeCall(
325                DisconnectCause.OTHER, // disconnectCauseCode
326                false, // isConference
327                false, // isIncoming
328                currentTime, // creationTimeMillis
329                duration, // ageMillis
330                TEL_PHONEHANDLE, // callHandle
331                mDefaultAccountHandle, // phoneAccountHandle
332                NO_VIDEO_STATE, // callVideoState
333                POST_DIAL_STRING, // postDialDigits
334                VIA_NUMBER_STRING, // viaNumber
335                UserHandle.of(CURRENT_USER_ID)
336        );
337        mCallLogManager.onCallStateChanged(fakeCall, CallState.ACTIVE, CallState.DISCONNECTED);
338        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
339        assertEquals(insertedValues.getAsLong(CallLog.Calls.DATE),
340                Long.valueOf(currentTime));
341        assertEquals(insertedValues.getAsLong(CallLog.Calls.DURATION),
342                Long.valueOf(duration / 1000));
343    }
344
345    @MediumTest
346    public void testLogPhoneAccountId() {
347        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
348                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
349        Call fakeCall = makeFakeCall(
350                DisconnectCause.OTHER, // disconnectCauseCode
351                false, // isConference
352                true, // isIncoming
353                1L, // creationTimeMillis
354                1000L, // ageMillis
355                TEL_PHONEHANDLE, // callHandle
356                mDefaultAccountHandle, // phoneAccountHandle
357                NO_VIDEO_STATE, // callVideoState
358                POST_DIAL_STRING, // postDialDigits
359                VIA_NUMBER_STRING, // viaNumber
360                UserHandle.of(CURRENT_USER_ID)
361        );
362        mCallLogManager.onCallStateChanged(fakeCall, CallState.ACTIVE, CallState.DISCONNECTED);
363        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
364        assertEquals(insertedValues.getAsString(CallLog.Calls.PHONE_ACCOUNT_ID),
365                TEST_PHONE_ACCOUNT_ID);
366    }
367
368    @MediumTest
369    public void testLogCorrectPhoneNumber() {
370        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
371                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
372        Call fakeCall = makeFakeCall(
373                DisconnectCause.OTHER, // disconnectCauseCode
374                false, // isConference
375                true, // isIncoming
376                1L, // creationTimeMillis
377                1000L, // ageMillis
378                TEL_PHONEHANDLE, // callHandle
379                mDefaultAccountHandle, // phoneAccountHandle
380                NO_VIDEO_STATE, // callVideoState
381                POST_DIAL_STRING, // postDialDigits
382                VIA_NUMBER_STRING, // viaNumber
383                UserHandle.of(CURRENT_USER_ID)
384        );
385        mCallLogManager.onCallStateChanged(fakeCall, CallState.ACTIVE, CallState.DISCONNECTED);
386        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
387        assertEquals(insertedValues.getAsString(CallLog.Calls.NUMBER),
388                TEL_PHONEHANDLE.getSchemeSpecificPart());
389        assertEquals(insertedValues.getAsString(CallLog.Calls.POST_DIAL_DIGITS), POST_DIAL_STRING);
390        String expectedNumber = PhoneNumberUtils.formatNumber(VIA_NUMBER_STRING, "US");
391        assertEquals(insertedValues.getAsString(Calls.VIA_NUMBER), expectedNumber);
392    }
393
394    @MediumTest
395    public void testLogCallVideoFeatures() {
396        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
397                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
398        Call fakeVideoCall = makeFakeCall(
399                DisconnectCause.OTHER, // disconnectCauseCode
400                false, // isConference
401                true, // isIncoming
402                1L, // creationTimeMillis
403                1000L, // ageMillis
404                TEL_PHONEHANDLE, // callHandle
405                mDefaultAccountHandle, // phoneAccountHandle
406                BIDIRECTIONAL_VIDEO_STATE, // callVideoState
407                POST_DIAL_STRING, // postDialDigits
408                VIA_NUMBER_STRING, // viaNumber
409                UserHandle.of(CURRENT_USER_ID)
410        );
411        mCallLogManager.onCallStateChanged(fakeVideoCall, CallState.ACTIVE, CallState.DISCONNECTED);
412        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
413        assertTrue((insertedValues.getAsInteger(CallLog.Calls.FEATURES)
414                & CallLog.Calls.FEATURES_VIDEO) == CallLog.Calls.FEATURES_VIDEO);
415    }
416
417    @MediumTest
418    public void testLogCallDirectionOutgoingWithMultiUserCapability() {
419        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
420                .thenReturn(makeFakePhoneAccount(mOtherUserAccountHandle,
421                        PhoneAccount.CAPABILITY_MULTI_USER));
422        Call fakeOutgoingCall = makeFakeCall(
423                DisconnectCause.OTHER, // disconnectCauseCode
424                false, // isConference
425                false, // isIncoming
426                1L, // creationTimeMillis
427                1000L, // ageMillis
428                TEL_PHONEHANDLE, // callHandle
429                mDefaultAccountHandle, // phoneAccountHandle
430                NO_VIDEO_STATE, // callVideoState
431                POST_DIAL_STRING, // postDialDigits
432                VIA_NUMBER_STRING, // viaNumber
433                UserHandle.of(CURRENT_USER_ID)
434        );
435        mCallLogManager.onCallStateChanged(fakeOutgoingCall, CallState.ACTIVE,
436                CallState.DISCONNECTED);
437
438        // Outgoing call placed through a phone account with multi user capability is inserted to
439        // all users except managed profile.
440        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
441        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
442                Integer.valueOf(CallLog.Calls.OUTGOING_TYPE));
443        insertedValues = verifyInsertionWithCapture(OTHER_USER_ID);
444        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
445                Integer.valueOf(CallLog.Calls.OUTGOING_TYPE));
446        verifyNoInsertionInUser(MANAGED_USER_ID);
447    }
448
449    @MediumTest
450    public void testLogCallDirectionIncomingWithMultiUserCapability() {
451        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
452                .thenReturn(makeFakePhoneAccount(mOtherUserAccountHandle,
453                        PhoneAccount.CAPABILITY_MULTI_USER));
454        Call fakeIncomingCall = makeFakeCall(
455                DisconnectCause.OTHER, // disconnectCauseCode
456                false, // isConference
457                true, // isIncoming
458                1L, // creationTimeMillis
459                1000L, // ageMillis
460                TEL_PHONEHANDLE, // callHandle
461                mDefaultAccountHandle, // phoneAccountHandle
462                NO_VIDEO_STATE, // callVideoState
463                POST_DIAL_STRING, // postDialDigits
464                VIA_NUMBER_STRING, // viaNumber
465                null
466        );
467        mCallLogManager.onCallStateChanged(fakeIncomingCall, CallState.ACTIVE,
468                CallState.DISCONNECTED);
469
470        // Incoming call using a phone account with multi user capability is inserted to all users
471        // except managed profile.
472        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
473        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
474                Integer.valueOf(CallLog.Calls.INCOMING_TYPE));
475        insertedValues = verifyInsertionWithCapture(OTHER_USER_ID);
476        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
477                Integer.valueOf(CallLog.Calls.INCOMING_TYPE));
478        verifyNoInsertionInUser(MANAGED_USER_ID);
479    }
480
481    @MediumTest
482    public void testLogCallDirectionOutgoingWithMultiUserCapabilityFromManagedProfile() {
483        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
484                .thenReturn(makeFakePhoneAccount(mManagedProfileAccountHandle,
485                        PhoneAccount.CAPABILITY_MULTI_USER));
486        Call fakeOutgoingCall = makeFakeCall(
487                DisconnectCause.OTHER, // disconnectCauseCode
488                false, // isConference
489                false, // isIncoming
490                1L, // creationTimeMillis
491                1000L, // ageMillis
492                TEL_PHONEHANDLE, // callHandle
493                mManagedProfileAccountHandle, // phoneAccountHandle
494                NO_VIDEO_STATE, // callVideoState
495                POST_DIAL_STRING, // postDialDigits
496                VIA_NUMBER_STRING, // viaNumber
497                UserHandle.of(MANAGED_USER_ID)
498        );
499        mCallLogManager.onCallStateChanged(fakeOutgoingCall, CallState.ACTIVE,
500                CallState.DISCONNECTED);
501
502        // Outgoing call placed through work dialer should be inserted to managed profile only.
503        verifyNoInsertionInUser(CURRENT_USER_ID);
504        verifyNoInsertionInUser(OTHER_USER_ID);
505        ContentValues insertedValues = verifyInsertionWithCapture(MANAGED_USER_ID);
506        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
507                Integer.valueOf(CallLog.Calls.OUTGOING_TYPE));
508    }
509
510    @MediumTest
511    public void testLogCallDirectionOutgoingFromManagedProfile() {
512        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
513                .thenReturn(makeFakePhoneAccount(mManagedProfileAccountHandle, 0));
514        Call fakeOutgoingCall = makeFakeCall(
515                DisconnectCause.OTHER, // disconnectCauseCode
516                false, // isConference
517                false, // isIncoming
518                1L, // creationTimeMillis
519                1000L, // ageMillis
520                TEL_PHONEHANDLE, // callHandle
521                mManagedProfileAccountHandle, // phoneAccountHandle
522                NO_VIDEO_STATE, // callVideoState
523                POST_DIAL_STRING, // postDialDigits
524                VIA_NUMBER_STRING, // viaNumber
525                UserHandle.of(MANAGED_USER_ID)
526        );
527        mCallLogManager.onCallStateChanged(fakeOutgoingCall, CallState.ACTIVE,
528                CallState.DISCONNECTED);
529
530        // Outgoing call using phone account in managed profile should be inserted to managed
531        // profile only.
532        verifyNoInsertionInUser(CURRENT_USER_ID);
533        verifyNoInsertionInUser(OTHER_USER_ID);
534        ContentValues insertedValues = verifyInsertionWithCapture(MANAGED_USER_ID);
535        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
536                Integer.valueOf(CallLog.Calls.OUTGOING_TYPE));
537    }
538
539    @MediumTest
540    public void testLogCallDirectionIngoingFromManagedProfile() {
541        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
542                .thenReturn(makeFakePhoneAccount(mManagedProfileAccountHandle, 0));
543        Call fakeOutgoingCall = makeFakeCall(
544                DisconnectCause.OTHER, // disconnectCauseCode
545                false, // isConference
546                true, // isIncoming
547                1L, // creationTimeMillis
548                1000L, // ageMillis
549                TEL_PHONEHANDLE, // callHandle
550                mManagedProfileAccountHandle, // phoneAccountHandle
551                NO_VIDEO_STATE, // callVideoState
552                POST_DIAL_STRING, // postDialDigits
553                VIA_NUMBER_STRING, // viaNumber
554                null
555        );
556        mCallLogManager.onCallStateChanged(fakeOutgoingCall, CallState.ACTIVE,
557                CallState.DISCONNECTED);
558
559        // Incoming call using phone account in managed profile should be inserted to managed
560        // profile only.
561        verifyNoInsertionInUser(CURRENT_USER_ID);
562        verifyNoInsertionInUser(OTHER_USER_ID);
563        ContentValues insertedValues = verifyInsertionWithCapture(MANAGED_USER_ID);
564        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
565                Integer.valueOf(Calls.INCOMING_TYPE));
566    }
567
568    /**
569     * Ensure call data usage is persisted to the call log when present in the call.
570     */
571    @MediumTest
572    public void testLogCallDataUsageSet() {
573        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
574                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
575        Call fakeVideoCall = makeFakeCall(
576                DisconnectCause.OTHER, // disconnectCauseCode
577                false, // isConference
578                true, // isIncoming
579                1L, // creationTimeMillis
580                1000L, // ageMillis
581                TEL_PHONEHANDLE, // callHandle
582                mDefaultAccountHandle, // phoneAccountHandle
583                BIDIRECTIONAL_VIDEO_STATE, // callVideoState
584                POST_DIAL_STRING, // postDialDigits
585                VIA_NUMBER_STRING, // viaNumber
586                UserHandle.of(CURRENT_USER_ID), // initiatingUser
587                1000 // callDataUsage
588        );
589        mCallLogManager.onCallStateChanged(fakeVideoCall, CallState.ACTIVE, CallState.DISCONNECTED);
590        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
591        assertEquals(Long.valueOf(1000), insertedValues.getAsLong(CallLog.Calls.DATA_USAGE));
592    }
593
594    /**
595     * Ensures call data usage is null in the call log when not set on the call.
596     */
597    @MediumTest
598    public void testLogCallDataUsageNotSet() {
599        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
600                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
601        Call fakeVideoCall = makeFakeCall(
602                DisconnectCause.OTHER, // disconnectCauseCode
603                false, // isConference
604                true, // isIncoming
605                1L, // creationTimeMillis
606                1000L, // ageMillis
607                TEL_PHONEHANDLE, // callHandle
608                mDefaultAccountHandle, // phoneAccountHandle
609                BIDIRECTIONAL_VIDEO_STATE, // callVideoState
610                POST_DIAL_STRING, // postDialDigits
611                VIA_NUMBER_STRING, // viaNumber
612                UserHandle.of(CURRENT_USER_ID), // initiatingUser
613                Call.DATA_USAGE_NOT_SET // callDataUsage
614        );
615        mCallLogManager.onCallStateChanged(fakeVideoCall, CallState.ACTIVE, CallState.DISCONNECTED);
616        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
617        assertNull(insertedValues.getAsLong(CallLog.Calls.DATA_USAGE));
618    }
619
620    @SmallTest
621    public void testCountryIso_setCache() {
622        Country testCountry = new Country(TEST_ISO, Country.COUNTRY_SOURCE_LOCALE);
623        CountryDetector mockDetector = (CountryDetector) mContext.getSystemService(
624                Context.COUNTRY_DETECTOR);
625        when(mockDetector.detectCountry()).thenReturn(testCountry);
626
627        String resultIso = mCallLogManager.getCountryIso();
628
629        verifyCountryIso(mockDetector, resultIso);
630    }
631
632    @SmallTest
633    public void testCountryIso_newCountryDetected() {
634        Country testCountry = new Country(TEST_ISO, Country.COUNTRY_SOURCE_LOCALE);
635        Country testCountry2 = new Country(TEST_ISO_2, Country.COUNTRY_SOURCE_LOCALE);
636        CountryDetector mockDetector = (CountryDetector) mContext.getSystemService(
637                Context.COUNTRY_DETECTOR);
638        when(mockDetector.detectCountry()).thenReturn(testCountry);
639        // Put TEST_ISO in the Cache
640        String resultIso = mCallLogManager.getCountryIso();
641        ArgumentCaptor<CountryListener> captor = verifyCountryIso(mockDetector, resultIso);
642
643        // Change ISO to TEST_ISO_2
644        CountryListener listener = captor.getValue();
645        listener.onCountryDetected(testCountry2);
646
647        String resultIso2 = mCallLogManager.getCountryIso();
648        assertEquals(TEST_ISO_2, resultIso2);
649    }
650
651    private ArgumentCaptor<CountryListener> verifyCountryIso(CountryDetector mockDetector,
652            String resultIso) {
653        ArgumentCaptor<CountryListener> captor = ArgumentCaptor.forClass(CountryListener.class);
654        verify(mockDetector).addCountryListener(captor.capture(), any(Looper.class));
655        assertEquals(TEST_ISO, resultIso);
656        return captor;
657    }
658
659    private void verifyNoInsertion() {
660        try {
661            verify(mContentProvider, timeout(TEST_TIMEOUT_MILLIS).never()).insert(any(String.class),
662                    any(Uri.class), any(ContentValues.class));
663        } catch (android.os.RemoteException e) {
664            fail("Remote exception occurred during test execution");
665        }
666    }
667
668
669    private void verifyNoInsertionInUser(int userId) {
670        try {
671            Uri uri = ContentProvider.maybeAddUserId(CallLog.Calls.CONTENT_URI, userId);
672            verify(getContentProviderForUser(userId), timeout(TEST_TIMEOUT_MILLIS).never())
673                    .insert(any(String.class), eq(uri), any(ContentValues.class));
674        } catch (android.os.RemoteException e) {
675            fail("Remote exception occurred during test execution");
676        }
677    }
678
679    private ContentValues verifyInsertionWithCapture(int userId) {
680        ArgumentCaptor<ContentValues> captor = ArgumentCaptor.forClass(ContentValues.class);
681        try {
682            Uri uri = ContentProvider.maybeAddUserId(CallLog.Calls.CONTENT_URI, userId);
683            verify(getContentProviderForUser(userId), timeout(TEST_TIMEOUT_MILLIS).atLeastOnce())
684                    .insert(any(String.class), eq(uri), captor.capture());
685        } catch (android.os.RemoteException e) {
686            fail("Remote exception occurred during test execution");
687        }
688
689        return captor.getValue();
690    }
691
692    private IContentProvider getContentProviderForUser(int userId) {
693        return mContext.getContentResolver().acquireProvider(userId + "@call_log");
694    }
695
696    private Call makeFakeCall(int disconnectCauseCode, boolean isConference, boolean isIncoming,
697            long creationTimeMillis, long ageMillis, Uri callHandle,
698            PhoneAccountHandle phoneAccountHandle, int callVideoState,
699            String postDialDigits, String viaNumber, UserHandle initiatingUser) {
700        return makeFakeCall(disconnectCauseCode, isConference, isIncoming, creationTimeMillis,
701                ageMillis, callHandle, phoneAccountHandle, callVideoState, postDialDigits,
702                viaNumber, initiatingUser, Call.DATA_USAGE_NOT_SET);
703    }
704
705    private Call makeFakeCall(int disconnectCauseCode, boolean isConference, boolean isIncoming,
706            long creationTimeMillis, long ageMillis, Uri callHandle,
707            PhoneAccountHandle phoneAccountHandle, int callVideoState,
708            String postDialDigits, String viaNumber, UserHandle initiatingUser,
709            long callDataUsage) {
710        Call fakeCall = mock(Call.class);
711        when(fakeCall.getDisconnectCause()).thenReturn(
712                new DisconnectCause(disconnectCauseCode));
713        when(fakeCall.isConference()).thenReturn(isConference);
714        when(fakeCall.isIncoming()).thenReturn(isIncoming);
715        when(fakeCall.getCreationTimeMillis()).thenReturn(creationTimeMillis);
716        when(fakeCall.getAgeMillis()).thenReturn(ageMillis);
717        when(fakeCall.getOriginalHandle()).thenReturn(callHandle);
718        when(fakeCall.getTargetPhoneAccount()).thenReturn(phoneAccountHandle);
719        when(fakeCall.getVideoStateHistory()).thenReturn(callVideoState);
720        when(fakeCall.getPostDialDigits()).thenReturn(postDialDigits);
721        when(fakeCall.getViaNumber()).thenReturn(viaNumber);
722        when(fakeCall.getInitiatingUser()).thenReturn(initiatingUser);
723        when(fakeCall.getCallDataUsage()).thenReturn(callDataUsage);
724        return fakeCall;
725    }
726
727    private PhoneAccount makeFakePhoneAccount(PhoneAccountHandle phoneAccountHandle,
728            int capabilities) {
729        return PhoneAccount.builder(phoneAccountHandle, "testing")
730                .setCapabilities(capabilities).build();
731    }
732}
733