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

Commit 54b19bd3 authored by Juho Kim's avatar Juho Kim Committed by Android (Google) Code Review
Browse files

Merge "Implement CallStateChanged atom logging"

parents a7754cd0 f9345581
Loading
Loading
Loading
Loading
+29 −4
Original line number Diff line number Diff line
@@ -68,6 +68,8 @@ import android.widget.Toast;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telecom.IVideoProvider;
import com.android.internal.util.Preconditions;
import com.android.server.telecom.stats.CallFailureCause;
import com.android.server.telecom.stats.CallStateChangedAtomWriter;
import com.android.server.telecom.ui.ToastFactory;

import java.io.IOException;
@@ -506,6 +508,8 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
    private final String mId;
    private String mConnectionId;
    private Analytics.CallInfo mAnalytics = new Analytics.CallInfo();
    private CallStateChangedAtomWriter mCallStateChangedAtomWriter =
            new CallStateChangedAtomWriter();
    private char mPlayingDtmfTone;

    private boolean mWasConferencePreviouslyMerged = false;
@@ -799,6 +803,8 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
        mCreationTimeMillis = mClockProxy.currentTimeMillis();
        mMissedReason = MISSED_REASON_NOT_MISSED;
        mStartRingTime = 0;

        mCallStateChangedAtomWriter.setExistingCallCount(callsManager.getCalls().size());
    }

    /**
@@ -1275,10 +1281,15 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
                }
                Log.addEvent(this, event, stringData);
            }
            int statsdDisconnectCause = (newState == CallState.DISCONNECTED) ?
                    getDisconnectCause().getCode() : DisconnectCause.UNKNOWN;
            TelecomStatsLog.write(TelecomStatsLog.CALL_STATE_CHANGED, newState,
                    statsdDisconnectCause, isSelfManaged(), isExternalCall(), isEmergencyCall());

            mCallStateChangedAtomWriter
                    .setDisconnectCause(getDisconnectCause())
                    .setSelfManaged(isSelfManaged())
                    .setExternalCall(isExternalCall())
                    .setEmergencyCall(isEmergencyCall())
                    .setDurationSeconds(Long.valueOf(
                        (mDisconnectTimeMillis - mConnectTimeMillis) / 1000).intValue())
                    .write(newState);
        }
        return true;
    }
@@ -1650,6 +1661,12 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
        }
        checkIfVideoCapable();
        checkIfRttCapable();

        if (accountHandle != null) {
            mCallStateChangedAtomWriter.setUid(
                    accountHandle.getComponentName().getPackageName(),
                    mContext.getPackageManager());
        }
    }

    public UserHandle getUserHandleFromTargetPhoneAccount() {
@@ -4358,6 +4375,14 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
        mCallScreeningComponentName = callScreeningComponentName;
    }

    public void setStartFailCause(CallFailureCause cause) {
        mCallStateChangedAtomWriter.setStartFailCause(cause);
    }

    public void increaseHeldByThisCallCount() {
        mCallStateChangedAtomWriter.increaseHeldCallCount();
    }

    public void maybeOnInCallServiceTrackingChanged(boolean isTracking, boolean hasUi) {
        if (mTransactionalService != null) {
            Log.i(this,
+7 −3
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.util.LocalLog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.telecom.stats.CallStateChangedAtomWriter;

import java.util.Collections;
import java.util.Map;
@@ -345,9 +346,12 @@ public class CallAnomalyWatchdog extends CallsManagerListenerBase implements Cal
    }

    private void writeCallStateChangedAtom(Call call) {
        TelecomStatsLog.write(TelecomStatsLog.CALL_STATE_CHANGED, call.getState(),
                DisconnectCause.ERROR, call.isSelfManaged(),
                call.isExternalCall(), call.isEmergencyCall());
        new CallStateChangedAtomWriter()
                .setDisconnectCause(call.getDisconnectCause())
                .setSelfManaged(call.isSelfManaged())
                .setExternalCall(call.isExternalCall())
                .setEmergencyCall(call.isEmergencyCall())
                .write(call.getState());
    }

    /**
+42 −10
Original line number Diff line number Diff line
@@ -125,6 +125,7 @@ import com.android.server.telecom.callredirection.CallRedirectionProcessor;
import com.android.server.telecom.components.ErrorDialogActivity;
import com.android.server.telecom.components.TelecomBroadcastReceiver;
import com.android.server.telecom.settings.BlockedNumbersUtil;
import com.android.server.telecom.stats.CallFailureCause;
import com.android.server.telecom.ui.AudioProcessingNotification;
import com.android.server.telecom.ui.CallRedirectionTimeoutDialogActivity;
import com.android.server.telecom.ui.ConfirmCallDialogActivity;
@@ -1496,16 +1497,20 @@ public class CallsManager extends Call.ListenerBase
            }
        }

        if (!isHandoverAllowed || (call.isSelfManaged() && !isIncomingCallPermitted(call,
                call.getTargetPhoneAccount()))) {
        CallFailureCause startFailCause =
                checkIncomingCallPermitted(call, call.getTargetPhoneAccount());
        if (!isHandoverAllowed ||
                (call.isSelfManaged() && !startFailCause.isSuccess())) {
            if (isConference) {
                notifyCreateConferenceFailed(phoneAccountHandle, call);
            } else {
                if (hasMaximumManagedRingingCalls(call)) {
                    call.setMissedReason(AUTO_MISSED_MAXIMUM_RINGING);
                    call.setStartFailCause(CallFailureCause.MAX_RINGING_CALLS);
                    mCallLogManager.logCall(call, Calls.MISSED_TYPE,
                            true /*showNotificationForMissedCall*/, null /*CallFilteringResult*/);
                }
                call.setStartFailCause(startFailCause);
                notifyCreateConnectionFailed(phoneAccountHandle, call);
            }
        } else if (isInEmergencyCall()) {
@@ -1514,6 +1519,7 @@ public class CallsManager extends Call.ListenerBase
            // rejected since the user did not explicitly reject.
            call.setMissedReason(AUTO_MISSED_EMERGENCY_CALL);
            call.getAnalytics().setMissedReason(call.getMissedReason());
            call.setStartFailCause(CallFailureCause.IN_EMERGENCY_CALL);
            mCallLogManager.logCall(call, Calls.MISSED_TYPE,
                    true /*showNotificationForMissedCall*/, null /*CallFilteringResult*/);
            if (isConference) {
@@ -1829,6 +1835,7 @@ public class CallsManager extends Call.ListenerBase
                            notifyCreateConnectionFailed(
                                    finalCall.getTargetPhoneAccount(), finalCall);
                        }
                        finalCall.setStartFailCause(CallFailureCause.IN_EMERGENCY_CALL);
                        return CompletableFuture.completedFuture(null);
                    }

