summaryrefslogtreecommitdiff
path: root/telecomm
diff options
context:
space:
mode:
Diffstat (limited to 'telecomm')
-rw-r--r--telecomm/java/android/telecom/BluetoothCallQualityReport.aidl22
-rw-r--r--telecomm/java/android/telecom/BluetoothCallQualityReport.java278
-rwxr-xr-xtelecomm/java/android/telecom/Call.java58
-rw-r--r--telecomm/java/android/telecom/CallDiagnosticService.java328
-rw-r--r--telecomm/java/android/telecom/Connection.java60
-rw-r--r--telecomm/java/android/telecom/ConnectionRequest.java4
-rwxr-xr-xtelecomm/java/android/telecom/ConnectionService.java98
-rw-r--r--telecomm/java/android/telecom/DiagnosticCall.java381
-rw-r--r--telecomm/java/android/telecom/InCallService.java18
-rw-r--r--telecomm/java/android/telecom/Log.java2
-rw-r--r--telecomm/java/android/telecom/PhoneAccount.java9
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java35
-rw-r--r--telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl37
-rw-r--r--telecomm/java/com/android/internal/telecom/ICallDiagnosticServiceAdapter.aidl32
-rw-r--r--telecomm/java/com/android/internal/telecom/IConnectionService.aidl5
-rw-r--r--telecomm/java/com/android/internal/telecom/ITelecomService.aidl8
16 files changed, 1354 insertions, 21 deletions
diff --git a/telecomm/java/android/telecom/BluetoothCallQualityReport.aidl b/telecomm/java/android/telecom/BluetoothCallQualityReport.aidl
new file mode 100644
index 000000000000..685fe9c927b1
--- /dev/null
+++ b/telecomm/java/android/telecom/BluetoothCallQualityReport.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2021, 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;
+
+/**
+ * {@hide}
+ */
+parcelable BluetoothCallQualityReport;
diff --git a/telecomm/java/android/telecom/BluetoothCallQualityReport.java b/telecomm/java/android/telecom/BluetoothCallQualityReport.java
new file mode 100644
index 000000000000..8703d84831ff
--- /dev/null
+++ b/telecomm/java/android/telecom/BluetoothCallQualityReport.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2020 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.ElapsedRealtimeLong;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * This class represents the quality report that bluetooth framework sends
+ * whenever there's a bad voice quality is detected from their side.
+ * It is sent as part of a call event via {@link Call#sendCallEvent(String, Bundle)}
+ * associated with extra EXTRA_BLUETOOTH_CALL_QUALITY_REPORT.
+ * Note that this report will be sent only during an active voice/voip call.
+ * @hide
+ */
+@SystemApi
+public final class BluetoothCallQualityReport implements Parcelable {
+
+ /**
+ * Event that is sent via {@link Call#sendCallEvent(String, Bundle)} for a call quality report
+ */
+ public static final String EVENT_BLUETOOTH_CALL_QUALITY_REPORT =
+ "android.telecom.event.BLUETOOTH_CALL_QUALITY_REPORT";
+
+ /**
+ * Extra key sent with {@link Call#sendCallEvent(String, Bundle)}
+ */
+ public static final String EXTRA_BLUETOOTH_CALL_QUALITY_REPORT =
+ "android.telecom.extra.BLUETOOTH_CALL_QUALITY_REPORT";
+
+ private final long mSentTimestampMillis;
+ private final boolean mChoppyVoice;
+ private final int mRssiDbm;
+ private final int mSnrDb;
+ private final int mRetransmittedPacketsCount;
+ private final int mPacketsNotReceivedCount;
+ private final int mNegativeAcknowledgementCount;
+
+ /**
+ * @return Time in milliseconds since the epoch. Designates when report was sent.
+ * Used to determine whether this report arrived too late to be useful.
+ */
+ public @ElapsedRealtimeLong long getSentTimestampMillis() {
+ return mSentTimestampMillis;
+ }
+
+ /**
+ * @return {@code true} if bluetooth hardware detects voice is choppy
+ */
+ public boolean isChoppyVoice() {
+ return mChoppyVoice;
+ }
+
+ /**
+ * @return Received Signal Strength Indication (RSSI) value in dBm.
+ * This value shall be an absolute received signal strength value.
+ */
+ public @IntRange(from = -127, to = 20) int getRssiDbm() {
+ return mRssiDbm;
+ }
+
+ /**
+ * @return Signal-to-Noise Ratio (SNR) value in dB.
+ * The controller shall provide the average SNR of all the channels currently used by the link.
+ */
+ public int getSnrDb() {
+ return mSnrDb;
+ }
+
+ /**
+ * @return The number of retransmissions since the last event.
+ * This count shall be reset after it is reported.
+ */
+ public @IntRange(from = 0) int getRetransmittedPacketsCount() {
+ return mRetransmittedPacketsCount;
+ }
+
+ /**
+ * @return No RX count since the last event.
+ * The count increases when no packet is received at the scheduled time slot or the received
+ * packet is corrupted.
+ * This count shall be reset after it is reported.
+ */
+ public @IntRange(from = 0) int getPacketsNotReceivedCount() {
+ return mPacketsNotReceivedCount;
+ }
+
+ /**
+ * @return NAK (Negative Acknowledge) count since the last event.
+ * This count shall be reset after it is reported.
+ */
+ public @IntRange(from = 0) int getNegativeAcknowledgementCount() {
+ return mNegativeAcknowledgementCount;
+ }
+
+ //
+ // Parcelable implementation
+ //
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeLong(mSentTimestampMillis);
+ out.writeBoolean(mChoppyVoice);
+ out.writeInt(mRssiDbm);
+ out.writeInt(mSnrDb);
+ out.writeInt(mRetransmittedPacketsCount);
+ out.writeInt(mPacketsNotReceivedCount);
+ out.writeInt(mNegativeAcknowledgementCount);
+ }
+
+ public static final @android.annotation.NonNull Creator<BluetoothCallQualityReport> CREATOR =
+ new Creator<BluetoothCallQualityReport>() {
+ @Override
+ public BluetoothCallQualityReport createFromParcel(Parcel in) {
+ return new BluetoothCallQualityReport(in);
+ }
+
+ @Override
+ public BluetoothCallQualityReport[] newArray(int size) {
+ return new BluetoothCallQualityReport[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ BluetoothCallQualityReport that = (BluetoothCallQualityReport) o;
+ return mSentTimestampMillis == that.mSentTimestampMillis
+ && mChoppyVoice == that.mChoppyVoice && mRssiDbm == that.mRssiDbm
+ && mSnrDb == that.mSnrDb
+ && mRetransmittedPacketsCount == that.mRetransmittedPacketsCount
+ && mPacketsNotReceivedCount == that.mPacketsNotReceivedCount
+ && mNegativeAcknowledgementCount == that.mNegativeAcknowledgementCount;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSentTimestampMillis, mChoppyVoice, mRssiDbm, mSnrDb,
+ mRetransmittedPacketsCount, mPacketsNotReceivedCount,
+ mNegativeAcknowledgementCount);
+ }
+
+ /**
+ * Builder class for {@link ConnectionRequest}
+ */
+ public static final class Builder {
+ private long mSentTimestampMillis;
+ private boolean mChoppyVoice;
+ private int mRssiDbm;
+ private int mSnrDb;
+ private int mRetransmittedPacketsCount;
+ private int mPacketsNotReceivedCount;
+ private int mNegativeAcknowledgementCount;
+
+ public Builder() { }
+
+ /**
+ * Set the time when report was sent in milliseconds since the epoch.
+ * @param sentTimestampMillis
+ */
+ public @NonNull Builder setSentTimestampMillis(long sentTimestampMillis) {
+ mSentTimestampMillis = sentTimestampMillis;
+ return this;
+ }
+
+ /**
+ * Set if bluetooth hardware detects voice is choppy
+ * @param choppyVoice
+ */
+ public @NonNull Builder setChoppyVoice(boolean choppyVoice) {
+ mChoppyVoice = choppyVoice;
+ return this;
+ }
+
+ /**
+ * Set Received Signal Strength Indication (RSSI) value in dBm.
+ * @param rssiDbm
+ */
+ public @NonNull Builder setRssiDbm(int rssiDbm) {
+ mRssiDbm = rssiDbm;
+ return this;
+ }
+
+ /**
+ * Set Signal-to-Noise Ratio (SNR) value in dB.
+ * @param snrDb
+ */
+ public @NonNull Builder setSnrDb(int snrDb) {
+ mSnrDb = snrDb;
+ return this;
+ }
+
+ /**
+ * Set The number of retransmissions since the last event.
+ * @param retransmittedPacketsCount
+ */
+ public @NonNull Builder setRetransmittedPacketsCount(
+ int retransmittedPacketsCount) {
+ mRetransmittedPacketsCount = retransmittedPacketsCount;
+ return this;
+ }
+
+ /**
+ * Set No RX count since the last event.
+ * @param packetsNotReceivedCount
+ */
+ public @NonNull Builder setPacketsNotReceivedCount(
+ int packetsNotReceivedCount) {
+ mPacketsNotReceivedCount = packetsNotReceivedCount;
+ return this;
+ }
+
+ /**
+ * Set NAK (Negative Acknowledge) count since the last event.
+ * @param negativeAcknowledgementCount
+ */
+ public @NonNull Builder setNegativeAcknowledgementCount(
+ int negativeAcknowledgementCount) {
+ mNegativeAcknowledgementCount = negativeAcknowledgementCount;
+ return this;
+ }
+
+ /**
+ * Build the {@link BluetoothCallQualityReport}
+ * @return Result of the builder
+ */
+ public @NonNull BluetoothCallQualityReport build() {
+ return new BluetoothCallQualityReport(this);
+ }
+ }
+
+ private BluetoothCallQualityReport(Parcel in) {
+ mSentTimestampMillis = in.readLong();
+ mChoppyVoice = in.readBoolean();
+ mRssiDbm = in.readInt();
+ mSnrDb = in.readInt();
+ mRetransmittedPacketsCount = in.readInt();
+ mPacketsNotReceivedCount = in.readInt();
+ mNegativeAcknowledgementCount = in.readInt();
+ }
+
+ private BluetoothCallQualityReport(Builder builder) {
+ mSentTimestampMillis = builder.mSentTimestampMillis;
+ mChoppyVoice = builder.mChoppyVoice;
+ mRssiDbm = builder.mRssiDbm;
+ mSnrDb = builder.mSnrDb;
+ mRetransmittedPacketsCount = builder.mRetransmittedPacketsCount;
+ mPacketsNotReceivedCount = builder.mPacketsNotReceivedCount;
+ mNegativeAcknowledgementCount = builder.mNegativeAcknowledgementCount;
+ }
+}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 1238e7b69a87..4679b9c497bd 100755
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -267,6 +267,64 @@ public final class Call {
public static final String EVENT_HANDOVER_FAILED =
"android.telecom.event.HANDOVER_FAILED";
+ /**
+ * Event reported from the Telecom stack to report an in-call diagnostic message which the
+ * dialer app may opt to display to the user. A diagnostic message is used to communicate
+ * scenarios the device has detected which may impact the quality of the ongoing call.
+ * <p>
+ * For example a problem with a bluetooth headset may generate a recommendation for the user to
+ * try using the speakerphone instead, or if the device detects it has entered a poor service
+ * area, the user might be warned so that they can finish their call prior to it dropping.
+ * <p>
+ * A diagnostic message is considered persistent in nature. When the user enters a poor service
+ * area, for example, the accompanying diagnostic message persists until they leave the area
+ * of poor service. Each message is accompanied with a {@link #EXTRA_DIAGNOSTIC_MESSAGE_ID}
+ * which uniquely identifies the diagnostic condition being reported. The framework raises a
+ * call event of type {@link #EVENT_CLEAR_DIAGNOSTIC_MESSAGE} when the condition reported has
+ * been cleared. The dialer app should display the diagnostic message until it is cleared.
+ * If multiple diagnostic messages are sent with different IDs (which have not yet been cleared)
+ * the dialer app should prioritize the most recently received message, but still provide the
+ * user with a means to review past messages.
+ * <p>
+ * The text of the message is found in {@link #EXTRA_DIAGNOSTIC_MESSAGE} in the form of a human
+ * readable {@link CharSequence} which is intended for display in the call UX.
+ * <p>
+ * The telecom framework audibly notifies the user of the presence of a diagnostic message, so
+ * the dialer app needs only to concern itself with visually displaying the message.
+ * <p>
+ * The dialer app receives this event via
+ * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}.
+ */
+ public static final String EVENT_DISPLAY_DIAGNOSTIC_MESSAGE =
+ "android.telecom.event.DISPLAY_DIAGNOSTIC_MESSAGE";
+
+ /**
+ * Event reported from the telecom framework when a diagnostic message previously raised with
+ * {@link #EVENT_DISPLAY_DIAGNOSTIC_MESSAGE} has cleared and is no longer pertinent.
+ * <p>
+ * The {@link #EXTRA_DIAGNOSTIC_MESSAGE_ID} indicates the diagnostic message which has been
+ * cleared.
+ * <p>
+ * The dialer app receives this event via
+ * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}.
+ */
+ public static final String EVENT_CLEAR_DIAGNOSTIC_MESSAGE =
+ "android.telecom.event.CLEAR_DIAGNOSTIC_MESSAGE";
+
+ /**
+ * Integer extra representing a message ID for a message posted via
+ * {@link #EVENT_DISPLAY_DIAGNOSTIC_MESSAGE}. Used to ensure that the dialer app knows when
+ * the message in question has cleared via {@link #EVENT_CLEAR_DIAGNOSTIC_MESSAGE}.
+ */
+ public static final String EXTRA_DIAGNOSTIC_MESSAGE_ID =
+ "android.telecom.extra.DIAGNOSTIC_MESSAGE_ID";
+
+ /**
+ * {@link CharSequence} extra used with {@link #EVENT_DISPLAY_DIAGNOSTIC_MESSAGE}. This is the
+ * diagnostic message the dialer app should display.
+ */
+ public static final String EXTRA_DIAGNOSTIC_MESSAGE =
+ "android.telecom.extra.DIAGNOSTIC_MESSAGE";
/**
* Reject reason used with {@link #reject(int)} to indicate that the user is rejecting this
diff --git a/telecomm/java/android/telecom/CallDiagnosticService.java b/telecomm/java/android/telecom/CallDiagnosticService.java
new file mode 100644
index 000000000000..201c5db74e16
--- /dev/null
+++ b/telecomm/java/android/telecom/CallDiagnosticService.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2021 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.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+
+import com.android.internal.telecom.ICallDiagnosticService;
+import com.android.internal.telecom.ICallDiagnosticServiceAdapter;
+
+import java.util.Map;
+
+/**
+ * The platform supports a single OEM provided {@link CallDiagnosticService}, as defined by the
+ * {@code call_diagnostic_service_package_name} key in the
+ * {@code packages/services/Telecomm/res/values/config.xml} file. An OEM can use this API to help
+ * provide more actionable information about calling issues the user encounters during and after
+ * a call.
+ *
+ * <h1>Manifest Declaration</h1>
+ * The following is an example of how to declare the service entry in the
+ * {@link CallDiagnosticService} manifest file:
+ * <pre>
+ * {@code
+ * <service android:name="your.package.YourCallDiagnosticServiceImplementation"
+ * android:permission="android.permission.BIND_CALL_DIAGNOSTIC_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.telecom.CallDiagnosticService"/>
+ * </intent-filter>
+ * </service>
+ * }
+ * </pre>
+ * @hide
+ */
+@SystemApi
+public abstract class CallDiagnosticService extends Service {
+
+ /**
+ * Binder stub implementation which handles incoming requests from Telecom.
+ */
+ private final class CallDiagnosticServiceBinder extends ICallDiagnosticService.Stub {
+
+ @Override
+ public void setAdapter(ICallDiagnosticServiceAdapter adapter) throws RemoteException {
+ handleSetAdapter(adapter);
+ }
+
+ @Override
+ public void initializeDiagnosticCall(ParcelableCall call) throws RemoteException {
+ handleCallAdded(call);
+ }
+
+ @Override
+ public void updateCall(ParcelableCall call) throws RemoteException {
+ handleCallUpdated(call);
+ }
+
+ @Override
+ public void removeDiagnosticCall(String callId) throws RemoteException {
+ handleCallRemoved(callId);
+ }
+
+ @Override
+ public void updateCallAudioState(CallAudioState callAudioState) throws RemoteException {
+ onCallAudioStateChanged(callAudioState);
+ }
+
+ @Override
+ public void receiveDeviceToDeviceMessage(String callId, int message, int value) {
+ handleReceivedD2DMessage(callId, message, value);
+ }
+
+ @Override
+ public void receiveBluetoothCallQualityReport(BluetoothCallQualityReport qualityReport)
+ throws RemoteException {
+ handleBluetoothCallQualityReport(qualityReport);
+ }
+ }
+
+ /**
+ * Listens to events raised by a {@link DiagnosticCall}.
+ */
+ private android.telecom.DiagnosticCall.Listener mDiagnosticCallListener =
+ new android.telecom.DiagnosticCall.Listener() {
+
+ @Override
+ public void onSendDeviceToDeviceMessage(DiagnosticCall diagnosticCall,
+ @DiagnosticCall.MessageType int message, int value) {
+ handleSendDeviceToDeviceMessage(diagnosticCall, message, value);
+ }
+
+ @Override
+ public void onDisplayDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId,
+ CharSequence message) {
+ handleDisplayDiagnosticMessage(diagnosticCall, messageId, message);
+ }
+
+ @Override
+ public void onClearDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId) {
+ handleClearDiagnosticMessage(diagnosticCall, messageId);
+ }
+ };
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE = "android.telecom.CallDiagnosticService";
+
+ /**
+ * Map which tracks the Telecom calls received from the Telecom stack.
+ */
+ private final Map<String, Call.Details> mCallByTelecomCallId = new ArrayMap<>();
+ private final Map<String, DiagnosticCall> mDiagnosticCallByTelecomCallId = new ArrayMap<>();
+ private ICallDiagnosticServiceAdapter mAdapter;
+
+ @Nullable
+ @Override
+ public IBinder onBind(@NonNull Intent intent) {
+ Log.i(this, "onBind!");
+ return new CallDiagnosticServiceBinder();
+ }
+
+ /**
+ * Telecom calls this method on the {@link CallDiagnosticService} with details about a new call
+ * which was added to Telecom.
+ * <p>
+ * The {@link CallDiagnosticService} returns an implementation of {@link DiagnosticCall} to be
+ * used for the lifespan of this call.
+ *
+ * @param call The details of the new call.
+ * @return An instance of {@link DiagnosticCall} which the {@link CallDiagnosticService}
+ * provides to be used for the lifespan of the call.
+ * @throws IllegalArgumentException if a {@code null} {@link DiagnosticCall} is returned.
+ */
+ public abstract @NonNull DiagnosticCall onInitializeDiagnosticCall(@NonNull
+ android.telecom.Call.Details call);
+
+ /**
+ * Telecom calls this method when a previous created {@link DiagnosticCall} is no longer needed.
+ * This happens when Telecom is no longer tracking the call in question.
+ * @param call The diagnostic call which is no longer tracked by Telecom.
+ */
+ public abstract void onRemoveDiagnosticCall(@NonNull DiagnosticCall call);
+
+ /**
+ * Telecom calls this method when the audio routing or available audio route information
+ * changes.
+ * <p>
+ * Audio state is common to all calls.
+ *
+ * @param audioState The new audio state.
+ */
+ public abstract void onCallAudioStateChanged(
+ @NonNull CallAudioState audioState);
+
+ /**
+ * Telecom calls this method when a {@link BluetoothCallQualityReport} is received from the
+ * bluetooth stack.
+ * @param qualityReport the {@link BluetoothCallQualityReport}.
+ */
+ public abstract void onBluetoothCallQualityReportReceived(
+ @NonNull BluetoothCallQualityReport qualityReport);
+
+ /**
+ * Handles a request from Telecom to set the adapater used to communicate back to Telecom.
+ * @param adapter
+ */
+ private void handleSetAdapter(@NonNull ICallDiagnosticServiceAdapter adapter) {
+ mAdapter = adapter;
+ }
+
+ /**
+ * Handles a request from Telecom to add a new call.
+ * @param parcelableCall
+ */
+ private void handleCallAdded(@NonNull ParcelableCall parcelableCall) {
+ String telecomCallId = parcelableCall.getId();
+ Log.i(this, "handleCallAdded: callId=%s - added", telecomCallId);
+ Call.Details newCallDetails = Call.Details.createFromParcelableCall(parcelableCall);
+ mCallByTelecomCallId.put(telecomCallId, newCallDetails);
+
+ DiagnosticCall diagnosticCall = onInitializeDiagnosticCall(newCallDetails);
+ if (diagnosticCall == null) {
+ throw new IllegalArgumentException("A valid DiagnosticCall instance was not provided.");
+ }
+ diagnosticCall.setListener(mDiagnosticCallListener);
+ diagnosticCall.setCallId(telecomCallId);
+ mDiagnosticCallByTelecomCallId.put(telecomCallId, diagnosticCall);
+ }
+
+ /**
+ * Handles an update to {@link Call.Details} notified by Telecom.
+ * Caches the call details and notifies the {@link DiagnosticCall} of the change via
+ * {@link DiagnosticCall#onCallDetailsChanged(Call.Details)}.
+ * @param parcelableCall the new parceled call details from Telecom.
+ */
+ private void handleCallUpdated(@NonNull ParcelableCall parcelableCall) {
+ String telecomCallId = parcelableCall.getId();
+ Log.i(this, "handleCallUpdated: callId=%s - updated", telecomCallId);
+ Call.Details newCallDetails = Call.Details.createFromParcelableCall(parcelableCall);
+
+ DiagnosticCall diagnosticCall = mDiagnosticCallByTelecomCallId.get(telecomCallId);
+ mCallByTelecomCallId.put(telecomCallId, newCallDetails);
+ diagnosticCall.handleCallUpdated(newCallDetails);
+ }
+
+ /**
+ * Handles a request from Telecom to remove an existing call.
+ * @param telecomCallId
+ */
+ private void handleCallRemoved(@NonNull String telecomCallId) {
+ Log.i(this, "handleCallRemoved: callId=%s - removed", telecomCallId);
+
+ if (mCallByTelecomCallId.containsKey(telecomCallId)) {
+ mCallByTelecomCallId.remove(telecomCallId);
+ }
+ if (mDiagnosticCallByTelecomCallId.containsKey(telecomCallId)) {
+ DiagnosticCall call = mDiagnosticCallByTelecomCallId.remove(telecomCallId);
+ // Inform the service of the removed call.
+ onRemoveDiagnosticCall(call);
+ }
+ }
+
+ /**
+ * Handles an incoming device to device message received from Telecom. Notifies the
+ * {@link DiagnosticCall} via {@link DiagnosticCall#onReceiveDeviceToDeviceMessage(int, int)}.
+ * @param callId
+ * @param message
+ * @param value
+ */
+ private void handleReceivedD2DMessage(@NonNull String callId, int message, int value) {
+ Log.i(this, "handleReceivedD2DMessage: callId=%s, msg=%d/%d", callId, message, value);
+ DiagnosticCall diagnosticCall = mDiagnosticCallByTelecomCallId.get(callId);
+ diagnosticCall.onReceiveDeviceToDeviceMessage(message, value);
+ }
+
+ /**
+ * Handles an incoming bluetooth call quality report from Telecom. Notifies via
+ * {@link CallDiagnosticService#onBluetoothCallQualityReportReceived(
+ * BluetoothCallQualityReport)}.
+ * @param qualityReport The bluetooth call quality remote.
+ */
+ private void handleBluetoothCallQualityReport(@NonNull BluetoothCallQualityReport
+ qualityReport) {
+ Log.i(this, "handleBluetoothCallQualityReport; report=%s", qualityReport);
+ onBluetoothCallQualityReportReceived(qualityReport);
+ }
+
+ /**
+ * Handles a request from a {@link DiagnosticCall} to send a device to device message (received
+ * via {@link DiagnosticCall#sendDeviceToDeviceMessage(int, int)}.
+ * @param diagnosticCall
+ * @param message
+ * @param value
+ */
+ private void handleSendDeviceToDeviceMessage(@NonNull DiagnosticCall diagnosticCall,
+ int message, int value) {
+ String callId = diagnosticCall.getCallId();
+ try {
+ mAdapter.sendDeviceToDeviceMessage(callId, message, value);
+ Log.i(this, "handleSendDeviceToDeviceMessage: call=%s; msg=%d/%d", callId, message,
+ value);
+ } catch (RemoteException e) {
+ Log.w(this, "handleSendDeviceToDeviceMessage: call=%s; msg=%d/%d failed %s",
+ callId, message, value, e);
+ }
+ }
+
+ /**
+ * Handles a request from a {@link DiagnosticCall} to display an in-call diagnostic message.
+ * Originates from {@link DiagnosticCall#displayDiagnosticMessage(int, CharSequence)}.
+ * @param diagnosticCall
+ * @param messageId
+ * @param message
+ */
+ private void handleDisplayDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId,
+ CharSequence message) {
+ String callId = diagnosticCall.getCallId();
+ try {
+ mAdapter.displayDiagnosticMessage(callId, messageId, message);
+ Log.i(this, "handleDisplayDiagnosticMessage: call=%s; msg=%d/%s", callId, messageId,
+ message);
+ } catch (RemoteException e) {
+ Log.w(this, "handleDisplayDiagnosticMessage: call=%s; msg=%d/%s failed %s",
+ callId, messageId, message, e);
+ }
+ }
+
+ /**
+ * Handles a request from a {@link DiagnosticCall} to clear a previously shown diagnostic
+ * message.
+ * Originates from {@link DiagnosticCall#clearDiagnosticMessage(int)}.
+ * @param diagnosticCall
+ * @param messageId
+ */
+ private void handleClearDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId) {
+ String callId = diagnosticCall.getCallId();
+ try {
+ mAdapter.clearDiagnosticMessage(callId, messageId);
+ Log.i(this, "handleClearDiagnosticMessage: call=%s; msg=%d", callId, messageId);
+ } catch (RemoteException e) {
+ Log.w(this, "handleClearDiagnosticMessage: call=%s; msg=%d failed %s",
+ callId, messageId, e);
+ }
+ }
+}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index e55720c6dc7e..973b20ad0713 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -926,6 +926,46 @@ public abstract class Connection extends Conferenceable {
public static final String EVENT_RTT_AUDIO_INDICATION_CHANGED =
"android.telecom.event.RTT_AUDIO_INDICATION_CHANGED";
+ /**
+ * Connection event used to signal between the telephony {@link ConnectionService} and Telecom
+ * when device to device messages are sent/received.
+ * <p>
+ * Device to device messages originating from the network are sent by telephony using
+ * {@link Connection#sendConnectionEvent(String, Bundle)} and are routed up to any active
+ * {@link CallDiagnosticService} implementation which is active.
+ * <p>
+ * Likewise, if a {@link CallDiagnosticService} sends a message using
+ * {@link DiagnosticCall#sendDeviceToDeviceMessage(int, int)}, it will be routed to telephony
+ * via {@link Connection#onCallEvent(String, Bundle)}. The telephony stack will relay the
+ * message to the other device.
+ * @hide
+ */
+ @SystemApi
+ public static final String EVENT_DEVICE_TO_DEVICE_MESSAGE =
+ "android.telecom.event.DEVICE_TO_DEVICE_MESSAGE";
+
+ /**
+ * Sent along with {@link #EVENT_DEVICE_TO_DEVICE_MESSAGE} to indicate the device to device
+ * message type.
+ *
+ * See {@link DiagnosticCall} for more information.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE =
+ "android.telecom.extra.DEVICE_TO_DEVICE_MESSAGE_TYPE";
+
+ /**
+ * Sent along with {@link #EVENT_DEVICE_TO_DEVICE_MESSAGE} to indicate the device to device
+ * message value.
+ * <p>
+ * See {@link DiagnosticCall} for more information.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE =
+ "android.telecom.extra.DEVICE_TO_DEVICE_MESSAGE_VALUE";
+
// Flag controlling whether PII is emitted into the logs
private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
@@ -3005,6 +3045,26 @@ public abstract class Connection extends Conferenceable {
public void onCallAudioStateChanged(CallAudioState state) {}
/**
+ * Inform this Connection when it will or will not be tracked by an {@link InCallService} which
+ * can provide an InCall UI.
+ * This is primarily intended for use by Connections reported by self-managed
+ * {@link ConnectionService} which typically maintain their own UI.
+ *
+ * @param isUsingAlternativeUi Indicates whether an InCallService that can provide InCall UI is
+ * currently tracking the self-managed call.
+ */
+ public void onUsingAlternativeUi(boolean isUsingAlternativeUi) {}
+
+ /**
+ * Inform this Conenection when it will or will not be tracked by an non-UI
+ * {@link InCallService}.
+ *
+ * @param isTracked Indicates whether an non-UI InCallService is currently tracking the
+ * self-managed call.
+ */
+ public void onTrackedByNonUiService(boolean isTracked) {}
+
+ /**
* Notifies this Connection of an internal state change. This method is called after the
* state is changed.
*
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index b73ef9b794e4..be5fae488d5e 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -18,6 +18,7 @@ package android.telecom;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.net.Uri;
@@ -67,7 +68,8 @@ public final class ConnectionRequest implements Parcelable {
* Sets the participants for the resulting {@link ConnectionRequest}
* @param participants The participants to which the {@link Connection} is to connect.
*/
- public @NonNull Builder setParticipants(@Nullable List<Uri> participants) {
+ public @NonNull Builder setParticipants(
+ @SuppressLint("NullableCollection") @Nullable List<Uri> participants) {
this.mParticipants = participants;
return this;
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 334bc082a059..d82205e20538 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
@@ -137,6 +138,8 @@ public abstract class ConnectionService extends Service {
private static final String SESSION_HOLD = "CS.h";
private static final String SESSION_UNHOLD = "CS.u";
private static final String SESSION_CALL_AUDIO_SC = "CS.cASC";
+ private static final String SESSION_USING_ALTERNATIVE_UI = "CS.uAU";
+ private static final String SESSION_TRACKED_BY_NON_UI_SERVICE = "CS.tBNUS";
private static final String SESSION_PLAY_DTMF = "CS.pDT";
private static final String SESSION_STOP_DTMF = "CS.sDT";
private static final String SESSION_CONFERENCE = "CS.c";
@@ -200,6 +203,9 @@ public abstract class ConnectionService extends Service {
private static final int MSG_ADD_PARTICIPANT = 39;
private static final int MSG_EXPLICIT_CALL_TRANSFER = 40;
private static final int MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE = 41;
+ private static final int MSG_ON_CALL_FILTERING_COMPLETED = 42;
+ private static final int MSG_ON_USING_ALTERNATIVE_UI = 43;
+ private static final int MSG_ON_TRACKED_BY_NON_UI_SERVICE = 44;
private static Connection sNullConnection;
@@ -584,6 +590,36 @@ public abstract class ConnectionService extends Service {
}
@Override
+ public void onUsingAlternativeUi(String callId, boolean usingAlternativeUiShowing,
+ Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_USING_ALTERNATIVE_UI);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = usingAlternativeUiShowing;
+ args.arg3 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_ON_USING_ALTERNATIVE_UI, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void onTrackedByNonUiService(String callId, boolean isTracked,
+ Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_TRACKED_BY_NON_UI_SERVICE);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = isTracked;
+ args.arg3 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_ON_TRACKED_BY_NON_UI_SERVICE, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
public void playDtmfTone(String callId, char digit, Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_PLAY_DTMF);
try {
@@ -1226,6 +1262,34 @@ public abstract class ConnectionService extends Service {
}
break;
}
+ case MSG_ON_USING_ALTERNATIVE_UI: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession((Session) args.arg3,
+ SESSION_HANDLER + SESSION_USING_ALTERNATIVE_UI);
+ try {
+ String callId = (String) args.arg1;
+ boolean isUsingAlternativeUi = (boolean) args.arg2;
+ onUsingAlternativeUi(callId, isUsingAlternativeUi);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
+ case MSG_ON_TRACKED_BY_NON_UI_SERVICE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession((Session) args.arg3,
+ SESSION_HANDLER + SESSION_TRACKED_BY_NON_UI_SERVICE);
+ try {
+ String callId = (String) args.arg1;
+ boolean isTracked = (boolean) args.arg2;
+ onTrackedByNonUiService(callId, isTracked);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
case MSG_PLAY_DTMF_TONE: {
SomeArgs args = (SomeArgs) msg.obj;
try {
@@ -1824,6 +1888,7 @@ public abstract class ConnectionService extends Service {
/** {@inheritDoc} */
@Override
public final IBinder onBind(Intent intent) {
+ onBindClient(intent);
return mBinder;
}
@@ -1834,6 +1899,13 @@ public abstract class ConnectionService extends Service {
return super.onUnbind(intent);
}
+ /**
+ * Used for testing to let the test suite know when the connection service has been bound.
+ * @hide
+ */
+ @TestApi
+ public void onBindClient(@Nullable Intent intent) {
+ }
/**
* This can be used by telecom to either create a new outgoing conference call or attach
@@ -1928,10 +2000,12 @@ public abstract class ConnectionService extends Service {
request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER, false);
boolean isHandover = request.getExtras() != null && request.getExtras().getBoolean(
TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, false);
- Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +
- "isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b",
- callManagerAccount, callId, request, isIncoming, isUnknown, isLegacyHandover,
- isHandover);
+ boolean addSelfManaged = request.getExtras() != null && request.getExtras().getBoolean(
+ PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, true);
+ Log.i(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, "
+ + "isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b, "
+ + " addSelfManaged: %b", callManagerAccount, callId, request, isIncoming,
+ isUnknown, isLegacyHandover, isHandover, addSelfManaged);
Connection connection = null;
if (isHandover) {
@@ -2206,6 +2280,22 @@ public abstract class ConnectionService extends Service {
}
}
+ private void onUsingAlternativeUi(String callId, boolean isUsingAlternativeUi) {
+ Log.i(this, "onUsingAlternativeUi %s %s", callId, isUsingAlternativeUi);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "onUsingAlternativeUi")
+ .onUsingAlternativeUi(isUsingAlternativeUi);
+ }
+ }
+
+ private void onTrackedByNonUiService(String callId, boolean isTracked) {
+ Log.i(this, "onTrackedByNonUiService %s %s", callId, isTracked);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "onTrackedByNonUiService")
+ .onTrackedByNonUiService(isTracked);
+ }
+ }
+
private void playDtmfTone(String callId, char digit) {
Log.i(this, "playDtmfTone %s %c", callId, digit);
if (mConnectionById.containsKey(callId)) {
diff --git a/telecomm/java/android/telecom/DiagnosticCall.java b/telecomm/java/android/telecom/DiagnosticCall.java
new file mode 100644
index 000000000000..a4952899eb46
--- /dev/null
+++ b/telecomm/java/android/telecom/DiagnosticCall.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2021 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.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.telephony.Annotation;
+import android.telephony.CallQuality;
+import android.telephony.ims.ImsReasonInfo;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A {@link DiagnosticCall} provides a way for a {@link CallDiagnosticService} to receive diagnostic
+ * information about a mobile call on the device. The {@link CallDiagnosticService} can generate
+ * mid-call diagnostic messages using the {@link #displayDiagnosticMessage(int, CharSequence)} API
+ * which provides the user with valuable information about conditions impacting their call and
+ * corrective actions. For example, if the {@link CallDiagnosticService} determines that conditions
+ * on the call are degrading, it can inform the user that the call may soon drop and that they
+ * can try using a different calling method (e.g. VOIP or WIFI).
+ * @hide
+ */
+@SystemApi
+public abstract class DiagnosticCall {
+
+ /**
+ * @hide
+ */
+ public interface Listener {
+ void onSendDeviceToDeviceMessage(DiagnosticCall diagnosticCall, int message, int value);
+ void onDisplayDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId,
+ CharSequence message);
+ void onClearDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId);
+ }
+
+ /**
+ * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
+ * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the radio access type
+ * used for the current call. Based loosely on the
+ * {@link android.telephony.TelephonyManager#getNetworkType(int)} for the call, provides a
+ * high level summary of the call radio access type.
+ * <p>
+ * Valid values:
+ * <UL>
+ * <LI>{@link #NETWORK_TYPE_LTE}</LI>
+ * <LI>{@link #NETWORK_TYPE_IWLAN}</LI>
+ * <LI>{@link #NETWORK_TYPE_NR}</LI>
+ * </UL>
+ */
+ public static final int MESSAGE_CALL_NETWORK_TYPE = 1;
+
+ /**
+ * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
+ * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the call audio codec
+ * used for the current call. Based loosely on the {@link Connection#EXTRA_AUDIO_CODEC} for a
+ * call.
+ * <p>
+ * Valid values:
+ * <UL>
+ * <LI>{@link #AUDIO_CODEC_EVS}</LI>
+ * <LI>{@link #AUDIO_CODEC_AMR_WB}</LI>
+ * <LI>{@link #AUDIO_CODEC_AMR_NB}</LI>
+ * </UL>
+ */
+ public static final int MESSAGE_CALL_AUDIO_CODEC = 2;
+
+ /**
+ * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
+ * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the battery state of
+ * the device. Will typically mirror battery state reported via intents such as
+ * {@link android.content.Intent#ACTION_BATTERY_LOW}.
+ * <p>
+ * Valid values:
+ * <UL>
+ * <LI>{@link #BATTERY_STATE_LOW}</LI>
+ * <LI>{@link #BATTERY_STATE_GOOD}</LI>
+ * <LI>{@link #BATTERY_STATE_CHARGING}</LI>
+ * </UL>
+ */
+ public static final int MESSAGE_DEVICE_BATTERY_STATE = 3;
+
+ /**
+ * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
+ * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the overall network
+ * coverage as it pertains to the current call. A {@link CallDiagnosticService} should signal
+ * poor coverage if the network coverage reaches a level where there is a high probability of
+ * the call dropping as a result.
+ * <p>
+ * Valid values:
+ * <UL>
+ * <LI>{@link #COVERAGE_POOR}</LI>
+ * <LI>{@link #COVERAGE_GOOD}</LI>
+ * </UL>
+ */
+ public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4;
+
+ /**@hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "MESSAGE_", value = {
+ MESSAGE_CALL_NETWORK_TYPE,
+ MESSAGE_CALL_AUDIO_CODEC,
+ MESSAGE_DEVICE_BATTERY_STATE,
+ MESSAGE_DEVICE_NETWORK_COVERAGE
+ })
+ public @interface MessageType {}
+
+ /**
+ * Used with {@link #MESSAGE_CALL_NETWORK_TYPE} to indicate an LTE network is being used for the
+ * call.
+ */
+ public static final int NETWORK_TYPE_LTE = 1;
+
+ /**
+ * Used with {@link #MESSAGE_CALL_NETWORK_TYPE} to indicate WIFI calling is in use for the call.
+ */
+ public static final int NETWORK_TYPE_IWLAN = 2;
+
+ /**
+ * Used with {@link #MESSAGE_CALL_NETWORK_TYPE} to indicate a 5G NR (new radio) network is in
+ * used for the call.
+ */
+ public static final int NETWORK_TYPE_NR = 3;
+
+ /**
+ * Used with {@link #MESSAGE_CALL_AUDIO_CODEC} to indicate call audio is using the
+ * Enhanced Voice Services (EVS) codec for the call.
+ */
+ public static final int AUDIO_CODEC_EVS = 1;
+
+ /**
+ * Used with {@link #MESSAGE_CALL_AUDIO_CODEC} to indicate call audio is using the AMR
+ * (adaptive multi-rate) WB (wide band) audio codec.
+ */
+ public static final int AUDIO_CODEC_AMR_WB = 2;
+
+ /**
+ * Used with {@link #MESSAGE_CALL_AUDIO_CODEC} to indicate call audio is using the AMR
+ * (adaptive multi-rate) NB (narrow band) audio codec.
+ */
+ public static final int AUDIO_CODEC_AMR_NB = 3;
+
+ /**
+ * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is low.
+ */
+ public static final int BATTERY_STATE_LOW = 1;
+
+ /**
+ * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is not low.
+ */
+ public static final int BATTERY_STATE_GOOD = 2;
+
+ /**
+ * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is charging.
+ */
+ public static final int BATTERY_STATE_CHARGING = 3;
+
+ /**
+ * Used with {@link #MESSAGE_DEVICE_NETWORK_COVERAGE} to indicate that the coverage is poor.
+ */
+ public static final int COVERAGE_POOR = 1;
+
+ /**
+ * Used with {@link #MESSAGE_DEVICE_NETWORK_COVERAGE} to indicate that the coverage is good.
+ */
+ public static final int COVERAGE_GOOD = 2;
+
+ private Listener mListener;
+ private String mCallId;
+ private Call.Details mCallDetails;
+
+ /**
+ * @hide
+ */
+ public void setListener(@NonNull Listener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Sets the call ID for this {@link DiagnosticCall}.
+ * @param callId
+ * @hide
+ */
+ public void setCallId(@NonNull String callId) {
+ mCallId = callId;
+ }
+
+ /**
+ * @return the Telecom call ID for this {@link DiagnosticCall}.
+ * @hide
+ */
+ public @NonNull String getCallId() {
+ return mCallId;
+ }
+
+ /**
+ * Returns the latest {@link Call.Details} associated with this {@link DiagnosticCall} as
+ * reported by {@link #onCallDetailsChanged(Call.Details)}.
+ * @return The latest {@link Call.Details}.
+ */
+ public @NonNull Call.Details getCallDetails() {
+ return mCallDetails;
+ }
+
+ /**
+ * Telecom calls this method when the details of a call changes.
+ */
+ public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details details);
+
+ /**
+ * The {@link CallDiagnosticService} implements this method to handle messages received via
+ * device to device communication.
+ * <p>
+ * See {@link #sendDeviceToDeviceMessage(int, int)} for background on device to device
+ * communication.
+ * <p>
+ * The underlying device to device communication protocol assumes that where there the two
+ * devices communicating are using a different version of the protocol, messages the recipient
+ * are not aware of are silently discarded. This means an older client talking to a new client
+ * will not receive newer messages and values sent by the new client.
+ */
+ public abstract void onReceiveDeviceToDeviceMessage(
+ @MessageType int message,
+ int value);
+
+ /**
+ * Sends a device to device message to the device on the other end of this call.
+ * <p>
+ * Device to device communication is an Android platform feature which supports low bandwidth
+ * communication between Android devices while they are in a call. The device to device
+ * communication leverages DTMF tones or RTP header extensions to pass messages. The
+ * messages are unacknowledged and sent in a best-effort manner. The protocols assume that the
+ * nature of the message are informational only and are used only to convey basic state
+ * information between devices.
+ * <p>
+ * Device to device messages are intentional simplifications of more rich indicators in the
+ * platform due to the extreme bandwidth constraints inherent with underlying device to device
+ * communication transports used by the telephony framework. Device to device communication is
+ * either accomplished by adding RFC8285 compliant RTP header extensions to the audio packets
+ * for a call, or using the DTMF digits A-D as a communication pathway. Signalling requirements
+ * for DTMF digits place a significant limitation on the amount of information which can be
+ * communicated during a call.
+ * <p>
+ * Allowed message types and values are:
+ * <ul>
+ * <li>{@link #MESSAGE_CALL_NETWORK_TYPE}
+ * <ul>
+ * <li>{@link #NETWORK_TYPE_LTE}</li>
+ * <li>{@link #NETWORK_TYPE_IWLAN}</li>
+ * <li>{@link #NETWORK_TYPE_NR}</li>
+ * </ul>
+ * </li>
+ * <li>{@link #MESSAGE_CALL_AUDIO_CODEC}
+ * <ul>
+ * <li>{@link #AUDIO_CODEC_EVS}</li>
+ * <li>{@link #AUDIO_CODEC_AMR_WB}</li>
+ * <li>{@link #AUDIO_CODEC_AMR_NB}</li>
+ * </ul>
+ * </li>
+ * <li>{@link #MESSAGE_DEVICE_BATTERY_STATE}
+ * <ul>
+ * <li>{@link #BATTERY_STATE_LOW}</li>
+ * <li>{@link #BATTERY_STATE_GOOD}</li>
+ * <li>{@link #BATTERY_STATE_CHARGING}</li>
+ * </ul>
+ * </li>
+ * <li>{@link #MESSAGE_DEVICE_NETWORK_COVERAGE}
+ * <ul>
+ * <li>{@link #COVERAGE_POOR}</li>
+ * <li>{@link #COVERAGE_GOOD}</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * @param message The message type to send.
+ * @param value The message value corresponding to the type.
+ */
+ public final void sendDeviceToDeviceMessage(int message, int value) {
+ if (mListener != null) {
+ mListener.onSendDeviceToDeviceMessage(this, message, value);
+ }
+ }
+
+ /**
+ * Telecom calls this method when a GSM or CDMA call disconnects.
+ * The CallDiagnosticService can return a human readable disconnect message which will be passed
+ * to the Dialer app as the {@link DisconnectCause#getDescription()}. A dialer app typically
+ * shows this message at the termination of the call. If {@code null} is returned, the
+ * disconnect message generated by the telephony stack will be shown instead.
+ * <p>
+ * @param disconnectCause the disconnect cause for the call.
+ * @param preciseDisconnectCause the precise disconnect cause for the call.
+ * @return the disconnect message to use in place of the default Telephony message, or
+ * {@code null} if the default message will not be overridden.
+ */
+ // TODO: Wire in Telephony support for this.
+ public abstract @Nullable CharSequence onCallDisconnected(
+ @Annotation.DisconnectCauses int disconnectCause,
+ @Annotation.PreciseDisconnectCauses int preciseDisconnectCause);
+
+ /**
+ * Telecom calls this method when an IMS call disconnects and Telephony has already
+ * provided the disconnect reason info and disconnect message for the call. The
+ * {@link CallDiagnosticService} can intercept the raw IMS disconnect reason at this point and
+ * combine it with other call diagnostic information it is aware of to override the disconnect
+ * call message if desired.
+ *
+ * @param disconnectReason The {@link ImsReasonInfo} associated with the call disconnection.
+ * @return A user-readable call disconnect message to use in place of the platform-generated
+ * disconnect message, or {@code null} if the disconnect message should not be overridden.
+ */
+ // TODO: Wire in Telephony support for this.
+ public abstract @Nullable CharSequence onCallDisconnected(
+ @NonNull ImsReasonInfo disconnectReason);
+
+ /**
+ * Telecom calls this method when a {@link CallQuality} report is received from the telephony
+ * stack for a call.
+ * @param callQuality The call quality report for this call.
+ */
+ public abstract void onCallQualityReceived(@NonNull CallQuality callQuality);
+
+ /**
+ * Signals the active default dialer app to display a call diagnostic message. This can be
+ * used to report problems encountered during the span of a call.
+ * <p>
+ * The {@link CallDiagnosticService} provides a unique client-specific identifier used to
+ * identify the specific diagnostic message type.
+ * <p>
+ * The {@link CallDiagnosticService} should call {@link #clearDiagnosticMessage(int)} when the
+ * diagnostic condition has cleared.
+ * @param messageId the unique message identifier.
+ * @param message a human-readable, localized message to be shown to the user indicating a
+ * call issue which has occurred, along with potential mitigating actions.
+ */
+ public final void displayDiagnosticMessage(int messageId, @NonNull
+ CharSequence message) {
+ if (mListener != null) {
+ mListener.onDisplayDiagnosticMessage(this, messageId, message);
+ }
+ }
+
+ /**
+ * Signals to the active default dialer that the diagnostic message previously signalled using
+ * {@link #displayDiagnosticMessage(int, CharSequence)} with the specified messageId is no
+ * longer applicable (e.g. service has improved, for example.
+ * @param messageId the message identifier for a message previously shown via
+ * {@link #displayDiagnosticMessage(int, CharSequence)}.
+ */
+ public final void clearDiagnosticMessage(int messageId) {
+ if (mListener != null) {
+ mListener.onClearDiagnosticMessage(this, messageId);
+ }
+ }
+
+ /**
+ * Called by the {@link CallDiagnosticService} to update the call details for this
+ * {@link DiagnosticCall} based on an update received from Telecom.
+ * @param newDetails the new call details.
+ * @hide
+ */
+ public void handleCallUpdated(@NonNull Call.Details newDetails) {
+ mCallDetails = newDetails;
+ onCallDetailsChanged(newDetails);
+ }
+}
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 07de61759d59..0ff288b1ac63 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -138,6 +138,24 @@ import java.util.List;
* }
* }
* }
+ *
+ * </pre>
+ * <p id="companionInCallService">
+ * <h3>Access to InCallService for Wearable Devices</h3>
+ * <ol>
+ * If your app is a third-party companion app and wants to access InCallService APIs, what your
+ * app could do are:
+ * <p>
+ * <ol>
+ * <li> Declare MANAGE_ONGOING_CALLS permission in your manifest
+ * <li> Associate with a physical wearable device via the
+ * {@link android.companion.CompanionDeviceManager} API as a companion app. See:
+ * https://developer.android.com/guide/topics/connectivity/companion-device-pairing
+ * <li> Implement this InCallService with BIND_INCALL_SERVICE permission
+ * </ol>
+ * </ol>
+ * <p>
+ *
* </pre>
* <p id="incomingCallNotification">
* <h3>Showing the Incoming Call Notification</h3>
diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java
index 2a4fdcb1475d..922eddb6ac3e 100644
--- a/telecomm/java/android/telecom/Log.java
+++ b/telecomm/java/android/telecom/Log.java
@@ -522,7 +522,7 @@ public class Log {
return "";
}
return Arrays.stream(packageName.split("\\."))
- .map(s -> s.substring(0,1))
+ .map(s -> s.length() == 0 ? "" : s.substring(0, 1))
.collect(Collectors.joining(""));
}
}
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 835ecaa8c90d..579b33e9c283 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -188,6 +188,15 @@ public final class PhoneAccount implements Parcelable {
"android.telecom.extra.SKIP_CALL_FILTERING";
/**
+ * Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which
+ * indicates whether a Self-managed {@link PhoneAccount} want to expose its calls to all
+ * {@link InCallService} which declares the metadata
+ * {@link TelecomManager#METADATA_INCLUDE_SELF_MANAGED_CALLS}.
+ */
+ public static final String EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE =
+ "android.telecom.extra.ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE";
+
+ /**
* Flag indicating that this {@code PhoneAccount} can act as a connection manager for
* other connections. The {@link ConnectionService} associated with this {@code PhoneAccount}
* will be allowed to manage phone calls including using its own proprietary phone-call
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 3f8b68305914..746e72a0bde3 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -432,6 +432,14 @@ public class TelecomManager {
"android.telecom.extra.CALL_DISCONNECT_MESSAGE";
/**
+ * A string value for {@link #EXTRA_CALL_DISCONNECT_MESSAGE}, indicates the call was dropped by
+ * lower layers
+ * @hide
+ */
+ public static final String CALL_AUTO_DISCONNECT_MESSAGE_STRING =
+ "Call dropped by lower layers";
+
+ /**
* Optional extra for {@link android.telephony.TelephonyManager#ACTION_PHONE_STATE_CHANGED}
* containing the component name of the associated connection service.
* @hide
@@ -1649,24 +1657,25 @@ public class TelecomManager {
}
/**
- * Returns whether the caller has {@link InCallService} access for companion apps.
- *
- * A companion app is an app associated with a physical wearable device via the
- * {@link android.companion.CompanionDeviceManager} API.
+ * Returns whether the caller has {@link android.Manifest.permission#MANAGE_ONGOING_CALLS}
+ * permission. The permission can be obtained by associating with a physical wearable device
+ * via the {@link android.companion.CompanionDeviceManager} API as a companion app. If the
+ * caller app has the permission, it has {@link InCallService} access to manage ongoing calls.
*
* @return {@code true} if the caller has {@link InCallService} access for
* companion app; {@code false} otherwise.
*/
- public boolean hasCompanionInCallServiceAccess() {
- try {
- if (isServiceConnected()) {
- return getTelecomService().hasCompanionInCallServiceAccess(
+ public boolean hasManageOngoingCallsPermission() {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.hasManageOngoingCallsPermission(
mContext.getOpPackageName());
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling hasCompanionInCallServiceAccess().", e);
- if (!isSystemProcess()) {
- e.rethrowAsRuntimeException();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling hasManageOngoingCallsPermission().", e);
+ if (!isSystemProcess()) {
+ e.rethrowAsRuntimeException();
+ }
}
}
return false;
diff --git a/telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl b/telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl
new file mode 100644
index 000000000000..65b4d19b3d9b
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 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 com.android.internal.telecom;
+
+import android.telecom.BluetoothCallQualityReport;
+import android.telecom.CallAudioState;
+import android.telecom.ParcelableCall;
+import com.android.internal.telecom.ICallDiagnosticServiceAdapter;
+
+/**
+ * Internal remote interface for a call diagnostic service.
+ * @see android.telecom.CallDiagnosticService
+ * @hide
+ */
+oneway interface ICallDiagnosticService {
+ void setAdapter(in ICallDiagnosticServiceAdapter adapter);
+ void initializeDiagnosticCall(in ParcelableCall call);
+ void updateCall(in ParcelableCall call);
+ void updateCallAudioState(in CallAudioState callAudioState);
+ void removeDiagnosticCall(in String callId);
+ void receiveDeviceToDeviceMessage(in String callId, int message, int value);
+ void receiveBluetoothCallQualityReport(in BluetoothCallQualityReport qualityReport);
+}
diff --git a/telecomm/java/com/android/internal/telecom/ICallDiagnosticServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/ICallDiagnosticServiceAdapter.aidl
new file mode 100644
index 000000000000..92eec2a95430
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/ICallDiagnosticServiceAdapter.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 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 com.android.internal.telecom;
+
+import android.telecom.CallAudioState;
+import android.telecom.ParcelableCall;
+
+/**
+ * Remote interface for messages from the CallDiagnosticService to the platform.
+ * @see android.telecom.CallDiagnosticService
+ * @hide
+ */
+oneway interface ICallDiagnosticServiceAdapter {
+ void displayDiagnosticMessage(in String callId, int messageId, in CharSequence message);
+ void clearDiagnosticMessage(in String callId, int messageId);
+ void sendDeviceToDeviceMessage(in String callId, int message, int value);
+ void overrideDisconnectMessage(in String callId, in CharSequence message);
+}
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index fb5417994b57..d5555474f248 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -136,4 +136,9 @@ oneway interface IConnectionService {
int error, in Session.Info sessionInfo);
void handoverComplete(String callId, in Session.Info sessionInfo);
+
+ void onUsingAlternativeUi(String callId, boolean isUsingAlternativeUi,
+ in Session.Info sessionInfo);
+
+ void onTrackedByNonUiService(String callId, boolean isTracked, in Session.Info sessionInfo);
}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 6dc096daf4ea..0d190d89645a 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -179,9 +179,9 @@ interface ITelecomService {
boolean isInCall(String callingPackage, String callingFeatureId);
/**
- * @see TelecomServiceImpl#hasCompanionInCallServiceAccess
+ * @see TelecomServiceImpl#hasManageOngoingCallsPermission
*/
- boolean hasCompanionInCallServiceAccess(String callingPackage);
+ boolean hasManageOngoingCallsPermission(String callingPackage);
/**
* @see TelecomServiceImpl#isInManagedCall
@@ -351,4 +351,8 @@ interface ITelecomService {
*/
void setTestDefaultDialer(in String packageName);
+ /**
+ * @see TelecomServiceImpl#setTestCallDiagnosticService
+ */
+ void setTestCallDiagnosticService(in String packageName);
}