diff options
| author | 2021-10-07 23:50:15 +0000 | |
|---|---|---|
| committer | 2021-10-07 23:50:15 +0000 | |
| commit | c03b0fa033117c03430e361d561aa910e95a0478 (patch) | |
| tree | 9c6aaee5a3023a6c237394b44e06a3fdb46f6747 /telecomm/java/android | |
| parent | 8cc0f40cf250d9c66dc15d0e8bc3a41db9a7cfa1 (diff) | |
| parent | 531b8f4f2605c44cf73e8421f674a1c7a9c277ff (diff) | |
Merge "Merge Android 12"
Diffstat (limited to 'telecomm/java/android')
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; } } |