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