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

Commit a328efad authored by Pranav Madapurmath's avatar Pranav Madapurmath
Browse files

Associate calls with profile user.

When a secondary/guest user receives an incoming call and then places
an outgoing call on top of it, the incoming call should be held and the
outgoing call made active. However, due to the call user association
logic that was introduced in par with work profile support in U, note
that the associated user for outgoing calls will be the initiating user
while for incoming calls, this is derived from the target phone account
handle.

Hence, the user association for the MT and MO call would be different
when using an unassociated sim to call. There are no issues in the
initial binding with the 1st call but when we bind the 2nd call to the
ICS, we end up resetting the InCallAdapter because no associated ICS
were found for the incoming call. This, inadvertently, disregards the
initial binding so subsequent call updates sent on the incoming call
get ignored. We can disregard the request to reset the adapter
if it has already been set, though this is not a complete solution.

As is, because the services aren't considered connected for the MO call
initially, Telecom will try to reconnect to the service, where we will
send all active calls to the ICS (InCallController#onConnected). We
should not be hitting this part of the call flow and should instead add
the MO call directly to the available service (InCallService#addCall),
as is done in InCallController#onCallAdded.

We could copy the reference to the service available in the MT user over
to the MO user. Alternatively, we can have it so that the user
association for MT calls is dependent on whether the phone account has
the multi user capability, in which case, we will associate the user of
the call with the current user. If we're dealing with a SIM account, then
the user association will continue to use the phone account user handle.

This CL implements the latter approach as it offers a more robust
solution to the problem and allows the sim user association on phone
accounts to be respected in incoming calls if it's not a multi-user
account. In the case of unassociated sims, it makes sense to associate
the calls with the current user instead.

Bug: 294699269
Bug: 308856446
Test: Manual
Test: New unit test to verify that call updates are persevered when
MO/MT call are placed from the secondary/guest user. A unit test to
verify that the user to ICS connection mappings are properly removed in
multi user scenarios.
Test: atest TelecomUnitTests
Test: v2/android-platinum/telephony-platinum-tests-ATT

Change-Id: I47518ab5f3905991c16c0516ea87533b70827022
parent 1d16b5a1
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -35,6 +35,7 @@ aconfig_declarations {
      "telecom_calllog_flags.aconfig",
      "telecom_calllog_flags.aconfig",
      "telecom_resolve_hidden_dependencies.aconfig",
      "telecom_resolve_hidden_dependencies.aconfig",
      "telecom_bluetoothroutemanager_flags.aconfig",
      "telecom_bluetoothroutemanager_flags.aconfig",
      "telecom_work_profile_flags.aconfig"
    ],
    ],
}
}
+8 −0
Original line number Original line Diff line number Diff line
package: "com.android.server.telecom.flags"

flag {
  name: "work_profile_associated_user"
  namespace: "telecom"
  description: "Redefines the associated user for calls in the context of work profile support (U+)"
  bug: "294699269"
}
 No newline at end of file
