Merge "Read from correct extras when logging call composer" into sc-dev
diff --git a/Android.bp b/Android.bp
index 0d89b00..b7eb450 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
genrule {
name: "statslog-telecom-java-gen",
tools: ["stats-log-api-gen"],
diff --git a/res/raw/InCallQualityNotification.ogg b/res/raw/InCallQualityNotification.ogg
new file mode 100644
index 0000000..84c029a
--- /dev/null
+++ b/res/raw/InCallQualityNotification.ogg
Binary files differ
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 8b8a40c..d1142f7 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -43,7 +43,7 @@
<string name="respond_via_sms_setting_title_2" msgid="4914853536609553457">"Hariri majibu ya haraka"</string>
<string name="respond_via_sms_setting_summary" msgid="8054571501085436868"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="6579353156073272157">"Majibu ya haraka"</string>
- <string name="respond_via_sms_confirmation_format" msgid="2932395476561267842">"Ujumbe uliotumwa kwa <xliff:g id="PHONE_NUMBER">%s</xliff:g> ."</string>
+ <string name="respond_via_sms_confirmation_format" msgid="2932395476561267842">"Ujumbe umetumwa kwa <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
<string name="respond_via_sms_failure_format" msgid="5198680980054596391">"Imeshindwa kutuma SMS kwa <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
<string name="enable_account_preference_title" msgid="6949224486748457976">"Akaunti za simu"</string>
<string name="outgoing_call_not_allowed_user_restriction" msgid="3424338207838851646">"Piga simu za dharura pekee."</string>
diff --git a/res/values/config.xml b/res/values/config.xml
index 9cbbf46..b0e50b0 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -69,4 +69,8 @@
<!-- When true, the options in the call blocking settings to block restricted and unknown
callers are combined into a single toggle. -->
<bool name="combine_options_to_block_restricted_and_unknown_callers">true</bool>
+
+ <!-- When set, Telecom will attempt to bind to the {@link CallDiagnosticService} implementation
+ defined by the app with this package name. -->
+ <string name="call_diagnostic_service_package_name"></string>
</resources>
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 965af59..c4f398c 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -37,11 +37,13 @@
import android.os.UserHandle;
import android.provider.CallLog;
import android.provider.ContactsContract.Contacts;
+import android.telecom.BluetoothCallQualityReport;
import android.telecom.CallAudioState;
import android.telecom.CallerInfo;
import android.telecom.Conference;
import android.telecom.Connection;
import android.telecom.ConnectionService;
+import android.telecom.DiagnosticCall;
import android.telecom.DisconnectCause;
import android.telecom.GatewayInfo;
import android.telecom.InCallService;
@@ -156,6 +158,8 @@
Bundle extras, boolean isLegacy);
void onHandoverFailed(Call call, int error);
void onHandoverComplete(Call call);
+ void onBluetoothCallQualityReport(Call call, BluetoothCallQualityReport report);
+ void onReceivedDeviceToDeviceMessage(Call call, int messageType, int messageValue);
}
public abstract static class ListenerBase implements Listener {
@@ -244,6 +248,10 @@
public void onHandoverFailed(Call call, int error) {}
@Override
public void onHandoverComplete(Call call) {}
+ @Override
+ public void onBluetoothCallQualityReport(Call call, BluetoothCallQualityReport report) {}
+ @Override
+ public void onReceivedDeviceToDeviceMessage(Call call, int messageType, int messageValue) {}
}
private final CallerInfoLookupHelper.OnQueryCompleteListener mCallerInfoQueryListener =
@@ -647,6 +655,13 @@
private String mCallScreeningComponentName;
/**
+ * When {@code true} indicates this call originated from a SIM-based {@link PhoneAccount}.
+ * A sim-based {@link PhoneAccount} is one with {@link PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION}
+ * set.
+ */
+ private boolean mIsSimCall;
+
+ /**
* Persists the specified parameters and initializes the new instance.
* @param context The context.
* @param repository The connection service repository.
@@ -1077,6 +1092,10 @@
}
}
+ public void handleOverrideDisconnectMessage(@Nullable CharSequence message) {
+
+ }
+
/**
* Sets the call state. Although there exists the notion of appropriate state transitions
* (see {@link CallState}), in practice those expectations break down when cellular systems
@@ -1703,6 +1722,7 @@
PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
boolean isWorkCall = false;
boolean isCallRecordingToneSupported = false;
+ boolean isSimCall = false;
PhoneAccount phoneAccount =
phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
if (phoneAccount != null) {
@@ -1720,9 +1740,11 @@
PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) && phoneAccount.getExtras() != null
&& phoneAccount.getExtras().getBoolean(
PhoneAccount.EXTRA_PLAY_CALL_RECORDING_TONE, false));
+ isSimCall = phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION);
}
mIsWorkCall = isWorkCall;
mUseCallRecordingTone = isCallRecordingToneSupported;
+ mIsSimCall = isSimCall;
}
/**
@@ -1903,6 +1925,13 @@
if (didRttChange) {
if ((mConnectionProperties & Connection.PROPERTY_IS_RTT) ==
Connection.PROPERTY_IS_RTT) {
+ // If we already had RTT streams up, that means that either the call started
+ // with RTT or the user previously requested to start RTT. Either way, don't
+ // play the alert tone.
+ if (!areRttStreamsInitialized()) {
+ mCallsManager.playRttUpgradeToneForCall(this);
+ }
+
createRttStreams();
// Call startRtt to pass the RTT pipes down to the connection service.
// They already turned on the RTT property so no request should be sent.
@@ -2953,6 +2982,14 @@
}
requestHandover(phoneAccountHandle, videoState, handoverExtrasBundle, true);
} else {
+ // Relay bluetooth call quality reports to the call diagnostic service.
+ if (BluetoothCallQualityReport.EVENT_BLUETOOTH_CALL_QUALITY_REPORT.equals(event)
+ && extras.containsKey(
+ BluetoothCallQualityReport.EXTRA_BLUETOOTH_CALL_QUALITY_REPORT)) {
+ notifyBluetoothCallQualityReport(extras.getParcelable(
+ BluetoothCallQualityReport.EXTRA_BLUETOOTH_CALL_QUALITY_REPORT
+ ));
+ }
Log.addEvent(this, LogUtils.Events.CALL_EVENT, event);
mConnectionService.sendCallEvent(this, event, extras);
}
@@ -2963,6 +3000,17 @@
}
/**
+ * Notifies listeners when a bluetooth quality report is received.
+ * @param report The bluetooth quality report.
+ */
+ void notifyBluetoothCallQualityReport(@NonNull BluetoothCallQualityReport report) {
+ Log.addEvent(this, LogUtils.Events.BT_QUALITY_REPORT, "choppy=" + report.isChoppyVoice());
+ for (Listener l : mListeners) {
+ l.onBluetoothCallQualityReport(this, report);
+ }
+ }
+
+ /**
* Initiates a handover of this Call to the {@link ConnectionService} identified
* by destAcct.
* @param destAcct ConnectionService to which the call should be handed over.
@@ -3692,6 +3740,17 @@
for (Listener l : mListeners) {
l.onCallSwitchFailed(this);
}
+ } else if (Connection.EVENT_DEVICE_TO_DEVICE_MESSAGE.equals(event)
+ && extras != null && extras.containsKey(
+ Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE)
+ && extras.containsKey(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE)) {
+ // Relay an incoming D2D message to interested listeners; most notably the
+ // CallDiagnosticService.
+ int messageType = extras.getInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE);
+ int messageValue = extras.getInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE);
+ for (Listener l : mListeners) {
+ l.onReceivedDeviceToDeviceMessage(this, messageType, messageValue);
+ }
} else {
for (Listener l : mListeners) {
l.onConnectionEvent(this, event, extras);
@@ -3897,6 +3956,44 @@
}
/**
+ * Sends a device to device message to the other part of the call.
+ * @param message the message type to send.
+ * @param value the value for the message.
+ */
+ public void sendDeviceToDeviceMessage(@DiagnosticCall.MessageType int message, int value) {
+ Log.i(this, "sendDeviceToDeviceMessage; callId=%s, msg=%d/%d", getId(), message, value);
+ Bundle extras = new Bundle();
+ extras.putInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE, message);
+ extras.putInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE, value);
+ // Send to the connection service.
+ sendCallEvent(Connection.EVENT_DEVICE_TO_DEVICE_MESSAGE, extras);
+ }
+
+ /**
+ * Signals to the Dialer app to start displaying a diagnostic message.
+ * @param messageId a unique ID for the message to display.
+ * @param message the message to display.
+ */
+ public void displayDiagnosticMessage(int messageId, @NonNull CharSequence message) {
+ Bundle extras = new Bundle();
+ extras.putInt(android.telecom.Call.EXTRA_DIAGNOSTIC_MESSAGE_ID, messageId);
+ extras.putCharSequence(android.telecom.Call.EXTRA_DIAGNOSTIC_MESSAGE, message);
+ // Send to the dialer.
+ onConnectionEvent(android.telecom.Call.EVENT_DISPLAY_DIAGNOSTIC_MESSAGE, extras);
+ }
+
+ /**
+ * Signals to the Dialer app to stop displaying a diagnostic message.
+ * @param messageId a unique ID for the message to clear.
+ */
+ public void clearDiagnosticMessage(int messageId) {
+ Bundle extras = new Bundle();
+ extras.putInt(android.telecom.Call.EXTRA_DIAGNOSTIC_MESSAGE_ID, messageId);
+ // Send to the dialer.
+ onConnectionEvent(android.telecom.Call.EVENT_CLEAR_DIAGNOSTIC_MESSAGE, extras);
+ }
+
+ /**
* Remaps the call direction as indicated by an {@link android.telecom.Call.Details} direction
* constant to the constants (e.g. {@link #CALL_DIRECTION_INCOMING}) used in this call class.
* @param direction The android.telecom.Call direction.
@@ -3982,4 +4079,13 @@
}
}
}
+
+ /**
+ * @return {@code true} when this call originated from a SIM-based {@link PhoneAccount}.
+ * A sim-based {@link PhoneAccount} is one with {@link PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION}
+ * set.
+ */
+ public boolean isSimCall() {
+ return mIsSimCall;
+ }
}
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index a6509b4..6a7261e 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -264,6 +264,14 @@
}
}
+ public void playRttUpgradeTone(Call call) {
+ if (call != mForegroundCall) {
+ // We only play tones for foreground calls.
+ return;
+ }
+ mPlayerFactory.createPlayer(InCallTonePlayer.TONE_RTT_REQUEST).startTone();
+ }
+
/**
* Play or stop a call hold tone for a call. Triggered via
* {@link Connection#sendConnectionEvent(String)} when the
diff --git a/src/com/android/server/telecom/CallDiagnosticServiceAdapter.java b/src/com/android/server/telecom/CallDiagnosticServiceAdapter.java
new file mode 100644
index 0000000..79a94d3
--- /dev/null
+++ b/src/com/android/server/telecom/CallDiagnosticServiceAdapter.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom;
+
+import android.annotation.NonNull;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.telecom.CallDiagnosticService;
+import android.telecom.DiagnosticCall;
+import android.telecom.Log;
+
+import com.android.internal.telecom.ICallDiagnosticServiceAdapter;
+import com.android.internal.telecom.IInCallAdapter;
+
+/**
+ * Adapter class used to provide a path for messages FROM a {@link CallDiagnosticService} back to
+ * the telecom stack.
+ */
+public class CallDiagnosticServiceAdapter extends ICallDiagnosticServiceAdapter.Stub {
+ public interface TelecomAdapter {
+ void displayDiagnosticMessage(String callId, int messageId, CharSequence message);
+ void clearDiagnosticMessage(String callId, int messageId);
+ void sendDeviceToDeviceMessage(String callId, @DiagnosticCall.MessageType int message,
+ int value);
+ void overrideDisconnectMessage(String callId, CharSequence message);
+ }
+
+ private final TelecomAdapter mTelecomAdapter;
+ private final String mOwnerPackageName;
+ private final String mOwnerPackageAbbreviation;
+ private final TelecomSystem.SyncRoot mLock;
+
+ CallDiagnosticServiceAdapter(@NonNull TelecomAdapter telecomAdapter,
+ @NonNull String ownerPackageName, @NonNull TelecomSystem.SyncRoot lock) {
+ mTelecomAdapter = telecomAdapter;
+ mOwnerPackageName = ownerPackageName;
+ mOwnerPackageAbbreviation = Log.getPackageAbbreviation(ownerPackageName);
+ mLock = lock;
+ }
+
+ @Override
+ public void displayDiagnosticMessage(String callId, int messageId, CharSequence message)
+ throws RemoteException {
+ try {
+ Log.startSession("CDSA.dDM", mOwnerPackageAbbreviation);
+ long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ Log.i(this, "displayDiagnosticMessage; callId=%s, msg=%d/%s", callId, messageId,
+ message);
+ mTelecomAdapter.displayDiagnosticMessage(callId, messageId, message);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void clearDiagnosticMessage(String callId, int messageId) throws RemoteException {
+ try {
+ Log.startSession("CDSA.cDM", mOwnerPackageAbbreviation);
+ long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ Log.i(this, "clearDiagnosticMessage; callId=%s, msg=%d", callId, messageId);
+ mTelecomAdapter.clearDiagnosticMessage(callId, messageId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void sendDeviceToDeviceMessage(String callId, @DiagnosticCall.MessageType int message,
+ int value)
+ throws RemoteException {
+ try {
+ Log.startSession("CDSA.sDTDM", mOwnerPackageAbbreviation);
+ long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ Log.i(this, "sendDeviceToDeviceMessage; callId=%s, msg=%d/%d", callId, message,
+ value);
+ mTelecomAdapter.sendDeviceToDeviceMessage(callId, message, value);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void overrideDisconnectMessage(String callId, CharSequence message)
+ throws RemoteException {
+ try {
+ Log.startSession("CDSA.oDM", mOwnerPackageAbbreviation);
+ long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ Log.i(this, "overrideDisconnectMessage; callId=%s, msg=%s", callId, message);
+ mTelecomAdapter.overrideDisconnectMessage(callId, message);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ } finally {
+ Log.endSession();
+ }
+ }
+}
diff --git a/src/com/android/server/telecom/CallDiagnosticServiceController.java b/src/com/android/server/telecom/CallDiagnosticServiceController.java
new file mode 100644
index 0000000..943a176
--- /dev/null
+++ b/src/com/android/server/telecom/CallDiagnosticServiceController.java
@@ -0,0 +1,654 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.telecom.BluetoothCallQualityReport;
+import android.telecom.CallAudioState;
+import android.telecom.CallDiagnosticService;
+import android.telecom.ConnectionService;
+import android.telecom.DiagnosticCall;
+import android.telecom.InCallService;
+import android.telecom.Log;
+import android.telecom.ParcelableCall;
+import android.telephony.ims.ImsReasonInfo;
+import android.text.TextUtils;
+
+import com.android.internal.telecom.ICallDiagnosticService;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Responsible for maintaining binding to the {@link CallDiagnosticService} defined by the
+ * {@code call_diagnostic_service_package_name} key in the
+ * {@code packages/services/Telecomm/res/values/config.xml} file.
+ */
+public class CallDiagnosticServiceController extends CallsManagerListenerBase {
+ /**
+ * Context dependencies for the {@link CallDiagnosticServiceController}.
+ */
+ public interface ContextProxy {
+ List<ResolveInfo> queryIntentServicesAsUser(@NonNull Intent intent,
+ @PackageManager.ResolveInfoFlags int flags, @UserIdInt int userId);
+ boolean bindServiceAsUser(@NonNull @RequiresPermission Intent service,
+ @NonNull ServiceConnection conn, int flags, @NonNull UserHandle user);
+ void unbindService(@NonNull ServiceConnection conn);
+ UserHandle getCurrentUserHandle();
+ }
+
+ /**
+ * Listener for {@link Call} events; used to propagate these changes to the
+ * {@link CallDiagnosticService}.
+ */
+ private final Call.Listener mCallListener = new Call.ListenerBase() {
+ @Override
+ public void onConnectionCapabilitiesChanged(Call call) {
+ updateCall(call);
+ }
+
+ @Override
+ public void onConnectionPropertiesChanged(Call call, boolean didRttChange) {
+ updateCall(call);
+ }
+
+ /**
+ * Listens for changes to extras reported by a Telecom {@link Call}.
+ *
+ * Extras changes can originate from a {@link ConnectionService} or an {@link InCallService}
+ * so we will only trigger an update of the call information if the source of the extras
+ * change was a {@link ConnectionService}.
+ *
+ * @param call The call.
+ * @param source The source of the extras change ({@link Call#SOURCE_CONNECTION_SERVICE} or
+ * {@link Call#SOURCE_INCALL_SERVICE}).
+ * @param extras The extras.
+ */
+ @Override
+ public void onExtrasChanged(Call call, int source, Bundle extras) {
+ // Do not inform InCallServices of changes which originated there.
+ if (source == Call.SOURCE_INCALL_SERVICE) {
+ return;
+ }
+ updateCall(call);
+ }
+
+ /**
+ * Listens for changes to extras reported by a Telecom {@link Call}.
+ *
+ * Extras changes can originate from a {@link ConnectionService} or an {@link InCallService}
+ * so we will only trigger an update of the call information if the source of the extras
+ * change was a {@link ConnectionService}.
+ * @param call The call.
+ * @param source The source of the extras change ({@link Call#SOURCE_CONNECTION_SERVICE} or
+ * {@link Call#SOURCE_INCALL_SERVICE}).
+ * @param keys The extra key removed
+ */
+ @Override
+ public void onExtrasRemoved(Call call, int source, List<String> keys) {
+ // Do not inform InCallServices of changes which originated there.
+ if (source == Call.SOURCE_INCALL_SERVICE) {
+ return;
+ }
+ updateCall(call);
+ }
+
+ /**
+ * Handles changes to the video state of a call.
+ * @param call
+ * @param previousVideoState
+ * @param newVideoState
+ */
+ @Override
+ public void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) {
+ updateCall(call);
+ }
+
+ /**
+ * Relays a bluetooth call quality report received from the Bluetooth stack to the
+ * CallDiagnosticService.
+ * @param call The call.
+ * @param report The received report.
+ */
+ @Override
+ public void onBluetoothCallQualityReport(Call call, BluetoothCallQualityReport report) {
+ handleBluetoothCallQualityReport(call, report);
+ }
+
+ /**
+ * Relays a device to device message received from Telephony to the CallDiagnosticService.
+ * @param call
+ * @param messageType
+ * @param messageValue
+ */
+ @Override
+ public void onReceivedDeviceToDeviceMessage(Call call, int messageType, int messageValue) {
+ handleReceivedDeviceToDeviceMessage(call, messageType, messageValue);
+ }
+ };
+
+ /**
+ * {@link ServiceConnection} handling changes to binding of the {@link CallDiagnosticService}.
+ */
+ private class CallDiagnosticServiceConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.startSession("CDSC.oSC", Log.getPackageAbbreviation(name));
+ try {
+ synchronized (mLock) {
+ mCallDiagnosticService = ICallDiagnosticService.Stub.asInterface(service);
+
+ handleConnectionComplete(mCallDiagnosticService);
+ }
+ Log.i(CallDiagnosticServiceController.this, "onServiceConnected: cmp=%s", name);
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.startSession("CDSC.oSD", Log.getPackageAbbreviation(name));
+ try {
+ synchronized (mLock) {
+ mCallDiagnosticService = null;
+ mConnection = null;
+ }
+ Log.i(CallDiagnosticServiceController.this, "onServiceDisconnected: cmp=%s", name);
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void onBindingDied(ComponentName name) {
+ Log.startSession("CDSC.oBD", Log.getPackageAbbreviation(name));
+ try {
+ synchronized (mLock) {
+ mCallDiagnosticService = null;
+ mConnection = null;
+ }
+ Log.w(CallDiagnosticServiceController.this, "onBindingDied: cmp=%s", name);
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void onNullBinding(ComponentName name) {
+ Log.startSession("CDSC.oNB", Log.getPackageAbbreviation(name));
+ try {
+ synchronized (mLock) {
+ maybeUnbindCallScreeningService();
+ }
+ } finally {
+ Log.endSession();
+ }
+ }
+ }
+
+ private final String mPackageName;
+ private final ContextProxy mContextProxy;
+ private String mTestPackageName;
+ private CallDiagnosticServiceConnection mConnection;
+ private CallDiagnosticServiceAdapter mAdapter;
+ private final TelecomSystem.SyncRoot mLock;
+ private ICallDiagnosticService mCallDiagnosticService;
+ private final CallIdMapper mCallIdMapper = new CallIdMapper(Call::getId);
+
+ public CallDiagnosticServiceController(@NonNull ContextProxy contextProxy,
+ @Nullable String packageName, @NonNull TelecomSystem.SyncRoot lock) {
+ mContextProxy = contextProxy;
+ mPackageName = packageName;
+ mLock = lock;
+ }
+
+ /**
+ * Handles Telecom adding new calls. Will bind to the call diagnostic service if needed and
+ * send the calls, or send to an already bound service.
+ * @param call The call to add.
+ */
+ @Override
+ public void onCallAdded(@NonNull Call call) {
+ if (!call.isSimCall() || call.isExternalCall()) {
+ Log.i(this, "onCallAdded: skipping call %s as non-sim or external.", call.getId());
+ return;
+ }
+ if (mCallIdMapper.getCallId(call) == null) {
+ mCallIdMapper.addCall(call);
+ call.addListener(mCallListener);
+ }
+ if (isConnected()) {
+ sendCallToBoundService(call, mCallDiagnosticService);
+ } else {
+ maybeBindCallDiagnosticService();
+ }
+ }
+
+ /**
+ * Handles Telecom removal of calls; will remove the call from the bound service and if the
+ * number of tracked calls falls to zero, unbind from the service.
+ * @param call The call to remove from the bound CDS.
+ */
+ @Override
+ public void onCallRemoved(@NonNull Call call) {
+ if (!call.isSimCall() || call.isExternalCall()) {
+ Log.i(this, "onCallRemoved: skipping call %s as non-sim or external.", call.getId());
+ return;
+ }
+ mCallIdMapper.removeCall(call);
+ call.removeListener(mCallListener);
+ removeCallFromBoundService(call, mCallDiagnosticService);
+
+ if (mCallIdMapper.getCalls().size() == 0) {
+ maybeUnbindCallScreeningService();
+ }
+ }
+
+ @Override
+ public void onCallStateChanged(Call call, int oldState, int newState) {
+ updateCall(call);
+ }
+
+ @Override
+ public void onCallAudioStateChanged(CallAudioState oldCallAudioState,
+ CallAudioState newCallAudioState) {
+ if (mCallDiagnosticService != null) {
+ try {
+ mCallDiagnosticService.updateCallAudioState(newCallAudioState);
+ } catch (RemoteException e) {
+ Log.w(this, "onCallAudioStateChanged: failed %s", e);
+ }
+ }
+ }
+
+ /**
+ * Sets the test call diagnostic service; used by the telecom command line command to override
+ * the {@link CallDiagnosticService} to bind to for CTS test purposes.
+ * @param packageName The package name to set to.
+ */
+ public void setTestCallDiagnosticService(@Nullable String packageName) {
+ if (TextUtils.isEmpty(packageName)) {
+ mTestPackageName = null;
+ } else {
+ mTestPackageName = packageName;
+ }
+
+ Log.i(this, "setTestCallDiagnosticService: packageName=%s", packageName);
+ }
+
+ /**
+ * Determines the active call diagnostic service, taking into account the test override.
+ * @return The package name of the active call diagnostic service.
+ */
+ private @Nullable String getActiveCallDiagnosticService() {
+ if (mTestPackageName != null) {
+ return mTestPackageName;
+ }
+
+ return mPackageName;
+ }
+
+ /**
+ * If we are not already bound to the {@link CallDiagnosticService}, attempts to initiate a
+ * binding tho that service.
+ * @return {@code true} if we bound, {@code false} otherwise.
+ */
+ private boolean maybeBindCallDiagnosticService() {
+ if (mConnection != null) {
+ return false;
+ }
+
+ mConnection = new CallDiagnosticServiceConnection();
+ boolean bound = bindCallDiagnosticService(mContextProxy.getCurrentUserHandle(),
+ getActiveCallDiagnosticService(), mConnection);
+ if (!bound) {
+ mConnection = null;
+ }
+ return bound;
+ }
+
+ /**
+ * Performs binding to the {@link CallDiagnosticService}.
+ * @param userHandle user name to bind via.
+ * @param packageName package name of the CDS.
+ * @param serviceConnection The service connection to be notified of bind events.
+ * @return
+ */
+ private boolean bindCallDiagnosticService(UserHandle userHandle,
+ String packageName, CallDiagnosticServiceConnection serviceConnection) {
+
+ if (TextUtils.isEmpty(packageName)) {
+ Log.i(this, "bindCallDiagnosticService: no package; skip binding.");
+ return false;
+ }
+
+ Intent intent = new Intent(CallDiagnosticService.SERVICE_INTERFACE)
+ .setPackage(packageName);
+ Log.i(this, "bindCallDiagnosticService: user %d.", userHandle.getIdentifier());
+ List<ResolveInfo> entries = mContextProxy.queryIntentServicesAsUser(intent, 0,
+ userHandle.getIdentifier());
+ if (entries.isEmpty()) {
+ Log.i(this, "bindCallDiagnosticService: %s has no service.", packageName);
+ return false;
+ }
+
+ ResolveInfo entry = entries.get(0);
+ if (entry.serviceInfo == null) {
+ Log.i(this, "bindCallDiagnosticService: %s has no service info.", packageName);
+ return false;
+ }
+
+ if (entry.serviceInfo.permission == null || !entry.serviceInfo.permission.equals(
+ Manifest.permission.BIND_CALL_DIAGNOSTIC_SERVICE)) {
+ Log.i(this, "bindCallDiagnosticService: %s doesn't require "
+ + "BIND_CALL_DIAGNOSTIC_SERVICE.", packageName);
+ return false;
+ }
+
+ ComponentName componentName =
+ new ComponentName(entry.serviceInfo.packageName, entry.serviceInfo.name);
+ intent.setComponent(componentName);
+ if (mContextProxy.bindServiceAsUser(
+ intent,
+ serviceConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+ UserHandle.CURRENT)) {
+ Log.d(this, "bindCallDiagnosticService, found service, waiting for it to connect");
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * If we are bound to a {@link CallDiagnosticService}, unbind from it.
+ */
+ public void maybeUnbindCallScreeningService() {
+ if (mConnection != null) {
+ Log.i(this, "maybeUnbindCallScreeningService - unbinding from %s",
+ getActiveCallDiagnosticService());
+ try {
+ mContextProxy.unbindService(mConnection);
+ mCallDiagnosticService = null;
+ mConnection = null;
+ } catch (IllegalArgumentException e) {
+ Log.i(this, "maybeUnbindCallScreeningService: Exception when unbind %s : %s",
+ getActiveCallDiagnosticService(), e.getMessage());
+ }
+ } else {
+ Log.w(this, "maybeUnbindCallScreeningService - already unbound");
+ }
+ }
+
+ /**
+ * Implements the abstracted Telecom functionality the {@link CallDiagnosticServiceAdapter}
+ * depends on.
+ */
+ private CallDiagnosticServiceAdapter.TelecomAdapter mTelecomAdapter =
+ new CallDiagnosticServiceAdapter.TelecomAdapter() {
+
+ @Override
+ public void displayDiagnosticMessage(String callId, int messageId, CharSequence message) {
+ handleDisplayDiagnosticMessage(callId, messageId, message);
+ }
+
+ @Override
+ public void clearDiagnosticMessage(String callId, int messageId) {
+ handleClearDiagnosticMessage(callId, messageId);
+ }
+
+ @Override
+ public void sendDeviceToDeviceMessage(String callId,
+ @DiagnosticCall.MessageType int message, int value) {
+ handleSendD2DMessage(callId, message, value);
+ }
+
+ @Override
+ public void overrideDisconnectMessage(String callId, CharSequence message) {
+ handleOverrideDisconnectMessage(callId, message);
+ }
+ };
+
+ /**
+ * Sends all calls to the specified {@link CallDiagnosticService}.
+ * @param callDiagnosticService the CDS to send calls to.
+ */
+ private void handleConnectionComplete(@NonNull ICallDiagnosticService callDiagnosticService) {
+ mAdapter = new CallDiagnosticServiceAdapter(mTelecomAdapter,
+ getActiveCallDiagnosticService(), mLock);
+ try {
+ // Add adapter for communication back from the call diagnostic service to Telecom.
+ callDiagnosticService.setAdapter(mAdapter);
+
+ // Loop through all the calls we've got ready to send since binding.
+ for (Call call : mCallIdMapper.getCalls()) {
+ sendCallToBoundService(call, callDiagnosticService);
+ }
+ } catch (RemoteException e) {
+ Log.w(this, "handleConnectionComplete: error=%s", e);
+ }
+ }
+
+ /**
+ * Handles a request from a {@link CallDiagnosticService} to display a diagnostic message.
+ * @param callId the ID of the call to display the message for.
+ * @param message the message.
+ */
+ private void handleDisplayDiagnosticMessage(@NonNull String callId, int messageId,
+ @Nullable CharSequence message) {
+ Call call = mCallIdMapper.getCall(callId);
+ if (call == null) {
+ Log.w(this, "handleDisplayDiagnosticMessage: callId=%s; msg=%d/%s; invalid call",
+ callId, messageId, message);
+ return;
+ }
+ Log.i(this, "handleDisplayDiagnosticMessage: callId=%s; msg=%d/%s; invalid call",
+ callId, messageId, message);
+ call.displayDiagnosticMessage(messageId, message);
+ }
+
+ /**
+ * Handles a request from a {@link CallDiagnosticService} to clear a previously displayed
+ * diagnostic message.
+ * @param callId the ID of the call to display the message for.
+ * @param messageId the message ID which was previous posted.
+ */
+ private void handleClearDiagnosticMessage(@NonNull String callId, int messageId) {
+ Call call = mCallIdMapper.getCall(callId);
+ if (call == null) {
+ Log.w(this, "handleClearDiagnosticMessage: callId=%s; msg=%d; invalid call",
+ callId, messageId);
+ return;
+ }
+ Log.i(this, "handleClearDiagnosticMessage: callId=%s; msg=%d; invalid call",
+ callId, messageId);
+ call.clearDiagnosticMessage(messageId);
+ }
+
+ /**
+ * Handles a request from a {@link CallDiagnosticService} to send a device to device message.
+ * @param callId The ID of the call to send the D2D message for.
+ * @param message The message type.
+ * @param value The message value.
+ */
+ private void handleSendD2DMessage(@NonNull String callId,
+ @DiagnosticCall.MessageType int message, int value) {
+ Call call = mCallIdMapper.getCall(callId);
+ if (call == null) {
+ Log.w(this, "handleSendD2DMessage: callId=%s; msg=%d/%d; invalid call", callId,
+ message, value);
+ return;
+ }
+ Log.i(this, "handleSendD2DMessage: callId=%s; msg=%d/%d", callId, message, value);
+ call.sendDeviceToDeviceMessage(message, value);
+ }
+
+ /**
+ * Handles a request from a {@link CallDiagnosticService} to override the disconnect message
+ * for a call. This is the response path from a previous call into the
+ * {@link CallDiagnosticService} via {@link DiagnosticCall#onCallDisconnected(ImsReasonInfo)}.
+ * @param callId The telecom call ID the disconnect override is pending for.
+ * @param message The new disconnect message, or {@code null} if no override.
+ */
+ private void handleOverrideDisconnectMessage(@NonNull String callId,
+ @Nullable CharSequence message) {
+ Call call = mCallIdMapper.getCall(callId);
+ if (call == null) {
+ Log.w(this, "handleOverrideDisconnectMessage: callId=%s; msg=%s; invalid call", callId,
+ message);
+ return;
+ }
+ Log.i(this, "handleOverrideDisconnectMessage: callId=%s; msg=%s", callId, message);
+ call.handleOverrideDisconnectMessage(message);
+ }
+
+ /**
+ * Sends a single call to the bound {@link CallDiagnosticService}.
+ * @param call The call to send.
+ * @param callDiagnosticService The CDS to send it to.
+ */
+ private void sendCallToBoundService(@NonNull Call call,
+ @NonNull ICallDiagnosticService callDiagnosticService) {
+ try {
+ if (isConnected()) {
+ Log.w(this, "sendCallToBoundService: initializing %s", call.getId());
+ callDiagnosticService.initializeDiagnosticCall(getParceledCall(call));
+ } else {
+ Log.w(this, "sendCallToBoundService: not bound, skipping %s", call.getId());
+ }
+ } catch (RemoteException e) {
+ Log.w(this, "sendCallToBoundService: callId=%s, exception=%s", call.getId(), e);
+ }
+ }
+
+ /**
+ * Removes a call from a bound {@link CallDiagnosticService}.
+ * @param call The call to remove.
+ * @param callDiagnosticService The CDS to remove it from.
+ */
+ private void removeCallFromBoundService(@NonNull Call call,
+ @NonNull ICallDiagnosticService callDiagnosticService) {
+ try {
+ if (isConnected()) {
+ callDiagnosticService.removeDiagnosticCall(call.getId());
+ }
+ } catch (RemoteException e) {
+ Log.w(this, "removeCallFromBoundService: callId=%s, exception=%s", call.getId(), e);
+ }
+ }
+
+ /**
+ * @return {@code true} if the call diagnostic service is bound/connected.
+ */
+ private boolean isConnected() {
+ return mCallDiagnosticService != null;
+ }
+
+ /**
+ * Updates the Call diagnostic service with changes to a call.
+ * @param call The updated call.
+ */
+ private void updateCall(@NonNull Call call) {
+ try {
+ if (isConnected()) {
+ mCallDiagnosticService.updateCall(getParceledCall(call));
+ }
+ } catch (RemoteException e) {
+ Log.w(this, "updateCall: callId=%s, exception=%s", call.getId(), e);
+ }
+ }
+
+ /**
+ * Updates the call diagnostic service with a received bluetooth quality report.
+ * @param call The call.
+ * @param report The bluetooth call quality report.
+ */
+ private void handleBluetoothCallQualityReport(@NonNull Call call,
+ @NonNull BluetoothCallQualityReport report) {
+ try {
+ if (isConnected()) {
+ mCallDiagnosticService.receiveBluetoothCallQualityReport(report);
+ }
+ } catch (RemoteException e) {
+ Log.w(this, "handleBluetoothCallQualityReport: callId=%s, exception=%s", call.getId(),
+ e);
+ }
+ }
+
+ /**
+ * Informs a CallDiagnosticService of an incoming device to device message which was received
+ * via the carrier network.
+ * @param call the call the message was received via.
+ * @param messageType The message type.
+ * @param messageValue The message value.
+ */
+ private void handleReceivedDeviceToDeviceMessage(@NonNull Call call, int messageType,
+ int messageValue) {
+ try {
+ if (isConnected()) {
+ mCallDiagnosticService.receiveDeviceToDeviceMessage(call.getId(), messageType,
+ messageValue);
+ }
+ } catch (RemoteException e) {
+ Log.w(this, "handleReceivedDeviceToDeviceMessage: callId=%s, exception=%s",
+ call.getId(), e);
+ }
+ }
+
+ /**
+ * Get a parcelled representation of a call for transport to the service.
+ * @param call The call.
+ * @return The parcelled call.
+ */
+ private @NonNull ParcelableCall getParceledCall(@NonNull Call call) {
+ return ParcelableCallUtils.toParcelableCall(
+ call,
+ false /* includeVideoProvider */,
+ null /* phoneAcctRegistrar */,
+ false /* supportsExternalCalls */,
+ false /* includeRttCall */,
+ false /* isForSystemDialer */
+ );
+ }
+
+ /**
+ * Dumps the state of the {@link CallDiagnosticServiceController}.
+ *
+ * @param pw The {@code IndentingPrintWriter} to write the state to.
+ */
+ public void dump(IndentingPrintWriter pw) {
+ pw.print("activeCallDiagnosticService: ");
+ pw.println(getActiveCallDiagnosticService());
+ pw.print("isConnected: ");
+ pw.println(isConnected());
+ }
+}
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index ff68e7a..d2d50ad 100755
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -333,6 +333,7 @@
private final ConnectionServiceRepository mConnectionServiceRepository;
private final DtmfLocalTonePlayer mDtmfLocalTonePlayer;
private final InCallController mInCallController;
+ private final CallDiagnosticServiceController mCallDiagnosticServiceController;
private final CallAudioManager mCallAudioManager;
private final CallRecordingTonePlayer mCallRecordingTonePlayer;
private RespondViaSmsManager mRespondViaSmsManager;
@@ -488,6 +489,7 @@
CallAudioRouteStateMachine.Factory callAudioRouteStateMachineFactory,
CallAudioModeStateMachine.Factory callAudioModeStateMachineFactory,
InCallControllerFactory inCallControllerFactory,
+ CallDiagnosticServiceController callDiagnosticServiceController,
RoleManagerAdapter roleManagerAdapter,
ToastFactory toastFactory) {
mContext = context;
@@ -544,6 +546,7 @@
mInCallController = inCallControllerFactory.create(context, mLock, this,
systemStateHelper, defaultDialerCache, mTimeoutsAdapter,
emergencyCallHelper);
+ mCallDiagnosticServiceController = callDiagnosticServiceController;
mRinger = new Ringer(playerFactory, context, systemSettingsUtil, asyncRingtonePlayer,
ringtoneFactory, systemVibrator,
new Ringer.VibrationEffectProxy(), mInCallController);
@@ -573,6 +576,7 @@
mListeners.add(mCallLogManager);
mListeners.add(mPhoneStateBroadcaster);
mListeners.add(mInCallController);
+ mListeners.add(mCallDiagnosticServiceController);
mListeners.add(mCallAudioManager);
mListeners.add(mCallRecordingTonePlayer);
mListeners.add(missedCallNotifier);
@@ -623,6 +627,10 @@
return mRoleManagerAdapter;
}
+ public CallDiagnosticServiceController getCallDiagnosticServiceController() {
+ return mCallDiagnosticServiceController;
+ }
+
@Override
public void onSuccessfulOutgoingCall(Call call, int callState) {
Log.v(this, "onSuccessfulOutgoingCall, %s", call);
@@ -764,9 +772,16 @@
if (incomingCall.isUsingCallFiltering()) {
boolean isInContacts = incomingCall.getCallerInfo() != null
&& incomingCall.getCallerInfo().contactExists;
+ Connection.CallFilteringCompletionInfo completionInfo =
+ new Connection.CallFilteringCompletionInfo(!result.shouldAllowCall,
+ isInContacts,
+ result.mCallScreeningResponse == null
+ ? null : result.mCallScreeningResponse.toCallResponse(),
+ result.mCallScreeningComponentName == null ? null
+ : ComponentName.unflattenFromString(
+ result.mCallScreeningComponentName));
incomingCall.getConnectionService().onCallFilteringCompleted(incomingCall,
- !result.shouldAllowCall, isInContacts, result.mCallScreeningResponse,
- result.mIsResponseFromSystemDialer);
+ completionInfo);
}
// Get rid of the call composer attachments that aren't wanted
@@ -2766,6 +2781,16 @@
updateCanAddCall();
}
+ @Override
+ public void onRemoteRttRequest(Call call, int requestId) {
+ Log.i(this, "onRemoteRttRequest: call %s", call.getId());
+ playRttUpgradeToneForCall(call);
+ }
+
+ public void playRttUpgradeToneForCall(Call call) {
+ mCallAudioManager.playRttUpgradeTone(call);
+ }
+
// Construct the list of possible PhoneAccounts that the outgoing call can use based on the
// active calls in CallsManager. If any of the active calls are on a SIM based PhoneAccount,
// then include only that SIM based PhoneAccount and any non-SIM PhoneAccounts, such as SIP.
@@ -3114,7 +3139,11 @@
Log.i(this, "Auto-unholding held foreground call (call doesn't support hold)");
foregroundCall.unhold();
}
- }, new LoggedHandlerExecutor(mHandler, "CM.mCAR", mLock));
+ }, new LoggedHandlerExecutor(mHandler, "CM.mCAR", mLock))
+ .exceptionally((throwable) -> {
+ Log.e(TAG, throwable, "Error while executing call removal");
+ return null;
+ });
}
/**
@@ -3661,22 +3690,8 @@
Trace.beginSection("onCallStateChanged");
maybeHandleHandover(call, newState);
+ notifyCallStateChanged(call, oldState, newState);
- // Only broadcast state change for calls that are being tracked.
- if (mCalls.contains(call)) {
- updateCanAddCall();
- updateHasActiveRttCall();
- for (CallsManagerListener listener : mListeners) {
- if (LogUtils.SYSTRACE_DEBUG) {
- Trace.beginSection(listener.getClass().toString() +
- " onCallStateChanged");
- }
- listener.onCallStateChanged(call, oldState, newState);
- if (LogUtils.SYSTRACE_DEBUG) {
- Trace.endSection();
- }
- }
- }
Trace.endSection();
} else {
Log.i(this, "failed in setting the state to new state");
@@ -3684,6 +3699,24 @@
}
}
+ private void notifyCallStateChanged(Call call, int oldState, int newState) {
+ // Only broadcast state change for calls that are being tracked.
+ if (mCalls.contains(call)) {
+ updateCanAddCall();
+ updateHasActiveRttCall();
+ for (CallsManagerListener listener : mListeners) {
+ if (LogUtils.SYSTRACE_DEBUG) {
+ Trace.beginSection(listener.getClass().toString() +
+ " onCallStateChanged");
+ }
+ listener.onCallStateChanged(call, oldState, newState);
+ if (LogUtils.SYSTRACE_DEBUG) {
+ Trace.endSection();
+ }
+ }
+ }
+ }
+
/**
* Identifies call state transitions for a call which trigger handover events.
* - If this call has a handover to it which just started and this call goes active, treat
@@ -4721,6 +4754,13 @@
pw.decreaseIndent();
}
+ if (mCallDiagnosticServiceController != null) {
+ pw.println("mCallDiagnosticServiceController:");
+ pw.increaseIndent();
+ mCallDiagnosticServiceController.dump(pw);
+ pw.decreaseIndent();
+ }
+
if (mDefaultDialerCache != null) {
pw.println("mDefaultDialerCache:");
pw.increaseIndent();
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index 744c6a0..705fe4f 100755
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -1865,25 +1865,24 @@
}
}
- void onCallFilteringCompleted(Call call, boolean isBlocked, boolean isInContacts,
- CallScreeningService.ParcelableCallResponse callScreeningResponse,
- boolean isResponseFromSystemDialer) {
+ void onCallFilteringCompleted(Call call,
+ Connection.CallFilteringCompletionInfo completionInfo) {
final String callId = mCallIdMapper.getCallId(call);
if (callId != null && isServiceValid("onCallFilteringCompleted")) {
try {
- logOutgoing("onCallFilteringCompleted %b %b", isBlocked, isInContacts);
+ logOutgoing("onCallFilteringCompleted %s", completionInfo);
int contactsPermission = mContext.getPackageManager()
.checkPermission(Manifest.permission.READ_CONTACTS,
getComponentName().getPackageName());
if (contactsPermission == PackageManager.PERMISSION_GRANTED) {
- mServiceInterface.onCallFilteringCompleted(callId, isBlocked, isInContacts,
- callScreeningResponse, isResponseFromSystemDialer,
+ mServiceInterface.onCallFilteringCompleted(callId, completionInfo,
Log.getExternalSession(TELECOM_ABBREVIATION));
} else {
logOutgoing("Skipping call filtering complete message for %s due"
+ " to lack of READ_CONTACTS", getComponentName().getPackageName());
}
- } catch (RemoteException ignored) {
+ } catch (RemoteException e) {
+ Log.e(this, e, "Remote exception calling onCallFilteringCompleted");
}
}
}
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index dbf7f83..08a64ca 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -622,6 +622,10 @@
}
}
+ public boolean isCarMode() {
+ return mIsCarMode;
+ }
+
@Override
public int connect(Call call) {
if (mIsConnected) {
@@ -1379,7 +1383,7 @@
/**
* Unbinds an existing bound connection to the in-call app.
*/
- private void unbindFromServices() {
+ public void unbindFromServices() {
try {
mContext.unregisterReceiver(mPackageChangedReceiver);
} catch (IllegalArgumentException e) {
@@ -2048,10 +2052,15 @@
mCarModeTracker.getCarModeApps().stream().collect(Collectors.joining(", ")));
if (mInCallServiceConnection != null) {
if (shouldUseCarModeUI()) {
+ Log.i(this, "updateCarModeForConnections: potentially update car mode app.");
mInCallServiceConnection.changeCarModeApp(
mCarModeTracker.getCurrentCarModePackage());
} else {
- mInCallServiceConnection.disableCarMode();
+ if (mInCallServiceConnection.isCarMode()) {
+ Log.i(this, "updateCarModeForConnections: car mode no longer "
+ + "applicable; disabling");
+ mInCallServiceConnection.disableCarMode();
+ }
}
}
}
diff --git a/src/com/android/server/telecom/InCallTonePlayer.java b/src/com/android/server/telecom/InCallTonePlayer.java
index 4f0cf8d..7500436 100644
--- a/src/com/android/server/telecom/InCallTonePlayer.java
+++ b/src/com/android/server/telecom/InCallTonePlayer.java
@@ -162,6 +162,8 @@
public static final int TONE_UNOBTAINABLE_NUMBER = 12;
public static final int TONE_VOICE_PRIVACY = 13;
public static final int TONE_VIDEO_UPGRADE = 14;
+ public static final int TONE_RTT_REQUEST = 15;
+ public static final int TONE_IN_CALL_QUALITY_NOTIFICATION = 16;
private static final int TONE_RESOURCE_ID_UNDEFINED = -1;
@@ -329,12 +331,22 @@
// TODO: fill in.
throw new IllegalStateException("Voice privacy tone NYI.");
case TONE_VIDEO_UPGRADE:
+ case TONE_RTT_REQUEST:
// Similar to the call waiting tone, but does not repeat.
toneType = ToneGenerator.TONE_SUP_CALL_WAITING;
toneVolume = RELATIVE_VOLUME_HIPRI;
toneLengthMillis = 4000;
mediaResourceId = TONE_RESOURCE_ID_UNDEFINED;
break;
+ case TONE_IN_CALL_QUALITY_NOTIFICATION:
+ // Don't use tone generator
+ toneType = ToneGenerator.TONE_UNKNOWN;
+ toneVolume = RELATIVE_VOLUME_UNDEFINED;
+ toneLengthMillis = 0;
+
+ // Use a tone resource file for a more rich, full-bodied tone experience.
+ mediaResourceId = R.raw.InCallQualityNotification;
+ break;
default:
throw new IllegalStateException("Bad toneId: " + mToneId);
}
diff --git a/src/com/android/server/telecom/LogUtils.java b/src/com/android/server/telecom/LogUtils.java
index e4a414b..a9bf18c 100644
--- a/src/com/android/server/telecom/LogUtils.java
+++ b/src/com/android/server/telecom/LogUtils.java
@@ -196,6 +196,7 @@
public static final String REDIRECTION_USER_CONFIRMATION = "REDIRECTION_USER_CONFIRMATION";
public static final String REDIRECTION_USER_CONFIRMED = "REDIRECTION_USER_CONFIRMED";
public static final String REDIRECTION_USER_CANCELLED = "REDIRECTION_USER_CANCELLED";
+ public static final String BT_QUALITY_REPORT = "BT_QUALITY_REPORT";
public static class Timings {
public static final String ACCEPT_TIMING = "accept";
diff --git a/src/com/android/server/telecom/ParcelableCallUtils.java b/src/com/android/server/telecom/ParcelableCallUtils.java
index a3ef987..f500828 100644
--- a/src/com/android/server/telecom/ParcelableCallUtils.java
+++ b/src/com/android/server/telecom/ParcelableCallUtils.java
@@ -27,6 +27,7 @@
import android.telecom.ParcelableCall;
import android.telecom.ParcelableRttCall;
import android.telecom.TelecomManager;
+import android.telephony.ims.ImsCallProfile;
import android.text.TextUtils;
import java.util.ArrayList;
@@ -61,6 +62,7 @@
static {
RESTRICTED_CALL_SCREENING_EXTRA_KEYS = new ArrayList<>();
RESTRICTED_CALL_SCREENING_EXTRA_KEYS.add(android.telecom.Connection.EXTRA_SIP_INVITE);
+ RESTRICTED_CALL_SCREENING_EXTRA_KEYS.add(ImsCallProfile.EXTRA_IS_BUSINESS_CALL);
}
public static class Converter {
diff --git a/src/com/android/server/telecom/SystemStateHelper.java b/src/com/android/server/telecom/SystemStateHelper.java
index 8fb6bc5..f78d21c 100644
--- a/src/com/android/server/telecom/SystemStateHelper.java
+++ b/src/com/android/server/telecom/SystemStateHelper.java
@@ -75,37 +75,41 @@
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- Log.startSession("SSP.oR");
+ Log.startSession("SSH.oR");
try {
- String action = intent.getAction();
- if (UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED.equals(action)) {
- int priority = intent.getIntExtra(UiModeManager.EXTRA_PRIORITY,
- UiModeManager.DEFAULT_PRIORITY);
- String callingPackage = intent.getStringExtra(
- UiModeManager.EXTRA_CALLING_PACKAGE);
- Log.i(SystemStateHelper.this, "ENTER_CAR_MODE_PRIORITIZED; priority=%d, pkg=%s",
- priority, callingPackage);
- onEnterCarMode(priority, callingPackage);
- } else if (UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED.equals(action)) {
- int priority = intent.getIntExtra(UiModeManager.EXTRA_PRIORITY,
- UiModeManager.DEFAULT_PRIORITY);
- String callingPackage = intent.getStringExtra(
- UiModeManager.EXTRA_CALLING_PACKAGE);
- Log.i(SystemStateHelper.this, "EXIT_CAR_MODE_PRIORITIZED; priority=%d, pkg=%s",
- priority, callingPackage);
- onExitCarMode(priority, callingPackage);
- } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
- Uri data = intent.getData();
- if (data == null) {
+ synchronized (mLock) {
+ String action = intent.getAction();
+ if (UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED.equals(action)) {
+ int priority = intent.getIntExtra(UiModeManager.EXTRA_PRIORITY,
+ UiModeManager.DEFAULT_PRIORITY);
+ String callingPackage = intent.getStringExtra(
+ UiModeManager.EXTRA_CALLING_PACKAGE);
+ Log.i(SystemStateHelper.this,
+ "ENTER_CAR_MODE_PRIORITIZED; priority=%d, pkg=%s",
+ priority, callingPackage);
+ onEnterCarMode(priority, callingPackage);
+ } else if (UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED.equals(action)) {
+ int priority = intent.getIntExtra(UiModeManager.EXTRA_PRIORITY,
+ UiModeManager.DEFAULT_PRIORITY);
+ String callingPackage = intent.getStringExtra(
+ UiModeManager.EXTRA_CALLING_PACKAGE);
+ Log.i(SystemStateHelper.this,
+ "EXIT_CAR_MODE_PRIORITIZED; priority=%d, pkg=%s",
+ priority, callingPackage);
+ onExitCarMode(priority, callingPackage);
+ } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+ Uri data = intent.getData();
+ if (data == null) {
+ Log.w(SystemStateHelper.this,
+ "Got null data for package removed, ignoring");
+ return;
+ }
+ mListeners.forEach(
+ l -> l.onPackageUninstalled(data.getEncodedSchemeSpecificPart()));
+ } else {
Log.w(SystemStateHelper.this,
- "Got null data for package removed, ignoring");
- return;
+ "Unexpected intent received: %s", intent.getAction());
}
- mListeners.forEach(
- l -> l.onPackageUninstalled(data.getEncodedSchemeSpecificPart()));
- } else {
- Log.w(SystemStateHelper.this,
- "Unexpected intent received: %s", intent.getAction());
}
} finally {
Log.endSession();
@@ -116,18 +120,28 @@
@Override
public void onProjectionStateChanged(int activeProjectionTypes,
@NonNull Set<String> projectingPackages) {
- if (projectingPackages.isEmpty()) {
- onReleaseAutomotiveProjection();
- } else {
- onSetAutomotiveProjection(projectingPackages.iterator().next());
+ Log.startSession("SSH.oPSC");
+ try {
+ synchronized (mLock) {
+ if (projectingPackages.isEmpty()) {
+ onReleaseAutomotiveProjection();
+ } else {
+ onSetAutomotiveProjection(projectingPackages.iterator().next());
+ }
+ }
+ } finally {
+ Log.endSession();
}
+
}
private Set<SystemStateListener> mListeners = new CopyOnWriteArraySet<>();
private boolean mIsCarModeOrProjectionActive;
+ private final TelecomSystem.SyncRoot mLock;
- public SystemStateHelper(Context context) {
+ public SystemStateHelper(Context context, TelecomSystem.SyncRoot lock) {
mContext = context;
+ mLock = lock;
IntentFilter intentFilter1 = new IntentFilter(
UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED);
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index c0db71a..a18ae6d 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -832,12 +832,12 @@
}
/**
- * @see android.telecom.TelecomManager#hasCompanionInCallServiceAccess
+ * @see android.telecom.TelecomManager#hasManageOngoingCallsPermission
*/
@Override
- public boolean hasCompanionInCallServiceAccess(String callingPackage) {
+ public boolean hasManageOngoingCallsPermission(String callingPackage) {
try {
- Log.startSession("TSI.hCICSA");
+ Log.startSession("TSI.hMOCP");
return PermissionChecker.checkPermissionForPreflight(mContext,
Manifest.permission.MANAGE_ONGOING_CALLS,
PermissionChecker.PID_UNKNOWN, Binder.getCallingUid(),
@@ -1790,6 +1790,7 @@
mCallsManager.markCallAsRemoved(call);
}
}
+ mCallsManager.getInCallController().unbindFromServices();
});
}
} finally {
@@ -1921,6 +1922,30 @@
Log.endSession();
}
}
+
+ @Override
+ public void setTestCallDiagnosticService(String packageName) {
+ try {
+ Log.startSession("TSI.sTCDS");
+ enforceModifyPermission();
+ enforceShellOnly(Binder.getCallingUid(), "setTestCallDiagnosticService is for "
+ + "shell use only.");
+ synchronized (mLock) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ CallDiagnosticServiceController controller =
+ mCallsManager.getCallDiagnosticServiceController();
+ if (controller != null) {
+ controller.setTestCallDiagnosticService(packageName);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ } finally {
+ Log.endSession();
+ }
+ }
};
/**
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index 8928e76..613f50d 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -36,8 +36,10 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.UserHandle;
import android.os.UserManager;
@@ -45,8 +47,11 @@
import android.telecom.PhoneAccountHandle;
import android.widget.Toast;
+import androidx.annotation.NonNull;
+
import java.io.FileNotFoundException;
import java.io.InputStream;
+import java.util.List;
/**
* Top-level Application class for Telecom.
@@ -233,7 +238,7 @@
mContext.registerReceiver(bluetoothStateReceiver, BluetoothStateReceiver.INTENT_FILTER);
WiredHeadsetManager wiredHeadsetManager = new WiredHeadsetManager(mContext);
- SystemStateHelper systemStateHelper = new SystemStateHelper(mContext);
+ SystemStateHelper systemStateHelper = new SystemStateHelper(mContext, mLock);
mMissedCallNotifier = missedCallNotifierImplFactory
.makeMissedCallNotifierImpl(mContext, mPhoneAccountRegistrar, defaultDialerCache,
@@ -260,6 +265,39 @@
}
};
+ CallDiagnosticServiceController callDiagnosticServiceController =
+ new CallDiagnosticServiceController(
+ new CallDiagnosticServiceController.ContextProxy() {
+ @Override
+ public List<ResolveInfo> queryIntentServicesAsUser(
+ @NonNull Intent intent, int flags, int userId) {
+ return mContext.getPackageManager().queryIntentServicesAsUser(
+ intent, flags, userId);
+ }
+
+ @Override
+ public boolean bindServiceAsUser(@NonNull Intent service,
+ @NonNull ServiceConnection conn, int flags,
+ @NonNull UserHandle user) {
+ return mContext.bindServiceAsUser(service, conn, flags, user);
+ }
+
+ @Override
+ public void unbindService(@NonNull ServiceConnection conn) {
+ mContext.unbindService(conn);
+ }
+
+ @Override
+ public UserHandle getCurrentUserHandle() {
+ return mCallsManager.getCurrentUserHandle();
+ }
+ },
+ mContext.getResources().getString(
+ com.android.server.telecom.R.string
+ .call_diagnostic_service_package_name),
+ mLock
+ );
+
AudioProcessingNotification audioProcessingNotification =
new AudioProcessingNotification(mContext);
@@ -303,6 +341,7 @@
callAudioRouteStateMachineFactory,
callAudioModeStateMachineFactory,
inCallControllerFactory,
+ callDiagnosticServiceController,
roleManagerAdapter,
toastFactory);
diff --git a/src/com/android/server/telecom/callfiltering/CallFilteringResult.java b/src/com/android/server/telecom/callfiltering/CallFilteringResult.java
index a853f2f..84ce4d4 100644
--- a/src/com/android/server/telecom/callfiltering/CallFilteringResult.java
+++ b/src/com/android/server/telecom/callfiltering/CallFilteringResult.java
@@ -241,11 +241,22 @@
CallFilteringResult r2) {
if (r1.mIsResponseFromSystemDialer) {
builder.setCallScreeningResponse(r1.mCallScreeningResponse, true);
+ builder.setCallScreeningComponentName(r1.mCallScreeningComponentName);
+ builder.setCallScreeningAppName(r1.mCallScreeningAppName);
} else if (r2.mIsResponseFromSystemDialer) {
builder.setCallScreeningResponse(r2.mCallScreeningResponse, true);
+ builder.setCallScreeningComponentName(r2.mCallScreeningComponentName);
+ builder.setCallScreeningAppName(r2.mCallScreeningAppName);
} else {
- builder.setCallScreeningResponse(r1.mCallScreeningResponse == null
- ? r2.mCallScreeningResponse : r1.mCallScreeningResponse, false);
+ if (r1.mCallScreeningResponse != null) {
+ builder.setCallScreeningResponse(r1.mCallScreeningResponse, false);
+ builder.setCallScreeningComponentName(r1.mCallScreeningComponentName);
+ builder.setCallScreeningAppName(r1.mCallScreeningAppName);
+ } else {
+ builder.setCallScreeningResponse(r2.mCallScreeningResponse, false);
+ builder.setCallScreeningComponentName(r2.mCallScreeningComponentName);
+ builder.setCallScreeningAppName(r2.mCallScreeningAppName);
+ }
}
}
diff --git a/src/com/android/server/telecom/callfiltering/IncomingCallFilterGraph.java b/src/com/android/server/telecom/callfiltering/IncomingCallFilterGraph.java
index 50cd731..9fa864e 100644
--- a/src/com/android/server/telecom/callfiltering/IncomingCallFilterGraph.java
+++ b/src/com/android/server/telecom/callfiltering/IncomingCallFilterGraph.java
@@ -159,7 +159,11 @@
startFuture.thenComposeAsync(filter::startFilterLookup,
new LoggedHandlerExecutor(mHandler, "ICFG.sF", null))
.thenApplyAsync(postFilterTask::whenDone,
- new LoggedHandlerExecutor(mHandler, "ICFG.sF", null));
+ new LoggedHandlerExecutor(mHandler, "ICFG.sF", null))
+ .exceptionally((t) -> {
+ Log.e(filter, t, "Encountered exception running filter");
+ return null;
+ });
Log.i(TAG, "Filter %s scheduled.", filter);
}
diff --git a/src/com/android/server/telecom/ui/DisconnectedCallNotifier.java b/src/com/android/server/telecom/ui/DisconnectedCallNotifier.java
index 3f54689..66f9fe4 100644
--- a/src/com/android/server/telecom/ui/DisconnectedCallNotifier.java
+++ b/src/com/android/server/telecom/ui/DisconnectedCallNotifier.java
@@ -333,7 +333,8 @@
UserHandle userHandle) {
Intent intent = new Intent(action, data, mContext, TelecomBroadcastReceiver.class);
intent.putExtra(TelecomBroadcastIntentProcessor.EXTRA_USERHANDLE, userHandle);
- return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ return PendingIntent.getBroadcast(mContext, 0, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
}
private boolean canRespondViaSms(@NonNull CallInfo call) {
@@ -354,7 +355,7 @@
TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(mContext);
taskStackBuilder.addNextIntent(intent);
- return taskStackBuilder.getPendingIntent(0, 0, null, userHandle);
+ return taskStackBuilder.getPendingIntent(0, PendingIntent.FLAG_IMMUTABLE, null, userHandle);
}
/**
diff --git a/testapps/Android.bp b/testapps/Android.bp
index 26347fe..11ea474 100644
--- a/testapps/Android.bp
+++ b/testapps/Android.bp
@@ -14,6 +14,10 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
android_test {
name: "TelecomTestApps",
static_libs: [
diff --git a/testapps/AndroidManifest.xml b/testapps/AndroidManifest.xml
index 891a7a7..dd8258a 100644
--- a/testapps/AndroidManifest.xml
+++ b/testapps/AndroidManifest.xml
@@ -39,6 +39,13 @@
<uses-library android:name="android.test.runner"/>
<!-- Miscellaneous telecom app-related test activities. -->
+ <service android:name="com.android.server.telecom.testapps.TestCallDiagnosticService"
+ android:permission="android.permission.BIND_CALL_DIAGNOSTIC_SERVICE"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.telecom.CallDiagnosticService"/>
+ </intent-filter>
+ </service>
<service android:name="com.android.server.telecom.testapps.TestConnectionService"
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
diff --git a/testapps/carmodedialer/Android.bp b/testapps/carmodedialer/Android.bp
index 7179b1f..9f65b8c 100644
--- a/testapps/carmodedialer/Android.bp
+++ b/testapps/carmodedialer/Android.bp
@@ -14,6 +14,10 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
android_test {
name: "TelecomCarModeApp",
static_libs: [
diff --git a/testapps/companionapp/Android.bp b/testapps/companionapp/Android.bp
index e3612a1..8718b37 100644
--- a/testapps/companionapp/Android.bp
+++ b/testapps/companionapp/Android.bp
@@ -14,6 +14,10 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
android_test {
name: "TelecomCompanionApp",
static_libs: [
diff --git a/testapps/res/layout/incall_screen.xml b/testapps/res/layout/incall_screen.xml
index f8f919b..22d3574 100644
--- a/testapps/res/layout/incall_screen.xml
+++ b/testapps/res/layout/incall_screen.xml
@@ -131,6 +131,11 @@
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"/>
</LinearLayout>
+ <TextView
+ android:id="@+id/incoming_composer_attachments"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dp"/>
<Button
android:id="@+id/disable_incallservice"
android:layout_width="wrap_content"
diff --git a/testapps/res/layout/testdialer_main.xml b/testapps/res/layout/testdialer_main.xml
index 3f397b8..749d236 100644
--- a/testapps/res/layout/testdialer_main.xml
+++ b/testapps/res/layout/testdialer_main.xml
@@ -59,6 +59,11 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/startCallWithRtt"/>
+ <CheckBox
+ android:id="@+id/add_composer_attachments_checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/addComposerAttachments"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/testapps/res/values/donottranslate_strings.xml b/testapps/res/values/donottranslate_strings.xml
index 7331d5a..b1a1f80 100644
--- a/testapps/res/values/donottranslate_strings.xml
+++ b/testapps/res/values/donottranslate_strings.xml
@@ -44,6 +44,8 @@
<string name="startCallWithRtt">Start call with RTT</string>
+ <string name="addComposerAttachments">Add call composer attachments</string>
+
<string name="rttIfaceButton">RTT</string>
<string name="endRttButton">End RTT</string>
diff --git a/testapps/src/com/android/server/telecom/testapps/TestCallDiagnosticService.java b/testapps/src/com/android/server/telecom/testapps/TestCallDiagnosticService.java
new file mode 100644
index 0000000..73bf438
--- /dev/null
+++ b/testapps/src/com/android/server/telecom/testapps/TestCallDiagnosticService.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom.testapps;
+
+import android.telecom.BluetoothCallQualityReport;
+import android.telecom.Call;
+import android.telecom.CallAudioState;
+import android.telecom.CallDiagnosticService;
+import android.telecom.DiagnosticCall;
+import android.telecom.Log;
+import android.telephony.CallQuality;
+import android.telephony.ims.ImsReasonInfo;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+public class TestCallDiagnosticService extends CallDiagnosticService {
+
+ public static final class TestDiagnosticCall extends DiagnosticCall {
+ public Call.Details details;
+
+ TestDiagnosticCall(Call.Details details) {
+ this.details = details;
+ }
+
+ @Override
+ public void onCallDetailsChanged(@NonNull Call.Details details) {
+ Log.i(this, "onCallDetailsChanged; %s", details);
+ }
+
+ @Override
+ public void onReceiveDeviceToDeviceMessage(int message, int value) {
+ Log.i(this, "onReceiveDeviceToDeviceMessage; %d/%d", message, value);
+ }
+
+ @Nullable
+ @Override
+ public CharSequence onCallDisconnected(int disconnectCause, int preciseDisconnectCause) {
+ Log.i(this, "onCallDisconnected");
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public CharSequence onCallDisconnected(@NonNull ImsReasonInfo disconnectReason) {
+ Log.i(this, "onCallDisconnected");
+ return null;
+ }
+
+ @Override
+ public void onCallQualityReceived(@NonNull CallQuality callQuality) {
+ Log.i(this, "onCallQualityReceived %s", callQuality);
+ }
+ }
+
+ @NonNull
+ @Override
+ public DiagnosticCall onInitializeDiagnosticCall(@NonNull Call.Details call) {
+ Log.i(this, "onInitiatlizeDiagnosticCall %s", call);
+ return new TestDiagnosticCall(call);
+ }
+
+ @Override
+ public void onRemoveDiagnosticCall(@NonNull DiagnosticCall call) {
+ Log.i(this, "onRemoveDiagnosticCall %s", call);
+ }
+
+ @Override
+ public void onCallAudioStateChanged(@NonNull CallAudioState audioState) {
+ Log.i(this, "onCallAudioStateChanged %s", audioState);
+ }
+
+ @Override
+ public void onBluetoothCallQualityReportReceived(
+ @NonNull BluetoothCallQualityReport qualityReport) {
+ Log.i(this, "onBluetoothCallQualityReportReceived %s", qualityReport);
+ }
+}
diff --git a/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java b/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java
index 1e06387..010d6ee 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java
@@ -9,6 +9,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.location.Location;
import android.net.Uri;
import android.os.Bundle;
import android.os.PersistableBundle;
@@ -32,8 +33,18 @@
private EditText mNumberView;
private CheckBox mRttCheckbox;
+ private CheckBox mComposerCheckbox;
private EditText mPriorityView;
+ private static final String COMPOSER_SUBJECT = "Sample call composer subject";
+ private static final Location COMPOSER_LOCATION;
+ static {
+ // Area 51
+ COMPOSER_LOCATION = new Location("");
+ COMPOSER_LOCATION.setLongitude(-115.806407);
+ COMPOSER_LOCATION.setLatitude(37.236214);
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -68,6 +79,7 @@
mNumberView = (EditText) findViewById(R.id.number);
mRttCheckbox = (CheckBox) findViewById(R.id.call_with_rtt_checkbox);
+ mComposerCheckbox = (CheckBox) findViewById(R.id.add_composer_attachments_checkbox);
findViewById(R.id.enable_car_mode).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
@@ -169,6 +181,11 @@
if (mRttCheckbox.isChecked()) {
extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, true);
}
+ if (mComposerCheckbox.isChecked()) {
+ extras.putInt(TelecomManager.EXTRA_PRIORITY, TelecomManager.PRIORITY_URGENT);
+ extras.putParcelable(TelecomManager.EXTRA_LOCATION, COMPOSER_LOCATION);
+ extras.putString(TelecomManager.EXTRA_CALL_SUBJECT, COMPOSER_SUBJECT);
+ }
Bundle intentExtras = new Bundle();
intentExtras.putBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
diff --git a/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java b/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java
index 3f3a2c8..bdd4c1a 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java
@@ -21,6 +21,7 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.location.Location;
import android.os.Bundle;
import android.telecom.Call;
import android.telecom.CallAudioState;
@@ -28,6 +29,7 @@
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
+import android.telephony.ims.ImsCallProfile;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
@@ -248,6 +250,47 @@
enableInCallService();
}
});
+
+ // Find the ringing call and populate the composer extras
+ for (int i = 0; i < TestCallList.getInstance().size(); i++) {
+ Call call = TestCallList.getInstance().getCall(i);
+ if (call.getState() == Call.STATE_RINGING) {
+ int priority = call.getDetails()
+ .getIntentExtras().getInt(TelecomManager.EXTRA_PRIORITY, -1);
+ Location location = call.getDetails()
+ .getIntentExtras().getParcelable(TelecomManager.EXTRA_LOCATION);
+ String subject = call.getDetails()
+ .getIntentExtras().getString(TelecomManager.EXTRA_CALL_SUBJECT);
+ boolean isBusiness = call.getDetails()
+ .getExtras().getBoolean(ImsCallProfile.EXTRA_IS_BUSINESS_CALL);
+
+ StringBuilder display = new StringBuilder();
+ display.append("priority=");
+ switch (priority) {
+ case TelecomManager.PRIORITY_NORMAL:
+ display.append("normal");
+ break;
+ case TelecomManager.PRIORITY_URGENT:
+ display.append("urgent");
+ break;
+ default:
+ display.append("unset");
+ }
+ display.append(";");
+ if (location != null) {
+ display.append("lat=" + location.getLatitude());
+ display.append("lon=" + location.getLongitude());
+ } else {
+ display.append("loc=null");
+ }
+
+ display.append(" subject=" + subject);
+ display.append(" isBusiness=" + isBusiness);
+ TextView attachmentsTextView = findViewById(R.id.incoming_composer_attachments);
+ attachmentsTextView.setText(display.toString());
+ break;
+ }
+ }
}
public void updateCallAudioState(CallAudioState cas) {
diff --git a/tests/src/com/android/server/telecom/tests/CallDiagnosticServiceControllerTest.java b/tests/src/com/android/server/telecom/tests/CallDiagnosticServiceControllerTest.java
new file mode 100644
index 0000000..d420f1d
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/CallDiagnosticServiceControllerTest.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom.tests;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.telecom.ParcelableCall;
+
+import com.android.internal.telecom.ICallDiagnosticService;
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallDiagnosticServiceController;
+import com.android.server.telecom.TelecomSystem;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class CallDiagnosticServiceControllerTest {
+ private static final String TEST_CDS_PACKAGE = "com.test.stuff";
+ private static final String TEST_PACKAGE = "com.android.telecom.calldiagnosticservice";
+ private static final String TEST_CLASS =
+ "com.android.telecom.calldiagnosticservice.TestService";
+ private static final ComponentName TEST_COMPONENT = new ComponentName(TEST_PACKAGE, TEST_CLASS);
+ private static final List<ResolveInfo> RESOLVE_INFOS = new ArrayList<>();
+ private static final ResolveInfo TEST_RESOLVE_INFO = new ResolveInfo();
+ static {
+ TEST_RESOLVE_INFO.serviceInfo = new ServiceInfo();
+ TEST_RESOLVE_INFO.serviceInfo.packageName = TEST_PACKAGE;
+ TEST_RESOLVE_INFO.serviceInfo.name = TEST_CLASS;
+ TEST_RESOLVE_INFO.serviceInfo.permission = Manifest.permission.BIND_CALL_DIAGNOSTIC_SERVICE;
+ RESOLVE_INFOS.add(TEST_RESOLVE_INFO);
+ }
+ private static final String ID_1 = "1";
+ private static final String ID_2 = "2";
+
+ @Mock
+ private CallDiagnosticServiceController.ContextProxy mContextProxy;
+ @Mock
+ private Call mCall;
+ @Mock
+ private Call mCallTwo;
+ @Mock
+ private ICallDiagnosticService mICallDiagnosticService;
+ private TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { };
+
+ private CallDiagnosticServiceController mCallDiagnosticService;
+ private ServiceConnection mServiceConnection;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ when(mCall.getId()).thenReturn(ID_1);
+ when(mCall.isSimCall()).thenReturn(true);
+ when(mCall.isExternalCall()).thenReturn(false);
+
+ when(mCallTwo.getId()).thenReturn(ID_2);
+ when(mCallTwo.isSimCall()).thenReturn(true);
+ when(mCallTwo.isExternalCall()).thenReturn(false);
+ mServiceConnection = null;
+
+ // Mock out the context and other junk that we depend on.
+ when(mContextProxy.queryIntentServicesAsUser(any(Intent.class), anyInt(), anyInt()))
+ .thenReturn(RESOLVE_INFOS);
+ when(mContextProxy.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class),
+ anyInt(), any(UserHandle.class))).thenReturn(true);
+ when(mContextProxy.getCurrentUserHandle()).thenReturn(UserHandle.CURRENT);
+
+ mCallDiagnosticService = new CallDiagnosticServiceController(mContextProxy,
+ TEST_PACKAGE, mLock);
+ }
+
+ /**
+ * Verify no binding takes place for a non-sim call.
+ */
+ @Test
+ public void testNoBindOnNonSimCall() {
+ Call mockCall = Mockito.mock(Call.class);
+ when(mockCall.isSimCall()).thenReturn(false);
+
+ mCallDiagnosticService.onCallAdded(mockCall);
+
+ verify(mContextProxy, never()).bindServiceAsUser(any(Intent.class),
+ any(ServiceConnection.class), anyInt(), any(UserHandle.class));
+ }
+
+ /**
+ * Verify no binding takes place for a SIM external call.
+ */
+ @Test
+ public void testNoBindOnExternalCall() {
+ Call mockCall = Mockito.mock(Call.class);
+ when(mockCall.isSimCall()).thenReturn(true);
+ when(mockCall.isExternalCall()).thenReturn(true);
+
+ mCallDiagnosticService.onCallAdded(mockCall);
+
+ verify(mContextProxy, never()).bindServiceAsUser(any(Intent.class),
+ any(ServiceConnection.class), anyInt(), any(UserHandle.class));
+ }
+
+ /**
+ * Verify a valid SIM call causes binding to initiate.
+ */
+ @Test
+ public void testAddSimCallCausesBind() throws RemoteException {
+ mCallDiagnosticService.onCallAdded(mCall);
+
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ ArgumentCaptor<ServiceConnection> serviceConnectionCaptor = ArgumentCaptor.forClass(
+ ServiceConnection.class);
+ verify(mContextProxy).bindServiceAsUser(intentCaptor.capture(),
+ serviceConnectionCaptor.capture(), anyInt(), any(UserHandle.class));
+ assertEquals(TEST_PACKAGE, intentCaptor.getValue().getPackage());
+
+ // Now we'll pretend bind completed and we sent back the binder.
+ IBinder mockBinder = mock(IBinder.class);
+ when(mockBinder.queryLocalInterface(anyString())).thenReturn(mICallDiagnosticService);
+ serviceConnectionCaptor.getValue().onServiceConnected(TEST_COMPONENT, mockBinder);
+ mServiceConnection = serviceConnectionCaptor.getValue();
+
+ // Make sure it's sent
+ verify(mICallDiagnosticService).initializeDiagnosticCall(any(ParcelableCall.class));
+ }
+
+ /**
+ * Verify removing the active call causes it to be removed from the CallDiagnosticService and
+ * that an unbind takes place.
+ */
+ @Test
+ public void testRemoveSimCallCausesRemoveAndUnbind() throws RemoteException {
+ testAddSimCallCausesBind();
+ mCallDiagnosticService.onCallRemoved(mCall);
+
+ verify(mICallDiagnosticService).removeDiagnosticCall(eq(ID_1));
+ verify(mContextProxy).unbindService(eq(mServiceConnection));
+ }
+
+ /**
+ * Try to add and remove two and verify bind/unbind.
+ */
+ @Test
+ public void testAddTwo() throws RemoteException {
+ testAddSimCallCausesBind();
+ mCallDiagnosticService.onCallAdded(mCallTwo);
+ verify(mICallDiagnosticService, times(2)).initializeDiagnosticCall(
+ any(ParcelableCall.class));
+
+ mCallDiagnosticService.onCallRemoved(mCall);
+ // Not yet!
+ verify(mContextProxy, never()).unbindService(eq(mServiceConnection));
+
+ mCallDiagnosticService.onCallRemoved(mCallTwo);
+
+ verify(mICallDiagnosticService).removeDiagnosticCall(eq(ID_1));
+ verify(mICallDiagnosticService).removeDiagnosticCall(eq(ID_2));
+ verify(mContextProxy).unbindService(eq(mServiceConnection));
+ }
+
+ /**
+ * Verifies we can override the call diagnostic service package to a test package (used by CTS
+ * tests).
+ */
+ @Test
+ public void testTestOverride() {
+ mCallDiagnosticService.setTestCallDiagnosticService(TEST_CDS_PACKAGE);
+ mCallDiagnosticService.onCallAdded(mCall);
+
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContextProxy).bindServiceAsUser(intentCaptor.capture(),
+ any(ServiceConnection.class), anyInt(), any(UserHandle.class));
+ assertEquals(TEST_CDS_PACKAGE, intentCaptor.getValue().getPackage());
+ }
+}
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index 8378e3b..08f3536 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -67,6 +67,7 @@
import com.android.server.telecom.CallAudioManager;
import com.android.server.telecom.CallAudioModeStateMachine;
import com.android.server.telecom.CallAudioRouteStateMachine;
+import com.android.server.telecom.CallDiagnosticServiceController;
import com.android.server.telecom.CallState;
import com.android.server.telecom.CallerInfoLookupHelper;
import com.android.server.telecom.CallsManager;
@@ -196,6 +197,7 @@
@Mock private CallAudioRouteStateMachine.Factory mCallAudioRouteStateMachineFactory;
@Mock private CallAudioModeStateMachine mCallAudioModeStateMachine;
@Mock private CallAudioModeStateMachine.Factory mCallAudioModeStateMachineFactory;
+ @Mock private CallDiagnosticServiceController mCallDiagnosticServiceController;
@Mock private BluetoothStateReceiver mBluetoothStateReceiver;
@Mock private RoleManagerAdapter mRoleManagerAdapter;
@Mock private ToastFactory mToastFactory;
@@ -253,6 +255,7 @@
mCallAudioRouteStateMachineFactory,
mCallAudioModeStateMachineFactory,
mInCallControllerFactory,
+ mCallDiagnosticServiceController,
mRoleManagerAdapter,
mToastFactory);
diff --git a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
index 6736132..6e6646f 100755
--- a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
@@ -440,9 +440,8 @@
public void handoverComplete(String callId, Session.Info sessionInfo) {}
@Override
- public void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts,
- CallScreeningService.ParcelableCallResponse callScreeningResponse,
- boolean isResponseFromSystemDialer, Session.Info sessionInfo) { }
+ public void onCallFilteringCompleted(String callId,
+ Connection.CallFilteringCompletionInfo completionInfo, Session.Info sessionInfo) { }
}
FakeConnectionServiceDelegate mConnectionServiceDelegate;
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index c15a04f..8ae823e 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -254,6 +254,27 @@
assertFalse(mCarModeTracker.isInCarMode());
}
+ /**
+ * Ensure that if we remove a random unrelated app we don't exit car mode.
+ */
+ @SmallTest
+ @Test
+ public void testRandomAppRemovalInCarMode() {
+ setupMockPackageManager(true /* default */, true /* system */, true /* external calls */);
+ when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle);
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+
+ when(mMockSystemStateHelper.isCarModeOrProjectionActive()).thenReturn(true);
+
+ mSystemStateListener.onCarModeChanged(666, CAR_PKG, true);
+ verify(mCarModeTracker).handleEnterCarMode(666, CAR_PKG);
+ assertTrue(mCarModeTracker.isInCarMode());
+
+ mSystemStateListener.onPackageUninstalled("com.foo.test");
+ verify(mCarModeTracker, never()).forceRemove(CAR_PKG);
+ assertTrue(mCarModeTracker.isInCarMode());
+ }
+
@SmallTest
@Test
public void testAutomotiveProjectionAppRemoval() {
@@ -969,6 +990,31 @@
*/
@MediumTest
@Test
+ public void testRandomAppRemovalWhenNotInCarMode() throws Exception {
+ setupMocks(true /* isExternalCall */);
+ setupMockPackageManager(true /* default */, true /* system */, true /* external calls */);
+ // Bind to default dialer.
+ mInCallController.bindToServices(mMockCall);
+
+ // Uninstall an unrelated app.
+ mSystemStateListener.onPackageUninstalled("com.joe.stuff");
+
+ // Bind InCallServices, just once; we should not re-bind to the same app.
+ ArgumentCaptor<Intent> bindIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mMockContext).bindServiceAsUser(
+ bindIntentCaptor.capture(),
+ any(ServiceConnection.class),
+ eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
+ | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS),
+ eq(UserHandle.CURRENT));
+ }
+
+ /**
+ * Ensures that the {@link InCallController} will bind to a higher priority car mode service
+ * when one becomes available.
+ */
+ @MediumTest
+ @Test
public void testCarmodeRebindHigherPriority() throws Exception {
setupMocks(true /* isExternalCall */);
setupMockPackageManager(true /* default */, true /* system */, true /* external calls */);
diff --git a/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java b/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java
index db44dcd..2c3a6fc 100644
--- a/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java
@@ -611,7 +611,7 @@
assertNotNull("Not expecting null options bundle", bundleCaptor.getValue());
BroadcastOptions options = new BroadcastOptions(bundleCaptor.getValue());
assertTrue("App must have a temporary exemption set.",
- options.getTemporaryAppWhitelistDuration() > 0);
+ options.getTemporaryAppAllowlistDuration() > 0);
// A notification should never be posted by Telecom
verify(mNotificationManager, never()).notifyAsUser(nullable(String.class), anyInt(),
diff --git a/tests/src/com/android/server/telecom/tests/ParcelableCallUtilsTest.java b/tests/src/com/android/server/telecom/tests/ParcelableCallUtilsTest.java
index 6c941fe..a503283 100644
--- a/tests/src/com/android/server/telecom/tests/ParcelableCallUtilsTest.java
+++ b/tests/src/com/android/server/telecom/tests/ParcelableCallUtilsTest.java
@@ -1,13 +1,12 @@
package com.android.server.telecom.tests;
-import static com.android.server.telecom.TelecomSystem.*;
+import static com.android.server.telecom.TelecomSystem.SyncRoot;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
@@ -17,6 +16,7 @@
import android.telecom.Connection;
import android.telecom.ParcelableCall;
import android.telecom.PhoneAccountHandle;
+import android.telephony.ims.ImsCallProfile;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.server.telecom.Call;
@@ -26,7 +26,6 @@
import com.android.server.telecom.ParcelableCallUtils;
import com.android.server.telecom.PhoneAccountRegistrar;
import com.android.server.telecom.PhoneNumberUtilsAdapter;
-import com.android.server.telecom.TelecomSystem;
import com.android.server.telecom.ui.ToastFactory;
import org.junit.After;
@@ -98,6 +97,7 @@
Bundle parceledExtras = call.getExtras();
assertFalse(parceledExtras.containsKey(Connection.EXTRA_SIP_INVITE));
+ assertTrue(parceledExtras.containsKey(ImsCallProfile.EXTRA_IS_BUSINESS_CALL));
assertFalse(parceledExtras.containsKey("SomeExtra"));
assertTrue(parceledExtras.containsKey(Connection.EXTRA_CALL_SUBJECT));
}
@@ -115,6 +115,7 @@
Bundle parceledExtras = call.getExtras();
assertTrue(parceledExtras.containsKey(Connection.EXTRA_SIP_INVITE));
+ assertTrue(parceledExtras.containsKey(ImsCallProfile.EXTRA_IS_BUSINESS_CALL));
assertTrue(parceledExtras.containsKey("SomeExtra"));
assertTrue(parceledExtras.containsKey(Connection.EXTRA_CALL_SUBJECT));
}
@@ -128,6 +129,7 @@
Bundle parceledExtras = call.getExtras();
assertTrue(parceledExtras.containsKey(Connection.EXTRA_SIP_INVITE));
+ assertTrue(parceledExtras.containsKey(ImsCallProfile.EXTRA_IS_BUSINESS_CALL));
assertFalse(parceledExtras.containsKey("SomeExtra"));
assertFalse(parceledExtras.containsKey(Connection.EXTRA_CALL_SUBJECT));
}
@@ -141,6 +143,7 @@
Bundle parceledExtras = call.getExtras();
assertFalse(parceledExtras.containsKey(Connection.EXTRA_SIP_INVITE));
+ assertFalse(parceledExtras.containsKey(ImsCallProfile.EXTRA_IS_BUSINESS_CALL));
assertFalse(parceledExtras.containsKey("SomeExtra"));
assertFalse(parceledExtras.containsKey(Connection.EXTRA_CALL_SUBJECT));
}
@@ -192,6 +195,7 @@
extras.putString(Connection.EXTRA_SIP_INVITE, "scary data");
extras.putString("SomeExtra", "Extra Extra");
extras.putString(Connection.EXTRA_CALL_SUBJECT, "Blah");
+ extras.putBoolean(ImsCallProfile.EXTRA_IS_BUSINESS_CALL, true);
return extras;
}
}
diff --git a/tests/src/com/android/server/telecom/tests/SystemStateHelperTest.java b/tests/src/com/android/server/telecom/tests/SystemStateHelperTest.java
index ad52625..94b8463 100644
--- a/tests/src/com/android/server/telecom/tests/SystemStateHelperTest.java
+++ b/tests/src/com/android/server/telecom/tests/SystemStateHelperTest.java
@@ -48,6 +48,7 @@
import com.android.server.telecom.SystemStateHelper;
import com.android.server.telecom.SystemStateHelper.SystemStateListener;
+import com.android.server.telecom.TelecomSystem;
import org.junit.After;
import org.junit.Before;
@@ -78,6 +79,7 @@
@Mock SensorManager mSensorManager;
@Mock Intent mIntentEnter;
@Mock Intent mIntentExit;
+ TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { };
@Override
@Before
@@ -109,7 +111,7 @@
@SmallTest
@Test
public void testListeners() throws Exception {
- SystemStateHelper systemStateHelper = new SystemStateHelper(mContext);
+ SystemStateHelper systemStateHelper = new SystemStateHelper(mContext, mLock);
assertFalse(systemStateHelper.removeListener(mSystemStateListener));
systemStateHelper.addListener(mSystemStateListener);
@@ -121,14 +123,14 @@
@Test
public void testQuerySystemForCarMode_True() {
when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR);
- assertTrue(new SystemStateHelper(mContext).isCarModeOrProjectionActive());
+ assertTrue(new SystemStateHelper(mContext, mLock).isCarModeOrProjectionActive());
}
@SmallTest
@Test
public void testQuerySystemForCarMode_False() {
when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_NORMAL);
- assertFalse(new SystemStateHelper(mContext).isCarModeOrProjectionActive());
+ assertFalse(new SystemStateHelper(mContext, mLock).isCarModeOrProjectionActive());
}
@SmallTest
@@ -136,11 +138,11 @@
public void testQuerySystemForAutomotiveProjection_True() {
when(mUiModeManager.getActiveProjectionTypes())
.thenReturn(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE);
- assertTrue(new SystemStateHelper(mContext).isCarModeOrProjectionActive());
+ assertTrue(new SystemStateHelper(mContext, mLock).isCarModeOrProjectionActive());
when(mUiModeManager.getActiveProjectionTypes())
.thenReturn(UiModeManager.PROJECTION_TYPE_ALL);
- assertTrue(new SystemStateHelper(mContext).isCarModeOrProjectionActive());
+ assertTrue(new SystemStateHelper(mContext, mLock).isCarModeOrProjectionActive());
}
@SmallTest
@@ -148,7 +150,7 @@
public void testQuerySystemForAutomotiveProjection_False() {
when(mUiModeManager.getActiveProjectionTypes())
.thenReturn(UiModeManager.PROJECTION_TYPE_NONE);
- assertFalse(new SystemStateHelper(mContext).isCarModeOrProjectionActive());
+ assertFalse(new SystemStateHelper(mContext, mLock).isCarModeOrProjectionActive());
}
@SmallTest
@@ -157,7 +159,7 @@
when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR);
when(mUiModeManager.getActiveProjectionTypes())
.thenReturn(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE);
- assertTrue(new SystemStateHelper(mContext).isCarModeOrProjectionActive());
+ assertTrue(new SystemStateHelper(mContext, mLock).isCarModeOrProjectionActive());
}
@SmallTest
@@ -166,7 +168,7 @@
when(mContext.getSystemService(UiModeManager.class))
.thenReturn(mUiModeManager) // Without this, class construction will throw NPE.
.thenReturn(null);
- assertFalse(new SystemStateHelper(mContext).isCarModeOrProjectionActive());
+ assertFalse(new SystemStateHelper(mContext, mLock).isCarModeOrProjectionActive());
}
@SmallTest
@@ -174,7 +176,7 @@
public void testPackageRemoved() {
ArgumentCaptor<BroadcastReceiver> receiver =
ArgumentCaptor.forClass(BroadcastReceiver.class);
- new SystemStateHelper(mContext).addListener(mSystemStateListener);
+ new SystemStateHelper(mContext, mLock).addListener(mSystemStateListener);
verify(mContext, atLeastOnce())
.registerReceiver(receiver.capture(), any(IntentFilter.class));
Intent packageRemovedIntent = new Intent(Intent.ACTION_PACKAGE_REMOVED);
@@ -188,7 +190,7 @@
public void testReceiverAndIntentFilter() {
ArgumentCaptor<IntentFilter> intentFilterCaptor =
ArgumentCaptor.forClass(IntentFilter.class);
- new SystemStateHelper(mContext);
+ new SystemStateHelper(mContext, mLock);
verify(mContext, times(2)).registerReceiver(
any(BroadcastReceiver.class), intentFilterCaptor.capture());
@@ -225,7 +227,7 @@
public void testOnEnterExitCarMode() {
ArgumentCaptor<BroadcastReceiver> receiver =
ArgumentCaptor.forClass(BroadcastReceiver.class);
- new SystemStateHelper(mContext).addListener(mSystemStateListener);
+ new SystemStateHelper(mContext, mLock).addListener(mSystemStateListener);
verify(mContext, atLeastOnce())
.registerReceiver(receiver.capture(), any(IntentFilter.class));
@@ -244,7 +246,7 @@
@SmallTest
@Test
public void testOnSetReleaseAutomotiveProjection() {
- SystemStateHelper systemStateHelper = new SystemStateHelper(mContext);
+ SystemStateHelper systemStateHelper = new SystemStateHelper(mContext, mLock);
// We don't care what listener is registered, that's an implementation detail, but we need
// to call methods on whatever it is.
ArgumentCaptor<UiModeManager.OnProjectionStateChangeListener> listenerCaptor =