@@ -3292,6 +3299,7 @@ public class CallsManager extends Call.ListenerBase
                Log.i(this, "holdActiveCallForNewCall: Holding active %s before making %s active.",
                        activeCall.getId(), call.getId());
                activeCall.hold();
                call.increaseHeldByThisCallCount();
                return true;
            } else {
                // This call does not support hold. If it is from a different connection
@@ -4534,6 +4542,7 @@ public class CallsManager extends Call.ListenerBase
            }
            //  If the user tries to make two outgoing calls to different emergency call numbers,
            //  we will try to connect the first outgoing call and reject the second.
            emergencyCall.setStartFailCause(CallFailureCause.IN_EMERGENCY_CALL);
            return false;
        }

@@ -4621,12 +4630,14 @@ public class CallsManager extends Call.ListenerBase
        if (canHold(liveCall)) {
            Log.i(this, "makeRoomForOutgoingEmergencyCall: holding live call.");
            emergencyCall.getAnalytics().setCallIsAdditional(true);
            emergencyCall.increaseHeldByThisCallCount();
            liveCall.getAnalytics().setCallIsInterrupted(true);
            liveCall.hold("calling " + emergencyCall.getId());
            return true;
        }

        // The live call cannot be held so we're out of luck here.  There's no room.
        emergencyCall.setStartFailCause(CallFailureCause.CANNOT_HOLD_CALL);
        return false;
    }