+21 −8
Original line number Original line Diff line number Diff line
@@ -72,6 +72,7 @@ import android.widget.Toast;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telecom.IVideoProvider;
import com.android.internal.telecom.IVideoProvider;
import com.android.internal.util.Preconditions;
import com.android.internal.util.Preconditions;
import com.android.server.telecom.flags.FeatureFlags;
import com.android.server.telecom.stats.CallFailureCause;
import com.android.server.telecom.stats.CallFailureCause;
import com.android.server.telecom.stats.CallStateChangedAtomWriter;
import com.android.server.telecom.stats.CallStateChangedAtomWriter;
import com.android.server.telecom.ui.ToastFactory;
import com.android.server.telecom.ui.ToastFactory;
@@ -786,6 +787,8 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
     */
     */
    private CompletableFuture<Boolean> mDisconnectFuture;
    private CompletableFuture<Boolean> mDisconnectFuture;


    private FeatureFlags mFlags;

    /**
    /**
     * Persists the specified parameters and initializes the new instance.
     * Persists the specified parameters and initializes the new instance.
     * @param context The context.
     * @param context The context.
@@ -817,11 +820,12 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
            boolean shouldAttachToExistingConnection,
            boolean shouldAttachToExistingConnection,
            boolean isConference,
            boolean isConference,
            ClockProxy clockProxy,
            ClockProxy clockProxy,
            ToastFactory toastFactory) {
            ToastFactory toastFactory,
            FeatureFlags featureFlags) {
        this(callId, context, callsManager, lock, repository, phoneNumberUtilsAdapter,
        this(callId, context, callsManager, lock, repository, phoneNumberUtilsAdapter,
               handle, null, gatewayInfo, connectionManagerPhoneAccountHandle,
               handle, null, gatewayInfo, connectionManagerPhoneAccountHandle,
               targetPhoneAccountHandle, callDirection, shouldAttachToExistingConnection,
               targetPhoneAccountHandle, callDirection, shouldAttachToExistingConnection,
               isConference, clockProxy, toastFactory);
               isConference, clockProxy, toastFactory, featureFlags);


    }
    }


@@ -841,8 +845,10 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
            boolean shouldAttachToExistingConnection,
            boolean shouldAttachToExistingConnection,
            boolean isConference,
            boolean isConference,
            ClockProxy clockProxy,
            ClockProxy clockProxy,
            ToastFactory toastFactory) {
            ToastFactory toastFactory,
            FeatureFlags featureFlags) {


        mFlags = featureFlags;
        mId = callId;
        mId = callId;
        mConnectionId = callId;
        mConnectionId = callId;
        mState = (isConference && callDirection != CALL_DIRECTION_INCOMING &&
        mState = (isConference && callDirection != CALL_DIRECTION_INCOMING &&
@@ -892,6 +898,7 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
     * connection, regardless of whether it's incoming or outgoing.
     * connection, regardless of whether it's incoming or outgoing.
     * @param connectTimeMillis The connection time of the call.
     * @param connectTimeMillis The connection time of the call.
     * @param clockProxy
     * @param clockProxy
     * @param featureFlags The telecom feature flags.
     */
     */
    Call(
    Call(
            String callId,
            String callId,
@@ -910,11 +917,13 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
            long connectTimeMillis,
            long connectTimeMillis,
            long connectElapsedTimeMillis,
            long connectElapsedTimeMillis,
            ClockProxy clockProxy,
            ClockProxy clockProxy,
            ToastFactory toastFactory) {
            ToastFactory toastFactory,
            FeatureFlags featureFlags) {
        this(callId, context, callsManager, lock, repository,
        this(callId, context, callsManager, lock, repository,
                phoneNumberUtilsAdapter, handle, gatewayInfo,
                phoneNumberUtilsAdapter, handle, gatewayInfo,
                connectionManagerPhoneAccountHandle, targetPhoneAccountHandle, callDirection,
                connectionManagerPhoneAccountHandle, targetPhoneAccountHandle, callDirection,
                shouldAttachToExistingConnection, isConference, clockProxy, toastFactory);
                shouldAttachToExistingConnection, isConference, clockProxy, toastFactory,
                featureFlags);


        mConnectTimeMillis = connectTimeMillis;
        mConnectTimeMillis = connectTimeMillis;
        mConnectElapsedTimeMillis = connectElapsedTimeMillis;
        mConnectElapsedTimeMillis = connectElapsedTimeMillis;
@@ -1766,8 +1775,12 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
                    accountHandle.getComponentName().getPackageName(),
                    accountHandle.getComponentName().getPackageName(),
                    mContext.getPackageManager());
                    mContext.getPackageManager());
            // Set the associated user for the call for MT calls based on the target phone account.
            // Set the associated user for the call for MT calls based on the target phone account.
            if (isIncoming() && !accountHandle.getUserHandle().equals(mAssociatedUser)) {
            UserHandle associatedUser = UserUtil.getAssociatedUserForCall(
                setAssociatedUser(accountHandle.getUserHandle());
                    mFlags.workProfileAssociatedUser(),
                    mCallsManager.getPhoneAccountRegistrar(), mCallsManager.getCurrentUserHandle(),
                    accountHandle);
            if (isIncoming() && !associatedUser.equals(mAssociatedUser)) {
                setAssociatedUser(associatedUser);
            }
            }
        }
        }
    }
    }
