前段时间在公司项目有系统短信备份和恢复的功能,在4.4(也就是API 19)如下的版本一点问题没有,很简单,没啥好说的,可是在4.4以上以及5.1因为系统更新了 SMS 的部分API,增强了权限控制,所以如今只有default SMS app才能对短信数据库有写权限,可是用户能够把第三方应用设置为default SMS app。也就是说非default SMS app也能读写短信,只不过是不能写入短信数据库中,这也就直接致使在4.4以上短信没法恢复。没办法,功能确定还得完成,因而在我在搜索研究后发现了以下解决办法,但愿对一样遇到这个问题的人有所帮助。java
首先,在4.4以上和5.0如下能够利用权限管理功能(Application Operations)也就是来默认开启写短信的权限,可是坑爹的问题又来了,这个功能被谷歌给隐藏了,所以只能使用反射来搞定,具体作法以下:android
1、检查写入短信权限是否已开启,由于有的国产手机,好比魅族,因为它们定制过系统,因此颇有可能已经默认开启这个权限了。反射调用AppOpsManager类里的函数 int checkOp(int op, int uid, String packageName),其中op为15就是表明短信写入权限,代码以下:
数据库
@TargetApi(Build.VERSION_CODES.KITKAT) private int checkMode(){ AppOpsManager appOps = (AppOpsManager)getSystemService(Context.APP_OPS_SERVICE); Class c = appOps.getClass(); try { Class[] cArg = new Class[3]; cArg[0] = int.class; cArg[1] = int.class; cArg[2] = String.class; Method lMethod = c.getDeclaredMethod("checkOp", cArg); return (Integer) lMethod.invoke(appOps, 15, Binder.getCallingUid(), getPackageName()); } catch(NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return -1; }
2、上述checkMode()方法,返回 0 就表明有权限,1表明没有权限,-1表明函数出错了。此时若是返回0,表示没有开启,一样反射调用setMode函数,方法以下:
app
private boolean setMode(){ AppOpsManager appOps = (AppOpsManager)getSystemService(Context.APP_OPS_SERVICE); Class c = appOps.getClass(); Class[] cArg = new Class[4]; cArg[0] = int.class; cArg[1] = int.class; cArg[2] = String.class; cArg[3] = int.class; Method lMethod; try { lMethod = c.getDeclaredMethod("setMode", cArg); lMethod.invoke(appOps, 15, Binder.getCallingUid(), getPackageName(),AppOpsManager.MODE_ALLOWED); return true; } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return false; }
至此,在4.4以上以及5.0如下的就又能够愉快的恢复短信,写入短信到短信数据库了,细心的朋友能够注意到了,这个是5.0如下能用,没错,很是不幸,在5.0以上这种方法又行不通了(此刻心里早已经把谷歌祖宗八代都默默问候了下),没办法,继续折腾,最后发如今5.0以上只能经过弹框来让用户选择默认短信应用,临时的设置本身的应用为Default SMS app,临时获取一次写入短信数据库数据能力,等短信恢复完成再改回原来的应用为Default SMS app。,就是相似这种:ide
作法以下:函数
一、获取默认App的包名并保存。ui
String defaultSmsApp = Telephony.Sms.getDefaultSmsPackage(context);
二、让用户修改你的app为Default SMS app。spa
Intent intent = new Intent(context, Sms.Intents.ACTION_CHANGE_DEFAULT); intent.putExtra(Sms.Intents.EXTRA_PACKAGE_NAME, context.getPackageName()); startActivity(intent);
三、恢复完短信,再让用户修改回Default SMS app,使用第一步保存的包名。code
Intent intent = new Intent(context, Sms.Intents.ACTION_CHANGE_DEFAULT); intent.putExtra(Sms.Intents.EXTRA_PACKAGE_NAME, defaultSmsApp); startActivity(intent);
接下来,要将本身的应用设置为默认短信应用必须按照下面的步骤来,同样都不能少,否则成功不了,操做以下:get
一、首先在清单文件里作以下配置(一个都不能少):
<receiver android:name="com.boy.pro.defaultsms.SmsReceiver" android:permission="android.permission.BROADCAST_SMS"> <intent-filter> <action android:name="android.provider.Telephony.SMS_DELIVER" /> </intent-filter> </receiver> <receiver android:name="com.boy.pro.defaultsms.MmsReceiver" android:permission="android.permission.BROADCAST_WAP_PUSH"> <intent-filter> <action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" /> <data android:mimeType="application/vnd.wap.mms-message" /> </intent-filter> </receiver> <activity android:name="com.boy.pro.defaultsms.SmsActivity" > <intent-filter> <action android:name="android.intent.action.SEND" /> <action android:name="android.intent.action.SENDTO" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="sms" /> <data android:scheme="smsto" /> <data android:scheme="mms" /> <data android:scheme="mmsto" /> </intent-filter> </activity> <service android:name="com.boy.pro.defaultsms.SmsService" android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE" android:exported="true" > <intent-filter> <action android:name="android.intent.action.RESPOND_VIA_MESSAGE" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="sms" /> <data android:scheme="smsto" /> <data android:scheme="mms" /> <data android:scheme="mmsto" /> </intent-filter> </service>
二、而后按照清单文件里的新建对应的类,类里面能够什么都不用写,以下图:
至此,写入短信到短信数据库,恢复短信的功能就完成了,有须要的人拿去吧!
若有转载,请注明出处,原创不易!