Merge "[Telephony Mainline] Add permission check for isMvnoMatched" into rvc-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 18687d0..9ee5bab 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -49,6 +49,7 @@
<protected-broadcast android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />
<protected-broadcast android:name="android.provider.Telephony.SMS_CB_RECEIVED" />
<protected-broadcast android:name="android.provider.action.SMS_EMERGENCY_CB_RECEIVED" />
+ <protected-broadcast android:name="android.provider.Telephony.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED" />
<protected-broadcast android:name="android.provider.Telephony.SECRET_CODE" />
<protected-broadcast android:name= "com.android.internal.stk.command" />
<protected-broadcast android:name= "com.android.internal.stk.session_end" />
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 351aaa9..7b3d3c1 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -37,6 +38,7 @@
import android.os.IBinder;
import android.os.Message;
import android.os.PersistableBundle;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.UserHandle;
@@ -1123,6 +1125,9 @@
&& !TextUtils.isEmpty(args[requestingPackageIndex + 1])) {
String requestingPackage = args[requestingPackageIndex + 1];
indentPW.println("");
+ // Throws a SecurityException if the caller is impersonating another app in an effort to
+ // dump extra info (which may contain PII the caller doesn't have a right to).
+ enforceCallerIsSystemOrRequestingPackage(requestingPackage);
logd("Including default and requesting package " + requestingPackage
+ " carrier services in dump");
indentPW.println("Connected services");
@@ -1162,6 +1167,32 @@
indentPW.println("");
}
+ /**
+ * Passes without problem when one of these conditions is true:
+ * - The caller is a privileged UID (e.g. for dumpstate.cpp generating a bug report, where the
+ * system knows the true caller plumbed in through the {@link android.os.BugreportManager} API).
+ * - The caller's UID matches the supplied package.
+ *
+ * @throws SecurityException if none of the above conditions are met.
+ */
+ private void enforceCallerIsSystemOrRequestingPackage(String requestingPackage)
+ throws SecurityException {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid == Process.ROOT_UID || callingUid == Process.SYSTEM_UID
+ || callingUid == Process.SHELL_UID || callingUid == Process.PHONE_UID) {
+ // Bug reports (dumpstate.cpp) run as SHELL, and let some other privileged UIDs through
+ // as well.
+ return;
+ }
+ // An app is trying to dump extra detail, block it if they aren't who they claim to be.
+ AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+ if (appOps == null) {
+ throw new SecurityException("No AppOps");
+ }
+ // Will throw a SecurityException if the UID and package don't match.
+ appOps.checkPackage(callingUid, requestingPackage);
+ }
+
private void dumpCarrierServiceIfBound(FileDescriptor fd, IndentingPrintWriter indentPW,
String prefix, String pkgName) {
// Null package is possible if it's early in the boot process, there was a recent crash, we
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index bcde341..dcae24b 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -35,6 +35,7 @@
import com.android.ims.ImsManager;
import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.TelephonyPermissions;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.services.telephony.rcs.RcsFeatureController;
import com.android.services.telephony.rcs.TelephonyRcsService;
@@ -245,9 +246,13 @@
}
@Override
- public void requestCapabilities(int subId, List<Uri> contactNumbers,
- IRcsUceControllerCallback c) {
+ public void requestCapabilities(int subId, String callingPackage, String callingFeatureId,
+ List<Uri> contactNumbers, IRcsUceControllerCallback c) {
enforceReadPrivilegedPermission("requestCapabilities");
+ if (!isUceSettingEnabled(subId, callingPackage, callingFeatureId)) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "The user has not enabled UCE for this subscription.");
+ }
final long token = Binder.clearCallingIdentity();
try {
UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
@@ -280,17 +285,32 @@
}
@Override
- public boolean isUceSettingEnabled(int subId) {
- enforceReadPrivilegedPermission("isUceSettingEnabled");
- return SubscriptionManager.getBooleanSubscriptionProperty(subId,
- SubscriptionManager.IMS_RCS_UCE_ENABLED, false /*defaultValue*/, mApp);
+ public boolean isUceSettingEnabled(int subId, String callingPackage, String callingFeatureId) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, subId, callingPackage, callingFeatureId, "isUceSettingEnabled")) {
+ Log.w(TAG, "isUceSettingEnabled: READ_PHONE_STATE app op disabled when accessing "
+ + "isUceSettingEnabled");
+ return false;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return SubscriptionManager.getBooleanSubscriptionProperty(subId,
+ SubscriptionManager.IMS_RCS_UCE_ENABLED, false /*defaultValue*/, mApp);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override
public void setUceSettingEnabled(int subId, boolean isEnabled) {
enforceModifyPermission();
- SubscriptionManager.setSubscriptionProperty(subId, SubscriptionManager.IMS_RCS_UCE_ENABLED,
- (isEnabled ? "1" : "0"));
+ final long token = Binder.clearCallingIdentity();
+ try {
+ SubscriptionManager.setSubscriptionProperty(subId,
+ SubscriptionManager.IMS_RCS_UCE_ENABLED, (isEnabled ? "1" : "0"));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
/**
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 46c80cc..ce4f188 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -120,6 +120,7 @@
import com.android.ims.ImsManager;
import com.android.ims.internal.IImsServiceFeatureCallback;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CallForwardInfo;
import com.android.internal.telephony.CallManager;
import com.android.internal.telephony.CallStateException;
@@ -1617,7 +1618,8 @@
}
/** Private constructor; @see init() */
- private PhoneInterfaceManager(PhoneGlobals app) {
+ @VisibleForTesting
+ /* package */ PhoneInterfaceManager(PhoneGlobals app) {
mApp = app;
mCM = PhoneGlobals.getInstance().mCM;
mImsResolver = PhoneGlobals.getInstance().getImsResolver();
@@ -7446,6 +7448,15 @@
@Override
public List<UiccCardInfo> getUiccCardsInfo(String callingPackage) {
+ try {
+ PackageManager pm = mApp.getPackageManager();
+ if (Binder.getCallingUid() != pm.getPackageUid(callingPackage, 0)) {
+ throw new SecurityException("Calling package " + callingPackage + " does not match "
+ + "calling UID");
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new SecurityException("Invalid calling package. e=" + e);
+ }
boolean hasReadPermission = false;
try {
enforceReadPrivilegedPermission("getUiccCardsInfo");
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index b4b7405..8a09f8f 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -36,6 +36,7 @@
import android.os.PersistableBundle;
import android.os.UserHandle;
import android.provider.Settings;
+import android.provider.Telephony;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
@@ -48,6 +49,7 @@
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsException;
import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.ImsRcsManager;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.RegistrationManager;
import android.telephony.ims.feature.MmTelFeature;
@@ -542,16 +544,31 @@
}
/**
- * Determines from carrier configuration whether RCS presence indication for video calls is
- * supported.
+ * Determines from carrier configuration and user setting whether RCS presence indication
+ * for video calls is supported.
*
* @return {@code true} if RCS presence indication for video calls is supported.
*/
private boolean isCarrierVideoPresenceSupported() {
PersistableBundle b =
PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
- return b != null &&
- b.getBoolean(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL);
+ boolean carrierConfigEnabled = b != null
+ && b.getBoolean(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL);
+ return carrierConfigEnabled && isUserContactDiscoverySettingEnabled();
+ }
+
+ /**
+ * @return true if the user has enabled contact discovery for the subscription associated
+ * with this account entry, false otherwise.
+ */
+ private boolean isUserContactDiscoverySettingEnabled() {
+ try {
+ ImsRcsManager manager = mImsManager.getImsRcsManager(mPhone.getSubId());
+ return manager.getUceAdapter().isUceSettingEnabled();
+ } catch (Exception e) {
+ Log.w(LOG_TAG, "isUserContactDiscoverySettingEnabled caught exception: " + e);
+ return false;
+ }
}
/**
@@ -753,6 +770,25 @@
}
}
+ public void updateVideoPresenceCapability() {
+ synchronized (mAccountsLock) {
+ if (!mAccounts.contains(this)) {
+ // Account has already been torn down, don't try to register it again.
+ // This handles the case where teardown has already happened, and we got a Ims
+ // registration update that lost the race for the mAccountsLock. In such a
+ // scenario by the time we get here, the original phone account could have been
+ // torn down.
+ return;
+ }
+ boolean isVideoPresenceSupported = isCarrierVideoPresenceSupported();
+ if (mIsVideoPresenceSupported != isVideoPresenceSupported) {
+ Log.i(this, "updateVideoPresenceCapability for subId=" + mPhone.getSubId()
+ + ", new value= " + isVideoPresenceSupported);
+ mAccount = registerPstnPhoneAccount(mIsEmergency, mIsDummy);
+ }
+ }
+ }
+
public void updateRttCapability() {
boolean isRttEnabled = isRttCurrentlySupported();
if (isRttEnabled != mIsRttCapable) {
@@ -985,6 +1021,7 @@
private static TelecomAccountRegistry sInstance;
private final Context mContext;
private final TelecomManager mTelecomManager;
+ private final android.telephony.ims.ImsManager mImsManager;
private final TelephonyManager mTelephonyManager;
private final SubscriptionManager mSubscriptionManager;
private List<AccountEntry> mAccounts = new LinkedList<AccountEntry>();
@@ -1000,6 +1037,7 @@
TelecomAccountRegistry(Context context) {
mContext = context;
mTelecomManager = context.getSystemService(TelecomManager.class);
+ mImsManager = context.getSystemService(android.telephony.ims.ImsManager.class);
mTelephonyManager = TelephonyManager.from(context);
mSubscriptionManager = SubscriptionManager.from(context);
}
@@ -1238,6 +1276,10 @@
localeChangeFilter.addAction(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED);
mContext.registerReceiver(mLocaleChangeReceiver, localeChangeFilter);
+ registerContentObservers();
+ }
+
+ private void registerContentObservers() {
// Listen to the RTT system setting so that we update it when the user flips it.
ContentObserver rttUiSettingObserver = new ContentObserver(
new Handler(Looper.getMainLooper())) {
@@ -1254,6 +1296,23 @@
Uri rttSettingUri = Settings.Secure.getUriFor(Settings.Secure.RTT_CALLING_MODE);
mContext.getContentResolver().registerContentObserver(
rttSettingUri, false, rttUiSettingObserver);
+
+ // Listen to the changes to the user's Contacts Discovery Setting.
+ ContentObserver contactDiscoveryObserver = new ContentObserver(
+ new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange) {
+ synchronized (mAccountsLock) {
+ for (AccountEntry account : mAccounts) {
+ account.updateVideoPresenceCapability();
+ }
+ }
+ }
+ };
+ Uri contactDiscUri = Uri.withAppendedPath(Telephony.SimInfo.CONTENT_URI,
+ Telephony.SimInfo.IMS_RCS_UCE_ENABLED);
+ mContext.getContentResolver().registerContentObserver(
+ contactDiscUri, true /*notifyForDescendants*/, contactDiscoveryObserver);
}
/**
diff --git a/tests/src/com/android/phone/PhoneInterfaceManagerTest.java b/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
new file mode 100644
index 0000000..9f8de9e
--- /dev/null
+++ b/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.phone;
+
+import static junit.framework.TestCase.fail;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.util.Log;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@RunWith(AndroidJUnit4.class)
+public class PhoneInterfaceManagerTest extends TelephonyTestBase {
+
+ private static final String PRIVILEGED_PACKAGE_NAME = "test.package.name";
+
+ private static final String TAG = "PhoneInterfaceManagerTest";
+
+ private PhoneInterfaceManager mPhoneInterfaceManager;
+ private PhoneGlobals mMockPhoneGlobals;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ mMockPhoneGlobals = mock(PhoneGlobals.class);
+ //PhoneGlobals phoneGlobals = new PhoneGlobals(mContext);
+ mPhoneInterfaceManager = new PhoneInterfaceManager(mMockPhoneGlobals);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testGetUiccCardsInfoSecurity() {
+ // Set up mocks so that the supplied package UID does not equal the calling UID
+ PackageManager mockPackageManager = mock(PackageManager.class);
+ try {
+ doReturn(Binder.getCallingUid() + 1).when(mockPackageManager)
+ .getPackageUid(eq(PRIVILEGED_PACKAGE_NAME), anyInt());
+ } catch (Exception e) {
+ Log.d(TAG, "testGetUiccCardsInfoSecurity unable to setup mocks");
+ fail();
+ }
+ doReturn(mockPackageManager).when(mContext).getPackageManager();
+ doReturn(mockPackageManager).when(mMockPhoneGlobals).getPackageManager();
+ try {
+ mPhoneInterfaceManager.getUiccCardsInfo(PRIVILEGED_PACKAGE_NAME);
+ fail();
+ } catch (SecurityException e) {
+ Log.d(TAG, "testGetUiccCardsInfoSecurity e = " + e);
+ }
+ }
+}