关于android4.4及以上版本无法恢复还原短信的问题记录与解决

来源:互联网 发布:西安软件培训班 编辑:程序博客网 时间:2024/05/08 01:38
 前段时间在公司项目有系统短信备份和恢复的功能,在4.4(也就是API 19)以下的版本一点问题没有,很简单,没啥好说的,但是在4.4以上以及5.1由于系统更新了 SMS 的部分API,加强了权限控制,因此现在只有default SMS app才能对短信数据库有写权限,但是用户可以把第三方应用设置为default SMS app。也就是说非default SMS app也能读写短信,只不过是不能写入短信数据库中,这也就直接导致在4.4以上短信无法恢复。没办法,功能肯定还得完成,于是在我在搜索研究后发现了如下解决办法,希望对同样遇到这个问题的人有所帮助。

     首先,在4.4以上和5.0以下可以利用权限管理功能(Application Operations)也就是来默认开启写短信的权限,但是坑爹的问题又来了,这个功能被谷歌给隐藏了,因此只能使用反射来搞定,具体做法如下:

     一、检查写入短信权限是否已开启,因为有的国产手机,比如魅族,由于它们定制过系统,所以很有可能已经默认开启这个权限了。反射调用AppOpsManager类里的函数 int checkOp(int op, int uid, String packageName),其中op为15就是代表短信写入权限,代码如下:

[java] view plain copy
  1. @TargetApi(Build.VERSION_CODES.KITKAT)  
  2. private int checkMode(){  
  3.       AppOpsManager appOps = (AppOpsManager)getSystemService(Context.APP_OPS_SERVICE);  
  4.       Class c = appOps.getClass();  
  5.       try {            
  6.           Class[] cArg = new Class[3];  
  7.           cArg[0] = int.class;  
  8.           cArg[1] = int.class;  
  9.           cArg[2] = String.class;  
  10.           Method lMethod = c.getDeclaredMethod("checkOp", cArg);  
  11.           return (Integer) lMethod.invoke(appOps, 15, Binder.getCallingUid(), getPackageName());  
  12.       } catch(NoSuchMethodException e) {  
  13.           e.printStackTrace();  
  14.       } catch (IllegalAccessException e) {  
  15.           e.printStackTrace();  
  16.       } catch (IllegalArgumentException e) {  
  17.           e.printStackTrace();  
  18.       } catch (InvocationTargetException e) {  
  19.           e.printStackTrace();  
  20.       }          
  21.       return -1;  
  22.   }  

    二、上述checkMode()方法,返回 0 就代表有权限,1代表没有权限,-1代表函数出错了。此时如果返回0,表示没有开启,同样反射调用setMode函数,方法如下:

[java] view plain copy
  1. private boolean setMode(){  
  2.    AppOpsManager appOps = (AppOpsManager)getSystemService(Context.APP_OPS_SERVICE);  
  3.    Class c = appOps.getClass();  
  4.    Class[] cArg = new Class[4];  
  5.    cArg[0] = int.class;  
  6.    cArg[1] = int.class;  
  7.    cArg[2] = String.class;  
  8.    cArg[3] = int.class;  
  9.    Method lMethod;  
  10.    try {  
  11.        lMethod = c.getDeclaredMethod("setMode", cArg);  
  12.        lMethod.invoke(appOps, 15, Binder.getCallingUid(), getPackageName(),AppOpsManager.MODE_ALLOWED);  
  13.        return true;  
  14.    } catch (NoSuchMethodException e) {  
  15.        e.printStackTrace();  
  16.    } catch (IllegalAccessException e) {  
  17.        e.printStackTrace();  
  18.    } catch (IllegalArgumentException e) {  
  19.        e.printStackTrace();  
  20.    } catch (InvocationTargetException e) {  
  21.        e.printStackTrace();  
  22.    }  
  23.    return false;  

        至此,在4.4以上以及5.0以下的就又可以愉快的恢复短信,写入短信到短信数据库了,细心的朋友可以注意到了,这个是5.0以下能用,没错,非常不幸,在5.0以上这种方法又行不通了(此刻内心早已经把谷歌祖宗八代都默默问候了下),没办法,继续折腾,最后发现在5.0以上只能通过弹框来让用户选择默认短信应用,临时的设置自己的应用为Default SMS app,临时获取一次写入短信数据库数据能力,等短信恢复完成再改回原来的应用为Default SMS app,就是类似这种:

                                     

   做法如下:

      1、获取默认App的包名并保存。

[java] view plain copy
  1. String defaultSmsApp = Telephony.Sms.getDefaultSmsPackage(context);  

    2、让用户修改你的app为Default SMS app。

[java] view plain copy
  1. Intent intent = new Intent(context, Sms.Intents.ACTION_CHANGE_DEFAULT);    
  2. intent.putExtra(Sms.Intents.EXTRA_PACKAGE_NAME, context.getPackageName());    
  3. startActivity(intent);  

  3、恢复完短信,再让用户修改回Default SMS app,使用第一步保存的包名。

[java] view plain copy
  1. Intent intent = new Intent(context, Sms.Intents.ACTION_CHANGE_DEFAULT);    
  2. intent.putExtra(Sms.Intents.EXTRA_PACKAGE_NAME, defaultSmsApp);    
  3. startActivity(intent);  

    接下来,要将自己的应用设置为默认短信应用必须按照下面的步骤来,一样都不能少,不然成功不了,操作如下:

    1、首先在清单文件里做如下配置(一个都不能少):

[java] view plain copy
  1.  <receiver android:name="com.boy.pro.defaultsms.SmsReceiver"    
  2.          android:permission="android.permission.BROADCAST_SMS">    
  3.       <intent-filter>    
  4.           <action android:name="android.provider.Telephony.SMS_DELIVER" />    
  5.       </intent-filter>    
  6.  </receiver>    
  7.   <receiver android:name="com.boy.pro.defaultsms.MmsReceiver"  
  8.     android:permission="android.permission.BROADCAST_WAP_PUSH">  
  9.     <intent-filter>  
  10.         <action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />  
  11.         <data android:mimeType="application/vnd.wap.mms-message" />  
  12.     </intent-filter>  
  13. </receiver>  
  14.     
  15.  <activity android:name="com.boy.pro.defaultsms.SmsActivity" >  
  16.      <intent-filter>  
  17.             <action android:name="android.intent.action.SEND" />                  
  18.             <action android:name="android.intent.action.SENDTO" />  
  19.             <category android:name="android.intent.category.DEFAULT" />  
  20.             <category android:name="android.intent.category.BROWSABLE" />  
  21.             <data android:scheme="sms" />  
  22.             <data android:scheme="smsto" />  
  23.             <data android:scheme="mms" />  
  24.             <data android:scheme="mmsto" />  
  25.         </intent-filter>  
  26.   </activity>           
  27.  <service android:name="com.boy.pro.defaultsms.SmsService"  
  28.          android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"  
  29.          android:exported="true" >  
  30.     <intent-filter>  
  31.         <action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />  
  32.         <category android:name="android.intent.category.DEFAULT" />  
  33.         <data android:scheme="sms" />  
  34.         <data android:scheme="smsto" />  
  35.         <data android:scheme="mms" />  
  36.         <data android:scheme="mmsto" />  
  37.     </intent-filter>  
  38. </service>  

    2、然后按照清单文件里的新建对应的类,类里面可以什么都不用写,如下图:




 至此,写入短信到短信数据库,恢复短信的功能就完成了,有需要的人拿去吧!

0 0