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