@@ -4092,7 +4105,7 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
     * @param associatedUser
     * @param associatedUser
     */
     */
    public void setAssociatedUser(UserHandle associatedUser) {
    public void setAssociatedUser(UserHandle associatedUser) {
        Log.i(this, "Setting associated user for call");
        Log.i(this, "Setting associated user for call: %s", associatedUser);
        Preconditions.checkNotNull(associatedUser);
        Preconditions.checkNotNull(associatedUser);
        mAssociatedUser = associatedUser;
        mAssociatedUser = associatedUser;
    }
    }
+42 −20
Original line number Original line Diff line number Diff line
@@ -1482,7 +1482,8 @@ public class CallsManager extends Call.ListenerBase
                false /* forceAttachToExistingConnection */,
                false /* forceAttachToExistingConnection */,
                isConference, /* isConference */
                isConference, /* isConference */
                mClockProxy,
                mClockProxy,
                mToastFactory);
                mToastFactory,
                mFeatureFlags);
        // Ensure new calls related to self-managed calls/connections are set as such. This will
        // Ensure new calls related to self-managed calls/connections are set as such. This will
        // be overridden when the actual connection is returned in startCreateConnection, however
        // be overridden when the actual connection is returned in startCreateConnection, however
        // doing this now ensures the logs and any other logic will treat this call as self-managed
        // doing this now ensures the logs and any other logic will treat this call as self-managed
@@ -1509,7 +1510,10 @@ public class CallsManager extends Call.ListenerBase
                }
                }
            }
            }
            // Incoming address was set via EXTRA_INCOMING_CALL_ADDRESS above.
            // Incoming address was set via EXTRA_INCOMING_CALL_ADDRESS above.
            call.setAssociatedUser(phoneAccountHandle.getUserHandle());
            UserHandle associatedUser = UserUtil.getAssociatedUserForCall(
                    mFeatureFlags.workProfileAssociatedUser(),
                    getPhoneAccountRegistrar(), getCurrentUserHandle(), phoneAccountHandle);
            call.setAssociatedUser(associatedUser);
        }
        }


        if (phoneAccount != null) {
        if (phoneAccount != null) {
@@ -1629,15 +1633,19 @@ public class CallsManager extends Call.ListenerBase
        // Check if the target phone account is possibly in ECBM.
        // Check if the target phone account is possibly in ECBM.
        call.setIsInECBM(getEmergencyCallHelper()
        call.setIsInECBM(getEmergencyCallHelper()
                .isLastOutgoingEmergencyCallPAH(call.getTargetPhoneAccount()));
                .isLastOutgoingEmergencyCallPAH(call.getTargetPhoneAccount()));
        // If the phone account user profile is paused or the call isn't visible to the secondary/

        // guest user, reject the non-emergency incoming call. When the current user is the admin,
        // Check if call is visible to the current user.
        // we need to allow the calls to go through if the work profile isn't paused. We should
        boolean isCallHiddenFromProfile = !isCallVisibleForUser(call, mCurrentUserHandle);
        // always allow emergency calls and also allow non-emergency calls when ECBM is active for
        // For admins, we should check if the work profile is paused in order to reject
        // the phone account.
        // the call.
        if ((mUserManager.isQuietModeEnabled(call.getAssociatedUser())
        if (mUserManager.isUserAdmin(mCurrentUserHandle.getIdentifier())) {
                || (!mUserManager.isUserAdmin(mCurrentUserHandle.getIdentifier())
            isCallHiddenFromProfile &= mUserManager.isQuietModeEnabled(
                && !isCallVisibleForUser(call, mCurrentUserHandle)))
                call.getAssociatedUser());
                && !call.isEmergencyCall() && !call.isInECBM()) {
        }

        // We should always allow emergency calls and also allow non-emergency calls when ECBM
        // is active for the phone account.
        if (isCallHiddenFromProfile && !call.isEmergencyCall() && !call.isInECBM()) {
            Log.d(TAG, "Rejecting non-emergency call because the owner %s is not running.",
            Log.d(TAG, "Rejecting non-emergency call because the owner %s is not running.",
                    phoneAccountHandle.getUserHandle());
                    phoneAccountHandle.getUserHandle());
            call.setMissedReason(USER_MISSED_NOT_RUNNING);
            call.setMissedReason(USER_MISSED_NOT_RUNNING);
@@ -1710,11 +1718,15 @@ public class CallsManager extends Call.ListenerBase
                true /* forceAttachToExistingConnection */,
                true /* forceAttachToExistingConnection */,
                false, /* isConference */
                false, /* isConference */
                mClockProxy,
                mClockProxy,
                mToastFactory);
                mToastFactory,
                mFeatureFlags);
        call.initAnalytics();
        call.initAnalytics();


        // For unknown calls, base the associated user off of the target phone account handle.
        // For unknown calls, base the associated user off of the target phone account handle.
        call.setAssociatedUser(phoneAccountHandle.getUserHandle());
        UserHandle associatedUser = UserUtil.getAssociatedUserForCall(
                mFeatureFlags.workProfileAssociatedUser(),
                getPhoneAccountRegistrar(), getCurrentUserHandle(), phoneAccountHandle);
        call.setAssociatedUser(associatedUser);
        setIntentExtrasAndStartTime(call, extras);
        setIntentExtrasAndStartTime(call, extras);
        call.addListener(this);
        call.addListener(this);
        notifyStartCreateConnection(call);
        notifyStartCreateConnection(call);
@@ -1828,7 +1840,8 @@ public class CallsManager extends Call.ListenerBase
                    false /* forceAttachToExistingConnection */,
                    false /* forceAttachToExistingConnection */,
                    isConference, /* isConference */
                    isConference, /* isConference */
                    mClockProxy,
                    mClockProxy,
                    mToastFactory);
                    mToastFactory,
                    mFeatureFlags);


            if (extras.containsKey(TelecomManager.TRANSACTION_CALL_ID_KEY)) {
            if (extras.containsKey(TelecomManager.TRANSACTION_CALL_ID_KEY)) {
                call.setIsTransactionalCall(true);
                call.setIsTransactionalCall(true);
@@ -4235,7 +4248,8 @@ public class CallsManager extends Call.ListenerBase
                connectTime,
                connectTime,
                connectElapsedTime,
                connectElapsedTime,
                mClockProxy,
                mClockProxy,
                mToastFactory);
                mToastFactory,
                mFeatureFlags);


        // Unlike connections, conferences are not created first and then notified as create
        // Unlike connections, conferences are not created first and then notified as create
        // connection complete from the CS.  They originate from the CS and are reported directly to
        // connection complete from the CS.  They originate from the CS and are reported directly to
