diff options
author | Justin Klaassen <justinklaassen@google.com> | 2017-11-17 16:38:15 -0500 |
---|---|---|
committer | Justin Klaassen <justinklaassen@google.com> | 2017-11-17 16:38:15 -0500 |
commit | 6a65f2da209bff03cb0eb6da309710ac6ee5026d (patch) | |
tree | 48e2090e716d4178378cb0599fc5d9cffbcf3f63 /android/telecom | |
parent | 46c77c203439b3b37c99d09e326df4b1fe08c10b (diff) | |
download | android-28-6a65f2da209bff03cb0eb6da309710ac6ee5026d.tar.gz |
Import Android SDK Platform P [4456821]
/google/data/ro/projects/android/fetch_artifact \
--bid 4456821 \
--target sdk_phone_armv7-win_sdk \
sdk-repo-linux-sources-4456821.zip
AndroidVersion.ApiLevel has been modified to appear as 28
Change-Id: I2d206b200d7952f899a5d1647ab532638cc8dd43
Diffstat (limited to 'android/telecom')
-rw-r--r-- | android/telecom/Call.java | 66 | ||||
-rw-r--r-- | android/telecom/CallAudioState.java | 90 | ||||
-rw-r--r-- | android/telecom/Connection.java | 50 | ||||
-rw-r--r-- | android/telecom/ConnectionService.java | 77 | ||||
-rw-r--r-- | android/telecom/ConnectionServiceAdapter.java | 9 | ||||
-rw-r--r-- | android/telecom/ConnectionServiceAdapterServant.java | 10 | ||||
-rw-r--r-- | android/telecom/InCallAdapter.java | 35 | ||||
-rw-r--r-- | android/telecom/InCallService.java | 18 | ||||
-rw-r--r-- | android/telecom/Phone.java | 14 | ||||
-rw-r--r-- | android/telecom/PhoneAccount.java | 10 | ||||
-rw-r--r-- | android/telecom/RemoteConnectionService.java | 3 | ||||
-rw-r--r-- | android/telecom/TelecomManager.java | 35 |
12 files changed, 386 insertions, 31 deletions
diff --git a/android/telecom/Call.java b/android/telecom/Call.java index e13bd619..a07f2bbf 100644 --- a/android/telecom/Call.java +++ b/android/telecom/Call.java @@ -855,6 +855,39 @@ public final class Call { */ public static abstract class Callback { /** + * @hide + */ + @IntDef({HANDOVER_FAILURE_DEST_APP_REJECTED, HANDOVER_FAILURE_DEST_NOT_SUPPORTED, + HANDOVER_FAILURE_DEST_INVALID_PERM, HANDOVER_FAILURE_DEST_USER_REJECTED}) + @Retention(RetentionPolicy.SOURCE) + public @interface HandoverFailureErrors {} + + /** + * Handover failure reason returned via {@link #onHandoverFailed(Call, int)} when the app + * to handover the call rejects handover. + */ + public static final int HANDOVER_FAILURE_DEST_APP_REJECTED = 1; + + /** + * Handover failure reason returned via {@link #onHandoverFailed(Call, int)} when there is + * an error associated with unsupported handover. + */ + public static final int HANDOVER_FAILURE_DEST_NOT_SUPPORTED = 2; + + /** + * Handover failure reason returned via {@link #onHandoverFailed(Call, int)} when there + * are some permission errors associated with APIs doing handover. + */ + public static final int HANDOVER_FAILURE_DEST_INVALID_PERM = 3; + + /** + * Handover failure reason returned via {@link #onHandoverFailed(Call, int)} when user + * rejects handover. + */ + public static final int HANDOVER_FAILURE_DEST_USER_REJECTED = 4; + + + /** * Invoked when the state of this {@code Call} has changed. See {@link #getState()}. * * @param call The {@code Call} invoking this method. @@ -989,6 +1022,21 @@ public final class Call { * {@link android.telecom.Connection.RttModifyStatus#SESSION_MODIFY_REQUEST_SUCCESS}. */ public void onRttInitiationFailure(Call call, int reason) {} + + /** + * Invoked when Call handover from one {@link PhoneAccount} to other {@link PhoneAccount} + * has completed successfully. + * @param call The call which had initiated handover. + */ + public void onHandoverComplete(Call call) {} + + /** + * Invoked when Call handover from one {@link PhoneAccount} to other {@link PhoneAccount} + * has failed. + * @param call The call which had initiated handover. + * @param failureReason Error reason for failure + */ + public void onHandoverFailed(Call call, @HandoverFailureErrors int failureReason) {} } /** @@ -1367,6 +1415,24 @@ public final class Call { } /** + * Initiates a handover of this {@link Call} to the {@link ConnectionService} identified + * by {@code toHandle}. The videoState specified indicates the desired video state after the + * handover. + * <p> + * A handover request is initiated by the user from one app to indicate a desire + * to handover a call to another. + * + * @param toHandle {@link PhoneAccountHandle} of the {@link ConnectionService} to handover + * this call to. + * @param videoState Indicates the video state desired after the handover. + * @param extras Bundle containing extra information to be passed to the + * {@link ConnectionService} + */ + public void handoverTo(PhoneAccountHandle toHandle, int videoState, Bundle extras) { + mInCallAdapter.handoverTo(mTelecomCallId, toHandle, videoState, extras); + } + + /** * Terminate the RTT session on this call. The resulting state change will be notified via * the {@link Callback#onRttStatusChanged(Call, boolean, RttCall)} callback. */ diff --git a/android/telecom/CallAudioState.java b/android/telecom/CallAudioState.java index f601d8b5..4b827d2e 100644 --- a/android/telecom/CallAudioState.java +++ b/android/telecom/CallAudioState.java @@ -16,16 +16,35 @@ package android.telecom; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.bluetooth.BluetoothDevice; import android.os.Parcel; import android.os.Parcelable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.Locale; +import java.util.Objects; +import java.util.stream.Collectors; /** * Encapsulates the telecom audio state, including the current audio routing, supported audio * routing and mute. */ public final class CallAudioState implements Parcelable { + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value={ROUTE_EARPIECE, ROUTE_BLUETOOTH, ROUTE_WIRED_HEADSET, ROUTE_SPEAKER}, + flag=true) + public @interface CallAudioRoute {} + /** Direct the audio stream through the device's earpiece. */ public static final int ROUTE_EARPIECE = 0x00000001; @@ -55,6 +74,8 @@ public final class CallAudioState implements Parcelable { private final boolean isMuted; private final int route; private final int supportedRouteMask; + private final BluetoothDevice activeBluetoothDevice; + private final Collection<BluetoothDevice> supportedBluetoothDevices; /** * Constructor for a {@link CallAudioState} object. @@ -73,10 +94,21 @@ public final class CallAudioState implements Parcelable { * {@link #ROUTE_WIRED_HEADSET} * {@link #ROUTE_SPEAKER} */ - public CallAudioState(boolean muted, int route, int supportedRouteMask) { - this.isMuted = muted; + public CallAudioState(boolean muted, @CallAudioRoute int route, + @CallAudioRoute int supportedRouteMask) { + this(muted, route, supportedRouteMask, null, Collections.emptyList()); + } + + /** @hide */ + public CallAudioState(boolean isMuted, @CallAudioRoute int route, + @CallAudioRoute int supportedRouteMask, + @Nullable BluetoothDevice activeBluetoothDevice, + @NonNull Collection<BluetoothDevice> supportedBluetoothDevices) { + this.isMuted = isMuted; this.route = route; this.supportedRouteMask = supportedRouteMask; + this.activeBluetoothDevice = activeBluetoothDevice; + this.supportedBluetoothDevices = supportedBluetoothDevices; } /** @hide */ @@ -84,6 +116,8 @@ public final class CallAudioState implements Parcelable { isMuted = state.isMuted(); route = state.getRoute(); supportedRouteMask = state.getSupportedRouteMask(); + activeBluetoothDevice = state.activeBluetoothDevice; + supportedBluetoothDevices = state.getSupportedBluetoothDevices(); } /** @hide */ @@ -92,6 +126,8 @@ public final class CallAudioState implements Parcelable { isMuted = state.isMuted(); route = state.getRoute(); supportedRouteMask = state.getSupportedRouteMask(); + activeBluetoothDevice = null; + supportedBluetoothDevices = Collections.emptyList(); } @Override @@ -103,17 +139,32 @@ public final class CallAudioState implements Parcelable { return false; } CallAudioState state = (CallAudioState) obj; - return isMuted() == state.isMuted() && getRoute() == state.getRoute() && - getSupportedRouteMask() == state.getSupportedRouteMask(); + if (supportedBluetoothDevices.size() != state.supportedBluetoothDevices.size()) { + return false; + } + for (BluetoothDevice device : supportedBluetoothDevices) { + if (!state.supportedBluetoothDevices.contains(device)) { + return false; + } + } + return Objects.equals(activeBluetoothDevice, state.activeBluetoothDevice) && isMuted() == + state.isMuted() && getRoute() == state.getRoute() && getSupportedRouteMask() == + state.getSupportedRouteMask(); } @Override public String toString() { + String bluetoothDeviceList = supportedBluetoothDevices.stream() + .map(BluetoothDevice::getAddress).collect(Collectors.joining(", ")); + return String.format(Locale.US, - "[AudioState isMuted: %b, route: %s, supportedRouteMask: %s]", + "[AudioState isMuted: %b, route: %s, supportedRouteMask: %s, " + + "activeBluetoothDevice: [%s], supportedBluetoothDevices: [%s]]", isMuted, audioRouteToString(route), - audioRouteToString(supportedRouteMask)); + audioRouteToString(supportedRouteMask), + activeBluetoothDevice, + bluetoothDeviceList); } /** @@ -126,6 +177,7 @@ public final class CallAudioState implements Parcelable { /** * @return The current audio route being used. */ + @CallAudioRoute public int getRoute() { return route; } @@ -133,11 +185,27 @@ public final class CallAudioState implements Parcelable { /** * @return Bit mask of all routes supported by this call. */ + @CallAudioRoute public int getSupportedRouteMask() { return supportedRouteMask; } /** + * @return The {@link BluetoothDevice} through which audio is being routed. + * Will not be {@code null} if {@link #getRoute()} returns {@link #ROUTE_BLUETOOTH}. + */ + public BluetoothDevice getActiveBluetoothDevice() { + return activeBluetoothDevice; + } + + /** + * @return {@link List} of {@link BluetoothDevice}s that can be used for this call. + */ + public Collection<BluetoothDevice> getSupportedBluetoothDevices() { + return supportedBluetoothDevices; + } + + /** * Converts the provided audio route into a human readable string representation. * * @param route to convert into a string. @@ -177,7 +245,13 @@ public final class CallAudioState implements Parcelable { boolean isMuted = source.readByte() == 0 ? false : true; int route = source.readInt(); int supportedRouteMask = source.readInt(); - return new CallAudioState(isMuted, route, supportedRouteMask); + BluetoothDevice activeBluetoothDevice = source.readParcelable( + ClassLoader.getSystemClassLoader()); + List<BluetoothDevice> supportedBluetoothDevices = new ArrayList<>(); + source.readParcelableList(supportedBluetoothDevices, + ClassLoader.getSystemClassLoader()); + return new CallAudioState(isMuted, route, + supportedRouteMask, activeBluetoothDevice, supportedBluetoothDevices); } @Override @@ -202,6 +276,8 @@ public final class CallAudioState implements Parcelable { destination.writeByte((byte) (isMuted ? 1 : 0)); destination.writeInt(route); destination.writeInt(supportedRouteMask); + destination.writeParcelable(activeBluetoothDevice, 0); + destination.writeParcelableList(new ArrayList<>(supportedBluetoothDevices), 0); } private static void listAppend(StringBuffer buffer, String str) { diff --git a/android/telecom/Connection.java b/android/telecom/Connection.java index 8ba934cc..2bb1c4ed 100644 --- a/android/telecom/Connection.java +++ b/android/telecom/Connection.java @@ -25,6 +25,7 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; import android.app.Notification; +import android.bluetooth.BluetoothDevice; import android.content.Intent; import android.hardware.camera2.CameraManager; import android.net.Uri; @@ -819,7 +820,7 @@ public abstract class Connection extends Conferenceable { public void onConnectionEvent(Connection c, String event, Bundle extras) {} /** @hide */ public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {} - public void onAudioRouteChanged(Connection c, int audioRoute) {} + public void onAudioRouteChanged(Connection c, int audioRoute, String bluetoothAddress) {} public void onRttInitiationSuccess(Connection c) {} public void onRttInitiationFailure(Connection c, int reason) {} public void onRttSessionRemotelyTerminated(Connection c) {} @@ -1683,6 +1684,8 @@ public abstract class Connection extends Conferenceable { // The internal telecom call ID associated with this connection. private String mTelecomCallId; + // The PhoneAccountHandle associated with this connection. + private PhoneAccountHandle mPhoneAccountHandle; private int mState = STATE_NEW; private CallAudioState mCallAudioState; private Uri mAddress; @@ -2576,7 +2579,29 @@ public abstract class Connection extends Conferenceable { */ public final void setAudioRoute(int route) { for (Listener l : mListeners) { - l.onAudioRouteChanged(this, route); + l.onAudioRouteChanged(this, route, null); + } + } + + /** + * + * Request audio routing to a specific bluetooth device. Calling this method may result in + * the device routing audio to a different bluetooth device than the one specified if the + * bluetooth stack is unable to route audio to the requested device. + * A list of available devices can be obtained via + * {@link CallAudioState#getSupportedBluetoothDevices()} + * + * <p> + * Used by self-managed {@link ConnectionService}s which wish to use bluetooth audio for a + * self-managed {@link Connection} (see {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.) + * <p> + * See also {@link InCallService#requestBluetoothAudio(String)} + * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by + * {@link BluetoothDevice#getAddress()}. + */ + public void requestBluetoothAudio(@NonNull String bluetoothAddress) { + for (Listener l : mListeners) { + l.onAudioRouteChanged(this, CallAudioState.ROUTE_BLUETOOTH, bluetoothAddress); } } @@ -3076,6 +3101,27 @@ public abstract class Connection extends Conferenceable { } /** + * Sets the {@link PhoneAccountHandle} associated with this connection. + * + * @hide + */ + public void setPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) { + if (mPhoneAccountHandle != phoneAccountHandle) { + mPhoneAccountHandle = phoneAccountHandle; + notifyPhoneAccountChanged(phoneAccountHandle); + } + } + + /** + * Returns the {@link PhoneAccountHandle} associated with this connection. + * + * @hide + */ + public PhoneAccountHandle getPhoneAccountHandle() { + return mPhoneAccountHandle; + } + + /** * Sends an event associated with this {@code Connection} with associated event extras to the * {@link InCallService}. * <p> diff --git a/android/telecom/ConnectionService.java b/android/telecom/ConnectionService.java index a81fba95..7e833066 100644 --- a/android/telecom/ConnectionService.java +++ b/android/telecom/ConnectionService.java @@ -1294,10 +1294,10 @@ public abstract class ConnectionService extends Service { } @Override - public void onAudioRouteChanged(Connection c, int audioRoute) { + public void onAudioRouteChanged(Connection c, int audioRoute, String bluetoothAddress) { String id = mIdByConnection.get(c); if (id != null) { - mAdapter.setAudioRoute(id, audioRoute); + mAdapter.setAudioRoute(id, audioRoute, bluetoothAddress); } } @@ -1382,7 +1382,7 @@ public abstract class ConnectionService extends Service { connection.setTelecomCallId(callId); if (connection.getState() != Connection.STATE_DISCONNECTED) { - addConnection(callId, connection); + addConnection(request.getAccountHandle(), callId, connection); } Uri address = connection.getAddress(); @@ -1846,6 +1846,7 @@ public abstract class ConnectionService extends Service { mAdapter.setIsConferenced(connectionId, id); } } + onConferenceAdded(conference); } } @@ -2033,6 +2034,43 @@ public abstract class ConnectionService extends Service { } /** + * Called by Telecom on the initiating side of the handover to create an instance of a + * handover connection. + * @param fromPhoneAccountHandle {@link PhoneAccountHandle} associated with the + * ConnectionService which needs to handover the call. + * @param request Details about the call which needs to be handover. + * @return Connection object corresponding to the handover call. + */ + public Connection onCreateOutgoingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle, + ConnectionRequest request) { + return null; + } + + /** + * Called by Telecom on the receiving side of the handover to request the + * {@link ConnectionService} to create an instance of a handover connection. + * @param fromPhoneAccountHandle {@link PhoneAccountHandle} associated with the + * ConnectionService which needs to handover the call. + * @param request Details about the call which needs to be handover. + * @return {@link Connection} object corresponding to the handover call. + */ + public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle, + ConnectionRequest request) { + return null; + } + + /** + * Called by Telecom in response to a {@code TelecomManager#acceptHandover()} + * invocation which failed. + * @param request Details about the call which needs to be handover. + * @param error Reason for handover failure as defined in + * {@link android.telecom.Call.Callback#HANDOVER_FAILURE_DEST_INVALID_PERM} + */ + public void onHandoverFailed(ConnectionRequest request, int error) { + return; + } + + /** * Create a {@code Connection} for a new unknown call. An unknown call is a call originating * from the ConnectionService that was neither a user-initiated outgoing call, nor an incoming * call created using @@ -2056,6 +2094,30 @@ public abstract class ConnectionService extends Service { public void onConference(Connection connection1, Connection connection2) {} /** + * Called when a connection is added. + * @hide + */ + public void onConnectionAdded(Connection connection) {} + + /** + * Called when a connection is removed. + * @hide + */ + public void onConnectionRemoved(Connection connection) {} + + /** + * Called when a conference is added. + * @hide + */ + public void onConferenceAdded(Conference conference) {} + + /** + * Called when a conference is removed. + * @hide + */ + public void onConferenceRemoved(Conference conference) {} + + /** * Indicates that a remote conference has been created for existing {@link RemoteConnection}s. * When this method is invoked, this {@link ConnectionService} should create its own * representation of the conference call and send it to telecom using {@link #addConference}. @@ -2122,16 +2184,18 @@ public abstract class ConnectionService extends Service { // prefix for a unique incremental call ID. id = handle.getComponentName().getClassName() + "@" + getNextCallId(); } - addConnection(id, connection); + addConnection(handle, id, connection); return id; } - private void addConnection(String callId, Connection connection) { + private void addConnection(PhoneAccountHandle handle, String callId, Connection connection) { connection.setTelecomCallId(callId); mConnectionById.put(callId, connection); mIdByConnection.put(connection, callId); connection.addConnectionListener(mConnectionListener); connection.setConnectionService(this); + connection.setPhoneAccountHandle(handle); + onConnectionAdded(connection); } /** {@hide} */ @@ -2143,6 +2207,7 @@ public abstract class ConnectionService extends Service { mConnectionById.remove(id); mIdByConnection.remove(connection); mAdapter.removeCall(id); + onConnectionRemoved(connection); } } @@ -2179,6 +2244,8 @@ public abstract class ConnectionService extends Service { mConferenceById.remove(id); mIdByConference.remove(conference); mAdapter.removeCall(id); + + onConferenceRemoved(conference); } } diff --git a/android/telecom/ConnectionServiceAdapter.java b/android/telecom/ConnectionServiceAdapter.java index 111fcc78..92a9dc23 100644 --- a/android/telecom/ConnectionServiceAdapter.java +++ b/android/telecom/ConnectionServiceAdapter.java @@ -520,11 +520,14 @@ final class ConnectionServiceAdapter implements DeathRecipient { * @param callId The unique ID of the call. * @param audioRoute The new audio route (see {@code CallAudioState#ROUTE_*}). */ - void setAudioRoute(String callId, int audioRoute) { - Log.v(this, "setAudioRoute: %s %s", callId, CallAudioState.audioRouteToString(audioRoute)); + void setAudioRoute(String callId, int audioRoute, String bluetoothAddress) { + Log.v(this, "setAudioRoute: %s %s %s", callId, + CallAudioState.audioRouteToString(audioRoute), + bluetoothAddress); for (IConnectionServiceAdapter adapter : mAdapters) { try { - adapter.setAudioRoute(callId, audioRoute, Log.getExternalSession()); + adapter.setAudioRoute(callId, audioRoute, + bluetoothAddress, Log.getExternalSession()); } catch (RemoteException ignored) { } } diff --git a/android/telecom/ConnectionServiceAdapterServant.java b/android/telecom/ConnectionServiceAdapterServant.java index b1617f4d..3fbdeb1e 100644 --- a/android/telecom/ConnectionServiceAdapterServant.java +++ b/android/telecom/ConnectionServiceAdapterServant.java @@ -298,8 +298,8 @@ final class ConnectionServiceAdapterServant { case MSG_SET_AUDIO_ROUTE: { SomeArgs args = (SomeArgs) msg.obj; try { - mDelegate.setAudioRoute((String) args.arg1, args.argi1, - (Session.Info) args.arg2); + mDelegate.setAudioRoute((String) args.arg1, args.argi1, (String) args.arg2, + (Session.Info) args.arg3); } finally { args.recycle(); } @@ -548,12 +548,12 @@ final class ConnectionServiceAdapterServant { @Override public final void setAudioRoute(String connectionId, int audioRoute, - Session.Info sessionInfo) { - + String bluetoothAddress, Session.Info sessionInfo) { SomeArgs args = SomeArgs.obtain(); args.arg1 = connectionId; args.argi1 = audioRoute; - args.arg2 = sessionInfo; + args.arg2 = bluetoothAddress; + args.arg3 = sessionInfo; mHandler.obtainMessage(MSG_SET_AUDIO_ROUTE, args).sendToTarget(); } diff --git a/android/telecom/InCallAdapter.java b/android/telecom/InCallAdapter.java index 9559a28c..4bc2a9b1 100644 --- a/android/telecom/InCallAdapter.java +++ b/android/telecom/InCallAdapter.java @@ -16,6 +16,7 @@ package android.telecom; +import android.bluetooth.BluetoothDevice; import android.os.Bundle; import android.os.RemoteException; @@ -128,7 +129,22 @@ public final class InCallAdapter { */ public void setAudioRoute(int route) { try { - mAdapter.setAudioRoute(route); + mAdapter.setAudioRoute(route, null); + } catch (RemoteException e) { + } + } + + /** + * Request audio routing to a specific bluetooth device. Calling this method may result in + * the device routing audio to a different bluetooth device than the one specified. A list of + * available devices can be obtained via {@link CallAudioState#getSupportedBluetoothDevices()} + * + * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by + * {@link BluetoothDevice#getAddress()}, or {@code null} if no device is preferred. + */ + public void requestBluetoothAudio(String bluetoothAddress) { + try { + mAdapter.setAudioRoute(CallAudioState.ROUTE_BLUETOOTH, bluetoothAddress); } catch (RemoteException e) { } } @@ -419,4 +435,21 @@ public final class InCallAdapter { } catch (RemoteException ignored) { } } + + + /** + * Initiates a handover of this {@link Call} to the {@link ConnectionService} identified + * by destAcct. + * @param callId The callId of the Call which calls this function. + * @param destAcct ConnectionService to which the call should be handed over. + * @param videoState The video state desired after the handover. + * @param extras Extra information to be passed to ConnectionService + */ + public void handoverTo(String callId, PhoneAccountHandle destAcct, int videoState, + Bundle extras) { + try { + mAdapter.handoverTo(callId, destAcct, videoState, extras); + } catch (RemoteException ignored) { + } + } } diff --git a/android/telecom/InCallService.java b/android/telecom/InCallService.java index e384d469..d558bbae 100644 --- a/android/telecom/InCallService.java +++ b/android/telecom/InCallService.java @@ -16,9 +16,11 @@ package android.telecom; +import android.annotation.NonNull; import android.annotation.SdkConstant; import android.annotation.SystemApi; import android.app.Service; +import android.bluetooth.BluetoothDevice; import android.content.Intent; import android.hardware.camera2.CameraManager; import android.net.Uri; @@ -377,6 +379,22 @@ public abstract class InCallService extends Service { } /** + * Request audio routing to a specific bluetooth device. Calling this method may result in + * the device routing audio to a different bluetooth device than the one specified if the + * bluetooth stack is unable to route audio to the requested device. + * A list of available devices can be obtained via + * {@link CallAudioState#getSupportedBluetoothDevices()} + * + * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by + * {@link BluetoothDevice#getAddress()}. + */ + public final void requestBluetoothAudio(@NonNull String bluetoothAddress) { + if (mPhone != null) { + mPhone.requestBluetoothAudio(bluetoothAddress); + } + } + + /** * Invoked when the {@code Phone} has been created. This is a signal to the in-call experience * to start displaying in-call information to the user. Each instance of {@code InCallService} * will have only one {@code Phone}, and this method will be called exactly once in the lifetime diff --git a/android/telecom/Phone.java b/android/telecom/Phone.java index 066f6c26..421b1a4b 100644 --- a/android/telecom/Phone.java +++ b/android/telecom/Phone.java @@ -17,7 +17,9 @@ package android.telecom; import android.annotation.SystemApi; +import android.bluetooth.BluetoothDevice; import android.os.Bundle; +import android.os.RemoteException; import android.util.ArrayMap; import java.util.Collections; @@ -295,6 +297,18 @@ public final class Phone { } /** + * Request audio routing to a specific bluetooth device. Calling this method may result in + * the device routing audio to a different bluetooth device than the one specified. A list of + * available devices can be obtained via {@link CallAudioState#getSupportedBluetoothDevices()} + * + * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by + * {@link BluetoothDevice#getAddress()}, or {@code null} if no device is preferred. + */ + public void requestBluetoothAudio(String bluetoothAddress) { + mInCallAdapter.requestBluetoothAudio(bluetoothAddress); + } + + /** * Turns the proximity sensor on. When this request is made, the proximity sensor will * become active, and the touch screen and display will be turned off when the user's face * is detected to be in close proximity to the screen. This operation is a no-op on devices diff --git a/android/telecom/PhoneAccount.java b/android/telecom/PhoneAccount.java index 691e7cf1..74b94650 100644 --- a/android/telecom/PhoneAccount.java +++ b/android/telecom/PhoneAccount.java @@ -86,13 +86,11 @@ public final class PhoneAccount implements Parcelable { /** * Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which * indicates whether this {@link PhoneAccount} is capable of supporting a request to handover a - * connection (see {@link android.telecom.Call#EVENT_REQUEST_HANDOVER}) to this - * {@link PhoneAccount} from a {@link PhoneAccount} specifying - * {@link #EXTRA_SUPPORTS_HANDOVER_FROM}. + * connection (see {@code android.telecom.Call#handoverTo()}) to this {@link PhoneAccount} from + * a {@link PhoneAccount} specifying {@link #EXTRA_SUPPORTS_HANDOVER_FROM}. * <p> * A handover request is initiated by the user from the default dialer app to indicate a desire * to handover a call from one {@link PhoneAccount}/{@link ConnectionService} to another. - * @hide */ public static final String EXTRA_SUPPORTS_HANDOVER_TO = "android.telecom.extra.SUPPORTS_HANDOVER_TO"; @@ -113,12 +111,11 @@ public final class PhoneAccount implements Parcelable { * Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which * indicates whether this {@link PhoneAccount} is capable of supporting a request to handover a * connection from this {@link PhoneAccount} to another {@link PhoneAccount}. - * (see {@link android.telecom.Call#EVENT_REQUEST_HANDOVER}) which specifies + * (see {@code android.telecom.Call#handoverTo()}) which specifies * {@link #EXTRA_SUPPORTS_HANDOVER_TO}. * <p> * A handover request is initiated by the user from the default dialer app to indicate a desire * to handover a call from one {@link PhoneAccount}/{@link ConnectionService} to another. - * @hide */ public static final String EXTRA_SUPPORTS_HANDOVER_FROM = "android.telecom.extra.SUPPORTS_HANDOVER_FROM"; @@ -132,7 +129,6 @@ public final class PhoneAccount implements Parcelable { * <p> * By default, Self-Managed {@link PhoneAccount}s do not log their calls to the call log. * Setting this extra to {@code true} provides a means for them to log their calls. - * @hide */ public static final String EXTRA_LOG_SELF_MANAGED_CALLS = "android.telecom.extra.LOG_SELF_MANAGED_CALLS"; diff --git a/android/telecom/RemoteConnectionService.java b/android/telecom/RemoteConnectionService.java index 2cc43143..85906ad1 100644 --- a/android/telecom/RemoteConnectionService.java +++ b/android/telecom/RemoteConnectionService.java @@ -398,7 +398,8 @@ final class RemoteConnectionService { } @Override - public void setAudioRoute(String callId, int audioRoute, Session.Info sessionInfo) { + public void setAudioRoute(String callId, int audioRoute, String bluetoothAddress, + Session.Info sessionInfo) { if (hasConnection(callId)) { // TODO(3pcalls): handle this for remote connections. // Likely we don't want to do anything since it doesn't make sense for self-managed diff --git a/android/telecom/TelecomManager.java b/android/telecom/TelecomManager.java index 9e52c71b..da32e0be 100644 --- a/android/telecom/TelecomManager.java +++ b/android/telecom/TelecomManager.java @@ -1750,6 +1750,41 @@ public class TelecomManager { return false; } + /** + * Called from the recipient side of a handover to indicate a desire to accept the handover + * of an ongoing call to another {@link ConnectionService} identified by + * {@link PhoneAccountHandle} destAcct. For managed {@link ConnectionService}s, the specified + * {@link PhoneAccountHandle} must have been registered with {@link #registerPhoneAccount} and + * the user must have enabled the corresponding {@link PhoneAccount}. This can be checked using + * {@link #getPhoneAccount}. Self-managed {@link ConnectionService}s must have + * {@link android.Manifest.permission#MANAGE_OWN_CALLS} to handover a call to it. + * <p> + * Once invoked, this method will cause the system to bind to the {@link ConnectionService} + * associated with the {@link PhoneAccountHandle} destAcct and call + * (See {@link ConnectionService#onCreateIncomingHandoverConnection}). + * <p> + * For a managed {@link ConnectionService}, a {@link SecurityException} will be thrown if either + * the {@link PhoneAccountHandle} destAcct does not correspond to a registered + * {@link PhoneAccount} or the associated {@link PhoneAccount} is not currently enabled by the + * user. + * <p> + * For a self-managed {@link ConnectionService}, a {@link SecurityException} will be thrown if + * the calling app does not have {@link android.Manifest.permission#MANAGE_OWN_CALLS}. + * + * @param srcAddr The {@link android.net.Uri} of the ongoing call to handover to the caller’s + * {@link ConnectionService}. + * @param videoState Video state after the handover. + * @param destAcct The {@link PhoneAccountHandle} registered to the calling package. + */ + public void acceptHandover(Uri srcAddr, int videoState, PhoneAccountHandle destAcct) { + try { + if (isServiceConnected()) { + getTelecomService().acceptHandover(srcAddr, videoState, destAcct); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException acceptHandover: " + e); + } + } private ITelecomService getTelecomService() { if (mTelecomServiceOverride != null) { |