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

Commit 18bc8a5b authored by Grace Jia's avatar Grace Jia
Browse files

Bind self-managed calls to InCallService that support these kind of

calls.

Bug: 161144815
Test: TelecomUnitTest, manually test with test apps.
Change-Id: Ifa3566d5e681b5ab41fb3123a25fc841fa96fe60
Merged-In: Ifa3566d5e681b5ab41fb3123a25fc841fa96fe60
parent 53e4d9d3
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ import android.telecom.Connection;
import android.telecom.ConnectionService;
import android.telecom.DisconnectCause;
import android.telecom.GatewayInfo;
import android.telecom.InCallService;
import android.telecom.Log;
import android.telecom.Logging.EventManager;
import android.telecom.ParcelableConference;
@@ -58,6 +59,7 @@ import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.widget.Toast;

import com.android.internal.annotations.VisibleForTesting;
@@ -509,6 +511,15 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
     */
    private boolean mIsSelfManaged = false;

    /**
     * Indicates whether the {@link PhoneAccount} associated with an self-managed call want to
     * expose the call to an {@link android.telecom.InCallService} which declares the metadata
     * {@link TelecomManager#METADATA_INCLUDE_SELF_MANAGED_CALLS},
     * For calls that {@link #mIsSelfManaged} is {@code false}, this value should be {@code false}
     * as well.
     */
    private boolean mVisibleToInCallService = false;

    /**
     * Indicates whether the {@link PhoneAccount} associated with this call supports video calling.
     * {@code True} if the phone account supports video calling, {@code false} otherwise.
@@ -1628,6 +1639,14 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
        setConnectionProperties(getConnectionProperties());
    }

    public boolean visibleToInCallService() {
        return mVisibleToInCallService;
    }

    public void setVisibleToInCallService(boolean visibleToInCallService) {
        mVisibleToInCallService = visibleToInCallService;
    }

    public void markFinishedHandoverStateAndCleanup(int handoverState) {
        if (mHandoverSourceCall != null) {
            mHandoverSourceCall.setHandoverState(handoverState);
@@ -3945,4 +3964,17 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
    public void setCallScreeningComponentName(String callScreeningComponentName) {
        mCallScreeningComponentName = callScreeningComponentName;
    }

    public void maybeOnInCallServiceTrackingChanged(boolean isTracking, boolean hasUi) {
        if (mConnectionService == null) {
            Log.w(this, "maybeOnInCallServiceTrackingChanged() request on a call"
                    + " without a connection service.");
        } else {
            if (hasUi) {
                mConnectionService.onUsingAlternativeUi(this, isTracking);
            } else if (isTracking) {
                mConnectionService.onTrackedByNonUiService(this, isTracking);
            }
        }
    }
}
+11 −1
Original line number Diff line number Diff line
@@ -1237,10 +1237,14 @@ public class CallsManager extends Call.ListenerBase
        PhoneAccount phoneAccount = mPhoneAccountRegistrar.getPhoneAccountUnchecked(
                phoneAccountHandle);
        if (phoneAccount != null) {
            Bundle phoneAccountExtras = phoneAccount.getExtras();
            call.setIsSelfManaged(phoneAccount.isSelfManaged());
            if (call.isSelfManaged()) {
                // Self managed calls will always be voip audio mode.
                call.setIsVoipAudioMode(true);
                call.setVisibleToInCallService(phoneAccountExtras != null
                        && phoneAccountExtras.getBoolean(
                        PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, false));
            } else {
                // Incoming call is managed, the active call is self-managed and can't be held.
                // We need to set extras on it to indicate whether answering will cause a 
@@ -1259,7 +1263,6 @@ public class CallsManager extends Call.ListenerBase
                }
            }

            Bundle phoneAccountExtras = phoneAccount.getExtras();
            if (phoneAccountExtras != null
                    && phoneAccountExtras.getBoolean(
                            PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE)) {
@@ -1474,6 +1477,7 @@ public class CallsManager extends Call.ListenerBase

        PhoneAccount account =
                mPhoneAccountRegistrar.getPhoneAccount(requestedAccountHandle, initiatingUser);
        Bundle phoneAccountExtra = account != null ? account.getExtras() : null;
        boolean isSelfManaged = account != null && account.isSelfManaged();

        // Create a call with original handle. The handle may be changed when the call is attached
@@ -1504,6 +1508,9 @@ public class CallsManager extends Call.ListenerBase
            if (isSelfManaged) {
                // Self-managed calls will ALWAYS use voip audio mode.
                call.setIsVoipAudioMode(true);
                call.setVisibleToInCallService(phoneAccountExtra != null
                        && phoneAccountExtra.getBoolean(
                                PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, false));
            }
            call.setInitiatingUser(initiatingUser);
            isReusedCall = false;
@@ -4740,6 +4747,9 @@ public class CallsManager extends Call.ListenerBase
        extras.putLong(TelecomManager.EXTRA_CALL_TELECOM_ROUTING_START_TIME_MILLIS,
              SystemClock.elapsedRealtime());

        if (call.visibleToInCallService()) {
            extras.putBoolean(PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, true);
        }
        call.setIntentExtras(extras);
    }

+28 −0
Original line number Diff line number Diff line
@@ -1573,6 +1573,34 @@ public class ConnectionServiceWrapper extends ServiceBinder implements
        }
    }

    /** @see IConnectionService#onUsingAlternativeUi(String, boolean, Session.Info) */
    @VisibleForTesting
    public void onUsingAlternativeUi(Call activeCall, boolean isUsingAlternativeUi) {
        final String callId = mCallIdMapper.getCallId(activeCall);
        if (callId != null && isServiceValid("onUsingAlternativeUi")) {
            try {
                logOutgoing("onUsingAlternativeUi %s", isUsingAlternativeUi);
                mServiceInterface.onUsingAlternativeUi(callId, isUsingAlternativeUi,
                        Log.getExternalSession(TELECOM_ABBREVIATION));
            } catch (RemoteException e) {
            }
        }
    }

    /** @see IConnectionService#onTrackedByNonUiService(String, boolean, Session.Info) */
    @VisibleForTesting
    public void onTrackedByNonUiService(Call activeCall, boolean isTracked) {
        final String callId = mCallIdMapper.getCallId(activeCall);
        if (callId != null && isServiceValid("onTrackedByNonUiService")) {
            try {
                logOutgoing("onTrackedByNonUiService %s", isTracked);
                mServiceInterface.onTrackedByNonUiService(callId, isTracked,
                        Log.getExternalSession(TELECOM_ABBREVIATION));
            } catch (RemoteException e) {
            }
        }
    }

    /** @see IConnectionService#disconnect(String, Session.Info) */
    void disconnect(Call call) {
        final String callId = mCallIdMapper.getCallId(call);
+70 −32
Original line number Diff line number Diff line
@@ -113,7 +113,7 @@ public class InCallController extends CallsManagerListenerBase {
        public Call mCall;
    }

    private class InCallServiceInfo {
    public static class InCallServiceInfo {
        private final ComponentName mComponentName;
        private boolean mIsExternalCallsSupported;
        private boolean mIsSelfManagedCallsSupported;
@@ -279,7 +279,8 @@ public class InCallController extends CallsManagerListenerBase {
            }

            if (call != null && call.isSelfManaged() &&
                    !mInCallServiceInfo.isSelfManagedCallsSupported()) {
                    (!mInCallServiceInfo.isSelfManagedCallsSupported()
                            || !call.visibleToInCallService())) {
                Log.i(this, "Skipping binding to %s - doesn't support self-mgd calls",
                        mInCallServiceInfo);
                mIsConnected = false;
@@ -342,6 +343,7 @@ public class InCallController extends CallsManagerListenerBase {
                            mInCallServiceInfo.getType(),
                            mInCallServiceInfo.getDisconnectTime()
                                    - mInCallServiceInfo.getBindingStartTime(), mIsNullBinding);
                    updateCallTracking(mCall, mInCallServiceInfo, false /* isAdd */);
                }

                InCallController.this.onDisconnected(mInCallServiceInfo);
@@ -748,6 +750,10 @@ public class InCallController extends CallsManagerListenerBase {
                newConnection.connect(callToConnectWith);
            }
        }

        public List<InCallServiceBindingConnection> getSubConnections() {
            return mSubConnections;
        }
    }

    private final Call.Listener mCallListener = new Call.ListenerBase() {
@@ -915,12 +921,12 @@ public class InCallController extends CallsManagerListenerBase {
        @Override
        public void onPackageUninstalled(String packageName) {
            mCarModeTracker.forceExitCarMode(packageName);
            updateCarModeForSwitchingConnection();
            updateCarModeForConnections();
        }
    };

    private static final int IN_CALL_SERVICE_TYPE_INVALID = 0;
    private static final int IN_CALL_SERVICE_TYPE_DIALER_UI = 1;
    private static final int IN_CALL_SERVICE_TYPE_DEFAULT_DIALER_UI = 1;
    private static final int IN_CALL_SERVICE_TYPE_SYSTEM_UI = 2;
    private static final int IN_CALL_SERVICE_TYPE_CAR_MODE_UI = 3;
    private static final int IN_CALL_SERVICE_TYPE_NON_UI = 4;
@@ -967,10 +973,9 @@ public class InCallController extends CallsManagerListenerBase {
    private boolean mIsCallUsingMicrophone = false;

    public InCallController(Context context, TelecomSystem.SyncRoot lock, CallsManager callsManager,
            SystemStateHelper systemStateHelper,
            DefaultDialerCache defaultDialerCache, Timeouts.Adapter timeoutsAdapter,
            EmergencyCallHelper emergencyCallHelper, CarModeTracker carModeTracker,
            ClockProxy clockProxy) {
            SystemStateHelper systemStateHelper, DefaultDialerCache defaultDialerCache,
            Timeouts.Adapter timeoutsAdapter, EmergencyCallHelper emergencyCallHelper,
            CarModeTracker carModeTracker, ClockProxy clockProxy) {
        mContext = context;
        mAppOpsManager = context.getSystemService(AppOpsManager.class);
        mLock = lock;
@@ -1013,12 +1018,16 @@ public class InCallController extends CallsManagerListenerBase {
                    continue;
                }

                if (call.isSelfManaged() && !info.isSelfManagedCallsSupported()) {
                if (call.isSelfManaged() && (!call.visibleToInCallService()
                        || !info.isSelfManagedCallsSupported())) {
                    continue;
                }

                // Only send the RTT call if it's a UI in-call service
                boolean includeRttCall = info.equals(mInCallServiceConnection.getInfo());
                boolean includeRttCall = false;
                if (mInCallServiceConnection != null) {
                    includeRttCall = info.equals(mInCallServiceConnection.getInfo());
                }

                componentsUpdated.add(info.getComponentName());
                IInCallService inCallService = entry.getValue();
@@ -1030,6 +1039,7 @@ public class InCallController extends CallsManagerListenerBase {
                        info.getType() == IN_CALL_SERVICE_TYPE_NON_UI);
                try {
                    inCallService.addCall(sanitizeParcelableCallForService(info, parcelableCall));
                    updateCallTracking(call, info, true /* isAdd */);
                } catch (RemoteException ignored) {
                }
            }
@@ -1079,7 +1089,8 @@ public class InCallController extends CallsManagerListenerBase {
                    continue;
                }

                if (call.isSelfManaged() && !info.isSelfManagedCallsSupported()) {
                if (call.isSelfManaged() && !call.visibleToInCallService()
                        && !info.isSelfManagedCallsSupported()) {
                    continue;
                }

@@ -1096,6 +1107,7 @@ public class InCallController extends CallsManagerListenerBase {
                        info.getType() == IN_CALL_SERVICE_TYPE_NON_UI);
                try {
                    inCallService.addCall(sanitizeParcelableCallForService(info, parcelableCall));
                    updateCallTracking(call, info, true /* isAdd */);
                } catch (RemoteException ignored) {
                }
            }
@@ -1356,7 +1368,8 @@ public class InCallController extends CallsManagerListenerBase {

    /**
     * Binds to all the UI-providing InCallService as well as system-implemented non-UI
     * InCallServices. Method-invoker must check {@link #isBoundAndConnectedToServices()} before invoking.
     * InCallServices. Method-invoker must check {@link #isBoundAndConnectedToServices()}
     * before invoking.
     *
     * @param call The newly added call that triggered the binding to the in-call services.
     */
@@ -1409,7 +1422,7 @@ public class InCallController extends CallsManagerListenerBase {
        }
    }

    private void connectToNonUiInCallServices(Call call) {
    private void updateNonUiInCallServices() {
        List<InCallServiceInfo> nonUIInCallComponents =
                getInCallServiceComponents(IN_CALL_SERVICE_TYPE_NON_UI);
        List<InCallServiceBindingConnection> nonUIInCalls = new LinkedList<>();
@@ -1427,7 +1440,14 @@ public class InCallController extends CallsManagerListenerBase {
                }
            }
        }
        mNonUIInCallServiceConnections = new NonUIInCallServiceConnectionCollection(nonUIInCalls);
        mNonUIInCallServiceConnections = new NonUIInCallServiceConnectionCollection(
                nonUIInCalls);
    }

    private void connectToNonUiInCallServices(Call call) {
        if (mNonUIInCallServiceConnections == null) {
            updateNonUiInCallServices();
        }
        mNonUIInCallServiceConnections.connect(call);

        IntentFilter packageChangedFilter = new IntentFilter(Intent.ACTION_PACKAGE_CHANGED);
@@ -1444,7 +1464,7 @@ public class InCallController extends CallsManagerListenerBase {
        InCallServiceInfo defaultDialerComponent =
                (systemPackageName != null && systemPackageName.equals(packageName))
                ? getInCallServiceComponent(packageName, IN_CALL_SERVICE_TYPE_SYSTEM_UI)
                : getInCallServiceComponent(packageName, IN_CALL_SERVICE_TYPE_DIALER_UI);
                : getInCallServiceComponent(packageName, IN_CALL_SERVICE_TYPE_DEFAULT_DIALER_UI);
        /* TODO: in Android 12 re-enable this an InCallService is required by the dialer role.
            if (packageName != null && defaultDialerComponent == null) {
                // The in call service of default phone app is disabled, send notification.
@@ -1494,7 +1514,6 @@ public class InCallController extends CallsManagerListenerBase {

    private List<InCallServiceInfo> getInCallServiceComponents(String packageName,
            ComponentName componentName, int requestedType) {

        List<InCallServiceInfo> retval = new LinkedList<>();

        Intent serviceIntent = new Intent(InCallService.SERVICE_INTERFACE);
@@ -1526,13 +1545,19 @@ public class InCallController extends CallsManagerListenerBase {
                if (requestedType == IN_CALL_SERVICE_TYPE_NON_UI) {
                    mKnownNonUiInCallServices.add(foundComponentName);
                }
                if (serviceInfo.enabled && (requestedType == 0 || requestedType == currentType)) {
                    retval.add(new InCallServiceInfo(foundComponentName,
                            isExternalCallsSupported, isSelfManageCallsSupported, requestedType));

                boolean isRequestedType;
                if (requestedType == IN_CALL_SERVICE_TYPE_INVALID) {
                    isRequestedType = true;
                } else {
                    isRequestedType = requestedType == currentType;
                }
                if (serviceInfo.enabled && isRequestedType) {
                    retval.add(new InCallServiceInfo(foundComponentName, isExternalCallsSupported,
                            isSelfManageCallsSupported, requestedType));
                }
            }
        }

        return retval;
    }

@@ -1598,7 +1623,7 @@ public class InCallController extends CallsManagerListenerBase {
                mDefaultDialerCache.getDefaultDialerApplication(
                    mCallsManager.getCurrentUserHandle().getIdentifier()));
        if (isDefaultDialerPackage && isUIService) {
            return IN_CALL_SERVICE_TYPE_DIALER_UI;
            return IN_CALL_SERVICE_TYPE_DEFAULT_DIALER_UI;
        }

        // Also allow any in-call service that has the control-experience permission (to ensure
@@ -1637,7 +1662,7 @@ public class InCallController extends CallsManagerListenerBase {

        if (info.getType() == IN_CALL_SERVICE_TYPE_CAR_MODE_UI
                || info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI
                || info.getType() == IN_CALL_SERVICE_TYPE_DIALER_UI) {
                || info.getType() == IN_CALL_SERVICE_TYPE_DEFAULT_DIALER_UI) {
            trackCallingUserInterfaceStarted(info);
        }
        IInCallService inCallService = IInCallService.Stub.asInterface(service);
@@ -1663,13 +1688,17 @@ public class InCallController extends CallsManagerListenerBase {
        int numCallsSent = 0;
        for (Call call : calls) {
            try {
                if ((call.isSelfManaged() && !info.isSelfManagedCallsSupported()) ||
                if ((call.isSelfManaged() && (!info.isSelfManagedCallsSupported()
                        || !call.visibleToInCallService())) ||
                        (call.isExternalCall() && !info.isExternalCallsSupported())) {
                    continue;
                }

                // Only send the RTT call if it's a UI in-call service
                boolean includeRttCall = info.equals(mInCallServiceConnection.getInfo());
                boolean includeRttCall = false;
                if (mInCallServiceConnection != null) {
                    includeRttCall = info.equals(mInCallServiceConnection.getInfo());
                }

                // Track the call if we don't already know about it.
                addCall(call);
@@ -1683,6 +1712,7 @@ public class InCallController extends CallsManagerListenerBase {
                        info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI ||
                        info.getType() == IN_CALL_SERVICE_TYPE_NON_UI);
                inCallService.addCall(sanitizeParcelableCallForService(info, parcelableCall));
                updateCallTracking(call, info, true /* isAdd */);
            } catch (RemoteException ignored) {
            }
        }
@@ -1709,7 +1739,7 @@ public class InCallController extends CallsManagerListenerBase {
        Log.i(this, "onDisconnected from %s", disconnectedInfo.getComponentName());
        if (disconnectedInfo.getType() == IN_CALL_SERVICE_TYPE_CAR_MODE_UI
                || disconnectedInfo.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI
                || disconnectedInfo.getType() == IN_CALL_SERVICE_TYPE_DIALER_UI) {
                || disconnectedInfo.getType() == IN_CALL_SERVICE_TYPE_DEFAULT_DIALER_UI) {
            trackCallingUserInterfaceStopped(disconnectedInfo);
        }
        mInCallServices.remove(disconnectedInfo);
@@ -1743,7 +1773,8 @@ public class InCallController extends CallsManagerListenerBase {
                    continue;
                }

                if (call.isSelfManaged() && !info.isSelfManagedCallsSupported()) {
                if (call.isSelfManaged() && (!call.visibleToInCallService()
                        || !info.isSelfManagedCallsSupported())) {
                    continue;
                }

@@ -1824,7 +1855,7 @@ public class InCallController extends CallsManagerListenerBase {
     */
    private ComponentName getConnectedUi() {
        InCallServiceInfo connectedUi = mInCallServices.keySet().stream().filter(
                i -> i.getType() == IN_CALL_SERVICE_TYPE_DIALER_UI
                i -> i.getType() == IN_CALL_SERVICE_TYPE_DEFAULT_DIALER_UI
                        || i.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI)
                .findAny()
                .orElse(null);
@@ -1942,13 +1973,13 @@ public class InCallController extends CallsManagerListenerBase {
            mCarModeTracker.handleExitCarMode(priority, packageName);
        }

        updateCarModeForSwitchingConnection();
        updateCarModeForConnections();
    }

    public void updateCarModeForSwitchingConnection() {
        if (mInCallServiceConnection != null) {
            Log.i(this, "updateCarModeForSwitchingConnection: car mode apps: %s",
    public void updateCarModeForConnections() {
        Log.i(this, "updateCarModeForConnections: car mode apps: %s",
                mCarModeTracker.getCarModeApps().stream().collect(Collectors.joining(", ")));
        if (mInCallServiceConnection != null) {
            if (shouldUseCarModeUI()) {
                mInCallServiceConnection.changeCarModeApp(
                        mCarModeTracker.getCurrentCarModePackage());
@@ -2058,4 +2089,11 @@ public class InCallController extends CallsManagerListenerBase {
        notificationManager.notify(NOTIFICATION_TAG, IN_CALL_SERVICE_NOTIFICATION_ID,
                builder.build());
    }

    private void updateCallTracking(Call call, InCallServiceInfo info, boolean isAdd) {
        int type = info.getType();
        boolean hasUi = type == IN_CALL_SERVICE_TYPE_CAR_MODE_UI
                || type == IN_CALL_SERVICE_TYPE_DEFAULT_DIALER_UI;
        call.maybeOnInCallServiceTrackingChanged(isAdd, hasUi);
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -70,6 +70,8 @@
             android:exported="true">
            <meta-data android:name="android.telecom.IN_CALL_SERVICE_UI"
                 android:value="true"/>
            <meta-data android:name="android.telecom.INCLUDE_SELF_MANAGED_CALLS"
                 android:value="true" />
            <intent-filter>
                <action android:name="android.telecom.InCallService"/>
            </intent-filter>
Loading