@@ -4668,6 +4679,7 @@ public class CallsManager extends Call.ListenerBase
                        + " of new outgoing call.");
                return true;
            }
            call.setStartFailCause(CallFailureCause.MAX_OUTGOING_CALLS);
            return false;
        }

@@ -4716,6 +4728,7 @@ public class CallsManager extends Call.ListenerBase
        }

        // The live call cannot be held so we're out of luck here.  There's no room.
        call.setStartFailCause(CallFailureCause.CANNOT_HOLD_CALL);
        return false;
    }

@@ -4940,25 +4953,44 @@ public class CallsManager extends Call.ListenerBase

    public boolean isIncomingCallPermitted(Call excludeCall,
                                           PhoneAccountHandle phoneAccountHandle) {
        return checkIncomingCallPermitted(excludeCall, phoneAccountHandle).isSuccess();
    }

    private CallFailureCause checkIncomingCallPermitted(
            Call call, PhoneAccountHandle phoneAccountHandle) {
        if (phoneAccountHandle == null) {
            return false;
            return CallFailureCause.INVALID_USE;
        }

        PhoneAccount phoneAccount =
                mPhoneAccountRegistrar.getPhoneAccountUnchecked(phoneAccountHandle);
        if (phoneAccount == null) {
            return false;
            return CallFailureCause.INVALID_USE;
        }
        if (isInEmergencyCall()) return false;

        if (!phoneAccount.isSelfManaged()) {
            return !hasMaximumManagedRingingCalls(excludeCall) &&
                    !hasMaximumManagedHoldingCalls(excludeCall);
        if (isInEmergencyCall()) {
            return CallFailureCause.IN_EMERGENCY_CALL;
        }

        if (phoneAccount.isSelfManaged()) {
            if (hasMaximumSelfManagedRingingCalls(call, phoneAccountHandle)) {
                return CallFailureCause.MAX_RINGING_CALLS;
            }
            if (hasMaximumSelfManagedCalls(call, phoneAccountHandle)) {
                return CallFailureCause.MAX_SELF_MANAGED_CALLS;
            }
        } else {
            return !hasMaximumSelfManagedRingingCalls(excludeCall, phoneAccountHandle) &&
                    !hasMaximumSelfManagedCalls(excludeCall, phoneAccountHandle);
            if (hasMaximumManagedRingingCalls(call)) {
                return CallFailureCause.MAX_RINGING_CALLS;
            }
            if (hasMaximumManagedHoldingCalls(call)) {
                return CallFailureCause.MAX_HOLD_CALLS;
            }
        }

        return CallFailureCause.NONE;
    }

    public boolean isOutgoingCallPermitted(PhoneAccountHandle phoneAccountHandle) {
        return isOutgoingCallPermitted(null /* excludeCall */, phoneAccountHandle);
    }
+69 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.stats;

/**
 * Indicating the failure reason why a new call cannot be made.
 * The codes are synced with CallFailureCauseEnum defined in enums.proto.
 */
public enum CallFailureCause {
    /** The call is normally started. */
    NONE(0),
    /** Necessary parameters are invalid or null. */
    INVALID_USE(1),
    /** There is an emergency call ongoing. */
    IN_EMERGENCY_CALL(2),
    /** There is an live call that cannot be held. */
    CANNOT_HOLD_CALL(3),
    /** There are maximum number of outgoing calls already. */
    MAX_OUTGOING_CALLS(4),
    /** There are maximum number of ringing calls already. */
    MAX_RINGING_CALLS(5),
    /** There are maximum number of calls in hold already. */
    MAX_HOLD_CALLS(6),
    /* There are maximum number of self-managed calls already. */
    MAX_SELF_MANAGED_CALLS(7);

    private final int mCode;

    /**
     * Creates a new CallFailureCause.
     *
     * @param code The code for the failure cause.
     */
    CallFailureCause(int code) {
        mCode = code;
    }

    /**
     * Returns the code for the failure.
     *
     * @return The code for the failure cause.
     */
    public int getCode() {
        return mCode;
    }

    /**
     * Check if this enum represents a non-failure case.
     *
     * @return True if success.
     */
    public boolean isSuccess() {
        return this == NONE;
    }
}
+119 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.stats;

import android.content.pm.PackageManager;
import android.telecom.DisconnectCause;
import android.telecom.Log;

import com.android.server.telecom.CallState;
import com.android.server.telecom.TelecomStatsLog;

/**
 * Collects and stores data for CallStateChanged atom for each call, and provide a
 * method to write the data to statsd whenever the call state changes.
 */
public class CallStateChangedAtomWriter {
    private boolean mIsSelfManaged = false;
    private boolean mIsExternalCall = false;
    private boolean mIsEmergencyCall = false;
    private int mUid = -1;
    private int mDurationSeconds = 0;
    private int mExistingCallCount = 0;
    private int mHeldCallCount = 0;
    private CallFailureCause mStartFailCause = CallFailureCause.NONE;
    private DisconnectCause mDisconnectCause = new DisconnectCause(DisconnectCause.UNKNOWN);

    /**
     * Write collected data and current call state to statsd.
     *
     * @param state Current call state.
     */
    public void write(int state) {
        TelecomStatsLog.write(TelecomStatsLog.CALL_STATE_CHANGED,
                state,
                state == CallState.DISCONNECTED ?
                    mDisconnectCause.getCode() : DisconnectCause.UNKNOWN,
                mIsSelfManaged,
                mIsExternalCall,
                mIsEmergencyCall,
                mUid,
                state == CallState.DISCONNECTED ? mDurationSeconds : 0,
                mExistingCallCount,
                mHeldCallCount,
                state == CallState.DISCONNECTED ?
                    mStartFailCause.getCode() : CallFailureCause.NONE.getCode());
    }

    public CallStateChangedAtomWriter setSelfManaged(boolean isSelfManaged) {
        mIsSelfManaged = isSelfManaged;
        return this;
    }

    public CallStateChangedAtomWriter setExternalCall(boolean isExternalCall) {
        mIsExternalCall = isExternalCall;
        return this;
    }

    public CallStateChangedAtomWriter setEmergencyCall(boolean isEmergencyCall) {
        mIsEmergencyCall = isEmergencyCall;
        return this;
    }

    public CallStateChangedAtomWriter setUid(int uid) {
        mUid = uid;
        return this;
    }

    public CallStateChangedAtomWriter setUid(String packageName, PackageManager pm) {
        try {
            final int uid = pm.getPackageUid(packageName, PackageManager.PackageInfoFlags.of(0));
            return setUid(uid);

        } catch (PackageManager.NameNotFoundException e) {
            Log.e(this, e, "Could not find the package");
        }
        return setUid(-1);
    }

    public CallStateChangedAtomWriter setDurationSeconds(int duration) {
        if (duration >= 0) {
            mDurationSeconds = duration;
        }
        return this;
    }

    public CallStateChangedAtomWriter setExistingCallCount(int count) {
        mExistingCallCount = count;
        return this;
    }

    public CallStateChangedAtomWriter increaseHeldCallCount() {
        mHeldCallCount++;
        return this;
    }

    public CallStateChangedAtomWriter setDisconnectCause(DisconnectCause cause) {
        mDisconnectCause = cause;
        return this;
    }

    public CallStateChangedAtomWriter setStartFailCause(CallFailureCause cause) {
        mStartFailCause = cause;
        return this;
    }
}
Loading