@@ -4253,7 +4267,10 @@ public class CallsManager extends Call.ListenerBase
        call.setStatusHints(parcelableConference.getStatusHints());
        call.setStatusHints(parcelableConference.getStatusHints());
        call.putConnectionServiceExtras(parcelableConference.getExtras());
        call.putConnectionServiceExtras(parcelableConference.getExtras());
        // For conference calls, set the associated user from the target phone account user handle.
        // For conference calls, set the associated user from the target phone account user handle.
        call.setAssociatedUser(phoneAccount.getUserHandle());
        UserHandle associatedUser = UserUtil.getAssociatedUserForCall(
                mFeatureFlags.workProfileAssociatedUser(), getPhoneAccountRegistrar(),
                getCurrentUserHandle(), phoneAccount);
        call.setAssociatedUser(associatedUser);
        // In case this Conference was added via a ConnectionManager, keep track of the original
        // In case this Conference was added via a ConnectionManager, keep track of the original
        // Connection ID as created by the originating ConnectionService.
        // Connection ID as created by the originating ConnectionService.
        Bundle extras = parcelableConference.getExtras();
        Bundle extras = parcelableConference.getExtras();
@@ -5291,7 +5308,8 @@ public class CallsManager extends Call.ListenerBase
                connection.getConnectTimeMillis() /* connectTimeMillis */,
                connection.getConnectTimeMillis() /* connectTimeMillis */,
                connection.getConnectElapsedTimeMillis(), /* connectElapsedTimeMillis */
                connection.getConnectElapsedTimeMillis(), /* connectElapsedTimeMillis */
                mClockProxy,
                mClockProxy,
                mToastFactory);
                mToastFactory,
                mFeatureFlags);


        call.initAnalytics();
        call.initAnalytics();
        call.getAnalytics().setCreatedFromExistingConnection(true);
        call.getAnalytics().setCreatedFromExistingConnection(true);
