应用偷偷在后台发送短信,并让用户无法感知,这种做法虽然"鸡贼”,但是从技术角度上来说,值得我们探究一番。项目有需求的可以借鉴如下方法,业余者可以围观此种“鸡贼”方式是如何耗掉您一毛钱的短信费用,却让你毫无知觉的。下面开始介绍MTK平台、展讯平台上如何过滤。
MTK平台上如何过滤掉应用发送的短信记录?
是否插入数据库是由MMS应用来负责的。MTK平台上位于framework下的SmsApplication.java类,我们可以通过以下方式屏蔽:
/**
* Returns whether need to write the SMS message to SMS database for this package.
* <p>
* Caller must pass in the correct user context if calling from a singleton service.
*/
public static boolean shouldWriteMessageForPackage(String packageName, Context context) {
//Start:判断包名,返回false屏蔽短信.例如com.ape....就是我的包名
if ((packageName != null) && (packageName.equals("com.ape.saletracker") || packageName.equals("com.ape.saletrackerPK") || packageName.equals("com.ape.saletrackerBD"))) {
Log.d(LOG_TAG, "don't save msg for sts");
return false;
}
//End:判断包名,返回false屏蔽短信
if (SmsManager.getDefault().getAutoPersisting()) {
return true;
}
return !isDefaultSmsApplication(context, packageName);
}
注:其实通过上述方法的注释“ Returns whether need to write the SMS message to SMS database for this package.” 我们也可以知道,此方法是判断是否将该应用发送短信的记录写入数据库中。
展讯平台上如何过滤掉应用发送的短信记录?
展讯平台Android P上类似的需要在上述SmsApplication.java中添加过滤的包名之外,还存在一点点小小的bug,需要通过如下方式修复一下,才能正常的过滤包名。在展讯P项目上,按照上述方式添加之后,通过log我发现能过滤短信是概率性的,说明包名的获取还存在缺陷。bug修复位于SMSDispatcher.java中:
1)修改点1:
/**
* Send an SMS
* @param tracker will contain:
* -smsc the SMSC to send the message through, or NULL for the
* default SMSC
* -pdu the raw PDU to send
* -sentIntent if not NULL this <code>Intent</code> is
* broadcast when the message is successfully sent, or failed.
* The result code will be <code>Activity.RESULT_OK<code> for success,
* or one of these errors:
* <code>RESULT_ERROR_GENERIC_FAILURE</code>
* <code>RESULT_ERROR_RADIO_OFF</code>
* <code>RESULT_ERROR_NULL_PDU</code>
* <code>RESULT_ERROR_NO_SERVICE</code>.
* The per-application based SMS control checks sentIntent. If sentIntent
* is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
* -deliveryIntent if not NULL this <code>Intent</code> is
* broadcast when the message is delivered to the recipient. The
* raw pdu of the status report is in the extended data ("pdu").
* -param destAddr the destination phone number (for short code confirmation)
*/
@VisibleForTesting
public void sendRawPdu(SmsTracker tracker) {
HashMap map = tracker.getData();
byte pdu[] = (byte[]) map.get(MAP_KEY_PDU);
if (mSmsSendDisabled) {
Rlog.e(TAG, "Device does not support sending sms.");
tracker.onFailed(mContext, RESULT_ERROR_NO_SERVICE, 0/*errorCode*/);
return;
}
if(checktheOperatorIsThailand()){
if((judgeTrueSIM2(mPhone,mContext)==NOT_IN_THAILAND)||(judgeTrueSIM2(mPhone,mContext)==NOT_TRUE_SIM_IN_THAILAND)){
Log.w(TAG,"the number does not support sending sms");
tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/);
return;
}
}
if (pdu == null) {
Rlog.e(TAG, "Empty PDU");
tracker.onFailed(mContext, RESULT_ERROR_NULL_PDU, 0/*errorCode*/);
return;
}
// Get calling app package name via UID from Binder call
PackageManager pm = mContext.getPackageManager();
String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid());
if (packageNames == null || packageNames.length == 0) {
// Refuse to send SMS if we can't get the calling package name.
Rlog.e(TAG, "Can't get calling app package name: refusing to send SMS");
tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/);
return;
}
// Start:过滤短信添加代码1
String packageName = getPackageNameByProcessId(packageNames);
if (packageName != null) {
packageNames[0] = packageName;
}
Rlog.d(TAG, "sendRawPdu show the package name by process id: " + packageNames[0]);
// End:过滤短信添加代码1
// Get package info via packagemanager
PackageInfo appInfo;
try {
// XXX this is lossy- apps can share a UID
appInfo = pm.getPackageInfoAsUser(
packageNames[0], PackageManager.GET_SIGNATURES, tracker.mUserId);
} catch (PackageManager.NameNotFoundException e) {
Rlog.e(TAG, "Can't get calling app package info: refusing to send SMS");
tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/);
return;
}
// checkDestination() returns true if the destination is not a premium short code or the
// sending app is approved to send to short codes. Otherwise, a message is sent to our
// handler with the SmsTracker to request user confirmation before sending.
if (checkDestination(tracker)) {
// check for excessive outgoing SMS usage by this app
if (!mSmsDispatchersController.getUsageMonitor().check(
appInfo.packageName, SINGLE_PART_SMS)) {
sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker));
return;
}
sendSms(tracker);
}
if (PhoneNumberUtils.isLocalEmergencyNumber(mContext, tracker.mDestAddress)) {
new AsyncEmergencyContactNotifier(mContext).execute();
}
}
2)修改点2,重新定义方法getPackageNameByProcessId(),这个方法返回的包名,即是在SmsApplication.java中传入的包名,保持一致,既不会出现概率性可以过滤短信的bug.
private String getPackageNameByProcessId(String[] packageNames) {
String packageName = null;
if (packageNames.length == 1) {
packageName = packageNames[0];
} else if (packageNames.length > 1) {
int callingPid = Binder.getCallingPid();
Iterator index = null;
ActivityManager am = (ActivityManager) mContext.getSystemService(
Context.ACTIVITY_SERVICE);
List processList = am.getRunningAppProcesses();
if (processList != null)
{
index = processList.iterator();
while (index.hasNext()) {
ActivityManager.RunningAppProcessInfo processInfo =
(ActivityManager.RunningAppProcessInfo) (index.next());
if (callingPid == processInfo.pid) {
if(processInfo.processName != null && processInfo.processName.equals("system")){
packageName = packageNames[0];
}else{
packageName = processInfo.processName;
}
break;
}
}
}
}
return packageName;
}