前段時間在公司項目有系統短信備份和恢復的功能,在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>
二、而後按照清單文件裏的新建對應的類,類裏面能夠什麼都不用寫,以下圖:
至此,寫入短信到短信數據庫,恢復短信的功能就完成了,有須要的人拿去吧!
若有轉載,請註明出處,原創不易!