@@ -5306,7 +5324,10 @@ public class CallsManager extends Call.ListenerBase
                connection.getCallerDisplayNamePresentation());
                connection.getCallerDisplayNamePresentation());
        // For existing connections, use the phone account user handle to determine the user
        // For existing connections, use the phone account user handle to determine the user
        // association with the call.
        // association with the call.
        call.setAssociatedUser(connection.getPhoneAccount().getUserHandle());
        UserHandle associatedUser = UserUtil.getAssociatedUserForCall(
                mFeatureFlags.workProfileAssociatedUser(), getPhoneAccountRegistrar(),
                getCurrentUserHandle(), connection.getPhoneAccount());
        call.setAssociatedUser(associatedUser);
        call.addListener(this);
        call.addListener(this);
        call.putConnectionServiceExtras(connection.getExtras());
        call.putConnectionServiceExtras(connection.getExtras());


@@ -5951,7 +5972,7 @@ public class CallsManager extends Call.ListenerBase
                handoverFromCall.getHandle(), null,
                handoverFromCall.getHandle(), null,
                null, null,
                null, null,
                Call.CALL_DIRECTION_OUTGOING, false,
                Call.CALL_DIRECTION_OUTGOING, false,
                false, mClockProxy, mToastFactory);
                false, mClockProxy, mToastFactory, mFeatureFlags);
        call.initAnalytics();
        call.initAnalytics();


        // Set self-managed and voipAudioMode if destination is self-managed CS
        // Set self-managed and voipAudioMode if destination is self-managed CS
