Merge "Introduce Session logging to CallAudioManager"
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 6d6ee29..382f2aa 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -1030,6 +1030,19 @@
         }
     }
 
+    /**
+     * Silences the ringer.
+     */
+    void silence() {
+        if (mConnectionService == null) {
+            Log.w(this, "silence() request on a call without a connection service.");
+        } else {
+            Log.i(this, "Send silence to connection service for call %s", this);
+            Log.event(this, Log.Events.STOP_DTMF);
+            mConnectionService.silence(this);
+        }
+    }
+
     void disconnect() {
         disconnect(false);
     }
diff --git a/src/com/android/server/telecom/CallLogManager.java b/src/com/android/server/telecom/CallLogManager.java
index 8a0adfb..5567fd8 100755
--- a/src/com/android/server/telecom/CallLogManager.java
+++ b/src/com/android/server/telecom/CallLogManager.java
@@ -20,8 +20,10 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.AsyncTask;
+import android.os.UserHandle;
 import android.provider.CallLog.Calls;
 import android.telecom.DisconnectCause;
+import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.VideoProfile;
 import android.telephony.PhoneNumberUtils;
@@ -55,7 +57,8 @@
          */
         public AddCallArgs(Context context, CallerInfo callerInfo, String number, String postDialDigits,
                 int presentation, int callType, int features, PhoneAccountHandle accountHandle,
-                long creationDate, long durationInMillis, Long dataUsage) {
+                long creationDate, long durationInMillis, Long dataUsage,
+                UserHandle initiatingUser) {
             this.context = context;
             this.callerInfo = callerInfo;
             this.number = number;
@@ -67,6 +70,7 @@
             this.timestamp = creationDate;
             this.durationInSec = (int)(durationInMillis / 1000);
             this.dataUsage = dataUsage;
+            this.initiatingUser = initiatingUser;
         }
         // Since the members are accessed directly, we don't use the
         // mXxxx notation.
@@ -81,11 +85,13 @@
         public final long timestamp;
         public final int durationInSec;
         public final Long dataUsage;
+        public final UserHandle initiatingUser;
     }
 
     private static final String TAG = CallLogManager.class.getSimpleName();
 
     private final Context mContext;
+    private final PhoneAccountRegistrar mPhoneAccountRegistrar;
     private static final String ACTION_CALLS_TABLE_ADD_ENTRY =
                 "com.android.server.telecom.intent.action.CALLS_ADD_ENTRY";
     private static final String PERMISSION_PROCESS_CALLLOG_INFO =
@@ -93,8 +99,9 @@
     private static final String CALL_TYPE = "callType";
     private static final String CALL_DURATION = "duration";
 
-    public CallLogManager(Context context) {
+    public CallLogManager(Context context, PhoneAccountRegistrar phoneAccountRegistrar) {
         mContext = context;
+        mPhoneAccountRegistrar = phoneAccountRegistrar;
     }
 
     @Override
@@ -153,7 +160,7 @@
         int callFeatures = getCallFeatures(call.getVideoStateHistory());
         logCall(call.getCallerInfo(), logNumber, call.getPostDialDigits(),
                 call.getHandlePresentation(), callLogType, callFeatures, accountHandle,
-                creationTime, age, null, call.isEmergencyCall());
+                creationTime, age, null, call.isEmergencyCall(), call.getInitiatingUser());
     }
 
     /**
@@ -182,7 +189,8 @@
             long start,
             long duration,
             Long dataUsage,
-            boolean isEmergency) {
+            boolean isEmergency,
+            UserHandle initiatingUser) {
 
         // On some devices, to avoid accidental redialing of emergency numbers, we *never* log
         // emergency calls to the Call Log.  (This behavior is set on a per-product basis, based
@@ -200,7 +208,8 @@
                     + Log.pii(number) + "," + presentation + ", " + callType
                     + ", " + start + ", " + duration);
             AddCallArgs args = new AddCallArgs(mContext, callerInfo, number, postDialDigits,
-                    presentation, callType, features, accountHandle, start, duration, dataUsage);
+                    presentation, callType, features, accountHandle, start, duration, dataUsage,
+                    initiatingUser);
             logCallAsync(args);
         } else {
           Log.d(TAG, "Not adding emergency call to call log.");
@@ -264,12 +273,9 @@
             Uri[] result = new Uri[count];
             for (int i = 0; i < count; i++) {
                 AddCallArgs c = callList[i];
-
                 try {
                     // May block.
-                    result[i] = Calls.addCall(c.callerInfo, c.context, c.number, c.postDialDigits,
-                            c.presentation, c.callType, c.features, c.accountHandle, c.timestamp,
-                            c.durationInSec, c.dataUsage, true /* addForAllUsers */);
+                    result[i] = addCall(c);
                 } catch (Exception e) {
                     // This is very rare but may happen in legitimate cases.
                     // E.g. If the phone is encrypted and thus write request fails, it may cause
@@ -285,6 +291,34 @@
             return result;
         }
 
