關於android4.4及以上版本沒法恢復還原短信的問題記錄與解決

    前段時間在公司項目有系統短信備份和恢復的功能,在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>

 二、而後按照清單文件裏的新建對應的類,類裏面能夠什麼都不用寫,以下圖:


 至此,寫入短信到短信數據庫,恢復短信的功能就完成了,有須要的人拿去吧!

若有轉載,請註明出處,原創不易!

相關文章
相關標籤/搜索