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

Commit 1cc6c776 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 am: 154133c8

parents 09e7dccf 154133c8
Loading
Loading
Loading
Loading
+36 −6
Original line number Original line 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
      // Caller's name and/or phone number; what a user would see displayed when receiving an
      // incoming call on the local device
      // incoming call on the local device
      string caller_id = 1;
      string caller_id = 1;
      // Human-readable name of the app processing this call
      bytes app_icon = 2;
      string app_name = 2;
      CallFacilitator facilitator = 3;
      bytes app_icon = 3;
      // Unique identifier for this app, such as a package name.
      string app_identifier = 4;
    }
    }
    Origin origin = 2;
    Origin origin = 2;


@@ -51,6 +48,37 @@ message Telecom {
    repeated Control controls = 4;
    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 {
  enum Control {
    UNKNOWN_CONTROL = 0;
    UNKNOWN_CONTROL = 0;
    ACCEPT = 1;
    ACCEPT = 1;
@@ -68,5 +96,7 @@ message Telecom {
  // The list of active calls.
  // The list of active calls.
  repeated Call calls = 1;
  repeated Call calls = 1;
  // The list of requested calls or call changes.
  // 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 Original line Diff line number Diff line
@@ -16,18 +16,21 @@


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


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


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


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


    private static final String TAG = "CallMetadataSyncConnectionService";
    private static final String TAG = "CallMetadataSyncConnectionService";


    private AudioManager mAudioManager;
    @VisibleForTesting
    private TelecomManager mTelecomManager;
    AudioManager mAudioManager;
    private final Map<PhoneAccountHandleIdentifier, PhoneAccountHandle> mPhoneAccountHandles =
    @VisibleForTesting
            new HashMap<>();
    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
    @Override
    public void onCreate() {
    public void onCreate() {
@@ -50,83 +90,96 @@ public class CallMetadataSyncConnectionService extends ConnectionService {


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


    /**
    @Override
     * Registers a {@link android.telecom.PhoneAccount} for a given call-capable app on the synced
    public Connection onCreateIncomingConnection(PhoneAccountHandle phoneAccountHandle,
     * device.
            ConnectionRequest connectionRequest) {
     */
        final int associationId = connectionRequest.getExtras().getInt(
    private void registerPhoneAccount(int associationId, String appIdentifier,
                CrossDeviceSyncController.EXTRA_ASSOCIATION_ID);
            String humanReadableAppName) {
        final CallMetadataSyncData.Call call = connectionRequest.getExtras().getParcelable(
        final PhoneAccountHandleIdentifier phoneAccountHandleIdentifier =
                CrossDeviceSyncController.EXTRA_CALL, CallMetadataSyncData.Call.class);
                new PhoneAccountHandleIdentifier(associationId, appIdentifier);
        final CallMetadataSyncConnection connection = new CallMetadataSyncConnection(
        final PhoneAccount phoneAccount = createPhoneAccount(phoneAccountHandleIdentifier,
                mTelecomManager,
                humanReadableAppName);
                mAudioManager,
        mTelecomManager.registerPhoneAccount(phoneAccount);
                associationId,
        mTelecomManager.enablePhoneAccount(mPhoneAccountHandles.get(phoneAccountHandleIdentifier),
                call,
                true);
                new CallMetadataSyncConnectionCallback() {
    }
                    @Override

                    void sendCallAction(int associationId, String callId, int action) {
    /**
                        mCdmsi.sendCrossDeviceSyncMessage(associationId,
     * Unregisters a {@link android.telecom.PhoneAccount} for a given call-capable app on the synced
                                CrossDeviceSyncController.createCallControlMessage(callId, action));
     * device.
                    }
     */
                });
    private void unregisterPhoneAccount(int associationId, String appIdentifier) {
        connection.setConnectionProperties(
        mTelecomManager.unregisterPhoneAccount(mPhoneAccountHandles.remove(
                Connection.PROPERTY_IS_EXTERNAL_CALL | Connection.PROPERTY_SELF_MANAGED);
                new PhoneAccountHandleIdentifier(associationId, appIdentifier)));
        return connection;
    }
    }


    @VisibleForTesting
    @Override
    PhoneAccount createPhoneAccount(PhoneAccountHandleIdentifier phoneAccountHandleIdentifier,
    public void onCreateIncomingConnectionFailed(PhoneAccountHandle phoneAccountHandle,
            String humanReadableAppName) {
            ConnectionRequest connectionRequest) {
        if (mPhoneAccountHandles.containsKey(phoneAccountHandleIdentifier)) {
        Slog.e(TAG, "onCreateIncomingConnectionFailed for: " + phoneAccountHandle.getId());
            // 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;
    }
    }


        public int getAssociationId() {
    @Override
            return mAssociationId;
    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() {
        mCdmsi.sendCrossDeviceSyncMessage(associationId,
            return mAppIdentifier;
                CrossDeviceSyncController.createCallCreateMessage(call.getId(),
                        connectionRequest.getAddress().toString(),
                        call.getFacilitator().getIdentifier()));

        return connection;
    }
    }


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


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


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


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


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


        abstract void sendCallAction(int associationId, String callId, int action);
        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 TelecomManager mTelecomManager;
        private final AudioManager mAudioManager;
        private final AudioManager mAudioManager;
+150 −19
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.companion.ContextSyncMessage;
import android.companion.ContextSyncMessage;
import android.os.Parcel;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Parcelable;
import android.telecom.PhoneAccountHandle;


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


    final Map<String, CallMetadataSyncData.Call> mCalls = new HashMap<>();
    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) {
    public void addCall(CallMetadataSyncData.Call call) {
        mCalls.put(call.getId(), call);
        mCalls.put(call.getId(), call);
@@ -48,20 +51,145 @@ class CallMetadataSyncData {
        return mCalls.values();
        return mCalls.values();
    }
    }


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


    public List<CallMetadataSyncData.Call> getRequests() {
    public List<CallMetadataSyncData.CallCreateRequest> getCallCreateRequests() {
        return mRequests;
        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 {
    public static class Call implements Parcelable {
        private String mId;
        private String mId;
        private String mCallerId;
        private String mCallerId;
        private byte[] mAppIcon;
        private byte[] mAppIcon;
        private String mAppName;
        private CallFacilitator mFacilitator;
        private String mAppIdentifier;
        private PhoneAccountHandle mPhoneAccountHandle;
        private int mStatus;
        private int mStatus;
        private final Set<Integer> mControls = new HashSet<>();
        private final Set<Integer> mControls = new HashSet<>();


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


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


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


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


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


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


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

File changed.

Preview size limit exceeded, changes collapsed.

Loading