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