Loading src/java/com/android/internal/telephony/IccSmsInterfaceManager.java +112 −95 Original line number Diff line number Diff line Loading @@ -40,15 +40,14 @@ import android.service.carrier.CarrierMessagingService; import android.telephony.Rlog; import android.telephony.SmsManager; import android.telephony.SmsMessage; import android.telephony.TelephonyManager; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo; import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo; import com.android.internal.telephony.uicc.IccConstants; import com.android.internal.telephony.uicc.IccFileHandler; import com.android.internal.telephony.uicc.IccUtils; import com.android.internal.telephony.uicc.UiccController; import com.android.internal.util.HexDump; import java.util.ArrayList; Loading Loading @@ -129,12 +128,22 @@ public class IccSmsInterfaceManager { }; protected IccSmsInterfaceManager(Phone phone) { this(phone, phone.getContext(), (AppOpsManager) phone.getContext().getSystemService(Context.APP_OPS_SERVICE), (UserManager) phone.getContext().getSystemService(Context.USER_SERVICE), new SmsDispatchersController( phone, phone.mSmsStorageMonitor, phone.mSmsUsageMonitor)); } @VisibleForTesting public IccSmsInterfaceManager( Phone phone, Context context, AppOpsManager appOps, UserManager userManager, SmsDispatchersController dispatchersController) { mPhone = phone; mContext = phone.getContext(); mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); mDispatchersController = new SmsDispatchersController(phone, phone.mSmsStorageMonitor, phone.mSmsUsageMonitor); mContext = context; mAppOps = appOps; mUserManager = userManager; mDispatchersController = dispatchersController; } protected void markMessagesAsRead(ArrayList<byte[]> messages) { Loading Loading @@ -323,11 +332,10 @@ public class IccSmsInterfaceManager { */ public void sendDataWithSelfPermissions(String callingPackage, String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { mPhone.getContext().enforceCallingOrSelfPermission( Manifest.permission.SEND_SMS, "Sending SMS message"); sendDataInternal(callingPackage, destAddr, scAddr, destPort, data, sentIntent, deliveryIntent); if (!checkCallingOrSelfSendSmsPermission(callingPackage, "Sending SMS message")) { return; } sendDataInternal(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent); } /** Loading @@ -336,11 +344,10 @@ public class IccSmsInterfaceManager { */ public void sendData(String callingPackage, String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { mPhone.getContext().enforceCallingPermission( Manifest.permission.SEND_SMS, "Sending SMS message"); sendDataInternal(callingPackage, destAddr, scAddr, destPort, data, sentIntent, deliveryIntent); if (!checkCallingSendSmsPermission(callingPackage, "Sending SMS message")) { return; } sendDataInternal(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent); } /** Loading Loading @@ -369,17 +376,13 @@ public class IccSmsInterfaceManager { * raw pdu of the status report is in the extended data ("pdu"). */ private void sendDataInternal(String callingPackage, String destAddr, String scAddr, private void sendDataInternal(String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { if (Rlog.isLoggable("SMS", Log.VERBOSE)) { log("sendData: destAddr=" + destAddr + " scAddr=" + scAddr + " destPort=" + destPort + " data='"+ HexDump.toHexString(data) + "' sentIntent=" + sentIntent + " deliveryIntent=" + deliveryIntent); } if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPackage) != AppOpsManager.MODE_ALLOWED) { return; } destAddr = filterDestAddress(destAddr); mDispatchersController.sendData(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent); Loading @@ -392,9 +395,10 @@ public class IccSmsInterfaceManager { public void sendText(String callingPackage, String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp) { mPhone.getContext().enforceCallingPermission( Manifest.permission.SEND_SMS, "Sending SMS message"); if (!checkCallingSendTextPermissions( persistMessageForNonDefaultSmsApp, callingPackage, "Sending SMS message")) { return; } sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent, persistMessageForNonDefaultSmsApp, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED, false /* expectMore */, SMS_MESSAGE_PERIOD_NOT_SPECIFIED); Loading @@ -407,9 +411,9 @@ public class IccSmsInterfaceManager { public void sendTextWithSelfPermissions(String callingPackage, String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessage) { mPhone.getContext().enforceCallingOrSelfPermission( Manifest.permission.SEND_SMS, "Sending SMS message"); if (!checkCallingOrSelfSendSmsPermission(callingPackage, "Sending SMS message")) { return; } sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent, persistMessage, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED, false /* expectMore */, SMS_MESSAGE_PERIOD_NOT_SPECIFIED); Loading Loading @@ -472,13 +476,6 @@ public class IccSmsInterfaceManager { + " priority=" + priority + " expectMore=" + expectMore + " validityPeriod=" + validityPeriod); } if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPackage) != AppOpsManager.MODE_ALLOWED) { return; } if (!persistMessageForNonDefaultSmsApp) { enforcePrivilegedAppPermissions(); } destAddr = filterDestAddress(destAddr); mDispatchersController.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp, Loading Loading @@ -535,9 +532,9 @@ public class IccSmsInterfaceManager { String text, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp, int priority, boolean expectMore, int validityPeriod) { mPhone.getContext().enforceCallingOrSelfPermission( Manifest.permission.SEND_SMS, "Sending SMS message"); if (!checkCallingOrSelfSendSmsPermission(callingPackage, "Sending SMS message")) { return; } sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent, persistMessageForNonDefaultSmsApp, priority, expectMore, validityPeriod); } Loading @@ -553,7 +550,11 @@ public class IccSmsInterfaceManager { * the same time an SMS received from radio is acknowledged back. */ public void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) { enforcePrivilegedAppPermissions(); if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) { enforceCallerIsImsAppOrCarrierApp("injectSmsPdu"); } if (Rlog.isLoggable("SMS", Log.VERBOSE)) { log("pdu: " + pdu + "\n format=" + format + Loading Loading @@ -658,12 +659,9 @@ public class IccSmsInterfaceManager { String scAddr, List<String> parts, List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp, int priority, boolean expectMore, int validityPeriod) { mPhone.getContext().enforceCallingPermission( Manifest.permission.SEND_SMS, "Sending SMS message"); if (!persistMessageForNonDefaultSmsApp) { // Only allow carrier app or carrier ims to skip auto message persistence. enforcePrivilegedAppPermissions(); if (!checkCallingSendTextPermissions( persistMessageForNonDefaultSmsApp, callingPackage, "Sending SMS message")) { return; } if (Rlog.isLoggable("SMS", Log.VERBOSE)) { int i = 0; Loading @@ -672,10 +670,6 @@ public class IccSmsInterfaceManager { ", part[" + (i++) + "]=" + part); } } if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPackage) != AppOpsManager.MODE_ALLOWED) { return; } destAddr = filterDestAddress(destAddr); Loading Loading @@ -810,13 +804,11 @@ public class IccSmsInterfaceManager { synchronized public boolean enableGsmBroadcastRange(int startMessageId, int endMessageId) { Context context = mPhone.getContext(); context.enforceCallingPermission( mContext.enforceCallingPermission( "android.permission.RECEIVE_SMS", "Enabling cell broadcast SMS"); String client = context.getPackageManager().getNameForUid( String client = mContext.getPackageManager().getNameForUid( Binder.getCallingUid()); if (!mCellBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) { Loading @@ -836,13 +828,11 @@ public class IccSmsInterfaceManager { synchronized public boolean disableGsmBroadcastRange(int startMessageId, int endMessageId) { Context context = mPhone.getContext(); context.enforceCallingPermission( mContext.enforceCallingPermission( "android.permission.RECEIVE_SMS", "Disabling cell broadcast SMS"); String client = context.getPackageManager().getNameForUid( String client = mContext.getPackageManager().getNameForUid( Binder.getCallingUid()); if (!mCellBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) { Loading @@ -862,13 +852,11 @@ public class IccSmsInterfaceManager { synchronized public boolean enableCdmaBroadcastRange(int startMessageId, int endMessageId) { Context context = mPhone.getContext(); context.enforceCallingPermission( mContext.enforceCallingPermission( "android.permission.RECEIVE_SMS", "Enabling cdma broadcast SMS"); String client = context.getPackageManager().getNameForUid( String client = mContext.getPackageManager().getNameForUid( Binder.getCallingUid()); if (!mCdmaBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) { Loading @@ -888,13 +876,11 @@ public class IccSmsInterfaceManager { synchronized public boolean disableCdmaBroadcastRange(int startMessageId, int endMessageId) { Context context = mPhone.getContext(); context.enforceCallingPermission( mContext.enforceCallingPermission( "android.permission.RECEIVE_SMS", "Disabling cell broadcast SMS"); String client = context.getPackageManager().getNameForUid( String client = mContext.getPackageManager().getNameForUid( Binder.getCallingUid()); if (!mCdmaBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) { Loading Loading @@ -1086,17 +1072,14 @@ public class IccSmsInterfaceManager { public void sendStoredText(String callingPkg, Uri messageUri, String scAddress, PendingIntent sentIntent, PendingIntent deliveryIntent) { mPhone.getContext().enforceCallingPermission(Manifest.permission.SEND_SMS, "Sending SMS message"); if (!checkCallingSendSmsPermission(callingPkg, "Sending SMS message")) { return; } if (Rlog.isLoggable("SMS", Log.VERBOSE)) { log("sendStoredText: scAddr=" + scAddress + " messageUri=" + messageUri + " sentIntent=" + sentIntent + " deliveryIntent=" + deliveryIntent); } if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPkg) != AppOpsManager.MODE_ALLOWED) { return; } final ContentResolver resolver = mPhone.getContext().getContentResolver(); final ContentResolver resolver = mContext.getContentResolver(); if (!isFailedOrDraft(resolver, messageUri)) { Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredText: not FAILED or DRAFT message"); returnUnspecifiedFailure(sentIntent); Loading @@ -1117,13 +1100,10 @@ public class IccSmsInterfaceManager { public void sendStoredMultipartText(String callingPkg, Uri messageUri, String scAddress, List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) { mPhone.getContext().enforceCallingPermission(Manifest.permission.SEND_SMS, "Sending SMS message"); if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPkg) != AppOpsManager.MODE_ALLOWED) { if (!checkCallingSendSmsPermission(callingPkg, "Sending SMS message")) { return; } final ContentResolver resolver = mPhone.getContext().getContentResolver(); final ContentResolver resolver = mContext.getContentResolver(); if (!isFailedOrDraft(resolver, messageUri)) { Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredMultipartText: " + "not FAILED or DRAFT message"); Loading Loading @@ -1268,32 +1248,69 @@ public class IccSmsInterfaceManager { } } private void enforceCarrierPrivilege() { UiccController controller = UiccController.getInstance(); if (controller == null || controller.getUiccCard(mPhone.getPhoneId()) == null) { throw new SecurityException("No Carrier Privilege: No UICC"); /** * Check that the caller can send text messages. * * For persisted messages, the caller just needs the SEND_SMS permission. For unpersisted * messages, the caller must either be the IMS app or a carrier-privileged app, or they must * have both the MODIFY_PHONE_STATE and SEND_SMS permissions. * * @throws SecurityException if the caller is missing all necessary permission declaration or * has had a necessary runtime permission revoked. * @return true unless the caller has all necessary permissions but has a revoked AppOps bit. */ @VisibleForTesting public boolean checkCallingSendTextPermissions( boolean persistMessageForNonDefaultSmsApp, String callingPackage, String message) { // TODO(b/75978989): Should we allow IMS/carrier apps for persisted messages as well? if (!persistMessageForNonDefaultSmsApp) { try { enforceCallerIsImsAppOrCarrierApp(message); // No need to also check SEND_SMS. return true; } catch (SecurityException e) { mContext.enforceCallingPermission( android.Manifest.permission.MODIFY_PHONE_STATE, message); } if (controller.getUiccCard(mPhone.getPhoneId()).getCarrierPrivilegeStatusForCurrentTransaction( mContext.getPackageManager()) != TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { throw new SecurityException("No Carrier Privilege."); } return checkCallingSendSmsPermission(callingPackage, message); } /** * Enforces that the caller has {@link android.Manifest.permission#MODIFY_PHONE_STATE} * permission or is one of the following apps: * Check that the caller (or self, if this is not an IPC) has SEND_SMS permissions. * * @throws SecurityException if the caller is missing the permission declaration or has had the * permission revoked at runtime. * @return whether the caller has the OP_SEND_SMS AppOps bit. */ private boolean checkCallingOrSelfSendSmsPermission(String callingPackage, String message) { mContext.enforceCallingOrSelfPermission(Manifest.permission.SEND_SMS, message); return mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED; } /** * Check that the caller has SEND_SMS permissions. Can only be called during an IPC. * * @throws SecurityException if the caller is missing the permission declaration or has had the * permission revoked at runtime. * @return whether the caller has the OP_SEND_SMS AppOps bit. */ private boolean checkCallingSendSmsPermission(String callingPackage, String message) { mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, message); return mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED; } /** * Enforces that the caller is one of the following apps: * <ul> * <li> IMS App * <li> Carrier App * </ul> */ private void enforcePrivilegedAppPermissions() { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) { return; } @VisibleForTesting public void enforceCallerIsImsAppOrCarrierApp(String message) { int callingUid = Binder.getCallingUid(); String carrierImsPackage = CarrierSmsUtils.getCarrierImsPackageForIntent(mContext, mPhone, new Intent(CarrierMessagingService.SERVICE_INTERFACE)); Loading @@ -1309,7 +1326,7 @@ public class IccSmsInterfaceManager { } } enforceCarrierPrivilege(); TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mPhone.getSubId(), message); } private String filterDestAddress(String destAddr) { Loading tests/telephonytests/src/com/android/internal/telephony/IccSmsInterfaceManagerTest.java 0 → 100644 +175 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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.internal.telephony; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.Manifest; import android.app.AppOpsManager; import android.content.Context; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; import android.os.UserManager; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class IccSmsInterfaceManagerTest { private static final String PACKAGE = "com.example.package"; private static final String MESSAGE = "msg"; private HandlerThread mHandlerThread; @Mock private Phone mMockPhone; @Mock private Context mMockContext; @Mock private AppOpsManager mMockAppOps; @Mock private UserManager mMockUserManager; @Mock private SmsDispatchersController mMockDispatchersController; private IccSmsInterfaceManager mIccSmsInterfaceManager; private boolean mCallerHasCarrierPrivileges; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mHandlerThread = new HandlerThread("IccSmsInterfaceManagerTest"); mHandlerThread.start(); final CountDownLatch initialized = new CountDownLatch(1); new Handler(mHandlerThread.getLooper()).post(() -> { mIccSmsInterfaceManager = new IccSmsInterfaceManager( mMockPhone, mMockContext, mMockAppOps, mMockUserManager, mMockDispatchersController) { @Override public void enforceCallerIsImsAppOrCarrierApp(String message) { if (!mCallerHasCarrierPrivileges) { throw new SecurityException(message); } } }; initialized.countDown(); }); // Wait for object to initialize. if (!initialized.await(30, TimeUnit.SECONDS)) { fail("Could not initialize IccSmsInterfaceManager"); } } @After public void tearDown() throws Exception { mHandlerThread.quit(); } @Test public void testCheckCallingSendTextPermissions_persist_grant() { assertTrue(mIccSmsInterfaceManager.checkCallingSendTextPermissions( true /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE)); } @Test public void testCheckCallingSendTextPermissions_persist_noGrant() { Mockito.doThrow(new SecurityException(MESSAGE)).when(mMockContext) .enforceCallingPermission(Manifest.permission.SEND_SMS, MESSAGE); try { mIccSmsInterfaceManager.checkCallingSendTextPermissions( true /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE); fail(); } catch (SecurityException e) { // expected } } @Test public void testCheckCallingSendTextPermissions_persist_noAppOps() { Mockito.when(mMockAppOps.noteOp( AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), PACKAGE)) .thenReturn(AppOpsManager.MODE_ERRORED); assertFalse(mIccSmsInterfaceManager.checkCallingSendTextPermissions( true /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE)); } @Test public void testCheckCallingSendTextPermissions_noPersist_grantViaCarrierApp() { mCallerHasCarrierPrivileges = true; // Other permissions shouldn't matter. Mockito.doThrow(new SecurityException(MESSAGE)).when(mMockContext) .enforceCallingPermission(Manifest.permission.MODIFY_PHONE_STATE, MESSAGE); Mockito.doThrow(new SecurityException(MESSAGE)).when(mMockContext) .enforceCallingPermission(Manifest.permission.SEND_SMS, MESSAGE); Mockito.when(mMockAppOps.noteOp( AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), PACKAGE)) .thenReturn(AppOpsManager.MODE_ERRORED); assertTrue(mIccSmsInterfaceManager.checkCallingSendTextPermissions( false /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE)); } @Test public void testCheckCallingSendTextPermissions_noPersist_grantViaModifyAndSend() { assertTrue(mIccSmsInterfaceManager.checkCallingSendTextPermissions( false /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE)); } @Test public void testCheckCallingSendTextPermissions_noPersist_noModify() { Mockito.doThrow(new SecurityException(MESSAGE)).when(mMockContext) .enforceCallingPermission(Manifest.permission.MODIFY_PHONE_STATE, MESSAGE); try { mIccSmsInterfaceManager.checkCallingSendTextPermissions( false /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE); fail(); } catch (SecurityException e) { // expected } } @Test public void testCheckCallingSendTextPermissions_noPersist_noSendSmsPermission() { Mockito.doThrow(new SecurityException(MESSAGE)).when(mMockContext) .enforceCallingPermission(Manifest.permission.SEND_SMS, MESSAGE); try { mIccSmsInterfaceManager.checkCallingSendTextPermissions( false /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE); fail(); } catch (SecurityException e) { // expected } } @Test public void testCheckCallingSendTextPermissions_noPersist_noAppOps() { Mockito.when(mMockAppOps.noteOp( AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), PACKAGE)) .thenReturn(AppOpsManager.MODE_ERRORED); assertFalse(mIccSmsInterfaceManager.checkCallingSendTextPermissions( false /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE)); } } Loading
src/java/com/android/internal/telephony/IccSmsInterfaceManager.java +112 −95 Original line number Diff line number Diff line Loading @@ -40,15 +40,14 @@ import android.service.carrier.CarrierMessagingService; import android.telephony.Rlog; import android.telephony.SmsManager; import android.telephony.SmsMessage; import android.telephony.TelephonyManager; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo; import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo; import com.android.internal.telephony.uicc.IccConstants; import com.android.internal.telephony.uicc.IccFileHandler; import com.android.internal.telephony.uicc.IccUtils; import com.android.internal.telephony.uicc.UiccController; import com.android.internal.util.HexDump; import java.util.ArrayList; Loading Loading @@ -129,12 +128,22 @@ public class IccSmsInterfaceManager { }; protected IccSmsInterfaceManager(Phone phone) { this(phone, phone.getContext(), (AppOpsManager) phone.getContext().getSystemService(Context.APP_OPS_SERVICE), (UserManager) phone.getContext().getSystemService(Context.USER_SERVICE), new SmsDispatchersController( phone, phone.mSmsStorageMonitor, phone.mSmsUsageMonitor)); } @VisibleForTesting public IccSmsInterfaceManager( Phone phone, Context context, AppOpsManager appOps, UserManager userManager, SmsDispatchersController dispatchersController) { mPhone = phone; mContext = phone.getContext(); mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); mDispatchersController = new SmsDispatchersController(phone, phone.mSmsStorageMonitor, phone.mSmsUsageMonitor); mContext = context; mAppOps = appOps; mUserManager = userManager; mDispatchersController = dispatchersController; } protected void markMessagesAsRead(ArrayList<byte[]> messages) { Loading Loading @@ -323,11 +332,10 @@ public class IccSmsInterfaceManager { */ public void sendDataWithSelfPermissions(String callingPackage, String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { mPhone.getContext().enforceCallingOrSelfPermission( Manifest.permission.SEND_SMS, "Sending SMS message"); sendDataInternal(callingPackage, destAddr, scAddr, destPort, data, sentIntent, deliveryIntent); if (!checkCallingOrSelfSendSmsPermission(callingPackage, "Sending SMS message")) { return; } sendDataInternal(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent); } /** Loading @@ -336,11 +344,10 @@ public class IccSmsInterfaceManager { */ public void sendData(String callingPackage, String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { mPhone.getContext().enforceCallingPermission( Manifest.permission.SEND_SMS, "Sending SMS message"); sendDataInternal(callingPackage, destAddr, scAddr, destPort, data, sentIntent, deliveryIntent); if (!checkCallingSendSmsPermission(callingPackage, "Sending SMS message")) { return; } sendDataInternal(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent); } /** Loading Loading @@ -369,17 +376,13 @@ public class IccSmsInterfaceManager { * raw pdu of the status report is in the extended data ("pdu"). */ private void sendDataInternal(String callingPackage, String destAddr, String scAddr, private void sendDataInternal(String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { if (Rlog.isLoggable("SMS", Log.VERBOSE)) { log("sendData: destAddr=" + destAddr + " scAddr=" + scAddr + " destPort=" + destPort + " data='"+ HexDump.toHexString(data) + "' sentIntent=" + sentIntent + " deliveryIntent=" + deliveryIntent); } if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPackage) != AppOpsManager.MODE_ALLOWED) { return; } destAddr = filterDestAddress(destAddr); mDispatchersController.sendData(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent); Loading @@ -392,9 +395,10 @@ public class IccSmsInterfaceManager { public void sendText(String callingPackage, String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp) { mPhone.getContext().enforceCallingPermission( Manifest.permission.SEND_SMS, "Sending SMS message"); if (!checkCallingSendTextPermissions( persistMessageForNonDefaultSmsApp, callingPackage, "Sending SMS message")) { return; } sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent, persistMessageForNonDefaultSmsApp, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED, false /* expectMore */, SMS_MESSAGE_PERIOD_NOT_SPECIFIED); Loading @@ -407,9 +411,9 @@ public class IccSmsInterfaceManager { public void sendTextWithSelfPermissions(String callingPackage, String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessage) { mPhone.getContext().enforceCallingOrSelfPermission( Manifest.permission.SEND_SMS, "Sending SMS message"); if (!checkCallingOrSelfSendSmsPermission(callingPackage, "Sending SMS message")) { return; } sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent, persistMessage, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED, false /* expectMore */, SMS_MESSAGE_PERIOD_NOT_SPECIFIED); Loading Loading @@ -472,13 +476,6 @@ public class IccSmsInterfaceManager { + " priority=" + priority + " expectMore=" + expectMore + " validityPeriod=" + validityPeriod); } if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPackage) != AppOpsManager.MODE_ALLOWED) { return; } if (!persistMessageForNonDefaultSmsApp) { enforcePrivilegedAppPermissions(); } destAddr = filterDestAddress(destAddr); mDispatchersController.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp, Loading Loading @@ -535,9 +532,9 @@ public class IccSmsInterfaceManager { String text, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp, int priority, boolean expectMore, int validityPeriod) { mPhone.getContext().enforceCallingOrSelfPermission( Manifest.permission.SEND_SMS, "Sending SMS message"); if (!checkCallingOrSelfSendSmsPermission(callingPackage, "Sending SMS message")) { return; } sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent, persistMessageForNonDefaultSmsApp, priority, expectMore, validityPeriod); } Loading @@ -553,7 +550,11 @@ public class IccSmsInterfaceManager { * the same time an SMS received from radio is acknowledged back. */ public void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) { enforcePrivilegedAppPermissions(); if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) { enforceCallerIsImsAppOrCarrierApp("injectSmsPdu"); } if (Rlog.isLoggable("SMS", Log.VERBOSE)) { log("pdu: " + pdu + "\n format=" + format + Loading Loading @@ -658,12 +659,9 @@ public class IccSmsInterfaceManager { String scAddr, List<String> parts, List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp, int priority, boolean expectMore, int validityPeriod) { mPhone.getContext().enforceCallingPermission( Manifest.permission.SEND_SMS, "Sending SMS message"); if (!persistMessageForNonDefaultSmsApp) { // Only allow carrier app or carrier ims to skip auto message persistence. enforcePrivilegedAppPermissions(); if (!checkCallingSendTextPermissions( persistMessageForNonDefaultSmsApp, callingPackage, "Sending SMS message")) { return; } if (Rlog.isLoggable("SMS", Log.VERBOSE)) { int i = 0; Loading @@ -672,10 +670,6 @@ public class IccSmsInterfaceManager { ", part[" + (i++) + "]=" + part); } } if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPackage) != AppOpsManager.MODE_ALLOWED) { return; } destAddr = filterDestAddress(destAddr); Loading Loading @@ -810,13 +804,11 @@ public class IccSmsInterfaceManager { synchronized public boolean enableGsmBroadcastRange(int startMessageId, int endMessageId) { Context context = mPhone.getContext(); context.enforceCallingPermission( mContext.enforceCallingPermission( "android.permission.RECEIVE_SMS", "Enabling cell broadcast SMS"); String client = context.getPackageManager().getNameForUid( String client = mContext.getPackageManager().getNameForUid( Binder.getCallingUid()); if (!mCellBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) { Loading @@ -836,13 +828,11 @@ public class IccSmsInterfaceManager { synchronized public boolean disableGsmBroadcastRange(int startMessageId, int endMessageId) { Context context = mPhone.getContext(); context.enforceCallingPermission( mContext.enforceCallingPermission( "android.permission.RECEIVE_SMS", "Disabling cell broadcast SMS"); String client = context.getPackageManager().getNameForUid( String client = mContext.getPackageManager().getNameForUid( Binder.getCallingUid()); if (!mCellBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) { Loading @@ -862,13 +852,11 @@ public class IccSmsInterfaceManager { synchronized public boolean enableCdmaBroadcastRange(int startMessageId, int endMessageId) { Context context = mPhone.getContext(); context.enforceCallingPermission( mContext.enforceCallingPermission( "android.permission.RECEIVE_SMS", "Enabling cdma broadcast SMS"); String client = context.getPackageManager().getNameForUid( String client = mContext.getPackageManager().getNameForUid( Binder.getCallingUid()); if (!mCdmaBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) { Loading @@ -888,13 +876,11 @@ public class IccSmsInterfaceManager { synchronized public boolean disableCdmaBroadcastRange(int startMessageId, int endMessageId) { Context context = mPhone.getContext(); context.enforceCallingPermission( mContext.enforceCallingPermission( "android.permission.RECEIVE_SMS", "Disabling cell broadcast SMS"); String client = context.getPackageManager().getNameForUid( String client = mContext.getPackageManager().getNameForUid( Binder.getCallingUid()); if (!mCdmaBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) { Loading Loading @@ -1086,17 +1072,14 @@ public class IccSmsInterfaceManager { public void sendStoredText(String callingPkg, Uri messageUri, String scAddress, PendingIntent sentIntent, PendingIntent deliveryIntent) { mPhone.getContext().enforceCallingPermission(Manifest.permission.SEND_SMS, "Sending SMS message"); if (!checkCallingSendSmsPermission(callingPkg, "Sending SMS message")) { return; } if (Rlog.isLoggable("SMS", Log.VERBOSE)) { log("sendStoredText: scAddr=" + scAddress + " messageUri=" + messageUri + " sentIntent=" + sentIntent + " deliveryIntent=" + deliveryIntent); } if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPkg) != AppOpsManager.MODE_ALLOWED) { return; } final ContentResolver resolver = mPhone.getContext().getContentResolver(); final ContentResolver resolver = mContext.getContentResolver(); if (!isFailedOrDraft(resolver, messageUri)) { Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredText: not FAILED or DRAFT message"); returnUnspecifiedFailure(sentIntent); Loading @@ -1117,13 +1100,10 @@ public class IccSmsInterfaceManager { public void sendStoredMultipartText(String callingPkg, Uri messageUri, String scAddress, List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) { mPhone.getContext().enforceCallingPermission(Manifest.permission.SEND_SMS, "Sending SMS message"); if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPkg) != AppOpsManager.MODE_ALLOWED) { if (!checkCallingSendSmsPermission(callingPkg, "Sending SMS message")) { return; } final ContentResolver resolver = mPhone.getContext().getContentResolver(); final ContentResolver resolver = mContext.getContentResolver(); if (!isFailedOrDraft(resolver, messageUri)) { Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredMultipartText: " + "not FAILED or DRAFT message"); Loading Loading @@ -1268,32 +1248,69 @@ public class IccSmsInterfaceManager { } } private void enforceCarrierPrivilege() { UiccController controller = UiccController.getInstance(); if (controller == null || controller.getUiccCard(mPhone.getPhoneId()) == null) { throw new SecurityException("No Carrier Privilege: No UICC"); /** * Check that the caller can send text messages. * * For persisted messages, the caller just needs the SEND_SMS permission. For unpersisted * messages, the caller must either be the IMS app or a carrier-privileged app, or they must * have both the MODIFY_PHONE_STATE and SEND_SMS permissions. * * @throws SecurityException if the caller is missing all necessary permission declaration or * has had a necessary runtime permission revoked. * @return true unless the caller has all necessary permissions but has a revoked AppOps bit. */ @VisibleForTesting public boolean checkCallingSendTextPermissions( boolean persistMessageForNonDefaultSmsApp, String callingPackage, String message) { // TODO(b/75978989): Should we allow IMS/carrier apps for persisted messages as well? if (!persistMessageForNonDefaultSmsApp) { try { enforceCallerIsImsAppOrCarrierApp(message); // No need to also check SEND_SMS. return true; } catch (SecurityException e) { mContext.enforceCallingPermission( android.Manifest.permission.MODIFY_PHONE_STATE, message); } if (controller.getUiccCard(mPhone.getPhoneId()).getCarrierPrivilegeStatusForCurrentTransaction( mContext.getPackageManager()) != TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { throw new SecurityException("No Carrier Privilege."); } return checkCallingSendSmsPermission(callingPackage, message); } /** * Enforces that the caller has {@link android.Manifest.permission#MODIFY_PHONE_STATE} * permission or is one of the following apps: * Check that the caller (or self, if this is not an IPC) has SEND_SMS permissions. * * @throws SecurityException if the caller is missing the permission declaration or has had the * permission revoked at runtime. * @return whether the caller has the OP_SEND_SMS AppOps bit. */ private boolean checkCallingOrSelfSendSmsPermission(String callingPackage, String message) { mContext.enforceCallingOrSelfPermission(Manifest.permission.SEND_SMS, message); return mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED; } /** * Check that the caller has SEND_SMS permissions. Can only be called during an IPC. * * @throws SecurityException if the caller is missing the permission declaration or has had the * permission revoked at runtime. * @return whether the caller has the OP_SEND_SMS AppOps bit. */ private boolean checkCallingSendSmsPermission(String callingPackage, String message) { mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, message); return mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED; } /** * Enforces that the caller is one of the following apps: * <ul> * <li> IMS App * <li> Carrier App * </ul> */ private void enforcePrivilegedAppPermissions() { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) { return; } @VisibleForTesting public void enforceCallerIsImsAppOrCarrierApp(String message) { int callingUid = Binder.getCallingUid(); String carrierImsPackage = CarrierSmsUtils.getCarrierImsPackageForIntent(mContext, mPhone, new Intent(CarrierMessagingService.SERVICE_INTERFACE)); Loading @@ -1309,7 +1326,7 @@ public class IccSmsInterfaceManager { } } enforceCarrierPrivilege(); TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mPhone.getSubId(), message); } private String filterDestAddress(String destAddr) { Loading
tests/telephonytests/src/com/android/internal/telephony/IccSmsInterfaceManagerTest.java 0 → 100644 +175 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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.internal.telephony; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.Manifest; import android.app.AppOpsManager; import android.content.Context; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; import android.os.UserManager; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class IccSmsInterfaceManagerTest { private static final String PACKAGE = "com.example.package"; private static final String MESSAGE = "msg"; private HandlerThread mHandlerThread; @Mock private Phone mMockPhone; @Mock private Context mMockContext; @Mock private AppOpsManager mMockAppOps; @Mock private UserManager mMockUserManager; @Mock private SmsDispatchersController mMockDispatchersController; private IccSmsInterfaceManager mIccSmsInterfaceManager; private boolean mCallerHasCarrierPrivileges; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mHandlerThread = new HandlerThread("IccSmsInterfaceManagerTest"); mHandlerThread.start(); final CountDownLatch initialized = new CountDownLatch(1); new Handler(mHandlerThread.getLooper()).post(() -> { mIccSmsInterfaceManager = new IccSmsInterfaceManager( mMockPhone, mMockContext, mMockAppOps, mMockUserManager, mMockDispatchersController) { @Override public void enforceCallerIsImsAppOrCarrierApp(String message) { if (!mCallerHasCarrierPrivileges) { throw new SecurityException(message); } } }; initialized.countDown(); }); // Wait for object to initialize. if (!initialized.await(30, TimeUnit.SECONDS)) { fail("Could not initialize IccSmsInterfaceManager"); } } @After public void tearDown() throws Exception { mHandlerThread.quit(); } @Test public void testCheckCallingSendTextPermissions_persist_grant() { assertTrue(mIccSmsInterfaceManager.checkCallingSendTextPermissions( true /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE)); } @Test public void testCheckCallingSendTextPermissions_persist_noGrant() { Mockito.doThrow(new SecurityException(MESSAGE)).when(mMockContext) .enforceCallingPermission(Manifest.permission.SEND_SMS, MESSAGE); try { mIccSmsInterfaceManager.checkCallingSendTextPermissions( true /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE); fail(); } catch (SecurityException e) { // expected } } @Test public void testCheckCallingSendTextPermissions_persist_noAppOps() { Mockito.when(mMockAppOps.noteOp( AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), PACKAGE)) .thenReturn(AppOpsManager.MODE_ERRORED); assertFalse(mIccSmsInterfaceManager.checkCallingSendTextPermissions( true /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE)); } @Test public void testCheckCallingSendTextPermissions_noPersist_grantViaCarrierApp() { mCallerHasCarrierPrivileges = true; // Other permissions shouldn't matter. Mockito.doThrow(new SecurityException(MESSAGE)).when(mMockContext) .enforceCallingPermission(Manifest.permission.MODIFY_PHONE_STATE, MESSAGE); Mockito.doThrow(new SecurityException(MESSAGE)).when(mMockContext) .enforceCallingPermission(Manifest.permission.SEND_SMS, MESSAGE); Mockito.when(mMockAppOps.noteOp( AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), PACKAGE)) .thenReturn(AppOpsManager.MODE_ERRORED); assertTrue(mIccSmsInterfaceManager.checkCallingSendTextPermissions( false /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE)); } @Test public void testCheckCallingSendTextPermissions_noPersist_grantViaModifyAndSend() { assertTrue(mIccSmsInterfaceManager.checkCallingSendTextPermissions( false /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE)); } @Test public void testCheckCallingSendTextPermissions_noPersist_noModify() { Mockito.doThrow(new SecurityException(MESSAGE)).when(mMockContext) .enforceCallingPermission(Manifest.permission.MODIFY_PHONE_STATE, MESSAGE); try { mIccSmsInterfaceManager.checkCallingSendTextPermissions( false /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE); fail(); } catch (SecurityException e) { // expected } } @Test public void testCheckCallingSendTextPermissions_noPersist_noSendSmsPermission() { Mockito.doThrow(new SecurityException(MESSAGE)).when(mMockContext) .enforceCallingPermission(Manifest.permission.SEND_SMS, MESSAGE); try { mIccSmsInterfaceManager.checkCallingSendTextPermissions( false /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE); fail(); } catch (SecurityException e) { // expected } } @Test public void testCheckCallingSendTextPermissions_noPersist_noAppOps() { Mockito.when(mMockAppOps.noteOp( AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), PACKAGE)) .thenReturn(AppOpsManager.MODE_ERRORED); assertFalse(mIccSmsInterfaceManager.checkCallingSendTextPermissions( false /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE)); } }