Merge "Adding SimPhonebookProvider"
diff --git a/res/values/config.xml b/res/values/config.xml
index 7dd26bb..0fd9b4b 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -307,6 +307,9 @@
<!-- Whether or not to support RCS VoLTE single registration -->
<bool name="config_rcsVolteSingleRegistrationEnabled">true</bool>
+ <!-- Whether or not to support RCS User Capability Exchange -->
+ <bool name="config_rcs_user_capability_exchange_enabled">true</bool>
+
<!-- Whether or not to support device to device communication using RTP and DTMF communication
transports. -->
<bool name="config_use_device_to_device_communication">false</bool>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 83a5673..1613ca8 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2172,4 +2172,12 @@
<string name="carrier_provisioning">Carrier Provisioning Info</string>
<!-- Trigger Carrier Provisioning [CHAR LIMIT=NONE] -->
<string name="trigger_carrier_provisioning">Trigger Carrier Provisioning</string>
+
+ <!-- details of the message popped up when there is
+ bad call quality caused by bluetooth connection-->
+ <string name="call_quality_notification_bluetooth_details">
+ Suggestion: Improve Bluetooth connectivity</string>
+ <!-- name of the notification that pops up during
+ a phone call when there is bad call quality -->
+ <string name="call_quality_notification_name">Call Quality Notification</string>
</resources>
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 5c97597..58382a1 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -943,6 +943,23 @@
}
/**
+ * @return whether the device supports RCS User Capability Exchange or not.
+ */
+ public boolean getDeviceUceEnabled() {
+ return (mTelephonyRcsService == null) ? false : mTelephonyRcsService.isDeviceUceEnabled();
+ }
+
+ /**
+ * Set the device supports RCS User Capability Exchange.
+ * @param isEnabled true if the device supports UCE.
+ */
+ public void setDeviceUceEnabled(boolean isEnabled) {
+ if (mTelephonyRcsService != null) {
+ mTelephonyRcsService.setDeviceUceEnabled(isEnabled);
+ }
+ }
+
+ /**
* Dump the state of the object, add calls to other objects as desired.
*
* @param fd File descriptor
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index e85b483..486021d 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -9864,6 +9864,28 @@
}
@Override
+ public boolean getDeviceUceEnabled() {
+ TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getDeviceUceEnabled");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mApp.getDeviceUceEnabled();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void setDeviceUceEnabled(boolean isEnabled) {
+ TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setDeviceUceEnabled");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mApp.setDeviceUceEnabled(isEnabled);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void setSignalStrengthUpdateRequest(int subId, SignalStrengthUpdateRequest request,
String callingPackage) {
TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index 1e87fbe..25f72aa 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -108,6 +108,8 @@
private static final String RCS_UCE_COMMAND = "uce";
private static final String UCE_GET_EAB_CONTACT = "get-eab-contact";
private static final String UCE_REMOVE_EAB_CONTACT = "remove-eab-contact";
+ private static final String UCE_GET_DEVICE_ENABLED = "get-device-enabled";
+ private static final String UCE_SET_DEVICE_ENABLED = "set-device-enabled";
// Take advantage of existing methods that already contain permissions checks when possible.
private final ITelephony mInterface;
@@ -307,6 +309,11 @@
pw.println(" -s: The SIM slot ID to read carrier config value for. If no option");
pw.println(" is specified, it will choose the default voice SIM slot.");
pw.println(" PHONE_NUMBER: The phone numbers to be removed from the EAB databases");
+ pw.println(" uce get-device-enabled");
+ pw.println(" Get the config to check whether the device supports RCS UCE or not.");
+ pw.println(" uce set-device-enabled true|false");
+ pw.println(" Set the device config for RCS User Capability Exchange to the value.");
+ pw.println(" The value could be true, false.");
}
private void onHelpNumberVerification() {
@@ -1621,6 +1628,10 @@
return handleRemovingEabContactCommand();
case UCE_GET_EAB_CONTACT:
return handleGettingEabContactCommand();
+ case UCE_GET_DEVICE_ENABLED:
+ return handleUceGetDeviceEnabledCommand();
+ case UCE_SET_DEVICE_ENABLED:
+ return handleUceSetDeviceEnabledCommand();
}
return -1;
}
@@ -1668,10 +1679,44 @@
if (VDBG) {
Log.v(LOG_TAG, "uce get-eab-contact, result: " + result);
}
+ return 0;
+ }
+
+ private int handleUceGetDeviceEnabledCommand() {
+ boolean result = false;
+ try {
+ result = mInterface.getDeviceUceEnabled();
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "uce get-device-enabled, error " + e.getMessage());
+ return -1;
+ }
+ if (VDBG) {
+ Log.v(LOG_TAG, "uce get-device-enabled, returned: " + result);
+ }
getOutPrintWriter().println(result);
return 0;
}
+ private int handleUceSetDeviceEnabledCommand() {
+ String enabledStr = getNextArg();
+ if (TextUtils.isEmpty(enabledStr)) {
+ return -1;
+ }
+
+ try {
+ boolean isEnabled = Boolean.parseBoolean(enabledStr);
+ mInterface.setDeviceUceEnabled(isEnabled);
+ if (VDBG) {
+ Log.v(LOG_TAG, "uce set-device-enabled " + enabledStr + ", done");
+ }
+ } catch (NumberFormatException | RemoteException e) {
+ Log.w(LOG_TAG, "uce set-device-enabled " + enabledStr + ", error " + e.getMessage());
+ getErrPrintWriter().println("Exception: " + e.getMessage());
+ return -1;
+ }
+ return 0;
+ }
+
private int handleSrcSetDeviceEnabledCommand() {
String enabledStr = getNextArg();
if (enabledStr == null) {
diff --git a/src/com/android/services/telephony/CallQualityManager.java b/src/com/android/services/telephony/CallQualityManager.java
new file mode 100644
index 0000000..01b5bae
--- /dev/null
+++ b/src/com/android/services/telephony/CallQualityManager.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.services.telephony;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Bundle;
+import android.telecom.BluetoothCallQualityReport;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.phone.R;
+
+/**
+ * class to handle call quality events that are received by telecom and telephony
+ */
+public class CallQualityManager {
+ private static final String TAG = CallQualityManager.class.getCanonicalName();
+ private static final String CALL_QUALITY_REPORT_CHANNEL = "call_quality_report_channel";
+
+ /** notification ids */
+ public static final int BLUETOOTH_CHOPPY_VOICE_NOTIFICATION_ID = 700;
+
+ public static final String CALL_QUALITY_CHANNEL_ID = "CallQualityNotification";
+
+ private final Context mContext;
+ private final NotificationChannel mNotificationChannel;
+ private final NotificationManager mNotificationManager;
+
+ public CallQualityManager(Context context) {
+ mContext = context;
+ mNotificationChannel = new NotificationChannel(CALL_QUALITY_CHANNEL_ID,
+ mContext.getString(R.string.call_quality_notification_name),
+ NotificationManager.IMPORTANCE_HIGH);
+ mNotificationManager = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ mNotificationManager.createNotificationChannel(mNotificationChannel);
+ }
+
+ /**
+ * method that is called whenever a
+ * {@code BluetoothCallQualityReport.EVENT_SEND_BLUETOOTH_CALL_QUALITY_REPORT} is received
+ * @param extras Bundle that includes serialized {@code BluetoothCallQualityReport} parcelable
+ */
+ @VisibleForTesting
+ public void onBluetoothCallQualityReported(Bundle extras) {
+ if (extras == null) {
+ Log.d(TAG, "onBluetoothCallQualityReported: no extras provided");
+ }
+
+ BluetoothCallQualityReport callQualityReport = extras.getParcelable(
+ BluetoothCallQualityReport.EXTRA_BLUETOOTH_CALL_QUALITY_REPORT);
+
+ if (callQualityReport.isChoppyVoice()) {
+ onChoppyVoice();
+ }
+ // TODO: once other signals are also sent, we will add more actions here
+ }
+
+ /**
+ * method to post a notification to user suggesting ways to improve call quality in case of
+ * bluetooth choppy voice
+ */
+ @VisibleForTesting
+ public void onChoppyVoice() {
+ String title = "Call Quality Improvement";
+ //TODO: update call_quality_bluetooth_enhancement_suggestion with below before submitting:
+// "Voice is not being transmitted properly via your bluetooth device."
+// + "To improve, try:\n"
+// + "1. moving your phone closer to your bluetooth device\n"
+// + "2. using a different bluetooth device, or your phone's speaker\n";
+ popUpNotification(title,
+ mContext.getText(R.string.call_quality_notification_bluetooth_details));
+ }
+
+ private void popUpNotification(String title, CharSequence details) {
+ int iconId = android.R.drawable.stat_notify_error;
+
+ Notification notification = new Notification.Builder(mContext)
+ .setSmallIcon(iconId)
+ .setWhen(System.currentTimeMillis())
+ .setAutoCancel(true)
+ .setContentTitle(title)
+ .setContentText(details)
+ .setStyle(new Notification.BigTextStyle().bigText(details))
+ .setOngoing(true)
+ .setChannelId(CALL_QUALITY_CHANNEL_ID)
+ .setOnlyAlertOnce(true)
+ .build();
+
+ mNotificationManager.notify(TAG, BLUETOOTH_CHOPPY_VOICE_NOTIFICATION_ID, notification);
+ }
+}
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 45c00e4..59466b0 100755
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -28,6 +28,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
+import android.telecom.BluetoothCallQualityReport;
import android.telecom.CallAudioState;
import android.telecom.Conference;
import android.telecom.Connection;
@@ -854,6 +855,8 @@
private final Set<TelephonyConnectionListener> mTelephonyListeners = Collections.newSetFromMap(
new ConcurrentHashMap<TelephonyConnectionListener, Boolean>(8, 0.9f, 1));
+ private CallQualityManager mCallQualityManager;
+
protected TelephonyConnection(com.android.internal.telephony.Connection originalConnection,
String callId, @android.telecom.Call.Details.CallDirection int callDirection) {
setCallDirection(callDirection);
@@ -863,6 +866,20 @@
}
}
+ @Override
+ public void onCallEvent(String event, Bundle extras) {
+ switch (event) {
+ case BluetoothCallQualityReport.EVENT_BLUETOOTH_CALL_QUALITY_REPORT:
+ if (mCallQualityManager == null) {
+ mCallQualityManager = new CallQualityManager(getPhone().getContext());
+ }
+ mCallQualityManager.onBluetoothCallQualityReported(extras);
+ break;
+ default:
+ break;
+ }
+
+ }
/**
* Creates a clone of the current {@link TelephonyConnection}.
*
diff --git a/src/com/android/services/telephony/rcs/TelephonyRcsService.java b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
index a6789f5..66492ae 100644
--- a/src/com/android/services/telephony/rcs/TelephonyRcsService.java
+++ b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
@@ -33,6 +33,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.PhoneConfigurationManager;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.phone.R;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -87,6 +88,22 @@
}
};
+ /**
+ * Used to inject device resource for testing.
+ */
+ @VisibleForTesting
+ public interface ResourceProxy {
+ /**
+ * @return an whether the device supports User Capability Exchange.
+ */
+ boolean getDeviceUceEnabled(Context context);
+ }
+
+ private static ResourceProxy sResourceProxy = context -> {
+ return context.getResources().getBoolean(
+ R.bool.config_rcs_user_capability_exchange_enabled);
+ };
+
// Notifies this service that there has been a change in available slots.
private static final int HANDLER_MSIM_CONFIGURATION_CHANGE = 1;
@@ -97,6 +114,9 @@
// Maps slot ID -> RcsFeatureController.
private SparseArray<RcsFeatureController> mFeatureControllers;
+ // Whether the device supports User Capability Exchange
+ private boolean mRcsUceEnabled;
+
private BroadcastReceiver mCarrierConfigChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -139,6 +159,16 @@
mContext = context;
mNumSlots = numSlots;
mFeatureControllers = new SparseArray<>(numSlots);
+ mRcsUceEnabled = sResourceProxy.getDeviceUceEnabled(mContext);
+ }
+
+ @VisibleForTesting
+ public TelephonyRcsService(Context context, int numSlots, ResourceProxy resourceProxy) {
+ mContext = context;
+ mNumSlots = numSlots;
+ mFeatureControllers = new SparseArray<>(numSlots);
+ sResourceProxy = resourceProxy;
+ mRcsUceEnabled = sResourceProxy.getDeviceUceEnabled(mContext);
}
/**
@@ -234,7 +264,7 @@
}
private void updateSupportedFeatures(RcsFeatureController c, int slotId, int subId) {
- if (doesSubscriptionSupportPresence(subId)) {
+ if (isDeviceUceEnabled() && doesSubscriptionSupportPresence(subId)) {
if (c.getFeature(UceControllerManager.class) == null) {
c.addFeature(mFeatureFactory.createUceControllerManager(mContext, slotId, subId),
UceControllerManager.class);
@@ -259,6 +289,20 @@
if (c.hasActiveFeatures()) c.connect();
}
+ /**
+ * Get whether the device supports RCS User Capability Exchange or not.
+ */
+ public boolean isDeviceUceEnabled() {
+ return mRcsUceEnabled;
+ }
+
+ /**
+ * Set the device supports RCS User Capability Exchange.
+ */
+ public void setDeviceUceEnabled(boolean isEnabled) {
+ mRcsUceEnabled = isEnabled;
+ }
+
private boolean doesSubscriptionSupportPresence(int subId) {
if (!SubscriptionManager.isValidSubscriptionId(subId)) return false;
CarrierConfigManager carrierConfigManager =
diff --git a/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java b/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
index a1f97a0..c367af3 100644
--- a/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
+++ b/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
@@ -50,6 +50,7 @@
@Captor ArgumentCaptor<BroadcastReceiver> mReceiverCaptor;
@Mock TelephonyRcsService.FeatureFactory mFeatureFactory;
+ @Mock TelephonyRcsService.ResourceProxy mResourceProxy;
@Mock UceControllerManager mMockUceSlot0;
@Mock UceControllerManager mMockUceSlot1;
@Mock SipTransportController mMockSipTransportSlot0;
@@ -78,6 +79,7 @@
eq(0), anyInt());
doReturn(mMockSipTransportSlot1).when(mFeatureFactory).createSipTransportController(any(),
eq(1), anyInt());
+ doReturn(true).when(mResourceProxy).getDeviceUceEnabled(any());
//set up default slot-> sub ID mappings.
setSlotToSubIdMapping(0 /*slotId*/, 1/*subId*/);
setSlotToSubIdMapping(1 /*slotId*/, 2/*subId*/);
@@ -325,7 +327,7 @@
}
private TelephonyRcsService createRcsService(int numSlots) {
- TelephonyRcsService service = new TelephonyRcsService(mContext, numSlots);
+ TelephonyRcsService service = new TelephonyRcsService(mContext, numSlots, mResourceProxy);
service.setFeatureFactory(mFeatureFactory);
service.initialize();
verify(mContext).registerReceiver(mReceiverCaptor.capture(), any());