@@ -6158,7 +6179,8 @@ public class CallsManager extends Call.ListenerBase
                false /* forceAttachToExistingConnection */,
                false /* forceAttachToExistingConnection */,
                false, /* isConference */
                false, /* isConference */
                mClockProxy,
                mClockProxy,
                mToastFactory);
                mToastFactory,
                mFeatureFlags);


        if (fromCall == null || isHandoverInProgress() ||
        if (fromCall == null || isHandoverInProgress() ||
                !isHandoverFromPhoneAccountSupported(fromCall.getTargetPhoneAccount()) ||
                !isHandoverFromPhoneAccountSupported(fromCall.getTargetPhoneAccount()) ||
+32 −8
Original line number Original line Diff line number Diff line
@@ -82,6 +82,7 @@ import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Collectors;
import java.util.stream.Stream;


/**
/**
 * Binds to {@link IInCallService} and provides the service to {@link CallsManager} through which it
 * Binds to {@link IInCallService} and provides the service to {@link CallsManager} through which it
@@ -1406,17 +1407,31 @@ public class InCallController extends CallsManagerListenerBase implements
    @Override
    @Override
    public void onCallRemoved(Call call) {
    public void onCallRemoved(Call call) {
        Log.i(this, "onCallRemoved: %s", call);
        Log.i(this, "onCallRemoved: %s", call);
        if (mCallsManager.getCalls().isEmpty()) {
        // Instead of checking if there are no active calls, we should check if there any calls with
        // the same associated user returned from getUserFromCall. For instance, it's possible to
        // have calls coexist on the personal profile and work profile, in which case, we would only
        // remove the ICS connection for the user associated with the call to be disconnected.
        UserHandle userFromCall = getUserFromCall(call);
        Stream<Call> callsAssociatedWithUserFromCall = mCallsManager.getCalls().stream()
                .filter((c) -> getUserFromCall(c).equals(userFromCall));
        boolean isCallCountZero = mFeatureFlags.workProfileAssociatedUser()
                ? callsAssociatedWithUserFromCall.count() == 0
                : mCallsManager.getCalls().isEmpty();
        if (isCallCountZero) {
            /** Let's add a 2 second delay before we send unbind to the services to hopefully
            /** Let's add a 2 second delay before we send unbind to the services to hopefully
             *  give them enough time to process all the pending messages.
             *  give them enough time to process all the pending messages.
             */
             */
            mHandler.postDelayed(new Runnable("ICC.oCR", mLock) {
            mHandler.postDelayed(new Runnable("ICC.oCR", mLock) {
                @Override
                @Override
                public void loggedRun() {
                public void loggedRun() {
                    // Check again to make sure there are no active calls.
                    // Check again to make sure there are no active calls for the associated user.
                    if (mCallsManager.getCalls().isEmpty()) {
                    Stream<Call> callsAssociatedWithUserFromCall = mCallsManager.getCalls().stream()
                        unbindFromServices(getUserFromCall(call));
                            .filter((c) -> getUserFromCall(c).equals(userFromCall));

                    boolean isCallCountZero = mFeatureFlags.workProfileAssociatedUser()
                            ? callsAssociatedWithUserFromCall.count() == 0
                            : mCallsManager.getCalls().isEmpty();
                    if (isCallCountZero) {
                        unbindFromServices(userFromCall);
                        mEmergencyCallHelper.maybeRevokeTemporaryLocationPermission();
                        mEmergencyCallHelper.maybeRevokeTemporaryLocationPermission();
                    }
                    }
                }
                }
@@ -1832,6 +1847,7 @@ public class InCallController extends CallsManagerListenerBase implements
     * Unbinds an existing bound connection to the in-call app.
     * Unbinds an existing bound connection to the in-call app.
     */
     */
    public void unbindFromServices(UserHandle userHandle) {
    public void unbindFromServices(UserHandle userHandle) {
        Log.i(this, "Unbinding from services for user %s", userHandle);
        try {
        try {
            mContext.unregisterReceiver(mPackageChangedReceiver);
            mContext.unregisterReceiver(mPackageChangedReceiver);
        } catch (IllegalArgumentException e) {
        } catch (IllegalArgumentException e) {
@@ -2320,7 +2336,9 @@ public class InCallController extends CallsManagerListenerBase implements
        }
        }


        // Upon successful connection, send the state of the world to the service.
        // Upon successful connection, send the state of the world to the service.
        List<Call> calls = orderCallsWithChildrenFirst(mCallsManager.getCalls());
        List<Call> calls = orderCallsWithChildrenFirst(mCallsManager.getCalls().stream().filter(
                call -> getUserFromCall(call).equals(userHandle))
                .collect(Collectors.toUnmodifiableList()));
        Log.i(this, "Adding %s calls to InCallService after onConnected: %s, including external " +
        Log.i(this, "Adding %s calls to InCallService after onConnected: %s, including external " +
                "calls", calls.size(), info.getComponentName());
                "calls", calls.size(), info.getComponentName());
        int numCallsSent = 0;
        int numCallsSent = 0;
@@ -2463,6 +2481,9 @@ public class InCallController extends CallsManagerListenerBase implements
                }
                }
            }
            }
            Log.i(this, "Components updated: %s", componentsUpdated);
            Log.i(this, "Components updated: %s", componentsUpdated);
        } else {
            Log.i(this,
                    "Unable to update call. InCallService not found for user: %s", userFromCall);
        }
        }
    }
    }


@@ -2901,8 +2922,11 @@ public class InCallController extends CallsManagerListenerBase implements
        } else {
        } else {
            UserHandle userFromCall = call.getAssociatedUser();
            UserHandle userFromCall = call.getAssociatedUser();
            UserManager userManager = mContext.getSystemService(UserManager.class);
            UserManager userManager = mContext.getSystemService(UserManager.class);
            // Emergency call should never be blocked, so if the user associated with call is in
            // Emergency call should never be blocked, so if the user associated with the target
            // quite mode, use the primary user for the emergency call.
            // phone account handle user is in quiet mode, use the current user for the ecall.
            // Note, that this only applies to incoming calls that are received on assigned
            // sims (i.e. work sim), where the associated user would be the target phone account
            // handle user.
            if ((call.isEmergencyCall() || call.isInECBM())
            if ((call.isEmergencyCall() || call.isInECBM())
                    && (userManager.isQuietModeEnabled(userFromCall)
                    && (userManager.isQuietModeEnabled(userFromCall)
                    // We should also account for secondary/guest users where the profile may not
                    // We should also account for secondary/guest users where the profile may not
Loading