228 lines
9.4 KiB
Java
228 lines
9.4 KiB
Java
/*
|
|
* 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.os.Build;
|
|
import android.os.UserHandle;
|
|
import android.service.carrier.CarrierMessagingService;
|
|
|
|
import com.android.internal.annotations.VisibleForTesting;
|
|
import com.android.internal.telephony.util.TelephonyUtils;
|
|
import com.android.telephony.Rlog;
|
|
|
|
/**
|
|
* Permissions checks for SMS functionality
|
|
*/
|
|
public class SmsPermissions {
|
|
static final String LOG_TAG = "SmsPermissions";
|
|
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
|
private final Phone mPhone;
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
|
private final Context mContext;
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
|
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 determined by telephony to implement RCS features
|
|
* <li> Carrier App
|
|
* </ul>
|
|
*/
|
|
public void enforceCallerIsImsAppOrCarrierApp(String message) {
|
|
String imsRcsPackage = CarrierSmsUtils.getImsRcsPackageForIntent(mContext,
|
|
mPhone, new Intent(CarrierMessagingService.SERVICE_INTERFACE));
|
|
if (imsRcsPackage != null && packageNameMatchesCallingUid(imsRcsPackage)) {
|
|
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.
|
|
boolean isDefaultSmsPackage;
|
|
int callerUid = Binder.getCallingUid();
|
|
final long identity = Binder.clearCallingIdentity();
|
|
try {
|
|
isDefaultSmsPackage = isCallerDefaultSmsPackage(callingPackage, callerUid);
|
|
} finally {
|
|
Binder.restoreCallingIdentity(identity);
|
|
}
|
|
if (!isDefaultSmsPackage) {
|
|
Rlog.d(LOG_TAG, "Caller is not a default SMS application");
|
|
TelephonyPermissions.
|
|
enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
|
|
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.
|
|
boolean isDefaultSmsPackage;
|
|
int callerUid = Binder.getCallingUid();
|
|
final long identity = Binder.clearCallingIdentity();
|
|
try {
|
|
isDefaultSmsPackage = isCallerDefaultSmsPackage(callingPackage, callerUid);
|
|
} finally {
|
|
Binder.restoreCallingIdentity(identity);
|
|
}
|
|
if (!isDefaultSmsPackage) {
|
|
Rlog.d(LOG_TAG, "Caller is not a default SMS application");
|
|
// 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, int callerUid) {
|
|
if (packageNameMatchesCallingUid(packageName, callerUid)) {
|
|
UserHandle userHandle = TelephonyUtils.getSubscriptionUserHandle(mContext,
|
|
mPhone.getSubId());
|
|
return SmsApplication.isDefaultSmsApplicationAsUser(mContext, packageName, userHandle);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@VisibleForTesting
|
|
public boolean packageNameMatchesCallingUid(String packageName) {
|
|
return packageNameMatchesCallingUid(packageName, Binder.getCallingUid());
|
|
}
|
|
|
|
/**
|
|
* 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, int callerUid) {
|
|
try {
|
|
((AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE))
|
|
.checkPackage(callerUid, packageName);
|
|
// If checkPackage doesn't throw an exception then we are the given package
|
|
return true;
|
|
} catch (SecurityException e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
|
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);
|
|
}
|
|
}
|