+        private Uri addCall(AddCallArgs c) {
+            PhoneAccount phoneAccount = mPhoneAccountRegistrar
+                    .getPhoneAccountUnchecked(c.accountHandle);
+            if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
+                if (c.initiatingUser != null &&
+                        UserUtil.isManagedProfile(mContext, c.initiatingUser)) {
+                    return addCall(c, c.initiatingUser);
+                } else {
+                    return addCall(c, null);
+                }
+            } else {
+                return addCall(c, c.accountHandle.getUserHandle());
+            }
+        }
+
+        /**
+         * Insert the call to a specific user or all users except managed profile.
+         * @param c context
+         * @param userToBeInserted user handle of user that the call going be inserted to. null
+         *                         if insert to all users except managed profile.
+         */
+        private Uri addCall(AddCallArgs c, UserHandle userToBeInserted) {
+            return Calls.addCall(c.callerInfo, c.context, c.number, c.postDialDigits,
+                    c.presentation, c.callType, c.features, c.accountHandle, c.timestamp,
+                    c.durationInSec, c.dataUsage, userToBeInserted == null,
+                    userToBeInserted);
+        }
+
         /**
          * Performs a simple sanity check to make sure the call was written in the database.
          * Typically there is only one result per call so it is easy to identify which one failed.
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 56f0963..6919afa 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -217,7 +217,7 @@
         mTtyManager = new TtyManager(context, mWiredHeadsetManager);
         mProximitySensorManager = proximitySensorManagerFactory.create(context, this);
         mPhoneStateBroadcaster = new PhoneStateBroadcaster(this);
-        mCallLogManager = new CallLogManager(context);
+        mCallLogManager = new CallLogManager(context, phoneAccountRegistrar);
         mInCallController = new InCallController(context, mLock, this);
         mDtmfLocalTonePlayer = new DtmfLocalTonePlayer(context);
         mConnectionServiceRepository =
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index f7046bc..792eb8b 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -661,6 +661,18 @@
         removeCall(call, new DisconnectCause(DisconnectCause.LOCAL));
     }
 
+    /** @see IConnectionService#silence(String) */
+    void silence(Call call) {
+        final String callId = mCallIdMapper.getCallId(call);
+        if (callId != null && isServiceValid("silence")) {
+            try {
+                logOutgoing("silence %s", callId);
+                mServiceInterface.silence(callId);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
     /** @see IConnectionService#hold(String) */
     void hold(Call call) {
         final String callId = mCallIdMapper.getCallId(call);
diff --git a/src/com/android/server/telecom/PhoneAccountRegistrar.java b/src/com/android/server/telecom/PhoneAccountRegistrar.java
index 15a2a30..9780633 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -235,7 +235,7 @@
             mState.defaultOutgoing = null;
         } else {
             // TODO: Do we really want to return for *any* user?
-            PhoneAccount account = getPhoneAccount(accountHandle);
+            PhoneAccount account = getPhoneAccountUnchecked(accountHandle);
             if (account == null) {
                 Log.w(this, "Trying to set nonexistent default outgoing %s",
                         accountHandle);
@@ -374,7 +374,7 @@
      *         otherwise.
      */
     public boolean enablePhoneAccount(PhoneAccountHandle accountHandle, boolean isEnabled) {
-        PhoneAccount account = getPhoneAccount(accountHandle);
+        PhoneAccount account = getPhoneAccountUnchecked(accountHandle);
         if (account == null) {
             Log.w(this, "Could not find account to enable: " + accountHandle);
             return false;
@@ -525,7 +525,7 @@
         // source app provides or else an third party app could enable itself.
         boolean isEnabled = false;
 
-        PhoneAccount oldAccount = getPhoneAccount(account.getAccountHandle());
+        PhoneAccount oldAccount = getPhoneAccountUnchecked(account.getAccountHandle());
         if (oldAccount != null) {
             mState.accounts.remove(oldAccount);
             isEnabled = oldAccount.isEnabled();
@@ -545,7 +545,7 @@
     }
 
     public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
-        PhoneAccount account = getPhoneAccount(accountHandle);
+        PhoneAccount account = getPhoneAccountUnchecked(accountHandle);
         if (account != null) {
             if (mState.accounts.remove(account)) {
                 write();
@@ -688,7 +688,7 @@
      * @param handle
      * @return The corresponding phone account if one exists.
      */
-    PhoneAccount getPhoneAccount(PhoneAccountHandle handle) {
+    public PhoneAccount getPhoneAccountUnchecked(PhoneAccountHandle handle) {
         for (PhoneAccount m : mState.accounts) {
             if (Objects.equals(handle, m.getAccountHandle())) {
                 return m;
@@ -703,7 +703,7 @@
      * device.
      */
     public PhoneAccount getPhoneAccountCheckCallingUser(PhoneAccountHandle handle) {
-        PhoneAccount account = getPhoneAccount(handle);
+        PhoneAccount account = getPhoneAccountUnchecked(handle);
         if (account != null && isVisibleForUser(account)) {
             return account;
         }
diff --git a/src/com/android/server/telecom/Ringer.java b/src/com/android/server/telecom/Ringer.java
index 0ec2298..da22e98 100644
--- a/src/com/android/server/telecom/Ringer.java
+++ b/src/com/android/server/telecom/Ringer.java
@@ -151,6 +151,10 @@
      * Silences the ringer for any actively ringing calls.
      */
     void silence() {
+        for (Call call : mRingingCalls) {
+            call.silence();
+        }
+
         // Remove all calls from the "ringing" set and then update the ringer.
         mRingingCalls.clear();
         updateRinging(null);
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index cd30e52..43084bb 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -201,7 +201,7 @@
                         return null;
                     }
                     // TODO: Do we really want to return for *any* user?
-                    return mPhoneAccountRegistrar.getPhoneAccount(accountHandle);
+                    return mPhoneAccountRegistrar.getPhoneAccountUnchecked(accountHandle);
                 } catch (Exception e) {
                     Log.e(this, e, "getPhoneAccount %s", accountHandle);
                     throw e;
@@ -987,7 +987,7 @@
         if (accountHandle == null) {
             return false;
         }
-        return isVisibleToCaller(mPhoneAccountRegistrar.getPhoneAccount(accountHandle));
+        return isVisibleToCaller(mPhoneAccountRegistrar.getPhoneAccountUnchecked(accountHandle));
     }
 
     private boolean isVisibleToCaller(PhoneAccount account) {
diff --git a/src/com/android/server/telecom/UserUtil.java b/src/com/android/server/telecom/UserUtil.java
new file mode 100644
index 0000000..a35a61e
--- /dev/null
+++ b/src/com/android/server/telecom/UserUtil.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom;
+
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+public final class UserUtil {
+
+    private UserUtil() {
+    }
+
+    private static UserInfo getUserInfoFromUserHandle(Context context, UserHandle userHandle) {
+        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        return userManager.getUserInfo(userHandle.getIdentifier());
+    }
+
+    public static boolean isManagedProfile(Context context, UserHandle userHandle) {
+        UserInfo userInfo = getUserInfoFromUserHandle(context, userHandle);
+        return userInfo != null && userInfo.isManagedProfile();
+    }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java b/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
index a1e58ee..48e8fb3 100644
--- a/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
@@ -18,6 +18,7 @@
 
 
 import android.content.ComponentName;
+import android.content.ContentProvider;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.IContentProvider;
@@ -26,13 +27,16 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.CallLog;
+import android.provider.CallLog.Calls;
 import android.telecom.DisconnectCause;
+import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.VideoProfile;
 
 import com.android.server.telecom.Call;
 import com.android.server.telecom.CallLogManager;
 import com.android.server.telecom.CallState;
+import com.android.server.telecom.PhoneAccountRegistrar;
 import com.android.server.telecom.TelephonyUtil;
 
 import static org.mockito.Matchers.any;
@@ -43,14 +47,17 @@
 import static org.mockito.Mockito.when;
 
 import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
 
-import java.util.Collections;
+import java.util.Arrays;
 
 public class CallLogManagerTest extends TelecomTestCase {
 
     private CallLogManager mCallLogManager;
     private IContentProvider mContentProvider;
     private PhoneAccountHandle mDefaultAccountHandle;
+    private PhoneAccountHandle mOtherUserAccountHandle;
+    private PhoneAccountHandle mManagedProfileAccountHandle;
 
     private static final Uri TEL_PHONEHANDLE = Uri.parse("tel:5555551234");
 
@@ -64,25 +71,52 @@
     private static final String TEST_PHONE_ACCOUNT_ID= "testPhoneAccountId";
 
     private static final int TEST_TIMEOUT_MILLIS = 100;
+    private static final int CURRENT_USER_ID = 0;
+    private static final int OTHER_USER_ID = 10;
+    private static final int MANAGED_USER_ID = 11;
+
+    @Mock
+    PhoneAccountRegistrar mMockPhoneAccountRegistrar;
+
 
     @Override
     public void setUp() throws Exception {
         super.setUp();
         mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
-        mCallLogManager = new CallLogManager(mContext);
+        mCallLogManager = new CallLogManager(mContext, mMockPhoneAccountRegistrar);
         mContentProvider = mContext.getContentResolver().acquireProvider("test");
         mDefaultAccountHandle = new PhoneAccountHandle(
                 new ComponentName("com.android.server.telecom.tests", "CallLogManagerTest"),
-                TEST_PHONE_ACCOUNT_ID
+                TEST_PHONE_ACCOUNT_ID,
+                UserHandle.of(CURRENT_USER_ID)
+        );
+
+        mOtherUserAccountHandle = new PhoneAccountHandle(
+                new ComponentName("com.android.server.telecom.tests", "CallLogManagerTest"),
+                TEST_PHONE_ACCOUNT_ID,
+                UserHandle.of(OTHER_USER_ID)
+        );
+
+        mManagedProfileAccountHandle = new PhoneAccountHandle(
+                new ComponentName("com.android.server.telecom.tests", "CallLogManagerTest"),
+                TEST_PHONE_ACCOUNT_ID,
+                UserHandle.of(MANAGED_USER_ID)
         );
 
         UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-        UserInfo userInfo = new UserInfo(UserHandle.USER_CURRENT, "test", 0);
+        UserInfo userInfo = new UserInfo(CURRENT_USER_ID, "test", 0);
+        UserInfo otherUserInfo = new UserInfo(OTHER_USER_ID, "test2", 0);
+        UserInfo managedProfileUserInfo = new UserInfo(OTHER_USER_ID, "test3",
+                UserInfo.FLAG_MANAGED_PROFILE);
+
         when(userManager.isUserRunning(any(UserHandle.class))).thenReturn(true);
         when(userManager.hasUserRestriction(any(String.class), any(UserHandle.class)))
                 .thenReturn(false);
         when(userManager.getUsers(any(Boolean.class)))
-                .thenReturn(Collections.singletonList(userInfo));
+                .thenReturn(Arrays.asList(userInfo, otherUserInfo, managedProfileUserInfo));
+        when(userManager.getUserInfo(eq(CURRENT_USER_ID))).thenReturn(userInfo);
+        when(userManager.getUserInfo(eq(OTHER_USER_ID))).thenReturn(otherUserInfo);
+        when(userManager.getUserInfo(eq(MANAGED_USER_ID))).thenReturn(managedProfileUserInfo);
     }
 
     public void testDontLogCancelledCall() {
@@ -95,7 +129,8 @@
                 TEL_PHONEHANDLE, // callHandle
                 mDefaultAccountHandle, // phoneAccountHandle
                 NO_VIDEO_STATE, // callVideoState
-                POST_DIAL_STRING // postDialDigits
+                POST_DIAL_STRING, // postDialDigits,
+                UserHandle.of(CURRENT_USER_ID)
         );
         mCallLogManager.onCallStateChanged(fakeCall, CallState.DIALING, CallState.DISCONNECTED);
         verifyNoInsertion();
@@ -104,6 +139,8 @@
     }
 
     public void testDontLogChoosingAccountCall() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
         Call fakeCall = makeFakeCall(
                 DisconnectCause.OTHER, // disconnectCauseCode
                 false, // isConference
@@ -113,7 +150,8 @@
                 TEL_PHONEHANDLE, // callHandle
                 mDefaultAccountHandle, // phoneAccountHandle
                 NO_VIDEO_STATE, // callVideoState
-                POST_DIAL_STRING // postDialDigits
+                POST_DIAL_STRING, // postDialDigits
+                UserHandle.of(CURRENT_USER_ID)
         );
         mCallLogManager.onCallStateChanged(fakeCall, CallState.SELECT_PHONE_ACCOUNT,
                 CallState.DISCONNECTED);
@@ -121,6 +159,8 @@
     }
 
     public void testDontLogCallsFromEmergencyAccount() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(EMERGENCY_ACCT_HANDLE, 0));
         mComponentContextFixture.putBooleanResource(R.bool.allow_emergency_numbers_in_call_log,
                 false);
         Call fakeCall = makeFakeCall(
@@ -132,13 +172,16 @@
                 TEL_PHONEHANDLE, // callHandle
                 EMERGENCY_ACCT_HANDLE, // phoneAccountHandle
                 NO_VIDEO_STATE, // callVideoState
-                POST_DIAL_STRING // postDialDigits
+                POST_DIAL_STRING, // postDialDigits,
+                UserHandle.of(CURRENT_USER_ID)
         );
         mCallLogManager.onCallStateChanged(fakeCall, CallState.ACTIVE, CallState.DISCONNECTED);
         verifyNoInsertion();
     }
 
     public void testLogCallDirectionOutgoing() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
         Call fakeOutgoingCall = makeFakeCall(
                 DisconnectCause.OTHER, // disconnectCauseCode
                 false, // isConference
@@ -148,16 +191,19 @@
                 TEL_PHONEHANDLE, // callHandle
                 mDefaultAccountHandle, // phoneAccountHandle
                 NO_VIDEO_STATE, // callVideoState
-                POST_DIAL_STRING // postDialDigits
+                POST_DIAL_STRING, // postDialDigits
+                UserHandle.of(CURRENT_USER_ID)
         );
         mCallLogManager.onCallStateChanged(fakeOutgoingCall, CallState.ACTIVE,
                 CallState.DISCONNECTED);
-        ContentValues insertedValues = verifyInsertionWithCapture();
+        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
         assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
                 Integer.valueOf(CallLog.Calls.OUTGOING_TYPE));
     }
 
     public void testLogCallDirectionIncoming() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
         Call fakeIncomingCall = makeFakeCall(
                 DisconnectCause.OTHER, // disconnectCauseCode
                 false, // isConference
@@ -167,16 +213,19 @@
                 TEL_PHONEHANDLE, // callHandle
                 mDefaultAccountHandle, // phoneAccountHandle
                 NO_VIDEO_STATE, // callVideoState
-                POST_DIAL_STRING // postDialDigits
+                POST_DIAL_STRING, // postDialDigits
+                null
         );
         mCallLogManager.onCallStateChanged(fakeIncomingCall, CallState.ACTIVE,
                 CallState.DISCONNECTED);
-        ContentValues insertedValues = verifyInsertionWithCapture();
+        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
         assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
                 Integer.valueOf(CallLog.Calls.INCOMING_TYPE));
     }
 
     public void testLogCallDirectionMissed() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
         Call fakeMissedCall = makeFakeCall(
                 DisconnectCause.MISSED, // disconnectCauseCode
                 false, // isConference
@@ -186,17 +235,20 @@
                 TEL_PHONEHANDLE, // callHandle
                 mDefaultAccountHandle, // phoneAccountHandle
                 NO_VIDEO_STATE, // callVideoState
-                POST_DIAL_STRING // postDialDigits
+                POST_DIAL_STRING, // postDialDigits
+                null
         );
 
         mCallLogManager.onCallStateChanged(fakeMissedCall, CallState.ACTIVE,
                 CallState.DISCONNECTED);
-        ContentValues insertedValues = verifyInsertionWithCapture();
+        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
         assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
                 Integer.valueOf(CallLog.Calls.MISSED_TYPE));
     }
 
     public void testCreationTimeAndAge() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
         long currentTime = System.currentTimeMillis();
         long duration = 1000L;
         Call fakeCall = makeFakeCall(
@@ -208,10 +260,11 @@
                 TEL_PHONEHANDLE, // callHandle
                 mDefaultAccountHandle, // phoneAccountHandle
                 NO_VIDEO_STATE, // callVideoState
-                POST_DIAL_STRING // postDialDigits
+                POST_DIAL_STRING, // postDialDigits
+                UserHandle.of(CURRENT_USER_ID)
         );
         mCallLogManager.onCallStateChanged(fakeCall, CallState.ACTIVE, CallState.DISCONNECTED);
-        ContentValues insertedValues = verifyInsertionWithCapture();
+        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
         assertEquals(insertedValues.getAsLong(CallLog.Calls.DATE),
                 Long.valueOf(currentTime));
         assertEquals(insertedValues.getAsLong(CallLog.Calls.DURATION),
@@ -219,6 +272,8 @@
     }
 
     public void testLogPhoneAccountId() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
         Call fakeCall = makeFakeCall(
                 DisconnectCause.OTHER, // disconnectCauseCode
                 false, // isConference
@@ -228,15 +283,18 @@
                 TEL_PHONEHANDLE, // callHandle
                 mDefaultAccountHandle, // phoneAccountHandle
                 NO_VIDEO_STATE, // callVideoState
-                POST_DIAL_STRING // postDialDigits
+                POST_DIAL_STRING, // postDialDigits
+                UserHandle.of(CURRENT_USER_ID)
         );
         mCallLogManager.onCallStateChanged(fakeCall, CallState.ACTIVE, CallState.DISCONNECTED);
-        ContentValues insertedValues = verifyInsertionWithCapture();
+        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
         assertEquals(insertedValues.getAsString(CallLog.Calls.PHONE_ACCOUNT_ID),
                 TEST_PHONE_ACCOUNT_ID);
     }
 
     public void testLogCorrectPhoneNumber() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
         Call fakeCall = makeFakeCall(
                 DisconnectCause.OTHER, // disconnectCauseCode
                 false, // isConference
@@ -246,16 +304,19 @@
                 TEL_PHONEHANDLE, // callHandle
                 mDefaultAccountHandle, // phoneAccountHandle
                 NO_VIDEO_STATE, // callVideoState
-                POST_DIAL_STRING // postDialDigits
+                POST_DIAL_STRING, // postDialDigits
+                UserHandle.of(CURRENT_USER_ID)
         );
         mCallLogManager.onCallStateChanged(fakeCall, CallState.ACTIVE, CallState.DISCONNECTED);
-        ContentValues insertedValues = verifyInsertionWithCapture();
+        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
         assertEquals(insertedValues.getAsString(CallLog.Calls.NUMBER),
                 TEL_PHONEHANDLE.getSchemeSpecificPart());
         assertEquals(insertedValues.getAsString(CallLog.Calls.POST_DIAL_DIGITS), POST_DIAL_STRING);
     }
 
     public void testLogCallVideoFeatures() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
         Call fakeVideoCall = makeFakeCall(
                 DisconnectCause.OTHER, // disconnectCauseCode
                 false, // isConference
@@ -265,14 +326,158 @@
                 TEL_PHONEHANDLE, // callHandle
                 mDefaultAccountHandle, // phoneAccountHandle
                 BIDIRECTIONAL_VIDEO_STATE, // callVideoState
-                POST_DIAL_STRING // postDialDigits
+                POST_DIAL_STRING, // postDialDigits
+                UserHandle.of(CURRENT_USER_ID)
         );
         mCallLogManager.onCallStateChanged(fakeVideoCall, CallState.ACTIVE, CallState.DISCONNECTED);
-        ContentValues insertedValues = verifyInsertionWithCapture();
+        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
         assertTrue((insertedValues.getAsInteger(CallLog.Calls.FEATURES)
                 & CallLog.Calls.FEATURES_VIDEO) == CallLog.Calls.FEATURES_VIDEO);
     }
 
+    public void testLogCallDirectionOutgoingWithMultiUserCapability() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mOtherUserAccountHandle,
+                        PhoneAccount.CAPABILITY_MULTI_USER));
+        Call fakeOutgoingCall = makeFakeCall(
+                DisconnectCause.OTHER, // disconnectCauseCode
+                false, // isConference
+                false, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mDefaultAccountHandle, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                UserHandle.of(CURRENT_USER_ID)
+        );
+        mCallLogManager.onCallStateChanged(fakeOutgoingCall, CallState.ACTIVE,
+                CallState.DISCONNECTED);
+
+        // Outgoing call placed through a phone account with multi user capability is inserted to
+        // all users except managed profile.
+        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
+        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
+                Integer.valueOf(CallLog.Calls.OUTGOING_TYPE));
+        insertedValues = verifyInsertionWithCapture(OTHER_USER_ID);
+        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
+                Integer.valueOf(CallLog.Calls.OUTGOING_TYPE));
+        verifyNoInsertionInUser(MANAGED_USER_ID);
+    }
+
+    public void testLogCallDirectionIncomingWithMultiUserCapability() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mOtherUserAccountHandle,
+                        PhoneAccount.CAPABILITY_MULTI_USER));
+        Call fakeIncomingCall = makeFakeCall(
+                DisconnectCause.OTHER, // disconnectCauseCode
+                false, // isConference
+                true, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mDefaultAccountHandle, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                null
+        );
+        mCallLogManager.onCallStateChanged(fakeIncomingCall, CallState.ACTIVE,
+                CallState.DISCONNECTED);
+
+        // Incoming call using a phone account with multi user capability is inserted to all users
+        // except managed profile.
+        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
+        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
+                Integer.valueOf(CallLog.Calls.INCOMING_TYPE));
+        insertedValues = verifyInsertionWithCapture(OTHER_USER_ID);
+        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
+                Integer.valueOf(CallLog.Calls.INCOMING_TYPE));
+        verifyNoInsertionInUser(MANAGED_USER_ID);
+    }
+
+
+    public void testLogCallDirectionOutgoingWithMultiUserCapabilityFromManagedProfile() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mManagedProfileAccountHandle,
+                        PhoneAccount.CAPABILITY_MULTI_USER));
+        Call fakeOutgoingCall = makeFakeCall(
+                DisconnectCause.OTHER, // disconnectCauseCode
+                false, // isConference
+                false, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mManagedProfileAccountHandle, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                UserHandle.of(MANAGED_USER_ID)
+        );
+        mCallLogManager.onCallStateChanged(fakeOutgoingCall, CallState.ACTIVE,
+                CallState.DISCONNECTED);
+
+        // Outgoing call placed through work dialer should be inserted to managed profile only.
+        verifyNoInsertionInUser(CURRENT_USER_ID);
+        verifyNoInsertionInUser(OTHER_USER_ID);
+        ContentValues insertedValues = verifyInsertionWithCapture(MANAGED_USER_ID);
+        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
+                Integer.valueOf(CallLog.Calls.OUTGOING_TYPE));
+    }
+
+    public void testLogCallDirectionOutgoingFromManagedProfile() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mManagedProfileAccountHandle, 0));
+        Call fakeOutgoingCall = makeFakeCall(
+                DisconnectCause.OTHER, // disconnectCauseCode
+                false, // isConference
+                false, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mManagedProfileAccountHandle, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                UserHandle.of(MANAGED_USER_ID)
+        );
+        mCallLogManager.onCallStateChanged(fakeOutgoingCall, CallState.ACTIVE,
+                CallState.DISCONNECTED);
+
+        // Outgoing call using phone account in managed profile should be inserted to managed
+        // profile only.
+        verifyNoInsertionInUser(CURRENT_USER_ID);
+        verifyNoInsertionInUser(OTHER_USER_ID);
+        ContentValues insertedValues = verifyInsertionWithCapture(MANAGED_USER_ID);
+        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
+                Integer.valueOf(CallLog.Calls.OUTGOING_TYPE));
+    }
+
+    public void testLogCallDirectionIngoingFromManagedProfile() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mManagedProfileAccountHandle, 0));
+        Call fakeOutgoingCall = makeFakeCall(
+                DisconnectCause.OTHER, // disconnectCauseCode
+                false, // isConference
+                true, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mManagedProfileAccountHandle, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                null
+        );
+        mCallLogManager.onCallStateChanged(fakeOutgoingCall, CallState.ACTIVE,
+                CallState.DISCONNECTED);
+
+        // Incoming call using phone account in managed profile should be inserted to managed
+        // profile only.
+        verifyNoInsertionInUser(CURRENT_USER_ID);
+        verifyNoInsertionInUser(OTHER_USER_ID);
+        ContentValues insertedValues = verifyInsertionWithCapture(MANAGED_USER_ID);
+        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
+                Integer.valueOf(Calls.INCOMING_TYPE));
+    }
+
+
     private void verifyNoInsertion() {
         try {
             verify(mContentProvider, timeout(TEST_TIMEOUT_MILLIS).never()).insert(any(String.class),
@@ -282,11 +487,23 @@
         }
     }
 
-    private ContentValues verifyInsertionWithCapture() {
+
+    private void verifyNoInsertionInUser(int userId) {
+        try {
+            Uri uri = ContentProvider.maybeAddUserId(CallLog.Calls.CONTENT_URI, userId);
+            verify(mContentProvider, timeout(TEST_TIMEOUT_MILLIS).never()).insert(any(String.class),
+                    eq(uri), any(ContentValues.class));
+        } catch (android.os.RemoteException e) {
+            fail("Remote exception occurred during test execution");
+        }
+    }
+
+    private ContentValues verifyInsertionWithCapture(int userId) {
         ArgumentCaptor<ContentValues> captor = ArgumentCaptor.forClass(ContentValues.class);
         try {
+            Uri uri = ContentProvider.maybeAddUserId(CallLog.Calls.CONTENT_URI, userId);
             verify(mContentProvider, timeout(TEST_TIMEOUT_MILLIS)).insert(any(String.class),
-                    eq(CallLog.Calls.CONTENT_URI), captor.capture());
+                    eq(uri), captor.capture());
         } catch (android.os.RemoteException e) {
             fail("Remote exception occurred during test execution");
         }
@@ -298,7 +515,7 @@
     private Call makeFakeCall(int disconnectCauseCode, boolean isConference, boolean isIncoming,
             long creationTimeMillis, long ageMillis, Uri callHandle,
             PhoneAccountHandle phoneAccountHandle, int callVideoState,
-            String postDialDigits) {
+            String postDialDigits, UserHandle initiatingUser) {
         Call fakeCall = mock(Call.class);
         when(fakeCall.getDisconnectCause()).thenReturn(
                 new DisconnectCause(disconnectCauseCode));
@@ -310,7 +527,13 @@
         when(fakeCall.getTargetPhoneAccount()).thenReturn(phoneAccountHandle);
         when(fakeCall.getVideoStateHistory()).thenReturn(callVideoState);
         when(fakeCall.getPostDialDigits()).thenReturn(postDialDigits);
-
+        when(fakeCall.getInitiatingUser()).thenReturn(initiatingUser);
         return fakeCall;
     }
+
+    private PhoneAccount makeFakePhoneAccount(PhoneAccountHandle phoneAccountHandle,
+            int capabilities) {
+        return PhoneAccount.builder(phoneAccountHandle, "testing")
+                .setCapabilities(capabilities).build();
+    }
 }
diff --git a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
index 903028c..bdcfb5b 100644
--- a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
@@ -40,6 +40,7 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.StatusHints;
 
+import java.lang.Override;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -113,6 +114,9 @@
         public void disconnect(String callId) throws RemoteException { }
 
         @Override
+        public void silence(String callId) throws RemoteException { }
+
+        @Override
         public void hold(String callId) throws RemoteException { }
 
         @Override