blob: 55de517821b73cee333a4dd6f67aff520a04c1fe [file] [log] [blame]
/*
* Copyright (C) 2019 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 android.Manifest;
import android.app.AppOpsManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.service.carrier.CarrierMessagingService;
import com.android.internal.annotations.VisibleForTesting;
import com.android.telephony.Rlog;
/**
* Permissions checks for SMS functionality
*/
public class SmsPermissions {
static final String LOG_TAG = "SmsPermissions";
@UnsupportedAppUsage
private final Phone mPhone;
@UnsupportedAppUsage
private final Context mContext;
@UnsupportedAppUsage
private final AppOpsManager mAppOps;
public SmsPermissions(Phone phone, Context context, AppOpsManager appOps) {
mPhone = phone;
mContext = context;
mAppOps = appOps;
}
/**
* 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.
*/
public boolean checkCallingCanSendText(
boolean persistMessageForNonDefaultSmsApp, String callingPackage,
String callingAttributionTag, 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);
}
}
return checkCallingCanSendSms(callingPackage, callingAttributionTag, message);
}
/**
* Enforces that the caller is one of the following apps:
* <ul>
* <li> IMS App
* <li> Carrier App
* </ul>
*/
public void enforceCallerIsImsAppOrCarrierApp(String message) {
String carrierImsPackage = CarrierSmsUtils.getCarrierImsPackageForIntent(mContext,
mPhone, new Intent(CarrierMessagingService.SERVICE_INTERFACE));
if (carrierImsPackage != null && packageNameMatchesCallingUid(carrierImsPackage)) {
return;
}
TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
mContext, mPhone.getSubId(), message);
}
/**
* 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.
*/
public boolean checkCallingCanSendSms(String callingPackage, String callingAttributionTag,
String message) {
mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, message);
return mAppOps.noteOp(AppOpsManager.OPSTR_SEND_SMS, Binder.getCallingUid(), callingPackage,
callingAttributionTag, null) == AppOpsManager.MODE_ALLOWED;
}
/**
* 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.
*/
public boolean checkCallingOrSelfCanSendSms(String callingPackage, String callingAttributionTag,
String message) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.SEND_SMS, message);
return mAppOps.noteOp(AppOpsManager.OPSTR_SEND_SMS, Binder.getCallingUid(), callingPackage,
callingAttributionTag, null)
== AppOpsManager.MODE_ALLOWED;
}
/**
* Check that the caller (or self, if this is not an IPC) can get SMSC address from (U)SIM.
*
* The default SMS application can get SMSC address, otherwise the caller must have
* {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or carrier privileges.
*
* @return true if the caller is default SMS app or has the required permission and privileges.
* Otherwise, false;
*/
public boolean checkCallingOrSelfCanGetSmscAddress(String callingPackage, String message) {
// Allow it to the default SMS app always.
if (!isCallerDefaultSmsPackage(callingPackage)) {
TelephonyPermissions
.enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
mContext, mPhone.getSubId(), message);
}
return true;
}
/**
* Check that the caller (or self, if this is not an IPC) can set SMSC address on (U)SIM.
*
* The default SMS application can set SMSC address, otherwise the caller must have
* {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier privileges.
*
* @return true if the caller is default SMS app or has the required permission and privileges.
* Otherwise, false.
*/
public boolean checkCallingOrSelfCanSetSmscAddress(String callingPackage, String message) {
// Allow it to the default SMS app always.
if (!isCallerDefaultSmsPackage(callingPackage)) {
// Allow it with MODIFY_PHONE_STATE or Carrier Privileges
TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
mContext, mPhone.getSubId(), message);
}
return true;
}
/** Check if a package is default SMS app. */
@VisibleForTesting
public boolean isCallerDefaultSmsPackage(String packageName) {
if (packageNameMatchesCallingUid(packageName)) {
return SmsApplication.isDefaultSmsApplication(mContext, packageName);
}
return false;
}
/**
* Check if the passed in packageName belongs to the calling uid.
* @param packageName name of the package to check
* @return true if package belongs to calling uid, false otherwise
*/
@VisibleForTesting
public boolean packageNameMatchesCallingUid(String packageName) {
try {
((AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE))
.checkPackage(Binder.getCallingUid(), packageName);
// If checkPackage doesn't throw an exception then we are the given package
return true;
} catch (SecurityException e) {
return false;
}
}
@UnsupportedAppUsage
protected void log(String msg) {
Rlog.d(LOG_TAG, msg);
}
protected void loge(String msg) {
Rlog.e(LOG_TAG, msg);
}
protected void loge(String msg, Throwable e) {
Rlog.e(LOG_TAG, msg, e);
}
}