summaryrefslogtreecommitdiffstats
path: root/telecomm/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'telecomm/java/android')
-rw-r--r--telecomm/java/android/telecom/AudioState.java29
-rw-r--r--telecomm/java/android/telecom/AuthenticatorService.java101
-rw-r--r--telecomm/java/android/telecom/Call.java469
-rw-r--r--telecomm/java/android/telecom/CallAudioState.aidl (renamed from telecomm/java/android/telecom/CameraCapabilities.aidl)8
-rw-r--r--telecomm/java/android/telecom/CallAudioState.java209
-rw-r--r--telecomm/java/android/telecom/CallProperties.java26
-rw-r--r--telecomm/java/android/telecom/CallState.java139
-rw-r--r--telecomm/java/android/telecom/CallbackRecord.java44
-rw-r--r--telecomm/java/android/telecom/CameraCapabilities.java144
-rw-r--r--telecomm/java/android/telecom/Conference.java206
-rw-r--r--telecomm/java/android/telecom/Conferenceable.java (renamed from telecomm/java/android/telecom/IConferenceable.java)13
-rw-r--r--telecomm/java/android/telecom/Connection.java743
-rw-r--r--telecomm/java/android/telecom/ConnectionRequest.java15
-rw-r--r--telecomm/java/android/telecom/ConnectionService.java110
-rw-r--r--telecomm/java/android/telecom/ConnectionServiceAdapter.java55
-rw-r--r--telecomm/java/android/telecom/ConnectionServiceAdapterServant.java35
-rw-r--r--telecomm/java/android/telecom/DefaultDialerManager.java228
-rw-r--r--telecomm/java/android/telecom/DisconnectCause.java16
-rw-r--r--telecomm/java/android/telecom/GatewayInfo.java5
-rw-r--r--telecomm/java/android/telecom/InCallAdapter.java2
-rw-r--r--telecomm/java/android/telecom/InCallService.java447
-rw-r--r--telecomm/java/android/telecom/ParcelableCall.java43
-rw-r--r--telecomm/java/android/telecom/ParcelableConference.java60
-rw-r--r--telecomm/java/android/telecom/ParcelableConnection.java21
-rw-r--r--telecomm/java/android/telecom/Phone.java66
-rw-r--r--telecomm/java/android/telecom/PhoneAccount.java280
-rw-r--r--telecomm/java/android/telecom/PhoneAccountHandle.java23
-rw-r--r--telecomm/java/android/telecom/RemoteConference.java346
-rw-r--r--telecomm/java/android/telecom/RemoteConnection.java550
-rw-r--r--telecomm/java/android/telecom/RemoteConnectionService.java34
-rw-r--r--telecomm/java/android/telecom/StatusHints.java75
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java443
-rw-r--r--telecomm/java/android/telecom/VideoCallImpl.java131
-rw-r--r--telecomm/java/android/telecom/VideoCallbackServant.java21
-rw-r--r--telecomm/java/android/telecom/VideoProfile.aidl1
-rw-r--r--telecomm/java/android/telecom/VideoProfile.java325
-rw-r--r--telecomm/java/android/telecom/Voicemail.java303
37 files changed, 4318 insertions, 1448 deletions
diff --git a/telecomm/java/android/telecom/AudioState.java b/telecomm/java/android/telecom/AudioState.java
index 9c03319..33013ac 100644
--- a/telecomm/java/android/telecom/AudioState.java
+++ b/telecomm/java/android/telecom/AudioState.java
@@ -25,10 +25,12 @@ import java.util.Locale;
/**
* Encapsulates the telecom audio state, including the current audio routing, supported audio
* routing and mute.
+ * @deprecated - use {@link CallAudioState} instead.
* @hide
*/
+@Deprecated
@SystemApi
-public final class AudioState implements Parcelable {
+public class AudioState implements Parcelable {
/** Direct the audio stream through the device's earpiece. */
public static final int ROUTE_EARPIECE = 0x00000001;
@@ -47,21 +49,13 @@ public final class AudioState implements Parcelable {
*/
public static final int ROUTE_WIRED_OR_EARPIECE = ROUTE_EARPIECE | ROUTE_WIRED_HEADSET;
- /** Bit mask of all possible audio routes.
- *
- * @hide
- */
- public static final int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET |
+ /** Bit mask of all possible audio routes. */
+ private static final int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET |
ROUTE_SPEAKER;
- /** Note: Deprecated, please do not use if possible. */
- @SystemApi public final boolean isMuted;
-
- /** Note: Deprecated, please do not use if possible. */
- @SystemApi public final int route;
-
- /** Note: Deprecated, please do not use if possible. */
- @SystemApi public final int supportedRouteMask;
+ private final boolean isMuted;
+ private final int route;
+ private final int supportedRouteMask;
public AudioState(boolean muted, int route, int supportedRouteMask) {
this.isMuted = muted;
@@ -75,6 +69,12 @@ public final class AudioState implements Parcelable {
supportedRouteMask = state.getSupportedRouteMask();
}
+ public AudioState(CallAudioState state) {
+ isMuted = state.isMuted();
+ route = state.getRoute();
+ supportedRouteMask = state.getSupportedRouteMask();
+ }
+
@Override
public boolean equals(Object obj) {
if (obj == null) {
@@ -97,7 +97,6 @@ public final class AudioState implements Parcelable {
audioRouteToString(supportedRouteMask));
}
- /** @hide */
public static String audioRouteToString(int route) {
if (route == 0 || (route & ~ROUTE_ALL) != 0x0) {
return "UNKNOWN";
diff --git a/telecomm/java/android/telecom/AuthenticatorService.java b/telecomm/java/android/telecom/AuthenticatorService.java
new file mode 100644
index 0000000..1e43c71
--- /dev/null
+++ b/telecomm/java/android/telecom/AuthenticatorService.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telecom;
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.NetworkErrorException;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+
+/**
+ * A generic stub account authenticator service often used for sync adapters that do not directly
+ * involve accounts.
+ *
+ * @hide
+ */
+public class AuthenticatorService extends Service {
+ private static Authenticator mAuthenticator;
+
+ @Override
+ public void onCreate() {
+ mAuthenticator = new Authenticator(this);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mAuthenticator.getIBinder();
+ }
+
+ /**
+ * Stub account authenticator. All methods either return null or throw an exception.
+ */
+ public class Authenticator extends AbstractAccountAuthenticator {
+ public Authenticator(Context context) {
+ super(context);
+ }
+
+ @Override
+ public Bundle editProperties(AccountAuthenticatorResponse accountAuthenticatorResponse,
+ String s) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Bundle addAccount(AccountAuthenticatorResponse accountAuthenticatorResponse,
+ String s, String s2, String[] strings, Bundle bundle)
+ throws NetworkErrorException {
+ return null;
+ }
+
+ @Override
+ public Bundle confirmCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse,
+ Account account, Bundle bundle)
+ throws NetworkErrorException {
+ return null;
+ }
+
+ @Override
+ public Bundle getAuthToken(AccountAuthenticatorResponse accountAuthenticatorResponse,
+ Account account, String s, Bundle bundle)
+ throws NetworkErrorException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getAuthTokenLabel(String s) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Bundle updateCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse,
+ Account account, String s, Bundle bundle)
+ throws NetworkErrorException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Bundle hasFeatures(AccountAuthenticatorResponse accountAuthenticatorResponse,
+ Account account, String[] strings)
+ throws NetworkErrorException {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index d8a14ef..4569549 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -19,10 +19,12 @@ package android.telecom;
import android.annotation.SystemApi;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import java.lang.String;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -30,10 +32,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
/**
* Represents an ongoing phone call that the in-call app should present to the user.
- *
- * {@hide}
*/
-@SystemApi
public final class Call {
/**
* The state of a {@code Call} when newly created.
@@ -69,9 +68,18 @@ public final class Call {
public static final int STATE_DISCONNECTED = 7;
/**
- * The state of an outgoing {@code Call}, but waiting for user input before proceeding.
+ * The state of an outgoing {@code Call} when waiting on user to select a
+ * {@link PhoneAccount} through which to place the call.
+ */
+ public static final int STATE_SELECT_PHONE_ACCOUNT = 8;
+
+ /**
+ * @hide
+ * @deprecated use STATE_SELECT_PHONE_ACCOUNT.
*/
- public static final int STATE_PRE_DIAL_WAIT = 8;
+ @Deprecated
+ @SystemApi
+ public static final int STATE_PRE_DIAL_WAIT = STATE_SELECT_PHONE_ACCOUNT;
/**
* The initial state of an outgoing {@code Call}.
@@ -91,8 +99,6 @@ public final class Call {
* The key to retrieve the optional {@code PhoneAccount}s Telecom can bundle with its Call
* extras. Used to pass the phone accounts to display on the front end to the user in order to
* select phone accounts to (for example) place a call.
- *
- * @hide
*/
public static final String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
@@ -126,7 +132,7 @@ public final class Call {
/**
* @hide
*/
- public static final int CAPABILITY_UNUSED = 0x00000010;
+ public static final int CAPABILITY_UNUSED_1 = 0x00000010;
/** Call supports responding via text option. */
public static final int CAPABILITY_RESPOND_VIA_TEXT = 0x00000020;
@@ -141,28 +147,36 @@ public final class Call {
public static final int CAPABILITY_MANAGE_CONFERENCE = 0x00000080;
/**
- * Local device supports video telephony.
- * @hide
+ * Local device supports receiving video.
*/
- public static final int CAPABILITY_SUPPORTS_VT_LOCAL = 0x00000100;
+ public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 0x00000100;
/**
- * Remote device supports video telephony.
- * @hide
+ * Local device supports transmitting video.
*/
- public static final int CAPABILITY_SUPPORTS_VT_REMOTE = 0x00000200;
+ public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 0x00000200;
/**
- * Call is using high definition audio.
- * @hide
+ * Local device supports bidirectional video calling.
*/
- public static final int CAPABILITY_HIGH_DEF_AUDIO = 0x00000400;
+ public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL =
+ CAPABILITY_SUPPORTS_VT_LOCAL_RX | CAPABILITY_SUPPORTS_VT_LOCAL_TX;
/**
- * Call is using voice over WIFI.
- * @hide
+ * Remote device supports receiving video.
*/
- public static final int CAPABILITY_VoWIFI = 0x00000800;
+ public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 0x00000400;
+
+ /**
+ * Remote device supports transmitting video.
+ */
+ public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 0x00000800;
+
+ /**
+ * Remote device supports bidirectional video calling.
+ */
+ public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL =
+ CAPABILITY_SUPPORTS_VT_REMOTE_RX | CAPABILITY_SUPPORTS_VT_REMOTE_TX;
/**
* Call is able to be separated from its parent {@code Conference}, if any.
@@ -173,20 +187,58 @@ public final class Call {
* Call is able to be individually disconnected when in a {@code Conference}.
*/
public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 0x00002000;
-
+
/**
- * Whether the call is a generic conference, where we do not know the precise state of
- * participants in the conference (eg. on CDMA).
- *
+ * Speed up audio setup for MT call.
* @hide
*/
- public static final int CAPABILITY_GENERIC_CONFERENCE = 0x00004000;
+ public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00040000;
/**
- * Speed up audio setup for MT call.
+ * Call can be upgraded to a video call.
* @hide
*/
- public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00008000;
+ public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000;
+
+ /**
+ * For video calls, indicates whether the outgoing video for the call can be paused using
+ * the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
+ */
+ public static final int CAPABILITY_CAN_PAUSE_VIDEO = 0x00100000;
+
+ //******************************************************************************************
+ // Next CAPABILITY value: 0x00004000
+ //******************************************************************************************
+
+ /**
+ * Whether the call is currently a conference.
+ */
+ public static final int PROPERTY_CONFERENCE = 0x00000001;
+
+ /**
+ * Whether the call is a generic conference, where we do not know the precise state of
+ * participants in the conference (eg. on CDMA).
+ */
+ public static final int PROPERTY_GENERIC_CONFERENCE = 0x00000002;
+
+ /**
+ * Whether the call is made while the device is in emergency callback mode.
+ */
+ public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 0x00000004;
+
+ /**
+ * Connection is using WIFI.
+ */
+ public static final int PROPERTY_WIFI = 0x00000008;
+
+ /**
+ * Call is using high definition audio.
+ */
+ public static final int PROPERTY_HIGH_DEF_AUDIO = 0x00000010;
+
+ //******************************************************************************************
+ // Next PROPERTY value: 0x00000020
+ //******************************************************************************************
private final Uri mHandle;
private final int mHandlePresentation;
@@ -201,6 +253,7 @@ public final class Call {
private final int mVideoState;
private final StatusHints mStatusHints;
private final Bundle mExtras;
+ private final Bundle mIntentExtras;
/**
* Whether the supplied capabilities supports the specified capability.
@@ -208,7 +261,6 @@ public final class Call {
* @param capabilities A bit field of capabilities.
* @param capability The capability to check capabilities for.
* @return Whether the specified capability is supported.
- * @hide
*/
public static boolean can(int capabilities, int capability) {
return (capabilities & capability) != 0;
@@ -219,7 +271,6 @@ public final class Call {
*
* @param capability The capability to check capabilities for.
* @return Whether the specified capability is supported.
- * @hide
*/
public boolean can(int capability) {
return can(mCallCapabilities, capability);
@@ -255,23 +306,81 @@ public final class Call {
if (can(capabilities, CAPABILITY_MANAGE_CONFERENCE)) {
builder.append(" CAPABILITY_MANAGE_CONFERENCE");
}
- if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL)) {
- builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL");
+ if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_RX)) {
+ builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_RX");
+ }
+ if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_TX)) {
+ builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_TX");
}
- if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE)) {
- builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE");
+ if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)) {
+ builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL");
}
- if (can(capabilities, CAPABILITY_HIGH_DEF_AUDIO)) {
- builder.append(" CAPABILITY_HIGH_DEF_AUDIO");
+ if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_RX)) {
+ builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_RX");
}
- if (can(capabilities, CAPABILITY_VoWIFI)) {
- builder.append(" CAPABILITY_VoWIFI");
+ if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_TX)) {
+ builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_TX");
}
- if (can(capabilities, CAPABILITY_GENERIC_CONFERENCE)) {
- builder.append(" CAPABILITY_GENERIC_CONFERENCE");
+ if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
+ builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL");
}
if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) {
- builder.append(" CAPABILITY_SPEED_UP_IMS_MT_AUDIO");
+ builder.append(" CAPABILITY_SPEED_UP_MT_AUDIO");
+ }
+ if (can(capabilities, CAPABILITY_CAN_UPGRADE_TO_VIDEO)) {
+ builder.append(" CAPABILITY_CAN_UPGRADE_TO_VIDEO");
+ }
+ if (can(capabilities, CAPABILITY_CAN_PAUSE_VIDEO)) {
+ builder.append(" CAPABILITY_CAN_PAUSE_VIDEO");
+ }
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /**
+ * Whether the supplied properties includes the specified property.
+ *
+ * @param properties A bit field of properties.
+ * @param property The property to check properties for.
+ * @return Whether the specified property is supported.
+ */
+ public static boolean hasProperty(int properties, int property) {
+ return (properties & property) != 0;
+ }
+
+ /**
+ * Whether the properties of this {@code Details} includes the specified property.
+ *
+ * @param property The property to check properties for.
+ * @return Whether the specified property is supported.
+ */
+ public boolean hasProperty(int property) {
+ return hasProperty(mCallProperties, property);
+ }
+
+ /**
+ * Render a set of property bits ({@code PROPERTY_*}) as a human readable string.
+ *
+ * @param properties A property bit field.
+ * @return A human readable string representation.
+ */
+ public static String propertiesToString(int properties) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("[Properties:");
+ if (hasProperty(properties, PROPERTY_CONFERENCE)) {
+ builder.append(" PROPERTY_CONFERENCE");
+ }
+ if (hasProperty(properties, PROPERTY_GENERIC_CONFERENCE)) {
+ builder.append(" PROPERTY_GENERIC_CONFERENCE");
+ }
+ if (hasProperty(properties, PROPERTY_WIFI)) {
+ builder.append(" PROPERTY_WIFI");
+ }
+ if (hasProperty(properties, PROPERTY_HIGH_DEF_AUDIO)) {
+ builder.append(" PROPERTY_HIGH_DEF_AUDIO");
+ }
+ if (hasProperty(properties, PROPERTY_EMERGENCY_CALLBACK_MODE)) {
+ builder.append(" PROPERTY_EMERGENCY_CALLBACK_MODE");
}
builder.append("]");
return builder.toString();
@@ -325,8 +434,8 @@ public final class Call {
}
/**
- * @return A bitmask of the properties of the {@code Call}, as defined in
- * {@link CallProperties}.
+ * @return A bitmask of the properties of the {@code Call}, as defined by the various
+ * {@code PROPERTY_*} constants in this class.
*/
public int getCallProperties() {
return mCallProperties;
@@ -345,7 +454,7 @@ public final class Call {
* periodically, but user interfaces should not rely on this to display any "call time
* clock".
*/
- public long getConnectTimeMillis() {
+ public final long getConnectTimeMillis() {
return mConnectTimeMillis;
}
@@ -372,12 +481,19 @@ public final class Call {
}
/**
- * @return A bundle extras to pass with the call
+ * @return The extras associated with this call.
*/
public Bundle getExtras() {
return mExtras;
}
+ /**
+ * @return The extras used with the original intent to place this call.
+ */
+ public Bundle getIntentExtras() {
+ return mIntentExtras;
+ }
+
@Override
public boolean equals(Object o) {
if (o instanceof Details) {
@@ -396,7 +512,8 @@ public final class Call {
Objects.equals(mGatewayInfo, d.mGatewayInfo) &&
Objects.equals(mVideoState, d.mVideoState) &&
Objects.equals(mStatusHints, d.mStatusHints) &&
- Objects.equals(mExtras, d.mExtras);
+ Objects.equals(mExtras, d.mExtras) &&
+ Objects.equals(mIntentExtras, d.mIntentExtras);
}
return false;
}
@@ -416,7 +533,8 @@ public final class Call {
Objects.hashCode(mGatewayInfo) +
Objects.hashCode(mVideoState) +
Objects.hashCode(mStatusHints) +
- Objects.hashCode(mExtras);
+ Objects.hashCode(mExtras) +
+ Objects.hashCode(mIntentExtras);
}
/** {@hide} */
@@ -433,7 +551,8 @@ public final class Call {
GatewayInfo gatewayInfo,
int videoState,
StatusHints statusHints,
- Bundle extras) {
+ Bundle extras,
+ Bundle intentExtras) {
mHandle = handle;
mHandlePresentation = handlePresentation;
mCallerDisplayName = callerDisplayName;
@@ -447,10 +566,11 @@ public final class Call {
mVideoState = videoState;
mStatusHints = statusHints;
mExtras = extras;
+ mIntentExtras = intentExtras;
}
}
- public static abstract class Listener {
+ public static abstract class Callback {
/**
* Invoked when the state of this {@code Call} has changed. See {@link #getState()}.
*
@@ -509,7 +629,6 @@ public final class Call {
*
* @param call The {@code Call} invoking this method.
* @param videoCall The {@code Call.VideoCall} associated with the {@code Call}.
- * @hide
*/
public void onVideoCallChanged(Call call, InCallService.VideoCall videoCall) {}
@@ -535,13 +654,21 @@ public final class Call {
public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {}
}
+ /**
+ * @deprecated Use {@code Call.Callback} instead.
+ * @hide
+ */
+ @Deprecated
+ @SystemApi
+ public static abstract class Listener extends Callback { }
+
private final Phone mPhone;
private final String mTelecomCallId;
private final InCallAdapter mInCallAdapter;
private final List<String> mChildrenIds = new ArrayList<>();
private final List<Call> mChildren = new ArrayList<>();
private final List<Call> mUnmodifiableChildren = Collections.unmodifiableList(mChildren);
- private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
+ private final List<CallbackRecord<Callback>> mCallbackRecords = new CopyOnWriteArrayList<>();
private final List<Call> mConferenceableCalls = new ArrayList<>();
private final List<Call> mUnmodifiableConferenceableCalls =
Collections.unmodifiableList(mConferenceableCalls);
@@ -636,8 +763,8 @@ public final class Call {
* {@code Call} will temporarily pause playing the tones for a pre-defined period of time.
*
* If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_WAIT} symbol, this
- * {@code Call} will pause playing the tones and notify listeners via
- * {@link Listener#onPostDialWait(Call, String)}. At this point, the in-call app
+ * {@code Call} will pause playing the tones and notify callbacks via
+ * {@link Callback#onPostDialWait(Call, String)}. At this point, the in-call app
* should display to the user an indication of this state and an affordance to continue
* the postdial sequence. When the user decides to continue the postdial sequence, the in-call
* app should invoke the {@link #postDialContinue(boolean)} method.
@@ -762,7 +889,6 @@ public final class Call {
* Obtains an object that can be used to display video from this {@code Call}.
*
* @return An {@code Call.VideoCall}.
- * @hide
*/
public InCallService.VideoCall getVideoCall() {
return mVideoCall;
@@ -779,25 +905,72 @@ public final class Call {
}
/**
+ * Registers a callback to this {@code Call}.
+ *
+ * @param callback A {@code Callback}.
+ */
+ public void registerCallback(Callback callback) {
+ registerCallback(callback, new Handler());
+ }
+
+ /**
+ * Registers a callback to this {@code Call}.
+ *
+ * @param callback A {@code Callback}.
+ * @param handler A handler which command and status changes will be delivered to.
+ */
+ public void registerCallback(Callback callback, Handler handler) {
+ unregisterCallback(callback);
+ // Don't allow new callback registration if the call is already being destroyed.
+ if (callback != null && handler != null && mState != STATE_DISCONNECTED) {
+ mCallbackRecords.add(new CallbackRecord<Callback>(callback, handler));
+ }
+ }
+
+ /**
+ * Unregisters a callback from this {@code Call}.
+ *
+ * @param callback A {@code Callback}.
+ */
+ public void unregisterCallback(Callback callback) {
+ // Don't allow callback deregistration if the call is already being destroyed.
+ if (callback != null && mState != STATE_DISCONNECTED) {
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ if (record.getCallback() == callback) {
+ mCallbackRecords.remove(record);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
* Adds a listener to this {@code Call}.
*
* @param listener A {@code Listener}.
+ * @deprecated Use {@link #registerCallback} instead.
+ * @hide
*/
+ @Deprecated
+ @SystemApi
public void addListener(Listener listener) {
- mListeners.add(listener);
+ registerCallback(listener);
}
/**
* Removes a listener from this {@code Call}.
*
* @param listener A {@code Listener}.
+ * @deprecated Use {@link #unregisterCallback} instead.
+ * @hide
*/
+ @Deprecated
+ @SystemApi
public void removeListener(Listener listener) {
- if (listener != null) {
- mListeners.remove(listener);
- }
+ unregisterCallback(listener);
}
+
/** {@hide} */
Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter) {
mPhone = phone;
@@ -827,7 +1000,8 @@ public final class Call {
parcelableCall.getGatewayInfo(),
parcelableCall.getVideoState(),
parcelableCall.getStatusHints(),
- parcelableCall.getExtras());
+ parcelableCall.getExtras(),
+ parcelableCall.getIntentExtras());
boolean detailsChanged = !Objects.equals(mDetails, details);
if (detailsChanged) {
mDetails = details;
@@ -840,12 +1014,13 @@ public final class Call {
Collections.unmodifiableList(parcelableCall.getCannedSmsResponses());
}
- boolean videoCallChanged = !Objects.equals(mVideoCall, parcelableCall.getVideoCall());
+ boolean videoCallChanged = parcelableCall.isVideoCallProviderChanged() &&
+ !Objects.equals(mVideoCall, parcelableCall.getVideoCall(this));
if (videoCallChanged) {
- mVideoCall = parcelableCall.getVideoCall();
+ mVideoCall = parcelableCall.getVideoCall(this);
}
- int state = stateFromParcelableCallState(parcelableCall.getState());
+ int state = parcelableCall.getState();
boolean stateChanged = mState != state;
if (stateChanged) {
mState = state;
@@ -907,7 +1082,6 @@ public final class Call {
// DISCONNECTED Call while still relying on the existence of that Call in the Phone's list.
if (mState == STATE_DISCONNECTED) {
fireCallDestroyed();
- mPhone.internalRemoveCall(this);
}
}
@@ -923,89 +1097,152 @@ public final class Call {
mState = Call.STATE_DISCONNECTED;
fireStateChanged(mState);
fireCallDestroyed();
- mPhone.internalRemoveCall(this);
}
}
- private void fireStateChanged(int newState) {
- for (Listener listener : mListeners) {
- listener.onStateChanged(this, newState);
+ private void fireStateChanged(final int newState) {
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final Call call = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onStateChanged(call, newState);
+ }
+ });
}
}
- private void fireParentChanged(Call newParent) {
- for (Listener listener : mListeners) {
- listener.onParentChanged(this, newParent);
+ private void fireParentChanged(final Call newParent) {
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final Call call = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onParentChanged(call, newParent);
+ }
+ });
}
}
- private void fireChildrenChanged(List<Call> children) {
- for (Listener listener : mListeners) {
- listener.onChildrenChanged(this, children);
+ private void fireChildrenChanged(final List<Call> children) {
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final Call call = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onChildrenChanged(call, children);
+ }
+ });
}
}
- private void fireDetailsChanged(Details details) {
- for (Listener listener : mListeners) {
- listener.onDetailsChanged(this, details);
+ private void fireDetailsChanged(final Details details) {
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final Call call = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onDetailsChanged(call, details);
+ }
+ });
}
}
- private void fireCannedTextResponsesLoaded(List<String> cannedTextResponses) {
- for (Listener listener : mListeners) {
- listener.onCannedTextResponsesLoaded(this, cannedTextResponses);
+ private void fireCannedTextResponsesLoaded(final List<String> cannedTextResponses) {
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final Call call = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onCannedTextResponsesLoaded(call, cannedTextResponses);
+ }
+ });
}
}
- private void fireVideoCallChanged(InCallService.VideoCall videoCall) {
- for (Listener listener : mListeners) {
- listener.onVideoCallChanged(this, videoCall);
+ private void fireVideoCallChanged(final InCallService.VideoCall videoCall) {
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final Call call = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onVideoCallChanged(call, videoCall);
+ }
+ });
}
}
- private void firePostDialWait(String remainingPostDialSequence) {
- for (Listener listener : mListeners) {
- listener.onPostDialWait(this, remainingPostDialSequence);
+ private void firePostDialWait(final String remainingPostDialSequence) {
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final Call call = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onPostDialWait(call, remainingPostDialSequence);
+ }
+ });
}
}
private void fireCallDestroyed() {
- for (Listener listener : mListeners) {
- listener.onCallDestroyed(this);
+ /**
+ * To preserve the ordering of the Call's onCallDestroyed callback and Phone's
+ * onCallRemoved callback, we remove this call from the Phone's record
+ * only once all of the registered onCallDestroyed callbacks are executed.
+ * All the callbacks get removed from our records as a part of this operation
+ * since onCallDestroyed is the final callback.
+ */
+ final Call call = this;
+ if (mCallbackRecords.isEmpty()) {
+ // No callbacks registered, remove the call from Phone's record.
+ mPhone.internalRemoveCall(call);
+ }
+ for (final CallbackRecord<Callback> record : mCallbackRecords) {
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ boolean isFinalRemoval = false;
+ RuntimeException toThrow = null;
+ try {
+ callback.onCallDestroyed(call);
+ } catch (RuntimeException e) {
+ toThrow = e;
+ }
+ synchronized(Call.this) {
+ mCallbackRecords.remove(record);
+ if (mCallbackRecords.isEmpty()) {
+ isFinalRemoval = true;
+ }
+ }
+ if (isFinalRemoval) {
+ mPhone.internalRemoveCall(call);
+ }
+ if (toThrow != null) {
+ throw toThrow;
+ }
+ }
+ });
}
}
private void fireConferenceableCallsChanged() {
- for (Listener listener : mListeners) {
- listener.onConferenceableCallsChanged(this, mUnmodifiableConferenceableCalls);
- }
- }
-
- private int stateFromParcelableCallState(int parcelableCallState) {
- switch (parcelableCallState) {
- case CallState.NEW:
- return STATE_NEW;
- case CallState.CONNECTING:
- return STATE_CONNECTING;
- case CallState.PRE_DIAL_WAIT:
- return STATE_PRE_DIAL_WAIT;
- case CallState.DIALING:
- return STATE_DIALING;
- case CallState.RINGING:
- return STATE_RINGING;
- case CallState.ACTIVE:
- return STATE_ACTIVE;
- case CallState.ON_HOLD:
- return STATE_HOLDING;
- case CallState.DISCONNECTED:
- return STATE_DISCONNECTED;
- case CallState.ABORTED:
- return STATE_DISCONNECTED;
- case CallState.DISCONNECTING:
- return STATE_DISCONNECTING;
- default:
- Log.wtf(this, "Unrecognized CallState %s", parcelableCallState);
- return STATE_NEW;
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final Call call = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onConferenceableCallsChanged(call, mUnmodifiableConferenceableCalls);
+ }
+ });
}
}
}
diff --git a/telecomm/java/android/telecom/CameraCapabilities.aidl b/telecomm/java/android/telecom/CallAudioState.aidl
index c8e0c5e..90dbbe5 100644
--- a/telecomm/java/android/telecom/CameraCapabilities.aidl
+++ b/telecomm/java/android/telecom/CallAudioState.aidl
@@ -1,17 +1,17 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright 2014, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
package android.telecom;
@@ -19,4 +19,4 @@ package android.telecom;
/**
* {@hide}
*/
-parcelable CameraCapabilities;
+parcelable CallAudioState;
diff --git a/telecomm/java/android/telecom/CallAudioState.java b/telecomm/java/android/telecom/CallAudioState.java
new file mode 100644
index 0000000..2b16722
--- /dev/null
+++ b/telecomm/java/android/telecom/CallAudioState.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Locale;
+
+/**
+ * Encapsulates the telecom audio state, including the current audio routing, supported audio
+ * routing and mute.
+ */
+public final class CallAudioState implements Parcelable {
+ /** Direct the audio stream through the device's earpiece. */
+ public static final int ROUTE_EARPIECE = 0x00000001;
+
+ /** Direct the audio stream through Bluetooth. */
+ public static final int ROUTE_BLUETOOTH = 0x00000002;
+
+ /** Direct the audio stream through a wired headset. */
+ public static final int ROUTE_WIRED_HEADSET = 0x00000004;
+
+ /** Direct the audio stream through the device's speakerphone. */
+ public static final int ROUTE_SPEAKER = 0x00000008;
+
+ /**
+ * Direct the audio stream through the device's earpiece or wired headset if one is
+ * connected.
+ */
+ public static final int ROUTE_WIRED_OR_EARPIECE = ROUTE_EARPIECE | ROUTE_WIRED_HEADSET;
+
+ /** Bit mask of all possible audio routes. */
+ private static final int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET |
+ ROUTE_SPEAKER;
+
+ private final boolean isMuted;
+ private final int route;
+ private final int supportedRouteMask;
+
+ /**
+ * Constructor for a {@link CallAudioState} object.
+ *
+ * @param muted {@code true} if the call is muted, {@code false} otherwise.
+ * @param route The current audio route being used.
+ * Allowed values:
+ * {@link #ROUTE_EARPIECE}
+ * {@link #ROUTE_BLUETOOTH}
+ * {@link #ROUTE_WIRED_HEADSET}
+ * {@link #ROUTE_SPEAKER}
+ * @param supportedRouteMask Bit mask of all routes supported by this call. This should be a
+ * bitwise combination of the following values:
+ * {@link #ROUTE_EARPIECE}
+ * {@link #ROUTE_BLUETOOTH}
+ * {@link #ROUTE_WIRED_HEADSET}
+ * {@link #ROUTE_SPEAKER}
+ */
+ public CallAudioState(boolean muted, int route, int supportedRouteMask) {
+ this.isMuted = muted;
+ this.route = route;
+ this.supportedRouteMask = supportedRouteMask;
+ }
+
+ /** @hide */
+ public CallAudioState(CallAudioState state) {
+ isMuted = state.isMuted();
+ route = state.getRoute();
+ supportedRouteMask = state.getSupportedRouteMask();
+ }
+
+ /** @hide */
+ @SuppressWarnings("deprecation")
+ public CallAudioState(AudioState state) {
+ isMuted = state.isMuted();
+ route = state.getRoute();
+ supportedRouteMask = state.getSupportedRouteMask();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof CallAudioState)) {
+ return false;
+ }
+ CallAudioState state = (CallAudioState) obj;
+ return isMuted() == state.isMuted() && getRoute() == state.getRoute() &&
+ getSupportedRouteMask() == state.getSupportedRouteMask();
+ }
+
+ @Override
+ public String toString() {
+ return String.format(Locale.US,
+ "[AudioState isMuted: %b, route: %s, supportedRouteMask: %s]",
+ isMuted,
+ audioRouteToString(route),
+ audioRouteToString(supportedRouteMask));
+ }
+
+ /**
+ * @return {@code true} if the call is muted, {@code false} otherwise.
+ */
+ public boolean isMuted() {
+ return isMuted;
+ }
+
+ /**
+ * @return The current audio route being used.
+ */
+ public int getRoute() {
+ return route;
+ }
+
+ /**
+ * @return Bit mask of all routes supported by this call.
+ */
+ public int getSupportedRouteMask() {
+ return supportedRouteMask;
+ }
+
+ /**
+ * Converts the provided audio route into a human readable string representation.
+ *
+ * @param route to convert into a string.
+ *
+ * @return String representation of the provided audio route.
+ */
+ public static String audioRouteToString(int route) {
+ if (route == 0 || (route & ~ROUTE_ALL) != 0x0) {
+ return "UNKNOWN";
+ }
+
+ StringBuffer buffer = new StringBuffer();
+ if ((route & ROUTE_EARPIECE) == ROUTE_EARPIECE) {
+ listAppend(buffer, "EARPIECE");
+ }
+ if ((route & ROUTE_BLUETOOTH) == ROUTE_BLUETOOTH) {
+ listAppend(buffer, "BLUETOOTH");
+ }
+ if ((route & ROUTE_WIRED_HEADSET) == ROUTE_WIRED_HEADSET) {
+ listAppend(buffer, "WIRED_HEADSET");
+ }
+ if ((route & ROUTE_SPEAKER) == ROUTE_SPEAKER) {
+ listAppend(buffer, "SPEAKER");
+ }
+
+ return buffer.toString();
+ }
+
+ /**
+ * Responsible for creating AudioState objects for deserialized Parcels.
+ */
+ public static final Parcelable.Creator<CallAudioState> CREATOR =
+ new Parcelable.Creator<CallAudioState> () {
+
+ @Override
+ public CallAudioState createFromParcel(Parcel source) {
+ boolean isMuted = source.readByte() == 0 ? false : true;
+ int route = source.readInt();
+ int supportedRouteMask = source.readInt();
+ return new CallAudioState(isMuted, route, supportedRouteMask);
+ }
+
+ @Override
+ public CallAudioState[] newArray(int size) {
+ return new CallAudioState[size];
+ }
+ };
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Writes AudioState object into a serializeable Parcel.
+ */
+ @Override
+ public void writeToParcel(Parcel destination, int flags) {
+ destination.writeByte((byte) (isMuted ? 1 : 0));
+ destination.writeInt(route);
+ destination.writeInt(supportedRouteMask);
+ }
+
+ private static void listAppend(StringBuffer buffer, String str) {
+ if (buffer.length() > 0) {
+ buffer.append(", ");
+ }
+ buffer.append(str);
+ }
+}
diff --git a/telecomm/java/android/telecom/CallProperties.java b/telecomm/java/android/telecom/CallProperties.java
deleted file mode 100644
index b1b82e2..0000000
--- a/telecomm/java/android/telecom/CallProperties.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telecom;
-
-/**
- * Defines properties of a phone call which may be affected by changes to the call.
- * @hide
- */
-public class CallProperties {
- /** Call is currently in a conference call. */
- public static final int CONFERENCE = 0x00000001;
-}
diff --git a/telecomm/java/android/telecom/CallState.java b/telecomm/java/android/telecom/CallState.java
deleted file mode 100644
index bd9223a..0000000
--- a/telecomm/java/android/telecom/CallState.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telecom;
-
-import android.annotation.SystemApi;
-
-/**
- * Defines call-state constants of the different states in which a call can exist. Although states
- * have the notion of normal transitions, due to the volatile nature of telephony systems, code
- * that uses these states should be resilient to unexpected state changes outside of what is
- * considered traditional.
- *
- * {@hide}
- */
-@SystemApi
-public final class CallState {
-
- private CallState() {}
-
- /**
- * Indicates that a call is new and not connected. This is used as the default state internally
- * within Telecom and should not be used between Telecom and call services. Call services are
- * not expected to ever interact with NEW calls, but {@link InCallService}s will see calls in
- * this state.
- */
- public static final int NEW = 0;
-
- /**
- * The initial state of an outgoing {@code Call}.
- * Common transitions are to {@link #DIALING} state for a successful call or
- * {@link #DISCONNECTED} if it failed.
- */
- public static final int CONNECTING = 1;
-
- /**
- * Indicates that the call is about to go into the outgoing and dialing state but is waiting for
- * user input before it proceeds. For example, where no default {@link PhoneAccount} is set,
- * this is the state where the InCallUI is waiting for the user to select a
- * {@link PhoneAccount} to call from.
- */
- public static final int PRE_DIAL_WAIT = 2;
-
- /**
- * Indicates that a call is outgoing and in the dialing state. A call transitions to this state
- * once an outgoing call has begun (e.g., user presses the dial button in Dialer). Calls in this
- * state usually transition to {@link #ACTIVE} if the call was answered or {@link #DISCONNECTED}
- * if the call was disconnected somehow (e.g., failure or cancellation of the call by the user).
- */
- public static final int DIALING = 3;
-
- /**
- * Indicates that a call is incoming and the user still has the option of answering, rejecting,
- * or doing nothing with the call. This state is usually associated with some type of audible
- * ringtone. Normal transitions are to {@link #ACTIVE} if answered or {@link #DISCONNECTED}
- * otherwise.
- */
- public static final int RINGING = 4;
-
- /**
- * Indicates that a call is currently connected to another party and a communication channel is
- * open between them. The normal transition to this state is by the user answering a
- * {@link #DIALING} call or a {@link #RINGING} call being answered by the other party.
- */
- public static final int ACTIVE = 5;
-
- /**
- * Indicates that the call is currently on hold. In this state, the call is not terminated
- * but no communication is allowed until the call is no longer on hold. The typical transition
- * to this state is by the user putting an {@link #ACTIVE} call on hold by explicitly performing
- * an action, such as clicking the hold button.
- */
- public static final int ON_HOLD = 6;
-
- /**
- * Indicates that a call is currently disconnected. All states can transition to this state
- * by the call service giving notice that the connection has been severed. When the user
- * explicitly ends a call, it will not transition to this state until the call service confirms
- * the disconnection or communication was lost to the call service currently responsible for
- * this call (e.g., call service crashes).
- */
- public static final int DISCONNECTED = 7;
-
- /**
- * Indicates that the call was attempted (mostly in the context of outgoing, at least at the
- * time of writing) but cancelled before it was successfully connected.
- */
- public static final int ABORTED = 8;
-
- /**
- * Indicates that the call is in the process of being disconnected and will transition next
- * to a {@link #DISCONNECTED} state.
- * <p>
- * This state is not expected to be communicated from the Telephony layer, but will be reported
- * to the InCall UI for calls where disconnection has been initiated by the user but the
- * ConnectionService has confirmed the call as disconnected.
- */
- public static final int DISCONNECTING = 9;
-
- public static String toString(int callState) {
- switch (callState) {
- case NEW:
- return "NEW";
- case CONNECTING:
- return "CONNECTING";
- case PRE_DIAL_WAIT:
- return "PRE_DIAL_WAIT";
- case DIALING:
- return "DIALING";
- case RINGING:
- return "RINGING";
- case ACTIVE:
- return "ACTIVE";
- case ON_HOLD:
- return "ON_HOLD";
- case DISCONNECTED:
- return "DISCONNECTED";
- case ABORTED:
- return "ABORTED";
- case DISCONNECTING:
- return "DISCONNECTING";
- default:
- return "UNKNOWN";
- }
- }
-}
diff --git a/telecomm/java/android/telecom/CallbackRecord.java b/telecomm/java/android/telecom/CallbackRecord.java
new file mode 100644
index 0000000..1a81925
--- /dev/null
+++ b/telecomm/java/android/telecom/CallbackRecord.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+import android.os.Handler;
+
+
+/**
+ * This class is used to associate a generic callback of type T with a handler to which commands and
+ * status updates will be delivered to.
+ *
+ * @hide
+ */
+class CallbackRecord<T> {
+ private final T mCallback;
+ private final Handler mHandler;
+
+ public CallbackRecord(T callback, Handler handler) {
+ mCallback = callback;
+ mHandler = handler;
+ }
+
+ public T getCallback() {
+ return mCallback;
+ }
+
+ public Handler getHandler() {
+ return mHandler;
+ }
+}
diff --git a/telecomm/java/android/telecom/CameraCapabilities.java b/telecomm/java/android/telecom/CameraCapabilities.java
deleted file mode 100644
index f968c13..0000000
--- a/telecomm/java/android/telecom/CameraCapabilities.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telecom;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Represents the camera capabilities important to a Video Telephony provider.
- * @hide
- */
-public final class CameraCapabilities implements Parcelable {
-
- /**
- * Whether the camera supports zoom.
- */
- private final boolean mZoomSupported;
-
- /**
- * The maximum zoom supported by the camera.
- */
- private final float mMaxZoom;
-
- /**
- * The width of the camera video in pixels.
- */
- private final int mWidth;
-
- /**
- * The height of the camera video in pixels.
- */
- private final int mHeight;
-
- /**
- * Create a call camera capabilities instance.
- *
- * @param zoomSupported True when camera supports zoom.
- * @param maxZoom Maximum zoom supported by camera.
- * @param width The width of the camera video (in pixels).
- * @param height The height of the camera video (in pixels).
- */
- public CameraCapabilities(boolean zoomSupported, float maxZoom, int width, int height) {
- mZoomSupported = zoomSupported;
- mMaxZoom = maxZoom;
- mWidth = width;
- mHeight = height;
- }
-
- /**
- * Responsible for creating CallCameraCapabilities objects from deserialized Parcels.
- **/
- public static final Parcelable.Creator<CameraCapabilities> CREATOR =
- new Parcelable.Creator<CameraCapabilities> () {
- /**
- * Creates a CallCameraCapabilities instances from a parcel.
- *
- * @param source The parcel.
- * @return The CallCameraCapabilities.
- */
- @Override
- public CameraCapabilities createFromParcel(Parcel source) {
- boolean supportsZoom = source.readByte() != 0;
- float maxZoom = source.readFloat();
- int width = source.readInt();
- int height = source.readInt();
-
- return new CameraCapabilities(supportsZoom, maxZoom, width, height);
- }
-
- @Override
- public CameraCapabilities[] newArray(int size) {
- return new CameraCapabilities[size];
- }
- };
-
- /**
- * Describe the kinds of special objects contained in this Parcelable's
- * marshalled representation.
- *
- * @return a bitmask indicating the set of special object types marshalled
- * by the Parcelable.
- */
- @Override
- public int describeContents() {
- return 0;
- }
-
- /**
- * Flatten this object in to a Parcel.
- *
- * @param dest The Parcel in which the object should be written.
- * @param flags Additional flags about how the object should be written.
- * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
- */
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeByte((byte) (isZoomSupported() ? 1 : 0));
- dest.writeFloat(getMaxZoom());
- dest.writeInt(getWidth());
- dest.writeInt(getHeight());
- }
-
- /**
- * Whether the camera supports zoom.
- */
- public boolean isZoomSupported() {
- return mZoomSupported;
- }
-
- /**
- * The maximum zoom supported by the camera.
- */
- public float getMaxZoom() {
- return mMaxZoom;
- }
-
- /**
- * The width of the camera video in pixels.
- */
- public int getWidth() {
- return mWidth;
- }
-
- /**
- * The height of the camera video in pixels.
- */
- public int getHeight() {
- return mHeight;
- }
-}
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 33bbb29..77fdb65 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -16,27 +16,29 @@
package android.telecom;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.telecom.Connection.VideoProvider;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* Represents a conference call which can contain any number of {@link Connection} objects.
- * @hide
*/
-@SystemApi
-public abstract class Conference implements IConferenceable {
+public abstract class Conference extends Conferenceable {
/**
* Used to indicate that the conference connection time is not specified. If not specified,
* Telecom will set the connect time.
*/
- public static long CONNECT_TIME_NOT_SPECIFIED = 0;
+ public static final long CONNECT_TIME_NOT_SPECIFIED = 0;
/** @hide */
public abstract static class Listener {
@@ -49,6 +51,10 @@ public abstract class Conference implements IConferenceable {
public void onDestroyed(Conference conference) {}
public void onConnectionCapabilitiesChanged(
Conference conference, int connectionCapabilities) {}
+ public void onVideoStateChanged(Conference c, int videoState) { }
+ public void onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider) {}
+ public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {}
+ public void onExtrasChanged(Conference conference, Bundle extras) {}
}
private final Set<Listener> mListeners = new CopyOnWriteArraySet<>();
@@ -59,13 +65,15 @@ public abstract class Conference implements IConferenceable {
private final List<Connection> mUnmodifiableConferenceableConnections =
Collections.unmodifiableList(mConferenceableConnections);
- protected PhoneAccountHandle mPhoneAccount;
- private AudioState mAudioState;
+ private PhoneAccountHandle mPhoneAccount;
+ private CallAudioState mCallAudioState;
private int mState = Connection.STATE_NEW;
private DisconnectCause mDisconnectCause;
private int mConnectionCapabilities;
private String mDisconnectMessage;
private long mConnectTimeMillis = CONNECT_TIME_NOT_SPECIFIED;
+ private StatusHints mStatusHints;
+ private Bundle mExtras;
private final Connection.Listener mConnectionDeathListener = new Connection.Listener() {
@Override
@@ -112,13 +120,8 @@ public abstract class Conference implements IConferenceable {
return mState;
}
- /** @hide */
- @Deprecated public final int getCapabilities() {
- return getConnectionCapabilities();
- }
-
/**
- * Returns the capabilities of a conference. See {@code CAPABILITY_*} constants in class
+ * Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class
* {@link Connection} for valid values.
*
* @return A bitmask of the capabilities of the conference call.
@@ -174,9 +177,36 @@ public abstract class Conference implements IConferenceable {
* @return The audio state of the conference, describing how its audio is currently
* being routed by the system. This is {@code null} if this Conference
* does not directly know about its audio state.
+ * @deprecated Use {@link #getCallAudioState()} instead.
+ * @hide
*/
+ @Deprecated
+ @SystemApi
public final AudioState getAudioState() {
- return mAudioState;
+ return new AudioState(mCallAudioState);
+ }
+
+ /**
+ * @return The audio state of the conference, describing how its audio is currently
+ * being routed by the system. This is {@code null} if this Conference
+ * does not directly know about its audio state.
+ */
+ public final CallAudioState getCallAudioState() {
+ return mCallAudioState;
+ }
+
+ /**
+ * Returns VideoProvider of the primary call. This can be null.
+ */
+ public VideoProvider getVideoProvider() {
+ return null;
+ }
+
+ /**
+ * Returns video state of the primary call.
+ */
+ public int getVideoState() {
+ return VideoProfile.STATE_AUDIO_ONLY;
}
/**
@@ -236,10 +266,21 @@ public abstract class Conference implements IConferenceable {
* Notifies this conference that the {@link #getAudioState()} property has a new value.
*
* @param state The new call audio state.
+ * @deprecated Use {@link #onCallAudioStateChanged(CallAudioState)} instead.
+ * @hide
*/
+ @SystemApi
+ @Deprecated
public void onAudioStateChanged(AudioState state) {}
/**
+ * Notifies this conference that the {@link #getCallAudioState()} property has a new value.
+ *
+ * @param state The new call audio state.
+ */
+ public void onCallAudioStateChanged(CallAudioState state) {}
+
+ /**
* Notifies this conference that a connection has been added to it.
*
* @param connection The newly added connection.
@@ -254,6 +295,13 @@ public abstract class Conference implements IConferenceable {
}
/**
+ * Sets state to be dialing.
+ */
+ public final void setDialing() {
+ setState(Connection.STATE_DIALING);
+ }
+
+ /**
* Sets state to be active.
*/
public final void setActive() {
@@ -281,11 +329,6 @@ public abstract class Conference implements IConferenceable {
return mDisconnectCause;
}
- /** @hide */
- @Deprecated public final void setCapabilities(int connectionCapabilities) {
- setConnectionCapabilities(connectionCapabilities);
- }
-
/**
* Sets the capabilities of a conference. See {@code CAPABILITY_*} constants of class
* {@link Connection} for valid values.
@@ -309,6 +352,7 @@ public abstract class Conference implements IConferenceable {
* @return True if the connection was successfully added.
*/
public final boolean addConnection(Connection connection) {
+ Log.d(this, "Connection=%s, connection=", connection);
if (connection != null && !mChildConnections.contains(connection)) {
if (connection.setConference(this)) {
mChildConnections.add(connection);
@@ -355,6 +399,36 @@ public abstract class Conference implements IConferenceable {
fireOnConferenceableConnectionsChanged();
}
+ /**
+ * Set the video state for the conference.
+ * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
+ * {@link VideoProfile#STATE_BIDIRECTIONAL},
+ * {@link VideoProfile#STATE_TX_ENABLED},
+ * {@link VideoProfile#STATE_RX_ENABLED}.
+ *
+ * @param videoState The new video state.
+ */
+ public final void setVideoState(Connection c, int videoState) {
+ Log.d(this, "setVideoState Conference: %s Connection: %s VideoState: %s",
+ this, c, videoState);
+ for (Listener l : mListeners) {
+ l.onVideoStateChanged(this, videoState);
+ }
+ }
+
+ /**
+ * Sets the video connection provider.
+ *
+ * @param videoProvider The video provider.
+ */
+ public final void setVideoProvider(Connection c, Connection.VideoProvider videoProvider) {
+ Log.d(this, "setVideoProvider Conference: %s Connection: %s VideoState: %s",
+ this, c, videoProvider);
+ for (Listener l : mListeners) {
+ l.onVideoProviderChanged(this, videoProvider);
+ }
+ }
+
private final void fireOnConferenceableConnectionsChanged() {
for (Listener l : mListeners) {
l.onConferenceableConnectionsChanged(this, getConferenceableConnections());
@@ -420,7 +494,9 @@ public abstract class Conference implements IConferenceable {
* the connection from which the conference will retrieve its current state.
*
* @return The primary connection.
+ * @hide
*/
+ @SystemApi
public Connection getPrimaryConnection() {
if (mUnmodifiableChildConnections == null || mUnmodifiableChildConnections.isEmpty()) {
return null;
@@ -429,22 +505,42 @@ public abstract class Conference implements IConferenceable {
}
/**
- * Sets the connect time of the {@code Conference}.
+ * @hide
+ * @deprecated Use {@link #setConnectionTime}.
+ */
+ @Deprecated
+ @SystemApi
+ public final void setConnectTimeMillis(long connectTimeMillis) {
+ setConnectionTime(connectTimeMillis);
+ }
+
+ /**
+ * Sets the connection start time of the {@code Conference}.
*
- * @param connectTimeMillis The connection time, in milliseconds.
+ * @param connectionTimeMillis The connection time, in milliseconds.
+ */
+ public final void setConnectionTime(long connectionTimeMillis) {
+ mConnectTimeMillis = connectionTimeMillis;
+ }
+
+ /**
+ * @hide
+ * @deprecated Use {@link #getConnectionTime}.
*/
- public void setConnectTimeMillis(long connectTimeMillis) {
- mConnectTimeMillis = connectTimeMillis;
+ @Deprecated
+ @SystemApi
+ public final long getConnectTimeMillis() {
+ return getConnectionTime();
}
/**
- * Retrieves the connect time of the {@code Conference}, if specified. A value of
+ * Retrieves the connection start time of the {@code Conference}, if specified. A value of
* {@link #CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the start time
* of the conference.
*
- * @return The time the {@code Conference} has been connected.
+ * @return The time at which the {@code Conference} was connected.
*/
- public long getConnectTimeMillis() {
+ public final long getConnectionTime() {
return mConnectTimeMillis;
}
@@ -454,10 +550,11 @@ public abstract class Conference implements IConferenceable {
* @param state The new audio state.
* @hide
*/
- final void setAudioState(AudioState state) {
- Log.d(this, "setAudioState %s", state);
- mAudioState = state;
- onAudioStateChanged(state);
+ final void setCallAudioState(CallAudioState state) {
+ Log.d(this, "setCallAudioState %s", state);
+ mCallAudioState = state;
+ onAudioStateChanged(getAudioState());
+ onCallAudioStateChanged(state);
}
private void setState(int newState) {
@@ -484,4 +581,55 @@ public abstract class Conference implements IConferenceable {
}
mConferenceableConnections.clear();
}
+
+ @Override
+ public String toString() {
+ return String.format(Locale.US,
+ "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s, ThisObject %s]",
+ Connection.stateToString(mState),
+ Call.Details.capabilitiesToString(mConnectionCapabilities),
+ getVideoState(),
+ getVideoProvider(),
+ super.toString());
+ }
+
+ /**
+ * Sets the label and icon status to display in the InCall UI.
+ *
+ * @param statusHints The status label and icon to set.
+ */
+ public final void setStatusHints(StatusHints statusHints) {
+ mStatusHints = statusHints;
+ for (Listener l : mListeners) {
+ l.onStatusHintsChanged(this, statusHints);
+ }
+ }
+
+ /**
+ * @return The status hints for this conference.
+ */
+ public final StatusHints getStatusHints() {
+ return mStatusHints;
+ }
+
+ /**
+ * Set some extras that can be associated with this {@code Conference}. No assumptions should
+ * be made as to how an In-Call UI or service will handle these extras.
+ * Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
+ *
+ * @param extras The extras associated with this {@code Connection}.
+ */
+ public final void setExtras(@Nullable Bundle extras) {
+ mExtras = extras;
+ for (Listener l : mListeners) {
+ l.onExtrasChanged(this, extras);
+ }
+ }
+
+ /**
+ * @return The extras associated with this conference.
+ */
+ public final Bundle getExtras() {
+ return mExtras;
+ }
}
diff --git a/telecomm/java/android/telecom/IConferenceable.java b/telecomm/java/android/telecom/Conferenceable.java
index 095d7cb..bb6f2b8 100644
--- a/telecomm/java/android/telecom/IConferenceable.java
+++ b/telecomm/java/android/telecom/Conferenceable.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,16 +16,11 @@
package android.telecom;
-import android.annotation.SystemApi;
-
/**
* Interface used to identify entities with which another entity can participate in a conference
* call with. The {@link ConnectionService} implementation will only recognize
- * {@link IConferenceable}s which are {@link Connection}s or {@link Conference}s.
- *
- * @hide
+ * {@link Conferenceable}s which are {@link Connection}s or {@link Conference}s.
*/
-@SystemApi
-public interface IConferenceable {
-
+public abstract class Conferenceable {
+ Conferenceable() {}
}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 00a4136..f304d1d 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -16,13 +16,18 @@
package android.telecom;
+import com.android.internal.os.SomeArgs;
import com.android.internal.telecom.IVideoCallback;
import com.android.internal.telecom.IVideoProvider;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.hardware.camera2.CameraManager;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.view.Surface;
@@ -34,7 +39,8 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
- * Represents a connection to a remote endpoint that carries voice traffic.
+ * Represents a phone call or connection to a remote endpoint that carries voice and/or video
+ * traffic.
* <p>
* Implementations create a custom subclass of {@code Connection} and return it to the framework
* as the return value of
@@ -44,26 +50,55 @@ import java.util.concurrent.ConcurrentHashMap;
* Implementations are then responsible for updating the state of the {@code Connection}, and
* must call {@link #destroy()} to signal to the framework that the {@code Connection} is no
* longer used and associated resources may be recovered.
- * @hide
*/
-@SystemApi
-public abstract class Connection implements IConferenceable {
+public abstract class Connection extends Conferenceable {
+ /**
+ * The connection is initializing. This is generally the first state for a {@code Connection}
+ * returned by a {@link ConnectionService}.
+ */
public static final int STATE_INITIALIZING = 0;
+ /**
+ * The connection is new and not connected.
+ */
public static final int STATE_NEW = 1;
+ /**
+ * An incoming connection is in the ringing state. During this state, the user's ringer or
+ * vibration feature will be activated.
+ */
public static final int STATE_RINGING = 2;
+ /**
+ * An outgoing connection is in the dialing state. In this state the other party has not yet
+ * answered the call and the user traditionally hears a ringback tone.
+ */
public static final int STATE_DIALING = 3;
+ /**
+ * A connection is active. Both parties are connected to the call and can actively communicate.
+ */
public static final int STATE_ACTIVE = 4;
+ /**
+ * A connection is on hold.
+ */
public static final int STATE_HOLDING = 5;
+ /**
+ * A connection has been disconnected. This is the final state once the user has been
+ * disconnected from a call either locally, remotely or by an error in the service.
+ */
public static final int STATE_DISCONNECTED = 6;
- /** Connection can currently be put on hold or unheld. */
+ /**
+ * Connection can currently be put on hold or unheld. This is distinct from
+ * {@link #CAPABILITY_SUPPORT_HOLD} in that although a connection may support 'hold' most times,
+ * it does not at the moment support the function. This can be true while the call is in the
+ * state {@link #STATE_DIALING}, for example. During this condition, an in-call UI may
+ * display a disabled 'hold' button.
+ */
public static final int CAPABILITY_HOLD = 0x00000001;
/** Connection supports the hold feature. */
@@ -106,28 +141,36 @@ public abstract class Connection implements IConferenceable {
public static final int CAPABILITY_MANAGE_CONFERENCE = 0x00000080;
/**
- * Local device supports video telephony.
- * @hide
+ * Local device supports receiving video.
*/
- public static final int CAPABILITY_SUPPORTS_VT_LOCAL = 0x00000100;
+ public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 0x00000100;
/**
- * Remote device supports video telephony.
- * @hide
+ * Local device supports transmitting video.
*/
- public static final int CAPABILITY_SUPPORTS_VT_REMOTE = 0x00000200;
+ public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 0x00000200;
/**
- * Connection is using high definition audio.
- * @hide
+ * Local device supports bidirectional video calling.
*/
- public static final int CAPABILITY_HIGH_DEF_AUDIO = 0x00000400;
+ public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL =
+ CAPABILITY_SUPPORTS_VT_LOCAL_RX | CAPABILITY_SUPPORTS_VT_LOCAL_TX;
/**
- * Connection is using voice over WIFI.
- * @hide
+ * Remote device supports receiving video.
+ */
+ public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 0x00000400;
+
+ /**
+ * Remote device supports transmitting video.
+ */
+ public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 0x00000800;
+
+ /**
+ * Remote device supports bidirectional video calling.
*/
- public static final int CAPABILITY_VoWIFI = 0x00000800;
+ public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL =
+ CAPABILITY_SUPPORTS_VT_REMOTE_RX | CAPABILITY_SUPPORTS_VT_REMOTE_TX;
/**
* Connection is able to be separated from its parent {@code Conference}, if any.
@@ -148,10 +191,66 @@ public abstract class Connection implements IConferenceable {
public static final int CAPABILITY_GENERIC_CONFERENCE = 0x00004000;
/**
+ * Connection is using high definition audio.
+ * @hide
+ */
+ public static final int CAPABILITY_HIGH_DEF_AUDIO = 0x00008000;
+
+ /**
+ * Connection is using WIFI.
+ * @hide
+ */
+ public static final int CAPABILITY_WIFI = 0x00010000;
+
+ /**
+ * Indicates that the current device callback number should be shown.
+ *
+ * @hide
+ */
+ public static final int CAPABILITY_SHOW_CALLBACK_NUMBER = 0x00020000;
+
+ /**
* Speed up audio setup for MT call.
* @hide
- */
- public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00008000;
+ */
+ public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00040000;
+
+ /**
+ * Call can be upgraded to a video call.
+ */
+ public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000;
+
+ /**
+ * For video calls, indicates whether the outgoing video for the call can be paused using
+ * the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
+ */
+ public static final int CAPABILITY_CAN_PAUSE_VIDEO = 0x00100000;
+
+ /**
+ * For a conference, indicates the conference will not have child connections.
+ * <p>
+ * An example of a conference with child connections is a GSM conference call, where the radio
+ * retains connections to the individual participants of the conference. Another example is an
+ * IMS conference call where conference event package functionality is supported; in this case
+ * the conference server ensures the radio is aware of the participants in the conference, which
+ * are represented by child connections.
+ * <p>
+ * An example of a conference with no child connections is an IMS conference call with no
+ * conference event package support. Such a conference is represented by the radio as a single
+ * connection to the IMS conference server.
+ * <p>
+ * Indicating whether a conference has children or not is important to help user interfaces
+ * visually represent a conference. A conference with no children, for example, will have the
+ * conference connection shown in the list of calls on a Bluetooth device, where if the
+ * conference has children, only the children will be shown in the list of calls on a Bluetooth
+ * device.
+ * @hide
+ */
+ public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 0x00200000;
+
+ //**********************************************************************************************
+ // Next CAPABILITY value: 0x00400000
+ //**********************************************************************************************
// Flag controlling whether PII is emitted into the logs
private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
@@ -224,23 +323,47 @@ public abstract class Connection implements IConferenceable {
if (can(capabilities, CAPABILITY_MANAGE_CONFERENCE)) {
builder.append(" CAPABILITY_MANAGE_CONFERENCE");
}
- if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL)) {
- builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL");
+ if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_RX)) {
+ builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_RX");
+ }
+ if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_TX)) {
+ builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_TX");
+ }
+ if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)) {
+ builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL");
+ }
+ if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_RX)) {
+ builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_RX");
+ }
+ if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_TX)) {
+ builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_TX");
}
- if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE)) {
- builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE");
+ if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
+ builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL");
}
if (can(capabilities, CAPABILITY_HIGH_DEF_AUDIO)) {
builder.append(" CAPABILITY_HIGH_DEF_AUDIO");
}
- if (can(capabilities, CAPABILITY_VoWIFI)) {
- builder.append(" CAPABILITY_VoWIFI");
+ if (can(capabilities, CAPABILITY_WIFI)) {
+ builder.append(" CAPABILITY_WIFI");
}
if (can(capabilities, CAPABILITY_GENERIC_CONFERENCE)) {
builder.append(" CAPABILITY_GENERIC_CONFERENCE");
}
+ if (can(capabilities, CAPABILITY_SHOW_CALLBACK_NUMBER)) {
+ builder.append(" CAPABILITY_SHOW_CALLBACK_NUMBER");
+ }
if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) {
- builder.append(" CAPABILITY_SPEED_UP_IMS_MT_AUDIO");
+ builder.append(" CAPABILITY_SPEED_UP_MT_AUDIO");
+ }
+ if (can(capabilities, CAPABILITY_CAN_UPGRADE_TO_VIDEO)) {
+ builder.append(" CAPABILITY_CAN_UPGRADE_TO_VIDEO");
+ }
+ if (can(capabilities, CAPABILITY_CAN_PAUSE_VIDEO)) {
+ builder.append(" CAPABILITY_CAN_PAUSE_VIDEO");
+ }
+ if (can(capabilities, CAPABILITY_CONFERENCE_HAS_NO_CHILDREN)) {
+ builder.append(" CAPABILITY_SINGLE_PARTY_CONFERENCE");
}
builder.append("]");
return builder.toString();
@@ -264,68 +387,106 @@ public abstract class Connection implements IConferenceable {
public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {}
public void onStatusHintsChanged(Connection c, StatusHints statusHints) {}
public void onConferenceablesChanged(
- Connection c, List<IConferenceable> conferenceables) {}
+ Connection c, List<Conferenceable> conferenceables) {}
public void onConferenceChanged(Connection c, Conference conference) {}
/** @hide */
public void onConferenceParticipantsChanged(Connection c,
List<ConferenceParticipant> participants) {}
public void onConferenceStarted() {}
+ public void onConferenceMergeFailed(Connection c) {}
+ public void onExtrasChanged(Connection c, Bundle extras) {}
}
- /** @hide */
+ /**
+ * Provides a means of controlling the video session associated with a {@link Connection}.
+ * <p>
+ * Implementations create a custom subclass of {@link VideoProvider} and the
+ * {@link ConnectionService} creates an instance sets it on the {@link Connection} using
+ * {@link Connection#setVideoProvider(VideoProvider)}. Any connection which supports video
+ * should set the {@link VideoProvider}.
+ * <p>
+ * The {@link VideoProvider} serves two primary purposes: it provides a means for Telecom and
+ * {@link InCallService} implementations to issue requests related to the video session;
+ * it provides a means for the {@link ConnectionService} to report events and information
+ * related to the video session to Telecom and the {@link InCallService} implementations.
+ * <p>
+ * {@link InCallService} implementations interact with the {@link VideoProvider} via
+ * {@link android.telecom.InCallService.VideoCall}.
+ */
public static abstract class VideoProvider {
/**
* Video is not being received (no protocol pause was issued).
+ * @see #handleCallSessionEvent(int)
*/
public static final int SESSION_EVENT_RX_PAUSE = 1;
/**
- * Video reception has resumed after a SESSION_EVENT_RX_PAUSE.
+ * Video reception has resumed after a {@link #SESSION_EVENT_RX_PAUSE}.
+ * @see #handleCallSessionEvent(int)
*/
public static final int SESSION_EVENT_RX_RESUME = 2;
/**
* Video transmission has begun. This occurs after a negotiated start of video transmission
* when the underlying protocol has actually begun transmitting video to the remote party.
+ * @see #handleCallSessionEvent(int)
*/
public static final int SESSION_EVENT_TX_START = 3;
/**
* Video transmission has stopped. This occurs after a negotiated stop of video transmission
* when the underlying protocol has actually stopped transmitting video to the remote party.
+ * @see #handleCallSessionEvent(int)
*/
public static final int SESSION_EVENT_TX_STOP = 4;
/**
- * A camera failure has occurred for the selected camera. The In-Call UI can use this as a
- * cue to inform the user the camera is not available.
+ * A camera failure has occurred for the selected camera. The {@link InCallService} can use
+ * this as a cue to inform the user the camera is not available.
+ * @see #handleCallSessionEvent(int)
*/
public static final int SESSION_EVENT_CAMERA_FAILURE = 5;
/**
- * Issued after {@code SESSION_EVENT_CAMERA_FAILURE} when the camera is once again ready for
- * operation. The In-Call UI can use this as a cue to inform the user that the camera has
- * become available again.
+ * Issued after {@link #SESSION_EVENT_CAMERA_FAILURE} when the camera is once again ready
+ * for operation. The {@link InCallService} can use this as a cue to inform the user that
+ * the camera has become available again.
+ * @see #handleCallSessionEvent(int)
*/
public static final int SESSION_EVENT_CAMERA_READY = 6;
/**
* Session modify request was successful.
+ * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
*/
public static final int SESSION_MODIFY_REQUEST_SUCCESS = 1;
/**
* Session modify request failed.
+ * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
*/
public static final int SESSION_MODIFY_REQUEST_FAIL = 2;
/**
* Session modify request ignored due to invalid parameters.
+ * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
*/
public static final int SESSION_MODIFY_REQUEST_INVALID = 3;
- private static final int MSG_SET_VIDEO_CALLBACK = 1;
+ /**
+ * Session modify request timed out.
+ * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
+ */
+ public static final int SESSION_MODIFY_REQUEST_TIMED_OUT = 4;
+
+ /**
+ * Session modify request rejected by remote user.
+ * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
+ */
+ public static final int SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE = 5;
+
+ private static final int MSG_ADD_VIDEO_CALLBACK = 1;
private static final int MSG_SET_CAMERA = 2;
private static final int MSG_SET_PREVIEW_SURFACE = 3;
private static final int MSG_SET_DISPLAY_SURFACE = 4;
@@ -336,22 +497,63 @@ public abstract class Connection implements IConferenceable {
private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 9;
private static final int MSG_REQUEST_CONNECTION_DATA_USAGE = 10;
private static final int MSG_SET_PAUSE_IMAGE = 11;
+ private static final int MSG_REMOVE_VIDEO_CALLBACK = 12;
- private final VideoProvider.VideoProviderHandler
- mMessageHandler = new VideoProvider.VideoProviderHandler();
+ private VideoProvider.VideoProviderHandler mMessageHandler;
private final VideoProvider.VideoProviderBinder mBinder;
- private IVideoCallback mVideoCallback;
+
+ /**
+ * Stores a list of the video callbacks, keyed by IBinder.
+ *
+ * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
+ * load factor before resizing, 1 means we only expect a single thread to
+ * access the map so make only a single shard
+ */
+ private ConcurrentHashMap<IBinder, IVideoCallback> mVideoCallbacks =
+ new ConcurrentHashMap<IBinder, IVideoCallback>(8, 0.9f, 1);
/**
* Default handler used to consolidate binder method calls onto a single thread.
*/
private final class VideoProviderHandler extends Handler {
+ public VideoProviderHandler() {
+ super();
+ }
+
+ public VideoProviderHandler(Looper looper) {
+ super(looper);
+ }
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_SET_VIDEO_CALLBACK:
- mVideoCallback = IVideoCallback.Stub.asInterface((IBinder) msg.obj);
+ case MSG_ADD_VIDEO_CALLBACK: {
+ IBinder binder = (IBinder) msg.obj;
+ IVideoCallback callback = IVideoCallback.Stub
+ .asInterface((IBinder) msg.obj);
+ if (callback == null) {
+ Log.w(this, "addVideoProvider - skipped; callback is null.");
+ break;
+ }
+
+ if (mVideoCallbacks.containsKey(binder)) {
+ Log.i(this, "addVideoProvider - skipped; already present.");
+ break;
+ }
+ mVideoCallbacks.put(binder, callback);
+ break;
+ }
+ case MSG_REMOVE_VIDEO_CALLBACK: {
+ IBinder binder = (IBinder) msg.obj;
+ IVideoCallback callback = IVideoCallback.Stub
+ .asInterface((IBinder) msg.obj);
+ if (!mVideoCallbacks.containsKey(binder)) {
+ Log.i(this, "removeVideoProvider - skipped; not present.");
+ break;
+ }
+ mVideoCallbacks.remove(binder);
break;
+ }
case MSG_SET_CAMERA:
onSetCamera((String) msg.obj);
break;
@@ -367,9 +569,16 @@ public abstract class Connection implements IConferenceable {
case MSG_SET_ZOOM:
onSetZoom((Float) msg.obj);
break;
- case MSG_SEND_SESSION_MODIFY_REQUEST:
- onSendSessionModifyRequest((VideoProfile) msg.obj);
+ case MSG_SEND_SESSION_MODIFY_REQUEST: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ onSendSessionModifyRequest((VideoProfile) args.arg1,
+ (VideoProfile) args.arg2);
+ } finally {
+ args.recycle();
+ }
break;
+ }
case MSG_SEND_SESSION_MODIFY_RESPONSE:
onSendSessionModifyResponse((VideoProfile) msg.obj);
break;
@@ -380,7 +589,7 @@ public abstract class Connection implements IConferenceable {
onRequestConnectionDataUsage();
break;
case MSG_SET_PAUSE_IMAGE:
- onSetPauseImage((String) msg.obj);
+ onSetPauseImage((Uri) msg.obj);
break;
default:
break;
@@ -392,9 +601,14 @@ public abstract class Connection implements IConferenceable {
* IVideoProvider stub implementation.
*/
private final class VideoProviderBinder extends IVideoProvider.Stub {
- public void setVideoCallback(IBinder videoCallbackBinder) {
+ public void addVideoCallback(IBinder videoCallbackBinder) {
mMessageHandler.obtainMessage(
- MSG_SET_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
+ MSG_ADD_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
+ }
+
+ public void removeVideoCallback(IBinder videoCallbackBinder) {
+ mMessageHandler.obtainMessage(
+ MSG_REMOVE_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
}
public void setCamera(String cameraId) {
@@ -410,16 +624,19 @@ public abstract class Connection implements IConferenceable {
}
public void setDeviceOrientation(int rotation) {
- mMessageHandler.obtainMessage(MSG_SET_DEVICE_ORIENTATION, rotation).sendToTarget();
+ mMessageHandler.obtainMessage(
+ MSG_SET_DEVICE_ORIENTATION, rotation, 0).sendToTarget();
}
public void setZoom(float value) {
mMessageHandler.obtainMessage(MSG_SET_ZOOM, value).sendToTarget();
}
- public void sendSessionModifyRequest(VideoProfile requestProfile) {
- mMessageHandler.obtainMessage(
- MSG_SEND_SESSION_MODIFY_REQUEST, requestProfile).sendToTarget();
+ public void sendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = fromProfile;
+ args.arg2 = toProfile;
+ mMessageHandler.obtainMessage(MSG_SEND_SESSION_MODIFY_REQUEST, args).sendToTarget();
}
public void sendSessionModifyResponse(VideoProfile responseProfile) {
@@ -435,13 +652,25 @@ public abstract class Connection implements IConferenceable {
mMessageHandler.obtainMessage(MSG_REQUEST_CONNECTION_DATA_USAGE).sendToTarget();
}
- public void setPauseImage(String uri) {
+ public void setPauseImage(Uri uri) {
mMessageHandler.obtainMessage(MSG_SET_PAUSE_IMAGE, uri).sendToTarget();
}
}
public VideoProvider() {
mBinder = new VideoProvider.VideoProviderBinder();
+ mMessageHandler = new VideoProvider.VideoProviderHandler(Looper.getMainLooper());
+ }
+
+ /**
+ * Creates an instance of the {@link VideoProvider}, specifying the looper to use.
+ *
+ * @param looper The looper.
+ * @hide
+ */
+ public VideoProvider(Looper looper) {
+ mBinder = new VideoProvider.VideoProviderBinder();
+ mMessageHandler = new VideoProvider.VideoProviderHandler(looper);
}
/**
@@ -453,9 +682,17 @@ public abstract class Connection implements IConferenceable {
}
/**
- * Sets the camera to be used for video recording in a video connection.
+ * Sets the camera to be used for the outgoing video.
+ * <p>
+ * The {@link VideoProvider} should respond by communicating the capabilities of the chosen
+ * camera via
+ * {@link VideoProvider#changeCameraCapabilities(VideoProfile.CameraCapabilities)}.
+ * <p>
+ * Sent from the {@link InCallService} via
+ * {@link InCallService.VideoCall#setCamera(String)}.
*
- * @param cameraId The id of the camera.
+ * @param cameraId The id of the camera (use ids as reported by
+ * {@link CameraManager#getCameraIdList()}).
*/
public abstract void onSetCamera(String cameraId);
@@ -463,21 +700,30 @@ public abstract class Connection implements IConferenceable {
* Sets the surface to be used for displaying a preview of what the user's camera is
* currently capturing. When video transmission is enabled, this is the video signal which
* is sent to the remote device.
+ * <p>
+ * Sent from the {@link InCallService} via
+ * {@link InCallService.VideoCall#setPreviewSurface(Surface)}.
*
- * @param surface The surface.
+ * @param surface The {@link Surface}.
*/
public abstract void onSetPreviewSurface(Surface surface);
/**
* Sets the surface to be used for displaying the video received from the remote device.
+ * <p>
+ * Sent from the {@link InCallService} via
+ * {@link InCallService.VideoCall#setDisplaySurface(Surface)}.
*
- * @param surface The surface.
+ * @param surface The {@link Surface}.
*/
public abstract void onSetDisplaySurface(Surface surface);
/**
* Sets the device orientation, in degrees. Assumes that a standard portrait orientation of
* the device is 0 degrees.
+ * <p>
+ * Sent from the {@link InCallService} via
+ * {@link InCallService.VideoCall#setDeviceOrientation(int)}.
*
* @param rotation The device orientation, in degrees.
*/
@@ -485,144 +731,281 @@ public abstract class Connection implements IConferenceable {
/**
* Sets camera zoom ratio.
+ * <p>
+ * Sent from the {@link InCallService} via {@link InCallService.VideoCall#setZoom(float)}.
*
* @param value The camera zoom ratio.
*/
public abstract void onSetZoom(float value);
/**
- * Issues a request to modify the properties of the current session. The request is
- * sent to the remote device where it it handled by the In-Call UI.
- * Some examples of session modification requests: upgrade connection from audio to video,
- * downgrade connection from video to audio, pause video.
+ * Issues a request to modify the properties of the current video session.
+ * <p>
+ * Example scenarios include: requesting an audio-only call to be upgraded to a
+ * bi-directional video call, turning on or off the user's camera, sending a pause signal
+ * when the {@link InCallService} is no longer the foreground application.
+ * <p>
+ * If the {@link VideoProvider} determines a request to be invalid, it should call
+ * {@link #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)} to report the
+ * invalid request back to the {@link InCallService}.
+ * <p>
+ * Where a request requires confirmation from the user of the peer device, the
+ * {@link VideoProvider} must communicate the request to the peer device and handle the
+ * user's response. {@link #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)}
+ * is used to inform the {@link InCallService} of the result of the request.
+ * <p>
+ * Sent from the {@link InCallService} via
+ * {@link InCallService.VideoCall#sendSessionModifyRequest(VideoProfile)}.
*
- * @param requestProfile The requested connection video properties.
+ * @param fromProfile The video profile prior to the request.
+ * @param toProfile The video profile with the requested changes made.
*/
- public abstract void onSendSessionModifyRequest(VideoProfile requestProfile);
+ public abstract void onSendSessionModifyRequest(VideoProfile fromProfile,
+ VideoProfile toProfile);
- /**te
- * Provides a response to a request to change the current connection session video
- * properties.
- * This is in response to a request the InCall UI has received via the InCall UI.
+ /**
+ * Provides a response to a request to change the current video session properties.
+ * <p>
+ * For example, if the peer requests and upgrade from an audio-only call to a bi-directional
+ * video call, could decline the request and keep the call as audio-only.
+ * In such a scenario, the {@code responseProfile} would have a video state of
+ * {@link VideoProfile#STATE_AUDIO_ONLY}. If the user had decided to accept the request,
+ * the video state would be {@link VideoProfile#STATE_BIDIRECTIONAL}.
+ * <p>
+ * Sent from the {@link InCallService} via
+ * {@link InCallService.VideoCall#sendSessionModifyResponse(VideoProfile)} in response to
+ * a {@link InCallService.VideoCall.Callback#onSessionModifyRequestReceived(VideoProfile)}
+ * callback.
*
- * @param responseProfile The response connection video properties.
+ * @param responseProfile The response video profile.
*/
public abstract void onSendSessionModifyResponse(VideoProfile responseProfile);
/**
- * Issues a request to the video provider to retrieve the camera capabilities.
- * Camera capabilities are reported back to the caller via the In-Call UI.
+ * Issues a request to the {@link VideoProvider} to retrieve the camera capabilities.
+ * <p>
+ * The {@link VideoProvider} should respond by communicating the capabilities of the chosen
+ * camera via
+ * {@link VideoProvider#changeCameraCapabilities(VideoProfile.CameraCapabilities)}.
+ * <p>
+ * Sent from the {@link InCallService} via
+ * {@link InCallService.VideoCall#requestCameraCapabilities()}.
*/
public abstract void onRequestCameraCapabilities();
/**
- * Issues a request to the video telephony framework to retrieve the cumulative data usage
- * for the current connection. Data usage is reported back to the caller via the
- * InCall UI.
+ * Issues a request to the {@link VideoProvider} to retrieve the current data usage for the
+ * video component of the current {@link Connection}.
+ * <p>
+ * The {@link VideoProvider} should respond by communicating current data usage, in bytes,
+ * via {@link VideoProvider#setCallDataUsage(long)}.
+ * <p>
+ * Sent from the {@link InCallService} via
+ * {@link InCallService.VideoCall#requestCallDataUsage()}.
*/
public abstract void onRequestConnectionDataUsage();
/**
- * Provides the video telephony framework with the URI of an image to be displayed to remote
- * devices when the video signal is paused.
+ * Provides the {@link VideoProvider} with the {@link Uri} of an image to be displayed to
+ * the peer device when the video signal is paused.
+ * <p>
+ * Sent from the {@link InCallService} via
+ * {@link InCallService.VideoCall#setPauseImage(Uri)}.
*
* @param uri URI of image to display.
*/
- public abstract void onSetPauseImage(String uri);
+ public abstract void onSetPauseImage(Uri uri);
/**
- * Invokes callback method defined in In-Call UI.
+ * Used to inform listening {@link InCallService} implementations when the
+ * {@link VideoProvider} receives a session modification request.
+ * <p>
+ * Received by the {@link InCallService} via
+ * {@link InCallService.VideoCall.Callback#onSessionModifyRequestReceived(VideoProfile)},
*
- * @param videoProfile The requested video connection profile.
+ * @param videoProfile The requested video profile.
+ * @see #onSendSessionModifyRequest(VideoProfile, VideoProfile)
*/
public void receiveSessionModifyRequest(VideoProfile videoProfile) {
- if (mVideoCallback != null) {
- try {
- mVideoCallback.receiveSessionModifyRequest(videoProfile);
- } catch (RemoteException ignored) {
+ if (mVideoCallbacks != null) {
+ for (IVideoCallback callback : mVideoCallbacks.values()) {
+ try {
+ callback.receiveSessionModifyRequest(videoProfile);
+ } catch (RemoteException ignored) {
+ Log.w(this, "receiveSessionModifyRequest callback failed", ignored);
+ }
}
}
}
/**
- * Invokes callback method defined in In-Call UI.
+ * Used to inform listening {@link InCallService} implementations when the
+ * {@link VideoProvider} receives a response to a session modification request.
+ * <p>
+ * Received by the {@link InCallService} via
+ * {@link InCallService.VideoCall.Callback#onSessionModifyResponseReceived(int,
+ * VideoProfile, VideoProfile)}.
*
* @param status Status of the session modify request. Valid values are
* {@link VideoProvider#SESSION_MODIFY_REQUEST_SUCCESS},
* {@link VideoProvider#SESSION_MODIFY_REQUEST_FAIL},
- * {@link VideoProvider#SESSION_MODIFY_REQUEST_INVALID}
- * @param requestedProfile The original request which was sent to the remote device.
- * @param responseProfile The actual profile changes made by the remote device.
+ * {@link VideoProvider#SESSION_MODIFY_REQUEST_INVALID},
+ * {@link VideoProvider#SESSION_MODIFY_REQUEST_TIMED_OUT},
+ * {@link VideoProvider#SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE}
+ * @param requestedProfile The original request which was sent to the peer device.
+ * @param responseProfile The actual profile changes agreed to by the peer device.
+ * @see #onSendSessionModifyRequest(VideoProfile, VideoProfile)
*/
public void receiveSessionModifyResponse(int status,
VideoProfile requestedProfile, VideoProfile responseProfile) {
- if (mVideoCallback != null) {
- try {
- mVideoCallback.receiveSessionModifyResponse(
- status, requestedProfile, responseProfile);
- } catch (RemoteException ignored) {
+ if (mVideoCallbacks != null) {
+ for (IVideoCallback callback : mVideoCallbacks.values()) {
+ try {
+ callback.receiveSessionModifyResponse(status, requestedProfile,
+ responseProfile);
+ } catch (RemoteException ignored) {
+ Log.w(this, "receiveSessionModifyResponse callback failed", ignored);
+ }
}
}
}
/**
- * Invokes callback method defined in In-Call UI.
+ * Used to inform listening {@link InCallService} implementations when the
+ * {@link VideoProvider} reports a call session event.
+ * <p>
+ * Received by the {@link InCallService} via
+ * {@link InCallService.VideoCall.Callback#onCallSessionEvent(int)}.
*
- * Valid values are: {@link VideoProvider#SESSION_EVENT_RX_PAUSE},
- * {@link VideoProvider#SESSION_EVENT_RX_RESUME},
- * {@link VideoProvider#SESSION_EVENT_TX_START},
- * {@link VideoProvider#SESSION_EVENT_TX_STOP}
- *
- * @param event The event.
+ * @param event The event. Valid values are: {@link VideoProvider#SESSION_EVENT_RX_PAUSE},
+ * {@link VideoProvider#SESSION_EVENT_RX_RESUME},
+ * {@link VideoProvider#SESSION_EVENT_TX_START},
+ * {@link VideoProvider#SESSION_EVENT_TX_STOP},
+ * {@link VideoProvider#SESSION_EVENT_CAMERA_FAILURE},
+ * {@link VideoProvider#SESSION_EVENT_CAMERA_READY}.
*/
public void handleCallSessionEvent(int event) {
- if (mVideoCallback != null) {
- try {
- mVideoCallback.handleCallSessionEvent(event);
- } catch (RemoteException ignored) {
+ if (mVideoCallbacks != null) {
+ for (IVideoCallback callback : mVideoCallbacks.values()) {
+ try {
+ callback.handleCallSessionEvent(event);
+ } catch (RemoteException ignored) {
+ Log.w(this, "handleCallSessionEvent callback failed", ignored);
+ }
}
}
}
/**
- * Invokes callback method defined in In-Call UI.
+ * Used to inform listening {@link InCallService} implementations when the dimensions of the
+ * peer's video have changed.
+ * <p>
+ * This could occur if, for example, the peer rotates their device, changing the aspect
+ * ratio of the video, or if the user switches between the back and front cameras.
+ * <p>
+ * Received by the {@link InCallService} via
+ * {@link InCallService.VideoCall.Callback#onPeerDimensionsChanged(int, int)}.
*
* @param width The updated peer video width.
* @param height The updated peer video height.
*/
public void changePeerDimensions(int width, int height) {
- if (mVideoCallback != null) {
- try {
- mVideoCallback.changePeerDimensions(width, height);
- } catch (RemoteException ignored) {
+ if (mVideoCallbacks != null) {
+ for (IVideoCallback callback : mVideoCallbacks.values()) {
+ try {
+ callback.changePeerDimensions(width, height);
+ } catch (RemoteException ignored) {
+ Log.w(this, "changePeerDimensions callback failed", ignored);
+ }
+ }
+ }
+ }
+
+ /**
+ * Used to inform listening {@link InCallService} implementations when the data usage of the
+ * video associated with the current {@link Connection} has changed.
+ * <p>
+ * This could be in response to a preview request via
+ * {@link #onRequestConnectionDataUsage()}, or as a periodic update by the
+ * {@link VideoProvider}. Where periodic updates of data usage are provided, they should be
+ * provided at most for every 1 MB of data transferred and no more than once every 10 sec.
+ * <p>
+ * Received by the {@link InCallService} via
+ * {@link InCallService.VideoCall.Callback#onCallDataUsageChanged(long)}.
+ *
+ * @param dataUsage The updated data usage (in bytes). Reported as the cumulative bytes
+ * used since the start of the call.
+ */
+ public void setCallDataUsage(long dataUsage) {
+ if (mVideoCallbacks != null) {
+ for (IVideoCallback callback : mVideoCallbacks.values()) {
+ try {
+ callback.changeCallDataUsage(dataUsage);
+ } catch (RemoteException ignored) {
+ Log.w(this, "setCallDataUsage callback failed", ignored);
+ }
}
}
}
/**
- * Invokes callback method defined in In-Call UI.
+ * @see #setCallDataUsage(long)
+ *
+ * @param dataUsage The updated data usage (in byes).
+ * @deprecated - Use {@link #setCallDataUsage(long)} instead.
+ * @hide
+ */
+ public void changeCallDataUsage(long dataUsage) {
+ setCallDataUsage(dataUsage);
+ }
+
+ /**
+ * Used to inform listening {@link InCallService} implementations when the capabilities of
+ * the current camera have changed.
+ * <p>
+ * The {@link VideoProvider} should call this in response to
+ * {@link VideoProvider#onRequestCameraCapabilities()}, or when the current camera is
+ * changed via {@link VideoProvider#onSetCamera(String)}.
+ * <p>
+ * Received by the {@link InCallService} via
+ * {@link InCallService.VideoCall.Callback#onCameraCapabilitiesChanged(
+ * VideoProfile.CameraCapabilities)}.
*
- * @param dataUsage The updated data usage.
+ * @param cameraCapabilities The new camera capabilities.
*/
- public void changeCallDataUsage(int dataUsage) {
- if (mVideoCallback != null) {
- try {
- mVideoCallback.changeCallDataUsage(dataUsage);
- } catch (RemoteException ignored) {
+ public void changeCameraCapabilities(VideoProfile.CameraCapabilities cameraCapabilities) {
+ if (mVideoCallbacks != null) {
+ for (IVideoCallback callback : mVideoCallbacks.values()) {
+ try {
+ callback.changeCameraCapabilities(cameraCapabilities);
+ } catch (RemoteException ignored) {
+ Log.w(this, "changeCameraCapabilities callback failed", ignored);
+ }
}
}
}
/**
- * Invokes callback method defined in In-Call UI.
+ * Used to inform listening {@link InCallService} implementations when the video quality
+ * of the call has changed.
+ * <p>
+ * Received by the {@link InCallService} via
+ * {@link InCallService.VideoCall.Callback#onVideoQualityChanged(int)}.
*
- * @param cameraCapabilities The changed camera capabilities.
+ * @param videoQuality The updated video quality. Valid values:
+ * {@link VideoProfile#QUALITY_HIGH},
+ * {@link VideoProfile#QUALITY_MEDIUM},
+ * {@link VideoProfile#QUALITY_LOW},
+ * {@link VideoProfile#QUALITY_DEFAULT}.
*/
- public void changeCameraCapabilities(CameraCapabilities cameraCapabilities) {
- if (mVideoCallback != null) {
- try {
- mVideoCallback.changeCameraCapabilities(cameraCapabilities);
- } catch (RemoteException ignored) {
+ public void changeVideoQuality(int videoQuality) {
+ if (mVideoCallbacks != null) {
+ for (IVideoCallback callback : mVideoCallbacks.values()) {
+ try {
+ callback.changeVideoQuality(videoQuality);
+ } catch (RemoteException ignored) {
+ Log.w(this, "changeVideoQuality callback failed", ignored);
+ }
}
}
}
@@ -653,12 +1036,12 @@ public abstract class Connection implements IConferenceable {
*/
private final Set<Listener> mListeners = Collections.newSetFromMap(
new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
- private final List<IConferenceable> mConferenceables = new ArrayList<>();
- private final List<IConferenceable> mUnmodifiableConferenceables =
+ private final List<Conferenceable> mConferenceables = new ArrayList<>();
+ private final List<Conferenceable> mUnmodifiableConferenceables =
Collections.unmodifiableList(mConferenceables);
private int mState = STATE_NEW;
- private AudioState mAudioState;
+ private CallAudioState mCallAudioState;
private Uri mAddress;
private int mAddressPresentation;
private String mCallerDisplayName;
@@ -672,6 +1055,7 @@ public abstract class Connection implements IConferenceable {
private DisconnectCause mDisconnectCause;
private Conference mConference;
private ConnectionService mConnectionService;
+ private Bundle mExtras;
/**
* Create a new Connection.
@@ -717,10 +1101,10 @@ public abstract class Connection implements IConferenceable {
/**
* Returns the video state of the connection.
- * Valid values: {@link VideoProfile.VideoState#AUDIO_ONLY},
- * {@link VideoProfile.VideoState#BIDIRECTIONAL},
- * {@link VideoProfile.VideoState#TX_ENABLED},
- * {@link VideoProfile.VideoState#RX_ENABLED}.
+ * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
+ * {@link VideoProfile#STATE_BIDIRECTIONAL},
+ * {@link VideoProfile#STATE_TX_ENABLED},
+ * {@link VideoProfile#STATE_RX_ENABLED}.
*
* @return The video state of the connection.
* @hide
@@ -733,9 +1117,25 @@ public abstract class Connection implements IConferenceable {
* @return The audio state of the connection, describing how its audio is currently
* being routed by the system. This is {@code null} if this Connection
* does not directly know about its audio state.
+ * @deprecated Use {@link #getCallAudioState()} instead.
+ * @hide
*/
+ @SystemApi
+ @Deprecated
public final AudioState getAudioState() {
- return mAudioState;
+ if (mCallAudioState == null) {
+ return null;
+ }
+ return new AudioState(mCallAudioState);
+ }
+
+ /**
+ * @return The audio state of the connection, describing how its audio is currently
+ * being routed by the system. This is {@code null} if this Connection
+ * does not directly know about its audio state.
+ */
+ public final CallAudioState getCallAudioState() {
+ return mCallAudioState;
}
/**
@@ -769,6 +1169,13 @@ public abstract class Connection implements IConferenceable {
}
/**
+ * @return The extras associated with this connection.
+ */
+ public final Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
* Assign a listener to be notified of state changes.
*
* @param l A listener.
@@ -809,11 +1216,12 @@ public abstract class Connection implements IConferenceable {
* @param state The new audio state.
* @hide
*/
- final void setAudioState(AudioState state) {
+ final void setCallAudioState(CallAudioState state) {
checkImmutable();
Log.d(this, "setAudioState %s", state);
- mAudioState = state;
- onAudioStateChanged(state);
+ mCallAudioState = state;
+ onAudioStateChanged(getAudioState());
+ onCallAudioStateChanged(state);
}
/**
@@ -849,11 +1257,6 @@ public abstract class Connection implements IConferenceable {
return mConnectionCapabilities;
}
- /** @hide */
- @SystemApi @Deprecated public final int getCallCapabilities() {
- return getConnectionCapabilities();
- }
-
/**
* Sets the value of the {@link #getAddress()} property.
*
@@ -890,13 +1293,12 @@ public abstract class Connection implements IConferenceable {
/**
* Set the video state for the connection.
- * Valid values: {@link VideoProfile.VideoState#AUDIO_ONLY},
- * {@link VideoProfile.VideoState#BIDIRECTIONAL},
- * {@link VideoProfile.VideoState#TX_ENABLED},
- * {@link VideoProfile.VideoState#RX_ENABLED}.
+ * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
+ * {@link VideoProfile#STATE_BIDIRECTIONAL},
+ * {@link VideoProfile#STATE_TX_ENABLED},
+ * {@link VideoProfile#STATE_RX_ENABLED}.
*
* @param videoState The new video state.
- * @hide
*/
public final void setVideoState(int videoState) {
checkImmutable();
@@ -960,7 +1362,6 @@ public abstract class Connection implements IConferenceable {
/**
* Sets the video connection provider.
* @param videoProvider The video provider.
- * @hide
*/
public final void setVideoProvider(VideoProvider videoProvider) {
checkImmutable();
@@ -970,7 +1371,6 @@ public abstract class Connection implements IConferenceable {
}
}
- /** @hide */
public final VideoProvider getVideoProvider() {
return mVideoProvider;
}
@@ -1011,14 +1411,11 @@ public abstract class Connection implements IConferenceable {
/**
* Informs listeners that this {@code Connection} has processed a character in the post-dial
* started state. This is done when (a) the {@code Connection} is issuing a DTMF sequence;
- * (b) it has encountered a "wait" character; and (c) it wishes to signal Telecom to play
- * the corresponding DTMF tone locally.
+ * and (b) it wishes to signal Telecom to play the corresponding DTMF tone locally.
*
* @param nextChar The DTMF character that was just processed by the {@code Connection}.
- *
- * @hide
*/
- public final void setNextPostDialWaitChar(char nextChar) {
+ public final void setNextPostDialChar(char nextChar) {
checkImmutable();
for (Listener l : mListeners) {
l.onPostDialChar(this, nextChar);
@@ -1041,11 +1438,6 @@ public abstract class Connection implements IConferenceable {
}
}
- /** @hide */
- @SystemApi @Deprecated public final void setCallCapabilities(int connectionCapabilities) {
- setConnectionCapabilities(connectionCapabilities);
- }
-
/**
* Sets the connection's capabilities as a bit mask of the {@code CAPABILITY_*} constants.
*
@@ -1121,9 +1513,9 @@ public abstract class Connection implements IConferenceable {
*
* @param conferenceables The conferenceables.
*/
- public final void setConferenceables(List<IConferenceable> conferenceables) {
+ public final void setConferenceables(List<Conferenceable> conferenceables) {
clearConferenceableList();
- for (IConferenceable c : conferenceables) {
+ for (Conferenceable c : conferenceables) {
// If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
// small amount of items here.
if (!mConferenceables.contains(c)) {
@@ -1143,7 +1535,7 @@ public abstract class Connection implements IConferenceable {
/**
* Returns the connections or conferences with which this connection can be conferenced.
*/
- public final List<IConferenceable> getConferenceables() {
+ public final List<Conferenceable> getConferenceables() {
return mUnmodifiableConferenceables;
}
@@ -1213,13 +1605,39 @@ public abstract class Connection implements IConferenceable {
}
/**
+ * Set some extras that can be associated with this {@code Connection}. No assumptions should
+ * be made as to how an In-Call UI or service will handle these extras.
+ * Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
+ *
+ * @param extras The extras associated with this {@code Connection}.
+ */
+ public final void setExtras(@Nullable Bundle extras) {
+ checkImmutable();
+ mExtras = extras;
+ for (Listener l : mListeners) {
+ l.onExtrasChanged(this, extras);
+ }
+ }
+
+ /**
* Notifies this Connection that the {@link #getAudioState()} property has a new value.
*
* @param state The new connection audio state.
+ * @deprecated Use {@link #onCallAudioStateChanged(CallAudioState)} instead.
+ * @hide
*/
+ @SystemApi
+ @Deprecated
public void onAudioStateChanged(AudioState state) {}
/**
+ * Notifies this Connection that the {@link #getCallAudioState()} property has a new value.
+ *
+ * @param state The new connection audio state.
+ */
+ public void onCallAudioStateChanged(CallAudioState state) {}
+
+ /**
* Notifies this Connection of an internal state change. This method is called after the
* state is changed.
*
@@ -1278,7 +1696,6 @@ public abstract class Connection implements IConferenceable {
* a request to accept.
*
* @param videoState The video state in which to answer the connection.
- * @hide
*/
public void onAnswer(int videoState) {}
@@ -1287,7 +1704,7 @@ public abstract class Connection implements IConferenceable {
* a request to accept.
*/
public void onAnswer() {
- onAnswer(VideoProfile.VideoState.AUDIO_ONLY);
+ onAnswer(VideoProfile.STATE_AUDIO_ONLY);
}
/**
@@ -1408,7 +1825,7 @@ public abstract class Connection implements IConferenceable {
}
private final void clearConferenceableList() {
- for (IConferenceable c : mConferenceables) {
+ for (Conferenceable c : mConferenceables) {
if (c instanceof Connection) {
Connection connection = (Connection) c;
connection.removeConnectionListener(mConnectionDeathListener);
@@ -1421,6 +1838,17 @@ public abstract class Connection implements IConferenceable {
}
/**
+ * Notifies listeners that the merge request failed.
+ *
+ * @hide
+ */
+ protected final void notifyConferenceMergeFailed() {
+ for (Listener l : mListeners) {
+ l.onConferenceMergeFailed(this);
+ }
+ }
+
+ /**
* Notifies listeners of a change to conference participant(s).
*
* @param conferenceParticipants The participants.
@@ -1435,6 +1863,7 @@ public abstract class Connection implements IConferenceable {
/**
* Notifies listeners that a conference call has been started.
+ * @hide
*/
protected void notifyConferenceStarted() {
for (Listener l : mListeners) {
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index f691c17..6863214 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -16,7 +16,6 @@
package android.telecom;
-import android.annotation.SystemApi;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
@@ -25,9 +24,7 @@ import android.os.Parcelable;
/**
* Simple data container encapsulating a request to some entity to
* create a new {@link Connection}.
- * @hide
*/
-@SystemApi
public final class ConnectionRequest implements Parcelable {
// TODO: Token to limit recursive invocations
@@ -45,7 +42,7 @@ public final class ConnectionRequest implements Parcelable {
PhoneAccountHandle accountHandle,
Uri handle,
Bundle extras) {
- this(accountHandle, handle, extras, VideoProfile.VideoState.AUDIO_ONLY);
+ this(accountHandle, handle, extras, VideoProfile.STATE_AUDIO_ONLY);
}
/**
@@ -53,7 +50,6 @@ public final class ConnectionRequest implements Parcelable {
* @param handle The handle (e.g., phone number) to which the {@link Connection} is to connect.
* @param extras Application-specific extra data.
* @param videoState Determines the video state for the connection.
- * @hide
*/
public ConnectionRequest(
PhoneAccountHandle accountHandle,
@@ -92,13 +88,12 @@ public final class ConnectionRequest implements Parcelable {
/**
* Describes the video states supported by the client requesting the connection.
- * Valid values: {@link VideoProfile.VideoState#AUDIO_ONLY},
- * {@link VideoProfile.VideoState#BIDIRECTIONAL},
- * {@link VideoProfile.VideoState#TX_ENABLED},
- * {@link VideoProfile.VideoState#RX_ENABLED}.
+ * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
+ * {@link VideoProfile#STATE_BIDIRECTIONAL},
+ * {@link VideoProfile#STATE_TX_ENABLED},
+ * {@link VideoProfile#STATE_RX_ENABLED}.
*
* @return The video state for the connection.
- * @hide
*/
public int getVideoState() {
return mVideoState;
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index dfdc3e1..d2e7a74 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -17,11 +17,11 @@
package android.telecom;
import android.annotation.SdkConstant;
-import android.annotation.SystemApi;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -41,8 +41,8 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
- * {@code ConnectionService} is an abstract service that should be implemented by any app which can
- * make phone calls and want those calls to be integrated into the built-in phone app.
+ * An abstract service that should be implemented by any apps which can make phone calls (VoIP or
+ * otherwise) and want those calls to be integrated into the built-in phone app.
* Once implemented, the {@code ConnectionService} needs two additional steps before it will be
* integrated into the phone app:
* <p>
@@ -51,7 +51,7 @@ import java.util.concurrent.ConcurrentHashMap;
* <pre>
* &lt;service android:name="com.example.package.MyConnectionService"
* android:label="@string/some_label_for_my_connection_service"
- * android:permission="android.permission.BIND_CONNECTION_SERVICE"&gt;
+ * android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"&gt;
* &lt;intent-filter&gt;
* &lt;action android:name="android.telecom.ConnectionService" /&gt;
* &lt;/intent-filter&gt;
@@ -62,7 +62,7 @@ import java.util.concurrent.ConcurrentHashMap;
* <br/>
* See {@link PhoneAccount} and {@link TelecomManager#registerPhoneAccount} for more information.
* <p>
- * Once registered and enabled by the user in the dialer settings, telecom will bind to a
+ * Once registered and enabled by the user in the phone app settings, telecom will bind to a
* {@code ConnectionService} implementation when it wants that {@code ConnectionService} to place
* a call or the service has indicated that is has an incoming call through
* {@link TelecomManager#addNewIncomingCall}. The {@code ConnectionService} can then expect a call
@@ -72,9 +72,7 @@ import java.util.concurrent.ConcurrentHashMap;
* receives call-commands such as answer, reject, hold and disconnect.
* <p>
* When there are no more live calls, telecom will unbind from the {@code ConnectionService}.
- * @hide
*/
-@SystemApi
public abstract class ConnectionService extends Service {
/**
* The {@link Intent} that must be declared as handled by the service.
@@ -93,7 +91,7 @@ public abstract class ConnectionService extends Service {
private static final int MSG_DISCONNECT = 6;
private static final int MSG_HOLD = 7;
private static final int MSG_UNHOLD = 8;
- private static final int MSG_ON_AUDIO_STATE_CHANGED = 9;
+ private static final int MSG_ON_CALL_AUDIO_STATE_CHANGED = 9;
private static final int MSG_PLAY_DTMF_TONE = 10;
private static final int MSG_STOP_DTMF_TONE = 11;
private static final int MSG_CONFERENCE = 12;
@@ -150,7 +148,6 @@ public abstract class ConnectionService extends Service {
}
@Override
- /** @hide */
public void answerVideo(String callId, int videoState) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = callId;
@@ -184,11 +181,11 @@ public abstract class ConnectionService extends Service {
}
@Override
- public void onAudioStateChanged(String callId, AudioState audioState) {
+ public void onCallAudioStateChanged(String callId, CallAudioState callAudioState) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = callId;
- args.arg2 = audioState;
- mHandler.obtainMessage(MSG_ON_AUDIO_STATE_CHANGED, args).sendToTarget();
+ args.arg2 = callAudioState;
+ mHandler.obtainMessage(MSG_ON_CALL_AUDIO_STATE_CHANGED, args).sendToTarget();
}
@Override
@@ -308,12 +305,12 @@ public abstract class ConnectionService extends Service {
case MSG_UNHOLD:
unhold((String) msg.obj);
break;
- case MSG_ON_AUDIO_STATE_CHANGED: {
+ case MSG_ON_CALL_AUDIO_STATE_CHANGED: {
SomeArgs args = (SomeArgs) msg.obj;
try {
String callId = (String) args.arg1;
- AudioState audioState = (AudioState) args.arg2;
- onAudioStateChanged(callId, audioState);
+ CallAudioState audioState = (CallAudioState) args.arg2;
+ onCallAudioStateChanged(callId, new CallAudioState(audioState));
} finally {
args.recycle();
}
@@ -415,6 +412,33 @@ public abstract class ConnectionService extends Service {
Connection.capabilitiesToString(connectionCapabilities));
mAdapter.setConnectionCapabilities(id, connectionCapabilities);
}
+
+ @Override
+ public void onVideoStateChanged(Conference c, int videoState) {
+ String id = mIdByConference.get(c);
+ Log.d(this, "onVideoStateChanged set video state %d", videoState);
+ mAdapter.setVideoState(id, videoState);
+ }
+
+ @Override
+ public void onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider) {
+ String id = mIdByConference.get(c);
+ Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c,
+ videoProvider);
+ mAdapter.setVideoProvider(id, videoProvider);
+ }
+
+ @Override
+ public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {
+ String id = mIdByConference.get(conference);
+ mAdapter.setStatusHints(id, statusHints);
+ }
+
+ @Override
+ public void onExtrasChanged(Conference conference, Bundle extras) {
+ String id = mIdByConference.get(conference);
+ mAdapter.setExtras(id, extras);
+ }
};
private final Connection.Listener mConnectionListener = new Connection.Listener() {
@@ -508,6 +532,8 @@ public abstract class ConnectionService extends Service {
@Override
public void onVideoProviderChanged(Connection c, Connection.VideoProvider videoProvider) {
String id = mIdByConnection.get(c);
+ Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c,
+ videoProvider);
mAdapter.setVideoProvider(id, videoProvider);
}
@@ -525,7 +551,7 @@ public abstract class ConnectionService extends Service {
@Override
public void onConferenceablesChanged(
- Connection connection, List<IConferenceable> conferenceables) {
+ Connection connection, List<Conferenceable> conferenceables) {
mAdapter.setConferenceableConnections(
mIdByConnection.get(connection),
createIdList(conferenceables));
@@ -542,6 +568,22 @@ public abstract class ConnectionService extends Service {
mAdapter.setIsConferenced(id, conferenceId);
}
}
+
+ @Override
+ public void onConferenceMergeFailed(Connection connection) {
+ String id = mIdByConnection.get(connection);
+ if (id != null) {
+ mAdapter.onConferenceMergeFailed(id);
+ }
+ }
+
+ @Override
+ public void onExtrasChanged(Connection connection, Bundle extras) {
+ String id = mIdByConnection.get(connection);
+ if (id != null) {
+ mAdapter.setExtras(id, extras);
+ }
+ }
};
/** {@inheritDoc} */
@@ -611,7 +653,8 @@ public abstract class ConnectionService extends Service {
connection.getAudioModeIsVoip(),
connection.getStatusHints(),
connection.getDisconnectCause(),
- createIdList(connection.getConferenceables())));
+ createIdList(connection.getConferenceables()),
+ connection.getExtras()));
}
private void abort(String callId) {
@@ -661,12 +704,14 @@ public abstract class ConnectionService extends Service {
}
}
- private void onAudioStateChanged(String callId, AudioState audioState) {
- Log.d(this, "onAudioStateChanged %s %s", callId, audioState);
+ private void onCallAudioStateChanged(String callId, CallAudioState callAudioState) {
+ Log.d(this, "onAudioStateChanged %s %s", callId, callAudioState);
if (mConnectionById.containsKey(callId)) {
- findConnectionForAction(callId, "onAudioStateChanged").setAudioState(audioState);
+ findConnectionForAction(callId, "onCallAudioStateChanged").setCallAudioState(
+ callAudioState);
} else {
- findConferenceForAction(callId, "onAudioStateChanged").setAudioState(audioState);
+ findConferenceForAction(callId, "onCallAudioStateChanged").setCallAudioState(
+ callAudioState);
}
}
@@ -871,6 +916,8 @@ public abstract class ConnectionService extends Service {
* @param conference The new conference object.
*/
public final void addConference(Conference conference) {
+ Log.d(this, "addConference: conference=%s", conference);
+
String id = addConferenceInternal(conference);
if (id != null) {
List<String> connectionIds = new ArrayList<>(2);
@@ -884,8 +931,16 @@ public abstract class ConnectionService extends Service {
conference.getState(),
conference.getConnectionCapabilities(),
connectionIds,
- conference.getConnectTimeMillis());
+ conference.getVideoProvider() == null ?
+ null : conference.getVideoProvider().getInterface(),
+ conference.getVideoState(),
+ conference.getConnectTimeMillis(),
+ conference.getStatusHints(),
+ conference.getExtras());
+
mAdapter.addConferenceCall(id, parcelableConference);
+ mAdapter.setVideoProvider(id, conference.getVideoProvider());
+ mAdapter.setVideoState(id, conference.getVideoState());
// Go through any child calls and set the parent.
for (Connection connection : conference.getConnections()) {
@@ -926,7 +981,8 @@ public abstract class ConnectionService extends Service {
connection.getAudioModeIsVoip(),
connection.getStatusHints(),
connection.getDisconnectCause(),
- emptyList);
+ emptyList,
+ connection.getExtras());
mAdapter.addExistingConnection(id, parcelableConnection);
}
}
@@ -1146,14 +1202,14 @@ public abstract class ConnectionService extends Service {
/**
* Builds a list of {@link Connection} and {@link Conference} IDs based on the list of
- * {@link IConferenceable}s passed in.
+ * {@link Conferenceable}s passed in.
*
- * @param conferenceables The {@link IConferenceable} connections and conferences.
+ * @param conferenceables The {@link Conferenceable} connections and conferences.
* @return List of string conference and call Ids.
*/
- private List<String> createIdList(List<IConferenceable> conferenceables) {
+ private List<String> createIdList(List<Conferenceable> conferenceables) {
List<String> ids = new ArrayList<>();
- for (IConferenceable c : conferenceables) {
+ for (Conferenceable c : conferenceables) {
// Only allow Connection and Conference conferenceables.
if (c instanceof Connection) {
Connection connection = (Connection) c;
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index d026a28..4562514 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -17,6 +17,7 @@
package android.telecom;
import android.net.Uri;
+import android.os.Bundle;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
@@ -47,6 +48,12 @@ final class ConnectionServiceAdapter implements DeathRecipient {
}
void addAdapter(IConnectionServiceAdapter adapter) {
+ for (IConnectionServiceAdapter it : mAdapters) {
+ if (it.asBinder() == adapter.asBinder()) {
+ Log.w(this, "Ignoring duplicate adapter addition.");
+ return;
+ }
+ }
if (mAdapters.add(adapter)) {
try {
adapter.asBinder().linkToDeath(this, 0);
@@ -57,8 +64,13 @@ final class ConnectionServiceAdapter implements DeathRecipient {
}
void removeAdapter(IConnectionServiceAdapter adapter) {
- if (adapter != null && mAdapters.remove(adapter)) {
- adapter.asBinder().unlinkToDeath(this, 0);
+ if (adapter != null) {
+ for (IConnectionServiceAdapter it : mAdapters) {
+ if (it.asBinder() == adapter.asBinder() && mAdapters.remove(it)) {
+ adapter.asBinder().unlinkToDeath(this, 0);
+ break;
+ }
+ }
}
}
@@ -203,6 +215,21 @@ final class ConnectionServiceAdapter implements DeathRecipient {
}
/**
+ * Indicates that the merge request on this call has failed.
+ *
+ * @param callId The unique ID of the call being conferenced.
+ */
+ void onConferenceMergeFailed(String callId) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ Log.d(this, "merge failed for call %s", callId);
+ adapter.setConferenceMergeFailed(callId);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /**
* Indicates that the call no longer exists. Can be used with either a call or a conference
* call.
*
@@ -326,10 +353,10 @@ final class ConnectionServiceAdapter implements DeathRecipient {
/**
* Sets the video state associated with a call.
*
- * Valid values: {@link VideoProfile.VideoState#AUDIO_ONLY},
- * {@link VideoProfile.VideoState#BIDIRECTIONAL},
- * {@link VideoProfile.VideoState#TX_ENABLED},
- * {@link VideoProfile.VideoState#RX_ENABLED}.
+ * Valid values: {@link VideoProfile#STATE_BIDIRECTIONAL},
+ * {@link VideoProfile#STATE_AUDIO_ONLY},
+ * {@link VideoProfile#STATE_TX_ENABLED},
+ * {@link VideoProfile#STATE_RX_ENABLED}.
*
* @param callId The unique ID of the call to set the video state for.
* @param videoState The video state.
@@ -369,4 +396,20 @@ final class ConnectionServiceAdapter implements DeathRecipient {
}
}
}
+
+ /**
+ * Sets extras associated with a connection.
+ *
+ * @param callId The unique ID of the call.
+ * @param extras The extras to associate with this call.
+ */
+ void setExtras(String callId, Bundle extras) {
+ Log.v(this, "setExtras: %s", extras);
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.setExtras(callId, extras);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
}
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index 429f296..293dc11 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -17,6 +17,7 @@
package android.telecom;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
@@ -59,6 +60,8 @@ final class ConnectionServiceAdapterServant {
private static final int MSG_SET_CONFERENCEABLE_CONNECTIONS = 20;
private static final int MSG_ADD_EXISTING_CONNECTION = 21;
private static final int MSG_ON_POST_DIAL_CHAR = 22;
+ private static final int MSG_SET_CONFERENCE_MERGE_FAILED = 23;
+ private static final int MSG_SET_EXTRAS = 24;
private final IConnectionServiceAdapter mDelegate;
@@ -220,6 +223,23 @@ final class ConnectionServiceAdapterServant {
}
break;
}
+ case MSG_SET_CONFERENCE_MERGE_FAILED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.setConferenceMergeFailed((String) args.arg1);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_SET_EXTRAS: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.setExtras((String) args.arg1, (Bundle) args.arg2);
+ } finally {
+ args.recycle();
+ }
+ }
}
}
};
@@ -280,6 +300,13 @@ final class ConnectionServiceAdapterServant {
}
@Override
+ public void setConferenceMergeFailed(String callId) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ mHandler.obtainMessage(MSG_SET_CONFERENCE_MERGE_FAILED, args).sendToTarget();
+ }
+
+ @Override
public void setIsConferenced(String callId, String conferenceCallId) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = callId;
@@ -384,6 +411,14 @@ final class ConnectionServiceAdapterServant {
args.arg2 = connection;
mHandler.obtainMessage(MSG_ADD_EXISTING_CONNECTION, args).sendToTarget();
}
+
+ @Override
+ public final void setExtras(String connectionId, Bundle extras) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = connectionId;
+ args.arg2 = extras;
+ mHandler.obtainMessage(MSG_SET_EXTRAS, args).sendToTarget();
+ }
};
public ConnectionServiceAdapterServant(IConnectionServiceAdapter delegate) {
diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java
new file mode 100644
index 0000000..3d49308
--- /dev/null
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.telecom;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class for managing the default dialer application that will receive incoming calls, and be
+ * allowed to make emergency outgoing calls.
+ *
+ * @hide
+ */
+public class DefaultDialerManager {
+ private static final String TAG = "DefaultDialerManager";
+
+ /**
+ * Sets the specified package name as the default dialer application for the current user.
+ * The caller of this method needs to have permission to write to secure settings and
+ * manage users on the device.
+ *
+ * @return {@code true} if the default dialer application was successfully changed,
+ * {@code false} otherwise.
+ *
+ * @hide
+ * */
+ public static boolean setDefaultDialerApplication(Context context, String packageName) {
+ return setDefaultDialerApplication(context, packageName, ActivityManager.getCurrentUser());
+ }
+
+ /**
+ * Sets the specified package name as the default dialer application for the specified user.
+ * The caller of this method needs to have permission to write to secure settings and
+ * manage users on the device.
+ *
+ * @return {@code true} if the default dialer application was successfully changed,
+ * {@code false} otherwise.
+ *
+ * @hide
+ * */
+ public static boolean setDefaultDialerApplication(Context context, String packageName,
+ int user) {
+ // Get old package name
+ String oldPackageName = Settings.Secure.getStringForUser(context.getContentResolver(),
+ Settings.Secure.DIALER_DEFAULT_APPLICATION, user);
+
+ if (packageName != null && oldPackageName != null && packageName.equals(oldPackageName)) {
+ // No change
+ return false;
+ }
+
+ // Only make the change if the new package belongs to a valid phone application
+ List<String> packageNames = getInstalledDialerApplications(context);
+
+ if (packageNames.contains(packageName)) {
+ // Update the secure setting.
+ Settings.Secure.putStringForUser(context.getContentResolver(),
+ Settings.Secure.DIALER_DEFAULT_APPLICATION, packageName, user);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the installed dialer application for the current user that will be used to receive
+ * incoming calls, and is allowed to make emergency calls.
+ *
+ * The application will be returned in order of preference:
+ * 1) User selected phone application (if still installed)
+ * 2) Pre-installed system dialer (if not disabled)
+ * 3) Null
+ *
+ * The caller of this method needs to have permission to manage users on the device.
+ *
+ * @hide
+ * */
+ public static String getDefaultDialerApplication(Context context) {
+ return getDefaultDialerApplication(context, ActivityManager.getCurrentUser());
+ }
+
+ /**
+ * Returns the installed dialer application for the specified user that will be used to receive
+ * incoming calls, and is allowed to make emergency calls.
+ *
+ * The application will be returned in order of preference:
+ * 1) User selected phone application (if still installed)
+ * 2) Pre-installed system dialer (if not disabled)
+ * 3) Null
+ *
+ * The caller of this method needs to have permission to manage users on the device.
+ *
+ * @hide
+ * */
+ public static String getDefaultDialerApplication(Context context, int user) {
+ String defaultPackageName = Settings.Secure.getStringForUser(context.getContentResolver(),
+ Settings.Secure.DIALER_DEFAULT_APPLICATION, user);
+
+ final List<String> packageNames = getInstalledDialerApplications(context);
+
+ // Verify that the default dialer has not been disabled or uninstalled.
+ if (packageNames.contains(defaultPackageName)) {
+ return defaultPackageName;
+ }
+
+ // No user-set dialer found, fallback to system dialer
+ String systemDialerPackageName = getTelecomManager(context).getSystemDialerPackage();
+
+ if (TextUtils.isEmpty(systemDialerPackageName)) {
+ // No system dialer configured at build time
+ return null;
+ }
+
+ if (packageNames.contains(systemDialerPackageName)) {
+ return systemDialerPackageName;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns a list of installed and available dialer applications.
+ *
+ * In order to appear in the list, a dialer application must implement an intent-filter with
+ * the DIAL intent for the following schemes:
+ *
+ * 1) Empty scheme
+ * 2) tel Uri scheme
+ *
+ * @hide
+ **/
+ public static List<String> getInstalledDialerApplications(Context context) {
+ PackageManager packageManager = context.getPackageManager();
+
+ // Get the list of apps registered for the DIAL intent with empty scheme
+ Intent intent = new Intent(Intent.ACTION_DIAL);
+ List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, 0);
+
+ List<String> packageNames = new ArrayList<>();
+
+ for (ResolveInfo resolveInfo : resolveInfoList) {
+ final ActivityInfo activityInfo = resolveInfo.activityInfo;
+ if (activityInfo != null && !packageNames.contains(activityInfo.packageName)) {
+ packageNames.add(activityInfo.packageName);
+ }
+ }
+
+ final Intent dialIntentWithTelScheme = new Intent(Intent.ACTION_DIAL);
+ dialIntentWithTelScheme.setData(Uri.fromParts(PhoneAccount.SCHEME_TEL, "", null));
+ return filterByIntent(context, packageNames, dialIntentWithTelScheme);
+ }
+
+ /**
+ * Determines if the package name belongs to the user-selected default dialer or the preloaded
+ * system dialer, and thus should be allowed to perform certain privileged operations.
+ *
+ * @param context A valid context.
+ * @param packageName of the package to check for.
+ *
+ * @return {@code true} if the provided package name corresponds to the user-selected default
+ * dialer or the preloaded system dialer, {@code false} otherwise.
+ *
+ * @hide
+ */
+ public static boolean isDefaultOrSystemDialer(Context context, String packageName) {
+ if (TextUtils.isEmpty(packageName)) {
+ return false;
+ }
+ final TelecomManager tm = getTelecomManager(context);
+ return packageName.equals(tm.getDefaultDialerPackage())
+ || packageName.equals(tm.getSystemDialerPackage());
+ }
+
+ /**
+ * Filter a given list of package names for those packages that contain an activity that has
+ * an intent filter for a given intent.
+ *
+ * @param context A valid context
+ * @param packageNames List of package names to filter.
+ * @return The filtered list.
+ */
+ private static List<String> filterByIntent(Context context, List<String> packageNames,
+ Intent intent) {
+ if (packageNames == null || packageNames.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ final List<String> result = new ArrayList<>();
+ final List<ResolveInfo> resolveInfoList =
+ context.getPackageManager().queryIntentActivities(intent, 0);
+ final int length = resolveInfoList.size();
+ for (int i = 0; i < length; i++) {
+ final ActivityInfo info = resolveInfoList.get(i).activityInfo;
+ if (info != null && packageNames.contains(info.packageName)
+ && !result.contains(info.packageName)) {
+ result.add(info.packageName);
+ }
+ }
+
+ return result;
+ }
+
+
+ private static TelecomManager getTelecomManager(Context context) {
+ return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
+ }
+}
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 130d676..3a7faf6 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -16,7 +16,6 @@
package android.telecom;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.media.ToneGenerator;
@@ -30,9 +29,7 @@ import java.util.Objects;
* user. It is the responsibility of the {@link ConnectionService} to provide localized versions of
* the label and description. It also may contain a reason for the disconnect, which is intended for
* logging and not for display to the user.
- * @hide
*/
-@SystemApi
public final class DisconnectCause implements Parcelable {
/** Disconnected because of an unknown or unspecified reason. */
@@ -133,8 +130,10 @@ public final class DisconnectCause implements Parcelable {
/**
* Returns a short label which explains the reason for the disconnect cause and is for display
- * in the user interface. The {@link ConnectionService } is responsible for providing and
- * localizing this label. If there is no string provided, returns null.
+ * in the user interface. If not null, it is expected that the In-Call UI should display this
+ * text where it would normally display the call state ("Dialing", "Disconnected") and is
+ * therefore expected to be relatively small. The {@link ConnectionService } is responsible for
+ * providing and localizing this label. If there is no string provided, returns null.
*
* @return The disconnect label.
*/
@@ -144,8 +143,11 @@ public final class DisconnectCause implements Parcelable {
/**
* Returns a description which explains the reason for the disconnect cause and is for display
- * in the user interface. The {@link ConnectionService } is responsible for providing and
- * localizing this message. If there is no string provided, returns null.
+ * in the user interface. This optional text is generally a longer and more descriptive version
+ * of {@link #getLabel}, however it can exist even if {@link #getLabel} is empty. The In-Call UI
+ * should display this relatively prominently; the traditional implementation displays this as
+ * an alert dialog. The {@link ConnectionService} is responsible for providing and localizing
+ * this message. If there is no string provided, returns null.
*
* @return The disconnect description.
*/
diff --git a/telecomm/java/android/telecom/GatewayInfo.java b/telecomm/java/android/telecom/GatewayInfo.java
index 5b8e4ab..928570e 100644
--- a/telecomm/java/android/telecom/GatewayInfo.java
+++ b/telecomm/java/android/telecom/GatewayInfo.java
@@ -16,7 +16,6 @@
package android.telecom;
-import android.annotation.SystemApi;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
@@ -34,17 +33,13 @@ import android.text.TextUtils;
* <li> Call the appropriate gateway address.
* <li> Display information about how the call is being routed to the user.
* </ol>
- * @hide
*/
-@SystemApi
public class GatewayInfo implements Parcelable {
private final String mGatewayProviderPackageName;
private final Uri mGatewayAddress;
private final Uri mOriginalAddress;
- /** @hide */
- @SystemApi
public GatewayInfo(String packageName, Uri gatewayUri, Uri originalAddress) {
mGatewayProviderPackageName = packageName;
mGatewayAddress = gatewayUri;
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index 62b8dea..0cf7212 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -119,7 +119,7 @@ public final class InCallAdapter {
}
/**
- * Sets the audio route (speaker, bluetooth, etc...). See {@link AudioState}.
+ * Sets the audio route (speaker, bluetooth, etc...). See {@link CallAudioState}.
*
* @param route The audio route to use.
*/
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index a85e84d..19c613d 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -16,10 +16,12 @@
package android.telecom;
-import android.annotation.SystemApi;
import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
+import android.hardware.camera2.CameraManager;
+import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -31,15 +33,31 @@ import com.android.internal.telecom.IInCallAdapter;
import com.android.internal.telecom.IInCallService;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
/**
* This service is implemented by any app that wishes to provide the user-interface for managing
* phone calls. Telecom binds to this service while there exists a live (active or incoming) call,
- * and uses it to notify the in-call app of any live and and recently disconnected calls.
- *
- * {@hide}
+ * and uses it to notify the in-call app of any live and recently disconnected calls. An app must
+ * first be set as the default phone app (See {@link TelecomManager#getDefaultDialerPackage()})
+ * before the telecom service will bind to its {@code InCallService} implementation.
+ * <p>
+ * Below is an example manifest registration for an {@code InCallService}. The meta-data
+ * ({@link TelecomManager#METADATA_IN_CALL_SERVICE_UI}) indicates that this particular
+ * {@code InCallService} implementation intends to replace the built-in in-call UI.
+ * <pre>
+ * {@code
+ * &lt;service android:name="your.package.YourInCallServiceImplementation"
+ * android:permission="android.permission.BIND_IN_CALL_SERVICE"&gt;
+ * &lt;meta-data android:name="android.telecom.IN_CALL_SERVICE_UI" android:value="true" /&gt;
+ * &lt;intent-filter&gt;
+ * &lt;action android:name="android.telecom.InCallService"/&gt;
+ * &lt;/intent-filter&gt;
+ * &lt;/service&gt;
+ * }
+ * </pre>
*/
-@SystemApi
public abstract class InCallService extends Service {
/**
@@ -52,7 +70,7 @@ public abstract class InCallService extends Service {
private static final int MSG_ADD_CALL = 2;
private static final int MSG_UPDATE_CALL = 3;
private static final int MSG_SET_POST_DIAL_WAIT = 4;
- private static final int MSG_ON_AUDIO_STATE_CHANGED = 5;
+ private static final int MSG_ON_CALL_AUDIO_STATE_CHANGED = 5;
private static final int MSG_BRING_TO_FOREGROUND = 6;
private static final int MSG_ON_CAN_ADD_CALL_CHANGED = 7;
@@ -67,6 +85,7 @@ public abstract class InCallService extends Service {
switch (msg.what) {
case MSG_SET_IN_CALL_ADAPTER:
mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj));
+ mPhone.addListener(mPhoneListener);
onPhoneCreated(mPhone);
break;
case MSG_ADD_CALL:
@@ -86,8 +105,8 @@ public abstract class InCallService extends Service {
}
break;
}
- case MSG_ON_AUDIO_STATE_CHANGED:
- mPhone.internalAudioStateChanged((AudioState) msg.obj);
+ case MSG_ON_CALL_AUDIO_STATE_CHANGED:
+ mPhone.internalCallAudioStateChanged((CallAudioState) msg.obj);
break;
case MSG_BRING_TO_FOREGROUND:
mPhone.internalBringToForeground(msg.arg1 == 1);
@@ -132,8 +151,8 @@ public abstract class InCallService extends Service {
}
@Override
- public void onAudioStateChanged(AudioState audioState) {
- mHandler.obtainMessage(MSG_ON_AUDIO_STATE_CHANGED, audioState).sendToTarget();
+ public void onCallAudioStateChanged(CallAudioState callAudioState) {
+ mHandler.obtainMessage(MSG_ON_CALL_AUDIO_STATE_CHANGED, callAudioState).sendToTarget();
}
@Override
@@ -148,6 +167,43 @@ public abstract class InCallService extends Service {
}
}
+ private Phone.Listener mPhoneListener = new Phone.Listener() {
+ /** ${inheritDoc} */
+ @Override
+ public void onAudioStateChanged(Phone phone, AudioState audioState) {
+ InCallService.this.onAudioStateChanged(audioState);
+ }
+
+ public void onCallAudioStateChanged(Phone phone, CallAudioState callAudioState) {
+ InCallService.this.onCallAudioStateChanged(callAudioState);
+ };
+
+ /** ${inheritDoc} */
+ @Override
+ public void onBringToForeground(Phone phone, boolean showDialpad) {
+ InCallService.this.onBringToForeground(showDialpad);
+ }
+
+ /** ${inheritDoc} */
+ @Override
+ public void onCallAdded(Phone phone, Call call) {
+ InCallService.this.onCallAdded(call);
+ }
+
+ /** ${inheritDoc} */
+ @Override
+ public void onCallRemoved(Phone phone, Call call) {
+ InCallService.this.onCallRemoved(call);
+ }
+
+ /** ${inheritDoc} */
+ @Override
+ public void onCanAddCallChanged(Phone phone, boolean canAddCall) {
+ InCallService.this.onCanAddCallChanged(canAddCall);
+ }
+
+ };
+
private Phone mPhone;
public InCallService() {
@@ -165,8 +221,14 @@ public abstract class InCallService extends Service {
mPhone = null;
oldPhone.destroy();
+ // destroy sets all the calls to disconnected if any live ones still exist. Therefore,
+ // it is important to remove the Listener *after* the call to destroy so that
+ // InCallService.on* callbacks are appropriately called.
+ oldPhone.removeListener(mPhoneListener);
+
onPhoneDestroyed(oldPhone);
}
+
return false;
}
@@ -176,19 +238,92 @@ public abstract class InCallService extends Service {
* @return The {@code Phone} object associated with this {@code InCallService}, or {@code null}
* if the {@code InCallService} is not in a state where it has an associated
* {@code Phone}.
+ * @hide
+ * @deprecated Use direct methods on InCallService instead of {@link Phone}.
*/
+ @SystemApi
+ @Deprecated
public Phone getPhone() {
return mPhone;
}
/**
+ * Obtains the current list of {@code Call}s to be displayed by this in-call service.
+ *
+ * @return A list of the relevant {@code Call}s.
+ */
+ public final List<Call> getCalls() {
+ return mPhone == null ? Collections.<Call>emptyList() : mPhone.getCalls();
+ }
+
+ /**
+ * Returns if the device can support additional calls.
+ *
+ * @return Whether the phone supports adding more calls.
+ */
+ public final boolean canAddCall() {
+ return mPhone == null ? false : mPhone.canAddCall();
+ }
+
+ /**
+ * Obtains the current phone call audio state.
+ *
+ * @return An object encapsulating the audio state. Returns null if the service is not
+ * fully initialized.
+ * @deprecated Use {@link #getCallAudioState()} instead.
+ * @hide
+ */
+ @Deprecated
+ public final AudioState getAudioState() {
+ return mPhone == null ? null : mPhone.getAudioState();
+ }
+
+ /**
+ * Obtains the current phone call audio state.
+ *
+ * @return An object encapsulating the audio state. Returns null if the service is not
+ * fully initialized.
+ */
+ public final CallAudioState getCallAudioState() {
+ return mPhone == null ? null : mPhone.getCallAudioState();
+ }
+
+ /**
+ * Sets the microphone mute state. When this request is honored, there will be change to
+ * the {@link #getCallAudioState()}.
+ *
+ * @param state {@code true} if the microphone should be muted; {@code false} otherwise.
+ */
+ public final void setMuted(boolean state) {
+ if (mPhone != null) {
+ mPhone.setMuted(state);
+ }
+ }
+
+ /**
+ * Sets the audio route (speaker, bluetooth, etc...). When this request is honored, there will
+ * be change to the {@link #getCallAudioState()}.
+ *
+ * @param route The audio route to use.
+ */
+ public final void setAudioRoute(int route) {
+ if (mPhone != null) {
+ mPhone.setAudioRoute(route);
+ }
+ }
+
+ /**
* 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
* of the {@code InCallService}.
*
* @param phone The {@code Phone} object associated with this {@code InCallService}.
+ * @hide
+ * @deprecated Use direct methods on InCallService instead of {@link Phone}.
*/
+ @SystemApi
+ @Deprecated
public void onPhoneCreated(Phone phone) {
}
@@ -199,28 +334,114 @@ public abstract class InCallService extends Service {
* call to {@link #onPhoneCreated(Phone)}.
*
* @param phone The {@code Phone} object associated with this {@code InCallService}.
+ * @hide
+ * @deprecated Use direct methods on InCallService instead of {@link Phone}.
*/
+ @SystemApi
+ @Deprecated
public void onPhoneDestroyed(Phone phone) {
}
/**
- * Class to invoke functionality related to video calls.
+ * Called when the audio state changes.
+ *
+ * @param audioState The new {@link AudioState}.
+ * @deprecated Use {@link #onCallAudioStateChanged(CallAudioState) instead}.
* @hide
*/
+ @Deprecated
+ public void onAudioStateChanged(AudioState audioState) {
+ }
+
+ /**
+ * Called when the audio state changes.
+ *
+ * @param audioState The new {@link CallAudioState}.
+ */
+ public void onCallAudioStateChanged(CallAudioState audioState) {
+ }
+
+ /**
+ * Called to bring the in-call screen to the foreground. The in-call experience should
+ * respond immediately by coming to the foreground to inform the user of the state of
+ * ongoing {@code Call}s.
+ *
+ * @param showDialpad If true, put up the dialpad when the screen is shown.
+ */
+ public void onBringToForeground(boolean showDialpad) {
+ }
+
+ /**
+ * Called when a {@code Call} has been added to this in-call session. The in-call user
+ * experience should add necessary state listeners to the specified {@code Call} and
+ * immediately start to show the user information about the existence
+ * and nature of this {@code Call}. Subsequent invocations of {@link #getCalls()} will
+ * include this {@code Call}.
+ *
+ * @param call A newly added {@code Call}.
+ */
+ public void onCallAdded(Call call) {
+ }
+
+ /**
+ * Called when a {@code Call} has been removed from this in-call session. The in-call user
+ * experience should remove any state listeners from the specified {@code Call} and
+ * immediately stop displaying any information about this {@code Call}.
+ * Subsequent invocations of {@link #getCalls()} will no longer include this {@code Call}.
+ *
+ * @param call A newly removed {@code Call}.
+ */
+ public void onCallRemoved(Call call) {
+ }
+
+ /**
+ * Called when the ability to add more calls changes. If the phone cannot
+ * support more calls then {@code canAddCall} is set to {@code false}. If it can, then it
+ * is set to {@code true}. This can be used to control the visibility of UI to add more calls.
+ *
+ * @param canAddCall Indicates whether an additional call can be added.
+ */
+ public void onCanAddCallChanged(boolean canAddCall) {
+ }
+
+ /**
+ * Used to issue commands to the {@link Connection.VideoProvider} associated with a
+ * {@link Call}.
+ */
public static abstract class VideoCall {
+ /** @hide */
+ public abstract void destroy();
+
/**
- * Sets a listener to invoke callback methods in the InCallUI after performing video
- * telephony actions.
+ * Registers a callback to receive commands and state changes for video calls.
*
- * @param videoCallListener The call video client.
+ * @param callback The video call callback.
*/
- public abstract void setVideoCallListener(VideoCall.Listener videoCallListener);
+ public abstract void registerCallback(VideoCall.Callback callback);
/**
- * Sets the camera to be used for video recording in a video call.
+ * Registers a callback to receive commands and state changes for video calls.
*
- * @param cameraId The id of the camera.
+ * @param callback The video call callback.
+ * @param handler A handler which commands and status changes will be delivered to.
+ */
+ public abstract void registerCallback(VideoCall.Callback callback, Handler handler);
+
+ /**
+ * Clears the video call callback set via {@link #registerCallback}.
+ *
+ * @param callback The video call callback to clear.
+ */
+ public abstract void unregisterCallback(VideoCall.Callback callback);
+
+ /**
+ * Sets the camera to be used for the outgoing video.
+ * <p>
+ * Handled by {@link Connection.VideoProvider#onSetCamera(String)}.
+ *
+ * @param cameraId The id of the camera (use ids as reported by
+ * {@link CameraManager#getCameraIdList()}).
*/
public abstract void setCamera(String cameraId);
@@ -228,21 +449,27 @@ public abstract class InCallService extends Service {
* Sets the surface to be used for displaying a preview of what the user's camera is
* currently capturing. When video transmission is enabled, this is the video signal which
* is sent to the remote device.
+ * <p>
+ * Handled by {@link Connection.VideoProvider#onSetPreviewSurface(Surface)}.
*
- * @param surface The surface.
+ * @param surface The {@link Surface}.
*/
public abstract void setPreviewSurface(Surface surface);
/**
* Sets the surface to be used for displaying the video received from the remote device.
+ * <p>
+ * Handled by {@link Connection.VideoProvider#onSetDisplaySurface(Surface)}.
*
- * @param surface The surface.
+ * @param surface The {@link Surface}.
*/
public abstract void setDisplaySurface(Surface surface);
/**
* Sets the device orientation, in degrees. Assumes that a standard portrait orientation of
* the device is 0 degrees.
+ * <p>
+ * Handled by {@link Connection.VideoProvider#onSetDeviceOrientation(int)}.
*
* @param rotation The device orientation, in degrees.
*/
@@ -250,110 +477,146 @@ public abstract class InCallService extends Service {
/**
* Sets camera zoom ratio.
+ * <p>
+ * Handled by {@link Connection.VideoProvider#onSetZoom(float)}.
*
* @param value The camera zoom ratio.
*/
public abstract void setZoom(float value);
/**
- * Issues a request to modify the properties of the current session. The request is sent to
- * the remote device where it it handled by
- * {@link VideoCall.Listener#onSessionModifyRequestReceived}.
- * Some examples of session modification requests: upgrade call from audio to video,
- * downgrade call from video to audio, pause video.
+ * Issues a request to modify the properties of the current video session.
+ * <p>
+ * Example scenarios include: requesting an audio-only call to be upgraded to a
+ * bi-directional video call, turning on or off the user's camera, sending a pause signal
+ * when the {@link InCallService} is no longer the foreground application.
+ * <p>
+ * Handled by
+ * {@link Connection.VideoProvider#onSendSessionModifyRequest(VideoProfile, VideoProfile)}.
*
* @param requestProfile The requested call video properties.
*/
public abstract void sendSessionModifyRequest(VideoProfile requestProfile);
/**
- * Provides a response to a request to change the current call session video
- * properties.
- * This is in response to a request the InCall UI has received via
- * {@link VideoCall.Listener#onSessionModifyRequestReceived}.
- * The response is handled on the remove device by
- * {@link VideoCall.Listener#onSessionModifyResponseReceived}.
+ * Provides a response to a request to change the current call video session
+ * properties. This should be called in response to a request the {@link InCallService} has
+ * received via {@link VideoCall.Callback#onSessionModifyRequestReceived}.
+ * <p>
+ * Handled by
+ * {@link Connection.VideoProvider#onSendSessionModifyResponse(VideoProfile)}.
*
* @param responseProfile The response call video properties.
*/
public abstract void sendSessionModifyResponse(VideoProfile responseProfile);
/**
- * Issues a request to the video provider to retrieve the camera capabilities.
- * Camera capabilities are reported back to the caller via
- * {@link VideoCall.Listener#onCameraCapabilitiesChanged(CameraCapabilities)}.
+ * Issues a request to the {@link Connection.VideoProvider} to retrieve the capabilities
+ * of the current camera. The current camera is selected using
+ * {@link VideoCall#setCamera(String)}.
+ * <p>
+ * Camera capabilities are reported to the caller via
+ * {@link VideoCall.Callback#onCameraCapabilitiesChanged(VideoProfile.CameraCapabilities)}.
+ * <p>
+ * Handled by {@link Connection.VideoProvider#onRequestCameraCapabilities()}.
*/
public abstract void requestCameraCapabilities();
/**
- * Issues a request to the video telephony framework to retrieve the cumulative data usage for
- * the current call. Data usage is reported back to the caller via
- * {@link VideoCall.Listener#onCallDataUsageChanged}.
+ * Issues a request to the {@link Connection.VideoProvider} to retrieve the cumulative data
+ * usage for the video component of the current call (in bytes). Data usage is reported
+ * to the caller via {@link VideoCall.Callback#onCallDataUsageChanged}.
+ * <p>
+ * Handled by {@link Connection.VideoProvider#onRequestConnectionDataUsage()}.
*/
public abstract void requestCallDataUsage();
/**
- * Provides the video telephony framework with the URI of an image to be displayed to remote
- * devices when the video signal is paused.
+ * Provides the {@link Connection.VideoProvider} with the {@link Uri} of an image to be
+ * displayed to the peer device when the video signal is paused.
+ * <p>
+ * Handled by {@link Connection.VideoProvider#onSetPauseImage(Uri)}.
*
* @param uri URI of image to display.
*/
- public abstract void setPauseImage(String uri);
+ public abstract void setPauseImage(Uri uri);
/**
- * Listener class which invokes callbacks after video call actions occur.
- * @hide
+ * The {@link InCallService} extends this class to provide a means of receiving callbacks
+ * from the {@link Connection.VideoProvider}.
+ * <p>
+ * When the {@link InCallService} receives the
+ * {@link Call.Callback#onVideoCallChanged(Call, VideoCall)} callback, it should create an
+ * instance its {@link VideoCall.Callback} implementation and set it on the
+ * {@link VideoCall} using {@link VideoCall#registerCallback(Callback)}.
*/
- public static abstract class Listener {
+ public static abstract class Callback {
/**
- * Called when a session modification request is received from the remote device.
- * The remote request is sent via
- * {@link Connection.VideoProvider#onSendSessionModifyRequest}. The InCall UI
- * is responsible for potentially prompting the user whether they wish to accept the new
- * call profile (e.g. prompt user if they wish to accept an upgrade from an audio to a
- * video call) and should call
- * {@link Connection.VideoProvider#onSendSessionModifyResponse} to indicate
- * the video settings the user has agreed to.
+ * Called when the {@link Connection.VideoProvider} receives a session modification
+ * request from the peer device.
+ * <p>
+ * The {@link InCallService} may potentially prompt the user to confirm whether they
+ * wish to accept the request, or decide to automatically accept the request. In either
+ * case the {@link InCallService} should call
+ * {@link VideoCall#sendSessionModifyResponse(VideoProfile)} to indicate the video
+ * profile agreed upon.
+ * <p>
+ * Callback originates from
+ * {@link Connection.VideoProvider#receiveSessionModifyRequest(VideoProfile)}.
*
- * @param videoProfile The requested video call profile.
+ * @param videoProfile The requested video profile.
*/
public abstract void onSessionModifyRequestReceived(VideoProfile videoProfile);
/**
- * Called when a response to a session modification request is received from the remote
- * device. The remote InCall UI sends the response using
- * {@link Connection.VideoProvider#onSendSessionModifyResponse}.
+ * Called when the {@link Connection.VideoProvider} receives a response to a session
+ * modification request previously sent to the peer device.
+ * <p>
+ * The new video state should not be considered active by the {@link InCallService}
+ * until the {@link Call} video state changes (the
+ * {@link Call.Callback#onDetailsChanged(Call, Call.Details)} callback is triggered
+ * when the video state changes).
+ * <p>
+ * Callback originates from
+ * {@link Connection.VideoProvider#receiveSessionModifyResponse(int, VideoProfile,
+ * VideoProfile)}.
*
* @param status Status of the session modify request. Valid values are
- * {@link Connection.VideoProvider#SESSION_MODIFY_REQUEST_SUCCESS},
- * {@link Connection.VideoProvider#SESSION_MODIFY_REQUEST_FAIL},
- * {@link Connection.VideoProvider#SESSION_MODIFY_REQUEST_INVALID}
- * @param requestedProfile The original request which was sent to the remote device.
- * @param responseProfile The actual profile changes made by the remote device.
+ * {@link Connection.VideoProvider#SESSION_MODIFY_REQUEST_SUCCESS},
+ * {@link Connection.VideoProvider#SESSION_MODIFY_REQUEST_FAIL},
+ * {@link Connection.VideoProvider#SESSION_MODIFY_REQUEST_INVALID},
+ * {@link Connection.VideoProvider#SESSION_MODIFY_REQUEST_TIMED_OUT},
+ * {@link Connection.VideoProvider#SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE}.
+ * @param requestedProfile The original request which was sent to the peer device.
+ * @param responseProfile The actual profile changes made by the peer device.
*/
public abstract void onSessionModifyResponseReceived(int status,
VideoProfile requestedProfile, VideoProfile responseProfile);
/**
- * Handles events related to the current session which the client may wish to handle.
- * These are separate from requested changes to the session due to the underlying
- * protocol or connection.
- *
- * Valid values are:
- * {@link Connection.VideoProvider#SESSION_EVENT_RX_PAUSE},
- * {@link Connection.VideoProvider#SESSION_EVENT_RX_RESUME},
- * {@link Connection.VideoProvider#SESSION_EVENT_TX_START},
- * {@link Connection.VideoProvider#SESSION_EVENT_TX_STOP},
- * {@link Connection.VideoProvider#SESSION_EVENT_CAMERA_FAILURE},
- * {@link Connection.VideoProvider#SESSION_EVENT_CAMERA_READY}
+ * Handles events related to the current video session which the {@link InCallService}
+ * may wish to handle. These are separate from requested changes to the session due to
+ * the underlying protocol or connection.
+ * <p>
+ * Callback originates from
+ * {@link Connection.VideoProvider#handleCallSessionEvent(int)}.
*
- * @param event The event.
+ * @param event The event. Valid values are:
+ * {@link Connection.VideoProvider#SESSION_EVENT_RX_PAUSE},
+ * {@link Connection.VideoProvider#SESSION_EVENT_RX_RESUME},
+ * {@link Connection.VideoProvider#SESSION_EVENT_TX_START},
+ * {@link Connection.VideoProvider#SESSION_EVENT_TX_STOP},
+ * {@link Connection.VideoProvider#SESSION_EVENT_CAMERA_FAILURE},
+ * {@link Connection.VideoProvider#SESSION_EVENT_CAMERA_READY}.
*/
public abstract void onCallSessionEvent(int event);
/**
- * Handles a change to the video dimensions from the remote caller (peer). This could
- * happen if, for example, the peer changes orientation of their device.
+ * Handles a change to the video dimensions from the peer device. This could happen if,
+ * for example, the peer changes orientation of their device, or switches cameras.
+ * <p>
+ * Callback originates from
+ * {@link Connection.VideoProvider#changePeerDimensions(int, int)}.
*
* @param width The updated peer video width.
* @param height The updated peer video height.
@@ -361,19 +624,47 @@ public abstract class InCallService extends Service {
public abstract void onPeerDimensionsChanged(int width, int height);
/**
- * Handles an update to the total data used for the current session.
+ * Handles a change to the video quality.
+ * <p>
+ * Callback originates from {@link Connection.VideoProvider#changeVideoQuality(int)}.
+ *
+ * @param videoQuality The updated peer video quality. Valid values:
+ * {@link VideoProfile#QUALITY_HIGH},
+ * {@link VideoProfile#QUALITY_MEDIUM},
+ * {@link VideoProfile#QUALITY_LOW},
+ * {@link VideoProfile#QUALITY_DEFAULT}.
+ */
+ public abstract void onVideoQualityChanged(int videoQuality);
+
+ /**
+ * Handles an update to the total data used for the current video session.
+ * <p>
+ * Used by the {@link Connection.VideoProvider} in response to
+ * {@link VideoCall#requestCallDataUsage()}. May also be called periodically by the
+ * {@link Connection.VideoProvider}.
+ * <p>
+ * Callback originates from {@link Connection.VideoProvider#setCallDataUsage(long)}.
*
- * @param dataUsage The updated data usage.
+ * @param dataUsage The updated data usage (in bytes).
*/
- public abstract void onCallDataUsageChanged(int dataUsage);
+ public abstract void onCallDataUsageChanged(long dataUsage);
/**
- * Handles a change in camera capabilities.
+ * Handles a change in the capabilities of the currently selected camera.
+ * <p>
+ * Used by the {@link Connection.VideoProvider} in response to
+ * {@link VideoCall#requestCameraCapabilities()}. The {@link Connection.VideoProvider}
+ * may also report the camera capabilities after a call to
+ * {@link VideoCall#setCamera(String)}.
+ * <p>
+ * Callback originates from
+ * {@link Connection.VideoProvider#changeCameraCapabilities(
+ * VideoProfile.CameraCapabilities)}.
*
* @param cameraCapabilities The changed camera capabilities.
*/
public abstract void onCameraCapabilitiesChanged(
- CameraCapabilities cameraCapabilities);
+ VideoProfile.CameraCapabilities cameraCapabilities);
}
}
}
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index c5c3d11..8cf4aeb 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -46,6 +46,7 @@ public final class ParcelableCall implements Parcelable {
private final int mCallerDisplayNamePresentation;
private final GatewayInfo mGatewayInfo;
private final PhoneAccountHandle mAccountHandle;
+ private final boolean mIsVideoCallProviderChanged;
private final IVideoProvider mVideoCallProvider;
private InCallService.VideoCall mVideoCall;
private final String mParentCallId;
@@ -53,6 +54,7 @@ public final class ParcelableCall implements Parcelable {
private final StatusHints mStatusHints;
private final int mVideoState;
private final List<String> mConferenceableCallIds;
+ private final Bundle mIntentExtras;
private final Bundle mExtras;
public ParcelableCall(
@@ -69,12 +71,14 @@ public final class ParcelableCall implements Parcelable {
int callerDisplayNamePresentation,
GatewayInfo gatewayInfo,
PhoneAccountHandle accountHandle,
+ boolean isVideoCallProviderChanged,
IVideoProvider videoCallProvider,
String parentCallId,
List<String> childCallIds,
StatusHints statusHints,
int videoState,
List<String> conferenceableCallIds,
+ Bundle intentExtras,
Bundle extras) {
mId = id;
mState = state;
@@ -89,12 +93,14 @@ public final class ParcelableCall implements Parcelable {
mCallerDisplayNamePresentation = callerDisplayNamePresentation;
mGatewayInfo = gatewayInfo;
mAccountHandle = accountHandle;
+ mIsVideoCallProviderChanged = isVideoCallProviderChanged;
mVideoCallProvider = videoCallProvider;
mParentCallId = parentCallId;
mChildCallIds = childCallIds;
mStatusHints = statusHints;
mVideoState = videoState;
mConferenceableCallIds = Collections.unmodifiableList(conferenceableCallIds);
+ mIntentExtras = intentExtras;
mExtras = extras;
}
@@ -175,10 +181,10 @@ public final class ParcelableCall implements Parcelable {
* Returns an object for remotely communicating through the video call provider's binder.
* @return The video call.
*/
- public InCallService.VideoCall getVideoCall() {
+ public InCallService.VideoCall getVideoCall(Call call) {
if (mVideoCall == null && mVideoCallProvider != null) {
try {
- mVideoCall = new VideoCallImpl(mVideoCallProvider);
+ mVideoCall = new VideoCallImpl(mVideoCallProvider, call);
} catch (RemoteException ignored) {
// Ignore RemoteException.
}
@@ -224,7 +230,7 @@ public final class ParcelableCall implements Parcelable {
}
/**
- * Any extras to pass with the call
+ * Any extras associated with this call.
*
* @return a bundle of extras
*/
@@ -232,6 +238,27 @@ public final class ParcelableCall implements Parcelable {
return mExtras;
}
+ /**
+ * Extras passed in as part of the original call intent.
+ *
+ * @return The intent extras.
+ */
+ public Bundle getIntentExtras() {
+ return mIntentExtras;
+ }
+
+ /**
+ * Indicates to the receiver of the {@link ParcelableCall} whether a change has occurred in the
+ * {@link android.telecom.InCallService.VideoCall} associated with this call. Since
+ * {@link #getVideoCall()} creates a new {@link VideoCallImpl}, it is useful to know whether
+ * the provider has changed (which can influence whether it is accessed).
+ *
+ * @return {@code true} if the video call changed, {@code false} otherwise.
+ */
+ public boolean isVideoCallProviderChanged() {
+ return mIsVideoCallProviderChanged;
+ }
+
/** Responsible for creating ParcelableCall objects for deserialized Parcels. */
public static final Parcelable.Creator<ParcelableCall> CREATOR =
new Parcelable.Creator<ParcelableCall> () {
@@ -252,6 +279,7 @@ public final class ParcelableCall implements Parcelable {
int callerDisplayNamePresentation = source.readInt();
GatewayInfo gatewayInfo = source.readParcelable(classLoader);
PhoneAccountHandle accountHandle = source.readParcelable(classLoader);
+ boolean isVideoCallProviderChanged = source.readByte() == 1;
IVideoProvider videoCallProvider =
IVideoProvider.Stub.asInterface(source.readStrongBinder());
String parentCallId = source.readString();
@@ -261,7 +289,8 @@ public final class ParcelableCall implements Parcelable {
int videoState = source.readInt();
List<String> conferenceableCallIds = new ArrayList<>();
source.readList(conferenceableCallIds, classLoader);
- Bundle extras = source.readParcelable(classLoader);
+ Bundle intentExtras = source.readBundle(classLoader);
+ Bundle extras = source.readBundle(classLoader);
return new ParcelableCall(
id,
state,
@@ -276,12 +305,14 @@ public final class ParcelableCall implements Parcelable {
callerDisplayNamePresentation,
gatewayInfo,
accountHandle,
+ isVideoCallProviderChanged,
videoCallProvider,
parentCallId,
childCallIds,
statusHints,
videoState,
conferenceableCallIds,
+ intentExtras,
extras);
}
@@ -313,6 +344,7 @@ public final class ParcelableCall implements Parcelable {
destination.writeInt(mCallerDisplayNamePresentation);
destination.writeParcelable(mGatewayInfo, 0);
destination.writeParcelable(mAccountHandle, 0);
+ destination.writeByte((byte) (mIsVideoCallProviderChanged ? 1 : 0));
destination.writeStrongBinder(
mVideoCallProvider != null ? mVideoCallProvider.asBinder() : null);
destination.writeString(mParentCallId);
@@ -320,7 +352,8 @@ public final class ParcelableCall implements Parcelable {
destination.writeParcelable(mStatusHints, 0);
destination.writeInt(mVideoState);
destination.writeList(mConferenceableCallIds);
- destination.writeParcelable(mExtras, 0);
+ destination.writeBundle(mIntentExtras);
+ destination.writeBundle(mExtras);
}
@Override
diff --git a/telecomm/java/android/telecom/ParcelableConference.java b/telecomm/java/android/telecom/ParcelableConference.java
index dcc2713..870f5ee 100644
--- a/telecomm/java/android/telecom/ParcelableConference.java
+++ b/telecomm/java/android/telecom/ParcelableConference.java
@@ -16,12 +16,15 @@
package android.telecom;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
import java.util.List;
+import com.android.internal.telecom.IVideoProvider;
+
/**
* A parcelable representation of a conference connection.
* @hide
@@ -32,28 +35,32 @@ public final class ParcelableConference implements Parcelable {
private int mState;
private int mConnectionCapabilities;
private List<String> mConnectionIds;
- private long mConnectTimeMillis;
+ private long mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
+ private final IVideoProvider mVideoProvider;
+ private final int mVideoState;
+ private StatusHints mStatusHints;
+ private Bundle mExtras;
public ParcelableConference(
PhoneAccountHandle phoneAccount,
int state,
int connectionCapabilities,
- List<String> connectionIds) {
+ List<String> connectionIds,
+ IVideoProvider videoProvider,
+ int videoState,
+ long connectTimeMillis,
+ StatusHints statusHints,
+ Bundle extras) {
mPhoneAccount = phoneAccount;
mState = state;
mConnectionCapabilities = connectionCapabilities;
mConnectionIds = connectionIds;
mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
- }
-
- public ParcelableConference(
- PhoneAccountHandle phoneAccount,
- int state,
- int connectionCapabilities,
- List<String> connectionIds,
- long connectTimeMillis) {
- this(phoneAccount, state, connectionCapabilities, connectionIds);
+ mVideoProvider = videoProvider;
+ mVideoState = videoState;
mConnectTimeMillis = connectTimeMillis;
+ mStatusHints = statusHints;
+ mExtras = extras;
}
@Override
@@ -69,6 +76,10 @@ public final class ParcelableConference implements Parcelable {
.append(mConnectTimeMillis)
.append(", children: ")
.append(mConnectionIds)
+ .append(", VideoState: ")
+ .append(mVideoState)
+ .append(", VideoProvider: ")
+ .append(mVideoProvider)
.toString();
}
@@ -91,6 +102,21 @@ public final class ParcelableConference implements Parcelable {
public long getConnectTimeMillis() {
return mConnectTimeMillis;
}
+ public IVideoProvider getVideoProvider() {
+ return mVideoProvider;
+ }
+
+ public int getVideoState() {
+ return mVideoState;
+ }
+
+ public StatusHints getStatusHints() {
+ return mStatusHints;
+ }
+
+ public Bundle getExtras() {
+ return mExtras;
+ }
public static final Parcelable.Creator<ParcelableConference> CREATOR =
new Parcelable.Creator<ParcelableConference> () {
@@ -103,9 +129,14 @@ public final class ParcelableConference implements Parcelable {
List<String> connectionIds = new ArrayList<>(2);
source.readList(connectionIds, classLoader);
long connectTimeMillis = source.readLong();
+ IVideoProvider videoCallProvider =
+ IVideoProvider.Stub.asInterface(source.readStrongBinder());
+ int videoState = source.readInt();
+ StatusHints statusHints = source.readParcelable(classLoader);
+ Bundle extras = source.readBundle(classLoader);
return new ParcelableConference(phoneAccount, state, capabilities, connectionIds,
- connectTimeMillis);
+ videoCallProvider, videoState, connectTimeMillis, statusHints, extras);
}
@Override
@@ -128,5 +159,10 @@ public final class ParcelableConference implements Parcelable {
destination.writeInt(mConnectionCapabilities);
destination.writeList(mConnectionIds);
destination.writeLong(mConnectTimeMillis);
+ destination.writeStrongBinder(
+ mVideoProvider != null ? mVideoProvider.asBinder() : null);
+ destination.writeInt(mVideoState);
+ destination.writeParcelable(mStatusHints, 0);
+ destination.writeBundle(mExtras);
}
}
diff --git a/telecomm/java/android/telecom/ParcelableConnection.java b/telecomm/java/android/telecom/ParcelableConnection.java
index 552e250..683ab6a 100644
--- a/telecomm/java/android/telecom/ParcelableConnection.java
+++ b/telecomm/java/android/telecom/ParcelableConnection.java
@@ -17,6 +17,7 @@
package android.telecom;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -46,6 +47,7 @@ public final class ParcelableConnection implements Parcelable {
private final StatusHints mStatusHints;
private final DisconnectCause mDisconnectCause;
private final List<String> mConferenceableConnectionIds;
+ private final Bundle mExtras;
/** @hide */
public ParcelableConnection(
@@ -62,7 +64,8 @@ public final class ParcelableConnection implements Parcelable {
boolean isVoipAudioMode,
StatusHints statusHints,
DisconnectCause disconnectCause,
- List<String> conferenceableConnectionIds) {
+ List<String> conferenceableConnectionIds,
+ Bundle extras) {
mPhoneAccount = phoneAccount;
mState = state;
mConnectionCapabilities = capabilities;
@@ -76,7 +79,8 @@ public final class ParcelableConnection implements Parcelable {
mIsVoipAudioMode = isVoipAudioMode;
mStatusHints = statusHints;
mDisconnectCause = disconnectCause;
- this.mConferenceableConnectionIds = conferenceableConnectionIds;
+ mConferenceableConnectionIds = conferenceableConnectionIds;
+ mExtras = extras;
}
public PhoneAccountHandle getPhoneAccount() {
@@ -136,15 +140,21 @@ public final class ParcelableConnection implements Parcelable {
return mConferenceableConnectionIds;
}
+ public final Bundle getExtras() {
+ return mExtras;
+ }
+
@Override
public String toString() {
return new StringBuilder()
.append("ParcelableConnection [act:")
.append(mPhoneAccount)
- .append(", state:")
+ .append("], state:")
.append(mState)
.append(", capabilities:")
.append(Connection.capabilitiesToString(mConnectionCapabilities))
+ .append(", extras:")
+ .append(mExtras)
.toString();
}
@@ -170,6 +180,7 @@ public final class ParcelableConnection implements Parcelable {
DisconnectCause disconnectCause = source.readParcelable(classLoader);
List<String> conferenceableConnectionIds = new ArrayList<>();
source.readStringList(conferenceableConnectionIds);
+ Bundle extras = source.readBundle(classLoader);
return new ParcelableConnection(
phoneAccount,
@@ -185,7 +196,8 @@ public final class ParcelableConnection implements Parcelable {
audioModeIsVoip,
statusHints,
disconnectCause,
- conferenceableConnectionIds);
+ conferenceableConnectionIds,
+ extras);
}
@Override
@@ -218,5 +230,6 @@ public final class ParcelableConnection implements Parcelable {
destination.writeParcelable(mStatusHints, 0);
destination.writeParcelable(mDisconnectCause, 0);
destination.writeStringList(mConferenceableConnectionIds);
+ destination.writeBundle(mExtras);
}
}
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index 6344181..8eb091b 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -28,9 +28,11 @@ import java.util.concurrent.CopyOnWriteArrayList;
/**
* A unified virtual device providing a means of voice (and other) communication on a device.
*
- * {@hide}
+ * @hide
+ * @deprecated Use {@link InCallService} directly instead of using this class.
*/
@SystemApi
+@Deprecated
public final class Phone {
public abstract static class Listener {
@@ -39,10 +41,21 @@ public final class Phone {
*
* @param phone The {@code Phone} calling this method.
* @param audioState The new {@link AudioState}.
+ *
+ * @deprecated Use {@link #onCallAudioStateChanged(Phone, CallAudioState)} instead.
*/
+ @Deprecated
public void onAudioStateChanged(Phone phone, AudioState audioState) { }
/**
+ * Called when the audio state changes.
+ *
+ * @param phone The {@code Phone} calling this method.
+ * @param callAudioState The new {@link CallAudioState}.
+ */
+ public void onCallAudioStateChanged(Phone phone, CallAudioState callAudioState) { }
+
+ /**
* Called to bring the in-call screen to the foreground. The in-call experience should
* respond immediately by coming to the foreground to inform the user of the state of
* ongoing {@code Call}s.
@@ -98,18 +111,16 @@ public final class Phone {
private final InCallAdapter mInCallAdapter;
- private AudioState mAudioState;
+ private CallAudioState mCallAudioState;
private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
private boolean mCanAddCall = true;
- /** {@hide} */
Phone(InCallAdapter adapter) {
mInCallAdapter = adapter;
}
- /** {@hide} */
final void internalAddCall(ParcelableCall parcelableCall) {
Call call = new Call(this, parcelableCall.getId(), mInCallAdapter);
mCallByTelecomCallId.put(parcelableCall.getId(), call);
@@ -119,14 +130,17 @@ public final class Phone {
fireCallAdded(call);
}
- /** {@hide} */
final void internalRemoveCall(Call call) {
mCallByTelecomCallId.remove(call.internalGetCallId());
mCalls.remove(call);
+
+ InCallService.VideoCall videoCall = call.getVideoCall();
+ if (videoCall != null) {
+ videoCall.destroy();
+ }
fireCallRemoved(call);
}
- /** {@hide} */
final void internalUpdateCall(ParcelableCall parcelableCall) {
Call call = mCallByTelecomCallId.get(parcelableCall.getId());
if (call != null) {
@@ -135,7 +149,6 @@ public final class Phone {
}
}
- /** {@hide} */
final void internalSetPostDialWait(String telecomId, String remaining) {
Call call = mCallByTelecomCallId.get(telecomId);
if (call != null) {
@@ -143,25 +156,21 @@ public final class Phone {
}
}
- /** {@hide} */
- final void internalAudioStateChanged(AudioState audioState) {
- if (!Objects.equals(mAudioState, audioState)) {
- mAudioState = audioState;
- fireAudioStateChanged(audioState);
+ final void internalCallAudioStateChanged(CallAudioState callAudioState) {
+ if (!Objects.equals(mCallAudioState, callAudioState)) {
+ mCallAudioState = callAudioState;
+ fireCallAudioStateChanged(callAudioState);
}
}
- /** {@hide} */
final Call internalGetCallByTelecomId(String telecomId) {
return mCallByTelecomCallId.get(telecomId);
}
- /** {@hide} */
final void internalBringToForeground(boolean showDialpad) {
fireBringToForeground(showDialpad);
}
- /** {@hide} */
final void internalSetCanAddCall(boolean canAddCall) {
if (mCanAddCall != canAddCall) {
mCanAddCall = canAddCall;
@@ -171,10 +180,13 @@ public final class Phone {
/**
* Called to destroy the phone and cleanup any lingering calls.
- * @hide
*/
final void destroy() {
for (Call call : mCalls) {
+ InCallService.VideoCall videoCall = call.getVideoCall();
+ if (videoCall != null) {
+ videoCall.destroy();
+ }
if (call.getState() != Call.STATE_DISCONNECTED) {
call.internalSetDisconnected();
}
@@ -244,6 +256,8 @@ public final class Phone {
* 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
* that do not have a proximity sensor.
+ *
+ * @hide
*/
public final void setProximitySensorOn() {
mInCallAdapter.turnProximitySensorOn();
@@ -257,6 +271,8 @@ public final class Phone {
* @param screenOnImmediately If true, the screen will be turned on immediately if it was
* previously off. Otherwise, the screen will only be turned on after the proximity sensor
* is no longer triggered.
+ *
+ * @hide
*/
public final void setProximitySensorOff(boolean screenOnImmediately) {
mInCallAdapter.turnProximitySensorOff(screenOnImmediately);
@@ -266,9 +282,20 @@ public final class Phone {
* Obtains the current phone call audio state of the {@code Phone}.
*
* @return An object encapsulating the audio state.
+ * @deprecated Use {@link #getCallAudioState()} instead.
*/
+ @Deprecated
public final AudioState getAudioState() {
- return mAudioState;
+ return new AudioState(mCallAudioState);
+ }
+
+ /**
+ * Obtains the current phone call audio state of the {@code Phone}.
+ *
+ * @return An object encapsulating the audio state.
+ */
+ public final CallAudioState getCallAudioState() {
+ return mCallAudioState;
}
private void fireCallAdded(Call call) {
@@ -283,9 +310,10 @@ public final class Phone {
}
}
- private void fireAudioStateChanged(AudioState audioState) {
+ private void fireCallAudioStateChanged(CallAudioState audioState) {
for (Listener listener : mListeners) {
- listener.onAudioStateChanged(this, audioState);
+ listener.onCallAudioStateChanged(this, audioState);
+ listener.onAudioStateChanged(this, new AudioState(audioState));
}
}
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 052a481..df6fa2e 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -26,6 +26,7 @@ import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
@@ -40,16 +41,14 @@ import java.util.MissingResourceException;
/**
* Represents a distinct method to place or receive a phone call. Apps which can place calls and
* want those calls to be integrated into the dialer and in-call UI should build an instance of
- * this class and register it with the system using {@link TelecomManager#registerPhoneAccount}.
+ * this class and register it with the system using {@link TelecomManager}.
* <p>
* {@link TelecomManager} uses registered {@link PhoneAccount}s to present the user with
* alternative options when placing a phone call. When building a {@link PhoneAccount}, the app
- * should supply a valid {@link PhoneAccountHandle} that references the {@link ConnectionService}
+ * should supply a valid {@link PhoneAccountHandle} that references the connection service
* implementation Telecom will use to interact with the app.
- * @hide
*/
-@SystemApi
-public class PhoneAccount implements Parcelable {
+public final class PhoneAccount implements Parcelable {
/**
* Flag indicating that this {@code PhoneAccount} can act as a connection manager for
@@ -74,7 +73,6 @@ public class PhoneAccount implements Parcelable {
* <p>
* See {@link #getCapabilities}
* <p>
- * {@hide}
*/
public static final int CAPABILITY_CALL_PROVIDER = 0x2;
@@ -92,7 +90,6 @@ public class PhoneAccount implements Parcelable {
* Flag indicating that this {@code PhoneAccount} is capable of placing video calls.
* <p>
* See {@link #getCapabilities}
- * @hide
*/
public static final int CAPABILITY_VIDEO_CALLING = 0x8;
@@ -111,6 +108,7 @@ public class PhoneAccount implements Parcelable {
* See {@link #getCapabilities}
* @hide
*/
+ @SystemApi
public static final int CAPABILITY_MULTI_USER = 0x20;
/**
@@ -130,6 +128,7 @@ public class PhoneAccount implements Parcelable {
/**
* Indicating no icon tint is set.
+ * @hide
*/
public static final int NO_ICON_TINT = 0;
@@ -147,14 +146,12 @@ public class PhoneAccount implements Parcelable {
private final Uri mAddress;
private final Uri mSubscriptionAddress;
private final int mCapabilities;
- private final int mIconResId;
- private final String mIconPackageName;
- private final Bitmap mIconBitmap;
- private final int mIconTint;
private final int mHighlightColor;
private final CharSequence mLabel;
private final CharSequence mShortDescription;
private final List<String> mSupportedUriSchemes;
+ private final Icon mIcon;
+ private boolean mIsEnabled;
/**
* Helper class for creating a {@link PhoneAccount}.
@@ -164,14 +161,12 @@ public class PhoneAccount implements Parcelable {
private Uri mAddress;
private Uri mSubscriptionAddress;
private int mCapabilities;
- private int mIconResId;
- private String mIconPackageName;
- private Bitmap mIconBitmap;
- private int mIconTint = NO_ICON_TINT;
private int mHighlightColor = NO_HIGHLIGHT_COLOR;
private CharSequence mLabel;
private CharSequence mShortDescription;
private List<String> mSupportedUriSchemes = new ArrayList<String>();
+ private Icon mIcon;
+ private boolean mIsEnabled = false;
/**
* Creates a builder with the specified {@link PhoneAccountHandle} and label.
@@ -192,20 +187,12 @@ public class PhoneAccount implements Parcelable {
mAddress = phoneAccount.getAddress();
mSubscriptionAddress = phoneAccount.getSubscriptionAddress();
mCapabilities = phoneAccount.getCapabilities();
- mIconResId = phoneAccount.getIconResId();
- mIconPackageName = phoneAccount.getIconPackageName();
- mIconBitmap = phoneAccount.getIconBitmap();
- mIconTint = phoneAccount.getIconTint();
mHighlightColor = phoneAccount.getHighlightColor();
mLabel = phoneAccount.getLabel();
mShortDescription = phoneAccount.getShortDescription();
mSupportedUriSchemes.addAll(phoneAccount.getSupportedUriSchemes());
- }
-
- /** @hide */
- public Builder setAccountHandle(PhoneAccountHandle accountHandle) {
- mAccountHandle = accountHandle;
- return this;
+ mIcon = phoneAccount.getIcon();
+ mIsEnabled = phoneAccount.isEnabled();
}
/**
@@ -242,65 +229,12 @@ public class PhoneAccount implements Parcelable {
}
/**
- * Sets the icon. See {@link PhoneAccount#createIconDrawable}.
- *
- * @param packageContext The package from which to load an icon.
- * @param iconResId The resource in {@code iconPackageName} representing the icon.
- * @return The builder.
- */
- public Builder setIcon(Context packageContext, int iconResId) {
- return setIcon(packageContext.getPackageName(), iconResId);
- }
-
- /**
- * Sets the icon. See {@link PhoneAccount#createIconDrawable}.
- *
- * @param iconPackageName The package from which to load an icon.
- * @param iconResId The resource in {@code iconPackageName} representing the icon.
- * @return The builder.
- */
- public Builder setIcon(String iconPackageName, int iconResId) {
- return setIcon(iconPackageName, iconResId, NO_ICON_TINT);
- }
-
- /**
- * Sets the icon. See {@link PhoneAccount#createIconDrawable}.
+ * Sets the icon. See {@link PhoneAccount#getIcon}.
*
- * @param packageContext The package from which to load an icon.
- * @param iconResId The resource in {@code iconPackageName} representing the icon.
- * @param iconTint A color with which to tint this icon.
- * @return The builder.
+ * @param icon The icon to set.
*/
- public Builder setIcon(Context packageContext, int iconResId, int iconTint) {
- return setIcon(packageContext.getPackageName(), iconResId, iconTint);
- }
-
- /**
- * Sets the icon. See {@link PhoneAccount#createIconDrawable}.
- *
- * @param iconPackageName The package from which to load an icon.
- * @param iconResId The resource in {@code iconPackageName} representing the icon.
- * @param iconTint A color with which to tint this icon.
- * @return The builder.
- */
- public Builder setIcon(String iconPackageName, int iconResId, int iconTint) {
- this.mIconPackageName = iconPackageName;
- this.mIconResId = iconResId;
- this.mIconTint = iconTint;
- return this;
- }
-
- /**
- * Sets the icon. See {@link PhoneAccount#createIconDrawable}.
- *
- * @param iconBitmap The icon bitmap.
- * @return The builder.
- */
- public Builder setIcon(Bitmap iconBitmap) {
- this.mIconBitmap = iconBitmap;
- this.mIconPackageName = null;
- this.mIconResId = NO_RESOURCE_ID;
- this.mIconTint = NO_ICON_TINT;
+ public Builder setIcon(Icon icon) {
+ mIcon = icon;
return this;
}
@@ -331,7 +265,6 @@ public class PhoneAccount implements Parcelable {
*
* @param uriScheme The URI scheme.
* @return The builder.
- * @hide
*/
public Builder addSupportedUriScheme(String uriScheme) {
if (!TextUtils.isEmpty(uriScheme) && !mSupportedUriSchemes.contains(uriScheme)) {
@@ -358,6 +291,18 @@ public class PhoneAccount implements Parcelable {
}
/**
+ * Sets the enabled state of the phone account.
+ *
+ * @param isEnabled The enabled state.
+ * @return The builder.
+ * @hide
+ */
+ public Builder setIsEnabled(boolean isEnabled) {
+ mIsEnabled = isEnabled;
+ return this;
+ }
+
+ /**
* Creates an instance of a {@link PhoneAccount} based on the current builder settings.
*
* @return The {@link PhoneAccount}.
@@ -373,14 +318,12 @@ public class PhoneAccount implements Parcelable {
mAddress,
mSubscriptionAddress,
mCapabilities,
- mIconResId,
- mIconPackageName,
- mIconBitmap,
- mIconTint,
+ mIcon,
mHighlightColor,
mLabel,
mShortDescription,
- mSupportedUriSchemes);
+ mSupportedUriSchemes,
+ mIsEnabled);
}
}
@@ -389,26 +332,22 @@ public class PhoneAccount implements Parcelable {
Uri address,
Uri subscriptionAddress,
int capabilities,
- int iconResId,
- String iconPackageName,
- Bitmap iconBitmap,
- int iconTint,
+ Icon icon,
int highlightColor,
CharSequence label,
CharSequence shortDescription,
- List<String> supportedUriSchemes) {
+ List<String> supportedUriSchemes,
+ boolean isEnabled) {
mAccountHandle = account;
mAddress = address;
mSubscriptionAddress = subscriptionAddress;
mCapabilities = capabilities;
- mIconResId = iconResId;
- mIconPackageName = iconPackageName;
- mIconBitmap = iconBitmap;
- mIconTint = iconTint;
+ mIcon = icon;
mHighlightColor = highlightColor;
mLabel = label;
mShortDescription = shortDescription;
mSupportedUriSchemes = Collections.unmodifiableList(supportedUriSchemes);
+ mIsEnabled = isEnabled;
}
public static Builder builder(
@@ -421,7 +360,6 @@ public class PhoneAccount implements Parcelable {
* Returns a builder initialized with the current {@link PhoneAccount} instance.
*
* @return The builder.
- * @hide
*/
public Builder toBuilder() { return new Builder(this); }
@@ -474,7 +412,7 @@ public class PhoneAccount implements Parcelable {
* bit mask.
*
* @param capability The capabilities to check.
- * @return {@code True} if the phone account has the capability.
+ * @return {@code true} if the phone account has the capability.
*/
public boolean hasCapabilities(int capability) {
return (mCapabilities & capability) == capability;
@@ -508,11 +446,30 @@ public class PhoneAccount implements Parcelable {
}
/**
+ * The icon to represent this {@code PhoneAccount}.
+ *
+ * @return The icon.
+ */
+ public Icon getIcon() {
+ return mIcon;
+ }
+
+ /**
+ * Indicates whether the user has enabled this {@code PhoneAccount} or not. This value is only
+ * populated for {@code PhoneAccount}s returned by {@link TelecomManager#getPhoneAccount}.
+ *
+ * @return {@code true} if the account is enabled by the user, {@code false} otherwise.
+ */
+ public boolean isEnabled() {
+ return mIsEnabled;
+ }
+
+ /**
* Determines if the {@link PhoneAccount} supports calls to/from addresses with a specified URI
* scheme.
*
* @param uriScheme The URI scheme to check.
- * @return {@code True} if the {@code PhoneAccount} supports calls to/from addresses with the
+ * @return {@code true} if the {@code PhoneAccount} supports calls to/from addresses with the
* specified URI scheme.
*/
public boolean supportsUriScheme(String uriScheme) {
@@ -529,59 +486,6 @@ public class PhoneAccount implements Parcelable {
}
/**
- * The icon resource ID for the icon of this {@code PhoneAccount}.
- * <p>
- * Creators of a {@code PhoneAccount} who possess the icon in static resources should prefer
- * this method of indicating the icon rather than using {@link #getIconBitmap()}, since it
- * leads to less resource usage.
- * <p>
- * Clients wishing to display a {@code PhoneAccount} should use {@link #createIconDrawable(Context)}.
- *
- * @return A resource ID.
- */
- public int getIconResId() {
- return mIconResId;
- }
-
- /**
- * The package name from which to load the icon of this {@code PhoneAccount}.
- * <p>
- * If this property is {@code null}, the resource {@link #getIconResId()} will be loaded from
- * the package in the {@link ComponentName} of the {@link #getAccountHandle()}.
- * <p>
- * Clients wishing to display a {@code PhoneAccount} should use {@link #createIconDrawable(Context)}.
- *
- * @return A package name.
- */
- public String getIconPackageName() {
- return mIconPackageName;
- }
-
- /**
- * A tint to apply to the icon of this {@code PhoneAccount}.
- *
- * @return A hexadecimal color value.
- */
- public int getIconTint() {
- return mIconTint;
- }
-
- /**
- * A literal icon bitmap to represent this {@code PhoneAccount} in a user interface.
- * <p>
- * If this property is specified, it is to be considered the preferred icon. Otherwise, the
- * resource specified by {@link #getIconResId()} should be used.
- * <p>
- * Clients wishing to display a {@code PhoneAccount} should use
- * {@link #createIconDrawable(Context)}.
- *
- * @return A bitmap.
- */
- public Bitmap getIconBitmap() {
- return mIconBitmap;
- }
-
- /**
* A highlight color to use in displaying information about this {@code PhoneAccount}.
*
* @return A hexadecimal color value.
@@ -591,38 +495,11 @@ public class PhoneAccount implements Parcelable {
}
/**
- * Builds and returns an icon {@code Drawable} to represent this {@code PhoneAccount} in a user
- * interface. Uses the properties {@link #getIconResId()}, {@link #getIconPackageName()}, and
- * {@link #getIconBitmap()} as necessary.
- *
- * @param context A {@code Context} to use for loading {@code Drawable}s.
- *
- * @return An icon for this {@code PhoneAccount}.
+ * Sets the enabled state of the phone account.
+ * @hide
*/
- public Drawable createIconDrawable(Context context) {
- if (mIconBitmap != null) {
- return new BitmapDrawable(context.getResources(), mIconBitmap);
- }
-
- if (mIconResId != 0) {
- try {
- Context packageContext = context.createPackageContext(mIconPackageName, 0);
- try {
- Drawable iconDrawable = packageContext.getDrawable(mIconResId);
- if (mIconTint != NO_ICON_TINT) {
- iconDrawable.setTint(mIconTint);
- }
- return iconDrawable;
- } catch (NotFoundException | MissingResourceException e) {
- Log.e(this, e, "Cannot find icon %d in package %s",
- mIconResId, mIconPackageName);
- }
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(this, "Cannot find package %s", mIconPackageName);
- }
- }
-
- return new ColorDrawable(Color.TRANSPARENT);
+ public void setIsEnabled(boolean isEnabled) {
+ mIsEnabled = isEnabled;
}
//
@@ -655,19 +532,18 @@ public class PhoneAccount implements Parcelable {
mSubscriptionAddress.writeToParcel(out, flags);
}
out.writeInt(mCapabilities);
- out.writeInt(mIconResId);
- out.writeString(mIconPackageName);
- if (mIconBitmap == null) {
- out.writeInt(0);
- } else {
- out.writeInt(1);
- mIconBitmap.writeToParcel(out, flags);
- }
- out.writeInt(mIconTint);
out.writeInt(mHighlightColor);
out.writeCharSequence(mLabel);
out.writeCharSequence(mShortDescription);
out.writeStringList(mSupportedUriSchemes);
+
+ if (mIcon == null) {
+ out.writeInt(0);
+ } else {
+ out.writeInt(1);
+ mIcon.writeToParcel(out, flags);
+ }
+ out.writeByte((byte) (mIsEnabled ? 1 : 0));
}
public static final Creator<PhoneAccount> CREATOR
@@ -700,23 +576,23 @@ public class PhoneAccount implements Parcelable {
mSubscriptionAddress = null;
}
mCapabilities = in.readInt();
- mIconResId = in.readInt();
- mIconPackageName = in.readString();
- if (in.readInt() > 0) {
- mIconBitmap = Bitmap.CREATOR.createFromParcel(in);
- } else {
- mIconBitmap = null;
- }
- mIconTint = in.readInt();
mHighlightColor = in.readInt();
mLabel = in.readCharSequence();
mShortDescription = in.readCharSequence();
mSupportedUriSchemes = Collections.unmodifiableList(in.createStringArrayList());
+ if (in.readInt() > 0) {
+ mIcon = Icon.CREATOR.createFromParcel(in);
+ } else {
+ mIcon = null;
+ }
+ mIsEnabled = in.readByte() == 1;
}
@Override
public String toString() {
- StringBuilder sb = new StringBuilder().append("[PhoneAccount: ")
+ StringBuilder sb = new StringBuilder().append("[[")
+ .append(mIsEnabled ? 'X' : ' ')
+ .append("] PhoneAccount: ")
.append(mAccountHandle)
.append(" Capabilities: ")
.append(mCapabilities)
diff --git a/telecomm/java/android/telecom/PhoneAccountHandle.java b/telecomm/java/android/telecom/PhoneAccountHandle.java
index 97af41a..6dc6e9c 100644
--- a/telecomm/java/android/telecom/PhoneAccountHandle.java
+++ b/telecomm/java/android/telecom/PhoneAccountHandle.java
@@ -16,7 +16,6 @@
package android.telecom;
-import android.annotation.SystemApi;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
@@ -29,17 +28,14 @@ import java.util.Objects;
* The unique identifier for a {@link PhoneAccount}. A {@code PhoneAccountHandle} is made of two
* parts:
* <ul>
- * <li>The component name of the associated {@link ConnectionService}.</li>
+ * <li>The component name of the associated connection service.</li>
* <li>A string identifier that is unique across {@code PhoneAccountHandle}s with the same
* component name.</li>
* </ul>
*
- * See {@link PhoneAccount},
- * {@link TelecomManager#registerPhoneAccount TelecomManager.registerPhoneAccount}.
- * @hide
+ * See {@link PhoneAccount}, {@link TelecomManager}.
*/
-@SystemApi
-public class PhoneAccountHandle implements Parcelable {
+public final class PhoneAccountHandle implements Parcelable {
private final ComponentName mComponentName;
private final String mId;
private final UserHandle mUserHandle;
@@ -50,7 +46,6 @@ public class PhoneAccountHandle implements Parcelable {
this(componentName, id, Process.myUserHandle());
}
- /** @hide */
public PhoneAccountHandle(
ComponentName componentName,
String id,
@@ -61,8 +56,8 @@ public class PhoneAccountHandle implements Parcelable {
}
/**
- * The {@code ComponentName} of the {@link android.telecom.ConnectionService} which is
- * responsible for making phone calls using this {@code PhoneAccountHandle}.
+ * The {@code ComponentName} of the connection service which is responsible for making phone
+ * calls using this {@code PhoneAccountHandle}.
*
* @return A suitable {@code ComponentName}.
*/
@@ -72,9 +67,9 @@ public class PhoneAccountHandle implements Parcelable {
/**
* A string that uniquely distinguishes this particular {@code PhoneAccountHandle} from all the
- * others supported by the {@link ConnectionService} that created it.
+ * others supported by the connection service that created it.
* <p>
- * A {@code ConnectionService} must select identifiers that are stable for the lifetime of
+ * A connection service must select identifiers that are stable for the lifetime of
* their users' relationship with their service, across many Android devices. For example, a
* good set of identifiers might be the email addresses with which with users registered for
* their accounts with a particular service. Depending on how a service chooses to operate,
@@ -82,6 +77,9 @@ public class PhoneAccountHandle implements Parcelable {
* ({@code 0}, {@code 1}, {@code 2}, ...) that are generated locally on each phone and could
* collide with values generated on other phones or after a data wipe of a given phone.
*
+ * Important: A non-unique identifier could cause non-deterministic call-log backup/restore
+ * behavior.
+ *
* @return A service-specific unique identifier for this {@code PhoneAccountHandle}.
*/
public String getId() {
@@ -90,7 +88,6 @@ public class PhoneAccountHandle implements Parcelable {
/**
* @return the {@link UserHandle} to use when connecting to this PhoneAccount.
- * @hide
*/
public UserHandle getUserHandle() {
return mUserHandle;
diff --git a/telecomm/java/android/telecom/RemoteConference.java b/telecomm/java/android/telecom/RemoteConference.java
index a8879ae..ae5cd46 100644
--- a/telecomm/java/android/telecom/RemoteConference.java
+++ b/telecomm/java/android/telecom/RemoteConference.java
@@ -18,7 +18,10 @@ package android.telecom;
import com.android.internal.telecom.IConnectionService;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.Handler;
import android.os.RemoteException;
import java.util.ArrayList;
@@ -29,30 +32,97 @@ import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
/**
- * Represents a conference call which can contain any number of {@link Connection} objects.
- * @hide
+ * A conference provided to a {@link ConnectionService} by another {@code ConnectionService} through
+ * {@link ConnectionService#conferenceRemoteConnections}. Once created, a {@code RemoteConference}
+ * can be used to control the conference call or monitor changes through
+ * {@link RemoteConnection.Callback}.
+ *
+ * @see ConnectionService#onRemoteConferenceAdded
*/
-@SystemApi
public final class RemoteConference {
+ /**
+ * Callback base class for {@link RemoteConference}.
+ */
public abstract static class Callback {
+ /**
+ * Invoked when the state of this {@code RemoteConferece} has changed. See
+ * {@link #getState()}.
+ *
+ * @param conference The {@code RemoteConference} invoking this method.
+ * @param oldState The previous state of the {@code RemoteConference}.
+ * @param newState The new state of the {@code RemoteConference}.
+ */
public void onStateChanged(RemoteConference conference, int oldState, int newState) {}
+
+ /**
+ * Invoked when this {@code RemoteConference} is disconnected.
+ *
+ * @param conference The {@code RemoteConference} invoking this method.
+ * @param disconnectCause The ({@see DisconnectCause}) associated with this failed
+ * conference.
+ */
public void onDisconnected(RemoteConference conference, DisconnectCause disconnectCause) {}
+
+ /**
+ * Invoked when a {@link RemoteConnection} is added to the conference call.
+ *
+ * @param conference The {@code RemoteConference} invoking this method.
+ * @param connection The {@link RemoteConnection} being added.
+ */
public void onConnectionAdded(RemoteConference conference, RemoteConnection connection) {}
+
+ /**
+ * Invoked when a {@link RemoteConnection} is removed from the conference call.
+ *
+ * @param conference The {@code RemoteConference} invoking this method.
+ * @param connection The {@link RemoteConnection} being removed.
+ */
public void onConnectionRemoved(RemoteConference conference, RemoteConnection connection) {}
+
+ /**
+ * Indicates that the call capabilities of this {@code RemoteConference} have changed.
+ * See {@link #getConnectionCapabilities()}.
+ *
+ * @param conference The {@code RemoteConference} invoking this method.
+ * @param connectionCapabilities The new capabilities of the {@code RemoteConference}.
+ */
public void onConnectionCapabilitiesChanged(
RemoteConference conference,
int connectionCapabilities) {}
+
+ /**
+ * Invoked when the set of {@link RemoteConnection}s which can be added to this conference
+ * call have changed.
+ *
+ * @param conference The {@code RemoteConference} invoking this method.
+ * @param conferenceableConnections The list of conferenceable {@link RemoteConnection}s.
+ */
public void onConferenceableConnectionsChanged(
RemoteConference conference,
List<RemoteConnection> conferenceableConnections) {}
+
+ /**
+ * Indicates that this {@code RemoteConference} has been destroyed. No further requests
+ * should be made to the {@code RemoteConference}, and references to it should be cleared.
+ *
+ * @param conference The {@code RemoteConference} invoking this method.
+ */
public void onDestroyed(RemoteConference conference) {}
+
+ /**
+ * Handles changes to the {@code RemoteConference} extras.
+ *
+ * @param conference The {@code RemoteConference} invoking this method.
+ * @param extras The extras containing other information associated with the conference.
+ */
+ public void onExtrasChanged(RemoteConference conference, @Nullable Bundle extras) {}
}
private final String mId;
private final IConnectionService mConnectionService;
- private final Set<Callback> mCallbacks = new CopyOnWriteArraySet<>();
+ private final Set<CallbackRecord<Callback>> mCallbackRecords = new CopyOnWriteArraySet<>();
private final List<RemoteConnection> mChildConnections = new CopyOnWriteArrayList<>();
private final List<RemoteConnection> mUnmodifiableChildConnections =
Collections.unmodifiableList(mChildConnections);
@@ -63,30 +133,38 @@ public final class RemoteConference {
private int mState = Connection.STATE_NEW;
private DisconnectCause mDisconnectCause;
private int mConnectionCapabilities;
+ private Bundle mExtras;
- /** {@hide} */
+ /** @hide */
RemoteConference(String id, IConnectionService connectionService) {
mId = id;
mConnectionService = connectionService;
}
- /** {@hide} */
+ /** @hide */
String getId() {
return mId;
}
- /** {@hide} */
+ /** @hide */
void setDestroyed() {
for (RemoteConnection connection : mChildConnections) {
connection.setConference(null);
}
- for (Callback c : mCallbacks) {
- c.onDestroyed(this);
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final RemoteConference conference = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onDestroyed(conference);
+ }
+ });
}
}
- /** {@hide} */
- void setState(int newState) {
+ /** @hide */
+ void setState(final int newState) {
if (newState != Connection.STATE_ACTIVE &&
newState != Connection.STATE_HOLDING &&
newState != Connection.STATE_DISCONNECTED) {
@@ -96,42 +174,71 @@ public final class RemoteConference {
}
if (mState != newState) {
- int oldState = mState;
+ final int oldState = mState;
mState = newState;
- for (Callback c : mCallbacks) {
- c.onStateChanged(this, oldState, newState);
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final RemoteConference conference = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onStateChanged(conference, oldState, newState);
+ }
+ });
}
}
}
- /** {@hide} */
- void addConnection(RemoteConnection connection) {
+ /** @hide */
+ void addConnection(final RemoteConnection connection) {
if (!mChildConnections.contains(connection)) {
mChildConnections.add(connection);
connection.setConference(this);
- for (Callback c : mCallbacks) {
- c.onConnectionAdded(this, connection);
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final RemoteConference conference = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onConnectionAdded(conference, connection);
+ }
+ });
}
}
}
- /** {@hide} */
- void removeConnection(RemoteConnection connection) {
+ /** @hide */
+ void removeConnection(final RemoteConnection connection) {
if (mChildConnections.contains(connection)) {
mChildConnections.remove(connection);
connection.setConference(null);
- for (Callback c : mCallbacks) {
- c.onConnectionRemoved(this, connection);
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final RemoteConference conference = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onConnectionRemoved(conference, connection);
+ }
+ });
}
}
}
- /** {@hide} */
- void setConnectionCapabilities(int connectionCapabilities) {
+ /** @hide */
+ void setConnectionCapabilities(final int connectionCapabilities) {
if (mConnectionCapabilities != connectionCapabilities) {
mConnectionCapabilities = connectionCapabilities;
- for (Callback c : mCallbacks) {
- c.onConnectionCapabilitiesChanged(this, mConnectionCapabilities);
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final RemoteConference conference = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onConnectionCapabilitiesChanged(
+ conference, mConnectionCapabilities);
+ }
+ });
}
}
}
@@ -140,39 +247,92 @@ public final class RemoteConference {
void setConferenceableConnections(List<RemoteConnection> conferenceableConnections) {
mConferenceableConnections.clear();
mConferenceableConnections.addAll(conferenceableConnections);
- for (Callback c : mCallbacks) {
- c.onConferenceableConnectionsChanged(this, mUnmodifiableConferenceableConnections);
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final RemoteConference conference = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onConferenceableConnectionsChanged(
+ conference, mUnmodifiableConferenceableConnections);
+ }
+ });
}
}
- /** {@hide} */
- void setDisconnected(DisconnectCause disconnectCause) {
+ /** @hide */
+ void setDisconnected(final DisconnectCause disconnectCause) {
if (mState != Connection.STATE_DISCONNECTED) {
mDisconnectCause = disconnectCause;
setState(Connection.STATE_DISCONNECTED);
- for (Callback c : mCallbacks) {
- c.onDisconnected(this, disconnectCause);
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final RemoteConference conference = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onDisconnected(conference, disconnectCause);
+ }
+ });
}
}
}
+ /** @hide */
+ void setExtras(final Bundle extras) {
+ mExtras = extras;
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final RemoteConference conference = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onExtrasChanged(conference, extras);
+ }
+ });
+ }
+ }
+
+ /**
+ * Returns the list of {@link RemoteConnection}s contained in this conference.
+ *
+ * @return A list of child connections.
+ */
public final List<RemoteConnection> getConnections() {
return mUnmodifiableChildConnections;
}
+ /**
+ * Gets the state of the conference call. See {@link Connection} for valid values.
+ *
+ * @return A constant representing the state the conference call is currently in.
+ */
public final int getState() {
return mState;
}
- /** @hide */
- @Deprecated public final int getCallCapabilities() {
- return getConnectionCapabilities();
- }
-
+ /**
+ * Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class
+ * {@link Connection} for valid values.
+ *
+ * @return A bitmask of the capabilities of the conference call.
+ */
public final int getConnectionCapabilities() {
return mConnectionCapabilities;
}
+ /**
+ * Obtain the extras associated with this {@code RemoteConnection}.
+ *
+ * @return The extras for this connection.
+ */
+ public final Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
+ * Disconnects the conference call as well as the child {@link RemoteConnection}s.
+ */
public void disconnect() {
try {
mConnectionService.disconnect(mId);
@@ -180,6 +340,13 @@ public final class RemoteConference {
}
}
+ /**
+ * Removes the specified {@link RemoteConnection} from the conference. This causes the
+ * {@link RemoteConnection} to become a standalone connection. This is a no-op if the
+ * {@link RemoteConnection} does not belong to this conference.
+ *
+ * @param connection The remote-connection to remove.
+ */
public void separate(RemoteConnection connection) {
if (mChildConnections.contains(connection)) {
try {
@@ -189,6 +356,16 @@ public final class RemoteConference {
}
}
+ /**
+ * Merges all {@link RemoteConnection}s of this conference into a single call. This should be
+ * invoked only if the conference contains the capability
+ * {@link Connection#CAPABILITY_MERGE_CONFERENCE}, otherwise it is a no-op. The presence of said
+ * capability indicates that the connections of this conference, despite being part of the
+ * same conference object, are yet to have their audio streams merged; this is a common pattern
+ * for CDMA conference calls, but the capability is not used for GSM and SIP conference calls.
+ * Invoking this method will cause the unmerged child connections to merge their audio
+ * streams.
+ */
public void merge() {
try {
mConnectionService.mergeConference(mId);
@@ -196,6 +373,15 @@ public final class RemoteConference {
}
}
+ /**
+ * Swaps the active audio stream between the conference's child {@link RemoteConnection}s.
+ * This should be invoked only if the conference contains the capability
+ * {@link Connection#CAPABILITY_SWAP_CONFERENCE}, otherwise it is a no-op. This is only used by
+ * {@link ConnectionService}s that create conferences for connections that do not yet have
+ * their audio streams merged; this is a common pattern for CDMA conference calls, but the
+ * capability is not used for GSM and SIP conference calls. Invoking this method will change the
+ * active audio stream to a different child connection.
+ */
public void swap() {
try {
mConnectionService.swapConference(mId);
@@ -203,6 +389,9 @@ public final class RemoteConference {
}
}
+ /**
+ * Puts the conference on hold.
+ */
public void hold() {
try {
mConnectionService.hold(mId);
@@ -210,6 +399,9 @@ public final class RemoteConference {
}
}
+ /**
+ * Unholds the conference call.
+ */
public void unhold() {
try {
mConnectionService.unhold(mId);
@@ -217,10 +409,22 @@ public final class RemoteConference {
}
}
+ /**
+ * Returns the {@link DisconnectCause} for the conference if it is in the state
+ * {@link Connection#STATE_DISCONNECTED}. If the conference is not disconnected, this will
+ * return null.
+ *
+ * @return The disconnect cause.
+ */
public DisconnectCause getDisconnectCause() {
return mDisconnectCause;
}
+ /**
+ * Requests that the conference start playing the specified DTMF tone.
+ *
+ * @param digit The digit for which to play a DTMF tone.
+ */
public void playDtmfTone(char digit) {
try {
mConnectionService.playDtmfTone(mId, digit);
@@ -228,6 +432,11 @@ public final class RemoteConference {
}
}
+ /**
+ * Stops the most recent request to play a DTMF tone.
+ *
+ * @see #playDtmfTone
+ */
public void stopDtmfTone() {
try {
mConnectionService.stopDtmfTone(mId);
@@ -235,22 +444,79 @@ public final class RemoteConference {
}
}
+ /**
+ * Request to change the conference's audio routing to the specified state. The specified state
+ * can include audio routing (Bluetooth, Speaker, etc) and muting state.
+ *
+ * @see android.telecom.AudioState
+ * @deprecated Use {@link #setCallAudioState(CallAudioState)} instead.
+ * @hide
+ */
+ @SystemApi
+ @Deprecated
public void setAudioState(AudioState state) {
+ setCallAudioState(new CallAudioState(state));
+ }
+
+ /**
+ * Request to change the conference's audio routing to the specified state. The specified state
+ * can include audio routing (Bluetooth, Speaker, etc) and muting state.
+ */
+ public void setCallAudioState(CallAudioState state) {
try {
- mConnectionService.onAudioStateChanged(mId, state);
+ mConnectionService.onCallAudioStateChanged(mId, state);
} catch (RemoteException e) {
}
}
+
+ /**
+ * Returns a list of independent connections that can me merged with this conference.
+ *
+ * @return A list of conferenceable connections.
+ */
public List<RemoteConnection> getConferenceableConnections() {
return mUnmodifiableConferenceableConnections;
}
+ /**
+ * Register a callback through which to receive state updates for this conference.
+ *
+ * @param callback The callback to notify of state changes.
+ */
public final void registerCallback(Callback callback) {
- mCallbacks.add(callback);
+ registerCallback(callback, new Handler());
+ }
+
+ /**
+ * Registers a callback through which to receive state updates for this conference.
+ * Callbacks will be notified using the specified handler, if provided.
+ *
+ * @param callback The callback to notify of state changes.
+ * @param handler The handler on which to execute the callbacks.
+ */
+ public final void registerCallback(Callback callback, Handler handler) {
+ unregisterCallback(callback);
+ if (callback != null && handler != null) {
+ mCallbackRecords.add(new CallbackRecord(callback, handler));
+ }
}
+ /**
+ * Unregisters a previously registered callback.
+ *
+ * @see #registerCallback
+ *
+ * @param callback The callback to unregister.
+ */
public final void unregisterCallback(Callback callback) {
- mCallbacks.remove(callback);
+ if (callback != null) {
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ if (record.getCallback() == callback) {
+ mCallbackRecords.remove(record);
+ break;
+ }
+ }
+ }
}
}
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 486691f..f960959 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -20,8 +20,12 @@ import com.android.internal.telecom.IConnectionService;
import com.android.internal.telecom.IVideoCallback;
import com.android.internal.telecom.IVideoProvider;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.hardware.camera2.CameraManager;
import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.Surface;
@@ -38,11 +42,12 @@ import java.util.concurrent.ConcurrentHashMap;
*
* @see ConnectionService#createRemoteOutgoingConnection(PhoneAccountHandle, ConnectionRequest)
* @see ConnectionService#createRemoteIncomingConnection(PhoneAccountHandle, ConnectionRequest)
- * @hide
*/
-@SystemApi
public final class RemoteConnection {
+ /**
+ * Callback base class for {@link RemoteConnection}.
+ */
public static abstract class Callback {
/**
* Invoked when the state of this {@code RemoteConnection} has changed. See
@@ -73,11 +78,6 @@ public final class RemoteConnection {
*/
public void onRingbackRequested(RemoteConnection connection, boolean ringback) {}
- /** @hide */
- @Deprecated public void onCallCapabilitiesChanged(
- RemoteConnection connection,
- int callCapabilities) {}
-
/**
* Indicates that the call capabilities of this {@code RemoteConnection} have changed.
* See {@link #getConnectionCapabilities()}.
@@ -156,7 +156,6 @@ public final class RemoteConnection {
*
* @param connection The {@code RemoteConnection} invoking this method.
* @param videoState The new video state of the {@code RemoteConnection}.
- * @hide
*/
public void onVideoStateChanged(RemoteConnection connection, int videoState) {}
@@ -187,7 +186,6 @@ public final class RemoteConnection {
* @param connection The {@code RemoteConnection} invoking this method.
* @param videoProvider The new {@code VideoProvider} associated with this
* {@code RemoteConnection}.
- * @hide
*/
public void onVideoProviderChanged(
RemoteConnection connection, VideoProvider videoProvider) {}
@@ -203,46 +201,137 @@ public final class RemoteConnection {
public void onConferenceChanged(
RemoteConnection connection,
RemoteConference conference) {}
+
+ /**
+ * Handles changes to the {@code RemoteConnection} extras.
+ *
+ * @param connection The {@code RemoteConnection} invoking this method.
+ * @param extras The extras containing other information associated with the connection.
+ */
+ public void onExtrasChanged(RemoteConnection connection, @Nullable Bundle extras) {}
}
- /** {@hide} */
+ /**
+ * {@link RemoteConnection.VideoProvider} associated with a {@link RemoteConnection}. Used to
+ * receive video related events and control the video associated with a
+ * {@link RemoteConnection}.
+ *
+ * @see Connection.VideoProvider
+ */
public static class VideoProvider {
- public abstract static class Listener {
- public void onReceiveSessionModifyRequest(
+ /**
+ * Callback class used by the {@link RemoteConnection.VideoProvider} to relay events from
+ * the {@link Connection.VideoProvider}.
+ */
+ public abstract static class Callback {
+ /**
+ * Reports a session modification request received from the
+ * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
+ *
+ * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
+ * @param videoProfile The requested video call profile.
+ * @see InCallService.VideoCall.Callback#onSessionModifyRequestReceived(VideoProfile)
+ * @see Connection.VideoProvider#receiveSessionModifyRequest(VideoProfile)
+ */
+ public void onSessionModifyRequestReceived(
VideoProvider videoProvider,
VideoProfile videoProfile) {}
- public void onReceiveSessionModifyResponse(
+ /**
+ * Reports a session modification response received from the
+ * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
+ *
+ * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
+ * @param status Status of the session modify request.
+ * @param requestedProfile The original request which was sent to the peer device.
+ * @param responseProfile The actual profile changes made by the peer device.
+ * @see InCallService.VideoCall.Callback#onSessionModifyResponseReceived(int,
+ * VideoProfile, VideoProfile)
+ * @see Connection.VideoProvider#receiveSessionModifyResponse(int, VideoProfile,
+ * VideoProfile)
+ */
+ public void onSessionModifyResponseReceived(
VideoProvider videoProvider,
int status,
VideoProfile requestedProfile,
VideoProfile responseProfile) {}
- public void onHandleCallSessionEvent(VideoProvider videoProvider, int event) {}
-
- public void onPeerDimensionsChanged(VideoProvider videoProvider, int width, int height) {}
-
- public void onCallDataUsageChanged(VideoProvider videoProvider, int dataUsage) {}
-
+ /**
+ * Reports a call session event received from the {@link Connection.VideoProvider}
+ * associated with a {@link RemoteConnection}.
+ *
+ * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
+ * @param event The event.
+ * @see InCallService.VideoCall.Callback#onCallSessionEvent(int)
+ * @see Connection.VideoProvider#handleCallSessionEvent(int)
+ */
+ public void onCallSessionEvent(VideoProvider videoProvider, int event) {}
+
+ /**
+ * Reports a change in the peer video dimensions received from the
+ * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
+ *
+ * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
+ * @param width The updated peer video width.
+ * @param height The updated peer video height.
+ * @see InCallService.VideoCall.Callback#onPeerDimensionsChanged(int, int)
+ * @see Connection.VideoProvider#changePeerDimensions(int, int)
+ */
+ public void onPeerDimensionsChanged(VideoProvider videoProvider, int width,
+ int height) {}
+
+ /**
+ * Reports a change in the data usage (in bytes) received from the
+ * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
+ *
+ * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
+ * @param dataUsage The updated data usage (in bytes).
+ * @see InCallService.VideoCall.Callback#onCallDataUsageChanged(long)
+ * @see Connection.VideoProvider#setCallDataUsage(long)
+ */
+ public void onCallDataUsageChanged(VideoProvider videoProvider, long dataUsage) {}
+
+ /**
+ * Reports a change in the capabilities of the current camera, received from the
+ * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
+ *
+ * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
+ * @param cameraCapabilities The changed camera capabilities.
+ * @see InCallService.VideoCall.Callback#onCameraCapabilitiesChanged(
+ * VideoProfile.CameraCapabilities)
+ * @see Connection.VideoProvider#changeCameraCapabilities(
+ * VideoProfile.CameraCapabilities)
+ */
public void onCameraCapabilitiesChanged(
VideoProvider videoProvider,
- CameraCapabilities cameraCapabilities) {}
+ VideoProfile.CameraCapabilities cameraCapabilities) {}
+
+ /**
+ * Reports a change in the video quality received from the
+ * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
+ *
+ * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
+ * @param videoQuality The updated peer video quality.
+ * @see InCallService.VideoCall.Callback#onVideoQualityChanged(int)
+ * @see Connection.VideoProvider#changeVideoQuality(int)
+ */
+ public void onVideoQualityChanged(VideoProvider videoProvider, int videoQuality) {}
}
private final IVideoCallback mVideoCallbackDelegate = new IVideoCallback() {
@Override
public void receiveSessionModifyRequest(VideoProfile videoProfile) {
- for (Listener l : mListeners) {
- l.onReceiveSessionModifyRequest(VideoProvider.this, videoProfile);
+ for (Callback l : mCallbacks) {
+ l.onSessionModifyRequestReceived(VideoProvider.this, videoProfile);
}
}
@Override
public void receiveSessionModifyResponse(int status, VideoProfile requestedProfile,
VideoProfile responseProfile) {
- for (Listener l : mListeners) {
- l.onReceiveSessionModifyResponse(
+ for (Callback l : mCallbacks) {
+ l.onSessionModifyResponseReceived(
VideoProvider.this,
status,
requestedProfile,
@@ -252,33 +341,41 @@ public final class RemoteConnection {
@Override
public void handleCallSessionEvent(int event) {
- for (Listener l : mListeners) {
- l.onHandleCallSessionEvent(VideoProvider.this, event);
+ for (Callback l : mCallbacks) {
+ l.onCallSessionEvent(VideoProvider.this, event);
}
}
@Override
public void changePeerDimensions(int width, int height) {
- for (Listener l : mListeners) {
+ for (Callback l : mCallbacks) {
l.onPeerDimensionsChanged(VideoProvider.this, width, height);
}
}
@Override
- public void changeCallDataUsage(int dataUsage) {
- for (Listener l : mListeners) {
+ public void changeCallDataUsage(long dataUsage) {
+ for (Callback l : mCallbacks) {
l.onCallDataUsageChanged(VideoProvider.this, dataUsage);
}
}
@Override
- public void changeCameraCapabilities(CameraCapabilities cameraCapabilities) {
- for (Listener l : mListeners) {
+ public void changeCameraCapabilities(
+ VideoProfile.CameraCapabilities cameraCapabilities) {
+ for (Callback l : mCallbacks) {
l.onCameraCapabilitiesChanged(VideoProvider.this, cameraCapabilities);
}
}
@Override
+ public void changeVideoQuality(int videoQuality) {
+ for (Callback l : mCallbacks) {
+ l.onVideoQualityChanged(VideoProvider.this, videoQuality);
+ }
+ }
+
+ @Override
public IBinder asBinder() {
return null;
}
@@ -294,25 +391,43 @@ public final class RemoteConnection {
* load factor before resizing, 1 means we only expect a single thread to
* access the map so make only a single shard
*/
- private final Set<Listener> mListeners = Collections.newSetFromMap(
- new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
+ private final Set<Callback> mCallbacks = Collections.newSetFromMap(
+ new ConcurrentHashMap<Callback, Boolean>(8, 0.9f, 1));
- public VideoProvider(IVideoProvider videoProviderBinder) {
+ VideoProvider(IVideoProvider videoProviderBinder) {
mVideoProviderBinder = videoProviderBinder;
try {
- mVideoProviderBinder.setVideoCallback(mVideoCallbackServant.getStub().asBinder());
+ mVideoProviderBinder.addVideoCallback(mVideoCallbackServant.getStub().asBinder());
} catch (RemoteException e) {
}
}
- public void addListener(Listener l) {
- mListeners.add(l);
+ /**
+ * Registers a callback to receive commands and state changes for video calls.
+ *
+ * @param l The video call callback.
+ */
+ public void registerCallback(Callback l) {
+ mCallbacks.add(l);
}
- public void removeListener(Listener l) {
- mListeners.remove(l);
+ /**
+ * Clears the video call callback set via {@link #registerCallback}.
+ *
+ * @param l The video call callback to clear.
+ */
+ public void unregisterCallback(Callback l) {
+ mCallbacks.remove(l);
}
+ /**
+ * Sets the camera to be used for the outgoing video for the
+ * {@link RemoteConnection.VideoProvider}.
+ *
+ * @param cameraId The id of the camera (use ids as reported by
+ * {@link CameraManager#getCameraIdList()}).
+ * @see Connection.VideoProvider#onSetCamera(String)
+ */
public void setCamera(String cameraId) {
try {
mVideoProviderBinder.setCamera(cameraId);
@@ -320,6 +435,13 @@ public final class RemoteConnection {
}
}
+ /**
+ * Sets the surface to be used for displaying a preview of what the user's camera is
+ * currently capturing for the {@link RemoteConnection.VideoProvider}.
+ *
+ * @param surface The {@link Surface}.
+ * @see Connection.VideoProvider#onSetPreviewSurface(Surface)
+ */
public void setPreviewSurface(Surface surface) {
try {
mVideoProviderBinder.setPreviewSurface(surface);
@@ -327,6 +449,13 @@ public final class RemoteConnection {
}
}
+ /**
+ * Sets the surface to be used for displaying the video received from the remote device for
+ * the {@link RemoteConnection.VideoProvider}.
+ *
+ * @param surface The {@link Surface}.
+ * @see Connection.VideoProvider#onSetDisplaySurface(Surface)
+ */
public void setDisplaySurface(Surface surface) {
try {
mVideoProviderBinder.setDisplaySurface(surface);
@@ -334,6 +463,13 @@ public final class RemoteConnection {
}
}
+ /**
+ * Sets the device orientation, in degrees, for the {@link RemoteConnection.VideoProvider}.
+ * Assumes that a standard portrait orientation of the device is 0 degrees.
+ *
+ * @param rotation The device orientation, in degrees.
+ * @see Connection.VideoProvider#onSetDeviceOrientation(int)
+ */
public void setDeviceOrientation(int rotation) {
try {
mVideoProviderBinder.setDeviceOrientation(rotation);
@@ -341,6 +477,12 @@ public final class RemoteConnection {
}
}
+ /**
+ * Sets camera zoom ratio for the {@link RemoteConnection.VideoProvider}.
+ *
+ * @param value The camera zoom ratio.
+ * @see Connection.VideoProvider#onSetZoom(float)
+ */
public void setZoom(float value) {
try {
mVideoProviderBinder.setZoom(value);
@@ -348,13 +490,28 @@ public final class RemoteConnection {
}
}
- public void sendSessionModifyRequest(VideoProfile reqProfile) {
+ /**
+ * Issues a request to modify the properties of the current video session for the
+ * {@link RemoteConnection.VideoProvider}.
+ *
+ * @param fromProfile The video profile prior to the request.
+ * @param toProfile The video profile with the requested changes made.
+ * @see Connection.VideoProvider#onSendSessionModifyRequest(VideoProfile, VideoProfile)
+ */
+ public void sendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
try {
- mVideoProviderBinder.sendSessionModifyRequest(reqProfile);
+ mVideoProviderBinder.sendSessionModifyRequest(fromProfile, toProfile);
} catch (RemoteException e) {
}
}
+ /**
+ * Provides a response to a request to change the current call video session
+ * properties for the {@link RemoteConnection.VideoProvider}.
+ *
+ * @param responseProfile The response call video properties.
+ * @see Connection.VideoProvider#onSendSessionModifyResponse(VideoProfile)
+ */
public void sendSessionModifyResponse(VideoProfile responseProfile) {
try {
mVideoProviderBinder.sendSessionModifyResponse(responseProfile);
@@ -362,6 +519,12 @@ public final class RemoteConnection {
}
}
+ /**
+ * Issues a request to retrieve the capabilities of the current camera for the
+ * {@link RemoteConnection.VideoProvider}.
+ *
+ * @see Connection.VideoProvider#onRequestCameraCapabilities()
+ */
public void requestCameraCapabilities() {
try {
mVideoProviderBinder.requestCameraCapabilities();
@@ -369,6 +532,12 @@ public final class RemoteConnection {
}
}
+ /**
+ * Issues a request to retrieve the data usage (in bytes) of the video portion of the
+ * {@link RemoteConnection} for the {@link RemoteConnection.VideoProvider}.
+ *
+ * @see Connection.VideoProvider#onRequestConnectionDataUsage()
+ */
public void requestCallDataUsage() {
try {
mVideoProviderBinder.requestCallDataUsage();
@@ -376,7 +545,13 @@ public final class RemoteConnection {
}
}
- public void setPauseImage(String uri) {
+ /**
+ * Sets the {@link Uri} of an image to be displayed to the peer device when the video signal
+ * is paused, for the {@link RemoteConnection.VideoProvider}.
+ *
+ * @see Connection.VideoProvider#onSetPauseImage(Uri)
+ */
+ public void setPauseImage(Uri uri) {
try {
mVideoProviderBinder.setPauseImage(uri);
} catch (RemoteException e) {
@@ -391,8 +566,8 @@ public final class RemoteConnection {
* load factor before resizing, 1 means we only expect a single thread to
* access the map so make only a single shard
*/
- private final Set<Callback> mCallbacks = Collections.newSetFromMap(
- new ConcurrentHashMap<Callback, Boolean>(8, 0.9f, 1));
+ private final Set<CallbackRecord> mCallbackRecords = Collections.newSetFromMap(
+ new ConcurrentHashMap<CallbackRecord, Boolean>(8, 0.9f, 1));
private final List<RemoteConnection> mConferenceableConnections = new ArrayList<>();
private final List<RemoteConnection> mUnmodifiableconferenceableConnections =
Collections.unmodifiableList(mConferenceableConnections);
@@ -411,6 +586,7 @@ public final class RemoteConnection {
private String mCallerDisplayName;
private int mCallerDisplayNamePresentation;
private RemoteConference mConference;
+ private Bundle mExtras;
/**
* @hide
@@ -469,7 +645,20 @@ public final class RemoteConnection {
* @param callback A {@code Callback}.
*/
public void registerCallback(Callback callback) {
- mCallbacks.add(callback);
+ registerCallback(callback, new Handler());
+ }
+
+ /**
+ * Adds a callback to this {@code RemoteConnection}.
+ *
+ * @param callback A {@code Callback}.
+ * @param handler A {@code Handler} which command and status changes will be delivered to.
+ */
+ public void registerCallback(Callback callback, Handler handler) {
+ unregisterCallback(callback);
+ if (callback != null && handler != null) {
+ mCallbackRecords.add(new CallbackRecord(callback, handler));
+ }
}
/**
@@ -479,7 +668,12 @@ public final class RemoteConnection {
*/
public void unregisterCallback(Callback callback) {
if (callback != null) {
- mCallbacks.remove(callback);
+ for (CallbackRecord record : mCallbackRecords) {
+ if (record.getCallback() == callback) {
+ mCallbackRecords.remove(record);
+ break;
+ }
+ }
}
}
@@ -575,8 +769,7 @@ public final class RemoteConnection {
/**
* Obtains the video state of this {@code RemoteConnection}.
*
- * @return The video state of the {@code RemoteConnection}. See {@link VideoProfile.VideoState}.
- * @hide
+ * @return The video state of the {@code RemoteConnection}. See {@link VideoProfile}.
*/
public int getVideoState() {
return mVideoState;
@@ -584,22 +777,29 @@ public final class RemoteConnection {
/**
* Obtains the video provider of this {@code RemoteConnection}.
- *
* @return The video provider associated with this {@code RemoteConnection}.
- * @hide
*/
public final VideoProvider getVideoProvider() {
return mVideoProvider;
}
/**
+ * Obtain the extras associated with this {@code RemoteConnection}.
+ *
+ * @return The extras for this connection.
+ */
+ public final Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
* Determines whether this {@code RemoteConnection} is requesting ringback.
*
* @return Whether the {@code RemoteConnection} is requesting that the framework play a
* ringback tone on its behalf.
*/
public boolean isRingbackRequested() {
- return false;
+ return mRingbackRequested;
}
/**
@@ -756,11 +956,24 @@ public final class RemoteConnection {
* Set the audio state of this {@code RemoteConnection}.
*
* @param state The audio state of this {@code RemoteConnection}.
+ * @hide
+ * @deprecated Use {@link #setCallAudioState(CallAudioState) instead.
*/
+ @SystemApi
+ @Deprecated
public void setAudioState(AudioState state) {
+ setCallAudioState(new CallAudioState(state));
+ }
+
+ /**
+ * Set the audio state of this {@code RemoteConnection}.
+ *
+ * @param state The audio state of this {@code RemoteConnection}.
+ */
+ public void setCallAudioState(CallAudioState state) {
try {
if (mConnected) {
- mConnectionService.onAudioStateChanged(mConnectionId, state);
+ mConnectionService.onCallAudioStateChanged(mConnectionId, state);
}
} catch (RemoteException ignored) {
}
@@ -800,11 +1013,18 @@ public final class RemoteConnection {
/**
* @hide
*/
- void setState(int state) {
+ void setState(final int state) {
if (mState != state) {
mState = state;
- for (Callback c: mCallbacks) {
- c.onStateChanged(this, state);
+ for (CallbackRecord record: mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onStateChanged(connection, state);
+ }
+ });
}
}
}
@@ -812,13 +1032,20 @@ public final class RemoteConnection {
/**
* @hide
*/
- void setDisconnected(DisconnectCause disconnectCause) {
+ void setDisconnected(final DisconnectCause disconnectCause) {
if (mState != Connection.STATE_DISCONNECTED) {
mState = Connection.STATE_DISCONNECTED;
mDisconnectCause = disconnectCause;
- for (Callback c : mCallbacks) {
- c.onDisconnected(this, mDisconnectCause);
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onDisconnected(connection, disconnectCause);
+ }
+ });
}
}
}
@@ -826,11 +1053,18 @@ public final class RemoteConnection {
/**
* @hide
*/
- void setRingbackRequested(boolean ringback) {
+ void setRingbackRequested(final boolean ringback) {
if (mRingbackRequested != ringback) {
mRingbackRequested = ringback;
- for (Callback c : mCallbacks) {
- c.onRingbackRequested(this, ringback);
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onRingbackRequested(connection, ringback);
+ }
+ });
}
}
}
@@ -838,11 +1072,17 @@ public final class RemoteConnection {
/**
* @hide
*/
- void setConnectionCapabilities(int connectionCapabilities) {
+ void setConnectionCapabilities(final int connectionCapabilities) {
mConnectionCapabilities = connectionCapabilities;
- for (Callback c : mCallbacks) {
- c.onConnectionCapabilitiesChanged(this, connectionCapabilities);
- c.onCallCapabilitiesChanged(this, connectionCapabilities);
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onConnectionCapabilitiesChanged(connection, connectionCapabilities);
+ }
+ });
}
}
@@ -850,17 +1090,24 @@ public final class RemoteConnection {
* @hide
*/
void setDestroyed() {
- if (!mCallbacks.isEmpty()) {
+ if (!mCallbackRecords.isEmpty()) {
// Make sure that the callbacks are notified that the call is destroyed first.
if (mState != Connection.STATE_DISCONNECTED) {
setDisconnected(
new DisconnectCause(DisconnectCause.ERROR, "Connection destroyed."));
}
- for (Callback c : mCallbacks) {
- c.onDestroyed(this);
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onDestroyed(connection);
+ }
+ });
}
- mCallbacks.clear();
+ mCallbackRecords.clear();
mConnected = false;
}
@@ -869,94 +1116,181 @@ public final class RemoteConnection {
/**
* @hide
*/
- void setPostDialWait(String remainingDigits) {
- for (Callback c : mCallbacks) {
- c.onPostDialWait(this, remainingDigits);
+ void setPostDialWait(final String remainingDigits) {
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onPostDialWait(connection, remainingDigits);
+ }
+ });
}
}
/**
* @hide
*/
- void onPostDialChar(char nextChar) {
- for (Callback c : mCallbacks) {
- c.onPostDialChar(this, nextChar);
+ void onPostDialChar(final char nextChar) {
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onPostDialChar(connection, nextChar);
+ }
+ });
}
}
/**
* @hide
*/
- void setVideoState(int videoState) {
+ void setVideoState(final int videoState) {
mVideoState = videoState;
- for (Callback c : mCallbacks) {
- c.onVideoStateChanged(this, videoState);
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onVideoStateChanged(connection, videoState);
+ }
+ });
}
}
/**
* @hide
*/
- void setVideoProvider(VideoProvider videoProvider) {
+ void setVideoProvider(final VideoProvider videoProvider) {
mVideoProvider = videoProvider;
- for (Callback c : mCallbacks) {
- c.onVideoProviderChanged(this, videoProvider);
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onVideoProviderChanged(connection, videoProvider);
+ }
+ });
}
}
/** @hide */
- void setIsVoipAudioMode(boolean isVoip) {
+ void setIsVoipAudioMode(final boolean isVoip) {
mIsVoipAudioMode = isVoip;
- for (Callback c : mCallbacks) {
- c.onVoipAudioChanged(this, isVoip);
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onVoipAudioChanged(connection, isVoip);
+ }
+ });
}
}
/** @hide */
- void setStatusHints(StatusHints statusHints) {
+ void setStatusHints(final StatusHints statusHints) {
mStatusHints = statusHints;
- for (Callback c : mCallbacks) {
- c.onStatusHintsChanged(this, statusHints);
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onStatusHintsChanged(connection, statusHints);
+ }
+ });
}
}
/** @hide */
- void setAddress(Uri address, int presentation) {
+ void setAddress(final Uri address, final int presentation) {
mAddress = address;
mAddressPresentation = presentation;
- for (Callback c : mCallbacks) {
- c.onAddressChanged(this, address, presentation);
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onAddressChanged(connection, address, presentation);
+ }
+ });
}
}
/** @hide */
- void setCallerDisplayName(String callerDisplayName, int presentation) {
+ void setCallerDisplayName(final String callerDisplayName, final int presentation) {
mCallerDisplayName = callerDisplayName;
mCallerDisplayNamePresentation = presentation;
- for (Callback c : mCallbacks) {
- c.onCallerDisplayNameChanged(this, callerDisplayName, presentation);
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onCallerDisplayNameChanged(
+ connection, callerDisplayName, presentation);
+ }
+ });
}
}
/** @hide */
- void setConferenceableConnections(List<RemoteConnection> conferenceableConnections) {
+ void setConferenceableConnections(final List<RemoteConnection> conferenceableConnections) {
mConferenceableConnections.clear();
mConferenceableConnections.addAll(conferenceableConnections);
- for (Callback c : mCallbacks) {
- c.onConferenceableConnectionsChanged(this, mUnmodifiableconferenceableConnections);
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onConferenceableConnectionsChanged(
+ connection, mUnmodifiableconferenceableConnections);
+ }
+ });
}
}
/** @hide */
- void setConference(RemoteConference conference) {
+ void setConference(final RemoteConference conference) {
if (mConference != conference) {
mConference = conference;
- for (Callback c : mCallbacks) {
- c.onConferenceChanged(this, conference);
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onConferenceChanged(connection, conference);
+ }
+ });
}
}
}
+ /** @hide */
+ void setExtras(final Bundle extras) {
+ mExtras = extras;
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onExtrasChanged(connection, extras);
+ }
+ });
+ }
+ }
+
/**
* Create a RemoteConnection represents a failure, and which will be in
* {@link Connection#STATE_DISCONNECTED}. Attempting to use it for anything will almost
@@ -969,4 +1303,22 @@ public final class RemoteConnection {
public static RemoteConnection failure(DisconnectCause disconnectCause) {
return new RemoteConnection(disconnectCause);
}
+
+ private static final class CallbackRecord extends Callback {
+ private final Callback mCallback;
+ private final Handler mHandler;
+
+ public CallbackRecord(Callback callback, Handler handler) {
+ mCallback = callback;
+ mHandler = handler;
+ }
+
+ public Callback getCallback() {
+ return mCallback;
+ }
+
+ public Handler getHandler() {
+ return mHandler;
+ }
+ }
}
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 43a92cb..dc0de0c 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -17,6 +17,7 @@
package android.telecom;
import android.net.Uri;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
@@ -60,11 +61,16 @@ final class RemoteConnectionService {
mPendingConnections.remove(connection);
// Unconditionally initialize the connection ...
connection.setConnectionCapabilities(parcel.getConnectionCapabilities());
- connection.setAddress(
- parcel.getHandle(), parcel.getHandlePresentation());
- connection.setCallerDisplayName(
- parcel.getCallerDisplayName(),
- parcel.getCallerDisplayNamePresentation());
+ if (parcel.getHandle() != null
+ || parcel.getState() != Connection.STATE_DISCONNECTED) {
+ connection.setAddress(parcel.getHandle(), parcel.getHandlePresentation());
+ }
+ if (parcel.getCallerDisplayName() != null
+ || parcel.getState() != Connection.STATE_DISCONNECTED) {
+ connection.setCallerDisplayName(
+ parcel.getCallerDisplayName(),
+ parcel.getCallerDisplayNamePresentation());
+ }
// Set state after handle so that the client can identify the connection.
if (parcel.getState() == Connection.STATE_DISCONNECTED) {
connection.setDisconnected(parcel.getDisconnectCause());
@@ -171,6 +177,13 @@ final class RemoteConnectionService {
}
@Override
+ public void setConferenceMergeFailed(String callId) {
+ // Nothing to do here.
+ // The event has already been handled and there is no state to update
+ // in the underlying connection or conference objects
+ }
+
+ @Override
public void addConferenceCall(
final String callId,
ParcelableConference parcel) {
@@ -306,6 +319,17 @@ final class RemoteConnectionService {
mOurConnectionServiceImpl.addRemoteExistingConnection(remoteConnction);
}
+
+ @Override
+ public void setExtras(String callId, Bundle extras) {
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "setExtras")
+ .setExtras(extras);
+ } else {
+ findConferenceForAction(callId, "setExtras")
+ .setExtras(extras);
+ }
+ }
};
private final ConnectionServiceAdapterServant mServant =
diff --git a/telecomm/java/android/telecom/StatusHints.java b/telecomm/java/android/telecom/StatusHints.java
index dd3a639..453f408 100644
--- a/telecomm/java/android/telecom/StatusHints.java
+++ b/telecomm/java/android/telecom/StatusHints.java
@@ -19,40 +19,48 @@ package android.telecom;
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import java.util.MissingResourceException;
import java.util.Objects;
/**
* Contains status label and icon displayed in the in-call UI.
- * @hide
*/
-@SystemApi
public final class StatusHints implements Parcelable {
- private final ComponentName mPackageName;
private final CharSequence mLabel;
- private final int mIconResId;
+ private final Icon mIcon;
private final Bundle mExtras;
+ /**
+ * @hide
+ */
+ @SystemApi @Deprecated
public StatusHints(ComponentName packageName, CharSequence label, int iconResId,
Bundle extras) {
- mPackageName = packageName;
+ this(label, iconResId == 0 ? null : Icon.createWithResource(packageName.getPackageName(),
+ iconResId), extras);
+ }
+
+ public StatusHints(CharSequence label, Icon icon, Bundle extras) {
mLabel = label;
- mIconResId = iconResId;
+ mIcon = icon;
mExtras = extras;
}
/**
* @return A package used to load the icon.
+ *
+ * @hide
*/
+ @SystemApi @Deprecated
public ComponentName getPackageName() {
- return mPackageName;
+ // Minimal compatibility shim for legacy apps' tests
+ return new ComponentName("", "");
}
/**
@@ -66,16 +74,30 @@ public final class StatusHints implements Parcelable {
* The icon resource ID for the icon to show.
*
* @return A resource ID.
+ *
+ * @hide
*/
+ @SystemApi @Deprecated
public int getIconResId() {
- return mIconResId;
+ // Minimal compatibility shim for legacy apps' tests
+ return 0;
}
/**
* @return An icon displayed in the in-call UI.
+ *
+ * @hide
*/
+ @SystemApi @Deprecated
public Drawable getIcon(Context context) {
- return getIcon(context, mIconResId);
+ return mIcon.loadDrawable(context);
+ }
+
+ /**
+ * @return An icon depicting the status.
+ */
+ public Icon getIcon() {
+ return mIcon;
}
/**
@@ -92,9 +114,8 @@ public final class StatusHints implements Parcelable {
@Override
public void writeToParcel(Parcel out, int flags) {
- out.writeParcelable(mPackageName, flags);
out.writeCharSequence(mLabel);
- out.writeInt(mIconResId);
+ out.writeParcelable(mIcon, 0);
out.writeParcelable(mExtras, 0);
}
@@ -110,36 +131,17 @@ public final class StatusHints implements Parcelable {
};
private StatusHints(Parcel in) {
- mPackageName = in.readParcelable(getClass().getClassLoader());
mLabel = in.readCharSequence();
- mIconResId = in.readInt();
+ mIcon = in.readParcelable(getClass().getClassLoader());
mExtras = in.readParcelable(getClass().getClassLoader());
}
- private Drawable getIcon(Context context, int resId) {
- Context packageContext;
- try {
- packageContext = context.createPackageContext(mPackageName.getPackageName(), 0);
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(this, e, "Cannot find package %s", mPackageName.getPackageName());
- return null;
- }
- try {
- return packageContext.getDrawable(resId);
- } catch (MissingResourceException e) {
- Log.e(this, e, "Cannot find icon %d in package %s",
- resId, mPackageName.getPackageName());
- return null;
- }
- }
-
@Override
public boolean equals(Object other) {
if (other != null && other instanceof StatusHints) {
StatusHints otherHints = (StatusHints) other;
- return Objects.equals(otherHints.getPackageName(), getPackageName()) &&
- Objects.equals(otherHints.getLabel(), getLabel()) &&
- otherHints.getIconResId() == getIconResId() &&
+ return Objects.equals(otherHints.getLabel(), getLabel()) &&
+ Objects.equals(otherHints.getIcon(), getIcon()) &&
Objects.equals(otherHints.getExtras(), getExtras());
}
return false;
@@ -147,7 +149,6 @@ public final class StatusHints implements Parcelable {
@Override
public int hashCode() {
- return Objects.hashCode(mPackageName) + Objects.hashCode(mLabel) + mIconResId +
- Objects.hashCode(mExtras);
+ return Objects.hashCode(mLabel) + Objects.hashCode(mIcon) + Objects.hashCode(mExtras);
}
}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 1a6b292..a30e1c0 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -17,6 +17,7 @@ package android.telecom;
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
@@ -55,8 +56,6 @@ public class TelecomManager {
* Input: get*Extra field {@link #EXTRA_PHONE_ACCOUNT_HANDLE} contains the component name of the
* {@link android.telecom.ConnectionService} that Telecom should bind to. Telecom will then
* ask the connection service for more information about the call prior to showing any UI.
- *
- * @hide
*/
public static final String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
@@ -68,13 +67,25 @@ public class TelecomManager {
public static final String ACTION_NEW_UNKNOWN_CALL = "android.telecom.action.NEW_UNKNOWN_CALL";
/**
- * The {@link android.content.Intent} action used to configure a
- * {@link android.telecom.ConnectionService}.
- * @hide
+ * An {@link android.content.Intent} action sent by the telecom framework to start a
+ * configuration dialog for a registered {@link PhoneAccount}. There is no default dialog
+ * and each app that registers a {@link PhoneAccount} should provide one if desired.
+ * <p>
+ * A user can access the list of enabled {@link android.telecom.PhoneAccount}s through the Phone
+ * app's settings menu. For each entry, the settings app will add a click action. When
+ * triggered, the click-action will start this intent along with the extra
+ * {@link #EXTRA_PHONE_ACCOUNT_HANDLE} to indicate the {@link PhoneAccount} to configure. If the
+ * {@link PhoneAccount} package does not register an {@link android.app.Activity} for this
+ * intent, then it will not be sent.
*/
- @SystemApi
- public static final String ACTION_CONNECTION_SERVICE_CONFIGURE =
- "android.telecom.action.CONNECTION_SERVICE_CONFIGURE";
+ public static final String ACTION_CONFIGURE_PHONE_ACCOUNT =
+ "android.telecom.action.CONFIGURE_PHONE_ACCOUNT";
+
+ /**
+ * The {@link android.content.Intent} action used to show the call accessibility settings page.
+ */
+ public static final String ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS =
+ "android.telecom.action.SHOW_CALL_ACCESSIBILITY_SETTINGS";
/**
* The {@link android.content.Intent} action used to show the call settings page.
@@ -83,15 +94,60 @@ public class TelecomManager {
"android.telecom.action.SHOW_CALL_SETTINGS";
/**
+ * The {@link android.content.Intent} action used to show the respond via SMS settings page.
+ */
+ public static final String ACTION_SHOW_RESPOND_VIA_SMS_SETTINGS =
+ "android.telecom.action.SHOW_RESPOND_VIA_SMS_SETTINGS";
+
+ /**
* The {@link android.content.Intent} action used to show the settings page used to configure
* {@link PhoneAccount} preferences.
- * @hide
*/
- @SystemApi
public static final String ACTION_CHANGE_PHONE_ACCOUNTS =
"android.telecom.action.CHANGE_PHONE_ACCOUNTS";
/**
+ * The {@link android.content.Intent} action used indicate that a new phone account was
+ * just registered.
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_PHONE_ACCOUNT_REGISTERED =
+ "android.telecom.action.PHONE_ACCOUNT_REGISTERED";
+
+ /**
+ * Activity action: Shows a dialog asking the user whether or not they want to replace the
+ * current default Dialer with the one specified in
+ * {@link #EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME}.
+ *
+ * Usage example:
+ * <pre>
+ * Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER);
+ * intent.putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME,
+ * getActivity().getPackageName());
+ * startActivity(intent);
+ * </pre>
+ */
+ public static final String ACTION_CHANGE_DEFAULT_DIALER =
+ "android.telecom.action.CHANGE_DEFAULT_DIALER";
+
+ /**
+ * Broadcast intent action indicating that the current default dialer has changed.
+ * The string extra {@link #EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME} will contain the
+ * name of the package that the default dialer was changed to.
+ *
+ * @see #EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME
+ */
+ public static final String ACTION_DEFAULT_DIALER_CHANGED =
+ "android.telecom.action.DEFAULT_DIALER_CHANGED";
+
+ /**
+ * Extra value used to provide the package name for {@link #ACTION_CHANGE_DEFAULT_DIALER}.
+ */
+ public static final String EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME =
+ "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME";
+
+ /**
* Optional extra for {@link android.content.Intent#ACTION_CALL} containing a boolean that
* determines whether the speakerphone should be automatically turned on for an outgoing call.
*/
@@ -102,11 +158,10 @@ public class TelecomManager {
* Optional extra for {@link android.content.Intent#ACTION_CALL} containing an integer that
* determines the desired video state for an outgoing call.
* Valid options:
- * {@link VideoProfile.VideoState#AUDIO_ONLY},
- * {@link VideoProfile.VideoState#BIDIRECTIONAL},
- * {@link VideoProfile.VideoState#RX_ENABLED},
- * {@link VideoProfile.VideoState#TX_ENABLED}.
- * @hide
+ * {@link VideoProfile#STATE_AUDIO_ONLY},
+ * {@link VideoProfile#STATE_BIDIRECTIONAL},
+ * {@link VideoProfile#STATE_RX_ENABLED},
+ * {@link VideoProfile#STATE_TX_ENABLED}.
*/
public static final String EXTRA_START_CALL_WITH_VIDEO_STATE =
"android.telecom.extra.START_CALL_WITH_VIDEO_STATE";
@@ -117,20 +172,22 @@ public class TelecomManager {
* {@link PhoneAccountHandle} to use when making the call.
* <p class="note">
* Retrieve with {@link android.content.Intent#getParcelableExtra(String)}.
- * @hide
*/
- @SystemApi
public static final String EXTRA_PHONE_ACCOUNT_HANDLE =
"android.telecom.extra.PHONE_ACCOUNT_HANDLE";
/**
+ * The extra used by a {@link ConnectionService} to provide the handle of the caller that
+ * has initiated a new incoming call.
+ */
+ public static final String EXTRA_INCOMING_CALL_ADDRESS =
+ "android.telecom.extra.INCOMING_CALL_ADDRESS";
+
+ /**
* Optional extra for {@link #ACTION_INCOMING_CALL} containing a {@link Bundle} which contains
* metadata about the call. This {@link Bundle} will be returned to the
* {@link ConnectionService}.
- *
- * @hide
*/
- @SystemApi
public static final String EXTRA_INCOMING_CALL_EXTRAS =
"android.telecom.extra.INCOMING_CALL_EXTRAS";
@@ -138,11 +195,8 @@ public class TelecomManager {
* Optional extra for {@link android.content.Intent#ACTION_CALL} and
* {@link android.content.Intent#ACTION_DIAL} {@code Intent} containing a {@link Bundle}
* which contains metadata about the call. This {@link Bundle} will be saved into
- * {@code Call.Details}.
- *
- * @hide
+ * {@code Call.Details} and passed to the {@link ConnectionService} when placing the call.
*/
- @SystemApi
public static final String EXTRA_OUTGOING_CALL_EXTRAS =
"android.telecom.extra.OUTGOING_CALL_EXTRAS";
@@ -206,12 +260,18 @@ public class TelecomManager {
* {@link ConnectionService}s which interact with {@link RemoteConnection}s should only populate
* this if the {@link android.telephony.TelephonyManager#getLine1Number()} value, as that is the
* user's expected caller ID.
- * @hide
*/
- @SystemApi
public static final String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
/**
+ * A boolean meta-data value indicating whether an {@link InCallService} implements an
+ * in-call user interface. Dialer implementations (see {@link #getDefaultDialerPackage()}) which
+ * would also like to replace the in-call interface should set this meta-data to {@code true} in
+ * the manifest registration of their {@link InCallService}.
+ */
+ public static final String METADATA_IN_CALL_SERVICE_UI = "android.telecom.IN_CALL_SERVICE_UI";
+
+ /**
* The dual tone multi-frequency signaling character sent to indicate the dialing system should
* pause for a predefined period.
*/
@@ -304,16 +364,24 @@ public class TelecomManager {
* displayed to the user.
*/
- /** Property is displayed normally. */
+ /**
+ * Indicates that the address or number of a call is allowed to be displayed for caller ID.
+ */
public static final int PRESENTATION_ALLOWED = 1;
- /** Property was blocked. */
+ /**
+ * Indicates that the address or number of a call is blocked by the other party.
+ */
public static final int PRESENTATION_RESTRICTED = 2;
- /** Presentation was not specified or is unknown. */
+ /**
+ * Indicates that the address or number of a call is not specified or known by the carrier.
+ */
public static final int PRESENTATION_UNKNOWN = 3;
- /** Property should be displayed as a pay phone. */
+ /**
+ * Indicates that the address or number of a call belongs to a pay phone.
+ */
public static final int PRESENTATION_PAYPHONE = 4;
private static final String TAG = "TelecomManager";
@@ -340,22 +408,29 @@ public class TelecomManager {
}
/**
- * Return the {@link PhoneAccount} which is the user-chosen default for making outgoing phone
- * calls with a specified URI scheme.
- * <p>
- * Apps must be prepared for this method to return {@code null}, indicating that there currently
- * exists no user-chosen default {@code PhoneAccount}.
+ * Return the {@link PhoneAccount} which will be used to place outgoing calls to addresses with
+ * the specified {@code uriScheme}. This {@link PhoneAccount} will always be a member of the
+ * list which is returned from invoking {@link #getCallCapablePhoneAccounts()}. The specific
+ * account returned depends on the following priorities:
+ * <ul>
+ * <li> If the user-selected default {@link PhoneAccount} supports the specified scheme, it will
+ * be returned.
+ * </li>
+ * <li> If there exists only one {@link PhoneAccount} that supports the specified scheme, it
+ * will be returned.
+ * </li>
+ * </ul>
* <p>
+ * If no {@link PhoneAccount} fits the criteria above, this method will return {@code null}.
+ *
* @param uriScheme The URI scheme.
- * @return The {@link PhoneAccountHandle} corresponding to the user-chosen default for outgoing
- * phone calls for a specified URI scheme.
- * @hide
+ * @return The {@link PhoneAccountHandle} corresponding to the account to be used.
*/
- @SystemApi
public PhoneAccountHandle getDefaultOutgoingPhoneAccount(String uriScheme) {
try {
if (isServiceConnected()) {
- return getTelecomService().getDefaultOutgoingPhoneAccount(uriScheme);
+ return getTelecomService().getDefaultOutgoingPhoneAccount(uriScheme,
+ mContext.getOpPackageName());
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#getDefaultOutgoingPhoneAccount", e);
@@ -367,7 +442,7 @@ public class TelecomManager {
* Return the {@link PhoneAccount} which is the user-chosen default for making outgoing phone
* calls. This {@code PhoneAccount} will always be a member of the list which is returned from
* calling {@link #getCallCapablePhoneAccounts()}
- *
+ * <p>
* Apps must be prepared for this method to return {@code null}, indicating that there currently
* exists no user-chosen default {@code PhoneAccount}.
*
@@ -386,7 +461,7 @@ public class TelecomManager {
}
/**
- * Sets the default account for making outgoing phone calls.
+ * Sets the user-chosen default for making outgoing phone calls.
* @hide
*/
public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
@@ -403,8 +478,8 @@ public class TelecomManager {
* Returns the current SIM call manager. Apps must be prepared for this method to return
* {@code null}, indicating that there currently exists no user-chosen default
* {@code PhoneAccount}.
+ *
* @return The phone account handle of the current sim call manager.
- * @hide
*/
public PhoneAccountHandle getSimCallManager() {
try {
@@ -418,37 +493,6 @@ public class TelecomManager {
}
/**
- * Sets the SIM call manager to the specified phone account.
- * @param accountHandle The phone account handle of the account to set as the sim call manager.
- * @hide
- */
- public void setSimCallManager(PhoneAccountHandle accountHandle) {
- try {
- if (isServiceConnected()) {
- getTelecomService().setSimCallManager(accountHandle);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#setSimCallManager");
- }
- }
-
- /**
- * Returns the list of registered SIM call managers.
- * @return List of registered SIM call managers.
- * @hide
- */
- public List<PhoneAccountHandle> getSimCallManagers() {
- try {
- if (isServiceConnected()) {
- return getTelecomService().getSimCallManagers();
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#getSimCallManagers");
- }
- return new ArrayList<>();
- }
-
- /**
* Returns the current connection manager. Apps must be prepared for this method to return
* {@code null}, indicating that there currently exists no user-chosen default
* {@code PhoneAccount}.
@@ -462,16 +506,6 @@ public class TelecomManager {
}
/**
- * Returns the list of registered SIM call managers.
- * @return List of registered SIM call managers.
- * @hide
- */
- @SystemApi
- public List<PhoneAccountHandle> getRegisteredConnectionManagers() {
- return getSimCallManagers();
- }
-
- /**
* Returns a list of the {@link PhoneAccountHandle}s which can be used to make and receive phone
* calls which support the specified URI scheme.
* <P>
@@ -488,7 +522,8 @@ public class TelecomManager {
public List<PhoneAccountHandle> getPhoneAccountsSupportingScheme(String uriScheme) {
try {
if (isServiceConnected()) {
- return getTelecomService().getPhoneAccountsSupportingScheme(uriScheme);
+ return getTelecomService().getPhoneAccountsSupportingScheme(uriScheme,
+ mContext.getOpPackageName());
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#getPhoneAccountsSupportingScheme", e);
@@ -498,39 +533,38 @@ public class TelecomManager {
/**
- * Return a list of {@link PhoneAccountHandle}s which can be used to make and receive phone
- * calls.
+ * Returns a list of {@link PhoneAccountHandle}s which can be used to make and receive phone
+ * calls. The returned list includes only those accounts which have been explicitly enabled
+ * by the user.
*
* @see #EXTRA_PHONE_ACCOUNT_HANDLE
* @return A list of {@code PhoneAccountHandle} objects.
+ */
+ public List<PhoneAccountHandle> getCallCapablePhoneAccounts() {
+ return getCallCapablePhoneAccounts(false);
+ }
+
+ /**
+ * Returns a list of {@link PhoneAccountHandle}s including those which have not been enabled
+ * by the user.
*
+ * @return A list of {@code PhoneAccountHandle} objects.
* @hide
*/
- public List<PhoneAccountHandle> getCallCapablePhoneAccounts() {
+ public List<PhoneAccountHandle> getCallCapablePhoneAccounts(boolean includeDisabledAccounts) {
try {
if (isServiceConnected()) {
- return getTelecomService().getCallCapablePhoneAccounts();
+ return getTelecomService().getCallCapablePhoneAccounts(
+ includeDisabledAccounts, mContext.getOpPackageName());
}
} catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#getCallCapablePhoneAccounts", e);
+ Log.e(TAG, "Error calling ITelecomService#getCallCapablePhoneAccounts(" +
+ includeDisabledAccounts + ")", e);
}
return new ArrayList<>();
}
/**
- * Determine whether the device has more than one account registered that can make and receive
- * phone calls.
- *
- * @return {@code true} if the device has more than one account registered and {@code false}
- * otherwise.
- * @hide
- */
- @SystemApi
- public boolean hasMultipleCallCapableAccounts() {
- return getCallCapablePhoneAccounts().size() > 1;
- }
-
- /**
* Returns a list of all {@link PhoneAccount}s registered for the calling package.
*
* @return A list of {@code PhoneAccountHandle} objects.
@@ -554,9 +588,7 @@ public class TelecomManager {
*
* @param account The {@link PhoneAccountHandle}.
* @return The {@link PhoneAccount} object.
- * @hide
*/
- @SystemApi
public PhoneAccount getPhoneAccount(PhoneAccountHandle account) {
try {
if (isServiceConnected()) {
@@ -635,10 +667,7 @@ public class TelecomManager {
* {@link PhoneAccountHandle#getComponentName()} does not match the package name of the app.
*
* @param account The complete {@link PhoneAccount}.
- *
- * @hide
*/
- @SystemApi
public void registerPhoneAccount(PhoneAccount account) {
try {
if (isServiceConnected()) {
@@ -653,9 +682,7 @@ public class TelecomManager {
* Remove a {@link PhoneAccount} registration from the system.
*
* @param accountHandle A {@link PhoneAccountHandle} for the {@link PhoneAccount} to unregister.
- * @hide
*/
- @SystemApi
public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
try {
if (isServiceConnected()) {
@@ -671,6 +698,15 @@ public class TelecomManager {
* @hide
*/
@SystemApi
+ public void clearPhoneAccounts() {
+ clearAccounts();
+ }
+ /**
+ * Remove all Accounts that belong to the calling package from the system.
+ * @deprecated Use {@link #clearPhoneAccounts()} instead.
+ * @hide
+ */
+ @SystemApi
public void clearAccounts() {
try {
if (isServiceConnected()) {
@@ -695,7 +731,10 @@ public class TelecomManager {
}
}
+
/**
+ * @deprecated - Use {@link TelecomManager#getDefaultDialerPackage} to directly access
+ * the default dialer's package name instead.
* @hide
*/
@SystemApi
@@ -711,19 +750,76 @@ public class TelecomManager {
}
/**
+ * Used to determine the currently selected default dialer package.
+ *
+ * @return package name for the default dialer package or null if no package has been
+ * selected as the default dialer.
+ */
+ public String getDefaultDialerPackage() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().getDefaultDialerPackage();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException attempting to get the default dialer package name.", e);
+ }
+ return null;
+ }
+
+ /**
+ * Used to set the default dialer package.
+ *
+ * @param packageName to set the default dialer to..
+ *
+ * @result {@code true} if the default dialer was successfully changed, {@code false} if
+ * the specified package does not correspond to an installed dialer, or is already
+ * the default dialer.
+ *
+ * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+ * Requires permission: {@link android.Manifest.permission#WRITE_SECURE_SETTINGS}
+ *
+ * @hide
+ */
+ public boolean setDefaultDialer(String packageName) {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().setDefaultDialer(packageName);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException attempting to set the default dialer.", e);
+ }
+ return false;
+ }
+
+ /**
+ * Used to determine the dialer package that is preloaded on the system partition.
+ *
+ * @return package name for the system dialer package or null if no system dialer is preloaded.
+ * @hide
+ */
+ public String getSystemDialerPackage() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().getSystemDialerPackage();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException attempting to get the system dialer package name.", e);
+ }
+ return null;
+ }
+
+ /**
* Return whether a given phone number is the configured voicemail number for a
* particular phone account.
*
* @param accountHandle The handle for the account to check the voicemail number against
* @param number The number to look up.
- *
- * @hide
*/
- @SystemApi
public boolean isVoiceMailNumber(PhoneAccountHandle accountHandle, String number) {
try {
if (isServiceConnected()) {
- return getTelecomService().isVoiceMailNumber(accountHandle, number);
+ return getTelecomService().isVoiceMailNumber(accountHandle, number,
+ mContext.getOpPackageName());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException calling ITelecomService#isVoiceMailNumber.", e);
@@ -732,23 +828,22 @@ public class TelecomManager {
}
/**
- * Return whether a given phone account has a voicemail number configured.
- *
- * @param accountHandle The handle for the account to check for a voicemail number.
- * @return {@code true} If the given phone account has a voicemail number.
+ * Return the voicemail number for a given phone account.
*
- * @hide
+ * @param accountHandle The handle for the phone account.
+ * @return The voicemail number for the phone account, and {@code null} if one has not been
+ * configured.
*/
- @SystemApi
- public boolean hasVoiceMailNumber(PhoneAccountHandle accountHandle) {
+ public String getVoiceMailNumber(PhoneAccountHandle accountHandle) {
try {
if (isServiceConnected()) {
- return getTelecomService().hasVoiceMailNumber(accountHandle);
+ return getTelecomService().getVoiceMailNumber(accountHandle,
+ mContext.getOpPackageName());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException calling ITelecomService#hasVoiceMailNumber.", e);
}
- return false;
+ return null;
}
/**
@@ -756,14 +851,12 @@ public class TelecomManager {
*
* @param accountHandle The handle for the account retrieve a number for.
* @return A string representation of the line 1 phone number.
- *
- * @hide
*/
- @SystemApi
public String getLine1Number(PhoneAccountHandle accountHandle) {
try {
if (isServiceConnected()) {
- return getTelecomService().getLine1Number(accountHandle);
+ return getTelecomService().getLine1Number(accountHandle,
+ mContext.getOpPackageName());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException calling ITelecomService#getLine1Number.", e);
@@ -781,7 +874,7 @@ public class TelecomManager {
public boolean isInCall() {
try {
if (isServiceConnected()) {
- return getTelecomService().isInCall();
+ return getTelecomService().isInCall(mContext.getOpPackageName());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException calling isInCall().", e);
@@ -823,7 +916,7 @@ public class TelecomManager {
public boolean isRinging() {
try {
if (isServiceConnected()) {
- return getTelecomService().isRinging();
+ return getTelecomService().isRinging(mContext.getOpPackageName());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException attempting to get ringing state of phone app.", e);
@@ -869,14 +962,11 @@ public class TelecomManager {
/**
* Silences the ringer if a ringing call exists.
- *
- * @hide
*/
- @SystemApi
public void silenceRinger() {
try {
if (isServiceConnected()) {
- getTelecomService().silenceRinger();
+ getTelecomService().silenceRinger(mContext.getOpPackageName());
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#silenceRinger", e);
@@ -892,7 +982,7 @@ public class TelecomManager {
public boolean isTtySupported() {
try {
if (isServiceConnected()) {
- return getTelecomService().isTtySupported();
+ return getTelecomService().isTtySupported(mContext.getOpPackageName());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException attempting to get TTY supported state.", e);
@@ -913,7 +1003,7 @@ public class TelecomManager {
public int getCurrentTtyMode() {
try {
if (isServiceConnected()) {
- return getTelecomService().getCurrentTtyMode();
+ return getTelecomService().getCurrentTtyMode(mContext.getOpPackageName());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException attempting to get the current TTY mode.", e);
@@ -933,9 +1023,7 @@ public class TelecomManager {
* {@link #registerPhoneAccount}.
* @param extras A bundle that will be passed through to
* {@link ConnectionService#onCreateIncomingConnection}.
- * @hide
*/
- @SystemApi
public void addNewIncomingCall(PhoneAccountHandle phoneAccount, Bundle extras) {
try {
if (isServiceConnected()) {
@@ -986,7 +1074,7 @@ public class TelecomManager {
ITelecomService service = getTelecomService();
if (service != null) {
try {
- return service.handlePinMmi(dialString);
+ return service.handlePinMmi(dialString, mContext.getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#handlePinMmi", e);
}
@@ -1005,14 +1093,13 @@ public class TelecomManager {
* @param accountHandle The handle for the account the MMI code should apply to.
* @param dialString The digits to dial.
* @return True if the digits were processed as an MMI code, false otherwise.
- * @hide
*/
- @SystemApi
- public boolean handleMmi(PhoneAccountHandle accountHandle, String dialString) {
+ public boolean handleMmi(String dialString, PhoneAccountHandle accountHandle) {
ITelecomService service = getTelecomService();
if (service != null) {
try {
- return service.handlePinMmiForPhoneAccount(accountHandle, dialString);
+ return service.handlePinMmiForPhoneAccount(accountHandle, dialString,
+ mContext.getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#handlePinMmi", e);
}
@@ -1025,14 +1112,12 @@ public class TelecomManager {
* {@code null} to return a URI which will use the default account.
* @return The URI (with the content:// scheme) specific to the specified {@link PhoneAccount}
* for the the content retrieve.
- * @hide
*/
- @SystemApi
public Uri getAdnUriForPhoneAccount(PhoneAccountHandle accountHandle) {
ITelecomService service = getTelecomService();
if (service != null && accountHandle != null) {
try {
- return service.getAdnUriForPhoneAccount(accountHandle);
+ return service.getAdnUriForPhoneAccount(accountHandle, mContext.getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#getAdnUriForPhoneAccount", e);
}
@@ -1050,7 +1135,7 @@ public class TelecomManager {
ITelecomService service = getTelecomService();
if (service != null) {
try {
- service.cancelMissedCallsNotification();
+ service.cancelMissedCallsNotification(mContext.getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#cancelMissedCallsNotification", e);
}
@@ -1071,13 +1156,77 @@ public class TelecomManager {
ITelecomService service = getTelecomService();
if (service != null) {
try {
- service.showInCallScreen(showDialpad);
+ service.showInCallScreen(showDialpad, mContext.getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#showCallScreen", e);
}
}
}
+ /**
+ * Places a new outgoing call to the provided address using the system telecom service with
+ * the specified extras.
+ *
+ * This method is equivalent to placing an outgoing call using {@link Intent#ACTION_CALL},
+ * except that the outgoing call will always be sent via the system telecom service. If
+ * method-caller is either the user selected default dialer app or preloaded system dialer
+ * app, then emergency calls will also be allowed.
+ *
+ * Requires permission: {@link android.Manifest.permission#CALL_PHONE}
+ *
+ * Usage example:
+ * <pre>
+ * Uri uri = Uri.fromParts("tel", "12345", null);
+ * Bundle extras = new Bundle();
+ * extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true);
+ * telecomManager.placeCall(uri, extras);
+ * </pre>
+ *
+ * The following keys are supported in the supplied extras.
+ * <ul>
+ * <li>{@link #EXTRA_OUTGOING_CALL_EXTRAS}</li>
+ * <li>{@link #EXTRA_PHONE_ACCOUNT_HANDLE}</li>
+ * <li>{@link #EXTRA_START_CALL_WITH_SPEAKERPHONE}</li>
+ * <li>{@link #EXTRA_START_CALL_WITH_VIDEO_STATE}</li>
+ * </ul>
+ *
+ * @param address The address to make the call to.
+ * @param extras Bundle of extras to use with the call.
+ */
+ public void placeCall(Uri address, Bundle extras) {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ if (address == null) {
+ Log.w(TAG, "Cannot place call to empty address.");
+ }
+ try {
+ service.placeCall(address, extras == null ? new Bundle() : extras,
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#placeCall", e);
+ }
+ }
+ }
+
+ /**
+ * Enables and disables specified phone account.
+ *
+ * @param handle Handle to the phone account.
+ * @param isEnabled Enable state of the phone account.
+ * @hide
+ */
+ @SystemApi
+ public void enablePhoneAccount(PhoneAccountHandle handle, boolean isEnabled) {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ service.enablePhoneAccount(handle, isEnabled);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error enablePhoneAbbount", e);
+ }
+ }
+ }
+
private ITelecomService getTelecomService() {
return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));
}
diff --git a/telecomm/java/android/telecom/VideoCallImpl.java b/telecomm/java/android/telecom/VideoCallImpl.java
index 925058e..c8072d1 100644
--- a/telecomm/java/android/telecom/VideoCallImpl.java
+++ b/telecomm/java/android/telecom/VideoCallImpl.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -36,16 +37,12 @@ import com.android.internal.telecom.IVideoProvider;
* {@hide}
*/
public class VideoCallImpl extends VideoCall {
- private static final int MSG_RECEIVE_SESSION_MODIFY_REQUEST = 1;
- private static final int MSG_RECEIVE_SESSION_MODIFY_RESPONSE = 2;
- private static final int MSG_HANDLE_CALL_SESSION_EVENT = 3;
- private static final int MSG_CHANGE_PEER_DIMENSIONS = 4;
- private static final int MSG_CHANGE_CALL_DATA_USAGE = 5;
- private static final int MSG_CHANGE_CAMERA_CAPABILITIES = 6;
private final IVideoProvider mVideoProvider;
private final VideoCallListenerBinder mBinder;
- private VideoCall.Listener mVideoCallListener;
+ private VideoCall.Callback mCallback;
+ private int mVideoQuality = VideoProfile.QUALITY_UNKNOWN;
+ private Call mCall;
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
@@ -60,7 +57,7 @@ public class VideoCallImpl extends VideoCall {
private final class VideoCallListenerBinder extends IVideoCallback.Stub {
@Override
public void receiveSessionModifyRequest(VideoProfile videoProfile) {
- mHandler.obtainMessage(MSG_RECEIVE_SESSION_MODIFY_REQUEST,
+ mHandler.obtainMessage(MessageHandler.MSG_RECEIVE_SESSION_MODIFY_REQUEST,
videoProfile).sendToTarget();
}
@@ -71,12 +68,14 @@ public class VideoCallImpl extends VideoCall {
args.arg1 = status;
args.arg2 = requestProfile;
args.arg3 = responseProfile;
- mHandler.obtainMessage(MSG_RECEIVE_SESSION_MODIFY_RESPONSE, args).sendToTarget();
+ mHandler.obtainMessage(MessageHandler.MSG_RECEIVE_SESSION_MODIFY_RESPONSE, args)
+ .sendToTarget();
}
@Override
public void handleCallSessionEvent(int event) {
- mHandler.obtainMessage(MSG_HANDLE_CALL_SESSION_EVENT, event).sendToTarget();
+ mHandler.obtainMessage(MessageHandler.MSG_HANDLE_CALL_SESSION_EVENT, event)
+ .sendToTarget();
}
@Override
@@ -84,33 +83,52 @@ public class VideoCallImpl extends VideoCall {
SomeArgs args = SomeArgs.obtain();
args.arg1 = width;
args.arg2 = height;
- mHandler.obtainMessage(MSG_CHANGE_PEER_DIMENSIONS, args).sendToTarget();
+ mHandler.obtainMessage(MessageHandler.MSG_CHANGE_PEER_DIMENSIONS, args).sendToTarget();
}
@Override
- public void changeCallDataUsage(int dataUsage) {
- mHandler.obtainMessage(MSG_CHANGE_CALL_DATA_USAGE, dataUsage).sendToTarget();
+ public void changeVideoQuality(int videoQuality) {
+ mHandler.obtainMessage(MessageHandler.MSG_CHANGE_VIDEO_QUALITY, videoQuality, 0)
+ .sendToTarget();
}
@Override
- public void changeCameraCapabilities(CameraCapabilities cameraCapabilities) {
- mHandler.obtainMessage(MSG_CHANGE_CAMERA_CAPABILITIES,
+ public void changeCallDataUsage(long dataUsage) {
+ mHandler.obtainMessage(MessageHandler.MSG_CHANGE_CALL_DATA_USAGE, dataUsage)
+ .sendToTarget();
+ }
+
+ @Override
+ public void changeCameraCapabilities(VideoProfile.CameraCapabilities cameraCapabilities) {
+ mHandler.obtainMessage(MessageHandler.MSG_CHANGE_CAMERA_CAPABILITIES,
cameraCapabilities).sendToTarget();
}
}
/** Default handler used to consolidate binder method calls onto a single thread. */
- private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+ private final class MessageHandler extends Handler {
+ private static final int MSG_RECEIVE_SESSION_MODIFY_REQUEST = 1;
+ private static final int MSG_RECEIVE_SESSION_MODIFY_RESPONSE = 2;
+ private static final int MSG_HANDLE_CALL_SESSION_EVENT = 3;
+ private static final int MSG_CHANGE_PEER_DIMENSIONS = 4;
+ private static final int MSG_CHANGE_CALL_DATA_USAGE = 5;
+ private static final int MSG_CHANGE_CAMERA_CAPABILITIES = 6;
+ private static final int MSG_CHANGE_VIDEO_QUALITY = 7;
+
+ public MessageHandler(Looper looper) {
+ super(looper);
+ }
+
@Override
public void handleMessage(Message msg) {
- if (mVideoCallListener == null) {
+ if (mCallback == null) {
return;
}
SomeArgs args;
switch (msg.what) {
case MSG_RECEIVE_SESSION_MODIFY_REQUEST:
- mVideoCallListener.onSessionModifyRequestReceived((VideoProfile) msg.obj);
+ mCallback.onSessionModifyRequestReceived((VideoProfile) msg.obj);
break;
case MSG_RECEIVE_SESSION_MODIFY_RESPONSE:
args = (SomeArgs) msg.obj;
@@ -119,31 +137,35 @@ public class VideoCallImpl extends VideoCall {
VideoProfile requestProfile = (VideoProfile) args.arg2;
VideoProfile responseProfile = (VideoProfile) args.arg3;
- mVideoCallListener.onSessionModifyResponseReceived(
+ mCallback.onSessionModifyResponseReceived(
status, requestProfile, responseProfile);
} finally {
args.recycle();
}
break;
case MSG_HANDLE_CALL_SESSION_EVENT:
- mVideoCallListener.onCallSessionEvent((int) msg.obj);
+ mCallback.onCallSessionEvent((int) msg.obj);
break;
case MSG_CHANGE_PEER_DIMENSIONS:
args = (SomeArgs) msg.obj;
try {
int width = (int) args.arg1;
int height = (int) args.arg2;
- mVideoCallListener.onPeerDimensionsChanged(width, height);
+ mCallback.onPeerDimensionsChanged(width, height);
} finally {
args.recycle();
}
break;
case MSG_CHANGE_CALL_DATA_USAGE:
- mVideoCallListener.onCallDataUsageChanged(msg.arg1);
+ mCallback.onCallDataUsageChanged((long) msg.obj);
break;
case MSG_CHANGE_CAMERA_CAPABILITIES:
- mVideoCallListener.onCameraCapabilitiesChanged(
- (CameraCapabilities) msg.obj);
+ mCallback.onCameraCapabilitiesChanged(
+ (VideoProfile.CameraCapabilities) msg.obj);
+ break;
+ case MSG_CHANGE_VIDEO_QUALITY:
+ mVideoQuality = msg.arg1;
+ mCallback.onVideoQualityChanged(msg.arg1);
break;
default:
break;
@@ -151,18 +173,47 @@ public class VideoCallImpl extends VideoCall {
}
};
- /** {@hide} */
- VideoCallImpl(IVideoProvider videoProvider) throws RemoteException {
+ private Handler mHandler;
+
+ VideoCallImpl(IVideoProvider videoProvider, Call call) throws RemoteException {
mVideoProvider = videoProvider;
mVideoProvider.asBinder().linkToDeath(mDeathRecipient, 0);
mBinder = new VideoCallListenerBinder();
- mVideoProvider.setVideoCallback(mBinder);
+ mVideoProvider.addVideoCallback(mBinder);
+ mCall = call;
+ }
+
+ public void destroy() {
+ unregisterCallback(mCallback);
+ }
+
+ /** {@inheritDoc} */
+ public void registerCallback(VideoCall.Callback callback) {
+ registerCallback(callback, null);
}
/** {@inheritDoc} */
- public void setVideoCallListener(VideoCall.Listener videoCallListener) {
- mVideoCallListener = videoCallListener;
+ public void registerCallback(VideoCall.Callback callback, Handler handler) {
+ mCallback = callback;
+ if (handler == null) {
+ mHandler = new MessageHandler(Looper.getMainLooper());
+ } else {
+ mHandler = new MessageHandler(handler.getLooper());
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void unregisterCallback(VideoCall.Callback callback) {
+ if (callback != mCallback) {
+ return;
+ }
+
+ mCallback = null;
+ try {
+ mVideoProvider.removeVideoCallback(mBinder);
+ } catch (RemoteException e) {
+ }
}
/** {@inheritDoc} */
@@ -205,10 +256,24 @@ public class VideoCallImpl extends VideoCall {
}
}
- /** {@inheritDoc} */
+ /**
+ * Sends a session modification request to the video provider.
+ * <p>
+ * The {@link InCallService} will create the {@code requestProfile} based on the current
+ * video state (i.e. {@link Call.Details#getVideoState()}). It is, however, possible that the
+ * video state maintained by the {@link InCallService} could get out of sync with what is known
+ * by the {@link android.telecom.Connection.VideoProvider}. To remove ambiguity, the
+ * {@link VideoCallImpl} passes along the pre-modify video profile to the {@code VideoProvider}
+ * to ensure it has full context of the requested change.
+ *
+ * @param requestProfile The requested video profile.
+ */
public void sendSessionModifyRequest(VideoProfile requestProfile) {
try {
- mVideoProvider.sendSessionModifyRequest(requestProfile);
+ VideoProfile originalProfile = new VideoProfile(mCall.getDetails().getVideoState(),
+ mVideoQuality);
+
+ mVideoProvider.sendSessionModifyRequest(originalProfile, requestProfile);
} catch (RemoteException e) {
}
}
@@ -238,10 +303,10 @@ public class VideoCallImpl extends VideoCall {
}
/** {@inheritDoc} */
- public void setPauseImage(String uri) {
+ public void setPauseImage(Uri uri) {
try {
mVideoProvider.setPauseImage(uri);
} catch (RemoteException e) {
}
}
-} \ No newline at end of file
+}
diff --git a/telecomm/java/android/telecom/VideoCallbackServant.java b/telecomm/java/android/telecom/VideoCallbackServant.java
index d0e3f22..1fbad22 100644
--- a/telecomm/java/android/telecom/VideoCallbackServant.java
+++ b/telecomm/java/android/telecom/VideoCallbackServant.java
@@ -38,6 +38,7 @@ final class VideoCallbackServant {
private static final int MSG_CHANGE_PEER_DIMENSIONS = 3;
private static final int MSG_CHANGE_CALL_DATA_USAGE = 4;
private static final int MSG_CHANGE_CAMERA_CAPABILITIES = 5;
+ private static final int MSG_CHANGE_VIDEO_QUALITY = 6;
private final IVideoCallback mDelegate;
@@ -90,14 +91,18 @@ final class VideoCallbackServant {
case MSG_CHANGE_CALL_DATA_USAGE: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- mDelegate.changeCallDataUsage(args.argi1);
+ mDelegate.changeCallDataUsage((long) args.arg1);
} finally {
args.recycle();
}
break;
}
case MSG_CHANGE_CAMERA_CAPABILITIES: {
- mDelegate.changeCameraCapabilities((CameraCapabilities) msg.obj);
+ mDelegate.changeCameraCapabilities((VideoProfile.CameraCapabilities) msg.obj);
+ break;
+ }
+ case MSG_CHANGE_VIDEO_QUALITY: {
+ mDelegate.changeVideoQuality(msg.arg1);
break;
}
}
@@ -136,18 +141,24 @@ final class VideoCallbackServant {
}
@Override
- public void changeCallDataUsage(int dataUsage) throws RemoteException {
+ public void changeCallDataUsage(long dataUsage) throws RemoteException {
SomeArgs args = SomeArgs.obtain();
- args.argi1 = dataUsage;
+ args.arg1 = dataUsage;
mHandler.obtainMessage(MSG_CHANGE_CALL_DATA_USAGE, args).sendToTarget();
}
@Override
- public void changeCameraCapabilities(CameraCapabilities cameraCapabilities)
+ public void changeCameraCapabilities(
+ VideoProfile.CameraCapabilities cameraCapabilities)
throws RemoteException {
mHandler.obtainMessage(MSG_CHANGE_CAMERA_CAPABILITIES, cameraCapabilities)
.sendToTarget();
}
+
+ @Override
+ public void changeVideoQuality(int videoQuality) throws RemoteException {
+ mHandler.obtainMessage(MSG_CHANGE_VIDEO_QUALITY, videoQuality, 0).sendToTarget();
+ }
};
public VideoCallbackServant(IVideoCallback delegate) {
diff --git a/telecomm/java/android/telecom/VideoProfile.aidl b/telecomm/java/android/telecom/VideoProfile.aidl
index 091b569..0b32721 100644
--- a/telecomm/java/android/telecom/VideoProfile.aidl
+++ b/telecomm/java/android/telecom/VideoProfile.aidl
@@ -21,3 +21,4 @@ package android.telecom;
* {@hide}
*/
parcelable VideoProfile;
+parcelable VideoProfile.CameraCapabilities;
diff --git a/telecomm/java/android/telecom/VideoProfile.java b/telecomm/java/android/telecom/VideoProfile.java
index f5cb054..dabf706 100644
--- a/telecomm/java/android/telecom/VideoProfile.java
+++ b/telecomm/java/android/telecom/VideoProfile.java
@@ -21,11 +21,14 @@ import android.os.Parcelable;
/**
* Represents attributes of video calls.
- *
- * {@hide}
*/
public class VideoProfile implements Parcelable {
/**
+ * "Unknown" video quality.
+ * @hide
+ */
+ public static final int QUALITY_UNKNOWN = 0;
+ /**
* "High" video quality.
*/
public static final int QUALITY_HIGH = 1;
@@ -45,6 +48,56 @@ public class VideoProfile implements Parcelable {
*/
public static final int QUALITY_DEFAULT = 4;
+ /**
+ * Used when answering or dialing a call to indicate that the call does not have a video
+ * component.
+ * <p>
+ * Should <b>not</b> be used in comparison checks to determine if a video state represents an
+ * audio-only call.
+ * <p>
+ * The following, for example, is not the correct way to check if a call is audio-only:
+ * <pre>
+ * {@code
+ * // This is the incorrect way to check for an audio-only call.
+ * if (videoState == VideoProfile.STATE_AUDIO_ONLY) {
+ * // Handle audio-only call.
+ * }
+ * }
+ * </pre>
+ * <p>
+ * Instead, use the {@link VideoProfile#isAudioOnly(int)} helper function to check if a
+ * video state represents an audio-only call:
+ * <pre>
+ * {@code
+ * // This is the correct way to check for an audio-only call.
+ * if (VideoProfile.isAudioOnly(videoState)) {
+ * // Handle audio-only call.
+ * }
+ * }
+ * </pre>
+ */
+ public static final int STATE_AUDIO_ONLY = 0x0;
+
+ /**
+ * Video transmission is enabled.
+ */
+ public static final int STATE_TX_ENABLED = 0x1;
+
+ /**
+ * Video reception is enabled.
+ */
+ public static final int STATE_RX_ENABLED = 0x2;
+
+ /**
+ * Video signal is bi-directional.
+ */
+ public static final int STATE_BIDIRECTIONAL = STATE_TX_ENABLED | STATE_RX_ENABLED;
+
+ /**
+ * Video is paused.
+ */
+ public static final int STATE_PAUSED = 0x4;
+
private final int mVideoState;
private final int mQuality;
@@ -71,11 +124,11 @@ public class VideoProfile implements Parcelable {
/**
* The video state of the call.
- * Valid values: {@link VideoProfile.VideoState#AUDIO_ONLY},
- * {@link VideoProfile.VideoState#BIDIRECTIONAL},
- * {@link VideoProfile.VideoState#TX_ENABLED},
- * {@link VideoProfile.VideoState#RX_ENABLED},
- * {@link VideoProfile.VideoState#PAUSED}.
+ * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
+ * {@link VideoProfile#STATE_BIDIRECTIONAL},
+ * {@link VideoProfile#STATE_TX_ENABLED},
+ * {@link VideoProfile#STATE_RX_ENABLED},
+ * {@link VideoProfile#STATE_PAUSED}.
*/
public int getVideoState() {
return mVideoState;
@@ -141,91 +194,255 @@ public class VideoProfile implements Parcelable {
dest.writeInt(mQuality);
}
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[VideoProfile videoState = ");
+ sb.append(videoStateToString(mVideoState));
+ sb.append(" videoQuality = ");
+ sb.append(mQuality);
+ sb.append("]");
+ return sb.toString();
+ }
+
+ /**
+ * Generates a string representation of a video state.
+ *
+ * @param videoState The video state.
+ * @return String representation of the video state.
+ */
+ public static String videoStateToString(int videoState) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Audio");
+
+ if (isAudioOnly(videoState)) {
+ sb.append(" Only");
+ } else {
+ if (isTransmissionEnabled(videoState)) {
+ sb.append(" Tx");
+ }
+
+ if (isReceptionEnabled(videoState)) {
+ sb.append(" Rx");
+ }
+
+ if (isPaused(videoState)) {
+ sb.append(" Pause");
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Indicates whether the video state is audio only.
+ *
+ * @param videoState The video state.
+ * @return {@code True} if the video state is audio only, {@code false} otherwise.
+ */
+ public static boolean isAudioOnly(int videoState) {
+ return !hasState(videoState, VideoProfile.STATE_TX_ENABLED)
+ && !hasState(videoState, VideoProfile.STATE_RX_ENABLED);
+ }
+
+ /**
+ * Indicates whether video transmission or reception is enabled for a video state.
+ *
+ * @param videoState The video state.
+ * @return {@code True} if video transmission or reception is enabled, {@code false} otherwise.
+ */
+ public static boolean isVideo(int videoState) {
+ return hasState(videoState, VideoProfile.STATE_TX_ENABLED)
+ || hasState(videoState, VideoProfile.STATE_RX_ENABLED)
+ || hasState(videoState, VideoProfile.STATE_BIDIRECTIONAL);
+ }
+
+ /**
+ * Indicates whether the video state has video transmission enabled.
+ *
+ * @param videoState The video state.
+ * @return {@code True} if video transmission is enabled, {@code false} otherwise.
+ */
+ public static boolean isTransmissionEnabled(int videoState) {
+ return hasState(videoState, VideoProfile.STATE_TX_ENABLED);
+ }
+
+ /**
+ * Indicates whether the video state has video reception enabled.
+ *
+ * @param videoState The video state.
+ * @return {@code True} if video reception is enabled, {@code false} otherwise.
+ */
+ public static boolean isReceptionEnabled(int videoState) {
+ return hasState(videoState, VideoProfile.STATE_RX_ENABLED);
+ }
+
+ /**
+ * Indicates whether the video state is bi-directional.
+ *
+ * @param videoState The video state.
+ * @return {@code True} if the video is bi-directional, {@code false} otherwise.
+ */
+ public static boolean isBidirectional(int videoState) {
+ return hasState(videoState, VideoProfile.STATE_BIDIRECTIONAL);
+ }
+
+ /**
+ * Indicates whether the video state is paused.
+ *
+ * @param videoState The video state.
+ * @return {@code True} if the video is paused, {@code false} otherwise.
+ */
+ public static boolean isPaused(int videoState) {
+ return hasState(videoState, VideoProfile.STATE_PAUSED);
+ }
+
/**
- * The video state of the call, stored as a bit-field describing whether video transmission and
- * receipt it enabled, as well as whether the video is currently muted.
- */
- public static class VideoState {
+ * Indicates if a specified state is set in a videoState bit-mask.
+ *
+ * @param videoState The video state bit-mask.
+ * @param state The state to check.
+ * @return {@code True} if the state is set.
+ */
+ private static boolean hasState(int videoState, int state) {
+ return (videoState & state) == state;
+ }
+
+ /**
+ * Represents the camera capabilities important to a Video Telephony provider.
+ */
+ public static final class CameraCapabilities implements Parcelable {
+
/**
- * Call is currently in an audio-only mode with no video transmission or receipt.
+ * The width of the camera video in pixels.
*/
- public static final int AUDIO_ONLY = 0x0;
+ private final int mWidth;
/**
- * Video transmission is enabled.
+ * The height of the camera video in pixels.
*/
- public static final int TX_ENABLED = 0x1;
+ private final int mHeight;
/**
- * Video reception is enabled.
+ * Whether the camera supports zoom.
*/
- public static final int RX_ENABLED = 0x2;
+ private final boolean mZoomSupported;
/**
- * Video signal is bi-directional.
+ * The maximum zoom supported by the camera.
+ */
+ private final float mMaxZoom;
+
+ /**
+ * Create a call camera capabilities instance.
+ *
+ * @param width The width of the camera video (in pixels).
+ * @param height The height of the camera video (in pixels).
*/
- public static final int BIDIRECTIONAL = TX_ENABLED | RX_ENABLED;
+ public CameraCapabilities(int width, int height) {
+ this(width, height, false, 1.0f);
+ }
/**
- * Video is paused.
+ * Create a call camera capabilities instance that optionally
+ * supports zoom.
+ *
+ * @param width The width of the camera video (in pixels).
+ * @param height The height of the camera video (in pixels).
+ * @param zoomSupported True when camera supports zoom.
+ * @param maxZoom Maximum zoom supported by camera.
+ * @hide
*/
- public static final int PAUSED = 0x4;
+ public CameraCapabilities(int width, int height, boolean zoomSupported, float maxZoom) {
+ mWidth = width;
+ mHeight = height;
+ mZoomSupported = zoomSupported;
+ mMaxZoom = maxZoom;
+ }
/**
- * Whether the video state is audio only.
- * @param videoState The video state.
- * @return Returns true if the video state is audio only.
+ * Responsible for creating CallCameraCapabilities objects from deserialized Parcels.
+ **/
+ public static final Parcelable.Creator<CameraCapabilities> CREATOR =
+ new Parcelable.Creator<CameraCapabilities> () {
+ /**
+ * Creates a CallCameraCapabilities instances from a parcel.
+ *
+ * @param source The parcel.
+ * @return The CallCameraCapabilities.
+ */
+ @Override
+ public CameraCapabilities createFromParcel(Parcel source) {
+ int width = source.readInt();
+ int height = source.readInt();
+ boolean supportsZoom = source.readByte() != 0;
+ float maxZoom = source.readFloat();
+
+ return new CameraCapabilities(width, height, supportsZoom, maxZoom);
+ }
+
+ @Override
+ public CameraCapabilities[] newArray(int size) {
+ return new CameraCapabilities[size];
+ }
+ };
+
+ /**
+ * Describe the kinds of special objects contained in this Parcelable's
+ * marshalled representation.
+ *
+ * @return a bitmask indicating the set of special object types marshalled
+ * by the Parcelable.
*/
- public static boolean isAudioOnly(int videoState) {
- return !hasState(videoState, TX_ENABLED) && !hasState(videoState, RX_ENABLED);
+ @Override
+ public int describeContents() {
+ return 0;
}
/**
- * Whether the video transmission is enabled.
- * @param videoState The video state.
- * @return Returns true if the video transmission is enabled.
+ * Flatten this object in to a Parcel.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
*/
- public static boolean isTransmissionEnabled(int videoState) {
- return hasState(videoState, TX_ENABLED);
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(getWidth());
+ dest.writeInt(getHeight());
+ dest.writeByte((byte) (isZoomSupported() ? 1 : 0));
+ dest.writeFloat(getMaxZoom());
}
/**
- * Whether the video reception is enabled.
- * @param videoState The video state.
- * @return Returns true if the video transmission is enabled.
+ * The width of the camera video in pixels.
*/
- public static boolean isReceptionEnabled(int videoState) {
- return hasState(videoState, RX_ENABLED);
+ public int getWidth() {
+ return mWidth;
}
/**
- * Whether the video signal is bi-directional.
- * @param videoState
- * @return Returns true if the video signal is bi-directional.
+ * The height of the camera video in pixels.
*/
- public static boolean isBidirectional(int videoState) {
- return hasState(videoState, BIDIRECTIONAL);
+ public int getHeight() {
+ return mHeight;
}
/**
- * Whether the video is paused.
- * @param videoState The video state.
- * @return Returns true if the video is paused.
+ * Whether the camera supports zoom.
+ * @hide
*/
- public static boolean isPaused(int videoState) {
- return hasState(videoState, PAUSED);
+ public boolean isZoomSupported() {
+ return mZoomSupported;
}
/**
- * Determines if a specified state is set in a videoState bit-mask.
- *
- * @param videoState The video state bit-mask.
- * @param state The state to check.
- * @return {@code True} if the state is set.
- * {@hide}
+ * The maximum zoom supported by the camera.
+ * @hide
*/
- private static boolean hasState(int videoState, int state) {
- return (videoState & state) == state;
+ public float getMaxZoom() {
+ return mMaxZoom;
}
}
+
}
diff --git a/telecomm/java/android/telecom/Voicemail.java b/telecomm/java/android/telecom/Voicemail.java
new file mode 100644
index 0000000..151917e
--- /dev/null
+++ b/telecomm/java/android/telecom/Voicemail.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents a single voicemail stored in the voicemail content provider.
+ *
+ * @hide
+ */
+public class Voicemail implements Parcelable {
+ private final Long mTimestamp;
+ private final String mNumber;
+ private final PhoneAccountHandle mPhoneAccount;
+ private final Long mId;
+ private final Long mDuration;
+ private final String mSource;
+ private final String mProviderData;
+ private final Uri mUri;
+ private final Boolean mIsRead;
+ private final Boolean mHasContent;
+
+ private Voicemail(Long timestamp, String number, PhoneAccountHandle phoneAccountHandle, Long id,
+ Long duration, String source, String providerData, Uri uri, Boolean isRead,
+ Boolean hasContent) {
+ mTimestamp = timestamp;
+ mNumber = number;
+ mPhoneAccount = phoneAccountHandle;
+ mId = id;
+ mDuration = duration;
+ mSource = source;
+ mProviderData = providerData;
+ mUri = uri;
+ mIsRead = isRead;
+ mHasContent = hasContent;
+ }
+
+ /**
+ * Create a {@link Builder} for a new {@link Voicemail} to be inserted.
+ * <p>
+ * The number and the timestamp are mandatory for insertion.
+ */
+ public static Builder createForInsertion(long timestamp, String number) {
+ return new Builder().setNumber(number).setTimestamp(timestamp);
+ }
+
+ /**
+ * Create a {@link Builder} for a {@link Voicemail} to be updated (or deleted).
+ * <p>
+ * The id and source data fields are mandatory for update - id is necessary for updating the
+ * database and source data is necessary for updating the server.
+ */
+ public static Builder createForUpdate(long id, String sourceData) {
+ return new Builder().setId(id).setSourceData(sourceData);
+ }
+
+ /**
+ * Builder pattern for creating a {@link Voicemail}. The builder must be created with the
+ * {@link #createForInsertion(long, String)} method.
+ * <p>
+ * This class is <b>not thread safe</b>
+ */
+ public static class Builder {
+ private Long mBuilderTimestamp;
+ private String mBuilderNumber;
+ private PhoneAccountHandle mBuilderPhoneAccount;
+ private Long mBuilderId;
+ private Long mBuilderDuration;
+ private String mBuilderSourcePackage;
+ private String mBuilderSourceData;
+ private Uri mBuilderUri;
+ private Boolean mBuilderIsRead;
+ private boolean mBuilderHasContent;
+
+ /** You should use the correct factory method to construct a builder. */
+ private Builder() {
+ }
+
+ public Builder setNumber(String number) {
+ mBuilderNumber = number;
+ return this;
+ }
+
+ public Builder setTimestamp(long timestamp) {
+ mBuilderTimestamp = timestamp;
+ return this;
+ }
+
+ public Builder setPhoneAccount(PhoneAccountHandle phoneAccount) {
+ mBuilderPhoneAccount = phoneAccount;
+ return this;
+ }
+
+ public Builder setId(long id) {
+ mBuilderId = id;
+ return this;
+ }
+
+ public Builder setDuration(long duration) {
+ mBuilderDuration = duration;
+ return this;
+ }
+
+ public Builder setSourcePackage(String sourcePackage) {
+ mBuilderSourcePackage = sourcePackage;
+ return this;
+ }
+
+ public Builder setSourceData(String sourceData) {
+ mBuilderSourceData = sourceData;
+ return this;
+ }
+
+ public Builder setUri(Uri uri) {
+ mBuilderUri = uri;
+ return this;
+ }
+
+ public Builder setIsRead(boolean isRead) {
+ mBuilderIsRead = isRead;
+ return this;
+ }
+
+ public Builder setHasContent(boolean hasContent) {
+ mBuilderHasContent = hasContent;
+ return this;
+ }
+
+ public Voicemail build() {
+ mBuilderId = mBuilderId == null ? -1 : mBuilderId;
+ mBuilderTimestamp = mBuilderTimestamp == null ? 0 : mBuilderTimestamp;
+ mBuilderDuration = mBuilderDuration == null ? 0: mBuilderDuration;
+ mBuilderIsRead = mBuilderIsRead == null ? false : mBuilderIsRead;
+ return new Voicemail(mBuilderTimestamp, mBuilderNumber, mBuilderPhoneAccount,
+ mBuilderId, mBuilderDuration, mBuilderSourcePackage, mBuilderSourceData,
+ mBuilderUri, mBuilderIsRead, mBuilderHasContent);
+ }
+ }
+
+ /**
+ * The identifier of the voicemail in the content provider.
+ * <p>
+ * This may be missing in the case of a new {@link Voicemail} that we plan to insert into the
+ * content provider, since until it has been inserted we don't know what id it should have. If
+ * none is specified, we return -1.
+ */
+ public long getId() {
+ return mId;
+ }
+
+ /** The number of the person leaving the voicemail, empty string if unknown, null if not set. */
+ public String getNumber() {
+ return mNumber;
+ }
+
+ /** The phone account associated with the voicemail, null if not set. */
+ public PhoneAccountHandle getPhoneAccount() {
+ return mPhoneAccount;
+ }
+
+ /** The timestamp the voicemail was received, in millis since the epoch, zero if not set. */
+ public long getTimestampMillis() {
+ return mTimestamp;
+ }
+
+ /** Gets the duration of the voicemail in millis, or zero if the field is not set. */
+ public long getDuration() {
+ return mDuration;
+ }
+
+ /**
+ * Returns the package name of the source that added this voicemail, or null if this field is
+ * not set.
+ */
+ public String getSourcePackage() {
+ return mSource;
+ }
+
+ /**
+ * Returns the application-specific data type stored with the voicemail, or null if this field
+ * is not set.
+ * <p>
+ * Source data is typically used as an identifier to uniquely identify the voicemail against
+ * the voicemail server. This is likely to be something like the IMAP UID, or some other
+ * server-generated identifying string.
+ */
+ public String getSourceData() {
+ return mProviderData;
+ }
+
+ /**
+ * Gets the Uri that can be used to refer to this voicemail, and to make it play.
+ * <p>
+ * Returns null if we don't know the Uri.
+ */
+ public Uri getUri() {
+ return mUri;
+ }
+
+ /**
+ * Tells us if the voicemail message has been marked as read.
+ * <p>
+ * Always returns false if this field has not been set, i.e. if hasRead() returns false.
+ */
+ public boolean isRead() {
+ return mIsRead;
+ }
+
+ /**
+ * Tells us if there is content stored at the Uri.
+ */
+ public boolean hasContent() {
+ return mHasContent;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mTimestamp);
+ dest.writeCharSequence(mNumber);
+ if (mPhoneAccount == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ mPhoneAccount.writeToParcel(dest, flags);
+ }
+ dest.writeLong(mId);
+ dest.writeLong(mDuration);
+ dest.writeCharSequence(mSource);
+ dest.writeCharSequence(mProviderData);
+ if (mUri == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ mUri.writeToParcel(dest, flags);
+ }
+ if (mIsRead) {
+ dest.writeInt(1);
+ } else {
+ dest.writeInt(0);
+ }
+ if (mHasContent) {
+ dest.writeInt(1);
+ } else {
+ dest.writeInt(0);
+ }
+ }
+
+ public static final Creator<Voicemail> CREATOR
+ = new Creator<Voicemail>() {
+ @Override
+ public Voicemail createFromParcel(Parcel in) {
+ return new Voicemail(in);
+ }
+
+ @Override
+ public Voicemail[] newArray(int size) {
+ return new Voicemail[size];
+ }
+ };
+
+ private Voicemail(Parcel in) {
+ mTimestamp = in.readLong();
+ mNumber = (String) in.readCharSequence();
+ if (in.readInt() > 0) {
+ mPhoneAccount = PhoneAccountHandle.CREATOR.createFromParcel(in);
+ } else {
+ mPhoneAccount = null;
+ }
+ mId = in.readLong();
+ mDuration = in.readLong();
+ mSource = (String) in.readCharSequence();
+ mProviderData = (String) in.readCharSequence();
+ if (in.readInt() > 0) {
+ mUri = Uri.CREATOR.createFromParcel(in);
+ } else {
+ mUri = null;
+ }
+ mIsRead = in.readInt() > 0 ? true : false;
+ mHasContent = in.readInt() > 0 ? true : false;
+ }
+}