summaryrefslogtreecommitdiff
path: root/telecomm/java/android
diff options
context:
space:
mode:
author Xin Li <delphij@google.com> 2021-10-07 23:50:15 +0000
committer Gerrit Code Review <noreply-gerritcodereview@google.com> 2021-10-07 23:50:15 +0000
commitc03b0fa033117c03430e361d561aa910e95a0478 (patch)
tree9c6aaee5a3023a6c237394b44e06a3fdb46f6747 /telecomm/java/android
parent8cc0f40cf250d9c66dc15d0e8bc3a41db9a7cfa1 (diff)
parent531b8f4f2605c44cf73e8421f674a1c7a9c277ff (diff)
Merge "Merge Android 12"
Diffstat (limited to 'telecomm/java/android')
-rw-r--r--telecomm/java/android/telecom/Call.java83
-rw-r--r--telecomm/java/android/telecom/CallDiagnosticService.java254
-rw-r--r--telecomm/java/android/telecom/CallDiagnostics.java (renamed from telecomm/java/android/telecom/DiagnosticCall.java)199
-rw-r--r--telecomm/java/android/telecom/CallRedirectionService.java9
-rw-r--r--telecomm/java/android/telecom/CallScreeningService.aidl22
-rw-r--r--telecomm/java/android/telecom/CallScreeningService.java273
-rw-r--r--telecomm/java/android/telecom/CallerInfo.java3
-rw-r--r--telecomm/java/android/telecom/Connection.aidl22
-rw-r--r--telecomm/java/android/telecom/Connection.java188
-rwxr-xr-xtelecomm/java/android/telecom/ConnectionService.java50
-rw-r--r--telecomm/java/android/telecom/DefaultDialerManager.java4
-rw-r--r--telecomm/java/android/telecom/DisconnectCause.java89
-rw-r--r--telecomm/java/android/telecom/InCallService.java2
-rw-r--r--telecomm/java/android/telecom/Logging/SessionManager.java7
-rw-r--r--telecomm/java/android/telecom/ParcelableCall.java2
-rw-r--r--telecomm/java/android/telecom/RemoteConnection.java25
-rw-r--r--telecomm/java/android/telecom/RemoteConnectionManager.java5
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java568
18 files changed, 1374 insertions, 431 deletions
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index d2813062816d..d94fafc6a5bf 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -138,6 +138,27 @@ public final class Call {
public static final int STATE_SIMULATED_RINGING = 13;
/**
+ * @hide
+ */
+ @IntDef(prefix = { "STATE_" },
+ value = {
+ STATE_NEW,
+ STATE_DIALING,
+ STATE_RINGING,
+ STATE_HOLDING,
+ STATE_ACTIVE,
+ STATE_DISCONNECTED,
+ STATE_SELECT_PHONE_ACCOUNT,
+ STATE_CONNECTING,
+ STATE_DISCONNECTING,
+ STATE_PULLING_CALL,
+ STATE_AUDIO_PROCESSING,
+ STATE_SIMULATED_RINGING
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CallState {};
+
+ /**
* The key to retrieve the optional {@code PhoneAccount}s Telecom can bundle with its Call
* extras. Used to pass the phone accounts to display on the front end to the user in order to
* select phone accounts to (for example) place a call.
@@ -671,10 +692,20 @@ public final class Call {
*/
public static final int PROPERTY_IS_ADHOC_CONFERENCE = 0x00002000;
+ /**
+ * Connection is using cross sim technology.
+ * <p>
+ * Indicates that the {@link Connection} is using a cross sim technology which would
+ * register IMS over internet APN of default data subscription.
+ * <p>
+ */
+ public static final int PROPERTY_CROSS_SIM = 0x00004000;
+
//******************************************************************************************
// Next PROPERTY value: 0x00004000
//******************************************************************************************
+ private final @CallState int mState;
private final String mTelecomCallId;
private final Uri mHandle;
private final int mHandlePresentation;
@@ -865,10 +896,20 @@ public final class Call {
if (hasProperty(properties, PROPERTY_IS_ADHOC_CONFERENCE)) {
builder.append(" PROPERTY_IS_ADHOC_CONFERENCE");
}
+ if (hasProperty(properties, PROPERTY_CROSS_SIM)) {
+ builder.append(" PROPERTY_CROSS_SIM");
+ }
builder.append("]");
return builder.toString();
}
+ /**
+ * @return the state of the {@link Call} represented by this {@link Call.Details}.
+ */
+ public final @CallState int getState() {
+ return mState;
+ }
+
/** {@hide} */
@TestApi
public String getTelecomCallId() {
@@ -1070,6 +1111,7 @@ public final class Call {
if (o instanceof Details) {
Details d = (Details) o;
return
+ Objects.equals(mState, d.mState) &&
Objects.equals(mHandle, d.mHandle) &&
Objects.equals(mHandlePresentation, d.mHandlePresentation) &&
Objects.equals(mCallerDisplayName, d.mCallerDisplayName) &&
@@ -1096,7 +1138,8 @@ public final class Call {
@Override
public int hashCode() {
- return Objects.hash(mHandle,
+ return Objects.hash(mState,
+ mHandle,
mHandlePresentation,
mCallerDisplayName,
mCallerDisplayNamePresentation,
@@ -1118,6 +1161,7 @@ public final class Call {
/** {@hide} */
public Details(
+ @CallState int state,
String telecomCallId,
Uri handle,
int handlePresentation,
@@ -1137,6 +1181,7 @@ public final class Call {
String contactDisplayName,
int callDirection,
int callerNumberVerificationStatus) {
+ mState = state;
mTelecomCallId = telecomCallId;
mHandle = handle;
mHandlePresentation = handlePresentation;
@@ -1161,6 +1206,7 @@ public final class Call {
/** {@hide} */
public static Details createFromParcelableCall(ParcelableCall parcelableCall) {
return new Details(
+ parcelableCall.getState(),
parcelableCall.getId(),
parcelableCall.getHandle(),
parcelableCall.getHandlePresentation(),
@@ -1187,6 +1233,8 @@ public final class Call {
StringBuilder sb = new StringBuilder();
sb.append("[id: ");
sb.append(mTelecomCallId);
+ sb.append(", state: ");
+ sb.append(Call.stateToString(mState));
sb.append(", pa: ");
sb.append(mAccountHandle);
sb.append(", hdl: ");
@@ -1303,7 +1351,7 @@ public final class Call {
* @param call The {@code Call} invoking this method.
* @param state The new state of the {@code Call}.
*/
- public void onStateChanged(Call call, int state) {}
+ public void onStateChanged(Call call, @CallState int state) {}
/**
* Invoked when the parent of this {@code Call} has changed. See {@link #getParent()}.
@@ -2172,9 +2220,11 @@ public final class Call {
/**
* Obtains the state of this {@code Call}.
*
- * @return A state value, chosen from the {@code STATE_*} constants.
+ * @return The call state.
+ * @deprecated The call state is available via {@link Call.Details#getState()}.
*/
- public int getState() {
+ @Deprecated
+ public @CallState int getState() {
return mState;
}
@@ -2502,6 +2552,7 @@ public final class Call {
} else if (mRttCall != null && parcelableCall.getParcelableRttCall() == null
&& parcelableCall.getIsRttCallChanged()) {
isRttChanged = true;
+ mRttCall.close();
mRttCall = null;
}
@@ -2552,6 +2603,30 @@ public final class Call {
final void internalSetDisconnected() {
if (mState != Call.STATE_DISCONNECTED) {
mState = Call.STATE_DISCONNECTED;
+ if (mDetails != null) {
+ mDetails = new Details(mState,
+ mDetails.getTelecomCallId(),
+ mDetails.getHandle(),
+ mDetails.getHandlePresentation(),
+ mDetails.getCallerDisplayName(),
+ mDetails.getCallerDisplayNamePresentation(),
+ mDetails.getAccountHandle(),
+ mDetails.getCallCapabilities(),
+ mDetails.getCallProperties(),
+ mDetails.getDisconnectCause(),
+ mDetails.getConnectTimeMillis(),
+ mDetails.getGatewayInfo(),
+ mDetails.getVideoState(),
+ mDetails.getStatusHints(),
+ mDetails.getExtras(),
+ mDetails.getIntentExtras(),
+ mDetails.getCreationTimeMillis(),
+ mDetails.getContactDisplayName(),
+ mDetails.getCallDirection(),
+ mDetails.getCallerNumberVerificationStatus()
+ );
+ fireDetailsChanged(mDetails);
+ }
fireStateChanged(mState);
fireCallDestroyed();
}
diff --git a/telecomm/java/android/telecom/CallDiagnosticService.java b/telecomm/java/android/telecom/CallDiagnosticService.java
index 201c5db74e16..336a8ea3f310 100644
--- a/telecomm/java/android/telecom/CallDiagnosticService.java
+++ b/telecomm/java/android/telecom/CallDiagnosticService.java
@@ -19,17 +19,23 @@ package android.telecom;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
+import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.RemoteException;
+
+import android.telephony.CallQuality;
import android.util.ArrayMap;
import com.android.internal.telecom.ICallDiagnosticService;
import com.android.internal.telecom.ICallDiagnosticServiceAdapter;
import java.util.Map;
+import java.util.concurrent.Executor;
/**
* The platform supports a single OEM provided {@link CallDiagnosticService}, as defined by the
@@ -51,6 +57,11 @@ import java.util.Map;
* </service>
* }
* </pre>
+ * <p>
+ * <h2>Threading Model</h2>
+ * By default, all incoming IPC from Telecom in this service and in the {@link CallDiagnostics}
+ * instances will take place on the main thread. You can override {@link #getExecutor()} in your
+ * implementation to provide your own {@link Executor}.
* @hide
*/
@SystemApi
@@ -83,7 +94,7 @@ public abstract class CallDiagnosticService extends Service {
@Override
public void updateCallAudioState(CallAudioState callAudioState) throws RemoteException {
- onCallAudioStateChanged(callAudioState);
+ getExecutor().execute(() -> onCallAudioStateChanged(callAudioState));
}
@Override
@@ -96,29 +107,43 @@ public abstract class CallDiagnosticService extends Service {
throws RemoteException {
handleBluetoothCallQualityReport(qualityReport);
}
+
+ @Override
+ public void notifyCallDisconnected(@NonNull String callId,
+ @NonNull DisconnectCause disconnectCause) throws RemoteException {
+ handleCallDisconnected(callId, disconnectCause);
+ }
+
+ @Override
+ public void callQualityChanged(String callId, CallQuality callQuality)
+ throws RemoteException {
+ handleCallQualityChanged(callId, callQuality);
+ }
}
/**
- * Listens to events raised by a {@link DiagnosticCall}.
+ * Listens to events raised by a {@link CallDiagnostics}.
*/
- private android.telecom.DiagnosticCall.Listener mDiagnosticCallListener =
- new android.telecom.DiagnosticCall.Listener() {
+ private CallDiagnostics.Listener mDiagnosticCallListener =
+ new CallDiagnostics.Listener() {
@Override
- public void onSendDeviceToDeviceMessage(DiagnosticCall diagnosticCall,
- @DiagnosticCall.MessageType int message, int value) {
- handleSendDeviceToDeviceMessage(diagnosticCall, message, value);
+ public void onSendDeviceToDeviceMessage(CallDiagnostics callDiagnostics,
+ @CallDiagnostics.MessageType int message, int value) {
+ handleSendDeviceToDeviceMessage(callDiagnostics, message, value);
}
@Override
- public void onDisplayDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId,
+ public void onDisplayDiagnosticMessage(CallDiagnostics callDiagnostics,
+ int messageId,
CharSequence message) {
- handleDisplayDiagnosticMessage(diagnosticCall, messageId, message);
+ handleDisplayDiagnosticMessage(callDiagnostics, messageId, message);
}
@Override
- public void onClearDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId) {
- handleClearDiagnosticMessage(diagnosticCall, messageId);
+ public void onClearDiagnosticMessage(CallDiagnostics callDiagnostics,
+ int messageId) {
+ handleClearDiagnosticMessage(callDiagnostics, messageId);
}
};
@@ -132,9 +157,19 @@ public abstract class CallDiagnosticService extends Service {
* 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 final Map<String, CallDiagnostics> mDiagnosticCallByTelecomCallId = new ArrayMap<>();
+ private final Object mLock = new Object();
private ICallDiagnosticServiceAdapter mAdapter;
+ /**
+ * Handles binding to the {@link CallDiagnosticService}.
+ *
+ * @param intent The Intent that was used to bind to this service,
+ * as given to {@link android.content.Context#bindService
+ * Context.bindService}. Note that any extras that were included with
+ * the Intent at that point will <em>not</em> be seen here.
+ * @return
+ */
@Nullable
@Override
public IBinder onBind(@NonNull Intent intent) {
@@ -143,32 +178,57 @@ public abstract class CallDiagnosticService extends Service {
}
/**
+ * Returns the {@link Executor} to use for incoming IPS from Telecom into your service
+ * implementation.
+ * <p>
+ * Override this method in your {@link CallDiagnosticService} implementation to provide the
+ * executor you want to use for incoming IPC.
+ *
+ * @return the {@link Executor} to use for incoming IPC from Telecom to
+ * {@link CallDiagnosticService} and {@link CallDiagnostics}.
+ */
+ @SuppressLint("OnNameExpected")
+ @NonNull public Executor getExecutor() {
+ return new HandlerExecutor(Handler.createAsync(getMainLooper()));
+ }
+
+ /**
* 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
+ * The {@link CallDiagnosticService} returns an implementation of {@link CallDiagnostics} to be
* used for the lifespan of this call.
+ * <p>
+ * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see
+ * {@link CallDiagnosticService#getExecutor()} for more information.
*
* @param call The details of the new call.
- * @return An instance of {@link DiagnosticCall} which the {@link CallDiagnosticService}
+ * @return An instance of {@link CallDiagnostics} which the {@link CallDiagnosticService}
* provides to be used for the lifespan of the call.
- * @throws IllegalArgumentException if a {@code null} {@link DiagnosticCall} is returned.
+ * @throws IllegalArgumentException if a {@code null} {@link CallDiagnostics} is returned.
*/
- public abstract @NonNull DiagnosticCall onInitializeDiagnosticCall(@NonNull
+ public abstract @NonNull CallDiagnostics onInitializeCallDiagnostics(@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.
+ * Telecom calls this method when a previous created {@link CallDiagnostics} is no longer
+ * needed. This happens when Telecom is no longer tracking the call in question.
+ * <p>
+ * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see
+ * {@link CallDiagnosticService#getExecutor()} for more information.
+ *
* @param call The diagnostic call which is no longer tracked by Telecom.
*/
- public abstract void onRemoveDiagnosticCall(@NonNull DiagnosticCall call);
+ public abstract void onRemoveCallDiagnostics(@NonNull CallDiagnostics call);
/**
* Telecom calls this method when the audio routing or available audio route information
* changes.
* <p>
* Audio state is common to all calls.
+ * <p>
+ * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see
+ * {@link CallDiagnosticService#getExecutor()} for more information.
*
* @param audioState The new audio state.
*/
@@ -178,6 +238,10 @@ public abstract class CallDiagnosticService extends Service {
/**
* Telecom calls this method when a {@link BluetoothCallQualityReport} is received from the
* bluetooth stack.
+ * <p>
+ * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see
+ * {@link CallDiagnosticService#getExecutor()} for more information.
+ *
* @param qualityReport the {@link BluetoothCallQualityReport}.
*/
public abstract void onBluetoothCallQualityReportReceived(
@@ -199,31 +263,44 @@ public abstract class CallDiagnosticService extends Service {
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.");
+ synchronized (mLock) {
+ mCallByTelecomCallId.put(telecomCallId, newCallDetails);
}
- diagnosticCall.setListener(mDiagnosticCallListener);
- diagnosticCall.setCallId(telecomCallId);
- mDiagnosticCallByTelecomCallId.put(telecomCallId, diagnosticCall);
+
+ getExecutor().execute(() -> {
+ CallDiagnostics callDiagnostics = onInitializeCallDiagnostics(newCallDetails);
+ if (callDiagnostics == null) {
+ throw new IllegalArgumentException(
+ "A valid DiagnosticCall instance was not provided.");
+ }
+ synchronized (mLock) {
+ callDiagnostics.setListener(mDiagnosticCallListener);
+ callDiagnostics.setCallId(telecomCallId);
+ mDiagnosticCallByTelecomCallId.put(telecomCallId, callDiagnostics);
+ }
+ });
}
/**
* 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)}.
+ * Caches the call details and notifies the {@link CallDiagnostics} of the change via
+ * {@link CallDiagnostics#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);
+ CallDiagnostics callDiagnostics;
+ synchronized (mLock) {
+ callDiagnostics = mDiagnosticCallByTelecomCallId.get(telecomCallId);
+ if (callDiagnostics == null) {
+ // Possible to get a call update after a call is removed.
+ return;
+ }
+ mCallByTelecomCallId.put(telecomCallId, newCallDetails);
+ }
+ getExecutor().execute(() -> callDiagnostics.handleCallUpdated(newCallDetails));
}
/**
@@ -233,27 +310,71 @@ public abstract class CallDiagnosticService extends Service {
private void handleCallRemoved(@NonNull String telecomCallId) {
Log.i(this, "handleCallRemoved: callId=%s - removed", telecomCallId);
- if (mCallByTelecomCallId.containsKey(telecomCallId)) {
- mCallByTelecomCallId.remove(telecomCallId);
+ CallDiagnostics callDiagnostics;
+ synchronized (mLock) {
+ if (mCallByTelecomCallId.containsKey(telecomCallId)) {
+ mCallByTelecomCallId.remove(telecomCallId);
+ }
+
+ if (mDiagnosticCallByTelecomCallId.containsKey(telecomCallId)) {
+ callDiagnostics = mDiagnosticCallByTelecomCallId.remove(telecomCallId);
+ } else {
+ callDiagnostics = null;
+ }
}
- if (mDiagnosticCallByTelecomCallId.containsKey(telecomCallId)) {
- DiagnosticCall call = mDiagnosticCallByTelecomCallId.remove(telecomCallId);
- // Inform the service of the removed call.
- onRemoveDiagnosticCall(call);
+
+ // Inform the service of the removed call.
+ if (callDiagnostics != null) {
+ getExecutor().execute(() -> onRemoveCallDiagnostics(callDiagnostics));
}
}
/**
* Handles an incoming device to device message received from Telecom. Notifies the
- * {@link DiagnosticCall} via {@link DiagnosticCall#onReceiveDeviceToDeviceMessage(int, int)}.
+ * {@link CallDiagnostics} via {@link CallDiagnostics#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);
+ CallDiagnostics callDiagnostics;
+ synchronized (mLock) {
+ callDiagnostics = mDiagnosticCallByTelecomCallId.get(callId);
+ }
+ if (callDiagnostics != null) {
+ getExecutor().execute(
+ () -> callDiagnostics.onReceiveDeviceToDeviceMessage(message, value));
+ }
+ }
+
+ /**
+ * Handles a request from the Telecom framework to get a disconnect message from the
+ * {@link CallDiagnosticService}.
+ * @param callId The ID of the call.
+ * @param disconnectCause The telecom disconnect cause.
+ */
+ private void handleCallDisconnected(@NonNull String callId,
+ @NonNull DisconnectCause disconnectCause) {
+ Log.i(this, "handleCallDisconnected: call=%s; cause=%s", callId, disconnectCause);
+ CallDiagnostics callDiagnostics;
+ synchronized (mLock) {
+ callDiagnostics = mDiagnosticCallByTelecomCallId.get(callId);
+ }
+ CharSequence message;
+ if (disconnectCause.getImsReasonInfo() != null) {
+ message = callDiagnostics.onCallDisconnected(disconnectCause.getImsReasonInfo());
+ } else {
+ message = callDiagnostics.onCallDisconnected(
+ disconnectCause.getTelephonyDisconnectCause(),
+ disconnectCause.getTelephonyPreciseDisconnectCause());
+ }
+ try {
+ mAdapter.overrideDisconnectMessage(callId, message);
+ } catch (RemoteException e) {
+ Log.w(this, "handleCallDisconnected: call=%s; cause=%s; %s",
+ callId, disconnectCause, e);
+ }
}
/**
@@ -265,19 +386,36 @@ public abstract class CallDiagnosticService extends Service {
private void handleBluetoothCallQualityReport(@NonNull BluetoothCallQualityReport
qualityReport) {
Log.i(this, "handleBluetoothCallQualityReport; report=%s", qualityReport);
- onBluetoothCallQualityReportReceived(qualityReport);
+ getExecutor().execute(() -> onBluetoothCallQualityReportReceived(qualityReport));
+ }
+
+ /**
+ * Handles a change reported by Telecom to the call quality for a call.
+ * @param callId the call ID the change applies to.
+ * @param callQuality The new call quality.
+ */
+ private void handleCallQualityChanged(@NonNull String callId,
+ @NonNull CallQuality callQuality) {
+ Log.i(this, "handleCallQualityChanged; call=%s, cq=%s", callId, callQuality);
+ CallDiagnostics callDiagnostics;
+ synchronized(mLock) {
+ callDiagnostics = mDiagnosticCallByTelecomCallId.get(callId);
+ }
+ if (callDiagnostics != null) {
+ callDiagnostics.onCallQualityReceived(callQuality);
+ }
}
/**
- * Handles a request from a {@link DiagnosticCall} to send a device to device message (received
- * via {@link DiagnosticCall#sendDeviceToDeviceMessage(int, int)}.
- * @param diagnosticCall
+ * Handles a request from a {@link CallDiagnostics} to send a device to device message (received
+ * via {@link CallDiagnostics#sendDeviceToDeviceMessage(int, int)}.
+ * @param callDiagnostics
* @param message
* @param value
*/
- private void handleSendDeviceToDeviceMessage(@NonNull DiagnosticCall diagnosticCall,
+ private void handleSendDeviceToDeviceMessage(@NonNull CallDiagnostics callDiagnostics,
int message, int value) {
- String callId = diagnosticCall.getCallId();
+ String callId = callDiagnostics.getCallId();
try {
mAdapter.sendDeviceToDeviceMessage(callId, message, value);
Log.i(this, "handleSendDeviceToDeviceMessage: call=%s; msg=%d/%d", callId, message,
@@ -289,15 +427,15 @@ public abstract class CallDiagnosticService extends Service {
}
/**
- * Handles a request from a {@link DiagnosticCall} to display an in-call diagnostic message.
- * Originates from {@link DiagnosticCall#displayDiagnosticMessage(int, CharSequence)}.
- * @param diagnosticCall
+ * Handles a request from a {@link CallDiagnostics} to display an in-call diagnostic message.
+ * Originates from {@link CallDiagnostics#displayDiagnosticMessage(int, CharSequence)}.
+ * @param callDiagnostics
* @param messageId
* @param message
*/
- private void handleDisplayDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId,
+ private void handleDisplayDiagnosticMessage(CallDiagnostics callDiagnostics, int messageId,
CharSequence message) {
- String callId = diagnosticCall.getCallId();
+ String callId = callDiagnostics.getCallId();
try {
mAdapter.displayDiagnosticMessage(callId, messageId, message);
Log.i(this, "handleDisplayDiagnosticMessage: call=%s; msg=%d/%s", callId, messageId,
@@ -309,14 +447,14 @@ public abstract class CallDiagnosticService extends Service {
}
/**
- * Handles a request from a {@link DiagnosticCall} to clear a previously shown diagnostic
+ * Handles a request from a {@link CallDiagnostics} to clear a previously shown diagnostic
* message.
- * Originates from {@link DiagnosticCall#clearDiagnosticMessage(int)}.
- * @param diagnosticCall
+ * Originates from {@link CallDiagnostics#clearDiagnosticMessage(int)}.
+ * @param callDiagnostics
* @param messageId
*/
- private void handleClearDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId) {
- String callId = diagnosticCall.getCallId();
+ private void handleClearDiagnosticMessage(CallDiagnostics callDiagnostics, int messageId) {
+ String callId = callDiagnostics.getCallId();
try {
mAdapter.clearDiagnosticMessage(callId, messageId);
Log.i(this, "handleClearDiagnosticMessage: call=%s; msg=%d", callId, messageId);
diff --git a/telecomm/java/android/telecom/DiagnosticCall.java b/telecomm/java/android/telecom/CallDiagnostics.java
index a4952899eb46..3356431f17b1 100644
--- a/telecomm/java/android/telecom/DiagnosticCall.java
+++ b/telecomm/java/android/telecom/CallDiagnostics.java
@@ -26,42 +26,79 @@ import android.telephony.ims.ImsReasonInfo;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
/**
- * 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).
+ * {@link CallDiagnostics} provides a way for a {@link CallDiagnosticService} to receive diagnostic
+ * information about a mobile call on the device. A {@link CallDiagnostics} instance is similar to
+ * a {@link Call}, however it does not expose call control capabilities and exposes extra diagnostic
+ * and messaging capabilities not present on a {@link Call}. The {@link CallDiagnosticService}
+ * creates a {@link CallDiagnostics} for each {@link Call} on the device. This means that for each
+ * in progress call on the device, the {@link CallDiagnosticService} will create an instance of
+ * {@link CallDiagnostics}.
+ * <p>
+ * 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).
+ * <h2>Threading Model</h2>
+ * All incoming IPC from Telecom in this class will use the same {@link Executor} as the
+ * {@link CallDiagnosticService}. See {@link CallDiagnosticService#setExecutor(Executor)} for more
+ * information.
* @hide
*/
@SystemApi
-public abstract class DiagnosticCall {
+public abstract class CallDiagnostics {
/**
* @hide
*/
public interface Listener {
- void onSendDeviceToDeviceMessage(DiagnosticCall diagnosticCall, int message, int value);
- void onDisplayDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId,
+ /**
+ * Used to inform the {@link CallDiagnosticService} of a request to send a D2d message
+ * @param callDiagnostics the call the message is from.
+ * @param message the message type
+ * @param value the message value
+ */
+ void onSendDeviceToDeviceMessage(CallDiagnostics callDiagnostics, int message, int value);
+
+ /**
+ * Used to inform the {@link CallDiagnosticService} of a request to display a diagnostic
+ * message.
+ * @param callDiagnostics the call the message pertains to.
+ * @param messageId an identifier for the message.
+ * @param message the message to display.
+ */
+ void onDisplayDiagnosticMessage(CallDiagnostics callDiagnostics, int messageId,
CharSequence message);
- void onClearDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId);
+
+ /**
+ * Used to inform the {@link CallDiagnosticService} that a previously shown message is no
+ * longer pertinent.
+ * @param callDiagnostics the call the message pertains to.
+ * @param messageId the ID of the previously posted message.
+ */
+ void onClearDiagnosticMessage(CallDiagnostics callDiagnostics, 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.
+ * used for the current call. The call network type communicated here is an intentional
+ * simplification of the {@link android.telephony.TelephonyManager#getNetworkType(int)} which
+ * removes some of the resolution inherent in those values; the
+ * {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE_CA} value, for example is
+ * collapsed into the {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE} value for
+ * efficiency of transport. For a discussion on the necessity of this simplification, see
+ * {@link #sendDeviceToDeviceMessage(int, int)}.
* <p>
- * Valid values:
+ * Valid values are below:
* <UL>
- * <LI>{@link #NETWORK_TYPE_LTE}</LI>
- * <LI>{@link #NETWORK_TYPE_IWLAN}</LI>
- * <LI>{@link #NETWORK_TYPE_NR}</LI>
+ * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}</LI>
+ * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_IWLAN}</LI>
+ * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_NR}</LI>
* </UL>
*/
public static final int MESSAGE_CALL_NETWORK_TYPE = 1;
@@ -69,14 +106,21 @@ public abstract class DiagnosticCall {
/**
* 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.
+ * used for the current call.
+ * <p>
+ * The audio codec communicated here is an intentional simplification of the
+ * {@link Connection#EXTRA_AUDIO_CODEC} for a call and focuses on communicating the most common
+ * variants of these audio codecs. Other variants of these codecs are reported as the next
+ * closest variant. For example, the {@link Connection#AUDIO_CODEC_EVS_FB} full band codec
+ * is reported via device to device communication as {@link Connection#AUDIO_CODEC_EVS_WB}.
+ * For a discussion on the necessity of this simplification, see
+ * {@link #sendDeviceToDeviceMessage(int, int)}.
* <p>
* Valid values:
* <UL>
- * <LI>{@link #AUDIO_CODEC_EVS}</LI>
- * <LI>{@link #AUDIO_CODEC_AMR_WB}</LI>
- * <LI>{@link #AUDIO_CODEC_AMR_NB}</LI>
+ * <LI>{@link Connection#AUDIO_CODEC_EVS_WB}</LI>
+ * <LI>{@link Connection#AUDIO_CODEC_AMR_WB}</LI>
+ * <LI>{@link Connection#AUDIO_CODEC_AMR}</LI>
* </UL>
*/
public static final int MESSAGE_CALL_AUDIO_CODEC = 2;
@@ -122,41 +166,6 @@ public abstract class DiagnosticCall {
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;
@@ -183,7 +192,6 @@ public abstract class DiagnosticCall {
private Listener mListener;
private String mCallId;
- private Call.Details mCallDetails;
/**
* @hide
@@ -193,7 +201,7 @@ public abstract class DiagnosticCall {
}
/**
- * Sets the call ID for this {@link DiagnosticCall}.
+ * Sets the call ID for this {@link CallDiagnostics}.
* @param callId
* @hide
*/
@@ -202,7 +210,7 @@ public abstract class DiagnosticCall {
}
/**
- * @return the Telecom call ID for this {@link DiagnosticCall}.
+ * @return the Telecom call ID for this {@link CallDiagnostics}.
* @hide
*/
public @NonNull String getCallId() {
@@ -210,16 +218,10 @@ public abstract class DiagnosticCall {
}
/**
- * 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.
+ * <p>
+ * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+ * see {@link CallDiagnosticService#getExecutor()} for more information.
*/
public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details details);
@@ -234,6 +236,9 @@ public abstract class DiagnosticCall {
* 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.
+ * <p>
+ * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+ * see {@link CallDiagnosticService#getExecutor()} for more information.
*/
public abstract void onReceiveDeviceToDeviceMessage(
@MessageType int message,
@@ -253,39 +258,19 @@ public abstract class DiagnosticCall {
* 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.
+ * for a call, or using the DTMF digits A-D as a communication pathway. RTP header extension
+ * packets ride alongside a the audio for a call, and are thus limited to roughly a byte for
+ * a message. Signalling requirements for DTMF digits place even more significant limitations
+ * on the amount of information which can be communicated during a call, offering only a few
+ * bits of potential information per message. The messages and values are constrained in order
+ * to meet the limited bandwidth inherent with DTMF signalling.
* <p>
- * Allowed message types and values are:
+ * Allowed message types 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>
+ * <li>{@link #MESSAGE_CALL_NETWORK_TYPE}</LI>
+ * <li>{@link #MESSAGE_CALL_AUDIO_CODEC}</LI>
+ * <li>{@link #MESSAGE_DEVICE_BATTERY_STATE}</LI>
+ * <li>{@link #MESSAGE_DEVICE_NETWORK_COVERAGE}</LI>
* </ul>
* @param message The message type to send.
* @param value The message value corresponding to the type.
@@ -307,6 +292,9 @@ public abstract class DiagnosticCall {
* @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.
+ * <p>
+ * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+ * see {@link CallDiagnosticService#getExecutor()} for more information.
*/
// TODO: Wire in Telephony support for this.
public abstract @Nullable CharSequence onCallDisconnected(
@@ -323,6 +311,9 @@ public abstract class DiagnosticCall {
* @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.
+ * <p>
+ * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+ * see {@link CallDiagnosticService#getExecutor()} for more information.
*/
// TODO: Wire in Telephony support for this.
public abstract @Nullable CharSequence onCallDisconnected(
@@ -332,6 +323,9 @@ public abstract class DiagnosticCall {
* 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.
+ * <p>
+ * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+ * see {@link CallDiagnosticService#getExecutor()} for more information.
*/
public abstract void onCallQualityReceived(@NonNull CallQuality callQuality);
@@ -370,12 +364,11 @@ public abstract class DiagnosticCall {
/**
* Called by the {@link CallDiagnosticService} to update the call details for this
- * {@link DiagnosticCall} based on an update received from Telecom.
+ * {@link CallDiagnostics} 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/CallRedirectionService.java b/telecomm/java/android/telecom/CallRedirectionService.java
index 8dcae393aba9..93989b6744dc 100644
--- a/telecomm/java/android/telecom/CallRedirectionService.java
+++ b/telecomm/java/android/telecom/CallRedirectionService.java
@@ -107,6 +107,9 @@ public abstract class CallRedirectionService extends Service {
*/
public final void placeCallUnmodified() {
try {
+ if (mCallRedirectionAdapter == null) {
+ throw new IllegalStateException("Can only be called from onPlaceCall.");
+ }
mCallRedirectionAdapter.placeCallUnmodified();
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
@@ -135,6 +138,9 @@ public abstract class CallRedirectionService extends Service {
@NonNull PhoneAccountHandle targetPhoneAccount,
boolean confirmFirst) {
try {
+ if (mCallRedirectionAdapter == null) {
+ throw new IllegalStateException("Can only be called from onPlaceCall.");
+ }
mCallRedirectionAdapter.redirectCall(gatewayUri, targetPhoneAccount, confirmFirst);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
@@ -153,6 +159,9 @@ public abstract class CallRedirectionService extends Service {
*/
public final void cancelCall() {
try {
+ if (mCallRedirectionAdapter == null) {
+ throw new IllegalStateException("Can only be called from onPlaceCall.");
+ }
mCallRedirectionAdapter.cancelCall();
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
diff --git a/telecomm/java/android/telecom/CallScreeningService.aidl b/telecomm/java/android/telecom/CallScreeningService.aidl
new file mode 100644
index 000000000000..87b5138745f2
--- /dev/null
+++ b/telecomm/java/android/telecom/CallScreeningService.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 CallScreeningService.ParcelableCallResponse;
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index b980d3f55d2c..7861b11158cd 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -17,6 +17,7 @@
package android.telecom;
import android.Manifest;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
@@ -30,12 +31,18 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.RemoteException;
import com.android.internal.os.SomeArgs;
import com.android.internal.telecom.ICallScreeningAdapter;
import com.android.internal.telecom.ICallScreeningService;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
/**
* This service can be implemented by the default dialer (see
* {@link TelecomManager#getDefaultDialerPackage()}) or a third party app to allow or disallow
@@ -132,7 +139,10 @@ public abstract class CallScreeningService extends Service {
.createFromParcelableCall((ParcelableCall) args.arg2);
onScreenCall(callDetails);
if (callDetails.getCallDirection() == Call.Details.DIRECTION_OUTGOING) {
- mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
+ mCallScreeningAdapter.onScreeningResponse(
+ callDetails.getTelecomCallId(),
+ new ComponentName(getPackageName(), getClass().getName()),
+ null);
}
} catch (RemoteException e) {
Log.w(this, "Exception when screening call: " + e);
@@ -157,16 +167,171 @@ public abstract class CallScreeningService extends Service {
private ICallScreeningAdapter mCallScreeningAdapter;
- /*
- * Information about how to respond to an incoming call.
+ /**
+ * Parcelable version of {@link CallResponse} used to do IPC.
+ * @hide
+ */
+ public static class ParcelableCallResponse implements Parcelable {
+ private final boolean mShouldDisallowCall;
+ private final boolean mShouldRejectCall;
+ private final boolean mShouldSilenceCall;
+ private final boolean mShouldSkipCallLog;
+ private final boolean mShouldSkipNotification;
+ private final boolean mShouldScreenCallViaAudioProcessing;
+
+ private final int mCallComposerAttachmentsToShow;
+
+ private ParcelableCallResponse(
+ boolean shouldDisallowCall,
+ boolean shouldRejectCall,
+ boolean shouldSilenceCall,
+ boolean shouldSkipCallLog,
+ boolean shouldSkipNotification,
+ boolean shouldScreenCallViaAudioProcessing,
+ int callComposerAttachmentsToShow) {
+ mShouldDisallowCall = shouldDisallowCall;
+ mShouldRejectCall = shouldRejectCall;
+ mShouldSilenceCall = shouldSilenceCall;
+ mShouldSkipCallLog = shouldSkipCallLog;
+ mShouldSkipNotification = shouldSkipNotification;
+ mShouldScreenCallViaAudioProcessing = shouldScreenCallViaAudioProcessing;
+ mCallComposerAttachmentsToShow = callComposerAttachmentsToShow;
+ }
+
+ protected ParcelableCallResponse(Parcel in) {
+ mShouldDisallowCall = in.readBoolean();
+ mShouldRejectCall = in.readBoolean();
+ mShouldSilenceCall = in.readBoolean();
+ mShouldSkipCallLog = in.readBoolean();
+ mShouldSkipNotification = in.readBoolean();
+ mShouldScreenCallViaAudioProcessing = in.readBoolean();
+ mCallComposerAttachmentsToShow = in.readInt();
+ }
+
+ public CallResponse toCallResponse() {
+ return new CallResponse.Builder()
+ .setDisallowCall(mShouldDisallowCall)
+ .setRejectCall(mShouldRejectCall)
+ .setSilenceCall(mShouldSilenceCall)
+ .setSkipCallLog(mShouldSkipCallLog)
+ .setSkipNotification(mShouldSkipNotification)
+ .setShouldScreenCallViaAudioProcessing(mShouldScreenCallViaAudioProcessing)
+ .setCallComposerAttachmentsToShow(mCallComposerAttachmentsToShow)
+ .build();
+ }
+
+ public boolean shouldDisallowCall() {
+ return mShouldDisallowCall;
+ }
+
+ public boolean shouldRejectCall() {
+ return mShouldRejectCall;
+ }
+
+ public boolean shouldSilenceCall() {
+ return mShouldSilenceCall;
+ }
+
+ public boolean shouldSkipCallLog() {
+ return mShouldSkipCallLog;
+ }
+
+ public boolean shouldSkipNotification() {
+ return mShouldSkipNotification;
+ }
+
+ public boolean shouldScreenCallViaAudioProcessing() {
+ return mShouldScreenCallViaAudioProcessing;
+ }
+
+ public int getCallComposerAttachmentsToShow() {
+ return mCallComposerAttachmentsToShow;
+ }
+
+ public static final Creator<ParcelableCallResponse> CREATOR =
+ new Creator<ParcelableCallResponse>() {
+ @Override
+ public ParcelableCallResponse createFromParcel(Parcel in) {
+ return new ParcelableCallResponse(in);
+ }
+
+ @Override
+ public ParcelableCallResponse[] newArray(int size) {
+ return new ParcelableCallResponse[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeBoolean(mShouldDisallowCall);
+ dest.writeBoolean(mShouldRejectCall);
+ dest.writeBoolean(mShouldSilenceCall);
+ dest.writeBoolean(mShouldSkipCallLog);
+ dest.writeBoolean(mShouldSkipNotification);
+ dest.writeBoolean(mShouldScreenCallViaAudioProcessing);
+ dest.writeInt(mCallComposerAttachmentsToShow);
+ }
+ }
+
+ /**
+ * Information about how to respond to an incoming call. Call screening apps can construct an
+ * instance of this class using {@link CallResponse.Builder}.
*/
public static class CallResponse {
+ /**
+ * Bit flag indicating whether to show the picture attachment for call composer.
+ *
+ * Used with {@link Builder#setCallComposerAttachmentsToShow(int)}.
+ */
+ public static final int CALL_COMPOSER_ATTACHMENT_PICTURE = 1;
+
+ /**
+ * Bit flag indicating whether to show the location attachment for call composer.
+ *
+ * Used with {@link Builder#setCallComposerAttachmentsToShow(int)}.
+ */
+ public static final int CALL_COMPOSER_ATTACHMENT_LOCATION = 1 << 1;
+
+ /**
+ * Bit flag indicating whether to show the subject attachment for call composer.
+ *
+ * Used with {@link Builder#setCallComposerAttachmentsToShow(int)}.
+ */
+ public static final int CALL_COMPOSER_ATTACHMENT_SUBJECT = 1 << 2;
+
+ /**
+ * Bit flag indicating whether to show the priority attachment for call composer.
+ *
+ * Used with {@link Builder#setCallComposerAttachmentsToShow(int)}.
+ */
+ public static final int CALL_COMPOSER_ATTACHMENT_PRIORITY = 1 << 3;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "CALL_COMPOSER_ATTACHMENT_", flag = true,
+ value = {
+ CALL_COMPOSER_ATTACHMENT_PICTURE,
+ CALL_COMPOSER_ATTACHMENT_LOCATION,
+ CALL_COMPOSER_ATTACHMENT_SUBJECT,
+ CALL_COMPOSER_ATTACHMENT_PRIORITY
+ }
+ )
+ public @interface CallComposerAttachmentType {}
+
+ private static final int NUM_CALL_COMPOSER_ATTACHMENT_TYPES = 4;
+
private final boolean mShouldDisallowCall;
private final boolean mShouldRejectCall;
private final boolean mShouldSilenceCall;
private final boolean mShouldSkipCallLog;
private final boolean mShouldSkipNotification;
private final boolean mShouldScreenCallViaAudioProcessing;
+ private final int mCallComposerAttachmentsToShow;
private CallResponse(
boolean shouldDisallowCall,
@@ -174,7 +339,8 @@ public abstract class CallScreeningService extends Service {
boolean shouldSilenceCall,
boolean shouldSkipCallLog,
boolean shouldSkipNotification,
- boolean shouldScreenCallViaAudioProcessing) {
+ boolean shouldScreenCallViaAudioProcessing,
+ int callComposerAttachmentsToShow) {
if (!shouldDisallowCall
&& (shouldRejectCall || shouldSkipCallLog || shouldSkipNotification)) {
throw new IllegalStateException("Invalid response state for allowed call.");
@@ -190,6 +356,7 @@ public abstract class CallScreeningService extends Service {
mShouldSkipNotification = shouldSkipNotification;
mShouldSilenceCall = shouldSilenceCall;
mShouldScreenCallViaAudioProcessing = shouldScreenCallViaAudioProcessing;
+ mCallComposerAttachmentsToShow = callComposerAttachmentsToShow;
}
/*
@@ -237,6 +404,49 @@ public abstract class CallScreeningService extends Service {
return mShouldScreenCallViaAudioProcessing;
}
+ /**
+ * @return A bitmask of call composer attachments that should be shown to the user.
+ */
+ public @CallComposerAttachmentType int getCallComposerAttachmentsToShow() {
+ return mCallComposerAttachmentsToShow;
+ }
+
+ /** @hide */
+ public ParcelableCallResponse toParcelable() {
+ return new ParcelableCallResponse(
+ mShouldDisallowCall,
+ mShouldRejectCall,
+ mShouldSilenceCall,
+ mShouldSkipCallLog,
+ mShouldSkipNotification,
+ mShouldScreenCallViaAudioProcessing,
+ mCallComposerAttachmentsToShow
+ );
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ CallResponse that = (CallResponse) o;
+ return mShouldDisallowCall == that.mShouldDisallowCall &&
+ mShouldRejectCall == that.mShouldRejectCall &&
+ mShouldSilenceCall == that.mShouldSilenceCall &&
+ mShouldSkipCallLog == that.mShouldSkipCallLog &&
+ mShouldSkipNotification == that.mShouldSkipNotification &&
+ mShouldScreenCallViaAudioProcessing
+ == that.mShouldScreenCallViaAudioProcessing &&
+ mCallComposerAttachmentsToShow == that.mCallComposerAttachmentsToShow;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mShouldDisallowCall, mShouldRejectCall, mShouldSilenceCall,
+ mShouldSkipCallLog, mShouldSkipNotification,
+ mShouldScreenCallViaAudioProcessing,
+ mCallComposerAttachmentsToShow);
+ }
+
public static class Builder {
private boolean mShouldDisallowCall;
private boolean mShouldRejectCall;
@@ -244,6 +454,7 @@ public abstract class CallScreeningService extends Service {
private boolean mShouldSkipCallLog;
private boolean mShouldSkipNotification;
private boolean mShouldScreenCallViaAudioProcessing;
+ private int mCallComposerAttachmentsToShow = -1;
/**
* Sets whether the incoming call should be blocked.
@@ -329,6 +540,38 @@ public abstract class CallScreeningService extends Service {
return this;
}
+ /**
+ * Sets the call composer attachments that should be shown to the user.
+ *
+ * Attachments that are not shown will not be passed to the in-call UI responsible for
+ * displaying the call to the user.
+ *
+ * If this method is not called on a {@link Builder}, all attachments will be shown,
+ * except pictures, which will only be shown to users if the call is from a contact.
+ *
+ * Setting attachments to show will have no effect if the call screening service does
+ * not belong to the same package as the system dialer (as returned by
+ * {@link TelecomManager#getSystemDialerPackage()}).
+ *
+ * @param callComposerAttachmentsToShow A bitmask of call composer attachments to show.
+ */
+ public @NonNull Builder setCallComposerAttachmentsToShow(
+ @CallComposerAttachmentType int callComposerAttachmentsToShow) {
+ // If the argument is less than zero (meaning unset), no-op since the conversion
+ // to/from the parcelable version may call with that value.
+ if (callComposerAttachmentsToShow < 0) {
+ return this;
+ }
+
+ if ((callComposerAttachmentsToShow
+ & (1 << NUM_CALL_COMPOSER_ATTACHMENT_TYPES)) != 0) {
+ throw new IllegalArgumentException("Attachment types must match the ones"
+ + " defined in CallResponse");
+ }
+ mCallComposerAttachmentsToShow = callComposerAttachmentsToShow;
+ return this;
+ }
+
public CallResponse build() {
return new CallResponse(
mShouldDisallowCall,
@@ -336,7 +579,8 @@ public abstract class CallScreeningService extends Service {
mShouldSilenceCall,
mShouldSkipCallLog,
mShouldSkipNotification,
- mShouldScreenCallViaAudioProcessing);
+ mShouldScreenCallViaAudioProcessing,
+ mCallComposerAttachmentsToShow);
}
}
}
@@ -423,21 +667,12 @@ public abstract class CallScreeningService extends Service {
public final void respondToCall(@NonNull Call.Details callDetails,
@NonNull CallResponse response) {
try {
- if (response.getDisallowCall()) {
- mCallScreeningAdapter.disallowCall(
- callDetails.getTelecomCallId(),
- response.getRejectCall(),
- !response.getSkipCallLog(),
- !response.getSkipNotification(),
- new ComponentName(getPackageName(), getClass().getName()));
- } else if (response.getSilenceCall()) {
- mCallScreeningAdapter.silenceCall(callDetails.getTelecomCallId());
- } else if (response.getShouldScreenCallViaAudioProcessing()) {
- mCallScreeningAdapter.screenCallFurther(callDetails.getTelecomCallId());
- } else {
- mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
- }
+ mCallScreeningAdapter.onScreeningResponse(
+ callDetails.getTelecomCallId(),
+ new ComponentName(getPackageName(), getClass().getName()),
+ response.toParcelable());
} catch (RemoteException e) {
+ Log.e(this, e, "Got remote exception when returning response");
}
}
}
diff --git a/telecomm/java/android/telecom/CallerInfo.java b/telecomm/java/android/telecom/CallerInfo.java
index a63ee4664378..2983e6339d4b 100644
--- a/telecomm/java/android/telecom/CallerInfo.java
+++ b/telecomm/java/android/telecom/CallerInfo.java
@@ -406,7 +406,8 @@ public class CallerInfo {
// Change the callerInfo number ONLY if it is an emergency number
// or if it is the voicemail number. If it is either, take a
// shortcut and skip the query.
- if (PhoneNumberUtils.isLocalEmergencyNumber(context, number)) {
+ TelephonyManager tm = context.getSystemService(TelephonyManager.class);
+ if (tm.isEmergencyNumber(number)) {
return new CallerInfo().markAsEmergency(context);
} else if (PhoneNumberUtils.isVoiceMailNumber(null, subId, number)) {
return new CallerInfo().markAsVoiceMail(context, subId);
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 7774e49a0129..d06fe45a104c 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -18,6 +18,7 @@ package android.telecom;
import static android.Manifest.permission.MODIFY_PHONE_STATE;
+import android.Manifest;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -28,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;
@@ -37,9 +39,12 @@ 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.CallQuality;
import android.telephony.ims.ImsStreamMediaProfile;
import android.util.ArraySet;
import android.view.Surface;
@@ -547,9 +552,17 @@ public abstract class Connection extends Conferenceable {
*/
public static final int PROPERTY_IS_ADHOC_CONFERENCE = 1 << 12;
+ /**
+ * Connection is using cross sim technology.
+ * <p>
+ * Indicates that the {@link Connection} is using a cross sim technology which would
+ * register IMS over internet APN of default data subscription.
+ * <p>
+ */
+ public static final int PROPERTY_CROSS_SIM = 1 << 13;
//**********************************************************************************************
- // Next PROPERTY value: 1<<13
+ // Next PROPERTY value: 1<<14
//**********************************************************************************************
/**
@@ -664,6 +677,14 @@ public abstract class Connection extends Conferenceable {
public @interface AudioCodec {}
/**
+ * Contains the same value as {@link #getCallerNumberVerificationStatus()}, except will be
+ * present in the {@link #getExtras()} using this key.
+ * @hide
+ */
+ public static final String EXTRA_CALLER_NUMBER_VERIFICATION_STATUS =
+ "android.telecom.extra.CALLER_NUMBER_VERIFICATION_STATUS";
+
+ /**
* Connection extra key used to store the last forwarded number associated with the current
* connection. Used to communicate to the user interface that the connection was forwarded via
* the specified number.
@@ -799,6 +820,15 @@ public abstract class Connection extends Conferenceable {
*/
public static final String EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ =
"android.telecom.extra.AUDIO_CODEC_BANDWIDTH_KHZ";
+
+ /**
+ * Boolean connection extra key used to indicate whether device to device communication is
+ * available for the current call.
+ * @hide
+ */
+ public static final String EXTRA_IS_DEVICE_TO_DEVICE_COMMUNICATION_AVAILABLE =
+ "android.telecom.extra.IS_DEVICE_TO_DEVICE_COMMUNICATION_AVAILABLE";
+
/**
* Connection event used to inform Telecom that it should play the on hold tone. This is used
* to play a tone when the peer puts the current call on hold. Sent to Telecom via
@@ -941,7 +971,7 @@ public abstract class Connection extends Conferenceable {
* {@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
+ * {@link CallDiagnostics#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
@@ -954,7 +984,7 @@ public abstract class Connection extends Conferenceable {
* Sent along with {@link #EVENT_DEVICE_TO_DEVICE_MESSAGE} to indicate the device to device
* message type.
*
- * See {@link DiagnosticCall} for more information.
+ * See {@link CallDiagnostics} for more information.
* @hide
*/
@SystemApi
@@ -965,13 +995,30 @@ public abstract class Connection extends Conferenceable {
* Sent along with {@link #EVENT_DEVICE_TO_DEVICE_MESSAGE} to indicate the device to device
* message value.
* <p>
- * See {@link DiagnosticCall} for more information.
+ * See {@link CallDiagnostics} for more information.
* @hide
*/
@SystemApi
public static final String EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE =
"android.telecom.extra.DEVICE_TO_DEVICE_MESSAGE_VALUE";
+ /**
+ * Connection event used to communicate a {@link android.telephony.CallQuality} report from
+ * telephony to Telecom for relaying to
+ * {@link DiagnosticCall#onCallQualityReceived(CallQuality)}.
+ * @hide
+ */
+ public static final String EVENT_CALL_QUALITY_REPORT =
+ "android.telecom.event.CALL_QUALITY_REPORT";
+
+ /**
+ * Extra sent with {@link #EVENT_CALL_QUALITY_REPORT} containing the
+ * {@link android.telephony.CallQuality} data.
+ * @hide
+ */
+ public static final String EXTRA_CALL_QUALITY_REPORT =
+ "android.telecom.extra.CALL_QUALITY_REPORT";
+
// Flag controlling whether PII is emitted into the logs
private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
@@ -3367,7 +3414,7 @@ public abstract class Connection extends Conferenceable {
* Intent intent = new Intent(Intent.ACTION_MAIN, null);
* intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
* intent.setClass(context, YourIncomingCallActivity.class);
- * PendingIntent pendingIntent = PendingIntent.getActivity(context, 1, intent, 0);
+ * PendingIntent pendingIntent = PendingIntent.getActivity(context, 1, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
*
* // Build the notification as an ongoing high priority item; this ensures it will show as
* // a heads up notification which slides down over top of the current content.
@@ -3425,6 +3472,137 @@ 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
+ * {@link ConnectionService#onCreateIncomingConnection} when call filtering completes in
+ * Telecom, including checking the blocked number db, per-contact settings, and custom call
+ * filtering apps.
+ *
+ * @param callFilteringCompletionInfo Info provided by Telecom on the results of call filtering.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_CONTACTS)
+ public void onCallFilteringCompleted(
+ @NonNull CallFilteringCompletionInfo callFilteringCompletionInfo) { }
+
static String toLogSafePhoneNumber(String number) {
// For unknown number, log empty string.
if (number == null) {
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index d82205e20538..c5fc4365df7a 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -150,6 +150,7 @@ public abstract class ConnectionService extends Service {
private static final String SESSION_POST_DIAL_CONT = "CS.oPDC";
private static final String SESSION_PULL_EXTERNAL_CALL = "CS.pEC";
private static final String SESSION_SEND_CALL_EVENT = "CS.sCE";
+ private static final String SESSION_CALL_FILTERING_COMPLETED = "CS.oCFC";
private static final String SESSION_HANDOVER_COMPLETE = "CS.hC";
private static final String SESSION_EXTRAS_CHANGED = "CS.oEC";
private static final String SESSION_START_RTT = "CS.+RTT";
@@ -758,6 +759,22 @@ public abstract class ConnectionService extends Service {
}
@Override
+ 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 = completionInfo;
+ args.arg3 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_ON_CALL_FILTERING_COMPLETED, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
public void onExtrasChanged(String callId, Bundle extras, Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_EXTRAS_CHANGED);
try {
@@ -1418,6 +1435,21 @@ public abstract class ConnectionService extends Service {
}
break;
}
+ case MSG_ON_CALL_FILTERING_COMPLETED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ Log.continueSession((Session) args.arg3,
+ SESSION_HANDLER + SESSION_CALL_FILTERING_COMPLETED);
+ String callId = (String) args.arg1;
+ Connection.CallFilteringCompletionInfo completionInfo =
+ (Connection.CallFilteringCompletionInfo) args.arg2;
+ onCallFilteringCompleted(callId, completionInfo);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
case MSG_HANDOVER_COMPLETE: {
SomeArgs args = (SomeArgs) msg.obj;
try {
@@ -2435,6 +2467,15 @@ public abstract class ConnectionService extends Service {
}
}
+ 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(callFilteringCompletionInfo);
+ }
+ }
+
/**
* Notifies a {@link Connection} that a handover has completed.
*
@@ -3407,4 +3448,13 @@ public abstract class ConnectionService extends Service {
public Handler getHandler() {
return mHandler;
}
+
+ /**
+ * Sets this {@link ConnectionService} ready for testing purposes.
+ * @hide
+ */
+ @VisibleForTesting
+ public void setReadyForTest() {
+ mAreAccountsInitialized = true;
+ }
}
diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java
index 5b806a6e57da..1c070748400d 100644
--- a/telecomm/java/android/telecom/DefaultDialerManager.java
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -74,7 +74,7 @@ public class DefaultDialerManager {
* */
public static boolean setDefaultDialerApplication(Context context, String packageName,
int user) {
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
CompletableFuture<Void> future = new CompletableFuture<>();
Consumer<Boolean> callback = successful -> {
@@ -128,7 +128,7 @@ public class DefaultDialerManager {
* @hide
* */
public static String getDefaultDialerApplication(Context context, int user) {
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
return CollectionUtils.firstOrNull(context.getSystemService(RoleManager.class)
.getRoleHoldersAsUser(RoleManager.ROLE_DIALER, UserHandle.of(user)));
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 1472a4ac27bc..ed7b79f62753 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -16,9 +16,13 @@
package android.telecom;
+import android.annotation.Nullable;
import android.media.ToneGenerator;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.Annotation;
+import android.telephony.PreciseDisconnectCause;
+import android.telephony.ims.ImsReasonInfo;
import android.text.TextUtils;
import java.util.Objects;
@@ -112,6 +116,9 @@ public final class DisconnectCause implements Parcelable {
private CharSequence mDisconnectDescription;
private String mDisconnectReason;
private int mToneToPlay;
+ private int mTelephonyDisconnectCause;
+ private int mTelephonyPreciseDisconnectCause;
+ private ImsReasonInfo mImsReasonInfo;
/**
* Creates a new DisconnectCause.
@@ -155,11 +162,36 @@ public final class DisconnectCause implements Parcelable {
*/
public DisconnectCause(int code, CharSequence label, CharSequence description, String reason,
int toneToPlay) {
+ this(code, label, description, reason, toneToPlay,
+ android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
+ PreciseDisconnectCause.ERROR_UNSPECIFIED,
+ null /* imsReasonInfo */);
+ }
+
+ /**
+ * Creates a new DisconnectCause instance.
+ * @param code The code for the disconnect cause.
+ * @param label The localized label to show to the user to explain the disconnect.
+ * @param description The localized description to show to the user to explain the disconnect.
+ * @param reason The reason for the disconnect.
+ * @param toneToPlay The tone to play on disconnect, as defined in {@link ToneGenerator}.
+ * @param telephonyDisconnectCause The Telephony disconnect cause.
+ * @param telephonyPreciseDisconnectCause The Telephony precise disconnect cause.
+ * @param imsReasonInfo The relevant {@link ImsReasonInfo}, or {@code null} if not available.
+ * @hide
+ */
+ public DisconnectCause(int code, CharSequence label, CharSequence description, String reason,
+ int toneToPlay, @Annotation.DisconnectCauses int telephonyDisconnectCause,
+ @Annotation.PreciseDisconnectCauses int telephonyPreciseDisconnectCause,
+ @Nullable ImsReasonInfo imsReasonInfo) {
mDisconnectCode = code;
mDisconnectLabel = label;
mDisconnectDescription = description;
mDisconnectReason = reason;
mToneToPlay = toneToPlay;
+ mTelephonyDisconnectCause = telephonyDisconnectCause;
+ mTelephonyPreciseDisconnectCause = telephonyPreciseDisconnectCause;
+ mImsReasonInfo = imsReasonInfo;
}
/**
@@ -209,6 +241,33 @@ public final class DisconnectCause implements Parcelable {
}
/**
+ * Returns the telephony {@link android.telephony.DisconnectCause} for the call.
+ * @return The disconnect cause.
+ * @hide
+ */
+ public @Annotation.DisconnectCauses int getTelephonyDisconnectCause() {
+ return mTelephonyDisconnectCause;
+ }
+
+ /**
+ * Returns the telephony {@link android.telephony.PreciseDisconnectCause} for the call.
+ * @return The precise disconnect cause.
+ * @hide
+ */
+ public @Annotation.PreciseDisconnectCauses int getTelephonyPreciseDisconnectCause() {
+ return mTelephonyPreciseDisconnectCause;
+ }
+
+ /**
+ * Returns the telephony {@link ImsReasonInfo} associated with the call disconnection.
+ * @return The {@link ImsReasonInfo} or {@code null} if not known.
+ * @hide
+ */
+ public @Nullable ImsReasonInfo getImsReasonInfo() {
+ return mImsReasonInfo;
+ }
+
+ /**
* Returns the tone to play when disconnected.
*
* @return the tone as defined in {@link ToneGenerator} to play when disconnected.
@@ -217,7 +276,8 @@ public final class DisconnectCause implements Parcelable {
return mToneToPlay;
}
- public static final @android.annotation.NonNull Creator<DisconnectCause> CREATOR = new Creator<DisconnectCause>() {
+ public static final @android.annotation.NonNull Creator<DisconnectCause> CREATOR
+ = new Creator<DisconnectCause>() {
@Override
public DisconnectCause createFromParcel(Parcel source) {
int code = source.readInt();
@@ -225,7 +285,11 @@ public final class DisconnectCause implements Parcelable {
CharSequence description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
String reason = source.readString();
int tone = source.readInt();
- return new DisconnectCause(code, label, description, reason, tone);
+ int telephonyDisconnectCause = source.readInt();
+ int telephonyPreciseDisconnectCause = source.readInt();
+ ImsReasonInfo imsReasonInfo = source.readParcelable(null);
+ return new DisconnectCause(code, label, description, reason, tone,
+ telephonyDisconnectCause, telephonyPreciseDisconnectCause, imsReasonInfo);
}
@Override
@@ -241,6 +305,9 @@ public final class DisconnectCause implements Parcelable {
TextUtils.writeToParcel(mDisconnectDescription, destination, flags);
destination.writeString(mDisconnectReason);
destination.writeInt(mToneToPlay);
+ destination.writeInt(mTelephonyDisconnectCause);
+ destination.writeInt(mTelephonyPreciseDisconnectCause);
+ destination.writeParcelable(mImsReasonInfo, 0);
}
@Override
@@ -254,7 +321,10 @@ public final class DisconnectCause implements Parcelable {
+ Objects.hashCode(mDisconnectLabel)
+ Objects.hashCode(mDisconnectDescription)
+ Objects.hashCode(mDisconnectReason)
- + Objects.hashCode(mToneToPlay);
+ + Objects.hashCode(mToneToPlay)
+ + Objects.hashCode(mTelephonyDisconnectCause)
+ + Objects.hashCode(mTelephonyPreciseDisconnectCause)
+ + Objects.hashCode(mImsReasonInfo);
}
@Override
@@ -265,7 +335,11 @@ public final class DisconnectCause implements Parcelable {
&& Objects.equals(mDisconnectLabel, d.getLabel())
&& Objects.equals(mDisconnectDescription, d.getDescription())
&& Objects.equals(mDisconnectReason, d.getReason())
- && Objects.equals(mToneToPlay, d.getTone());
+ && Objects.equals(mToneToPlay, d.getTone())
+ && Objects.equals(mTelephonyDisconnectCause, d.getTelephonyDisconnectCause())
+ && Objects.equals(mTelephonyPreciseDisconnectCause,
+ d.getTelephonyPreciseDisconnectCause())
+ && Objects.equals(mImsReasonInfo, d.getImsReasonInfo());
}
return false;
}
@@ -325,6 +399,11 @@ public final class DisconnectCause implements Parcelable {
+ " Label: (" + label + ")"
+ " Description: (" + description + ")"
+ " Reason: (" + reason + ")"
- + " Tone: (" + mToneToPlay + ") ]";
+ + " Tone: (" + mToneToPlay + ") "
+ + " TelephonyCause: " + mTelephonyDisconnectCause + "/"
+ + mTelephonyPreciseDisconnectCause
+ + " ImsReasonInfo: "
+ + mImsReasonInfo
+ + "]";
}
}
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 705b4918726e..cac716efe78f 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -202,7 +202,7 @@ import java.util.List;
* Intent intent = new Intent(Intent.ACTION_MAIN, null);
* intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
* intent.setClass(context, YourIncomingCallActivity.class);
- * PendingIntent pendingIntent = PendingIntent.getActivity(context, 1, intent, 0);
+ * PendingIntent pendingIntent = PendingIntent.getActivity(context, 1, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
*
* // Build the notification as an ongoing high priority item; this ensures it will show as
* // a heads up notification which slides down over top of the current content.
diff --git a/telecomm/java/android/telecom/Logging/SessionManager.java b/telecomm/java/android/telecom/Logging/SessionManager.java
index 67e5eabf54eb..9d17219c1ae4 100644
--- a/telecomm/java/android/telecom/Logging/SessionManager.java
+++ b/telecomm/java/android/telecom/Logging/SessionManager.java
@@ -17,6 +17,7 @@
package android.telecom.Logging;
import android.annotation.Nullable;
+import android.content.ContentResolver;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
@@ -453,7 +454,9 @@ public class SessionManager {
* perform a sweep to check and make sure that the session is still not incomplete (stale).
*/
private long getCleanupTimeout(Context context) {
- return Settings.Secure.getLong(context.getContentResolver(), TIMEOUTS_PREFIX +
- "stale_session_cleanup_timeout_millis", DEFAULT_SESSION_TIMEOUT_MS);
+ final ContentResolver cr = context.getContentResolver();
+ return Settings.Secure.getLongForUser(cr, TIMEOUTS_PREFIX
+ + "stale_session_cleanup_timeout_millis", DEFAULT_SESSION_TIMEOUT_MS,
+ cr.getUserId());
}
}
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index 182dc8bb8325..320308c9e926 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -399,7 +399,7 @@ public final class ParcelableCall implements Parcelable {
}
/** The current state of the call. */
- public int getState() {
+ public @Call.CallState int getState() {
return mState;
}
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 52210a55c8d0..7a6fddb6f029 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -16,8 +16,10 @@
package android.telecom;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.hardware.camera2.CameraManager;
import android.net.Uri;
@@ -1198,6 +1200,29 @@ 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 completionInfo Info provided by Telecom on the results of call filtering.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_CONTACTS)
+ public void onCallFilteringCompleted(
+ @NonNull Connection.CallFilteringCompletionInfo completionInfo) {
+ Log.startSession("RC.oCFC", getActiveOwnerInfo());
+ try {
+ if (mConnected) {
+ mConnectionService.onCallFilteringCompleted(mConnectionId, completionInfo,
+ null /*Session.Info*/);
+ }
+ } catch (RemoteException ignored) {
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ /**
* Notifies this {@link RemoteConnection} of a response to a previous remotely-initiated RTT
* upgrade request sent via {@link Connection#sendRemoteRttRequest}.
* Acceptance of the request is indicated by the supplied {@link RttTextStream} being non-null,
diff --git a/telecomm/java/android/telecom/RemoteConnectionManager.java b/telecomm/java/android/telecom/RemoteConnectionManager.java
index f3c7bd83ed4b..fbbfefd9d00e 100644
--- a/telecomm/java/android/telecom/RemoteConnectionManager.java
+++ b/telecomm/java/android/telecom/RemoteConnectionManager.java
@@ -45,7 +45,10 @@ public class RemoteConnectionManager {
outgoingConnectionServiceRpc,
mOurConnectionServiceImpl);
mRemoteConnectionServices.put(componentName, remoteConnectionService);
- } catch (RemoteException ignored) {
+ } catch (RemoteException e) {
+ Log.w(RemoteConnectionManager.this,
+ "error when addConnectionService of %s: %s", componentName,
+ e.toString());
}
}
}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 746e72a0bde3..e000265f0a2c 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -25,6 +25,8 @@ import android.annotation.SuppressAutoDoc;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -32,6 +34,7 @@ import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -42,6 +45,7 @@ import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.telecom.ITelecomService;
import java.lang.annotation.Retention;
@@ -312,20 +316,22 @@ public class TelecomManager {
public static final String EXTRA_HAS_PICTURE = "android.telecom.extra.HAS_PICTURE";
/**
- * A URI representing the picture that was downloaded when a call is received.
+ * A {@link Uri} representing the picture that was downloaded when a call is received or
+ * uploaded when a call is placed.
+ *
* This is a content URI within the call log provider which can be used to open a file
* descriptor. This could be set a short time after a call is added to the Dialer app if the
- * download is delayed for some reason. The Dialer app will receive a callback via
+ * download/upload is delayed for some reason. The Dialer app will receive a callback via
* {@link Call.Callback#onDetailsChanged} when this value has changed.
*
* Reference: RCC.20 Section 2.4.3.2
*/
- public static final String EXTRA_INCOMING_PICTURE = "android.telecom.extra.INCOMING_PICTURE";
+ public static final String EXTRA_PICTURE_URI = "android.telecom.extra.PICTURE_URI";
- // TODO(hallliu), This UUID is obtained from TelephonyManager#uploadCallComposerPicture.
/**
* A ParcelUuid used as a token to represent a picture that was uploaded prior to the call
- * being placed.
+ * being placed. The value of this extra should be set using the {@link android.os.ParcelUuid}
+ * obtained from the callback in {@link TelephonyManager#uploadCallComposerPicture}.
*/
public static final String EXTRA_OUTGOING_PICTURE = "android.telecom.extra.OUTGOING_PICTURE";
@@ -1000,8 +1006,39 @@ public class TelecomManager {
PRESENTATION_PAYPHONE})
public @interface Presentation {}
+
+ /**
+ * Enable READ_PHONE_STATE protection on APIs querying and notifying call state, such as
+ * {@code TelecomManager#getCallState}, {@link TelephonyManager#getCallStateForSubscription()},
+ * and {@link android.telephony.TelephonyCallback.CallStateListener}.
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+ // this magic number is a bug ID
+ public static final long ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION = 157233955L;
+
+ /**
+ * Enable READ_PHONE_NUMBERS or READ_PRIVILEGED_PHONE_STATE protections on
+ * {@link TelecomManager#getPhoneAccount(PhoneAccountHandle)}.
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+ // bug ID
+ public static final long ENABLE_GET_PHONE_ACCOUNT_PERMISSION_PROTECTION = 183407956L;
+
private static final String TAG = "TelecomManager";
+
+ /** Cached service handles, cleared by resetServiceCache() at death */
+ private static final Object CACHE_LOCK = new Object();
+
+ @GuardedBy("CACHE_LOCK")
+ private static ITelecomService sTelecomService;
+ @GuardedBy("CACHE_LOCK")
+ private static final DeathRecipient SERVICE_DEATH = new DeathRecipient();
+
private final Context mContext;
private final ITelecomService mTelecomServiceOverride;
@@ -1056,13 +1093,14 @@ public class TelecomManager {
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public PhoneAccountHandle getDefaultOutgoingPhoneAccount(String uriScheme) {
- try {
- if (isServiceConnected()) {
- return getTelecomService().getDefaultOutgoingPhoneAccount(uriScheme,
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getDefaultOutgoingPhoneAccount(uriScheme,
mContext.getOpPackageName(), mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getDefaultOutgoingPhoneAccount", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#getDefaultOutgoingPhoneAccount", e);
}
return null;
}
@@ -1082,13 +1120,14 @@ public class TelecomManager {
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public @Nullable PhoneAccountHandle getUserSelectedOutgoingPhoneAccount() {
- try {
- if (isServiceConnected()) {
- return getTelecomService().getUserSelectedOutgoingPhoneAccount(
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getUserSelectedOutgoingPhoneAccount(
mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getUserSelectedOutgoingPhoneAccount", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#getUserSelectedOutgoingPhoneAccount", e);
}
return null;
}
@@ -1104,12 +1143,13 @@ public class TelecomManager {
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
public void setUserSelectedOutgoingPhoneAccount(@Nullable PhoneAccountHandle accountHandle) {
- try {
- if (isServiceConnected()) {
- getTelecomService().setUserSelectedOutgoingPhoneAccount(accountHandle);
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ service.setUserSelectedOutgoingPhoneAccount(accountHandle);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#setUserSelectedOutgoingPhoneAccount");
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#setUserSelectedOutgoingPhoneAccount");
}
}
@@ -1123,13 +1163,14 @@ public class TelecomManager {
* @see SubscriptionManager#getDefaultVoiceSubscriptionId()
*/
public PhoneAccountHandle getSimCallManager() {
- try {
- if (isServiceConnected()) {
- return getTelecomService().getSimCallManager(
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getSimCallManager(
SubscriptionManager.getDefaultSubscriptionId());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getSimCallManager");
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#getSimCallManager");
}
return null;
}
@@ -1145,12 +1186,13 @@ public class TelecomManager {
* @see SubscriptionManager#getActiveSubscriptionInfoList()
*/
public @Nullable PhoneAccountHandle getSimCallManagerForSubscription(int subscriptionId) {
- try {
- if (isServiceConnected()) {
- return getTelecomService().getSimCallManager(subscriptionId);
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getSimCallManager(subscriptionId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getSimCallManager");
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#getSimCallManager");
}
return null;
}
@@ -1168,12 +1210,13 @@ public class TelecomManager {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 119305590)
public PhoneAccountHandle getSimCallManager(int userId) {
- try {
- if (isServiceConnected()) {
- return getTelecomService().getSimCallManagerForUser(userId);
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getSimCallManagerForUser(userId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getSimCallManagerForUser");
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#getSimCallManagerForUser");
}
return null;
}
@@ -1210,13 +1253,14 @@ public class TelecomManager {
android.Manifest.permission.READ_PHONE_STATE
})
public List<PhoneAccountHandle> getPhoneAccountsSupportingScheme(String uriScheme) {
- try {
- if (isServiceConnected()) {
- return getTelecomService().getPhoneAccountsSupportingScheme(uriScheme,
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getPhoneAccountsSupportingScheme(uriScheme,
mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getPhoneAccountsSupportingScheme", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#getPhoneAccountsSupportingScheme", e);
}
return new ArrayList<>();
}
@@ -1251,13 +1295,14 @@ public class TelecomManager {
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public List<PhoneAccountHandle> getSelfManagedPhoneAccounts() {
- try {
- if (isServiceConnected()) {
- return getTelecomService().getSelfManagedPhoneAccounts(mContext.getOpPackageName(),
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getSelfManagedPhoneAccounts(mContext.getOpPackageName(),
mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getSelfManagedPhoneAccounts()", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#getSelfManagedPhoneAccounts()", e);
}
return new ArrayList<>();
}
@@ -1276,14 +1321,15 @@ public class TelecomManager {
@RequiresPermission(READ_PRIVILEGED_PHONE_STATE)
public @NonNull List<PhoneAccountHandle> getCallCapablePhoneAccounts(
boolean includeDisabledAccounts) {
- try {
- if (isServiceConnected()) {
- return getTelecomService().getCallCapablePhoneAccounts(includeDisabledAccounts,
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getCallCapablePhoneAccounts(includeDisabledAccounts,
mContext.getOpPackageName(), mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getCallCapablePhoneAccounts("
+ + includeDisabledAccounts + ")", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#getCallCapablePhoneAccounts(" +
- includeDisabledAccounts + ")", e);
}
return new ArrayList<>();
}
@@ -1291,18 +1337,22 @@ public class TelecomManager {
/**
* Returns a list of all {@link PhoneAccount}s registered for the calling package.
*
+ * @deprecated Use {@link #getSelfManagedPhoneAccounts()} instead to get only self-managed
+ * {@link PhoneAccountHandle} for the calling package.
* @return A list of {@code PhoneAccountHandle} objects.
* @hide
*/
@SystemApi
@SuppressLint("RequiresPermission")
+ @Deprecated
public List<PhoneAccountHandle> getPhoneAccountsForPackage() {
- try {
- if (isServiceConnected()) {
- return getTelecomService().getPhoneAccountsForPackage(mContext.getPackageName());
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getPhoneAccountsForPackage(mContext.getPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getPhoneAccountsForPackage", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#getPhoneAccountsForPackage", e);
}
return null;
}
@@ -1311,16 +1361,20 @@ public class TelecomManager {
* Return the {@link PhoneAccount} for a specified {@link PhoneAccountHandle}. Object includes
* resources which can be used in a user interface.
*
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_NUMBERS} for applications targeting API
+ * level 31+.
* @param account The {@link PhoneAccountHandle}.
* @return The {@link PhoneAccount} object.
*/
public PhoneAccount getPhoneAccount(PhoneAccountHandle account) {
- try {
- if (isServiceConnected()) {
- return getTelecomService().getPhoneAccount(account);
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getPhoneAccount(account, mContext.getPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getPhoneAccount", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#getPhoneAccount", e);
}
return null;
}
@@ -1333,12 +1387,13 @@ public class TelecomManager {
*/
@SystemApi
public int getAllPhoneAccountsCount() {
- try {
- if (isServiceConnected()) {
- return getTelecomService().getAllPhoneAccountsCount();
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getAllPhoneAccountsCount();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getAllPhoneAccountsCount", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#getAllPhoneAccountsCount", e);
}
return 0;
}
@@ -1351,12 +1406,13 @@ public class TelecomManager {
*/
@SystemApi
public List<PhoneAccount> getAllPhoneAccounts() {
- try {
- if (isServiceConnected()) {
- return getTelecomService().getAllPhoneAccounts();
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getAllPhoneAccounts();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getAllPhoneAccounts", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#getAllPhoneAccounts", e);
}
return Collections.EMPTY_LIST;
}
@@ -1369,12 +1425,13 @@ public class TelecomManager {
*/
@SystemApi
public List<PhoneAccountHandle> getAllPhoneAccountHandles() {
- try {
- if (isServiceConnected()) {
- return getTelecomService().getAllPhoneAccountHandles();
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getAllPhoneAccountHandles();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getAllPhoneAccountHandles", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#getAllPhoneAccountHandles", e);
}
return Collections.EMPTY_LIST;
}
@@ -1394,12 +1451,13 @@ public class TelecomManager {
* @param account The complete {@link PhoneAccount}.
*/
public void registerPhoneAccount(PhoneAccount account) {
- try {
- if (isServiceConnected()) {
- getTelecomService().registerPhoneAccount(account);
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ service.registerPhoneAccount(account);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#registerPhoneAccount", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#registerPhoneAccount", e);
}
}
@@ -1409,12 +1467,13 @@ public class TelecomManager {
* @param accountHandle A {@link PhoneAccountHandle} for the {@link PhoneAccount} to unregister.
*/
public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
- try {
- if (isServiceConnected()) {
- getTelecomService().unregisterPhoneAccount(accountHandle);
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ service.unregisterPhoneAccount(accountHandle);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#unregisterPhoneAccount", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#unregisterPhoneAccount", e);
}
}
@@ -1435,12 +1494,13 @@ public class TelecomManager {
@SystemApi
@SuppressLint("RequiresPermission")
public void clearAccounts() {
- try {
- if (isServiceConnected()) {
- getTelecomService().clearAccounts(mContext.getPackageName());
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ service.clearAccounts(mContext.getPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#clearAccounts", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#clearAccounts", e);
}
}
@@ -1449,12 +1509,15 @@ public class TelecomManager {
* @hide
*/
public void clearAccountsForPackage(String packageName) {
- try {
- if (isServiceConnected() && !TextUtils.isEmpty(packageName)) {
- getTelecomService().clearAccounts(packageName);
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ if (!TextUtils.isEmpty(packageName)) {
+ service.clearAccounts(packageName);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#clearAccountsForPackage", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#clearAccountsForPackage", e);
}
}
@@ -1467,12 +1530,13 @@ public class TelecomManager {
@SystemApi
@SuppressLint("RequiresPermission")
public ComponentName getDefaultPhoneApp() {
- try {
- if (isServiceConnected()) {
- return getTelecomService().getDefaultPhoneApp();
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getDefaultPhoneApp();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException attempting to get the default phone app.", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException attempting to get the default phone app.", e);
}
return null;
}
@@ -1484,12 +1548,13 @@ public class TelecomManager {
* selected as the default dialer.
*/
public String getDefaultDialerPackage() {
- try {
- if (isServiceConnected()) {
- return getTelecomService().getDefaultDialerPackage();
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getDefaultDialerPackage();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException attempting to get the default dialer package name.", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException attempting to get the default dialer package name.", e);
}
return null;
}
@@ -1505,13 +1570,14 @@ public class TelecomManager {
@SystemApi
@RequiresPermission(READ_PRIVILEGED_PHONE_STATE)
public @Nullable String getDefaultDialerPackage(@NonNull UserHandle userHandle) {
- try {
- if (isServiceConnected()) {
- return getTelecomService().getDefaultDialerPackageForUser(
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getDefaultDialerPackageForUser(
userHandle.getIdentifier());
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException attempting to get the default dialer package name.", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException attempting to get the default dialer package name.", e);
}
return null;
}
@@ -1538,12 +1604,13 @@ public class TelecomManager {
android.Manifest.permission.MODIFY_PHONE_STATE,
android.Manifest.permission.WRITE_SECURE_SETTINGS})
public boolean setDefaultDialer(@Nullable String packageName) {
- try {
- if (isServiceConnected()) {
- return getTelecomService().setDefaultDialer(packageName);
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.setDefaultDialer(packageName);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException attempting to set the default dialer.", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException attempting to set the default dialer.", e);
}
return false;
}
@@ -1555,12 +1622,13 @@ public class TelecomManager {
* preloaded.
*/
public @Nullable String getSystemDialerPackage() {
- try {
- if (isServiceConnected()) {
- return getTelecomService().getSystemDialerPackage();
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getSystemDialerPackage();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException attempting to get the system dialer package name.", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException attempting to get the system dialer package name.", e);
}
return null;
}
@@ -1574,13 +1642,14 @@ public class TelecomManager {
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public boolean isVoiceMailNumber(PhoneAccountHandle accountHandle, String number) {
- try {
- if (isServiceConnected()) {
- return getTelecomService().isVoiceMailNumber(accountHandle, number,
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.isVoiceMailNumber(accountHandle, number,
mContext.getOpPackageName(), mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling ITelecomService#isVoiceMailNumber.", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling ITelecomService#isVoiceMailNumber.", e);
}
return false;
}
@@ -1594,13 +1663,14 @@ public class TelecomManager {
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public String getVoiceMailNumber(PhoneAccountHandle accountHandle) {
- try {
- if (isServiceConnected()) {
- return getTelecomService().getVoiceMailNumber(accountHandle,
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getVoiceMailNumber(accountHandle,
mContext.getOpPackageName(), mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling ITelecomService#hasVoiceMailNumber.", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling ITelecomService#hasVoiceMailNumber.", e);
}
return null;
}
@@ -1625,13 +1695,14 @@ public class TelecomManager {
android.Manifest.permission.READ_PHONE_NUMBERS
}, conditional = true)
public String getLine1Number(PhoneAccountHandle accountHandle) {
- try {
- if (isServiceConnected()) {
- return getTelecomService().getLine1Number(accountHandle,
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getLine1Number(accountHandle,
mContext.getOpPackageName(), mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling ITelecomService#getLine1Number.", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling ITelecomService#getLine1Number.", e);
}
return null;
}
@@ -1645,13 +1716,14 @@ public class TelecomManager {
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public boolean isInCall() {
- try {
- if (isServiceConnected()) {
- return getTelecomService().isInCall(mContext.getOpPackageName(),
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.isInCall(mContext.getOpPackageName(),
mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling isInCall().", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling isInCall().", e);
}
return false;
}
@@ -1694,13 +1766,14 @@ public class TelecomManager {
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public boolean isInManagedCall() {
- try {
- if (isServiceConnected()) {
- return getTelecomService().isInManagedCall(mContext.getOpPackageName(),
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.isInManagedCall(mContext.getOpPackageName(),
mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling isInManagedCall().", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling isInManagedCall().", e);
}
return false;
}
@@ -1712,23 +1785,26 @@ public class TelecomManager {
* {@link TelephonyManager#CALL_STATE_OFFHOOK}
* {@link TelephonyManager#CALL_STATE_IDLE}
*
- * Note that this API does not require the
- * {@link android.Manifest.permission#READ_PHONE_STATE} permission. This is intentional, to
- * preserve the behavior of {@link TelephonyManager#getCallState()}, which also did not require
- * the permission.
- *
* Takes into consideration both managed and self-managed calls.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} for applications
+ * targeting API level 31+.
*
* @hide
*/
+ @RequiresPermission(anyOf = {READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PHONE_STATE}, conditional = true)
@SystemApi
public @CallState int getCallState() {
- try {
- if (isServiceConnected()) {
- return getTelecomService().getCallState();
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getCallStateUsingPackage(mContext.getPackageName(),
+ mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ Log.d(TAG, "RemoteException calling getCallState().", e);
}
- } catch (RemoteException e) {
- Log.d(TAG, "RemoteException calling getCallState().", e);
}
return TelephonyManager.CALL_STATE_IDLE;
}
@@ -1745,12 +1821,13 @@ public class TelecomManager {
android.Manifest.permission.READ_PHONE_STATE
})
public boolean isRinging() {
- try {
- if (isServiceConnected()) {
- return getTelecomService().isRinging(mContext.getOpPackageName());
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.isRinging(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException attempting to get ringing state of phone app.", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException attempting to get ringing state of phone app.", e);
}
return false;
}
@@ -1773,12 +1850,13 @@ public class TelecomManager {
@RequiresPermission(Manifest.permission.ANSWER_PHONE_CALLS)
@Deprecated
public boolean endCall() {
- try {
- if (isServiceConnected()) {
- return getTelecomService().endCall(mContext.getPackageName());
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.endCall(mContext.getPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#endCall", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#endCall", e);
}
return false;
}
@@ -1799,12 +1877,13 @@ public class TelecomManager {
{Manifest.permission.ANSWER_PHONE_CALLS, Manifest.permission.MODIFY_PHONE_STATE})
@Deprecated
public void acceptRingingCall() {
- try {
- if (isServiceConnected()) {
- getTelecomService().acceptRingingCall(mContext.getPackageName());
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ service.acceptRingingCall(mContext.getPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#acceptRingingCall", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#acceptRingingCall", e);
}
}
@@ -1820,13 +1899,14 @@ public class TelecomManager {
{Manifest.permission.ANSWER_PHONE_CALLS, Manifest.permission.MODIFY_PHONE_STATE})
@Deprecated
public void acceptRingingCall(int videoState) {
- try {
- if (isServiceConnected()) {
- getTelecomService().acceptRingingCallWithVideoState(
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ service.acceptRingingCallWithVideoState(
mContext.getPackageName(), videoState);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#acceptRingingCallWithVideoState", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#acceptRingingCallWithVideoState", e);
}
}
@@ -1850,12 +1930,13 @@ public class TelecomManager {
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void silenceRinger() {
- try {
- if (isServiceConnected()) {
- getTelecomService().silenceRinger(mContext.getOpPackageName());
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ service.silenceRinger(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#silenceRinger", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#silenceRinger", e);
}
}
@@ -1867,13 +1948,14 @@ public class TelecomManager {
android.Manifest.permission.READ_PHONE_STATE
})
public boolean isTtySupported() {
- try {
- if (isServiceConnected()) {
- return getTelecomService().isTtySupported(mContext.getOpPackageName(),
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.isTtySupported(mContext.getOpPackageName(),
mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException attempting to get TTY supported state.", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException attempting to get TTY supported state.", e);
}
return false;
}
@@ -1891,13 +1973,14 @@ public class TelecomManager {
@SystemApi
@RequiresPermission(READ_PRIVILEGED_PHONE_STATE)
public @TtyMode int getCurrentTtyMode() {
- try {
- if (isServiceConnected()) {
- return getTelecomService().getCurrentTtyMode(mContext.getOpPackageName(),
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getCurrentTtyMode(mContext.getOpPackageName(),
mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException attempting to get the current TTY mode.", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException attempting to get the current TTY mode.", e);
}
return TTY_MODE_OFF;
}
@@ -1933,8 +2016,9 @@ public class TelecomManager {
* {@link ConnectionService#onCreateIncomingConnection}.
*/
public void addNewIncomingCall(PhoneAccountHandle phoneAccount, Bundle extras) {
- try {
- if (isServiceConnected()) {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
if (extras != null && extras.getBoolean(EXTRA_IS_HANDOVER) &&
mContext.getApplicationContext().getApplicationInfo().targetSdkVersion >
Build.VERSION_CODES.O_MR1) {
@@ -1942,11 +2026,10 @@ public class TelecomManager {
"acceptHandover for API > O-MR1");
return;
}
- getTelecomService().addNewIncomingCall(
- phoneAccount, extras == null ? new Bundle() : extras);
+ service.addNewIncomingCall(phoneAccount, extras == null ? new Bundle() : extras);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException adding a new incoming call: " + phoneAccount, e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException adding a new incoming call: " + phoneAccount, e);
}
}
@@ -1981,13 +2064,14 @@ public class TelecomManager {
*/
public void addNewIncomingConference(@NonNull PhoneAccountHandle phoneAccount,
@NonNull Bundle extras) {
- try {
- if (isServiceConnected()) {
- getTelecomService().addNewIncomingConference(
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ service.addNewIncomingConference(
phoneAccount, extras == null ? new Bundle() : extras);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException adding a new incoming conference: " + phoneAccount, e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException adding a new incoming conference: " + phoneAccount, e);
}
}
@@ -2004,13 +2088,14 @@ public class TelecomManager {
*/
@SystemApi
public void addNewUnknownCall(PhoneAccountHandle phoneAccount, Bundle extras) {
- try {
- if (isServiceConnected()) {
- getTelecomService().addNewUnknownCall(
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ service.addNewUnknownCall(
phoneAccount, extras == null ? new Bundle() : extras);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException adding a new unknown call: " + phoneAccount, e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException adding a new unknown call: " + phoneAccount, e);
}
}
@@ -2431,12 +2516,13 @@ public class TelecomManager {
*/
public void acceptHandover(Uri srcAddr, @VideoProfile.VideoState int videoState,
PhoneAccountHandle destAcct) {
- try {
- if (isServiceConnected()) {
- getTelecomService().acceptHandover(srcAddr, videoState, destAcct);
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ service.acceptHandover(srcAddr, videoState, destAcct);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException acceptHandover: " + e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException acceptHandover: " + e);
}
}
@@ -2450,13 +2536,14 @@ public class TelecomManager {
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public boolean isInEmergencyCall() {
- try {
- if (isServiceConnected()) {
- return getTelecomService().isInEmergencyCall();
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.isInEmergencyCall();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException isInEmergencyCall: " + e);
+ return false;
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException isInEmergencyCall: " + e);
- return false;
}
return false;
}
@@ -2468,12 +2555,13 @@ public class TelecomManager {
* @hide
*/
public void handleCallIntent(Intent intent, String callingPackageProxy) {
- try {
- if (isServiceConnected()) {
- getTelecomService().handleCallIntent(intent, callingPackageProxy);
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ service.handleCallIntent(intent, callingPackageProxy);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException handleCallIntent: " + e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException handleCallIntent: " + e);
}
}
@@ -2485,14 +2573,36 @@ public class TelecomManager {
if (mTelecomServiceOverride != null) {
return mTelecomServiceOverride;
}
- return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));
+ if (sTelecomService == null) {
+ ITelecomService temp = ITelecomService.Stub.asInterface(
+ ServiceManager.getService(Context.TELECOM_SERVICE));
+ synchronized (CACHE_LOCK) {
+ if (sTelecomService == null && temp != null) {
+ try {
+ sTelecomService = temp;
+ sTelecomService.asBinder().linkToDeath(SERVICE_DEATH, 0);
+ } catch (Exception e) {
+ sTelecomService = null;
+ }
+ }
+ }
+ }
+ return sTelecomService;
+ }
+
+ private static class DeathRecipient implements IBinder.DeathRecipient {
+ @Override
+ public void binderDied() {
+ resetServiceCache();
+ }
}
- private boolean isServiceConnected() {
- boolean isConnected = getTelecomService() != null;
- if (!isConnected) {
- Log.w(TAG, "Telecom Service not found.");
+ private static void resetServiceCache() {
+ synchronized (CACHE_LOCK) {
+ if (sTelecomService != null) {
+ sTelecomService.asBinder().unlinkToDeath(SERVICE_DEATH, 0);
+ sTelecomService = null;
+ }
}
- return isConnected;
}
}