Merge "Add mapping for PROPERTY_CROSS_SIM" into sc-dev
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index e4d6e64..965af59 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -2626,7 +2626,8 @@
         return mState == CallState.ACTIVE;
     }
 
-    Bundle getExtras() {
+    @VisibleForTesting
+    public Bundle getExtras() {
         return mExtras;
     }
 
diff --git a/src/com/android/server/telecom/CallLogManager.java b/src/com/android/server/telecom/CallLogManager.java
index 3cec618..e848dd4 100755
--- a/src/com/android/server/telecom/CallLogManager.java
+++ b/src/com/android/server/telecom/CallLogManager.java
@@ -16,7 +16,7 @@
 
 package com.android.server.telecom;
 
-import static android.provider.CallLog.Calls.MISSED_REASON_NOT_MISSED;
+import static android.provider.CallLog.Calls.BLOCK_REASON_NOT_BLOCKED;
 import static android.telephony.CarrierConfigManager.KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL;
 
 import android.annotation.Nullable;
@@ -24,25 +24,26 @@
 import android.content.Intent;
 import android.location.Country;
 import android.location.CountryDetector;
+import android.location.Location;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Looper;
 import android.os.UserHandle;
 import android.os.PersistableBundle;
+import android.provider.CallLog;
 import android.provider.CallLog.Calls;
 import android.telecom.Connection;
 import android.telecom.DisconnectCause;
 import android.telecom.Log;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.SubscriptionManager;
 
-// TODO: Needed for move to system service: import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
-import android.telecom.CallerInfo;
 import com.android.server.telecom.callfiltering.CallFilteringResult;
 
 import java.util.Arrays;
@@ -66,71 +67,19 @@
      * Parameter object to hold the arguments to add a call in the call log DB.
      */
     private static class AddCallArgs {
-        /**
-         * @param callerInfo Caller details.
-         * @param number The phone number to be logged.
-         * @param presentation Number presentation of the phone number to be logged.
-         * @param callType The type of call (e.g INCOMING_TYPE). @see
-         *     {@link android.provider.CallLog} for the list of values.
-         * @param features The features of the call (e.g. FEATURES_VIDEO). @see
-         *     {@link android.provider.CallLog} for the list of values.
-         * @param creationDate Time when the call was created (milliseconds since epoch).
-         * @param durationInMillis Duration of the call (milliseconds).
-         * @param dataUsage Data usage in bytes, or null if not applicable.
-         * @param isRead Indicates if the entry has been read or not.
-         * @param logCallCompletedListener optional callback called after the call is logged.
-         */
-        public AddCallArgs(Context context, CallerInfo callerInfo, String number,
-                String postDialDigits, String viaNumber, int presentation, int callType,
-                int features, PhoneAccountHandle accountHandle, long creationDate,
-                long durationInMillis, Long dataUsage, UserHandle initiatingUser, boolean isRead,
-                @Nullable LogCallCompletedListener logCallCompletedListener, int callBlockReason,
-                CharSequence callScreeningAppName, String callScreeningComponentName,
-                long missedReason) {
+        public AddCallArgs(Context context, CallLog.AddCallParams params,
+                @Nullable LogCallCompletedListener logCallCompletedListener) {
             this.context = context;
-            this.callerInfo = callerInfo;
-            this.number = number;
-            this.postDialDigits = postDialDigits;
-            this.viaNumber = viaNumber;
-            this.presentation = presentation;
-            this.callType = callType;
-            this.features = features;
-            this.accountHandle = accountHandle;
-            this.timestamp = creationDate;
-            this.durationInSec = (int)(durationInMillis / 1000);
-            this.dataUsage = dataUsage;
-            this.initiatingUser = initiatingUser;
-            this.isRead = isRead;
+            this.params = params;
             this.logCallCompletedListener = logCallCompletedListener;
-            this.callBockReason = callBlockReason;
-            this.callScreeningAppName = callScreeningAppName;
-            this.callScreeningComponentName = callScreeningComponentName;
-            this.missedReason = missedReason;
+
         }
         // Since the members are accessed directly, we don't use the
         // mXxxx notation.
         public final Context context;
-        public final CallerInfo callerInfo;
-        public final String number;
-        public final String postDialDigits;
-        public final String viaNumber;
-        public final int presentation;
-        public final int callType;
-        public final int features;
-        public final PhoneAccountHandle accountHandle;
-        public final long timestamp;
-        public final int durationInSec;
-        public final Long dataUsage;
-        public final UserHandle initiatingUser;
-        public final boolean isRead;
-
+        public final CallLog.AddCallParams params;
         @Nullable
         public final LogCallCompletedListener logCallCompletedListener;
-
-        public final int callBockReason;
-        public final CharSequence callScreeningAppName;
-        public final String callScreeningComponentName;
-        public final long missedReason;
     }
 
     private static final String TAG = CallLogManager.class.getSimpleName();
@@ -315,47 +264,50 @@
      */
     void logCall(Call call, int callLogType,
         @Nullable LogCallCompletedListener logCallCompletedListener, CallFilteringResult result) {
-        long creationTime;
 
+        CallLog.AddCallParams.AddCallParametersBuilder paramBuilder =
+                new CallLog.AddCallParams.AddCallParametersBuilder();
         if (call.getConnectTimeMillis() != 0
                 && call.getConnectTimeMillis() < call.getCreationTimeMillis()) {
             // If connected time is available, use connected time. The connected time might be
             // earlier than created time since it might come from carrier sent special SMS to
             // notifier user earlier missed call.
-            creationTime = call.getConnectTimeMillis();
+            paramBuilder.setStart(call.getConnectTimeMillis());
         } else {
-            creationTime = call.getCreationTimeMillis();
+            paramBuilder.setStart(call.getCreationTimeMillis());
         }
 
-        final long age = call.getAgeMillis();
+        paramBuilder.setDuration((int) (call.getAgeMillis() / 1000));
 
-        final String logNumber = getLogNumber(call);
+        String logNumber = getLogNumber(call);
+        paramBuilder.setNumber(logNumber);
 
         Log.d(TAG, "logNumber set to: %s", Log.pii(logNumber));
 
-        final PhoneAccountHandle emergencyAccountHandle =
-                TelephonyUtil.getDefaultEmergencyPhoneAccount().getAccountHandle();
-
         String formattedViaNumber = PhoneNumberUtils.formatNumber(call.getViaNumber(),
                 getCountryIso());
         formattedViaNumber = (formattedViaNumber != null) ?
                 formattedViaNumber : call.getViaNumber();
+        paramBuilder.setViaNumber(formattedViaNumber);
 
+        final PhoneAccountHandle emergencyAccountHandle =
+                TelephonyUtil.getDefaultEmergencyPhoneAccount().getAccountHandle();
         PhoneAccountHandle accountHandle = call.getTargetPhoneAccount();
         if (emergencyAccountHandle.equals(accountHandle)) {
             accountHandle = null;
         }
+        paramBuilder.setAccountHandle(accountHandle);
 
-        Long callDataUsage = call.getCallDataUsage() == Call.DATA_USAGE_NOT_SET ? null :
-                call.getCallDataUsage();
+        paramBuilder.setDataUsage(call.getCallDataUsage() == Call.DATA_USAGE_NOT_SET
+                ? Long.MIN_VALUE : call.getCallDataUsage());
 
-        int callFeatures = getCallFeatures(call.getVideoStateHistory(),
+        paramBuilder.setFeatures(getCallFeatures(call.getVideoStateHistory(),
                 call.getDisconnectCause().getCode() == DisconnectCause.CALL_PULLED,
                 call.wasHighDefAudio(), call.wasWifi(),
                 (call.getConnectionProperties() & Connection.PROPERTY_ASSISTED_DIALING) ==
                         Connection.PROPERTY_ASSISTED_DIALING,
                 call.wasEverRttCall(),
-                call.wasVolte());
+                call.wasVolte()));
 
         if (result == null) {
             result = new CallFilteringResult.Builder()
@@ -363,67 +315,73 @@
                     .setCallScreeningComponentName(call.getCallScreeningComponentName())
                     .build();
         }
-
         if (callLogType == Calls.BLOCKED_TYPE || callLogType == Calls.MISSED_TYPE) {
-            logCall(call.getCallerInfo(), logNumber, call.getPostDialDigits(), formattedViaNumber,
-                    call.getHandlePresentation(), callLogType, callFeatures, accountHandle,
-                    creationTime, age, callDataUsage, call.isEmergencyCall(),
-                    call.getInitiatingUser(), call.isSelfManaged(), logCallCompletedListener,
-                    result.mCallBlockReason, result.mCallScreeningAppName,
-                    result.mCallScreeningComponentName, call.getMissedReason());
+            paramBuilder.setCallBlockReason(result.mCallBlockReason);
+            paramBuilder.setCallScreeningComponentName(result.mCallScreeningComponentName);
+            paramBuilder.setCallScreeningAppName(result.mCallScreeningAppName);
         } else {
-            logCall(call.getCallerInfo(), logNumber, call.getPostDialDigits(), formattedViaNumber,
-                    call.getHandlePresentation(), callLogType, callFeatures, accountHandle,
-                    creationTime, age, callDataUsage, call.isEmergencyCall(),
-                    call.getInitiatingUser(), call.isSelfManaged(), logCallCompletedListener,
-                    Calls.BLOCK_REASON_NOT_BLOCKED, null /*callScreeningAppName*/,
-                    null /*callScreeningComponentName*/, call.getMissedReason());
+            paramBuilder.setCallBlockReason(BLOCK_REASON_NOT_BLOCKED);
+        }
+
+        PhoneAccount phoneAccount = mPhoneAccountRegistrar.getPhoneAccountUnchecked(accountHandle);
+        UserHandle initiatingUser = call.getInitiatingUser();
+        if (phoneAccount != null &&
+                phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
+            if (initiatingUser != null &&
+                    UserUtil.isManagedProfile(mContext, initiatingUser)) {
+                paramBuilder.setUserToBeInsertedTo(initiatingUser);
+                paramBuilder.setAddForAllUsers(false);
+            } else {
+                paramBuilder.setAddForAllUsers(true);
+            }
+        } else {
+            if (accountHandle == null) {
+                paramBuilder.setAddForAllUsers(true);
+            } else {
+                paramBuilder.setUserToBeInsertedTo(accountHandle.getUserHandle());
+                paramBuilder.setAddForAllUsers(accountHandle.getUserHandle() == null);
+            }
+        }
+        if (call.getExtras() != null) {
+            if (call.getExtras().containsKey(TelecomManager.EXTRA_PRIORITY)) {
+                paramBuilder.setPriority(call.getExtras().getInt(TelecomManager.EXTRA_PRIORITY));
+            }
+            if (call.getExtras().containsKey(TelecomManager.EXTRA_CALL_SUBJECT)) {
+                paramBuilder.setSubject(call.getExtras()
+                        .getString(TelecomManager.EXTRA_CALL_SUBJECT));
+            }
+            if (call.getExtras().containsKey(TelecomManager.EXTRA_PICTURE_URI)) {
+                paramBuilder.setPictureUri(call.getExtras()
+                        .getParcelable(TelecomManager.EXTRA_PICTURE_URI));
+            }
+            if (call.getExtras().containsKey(TelecomManager.EXTRA_LOCATION)) {
+                Location l = call.getExtras().getParcelable(TelecomManager.EXTRA_LOCATION);
+                if (l != null) {
+                    paramBuilder.setLatitude(l.getLatitude());
+                    paramBuilder.setLongitude(l.getLongitude());
+                }
+            }
+        }
+
+        paramBuilder.setCallerInfo(call.getCallerInfo());
+        paramBuilder.setPostDialDigits(call.getPostDialDigits());
+        paramBuilder.setPresentation(call.getHandlePresentation());
+        paramBuilder.setCallType(callLogType);
+        paramBuilder.setIsRead(call.isSelfManaged());
+        paramBuilder.setMissedReason(call.getMissedReason());
+
+        sendAddCallBroadcast(callLogType, call.getAgeMillis());
+
+        boolean okayToLog =
+                okayToLogCall(accountHandle, logNumber, call.isEmergencyCall());
+        if (okayToLog) {
+            AddCallArgs args = new AddCallArgs(mContext, paramBuilder.build(),
+                    logCallCompletedListener);
+            logCallAsync(args);
         }
     }
 
-    /**
-     * Inserts a call into the call log, based on the parameters passed in.
-     *
-     * @param callerInfo Caller details.
-     * @param number The number the call was made to or from.
-     * @param postDialDigits The post-dial digits that were dialed after the number,
-     *                       if it was an outgoing call. Otherwise ''.
-     * @param presentation
-     * @param callType The type of call.
-     * @param features The features of the call.
-     * @param start The start time of the call, in milliseconds.
-     * @param duration The duration of the call, in milliseconds.
-     * @param dataUsage The data usage for the call, null if not applicable.
-     * @param isEmergency {@code true} if this is an emergency call, {@code false} otherwise.
-     * @param logCallCompletedListener optional callback called after the call is logged.
-     * @param initiatingUser The user the call was initiated under.
-     * @param isSelfManaged {@code true} if this is a self-managed call, {@code false} otherwise.
-     * @param callBlockReason The reason why the call is blocked.
-     * @param callScreeningAppName The call screening application name which block the call.
-     * @param callScreeningComponentName The call screening component name which block the call.
-     * @param missedReason The encoded information about reasons for missed call.
-     */
-    private void logCall(
-            CallerInfo callerInfo,
-            String number,
-            String postDialDigits,
-            String viaNumber,
-            int presentation,
-            int callType,
-            int features,
-            PhoneAccountHandle accountHandle,
-            long start,
-            long duration,
-            Long dataUsage,
-            boolean isEmergency,
-            UserHandle initiatingUser,
-            boolean isSelfManaged,
-            @Nullable LogCallCompletedListener logCallCompletedListener,
-            int callBlockReason,
-            CharSequence callScreeningAppName,
-            String callScreeningComponentName,
-            long missedReason) {
-
+    boolean okayToLogCall(PhoneAccountHandle accountHandle, String number, boolean isEmergency) {
         // 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
         // on carrier requirements.)
@@ -438,29 +396,8 @@
         }
 
         // Don't log emergency numbers if the device doesn't allow it.
-        final boolean isOkToLogThisCall = (!isEmergency || okToLogEmergencyNumber)
+        return (!isEmergency || okToLogEmergencyNumber)
                 && !isUnloggableNumber(number, configBundle);
-
-        sendAddCallBroadcast(callType, duration);
-
-        if (isOkToLogThisCall) {
-            Log.d(TAG, "Logging Call log entry: " + callerInfo + ", "
-                    + Log.pii(number) + "," + presentation + ", " + callType
-                    + ", " + start + ", " + duration);
-            boolean isRead = false;
-            if (isSelfManaged) {
-                // Mark self-managed calls are read since they're being handled by their own app.
-                // Their inclusion in the call log is informational only.
-                isRead = true;
-            }
-            AddCallArgs args = new AddCallArgs(mContext, callerInfo, number, postDialDigits,
-                    viaNumber, presentation, callType, features, accountHandle, start, duration,
-                    dataUsage, initiatingUser, isRead, logCallCompletedListener, callBlockReason,
-                    callScreeningAppName, callScreeningComponentName, missedReason);
-            logCallAsync(args);
-        } else {
-          Log.d(TAG, "Not adding emergency call to call log.");
-        }
     }
 
     private boolean isUnloggableNumber(String callNumber, PersistableBundle carrierConfig) {
@@ -566,7 +503,7 @@
                 mListeners[i] = c.logCallCompletedListener;
                 try {
                     // May block.
-                    result[i] = addCall(c);
+                    result[i] = Calls.addCall(c.context, c.params);
                 } 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
@@ -582,37 +519,6 @@
             return result;
         }
 
-        private Uri addCall(AddCallArgs c) {
-            PhoneAccount phoneAccount = mPhoneAccountRegistrar
-                    .getPhoneAccountUnchecked(c.accountHandle);
-            if (phoneAccount != null &&
-                    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 == null ? null : 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.viaNumber,
-                    c.presentation, c.callType, c.features, c.accountHandle, c.timestamp,
-                    c.durationInSec, c.dataUsage, userToBeInserted == null,
-                    userToBeInserted, c.isRead, c.callBockReason, c.callScreeningAppName,
-                    c.callScreeningComponentName, c.missedReason);
-        }
-
-
         @Override
         protected void onPostExecute(Uri[] result) {
             for (int i = 0; i < result.length; i++) {
diff --git a/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java b/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
index 7b19d29..62ff257 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 static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -40,7 +41,9 @@
 import android.location.Country;
 import android.location.CountryDetector;
 import android.location.CountryListener;
+import android.location.Location;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Looper;
 import android.os.PersistableBundle;
 import android.os.SystemClock;
@@ -52,6 +55,7 @@
 import android.telecom.DisconnectCause;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
@@ -819,6 +823,54 @@
 
     @SmallTest
     @Test
+    public void testCallComposerElements() {
+        Call fakeCall = makeFakeCall(
+                DisconnectCause.LOCAL, // disconnectCauseCode
+                false, // isConference
+                true, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mDefaultAccountHandle, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                VIA_NUMBER_STRING, // viaNumber
+                UserHandle.of(CURRENT_USER_ID)
+        );
+        String subject = "segmentation fault";
+        // =)
+        double lat = 40.649723;
+        double lon = -80.082090;
+        Location location = new Location("");
+        location.setLatitude(lat);
+        location.setLongitude(lon);
+
+        Uri fakeProviderUri = Uri.parse("content://nothing_to_see_here/12345");
+
+        Bundle extras = new Bundle();
+        extras.putInt(TelecomManager.EXTRA_PRIORITY, TelecomManager.PRIORITY_URGENT);
+        extras.putString(TelecomManager.EXTRA_CALL_SUBJECT, subject);
+        extras.putParcelable(TelecomManager.EXTRA_LOCATION, location);
+        extras.putParcelable(TelecomManager.EXTRA_PICTURE_URI, fakeProviderUri);
+        when(fakeCall.getExtras()).thenReturn(extras);
+
+        mCallLogManager.onCallStateChanged(fakeCall, CallState.ACTIVE,
+                CallState.DISCONNECTED);
+        ContentValues locationValues = verifyLocationInsertionWithCapture(CURRENT_USER_ID);
+        assertEquals(lat, locationValues.getAsDouble(CallLog.Locations.LATITUDE), 0);
+        assertEquals(lon, locationValues.getAsDouble(CallLog.Locations.LONGITUDE), 0);
+
+        ContentValues callLogValues = verifyInsertionWithCapture(CURRENT_USER_ID);
+        assertEquals(subject, callLogValues.getAsString(Calls.SUBJECT));
+        assertEquals(fakeProviderUri.toString(),
+                callLogValues.getAsString(Calls.COMPOSER_PHOTO_URI));
+        assertEquals(TelecomManager.PRIORITY_URGENT,
+                (int) callLogValues.getAsInteger(Calls.PRIORITY));
+        assertNotNull(callLogValues.getAsString(Calls.LOCATION));
+    }
+
+    @SmallTest
+    @Test
     public void testDoNotLogConferenceWithNoChildren() {
         Call fakeCall = makeFakeCall(
                 DisconnectCause.LOCAL, // disconnectCauseCode
@@ -976,6 +1028,14 @@
         return captor.getValue();
     }
 
+    private ContentValues verifyLocationInsertionWithCapture(int userId) {
+        Uri uri = ContentProvider.maybeAddUserId(CallLog.Locations.CONTENT_URI, userId);
+        ArgumentCaptor<ContentValues> captor = ArgumentCaptor.forClass(ContentValues.class);
+        verify(mContentProvider, timeout(TEST_TIMEOUT_MILLIS).times(1)).insert(
+                eq(uri), captor.capture());
+        return captor.getValue();
+    }
+
     private Call makeFakeCall(int disconnectCauseCode, boolean isConference, boolean isIncoming,
             long creationTimeMillis, long ageMillis, Uri callHandle,
             PhoneAccountHandle phoneAccountHandle, int callVideoState,