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

Commit 154133c8 authored by Christine Franks's avatar Christine Franks Committed by Automerger Merge Worker
Browse files

Merge "Split and streamline create and control requests" into udc-dev am: b6751e82

parents 6d9d7ded b6751e82
Loading
Loading
Loading
Loading
+36 −6
Original line number Diff line number Diff line
@@ -31,11 +31,8 @@ message Telecom {
      // Caller's name and/or phone number; what a user would see displayed when receiving an
      // incoming call on the local device
      string caller_id = 1;
      // Human-readable name of the app processing this call
      string app_name = 2;
      bytes app_icon = 3;
      // Unique identifier for this app, such as a package name.
      string app_identifier = 4;
      bytes app_icon = 2;
      CallFacilitator facilitator = 3;
    }
    Origin origin = 2;

@@ -51,6 +48,37 @@ message Telecom {
    repeated Control controls = 4;
  }

  message Request {
    message CreateAction {
      // UUID representing this request.
      string id = 1;
      // URI representing the address of the intended callee.
      string address = 2;
      // Which facilitator should handle this call.
      CallFacilitator facilitator = 3;
    }
    message ControlAction {
      // UUID representing the call to perform the control action on
      string id = 1;
      // The control to perform
      Control control = 2;
    }

    oneof action {
      CreateAction create_action = 1;
      ControlAction control_action = 2;
    }
  }

  // A facilitator (namely an app) that can be directed to place calls.
  // Next index: 3
  message CallFacilitator {
    // Human-readable name of the facilitator
    string name = 1;
    // Unique identifier for this facilitator, such as a package name.
    string identifier = 2;
  }

  enum Control {
    UNKNOWN_CONTROL = 0;
    ACCEPT = 1;
@@ -68,5 +96,7 @@ message Telecom {
  // The list of active calls.
  repeated Call calls = 1;
  // The list of requested calls or call changes.
  repeated Call requests = 2;
  repeated Request requests = 2;
  // The list of call facilitators that this device currently supports.
  repeated CallFacilitator facilitators = 3;
}
+129 −73
Original line number Diff line number Diff line
@@ -16,18 +16,21 @@

package com.android.server.companion.datatransfer.contextsync;

import android.content.ComponentName;
import android.media.AudioManager;
import android.os.Bundle;
import android.telecom.Call;
import android.telecom.Connection;
import android.telecom.ConnectionRequest;
import android.telecom.ConnectionService;
import android.telecom.DisconnectCause;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.companion.CompanionDeviceManagerServiceInternal;

import java.util.HashMap;
import java.util.Map;
@@ -39,10 +42,47 @@ public class CallMetadataSyncConnectionService extends ConnectionService {

    private static final String TAG = "CallMetadataSyncConnectionService";

    private AudioManager mAudioManager;
    private TelecomManager mTelecomManager;
    private final Map<PhoneAccountHandleIdentifier, PhoneAccountHandle> mPhoneAccountHandles =
            new HashMap<>();
    @VisibleForTesting
    AudioManager mAudioManager;
    @VisibleForTesting
    TelecomManager mTelecomManager;
    private CompanionDeviceManagerServiceInternal mCdmsi;
    @VisibleForTesting
    final Map<CallMetadataSyncConnectionIdentifier, CallMetadataSyncConnection>
            mActiveConnections = new HashMap<>();
    @VisibleForTesting
    final CrossDeviceSyncControllerCallback
            mCrossDeviceSyncControllerCallback = new CrossDeviceSyncControllerCallback() {

                @Override
                void processContextSyncMessage(int associationId,
                        CallMetadataSyncData callMetadataSyncData) {
                    // Add new calls or update existing calls.
                    for (CallMetadataSyncData.Call call : callMetadataSyncData.getCalls()) {
                        final CallMetadataSyncConnection existingConnection =
                                mActiveConnections.get(new CallMetadataSyncConnectionIdentifier(
                                        associationId, call.getId()));
                        if (existingConnection == null) {
                            final Bundle extras = new Bundle();
                            extras.putInt(CrossDeviceSyncController.EXTRA_ASSOCIATION_ID,
                                    associationId);
                            extras.putParcelable(CrossDeviceSyncController.EXTRA_CALL, call);
                            mTelecomManager.addNewIncomingCall(call.getPhoneAccountHandle(),
                                    extras);
                        } else {
                            existingConnection.update(call);
                        }
                    }
                    // Remove obsolete calls.
                    mActiveConnections.values().removeIf(connection -> {
                        if (!callMetadataSyncData.hasCall(connection.getCallId())) {
                            connection.setDisconnected(new DisconnectCause(DisconnectCause.REMOTE));
                            return true;
                        }
                        return false;
                    });
                }
            };

    @Override
    public void onCreate() {
@@ -50,83 +90,96 @@ public class CallMetadataSyncConnectionService extends ConnectionService {

        mAudioManager = getSystemService(AudioManager.class);
        mTelecomManager = getSystemService(TelecomManager.class);
        mCdmsi = LocalServices.getService(CompanionDeviceManagerServiceInternal.class);
        mCdmsi.registerCallMetadataSyncCallback(mCrossDeviceSyncControllerCallback);
    }

    /**
     * Registers a {@link android.telecom.PhoneAccount} for a given call-capable app on the synced
     * device.
     */
    private void registerPhoneAccount(int associationId, String appIdentifier,
            String humanReadableAppName) {
        final PhoneAccountHandleIdentifier phoneAccountHandleIdentifier =
                new PhoneAccountHandleIdentifier(associationId, appIdentifier);
        final PhoneAccount phoneAccount = createPhoneAccount(phoneAccountHandleIdentifier,
                humanReadableAppName);
        mTelecomManager.registerPhoneAccount(phoneAccount);
        mTelecomManager.enablePhoneAccount(mPhoneAccountHandles.get(phoneAccountHandleIdentifier),
                true);
    }

    /**
     * Unregisters a {@link android.telecom.PhoneAccount} for a given call-capable app on the synced
     * device.
     */
    private void unregisterPhoneAccount(int associationId, String appIdentifier) {
        mTelecomManager.unregisterPhoneAccount(mPhoneAccountHandles.remove(
                new PhoneAccountHandleIdentifier(associationId, appIdentifier)));
    @Override
    public Connection onCreateIncomingConnection(PhoneAccountHandle phoneAccountHandle,
            ConnectionRequest connectionRequest) {
        final int associationId = connectionRequest.getExtras().getInt(
                CrossDeviceSyncController.EXTRA_ASSOCIATION_ID);
        final CallMetadataSyncData.Call call = connectionRequest.getExtras().getParcelable(
                CrossDeviceSyncController.EXTRA_CALL, CallMetadataSyncData.Call.class);
        final CallMetadataSyncConnection connection = new CallMetadataSyncConnection(
                mTelecomManager,
                mAudioManager,
                associationId,
                call,
                new CallMetadataSyncConnectionCallback() {
                    @Override
                    void sendCallAction(int associationId, String callId, int action) {
                        mCdmsi.sendCrossDeviceSyncMessage(associationId,
                                CrossDeviceSyncController.createCallControlMessage(callId, action));
                    }
                });
        connection.setConnectionProperties(
                Connection.PROPERTY_IS_EXTERNAL_CALL | Connection.PROPERTY_SELF_MANAGED);
        return connection;
    }

    @VisibleForTesting
    PhoneAccount createPhoneAccount(PhoneAccountHandleIdentifier phoneAccountHandleIdentifier,
            String humanReadableAppName) {
        if (mPhoneAccountHandles.containsKey(phoneAccountHandleIdentifier)) {
            // Already exists!
            return null;
        }
        final PhoneAccountHandle handle = new PhoneAccountHandle(
                new ComponentName(this, CallMetadataSyncConnectionService.class),
                UUID.randomUUID().toString());
        mPhoneAccountHandles.put(phoneAccountHandleIdentifier, handle);
        return new PhoneAccount.Builder(handle, humanReadableAppName)
                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER
                        | PhoneAccount.CAPABILITY_SELF_MANAGED).build();
    }

    static final class PhoneAccountHandleIdentifier {
        private final int mAssociationId;
        private final String mAppIdentifier;

        PhoneAccountHandleIdentifier(int associationId, String appIdentifier) {
            mAssociationId = associationId;
            mAppIdentifier = appIdentifier;
    @Override
    public void onCreateIncomingConnectionFailed(PhoneAccountHandle phoneAccountHandle,
            ConnectionRequest connectionRequest) {
        Slog.e(TAG, "onCreateIncomingConnectionFailed for: " + phoneAccountHandle.getId());
    }

        public int getAssociationId() {
            return mAssociationId;
    @Override
    public Connection onCreateOutgoingConnection(PhoneAccountHandle phoneAccountHandle,
            ConnectionRequest connectionRequest) {
        final PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(phoneAccountHandle);

        final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call();
        call.setId(UUID.randomUUID().toString());
        call.setStatus(android.companion.Telecom.Call.UNKNOWN_STATUS);
        call.setPhoneAccountHandle(phoneAccountHandle);
        final CallMetadataSyncData.CallFacilitator callFacilitator =
                new CallMetadataSyncData.CallFacilitator(phoneAccount.getLabel().toString(),
                        phoneAccount.getExtras().getString(
                                CrossDeviceSyncController.EXTRA_CALL_FACILITATOR_ID));
        call.setFacilitator(callFacilitator);

        final int associationId = connectionRequest.getExtras().getInt(
                CrossDeviceSyncController.EXTRA_ASSOCIATION_ID);

        final CallMetadataSyncConnection connection = new CallMetadataSyncConnection(
                mTelecomManager,
                mAudioManager,
                associationId,
                call,
                new CallMetadataSyncConnectionCallback() {
                    @Override
                    void sendCallAction(int associationId, String callId, int action) {
                        mCdmsi.sendCrossDeviceSyncMessage(associationId,
                                CrossDeviceSyncController.createCallControlMessage(callId, action));
                    }
                });
        connection.setConnectionProperties(
                Connection.PROPERTY_IS_EXTERNAL_CALL | Connection.PROPERTY_SELF_MANAGED);

        public String getAppIdentifier() {
            return mAppIdentifier;
        mCdmsi.sendCrossDeviceSyncMessage(associationId,
                CrossDeviceSyncController.createCallCreateMessage(call.getId(),
                        connectionRequest.getAddress().toString(),
                        call.getFacilitator().getIdentifier()));

        return connection;
    }

    @Override
        public int hashCode() {
            return Objects.hash(mAssociationId, mAppIdentifier);
    public void onCreateOutgoingConnectionFailed(PhoneAccountHandle phoneAccountHandle,
            ConnectionRequest connectionRequest) {
        Slog.e(TAG, "onCreateIncomingConnectionFailed for: " + phoneAccountHandle.getId());
    }

    @Override
        public boolean equals(Object other) {
            if (other instanceof PhoneAccountHandleIdentifier) {
                return ((PhoneAccountHandleIdentifier) other).getAssociationId() == mAssociationId
                        && mAppIdentifier != null
                        && mAppIdentifier.equals(
                        ((PhoneAccountHandleIdentifier) other).getAppIdentifier());
            }
            return false;
    public void onCreateConnectionComplete(Connection connection) {
        if (connection instanceof CallMetadataSyncConnection) {
            ((CallMetadataSyncConnection) connection).initialize();
        }
    }

    private static final class CallMetadataSyncConnectionIdentifier {
    @VisibleForTesting
    static final class CallMetadataSyncConnectionIdentifier {
        private final int mAssociationId;
        private final String mCallId;

@@ -153,18 +206,21 @@ public class CallMetadataSyncConnectionService extends ConnectionService {
            if (other instanceof CallMetadataSyncConnectionIdentifier) {
                return ((CallMetadataSyncConnectionIdentifier) other).getAssociationId()
                        == mAssociationId
                        && (((CallMetadataSyncConnectionIdentifier) other).getCallId() == mCallId);
                        && mCallId != null && mCallId.equals(
                                ((CallMetadataSyncConnectionIdentifier) other).getCallId());
            }
            return false;
        }
    }

    private abstract static class CallMetadataSyncConnectionCallback {
    @VisibleForTesting
    abstract static class CallMetadataSyncConnectionCallback {

        abstract void sendCallAction(int associationId, String callId, int action);
    }

    private static class CallMetadataSyncConnection extends Connection {
    @VisibleForTesting
    static class CallMetadataSyncConnection extends Connection {

        private final TelecomManager mTelecomManager;
        private final AudioManager mAudioManager;
+150 −19
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.companion.ContextSyncMessage;
import android.os.Parcel;
import android.os.Parcelable;
import android.telecom.PhoneAccountHandle;

import java.util.ArrayList;
import java.util.Collection;
@@ -34,7 +35,9 @@ import java.util.Set;
class CallMetadataSyncData {

    final Map<String, CallMetadataSyncData.Call> mCalls = new HashMap<>();
    final List<CallMetadataSyncData.Call> mRequests = new ArrayList<>();
    final List<CallMetadataSyncData.CallCreateRequest> mCallCreateRequests = new ArrayList<>();
    final List<CallMetadataSyncData.CallControlRequest> mCallControlRequests = new ArrayList<>();
    final List<CallMetadataSyncData.CallFacilitator> mCallFacilitators = new ArrayList<>();

    public void addCall(CallMetadataSyncData.Call call) {
        mCalls.put(call.getId(), call);
@@ -48,20 +51,145 @@ class CallMetadataSyncData {
        return mCalls.values();
    }

    public void addRequest(CallMetadataSyncData.Call call) {
        mRequests.add(call);
    public void addCallCreateRequest(CallMetadataSyncData.CallCreateRequest request) {
        mCallCreateRequests.add(request);
    }

    public List<CallMetadataSyncData.Call> getRequests() {
        return mRequests;
    public List<CallMetadataSyncData.CallCreateRequest> getCallCreateRequests() {
        return mCallCreateRequests;
    }

    public void addCallControlRequest(CallMetadataSyncData.CallControlRequest request) {
        mCallControlRequests.add(request);
    }

    public List<CallMetadataSyncData.CallControlRequest> getCallControlRequests() {
        return mCallControlRequests;
    }

    public void addFacilitator(CallFacilitator facilitator) {
        mCallFacilitators.add(facilitator);
    }

    public List<CallFacilitator> getFacilitators() {
        return mCallFacilitators;
    }

    public static class CallFacilitator implements Parcelable {
        private String mName;
        private String mIdentifier;

        CallFacilitator() {}

        CallFacilitator(String name, String identifier) {
            mName = name;
            mIdentifier = identifier;
        }

        CallFacilitator(Parcel parcel) {
            this(parcel.readString(), parcel.readString());
        }

        @Override
        public void writeToParcel(Parcel parcel, int parcelableFlags) {
            parcel.writeString(mName);
            parcel.writeString(mIdentifier);
        }

        public String getName() {
            return mName;
        }

        public String getIdentifier() {
            return mIdentifier;
        }

        public void setName(String name) {
            mName = name;
        }

        public void setIdentifier(String identifier) {
            mIdentifier = identifier;
        }

        @Override
        public int describeContents() {
            return 0;
        }

        @NonNull
        public static final Parcelable.Creator<CallFacilitator> CREATOR =
                new Parcelable.Creator<>() {

                    @Override
                    public CallFacilitator createFromParcel(Parcel source) {
                        return new CallFacilitator(source);
                    }

                    @Override
                    public CallFacilitator[] newArray(int size) {
                        return new CallFacilitator[size];
                    }
                };
    }

    public static class CallControlRequest {
        private String mId;
        private int mControl;

        public void setId(String id) {
            mId = id;
        }

        public void setControl(int control) {
            mControl = control;
        }

        public String getId() {
            return mId;
        }

        public int getControl() {
            return mControl;
        }
    }

    public static class CallCreateRequest {
        private String mId;
        private String mAddress;
        private CallFacilitator mFacilitator;

        public void setId(String id) {
            mId = id;
        }

        public void setAddress(String address) {
            mAddress = address;
        }

        public void setFacilitator(CallFacilitator facilitator) {
            mFacilitator = facilitator;
        }

        public String getId() {
            return mId;
        }

        public String getAddress() {
            return mAddress;
        }

        public CallFacilitator getFacilitator() {
            return mFacilitator;
        }
    }

    public static class Call implements Parcelable {
        private String mId;
        private String mCallerId;
        private byte[] mAppIcon;
        private String mAppName;
        private String mAppIdentifier;
        private CallFacilitator mFacilitator;
        private PhoneAccountHandle mPhoneAccountHandle;
        private int mStatus;
        private final Set<Integer> mControls = new HashSet<>();

@@ -70,8 +198,11 @@ class CallMetadataSyncData {
            call.setId(parcel.readString());
            call.setCallerId(parcel.readString());
            call.setAppIcon(parcel.readBlob());
            call.setAppName(parcel.readString());
            call.setAppIdentifier(parcel.readString());
            call.setFacilitator(parcel.readParcelable(CallFacilitator.class.getClassLoader(),
                    CallFacilitator.class));
            call.setPhoneAccountHandle(
                    parcel.readParcelable(PhoneAccountHandle.class.getClassLoader(),
                            android.telecom.PhoneAccountHandle.class));
            call.setStatus(parcel.readInt());
            final int numberOfControls = parcel.readInt();
            for (int i = 0; i < numberOfControls; i++) {
@@ -85,8 +216,8 @@ class CallMetadataSyncData {
            parcel.writeString(mId);
            parcel.writeString(mCallerId);
            parcel.writeBlob(mAppIcon);
            parcel.writeString(mAppName);
            parcel.writeString(mAppIdentifier);
            parcel.writeParcelable(mFacilitator, parcelableFlags);
            parcel.writeParcelable(mPhoneAccountHandle, parcelableFlags);
            parcel.writeInt(mStatus);
            parcel.writeInt(mControls.size());
            for (int control : mControls) {
@@ -106,12 +237,12 @@ class CallMetadataSyncData {
            mAppIcon = appIcon;
        }

        void setAppName(String appName) {
            mAppName = appName;
        void setFacilitator(CallFacilitator facilitator) {
            mFacilitator = facilitator;
        }

        void setAppIdentifier(String appIdentifier) {
            mAppIdentifier = appIdentifier;
        void setPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) {
            mPhoneAccountHandle = phoneAccountHandle;
        }

        void setStatus(int status) {
@@ -134,12 +265,12 @@ class CallMetadataSyncData {
            return mAppIcon;
        }

        String getAppName() {
            return mAppName;
        CallFacilitator getFacilitator() {
            return mFacilitator;
        }

        String getAppIdentifier() {
            return mAppIdentifier;
        PhoneAccountHandle getPhoneAccountHandle() {
            return mPhoneAccountHandle;
        }

        int getStatus() {
+4 −9
Original line number Diff line number Diff line
@@ -66,16 +66,11 @@ public class CallMetadataSyncInCallService extends InCallService {
                @Override
                void processContextSyncMessage(int associationId,
                        CallMetadataSyncData callMetadataSyncData) {
                    final Iterator<CallMetadataSyncData.Call> iterator =
                            callMetadataSyncData.getRequests().iterator();
                    final Iterator<CallMetadataSyncData.CallControlRequest> iterator =
                            callMetadataSyncData.getCallControlRequests().iterator();
                    while (iterator.hasNext()) {
                        final CallMetadataSyncData.Call call = iterator.next();
                        if (call.getId() != null) {
                            // The call is already assigned an id; treat as control invocations.
                            for (int control : call.getControls()) {
                                processCallControlAction(call.getId(), control);
                            }
                        }
                        final CallMetadataSyncData.CallControlRequest request = iterator.next();
                        processCallControlAction(request.getId(), request.getControl());
                        iterator.remove();
                    }
                }
+319 −13

File changed.

Preview size limit exceeded, changes collapsed.

Loading