Merge "[Settings] Implement APIs in PhoneInterfaceManager"
diff --git a/Android.bp b/Android.bp
index a06d5ef..72712b8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -38,7 +38,7 @@
],
srcs: [
- ":framework-telephony-stack-shared-srcs",
+ ":framework-telephony-common-shared-srcs",
"src/**/*.java",
"sip/src/**/*.java",
"ecc/proto/**/*.proto",
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c737ce8..e800f7d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -507,7 +507,7 @@
<receiver android:name="com.android.services.telephony.sip.SipIncomingCallReceiver">
<intent-filter>
- <action android:name="com.android.phone.SIP_INCOMING_CALL" />
+ <action android:name="android.net.sip.action.SIP_INCOMING_CALL" />
</intent-filter>
</receiver>
@@ -538,6 +538,12 @@
android:uiOptions="splitActionBarWhenNarrow">
</activity>
+ <service android:name="com.android.services.telephony.sip.components.TelephonySipService">
+ <intent-filter>
+ <action android:name="android.net.sip.action.START_SIP" />
+ </intent-filter>
+ </service>
+
<!-- End SIP -->
<activity android:name="MMIDialogActivity"
diff --git a/res/values/config.xml b/res/values/config.xml
index 8c36b1a..6906089 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -257,6 +257,14 @@
audio stream which the remote party will be able to hear. -->
<bool name="config_support_telephony_audio_device">false</bool>
+ <string-array translatable="false" name="config_volte_provision_error_on_publish_response">
+ <item>403 not authorized for presence</item>
+ </string-array>
+
+ <string-array translatable="false" name="config_rcs_provision_error_on_publish_response">
+ <item>404 not found</item>
+ </string-array>
+
<!-- The country list that shortcut view can be enabled. -->
<string-array name="config_countries_to_enable_shortcut_view" translatable="false">
</string-array>
diff --git a/sip/src/com/android/services/telephony/sip/SipSettings.java b/sip/src/com/android/services/telephony/sip/SipSettings.java
index ded16df..700fe81 100644
--- a/sip/src/com/android/services/telephony/sip/SipSettings.java
+++ b/sip/src/com/android/services/telephony/sip/SipSettings.java
@@ -42,6 +42,7 @@
import com.android.phone.R;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
@@ -238,7 +239,7 @@
}
private void processActiveProfilesFromSipService() {
- SipProfile[] activeList = {};
+ List<SipProfile> activeList = new ArrayList<>();
try {
activeList = mSipManager.getListOfProfiles();
} catch (SipException e) {
diff --git a/sip/src/com/android/services/telephony/sip/SipUtil.java b/sip/src/com/android/services/telephony/sip/SipUtil.java
index ff38754..828174e 100644
--- a/sip/src/com/android/services/telephony/sip/SipUtil.java
+++ b/sip/src/com/android/services/telephony/sip/SipUtil.java
@@ -29,12 +29,11 @@
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import com.android.phone.PhoneGlobals;
-import com.android.phone.R;
-import com.android.server.sip.SipService;
import java.io.IOException;
import java.util.ArrayList;
@@ -46,6 +45,7 @@
"com.android.services.telephony.sip.incoming_call_intent";
static final String EXTRA_PHONE_ACCOUNT =
"com.android.services.telephony.sip.phone_account";
+ static final String PHONE_PACKAGE = "com.android.phone";
private SipUtil() {
}
@@ -53,9 +53,9 @@
public static boolean isVoipSupported(Context context) {
return SipManager.isVoipSupported(context) &&
context.getResources().getBoolean(
- com.android.internal.R.bool.config_built_in_sip_phone) &&
- context.getResources().getBoolean(
- com.android.internal.R.bool.config_voice_capable);
+ com.android.internal.R.bool.config_built_in_sip_phone)
+ && ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE))
+ .isVoiceCapable();
}
static PendingIntent createIncomingCallPendingIntent(
@@ -190,7 +190,10 @@
// Migrate SIP database from DE->CE storage if the device has just upgraded.
possiblyMigrateSipDb(phoneGlobalsContext);
// Wait until boot complete to start SIP so that it has access to CE storage.
- SipService.start(phoneGlobalsContext);
+ Intent startSipIntent = new Intent();
+ startSipIntent.setAction(SipManager.ACTION_START_SIP);
+ startSipIntent.setPackage(PHONE_PACKAGE);
+ phoneGlobalsContext.startService(startSipIntent);
}
/**
diff --git a/sip/src/com/android/services/telephony/sip/components/TelephonySipService.java b/sip/src/com/android/services/telephony/sip/components/TelephonySipService.java
new file mode 100644
index 0000000..5b863b1
--- /dev/null
+++ b/sip/src/com/android/services/telephony/sip/components/TelephonySipService.java
@@ -0,0 +1,43 @@
+/*
+ * 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.sip.components;
+
+import android.app.Service;
+import android.content.Intent;
+import android.net.sip.SipManager;
+import android.os.IBinder;
+
+import com.android.server.sip.SipService;
+
+/**
+ * This class represents telephony SIP service which handle start SIP service requests from
+ * Telephony.
+ */
+public class TelephonySipService extends Service {
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if ((intent != null)
+ && SipManager.ACTION_START_SIP.equals(intent.getAction())) {
+ SipService.start(this);
+ }
+ return START_STICKY;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+}
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index 698ad6e..ffac202 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -36,6 +36,7 @@
import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.services.telephony.rcs.TelephonyRcsService;
import java.util.List;
@@ -49,6 +50,7 @@
private static ImsRcsController sInstance;
private PhoneGlobals mApp;
+ private TelephonyRcsService mRcsService;
/**
* Initialize the singleton ImsRcsController instance.
@@ -339,4 +341,8 @@
}
return rcsFeatureManager;
}
+
+ void setRcsService(TelephonyRcsService rcsService) {
+ mRcsService = rcsService;
+ }
}
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 7f1e60d..a369423 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -71,6 +71,7 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.phone.settings.SettingsConstants;
import com.android.phone.vvm.CarrierVvmPackageInstalledReceiver;
+import com.android.services.telephony.rcs.TelephonyRcsService;
import com.android.services.telephony.sip.SipAccountRegistry;
import com.android.services.telephony.sip.SipUtil;
@@ -149,6 +150,7 @@
CallerInfoCache callerInfoCache;
NotificationMgr notificationMgr;
ImsResolver mImsResolver;
+ TelephonyRcsService mTelephonyRcsService;
public PhoneInterfaceManager phoneMgr;
public ImsRcsController imsRcsController;
CarrierConfigLoader configLoader;
@@ -303,8 +305,8 @@
// Cache the "voice capable" flag.
// This flag currently comes from a resource (which is
// overrideable on a per-product basis):
- sVoiceCapable =
- getResources().getBoolean(com.android.internal.R.bool.config_voice_capable);
+ sVoiceCapable = ((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE))
+ .isVoiceCapable();
// ...but this might eventually become a PackageManager "system
// feature" instead, in which case we'd do something like:
// sVoiceCapable =
@@ -372,6 +374,11 @@
imsRcsController = ImsRcsController.init(this);
+ if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS)) {
+ mTelephonyRcsService = new TelephonyRcsService(this);
+ imsRcsController.setRcsService(mTelephonyRcsService);
+ }
+
configLoader = CarrierConfigLoader.init(this);
// Create the CallNotifier singleton, which handles
@@ -403,7 +410,7 @@
IntentFilter sipIntentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
sipIntentFilter.addAction(SipManager.ACTION_SIP_SERVICE_UP);
sipIntentFilter.addAction(SipManager.ACTION_SIP_CALL_OPTION_CHANGED);
- sipIntentFilter.addAction(SipManager.ACTION_SIP_REMOVE_PHONE);
+ sipIntentFilter.addAction(SipManager.ACTION_SIP_REMOVE_PROFILE);
registerReceiver(mSipReceiver, sipIntentFilter);
mCarrierVvmPackageInstalledReceiver.register(this);
@@ -709,7 +716,7 @@
} else if (action.equals(SipManager.ACTION_SIP_SERVICE_UP)
|| action.equals(SipManager.ACTION_SIP_CALL_OPTION_CHANGED)) {
sipAccountRegistry.setup(context);
- } else if (action.equals(SipManager.ACTION_SIP_REMOVE_PHONE)) {
+ } else if (action.equals(SipManager.ACTION_SIP_REMOVE_PROFILE)) {
if (DBG) {
Log.d(LOG_TAG, "SIP_REMOVE_PHONE "
+ intent.getStringExtra(SipManager.EXTRA_LOCAL_URI));
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index d1d091a..1ccd4d3 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -3475,11 +3475,10 @@
enforceReadPrivilegedPermission("registerImsProvisioningChangedCallback");
final long identity = Binder.clearCallingIdentity();
try {
- if (isImsAvailableOnDevice()) {
- throw new ImsException("IMS not available on device.",
- ImsException.CODE_ERROR_UNSUPPORTED_OPERATION);
+ if (!isImsAvailableOnDevice()) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "IMS not available on device.");
}
-
// TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
.addProvisioningCallbackForSubscription(callback, subId);
@@ -4078,9 +4077,9 @@
@Override
public int getLteOnCdmaModeForSubscriber(int subId, String callingPackage,
String callingFeatureId) {
- if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mApp, subId, callingPackage, callingFeatureId,
- "getLteOnCdmaModeForSubscriber")) {
+ try {
+ enforceReadPrivilegedPermission("getLteOnCdmaModeForSubscriber");
+ } catch (SecurityException e) {
return PhoneConstants.LTE_ON_CDMA_UNKNOWN;
}
@@ -7714,6 +7713,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.
*
@@ -7774,6 +7782,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/phone/ServiceStateProvider.java b/src/com/android/phone/ServiceStateProvider.java
index 9e6ea83..00269e0 100644
--- a/src/com/android/phone/ServiceStateProvider.java
+++ b/src/com/android/phone/ServiceStateProvider.java
@@ -61,6 +61,8 @@
import com.android.internal.annotations.VisibleForTesting;
import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
/**
* The class to provide base facility to access ServiceState related content,
@@ -128,7 +130,7 @@
@Override
public Uri insert(Uri uri, ContentValues values) {
- if (uri.isPathPrefixMatch(CONTENT_URI)) {
+ if (isPathPrefixMatch(uri, CONTENT_URI)) {
// Parse the subId
int subId = 0;
try {
@@ -183,7 +185,7 @@
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
- if (!uri.isPathPrefixMatch(CONTENT_URI)) {
+ if (!isPathPrefixMatch(uri, CONTENT_URI)) {
throw new IllegalArgumentException("Invalid URI: " + uri);
} else {
// Parse the subId
@@ -362,4 +364,29 @@
context.getContentResolver().notifyChange(getUriForSubscriptionId(subId), null, false);
}
}
+
+ /**
+ * Test if this is a path prefix match against the given Uri. Verifies that
+ * scheme, authority, and atomic path segments match.
+ *
+ * Copied from frameworks/base/core/java/android/net/Uri.java
+ */
+ private boolean isPathPrefixMatch(Uri uriA, Uri uriB) {
+ if (!Objects.equals(uriA.getScheme(), uriB.getScheme())) return false;
+ if (!Objects.equals(uriA.getAuthority(), uriB.getAuthority())) return false;
+
+ List<String> segA = uriA.getPathSegments();
+ List<String> segB = uriB.getPathSegments();
+
+ final int size = segB.size();
+ if (segA.size() < size) return false;
+
+ for (int i = 0; i < size; i++) {
+ if (!Objects.equals(segA.get(i), segB.get(i))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
}
diff --git a/src/com/android/services/telephony/rcs/PresenceHelper.java b/src/com/android/services/telephony/rcs/PresenceHelper.java
new file mode 100644
index 0000000..5f7e35f
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/PresenceHelper.java
@@ -0,0 +1,166 @@
+/*
+ * 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.rcs;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.ims.RcsFeatureConnection;
+import com.android.ims.RcsFeatureManager;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.internal.telephony.imsphone.ImsRcsStatusListener;
+import com.android.phone.R;
+import com.android.service.ims.presence.PresencePublication;
+import com.android.service.ims.presence.PresenceSubscriber;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+
+class PresenceHelper {
+
+ private static final String LOG_TAG = "PresenceHelper";
+
+ private final Context mContext;
+ private final List<Phone> mPhones;
+
+ private final SparseArray<PresencePublication> mPresencePublications = new SparseArray<>();
+ private final SparseArray<PresenceSubscriber> mPresenceSubscribers = new SparseArray<>();
+
+ PresenceHelper(Context context) {
+ mContext = context;
+
+ // Get phones
+ Phone[] phoneAry = PhoneFactory.getPhones();
+ mPhones = (phoneAry != null) ? Arrays.asList(phoneAry) : new ArrayList<>();
+
+ initRcsPresencesInstance();
+ registerRcsConnectionStatus();
+
+ Log.i(LOG_TAG, "initialized: phone size=" + mPhones.size());
+ }
+
+ private void initRcsPresencesInstance() {
+ String[] volteError = mContext.getResources().getStringArray(
+ R.array.config_volte_provision_error_on_publish_response);
+ String[] rcsError = mContext.getResources().getStringArray(
+ R.array.config_rcs_provision_error_on_publish_response);
+
+ mPhones.forEach((phone) -> {
+ RcsFeatureConnection rcsConnection = getRcsFeatureConnection(phone);
+ // Initialize PresencePublication
+ mPresencePublications.put(
+ phone.getPhoneId(),
+ new PresencePublication(rcsConnection, mContext, volteError, rcsError));
+ // Initialize PresenceSubscriber
+ mPresenceSubscribers.put(
+ phone.getPhoneId(),
+ new PresenceSubscriber(rcsConnection, mContext, volteError, rcsError));
+ });
+ }
+
+ private @Nullable RcsFeatureConnection getRcsFeatureConnection(Phone phone) {
+ ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
+ if (imsPhone != null) {
+ RcsFeatureManager rcsFeatureManager = imsPhone.getRcsManager();
+ if (rcsFeatureManager != null) {
+ return rcsFeatureManager.getRcsFeatureConnection();
+ }
+ }
+ return null;
+ }
+
+ /*
+ * RcsFeatureManager in ImsPhone is not null only when RCS is connected. Register a callback to
+ * receive the RCS connection status.
+ */
+ private void registerRcsConnectionStatus() {
+ mPhones.forEach((phone) -> {
+ ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
+ if (imsPhone != null) {
+ imsPhone.setRcsStatusListener(mStatusListener);
+ }
+ });
+ }
+
+ /**
+ * The IMS RCS status listener to listen the status changed
+ */
+ private ImsRcsStatusListener mStatusListener = new ImsRcsStatusListener() {
+ @Override
+ public void onRcsConnected(int phoneId, RcsFeatureManager rcsFeatureManager) {
+ int subId = getSubscriptionId(phoneId);
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ Log.e(LOG_TAG, "onRcsConnected: invalid subId, phoneId=" + phoneId);
+ return;
+ }
+
+ Log.i(LOG_TAG, "onRcsConnected: phoneId=" + phoneId + ", subId=" + subId);
+ RcsFeatureConnection connection = rcsFeatureManager.getRcsFeatureConnection();
+ PresencePublication presencePublication = getPresencePublication(phoneId);
+ if (presencePublication != null) {
+ Log.i(LOG_TAG, "Update PresencePublisher because RCS is connected");
+ presencePublication.updatePresencePublisher(subId, connection);
+ }
+ PresenceSubscriber presenceSubscriber = getPresenceSubscriber(phoneId);
+ if (presenceSubscriber != null) {
+ Log.i(LOG_TAG, "Update PresenceSubscriber because RCS is connected");
+ presenceSubscriber.updatePresenceSubscriber(subId, connection);
+ }
+ }
+
+ @Override
+ public void onRcsDisconnected(int phoneId) {
+ int subId = getSubscriptionId(phoneId);
+ Log.i(LOG_TAG, "onRcsDisconnected: phoneId=" + phoneId + ", subId=" + subId);
+ PresencePublication publication = getPresencePublication(phoneId);
+ if (publication != null) {
+ Log.i(LOG_TAG, "Remove PresencePublisher because RCS is disconnected");
+ publication.removePresencePublisher(subId);
+ }
+
+ PresenceSubscriber subscriber = getPresenceSubscriber(phoneId);
+ if (subscriber != null) {
+ Log.i(LOG_TAG, "Remove PresencePublisher because RCS is disconnected");
+ subscriber.removePresenceSubscriber(subId);
+ }
+ }
+ };
+
+ private int getSubscriptionId(int phoneId) {
+ Optional<Phone> phone = mPhones.stream()
+ .filter(p -> p.getPhoneId() == phoneId).findFirst();
+ if (phone.isPresent()) {
+ return phone.get().getSubId();
+ }
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+
+ public @Nullable PresencePublication getPresencePublication(int phoneId) {
+ return mPresencePublications.get(phoneId);
+ }
+
+ public @Nullable PresenceSubscriber getPresenceSubscriber(int phoneId) {
+ return mPresenceSubscribers.get(phoneId);
+ }
+}
diff --git a/src/com/android/services/telephony/rcs/TelephonyRcsService.java b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
new file mode 100644
index 0000000..74765d6
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
@@ -0,0 +1,50 @@
+/*
+ * 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.rcs;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.android.service.ims.presence.PresencePublication;
+import com.android.service.ims.presence.PresenceSubscriber;
+
+/**
+ * Telephony RCS Service integrates PresencePublication and PresenceSubscriber into the service.
+ */
+public class TelephonyRcsService {
+
+ private static final String LOG_TAG = "TelephonyRcsService";
+
+ private final Context mContext;
+
+ // A helper class to manage the RCS Presences instances.
+ private final PresenceHelper mPresenceHelper;
+
+ public TelephonyRcsService(Context context) {
+ Log.i(LOG_TAG, "initialize");
+ mContext = context;
+ mPresenceHelper = new PresenceHelper(mContext);
+ }
+
+ private PresencePublication getPresencePublication(int phoneId) {
+ return mPresenceHelper.getPresencePublication(phoneId);
+ }
+
+ private PresenceSubscriber getPresenceSubscriber(int phoneId) {
+ return mPresenceHelper.getPresenceSubscriber(phoneId);
+ }
+}