Fix connection manager bugs related to work profiles
When calling TelecomManager.addNewIncomingCall with an account
that's was not registered we would crash. Fix was to check
for a null phone account.
Various TelecomManager APIs would throw security exceptions when
called from a work profile. Fix was to:
- switch to using APIs that allowed the user to be specified.
For example, using queryIntentServicesAsUse instead of
getServiceInfo
- don't look for work profiles if the calling user isn't the
owner.
The default connection manager (set using a config.xml overlay)
didn't work with work profiles. Fix was to allow the default
connection manager to resolve against the calling process's
user handle.
BUG: 19300886, 19301690, 19301359
Change-Id: I49d75b69dcfc829b74a5483d7a011f17d8d06838
diff --git a/src/com/android/server/telecom/CreateConnectionProcessor.java b/src/com/android/server/telecom/CreateConnectionProcessor.java
index eec1427..31114df 100644
--- a/src/com/android/server/telecom/CreateConnectionProcessor.java
+++ b/src/com/android/server/telecom/CreateConnectionProcessor.java
@@ -267,6 +267,10 @@
// Connection managers are only allowed to manage SIM subscriptions.
PhoneAccount targetPhoneAccount = mPhoneAccountRegistrar.getPhoneAccount(
targetPhoneAccountHandle);
+ if (targetPhoneAccount == null) {
+ Log.d(this, "shouldSetConnectionManager, phone account not found");
+ return false;
+ }
boolean isSimSubscription = (targetPhoneAccount.getCapabilities() &
PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) != 0;
if (!isSimSubscription) {
@@ -326,7 +330,8 @@
if (mShouldUseConnectionManager && callManagerHandle != null) {
PhoneAccount callManager = mPhoneAccountRegistrar
.getPhoneAccount(callManagerHandle);
- if (callManager.hasCapabilities(PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)) {
+ if (callManager != null && callManager.hasCapabilities(
+ PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)) {
CallAttemptRecord callAttemptRecord = new CallAttemptRecord(callManagerHandle,
mPhoneAccountRegistrar.
getDefaultOutgoingPhoneAccount(mCall.getHandle().getScheme())
diff --git a/src/com/android/server/telecom/PhoneAccountRegistrar.java b/src/com/android/server/telecom/PhoneAccountRegistrar.java
index bd6e2d2..759a31d 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -27,6 +27,7 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
+import android.os.Binder;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
@@ -65,6 +66,7 @@
import java.lang.SecurityException;
import java.lang.String;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
@@ -298,8 +300,16 @@
mContext.getResources().getString(R.string.default_connection_manager_component);
if (!TextUtils.isEmpty(defaultConnectionMgr)) {
ComponentName componentName = ComponentName.unflattenFromString(defaultConnectionMgr);
+ if (componentName == null) {
+ return null;
+ }
+
// Make sure that the component can be resolved.
List<ResolveInfo> resolveInfos = resolveComponent(componentName, null);
+ if (resolveInfos.isEmpty()) {
+ resolveInfos = resolveComponent(componentName, Binder.getCallingUserHandle());
+ }
+
if (!resolveInfos.isEmpty()) {
// See if there is registered PhoneAccount by this component.
List<PhoneAccountHandle> handles = getAllPhoneAccountHandles();
@@ -381,16 +391,23 @@
return true;
}
+ if (phoneAccountUserHandle.equals(Binder.getCallingUserHandle())) {
+ return true;
+ }
+
// Unlike in TelecomServiceImpl, we only care about *profiles* here. We want to make sure
// that we don't resolve PhoneAccount across *users*, but resolving across *profiles* is
// fine.
- List<UserInfo> profileUsers = mUserManager.getProfiles(mCurrentUserHandle.getIdentifier());
-
- for (UserInfo profileInfo : profileUsers) {
- if (profileInfo.getUserHandle().equals(phoneAccountUserHandle)) {
- return true;
+ if (UserHandle.getCallingUserId() == UserHandle.USER_OWNER) {
+ List<UserInfo> profileUsers =
+ mUserManager.getProfiles(mCurrentUserHandle.getIdentifier());
+ for (UserInfo profileInfo : profileUsers) {
+ if (profileInfo.getUserHandle().equals(phoneAccountUserHandle)) {
+ return true;
+ }
}
}
+
return false;
}
@@ -404,10 +421,15 @@
PackageManager pm = mContext.getPackageManager();
Intent intent = new Intent(ConnectionService.SERVICE_INTERFACE);
intent.setComponent(componentName);
- if (userHandle != null) {
- return pm.queryIntentServicesAsUser(intent, 0, userHandle.getIdentifier());
- } else {
- return pm.queryIntentServices(intent, 0);
+ try {
+ if (userHandle != null) {
+ return pm.queryIntentServicesAsUser(intent, 0, userHandle.getIdentifier());
+ } else {
+ return pm.queryIntentServices(intent, 0);
+ }
+ } catch (SecurityException e) {
+ Log.v(this, "%s is not visible for the calling user", componentName);
+ return Collections.EMPTY_LIST;
}
}
@@ -614,17 +636,19 @@
* @return {@code True} if the phone account has permission.
*/
public boolean phoneAccountHasPermission(PhoneAccountHandle phoneAccountHandle) {
- PackageManager packageManager = mContext.getPackageManager();
- try {
- ServiceInfo serviceInfo = packageManager.getServiceInfo(
- phoneAccountHandle.getComponentName(), 0);
-
- return serviceInfo.permission != null &&
- serviceInfo.permission.equals(Manifest.permission.BIND_CONNECTION_SERVICE);
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(this, "Name not found %s", e);
+ List<ResolveInfo> resolveInfos = resolveComponent(phoneAccountHandle);
+ if (resolveInfos.isEmpty()) {
+ Log.w(this, "phoneAccount %s not found", phoneAccountHandle.getComponentName());
return false;
}
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+ if (serviceInfo == null || !Objects.equals(serviceInfo.permission,
+ Manifest.permission.BIND_CONNECTION_SERVICE)) {
+ return false;
+ }
+ }
+ return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index e9df1a7..c52c525 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -747,10 +747,12 @@
return false;
}
+ if (phoneAccountUserHandle.equals(Binder.getCallingUserHandle())) {
+ return true;
+ }
+
List<UserHandle> profileUserHandles;
- if (isCallerSystemApp()) {
- // If the caller lives in /system/priv-app, it can see PhoneAccounts for all of the
- // *profiles* that the calling user owns, but not for any other *users*.
+ if (UserHandle.getCallingUserId() == UserHandle.USER_OWNER) {
profileUserHandles = mUserManager.getUserProfiles();
} else {
// Otherwise, it has to be owned by the current caller's profile.