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