Merge "Modify PhoneCapabilities for device capabilities"
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index 9d9010a..e09c6af 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -22,6 +22,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
+import android.telephony.SubscriptionManager;
import android.telephony.ims.ImsException;
import android.telephony.ims.RegistrationManager;
import android.telephony.ims.aidl.IImsCapabilityCallback;
@@ -249,23 +250,35 @@
public void requestCapabilities(int subId, List<Uri> contactNumbers,
IRcsUceControllerCallback c) {
enforceReadPrivilegedPermission("requestCapabilities");
+ if (mRcsService == null) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "IMS is not available on device.");
+ }
+ mRcsService.requestCapabilities(getImsPhone(subId).getPhoneId(), contactNumbers, c);
}
@Override
public int getUcePublishState(int subId) {
enforceReadPrivilegedPermission("getUcePublishState");
- return -1;
+ if (mRcsService == null) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "IMS is not available on device.");
+ }
+ return mRcsService.getUcePublishState(getImsPhone(subId).getPhoneId());
}
@Override
public boolean isUceSettingEnabled(int subId) {
enforceReadPrivilegedPermission("isUceSettingEnabled");
- return false;
+ return SubscriptionManager.getBooleanSubscriptionProperty(subId,
+ SubscriptionManager.IMS_RCS_UCE_ENABLED, false /*defaultValue*/, mApp);
}
@Override
public void setUceSettingEnabled(int subId, boolean isEnabled) {
enforceModifyPermission();
+ SubscriptionManager.setSubscriptionProperty(subId, SubscriptionManager.IMS_RCS_UCE_ENABLED,
+ (isEnabled ? "1" : "0"));
}
/**
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 1987756..9b75007 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -48,6 +48,7 @@
import android.os.Messenger;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -2451,6 +2452,17 @@
mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
}
+ /**
+ * Make sure the caller is system.
+ *
+ * @throws SecurityException if the caller is not system.
+ */
+ private void enforceSystemCaller() {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Caller must be system");
+ }
+ }
+
private void enforceActiveEmergencySessionPermission() {
mApp.enforceCallingOrSelfPermission(
android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null);
@@ -5290,6 +5302,34 @@
}
/**
+ * Enable or disable always reporting signal strength changes from radio.
+ *
+ * @param isEnable {@code true} for enabling; {@code false} for disabling.
+ */
+ @Override
+ public void setAlwaysReportSignalStrength(int subId, boolean isEnable) {
+ enforceModifyPermission();
+ enforceSystemCaller();
+
+ final long identity = Binder.clearCallingIdentity();
+ final Phone phone = getPhone(subId);
+ try {
+ if (phone != null) {
+ if (DBG) {
+ log("setAlwaysReportSignalStrength: subId=" + subId
+ + " isEnable=" + isEnable);
+ }
+ phone.setAlwaysReportSignalStrength(isEnable);
+ } else {
+ loge("setAlwaysReportSignalStrength: no phone found for subId="
+ + subId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
* Get the user enabled state of Mobile Data.
*
* TODO: remove and use isUserDataEnabled.
@@ -7834,6 +7874,15 @@
}
/**
+ * Get the current calling package name.
+ * @return the current calling package name
+ */
+ @Override
+ public String getCurrentPackageName() {
+ return mApp.getPackageManager().getPackagesForUid(Binder.getCallingUid())[0];
+ }
+
+ /**
* Return whether data is enabled for certain APN type. This will tell if framework will accept
* corresponding network requests on a subId.
*
@@ -7894,6 +7943,9 @@
@Override
public void enqueueSmsPickResult(String callingPackage, IIntegerConsumer pendingSubIdResult) {
+ if (callingPackage == null) {
+ callingPackage = getCurrentPackageName();
+ }
SmsPermissions permissions = new SmsPermissions(getDefaultPhone(), mApp,
(AppOpsManager) mApp.getSystemService(Context.APP_OPS_SERVICE));
if (!permissions.checkCallingCanSendSms(callingPackage, "Sending message")) {
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index dd000ce..f5f5c66 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -540,7 +540,7 @@
if (mConferenceHost == null) {
return;
}
- mConferenceHost.performReject();
+ mConferenceHost.performReject(android.telecom.Call.REJECT_REASON_DECLINED);
}
/**
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 919d3b2..1013927 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -41,7 +41,6 @@
import android.telecom.TelecomManager;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneStateListener;
-import com.android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -62,6 +61,7 @@
import com.android.phone.PhoneGlobals;
import com.android.phone.PhoneUtils;
import com.android.phone.R;
+import com.android.telephony.Rlog;
import java.util.Arrays;
import java.util.LinkedList;
@@ -1316,30 +1316,6 @@
// Clean up any PhoneAccounts that are no longer relevant
cleanupPhoneAccounts();
-
- // At some point, the phone account ID was switched from the subId to the iccId.
- // If there is a default account, check if this is the case, and upgrade the default account
- // from using the subId to iccId if so.
- PhoneAccountHandle defaultPhoneAccount =
- mTelecomManager.getUserSelectedOutgoingPhoneAccount();
- ComponentName telephonyComponentName =
- new ComponentName(mContext, TelephonyConnectionService.class);
-
- if (defaultPhoneAccount != null &&
- telephonyComponentName.equals(defaultPhoneAccount.getComponentName()) &&
- !hasAccountEntryForPhoneAccount(defaultPhoneAccount)) {
-
- String phoneAccountId = defaultPhoneAccount.getId();
- if (!TextUtils.isEmpty(phoneAccountId) && TextUtils.isDigitsOnly(phoneAccountId)) {
- PhoneAccountHandle upgradedPhoneAccount =
- PhoneUtils.makePstnPhoneAccountHandle(
- PhoneGlobals.getPhone(Integer.parseInt(phoneAccountId)));
-
- if (hasAccountEntryForPhoneAccount(upgradedPhoneAccount)) {
- mTelecomManager.setUserSelectedOutgoingPhoneAccount(upgradedPhoneAccount);
- }
- }
- }
}
private void tearDownAccounts() {
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index f2b2244..2f5e2a6 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -113,6 +113,7 @@
private static final int MSG_SET_CALL_RADIO_TECH = 18;
private static final int MSG_ON_CONNECTION_EVENT = 19;
private static final int MSG_REDIAL_CONNECTION_CHANGED = 20;
+ private static final int MSG_REJECT = 21;
private List<Uri> mParticipants;
private boolean mIsAdhocConferenceCall;
@@ -273,6 +274,10 @@
int cause = (int) msg.obj;
hangup(cause);
break;
+ case MSG_REJECT:
+ int rejectReason = (int) msg.obj;
+ reject(rejectReason);
+ break;
case MSG_SET_CALL_RADIO_TECH:
int vrat = (int) msg.obj;
@@ -926,13 +931,18 @@
@Override
public void onReject() {
- performReject();
+ performReject(android.telecom.Call.REJECT_REASON_DECLINED);
}
- public void performReject() {
+ @Override
+ public void onReject(@android.telecom.Call.RejectReason int rejectReason) {
+ performReject(rejectReason);
+ }
+
+ public void performReject(int rejectReason) {
Log.v(this, "performReject");
if (isValidRingingCall()) {
- mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.INCOMING_REJECTED)
+ mHandler.obtainMessage(MSG_REJECT, rejectReason)
.sendToTarget();
}
super.onReject();
@@ -1678,6 +1688,46 @@
}
}
+ protected void reject(@android.telecom.Call.RejectReason int rejectReason) {
+ if (mOriginalConnection != null) {
+ mHangupDisconnectCause = android.telephony.DisconnectCause.INCOMING_REJECTED;
+ try {
+ // Hanging up a ringing call requires that we invoke call.hangup() as opposed to
+ // connection.hangup(). Without this change, the party originating the call
+ // will not get sent to voicemail if the user opts to reject the call.
+ if (isValidRingingCall()) {
+ Call call = getCall();
+ if (call != null) {
+ call.hangup(rejectReason);
+ } else {
+ Log.w(this, "Attempting to hangup a connection without backing call.");
+ }
+ } else {
+ // We still prefer to call connection.hangup() for non-ringing calls
+ // in order to support hanging-up specific calls within a conference call.
+ // If we invoked call.hangup() while in a conference, we would end up
+ // hanging up the entire conference call instead of the specific connection.
+ mOriginalConnection.hangup();
+ }
+ } catch (CallStateException e) {
+ Log.e(this, e, "Call to Connection.hangup failed with exception");
+ }
+ } else {
+ if (getState() == STATE_DISCONNECTED) {
+ Log.i(this, "hangup called on an already disconnected call!");
+ close();
+ } else {
+ // There are a few cases where mOriginalConnection has not been set yet. For
+ // example, when the radio has to be turned on to make an emergency call,
+ // mOriginalConnection could not be set for many seconds.
+ setTelephonyConnectionDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
+ android.telephony.DisconnectCause.LOCAL,
+ "Local Disconnect before connection established."));
+ close();
+ }
+ }
+ }
+
com.android.internal.telephony.Connection getOriginalConnection() {
return mOriginalConnection;
}
diff --git a/src/com/android/services/telephony/rcs/TelephonyRcsService.java b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
index 74765d6..0e8f8de 100644
--- a/src/com/android/services/telephony/rcs/TelephonyRcsService.java
+++ b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
@@ -17,11 +17,25 @@
package com.android.services.telephony.rcs;
import android.content.Context;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.aidl.IRcsUceControllerCallback;
import android.util.Log;
+import com.android.ims.ResultCode;
+import com.android.service.ims.presence.ContactCapabilityResponse;
+import com.android.service.ims.presence.PresenceBase;
import com.android.service.ims.presence.PresencePublication;
import com.android.service.ims.presence.PresenceSubscriber;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
/**
* Telephony RCS Service integrates PresencePublication and PresenceSubscriber into the service.
*/
@@ -34,10 +48,104 @@
// A helper class to manage the RCS Presences instances.
private final PresenceHelper mPresenceHelper;
+ private ConcurrentHashMap<Integer, IRcsUceControllerCallback> mPendingRequests;
+
public TelephonyRcsService(Context context) {
Log.i(LOG_TAG, "initialize");
mContext = context;
mPresenceHelper = new PresenceHelper(mContext);
+ mPendingRequests = new ConcurrentHashMap<>();
+ }
+
+ /**
+ * @return the UCE Publish state for the phone ID specified.
+ */
+ public int getUcePublishState(int phoneId) {
+ PresencePublication publisher = getPresencePublication(phoneId);
+ if (publisher == null) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
+ "UCE service is not currently running.");
+ }
+ int publishState = publisher.getPublishState();
+ return toUcePublishState(publishState);
+ }
+
+ /**
+ * Perform a capabilities request and call {@link IRcsUceControllerCallback} with the result.
+ */
+ public void requestCapabilities(int phoneId, List<Uri> contactNumbers,
+ IRcsUceControllerCallback c) {
+ PresenceSubscriber subscriber = getPresenceSubscriber(phoneId);
+ if (subscriber == null) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
+ "UCE service is not currently running.");
+ }
+ List<String> numbers = contactNumbers.stream().map(TelephonyRcsService::getNumberFromUri)
+ .collect(Collectors.toList());
+ int taskId = subscriber.requestCapability(numbers, new ContactCapabilityResponse() {
+ @Override
+ public void onSuccess(int reqId) {
+ Log.i(LOG_TAG, "onSuccess called for reqId:" + reqId);
+ }
+
+ @Override
+ public void onError(int reqId, int resultCode) {
+ IRcsUceControllerCallback c = mPendingRequests.remove(reqId);
+ try {
+ if (c != null) {
+ c.onError(toUceError(resultCode));
+ } else {
+ Log.w(LOG_TAG, "onError called for unknown reqId:" + reqId);
+ }
+ } catch (RemoteException e) {
+ Log.i(LOG_TAG, "Calling back to dead service");
+ }
+ }
+
+ @Override
+ public void onFinish(int reqId) {
+ Log.i(LOG_TAG, "onFinish called for reqId:" + reqId);
+ }
+
+ @Override
+ public void onTimeout(int reqId) {
+ IRcsUceControllerCallback c = mPendingRequests.remove(reqId);
+ try {
+ if (c != null) {
+ c.onError(RcsUceAdapter.ERROR_REQUEST_TIMEOUT);
+ } else {
+ Log.w(LOG_TAG, "onTimeout called for unknown reqId:" + reqId);
+ }
+ } catch (RemoteException e) {
+ Log.i(LOG_TAG, "Calling back to dead service");
+ }
+ }
+
+ @Override
+ public void onCapabilitiesUpdated(int reqId,
+ List<RcsContactUceCapability> contactCapabilities,
+ boolean updateLastTimestamp) {
+ IRcsUceControllerCallback c = mPendingRequests.remove(reqId);
+ try {
+ if (c != null) {
+ c.onCapabilitiesReceived(contactCapabilities);
+ } else {
+ Log.w(LOG_TAG, "onCapabilitiesUpdated, unknown reqId:" + reqId);
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "onCapabilitiesUpdated on dead service");
+ }
+ }
+ });
+ if (taskId < 0) {
+ try {
+ c.onError(toUceError(taskId));
+ return;
+ } catch (RemoteException e) {
+ Log.i(LOG_TAG, "Calling back to dead service");
+ }
+ }
+ mPendingRequests.put(taskId, c);
}
private PresencePublication getPresencePublication(int phoneId) {
@@ -47,4 +155,56 @@
private PresenceSubscriber getPresenceSubscriber(int phoneId) {
return mPresenceHelper.getPresenceSubscriber(phoneId);
}
+
+ private static String getNumberFromUri(Uri uri) {
+ String number = uri.getSchemeSpecificPart();
+ String[] numberParts = number.split("[@;:]");
+
+ if (numberParts.length == 0) {
+ return null;
+ }
+ return numberParts[0];
+ }
+
+ private static int toUcePublishState(int publishState) {
+ switch (publishState) {
+ case PresenceBase.PUBLISH_STATE_200_OK:
+ return RcsUceAdapter.PUBLISH_STATE_200_OK;
+ case PresenceBase.PUBLISH_STATE_NOT_PUBLISHED:
+ return RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED;
+ case PresenceBase.PUBLISH_STATE_VOLTE_PROVISION_ERROR:
+ return RcsUceAdapter.PUBLISH_STATE_VOLTE_PROVISION_ERROR;
+ case PresenceBase.PUBLISH_STATE_RCS_PROVISION_ERROR:
+ return RcsUceAdapter.PUBLISH_STATE_RCS_PROVISION_ERROR;
+ case PresenceBase.PUBLISH_STATE_REQUEST_TIMEOUT:
+ return RcsUceAdapter.PUBLISH_STATE_REQUEST_TIMEOUT;
+ case PresenceBase.PUBLISH_STATE_OTHER_ERROR:
+ return RcsUceAdapter.PUBLISH_STATE_OTHER_ERROR;
+ default:
+ return RcsUceAdapter.PUBLISH_STATE_OTHER_ERROR;
+ }
+ }
+
+ private static int toUceError(int resultCode) {
+ switch(resultCode) {
+ case ResultCode.SUBSCRIBE_NOT_REGISTERED:
+ return RcsUceAdapter.ERROR_NOT_REGISTERED;
+ case ResultCode.SUBSCRIBE_REQUEST_TIMEOUT:
+ return RcsUceAdapter.ERROR_REQUEST_TIMEOUT;
+ case ResultCode.SUBSCRIBE_FORBIDDEN:
+ return RcsUceAdapter.ERROR_FORBIDDEN;
+ case ResultCode.SUBSCRIBE_NOT_FOUND:
+ return RcsUceAdapter.ERROR_NOT_FOUND;
+ case ResultCode.SUBSCRIBE_TOO_LARGE:
+ return RcsUceAdapter.ERROR_REQUEST_TOO_LARGE;
+ case ResultCode.SUBSCRIBE_INSUFFICIENT_MEMORY:
+ return RcsUceAdapter.ERROR_INSUFFICIENT_MEMORY;
+ case ResultCode.SUBSCRIBE_LOST_NETWORK:
+ return RcsUceAdapter.ERROR_LOST_NETWORK;
+ case ResultCode.SUBSCRIBE_ALREADY_IN_QUEUE:
+ return RcsUceAdapter.ERROR_ALREADY_IN_QUEUE;
+ default:
+ return RcsUceAdapter.ERROR_GENERIC_FAILURE;
+ }
+ }
}