Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d6d30931 authored by Hall Liu's avatar Hall Liu Committed by Automerger Merge Worker
Browse files

Refactor CallLogManager am: b170d5f9

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/services/Telecomm/+/13422468

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: Ifb173ec662f47c73b703555db2dfd2b98080ec88
parents d27eaa08 b170d5f9
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -2626,7 +2626,8 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
        return mState == CallState.ACTIVE;
    }

    Bundle getExtras() {
    @VisibleForTesting
    public Bundle getExtras() {
        return mExtras;
    }

+88 −182
Original line number Diff line number Diff line
@@ -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.Context;
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 @@ public final class CallLogManager extends CallsManagerListenerBase {
     * 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 @@ public final class CallLogManager extends CallsManagerListenerBase {
     */
    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 @@ public final class CallLogManager extends CallsManagerListenerBase {
                    .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 {
            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 {
            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.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());
                }
            }
        }

    /**
     * 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) {
        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);
        }
    }

    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 @@ public final class CallLogManager extends CallsManagerListenerBase {
        }

        // 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 @@ public final class CallLogManager extends CallsManagerListenerBase {
                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 @@ public final class CallLogManager extends CallsManagerListenerBase {
            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++) {
+60 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.telecom.tests;

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.content.res.Resources;
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.Connection;
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;
@@ -817,6 +821,54 @@ public class CallLogManagerTest extends TelecomTestCase {
        assertEquals(TEST_ISO_2, resultIso2);
    }

    @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() {
@@ -976,6 +1028,14 @@ public class CallLogManagerTest extends TelecomTestCase {
        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,