summaryrefslogtreecommitdiff
path: root/telecomm/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'telecomm/java/android')
-rw-r--r--telecomm/java/android/telecom/BluetoothCallQualityReport.aidl22
-rw-r--r--telecomm/java/android/telecom/BluetoothCallQualityReport.java22
-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.aidl22
-rw-r--r--telecomm/java/android/telecom/Connection.java175
-rwxr-xr-xtelecomm/java/android/telecom/ConnectionService.java45
-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/RemoteConnection.java19
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java14
12 files changed, 1046 insertions, 60 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
index 10339a818205..8703d84831ff 100644
--- a/telecomm/java/android/telecom/BluetoothCallQualityReport.java
+++ b/telecomm/java/android/telecom/BluetoothCallQualityReport.java
@@ -24,6 +24,8 @@ 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.
@@ -145,6 +147,26 @@ public final class BluetoothCallQualityReport implements Parcelable {
}
};
+ @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}
*/
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index d1cda08e8008..7334013093e2 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.aidl b/telecomm/java/android/telecom/Connection.aidl
new file mode 100644
index 000000000000..5b40036e46f4
--- /dev/null
+++ b/telecomm/java/android/telecom/Connection.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+/**
+ * {@hide}
+ */
+parcelable Connection.CallFilteringCompletionInfo;
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index e143160694a7..784a3af75f03 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -29,6 +29,7 @@ import android.annotation.SystemApi;
import android.app.Notification;
import android.bluetooth.BluetoothDevice;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
import android.content.Intent;
import android.hardware.camera2.CameraManager;
import android.net.Uri;
@@ -38,7 +39,9 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcel;
import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.os.SystemClock;
import android.telephony.ims.ImsStreamMediaProfile;
@@ -951,6 +954,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);
@@ -3398,6 +3441,121 @@ public abstract class Connection extends Conferenceable {
public void handleRttUpgradeResponse(@Nullable RttTextStream rttTextStream) {}
/**
+ * Information provided to a {@link Connection} upon completion of call filtering in Telecom.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class CallFilteringCompletionInfo implements Parcelable {
+ private final boolean mIsBlocked;
+ private final boolean mIsInContacts;
+ private final CallScreeningService.CallResponse mCallResponse;
+ private final ComponentName mCallScreeningComponent;
+
+ /**
+ * Constructor for {@link CallFilteringCompletionInfo}
+ *
+ * @param isBlocked Whether any part of the call filtering process indicated that this call
+ * should be blocked.
+ * @param isInContacts Whether the caller is in the user's contacts.
+ * @param callResponse The instance of {@link CallScreeningService.CallResponse} provided
+ * by the {@link CallScreeningService} that processed this call, or
+ * {@code null} if no call screening service ran.
+ * @param callScreeningComponent The component of the {@link CallScreeningService}
+ * that processed this call, or {@link null} if no
+ * call screening service ran.
+ */
+ public CallFilteringCompletionInfo(boolean isBlocked, boolean isInContacts,
+ @Nullable CallScreeningService.CallResponse callResponse,
+ @Nullable ComponentName callScreeningComponent) {
+ mIsBlocked = isBlocked;
+ mIsInContacts = isInContacts;
+ mCallResponse = callResponse;
+ mCallScreeningComponent = callScreeningComponent;
+ }
+
+ /** @hide */
+ protected CallFilteringCompletionInfo(Parcel in) {
+ mIsBlocked = in.readByte() != 0;
+ mIsInContacts = in.readByte() != 0;
+ CallScreeningService.ParcelableCallResponse response
+ = in.readParcelable(CallScreeningService.class.getClassLoader());
+ mCallResponse = response == null ? null : response.toCallResponse();
+ mCallScreeningComponent = in.readParcelable(ComponentName.class.getClassLoader());
+ }
+
+ @NonNull
+ public static final Creator<CallFilteringCompletionInfo> CREATOR =
+ new Creator<CallFilteringCompletionInfo>() {
+ @Override
+ public CallFilteringCompletionInfo createFromParcel(Parcel in) {
+ return new CallFilteringCompletionInfo(in);
+ }
+
+ @Override
+ public CallFilteringCompletionInfo[] newArray(int size) {
+ return new CallFilteringCompletionInfo[size];
+ }
+ };
+
+ /**
+ * @return Whether any part of the call filtering process indicated that this call should be
+ * blocked.
+ */
+ public boolean isBlocked() {
+ return mIsBlocked;
+ }
+
+ /**
+ * @return Whether the caller is in the user's contacts.
+ */
+ public boolean isInContacts() {
+ return mIsInContacts;
+ }
+
+ /**
+ * @return The instance of {@link CallScreeningService.CallResponse} provided
+ * by the {@link CallScreeningService} that processed this
+ * call, or {@code null} if no call screening service ran.
+ */
+ public @Nullable CallScreeningService.CallResponse getCallResponse() {
+ return mCallResponse;
+ }
+
+ /**
+ * @return The component of the {@link CallScreeningService}
+ * that processed this call, or {@code null} if no call screening service ran.
+ */
+ public @Nullable ComponentName getCallScreeningComponent() {
+ return mCallScreeningComponent;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "CallFilteringCompletionInfo{" +
+ "mIsBlocked=" + mIsBlocked +
+ ", mIsInContacts=" + mIsInContacts +
+ ", mCallResponse=" + mCallResponse +
+ ", mCallScreeningPackageName='" + mCallScreeningComponent + '\'' +
+ '}';
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByte((byte) (mIsBlocked ? 1 : 0));
+ dest.writeByte((byte) (mIsInContacts ? 1 : 0));
+ dest.writeParcelable(mCallResponse == null ? null : mCallResponse.toParcelable(), 0);
+ dest.writeParcelable(mCallScreeningComponent, 0);
+ }
+ }
+
+ /**
* Indicates that call filtering in Telecom is complete
*
* This method is called for a connection created via
@@ -3405,24 +3563,13 @@ public abstract class Connection extends Conferenceable {
* Telecom, including checking the blocked number db, per-contact settings, and custom call
* filtering apps.
*
- * @param isBlocked {@code true} if the call was blocked, {@code false} otherwise. If this is
- * {@code true}, {@link #onDisconnect()} will be called soon after
- * this is called.
- * @param isInContacts Indicates whether the caller is in the user's contacts list.
- * @param callScreeningResponse The response that was returned from the
- * {@link CallScreeningService} that handled this call. If no
- * response was received from a call screening service,
- * this will be {@code null}.
- * @param isResponseFromSystemDialer Whether {@code callScreeningResponse} was sent from the
- * system dialer. If {@code callScreeningResponse} is
- * {@code null}, this will be {@code false}.
+ * @param callFilteringCompletionInfo Info provided by Telecom on the results of call filtering.
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.READ_CONTACTS)
- public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts,
- @Nullable CallScreeningService.CallResponse callScreeningResponse,
- boolean isResponseFromSystemDialer) { }
+ public void onCallFilteringCompleted(
+ @NonNull CallFilteringCompletionInfo callFilteringCompletionInfo) { }
static String toLogSafePhoneNumber(String number) {
// For unknown number, log empty string.
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index ce9ce7b6c092..bf4d9fb1f6c8 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;
@@ -758,19 +759,15 @@ public abstract class ConnectionService extends Service {
}
@Override
- public void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts,
- CallScreeningService.ParcelableCallResponse callScreeningResponse,
- boolean isResponseFromSystemDialer,
+ public void onCallFilteringCompleted(String callId,
+ Connection.CallFilteringCompletionInfo completionInfo,
Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_CALL_FILTERING_COMPLETED);
try {
SomeArgs args = SomeArgs.obtain();
args.arg1 = callId;
- args.arg2 = isBlocked;
- args.arg3 = isInContacts;
- args.arg4 = callScreeningResponse;
- args.arg5 = isResponseFromSystemDialer;
- args.arg6 = Log.createSubsession();
+ args.arg2 = completionInfo;
+ args.arg3 = Log.createSubsession();
mHandler.obtainMessage(MSG_ON_CALL_FILTERING_COMPLETED, args).sendToTarget();
} finally {
Log.endSession();
@@ -1441,16 +1438,12 @@ public abstract class ConnectionService extends Service {
case MSG_ON_CALL_FILTERING_COMPLETED: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- Log.continueSession((Session) args.arg6,
+ Log.continueSession((Session) args.arg3,
SESSION_HANDLER + SESSION_CALL_FILTERING_COMPLETED);
String callId = (String) args.arg1;
- boolean isBlocked = (boolean) args.arg2;
- boolean isInContacts = (boolean) args.arg3;
- CallScreeningService.ParcelableCallResponse callScreeningResponse =
- (CallScreeningService.ParcelableCallResponse) args.arg4;
- boolean isResponseFromSystemDialer = (boolean) args.arg5;
- onCallFilteringCompleted(callId, isBlocked, isInContacts,
- callScreeningResponse, isResponseFromSystemDialer);
+ Connection.CallFilteringCompletionInfo completionInfo =
+ (Connection.CallFilteringCompletionInfo) args.arg2;
+ onCallFilteringCompleted(callId, completionInfo);
} finally {
args.recycle();
Log.endSession();
@@ -1927,6 +1920,7 @@ public abstract class ConnectionService extends Service {
/** {@inheritDoc} */
@Override
public final IBinder onBind(Intent intent) {
+ onBindClient(intent);
return mBinder;
}
@@ -1937,6 +1931,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
@@ -2470,16 +2471,12 @@ public abstract class ConnectionService extends Service {
}
}
- private void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts,
- CallScreeningService.ParcelableCallResponse callScreeningResponse,
- boolean isResponseFromSystemDialer) {
- Log.i(this, "onCallFilteringCompleted(%s, %b, %b, %s, %b)", callId,
- isBlocked, isInContacts, callScreeningResponse, isResponseFromSystemDialer);
+ private void onCallFilteringCompleted(String callId, Connection.CallFilteringCompletionInfo
+ callFilteringCompletionInfo) {
+ Log.i(this, "onCallFilteringCompleted(%s, %s)", callId, callFilteringCompletionInfo);
Connection connection = findConnectionForAction(callId, "onCallFilteringCompleted");
if (connection != null) {
- connection.onCallFilteringCompleted(isBlocked, isInContacts,
- callScreeningResponse == null ? null : callScreeningResponse.toCallResponse(),
- isResponseFromSystemDialer);
+ connection.onCallFilteringCompleted(callFilteringCompletionInfo);
}
}
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 5cf8de8f9078..f20ee7e56d05 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/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 6c6097ac71e5..7a6fddb6f029 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -1202,27 +1202,18 @@ public final class RemoteConnection {
/**
* Notifies this {@link RemoteConnection} that call filtering has completed, as well as
* the results of a contacts lookup for the remote party.
- * @param isBlocked Whether call filtering indicates that the call should be blocked
- * @param isInContacts Whether the remote party is in the user's contacts
- * @param callScreeningResponse The response that was returned from the
- * {@link CallScreeningService} that handled this call. If no
- * response was received from a call screening service,
- * this will be {@code null}.
- * @param isResponseFromSystemDialer Whether {@code callScreeningResponse} was sent from the
- * system dialer. If {@code callScreeningResponse} is
- * {@code null}, this will be {@code false}.
+ *
+ * @param completionInfo Info provided by Telecom on the results of call filtering.
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.READ_CONTACTS)
- public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts,
- @Nullable CallScreeningService.CallResponse callScreeningResponse,
- boolean isResponseFromSystemDialer) {
+ public void onCallFilteringCompleted(
+ @NonNull Connection.CallFilteringCompletionInfo completionInfo) {
Log.startSession("RC.oCFC", getActiveOwnerInfo());
try {
if (mConnected) {
- mConnectionService.onCallFilteringCompleted(mConnectionId, isBlocked, isInContacts,
- callScreeningResponse.toParcelable(), isResponseFromSystemDialer,
+ mConnectionService.onCallFilteringCompleted(mConnectionId, completionInfo,
null /*Session.Info*/);
}
} catch (RemoteException ignored) {
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 134386aa94d8..99e4571e937c 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1720,22 +1720,22 @@ 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() {
+ public boolean hasManageOngoingCallsPermission() {
ITelecomService service = getTelecomService();
if (service != null) {
try {
- return service.hasCompanionInCallServiceAccess(
+ return service.hasManageOngoingCallsPermission(
mContext.getOpPackageName());
} catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling hasCompanionInCallServiceAccess().", e);
+ Log.e(TAG, "RemoteException calling hasManageOngoingCallsPermission().", e);
if (!isSystemProcess()) {
e.rethrowAsRuntimeException();
}