这段时间稍微有点空闲,把前一段学习Android做过的一些小项目整理整理。虽然没有什么工程量很大的项目,但是对于一个新手,解决这些问题还是花了一段时间。感觉还是非常有记录的意义呢~~~么么哒*—*
今天要说明的这个项目,是要在Android手机里伪造一条短信,也就是在短信箱中插入一条自定义的短信,看上去就像自己的手机里收到了新的信息,但其实这并不是一条通过通信运营商的网络发过来的信息,这是一条假的信息。
在Android4.4之前的版本,往短信箱插入信息很方便,所以这个对用户来说很有威胁的漏洞,在Android4.4得到了修复。Android4.4中只有手机默认的消息App才能处理和短信相关的操作,而手机默认的消息App一般就是手机里自带的官方App,当然用户可以在设置里面,手动地将自己信任的消息App设置为默认App,总的来说,短信的操作控制权掌握到用户自己的手中。
那如何自己实现一个处理消息的App呢?可以参考这篇文章。让你的短信应用迎接Android 4.4(KitKat)。这篇文章是根据Google给出的官方文档翻译下来的。里面对消息App的必须的实现细节已经做了详细的说明。
下面是我自己用到的时候具体的代码:
MainActivity.java
1 package tina.messagebox; 2 3 import android.content.ContentResolver; 4 import android.content.ContentUris; 5 import android.content.ContentValues; 6 import android.content.Intent; 7 import android.database.Cursor; 8 import android.net.Uri; 9 import android.provider.Telephony; 10 import android.support.v7.app.ActionBarActivity; 11 import android.os.Bundle; 12 import android.text.method.MovementMethod; 13 import android.text.method.ScrollingMovementMethod; 14 import android.view.Menu; 15 import android.view.MenuItem; 16 import android.view.View; 17 import android.widget.Button; 18 import android.widget.EditText; 19 import android.widget.TextView; 20 import android.widget.Toast; 21 22 import java.text.SimpleDateFormat; 23 import java.util.Date; 24 25 26 public class MainActivity extends ActionBarActivity { 27 private String defaultSmsPkg; 28 private String mySmsPkg; 29 private TextView mMessageView=null; 30 private EditText mPhoneNumber=null; 31 private EditText mMsg=null; 32 33 @Override 34 protected void onCreate(Bundle savedInstanceState) { 35 super.onCreate(savedInstanceState); 36 setContentView(R.layout.activity_main); 37 38 mPhoneNumber=(EditText)findViewById(R.id.editText); 39 mMsg=(EditText)findViewById(R.id.editText2); 40 mMessageView=(TextView)findViewById(R.id.textView3); 41 mMessageView.setMovementMethod(ScrollingMovementMethod.getInstance()); //设置滚动 42 defaultSmsPkg= Telephony.Sms.getDefaultSmsPackage(this); 43 mySmsPkg= this.getPackageName(); 44 45 if(!defaultSmsPkg.equals(mySmsPkg)){ 46 // 如果这个App不是默认的Sms App,则修改成默认的SMS APP 47 // 因为从Android 4.4开始,只有默认的SMS APP才能对SMS数据库进行处理 48 Intent intent=new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT); 49 intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME,mySmsPkg); 50 startActivity(intent); 51 } 52 53 Button button=(Button)findViewById(R.id.button); 54 button.setOnClickListener(new View.OnClickListener() { 55 @Override 56 public void onClick(View v) { 57 if(mySmsPkg.equals(Telephony.Sms.getDefaultSmsPackage(MainActivity.this))){ 58 if(mPhoneNumber.getText().toString().isEmpty()){ 59 Toast.makeText(MainActivity.this,"Phone number cannot be empty!",Toast.LENGTH_LONG).show(); 60 return; 61 } 62 if(mMsg.getText().toString().isEmpty()){ 63 Toast.makeText(MainActivity.this,"Message cannot be empty!",Toast.LENGTH_LONG).show(); 64 return; 65 } 66 System.out.println("My App is default SMS App."); 67 // 对短信数据库进行处理 68 ContentResolver resolver=getContentResolver(); 69 70 ContentValues values=new ContentValues(); 71 values.put(Telephony.Sms.ADDRESS,mPhoneNumber.getText().toString()); 72 values.put(Telephony.Sms.DATE, System.currentTimeMillis()); 73 long dateSent=System.currentTimeMillis()-5000; 74 values.put(Telephony.Sms.DATE_SENT,dateSent); 75 values.put(Telephony.Sms.READ,false); 76 values.put(Telephony.Sms.SEEN,false); 77 values.put(Telephony.Sms.STATUS, Telephony.Sms.STATUS_COMPLETE); 78 values.put(Telephony.Sms.BODY, mMsg.getText().toString()); 79 values.put(Telephony.Sms.TYPE, Telephony.Sms.MESSAGE_TYPE_INBOX); 80 81 Uri uri=resolver.insert(Telephony.Sms.CONTENT_URI,values); 82 if(uri!=null){ 83 long uriId= ContentUris.parseId(uri); 84 System.out.println("uriId "+uriId); 85 } 86 87 Toast.makeText(MainActivity.this, "Insert a short Message.", 88 Toast.LENGTH_LONG).show(); 89 90 // 对短信数据库处理结束后,恢复原来的默认SMS APP 91 Intent intent=new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT); 92 intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME,defaultSmsPkg); 93 startActivity(intent); 94 System.out.println("Recover default SMS App"); 95 96 // 打印出收件箱里的最新5条短信 97 Cursor cursor=getContentResolver().query(Telephony.Sms.CONTENT_URI,null,null,null,null); 98 String msg=""; 99 while ((cursor.moveToNext()) && 100 (cursor.getPosition()<5)){ 101 int dateColumn=cursor.getColumnIndex("date"); 102 int phoneColumn=cursor.getColumnIndex("address"); 103 int smsColumn=cursor.getColumnIndex("body"); 104 105 System.out.println("count "+cursor.getCount()+" position "+cursor.getPosition()); 106 // 把从短信中获取的时间戳换成一定格式的时间 107 SimpleDateFormat sfd=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); 108 Date date=new Date(Long.parseLong(cursor.getString(dateColumn))); 109 String time=sfd.format(date); 110 msg=msg+time+" "+cursor.getString(phoneColumn)+":"+cursor.getString(smsColumn)+"\n"; 111 mMessageView.setText(msg); 112 } 113 114 } 115 else{ 116 Toast.makeText(MainActivity.this,"Sorry,the App is not default Sms App.", 117 Toast.LENGTH_LONG).show(); 118 } 119 } 120 }); 121 } 122 123 @Override 124 public boolean onCreateOptionsMenu(Menu menu) { 125 // Inflate the menu; this adds items to the action bar if it is present. 126 getMenuInflater().inflate(R.menu.menu_main, menu); 127 return true; 128 } 129 130 @Override 131 public boolean onOptionsItemSelected(MenuItem item) { 132 // Handle action bar item clicks here. The action bar will 133 // automatically handle clicks on the Home/Up button, so long 134 // as you specify a parent activity in AndroidManifest.xml. 135 int id = item.getItemId(); 136 137 //noinspection SimplifiableIfStatement 138 if (id == R.id.action_settings) { 139 return true; 140 } 141 142 return super.onOptionsItemSelected(item); 143 } 144 }
以上是具体的动作实现,要完成一个默认的消息App,还需要具备接收SMS和MMS的能力以及发送SMS的能力,所以还需要MmsReceiver/SmsReceiver/SmsSendService,具体到今天要实现的功能而言,这几个能力都不用具体实现,所以Receiver和Service并不用对具体的action进行相应。
MmsReceiver.java
1 package tina.messagebox; 2 3 import android.content.BroadcastReceiver; 4 import android.content.Context; 5 import android.content.Intent; 6 7 /** 8 * Created by Tina on 2015/8/11. 9 */ 10 public class MmsReceiver extends BroadcastReceiver { 11 @Override 12 public void onReceive(Context context, Intent intent) { 13 14 } 15 }
SmsReceiver.java
1 package tina.messagebox; 2 3 import android.content.BroadcastReceiver; 4 import android.content.Context; 5 import android.content.Intent; 6 7 /** 8 * Created by Tina on 2015/8/11. 9 */ 10 public class SmsReceiver extends BroadcastReceiver { 11 @Override 12 public void onReceive(Context context, Intent intent) { 13 14 } 15 }
SmsSendService.java
1 package tina.messagebox; 2 3 import android.app.Service; 4 import android.content.Intent; 5 import android.os.IBinder; 6 7 public class SmsSendService extends Service { 8 public SmsSendService() { 9 } 10 11 @Override 12 public IBinder onBind(Intent intent) { 13 // TODO: Return the communication channel to the service. 14 return null; 15 } 16 }
最后,要在AndroidManifest.xml中对Receiver和Service进行注册,并声明消息相关的权限。
1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="tina.messagebox" > 4 5 <!-- Adding --> 6 <uses-permission android:name="android.permission.WRITE_SMS" /> 7 <uses-permission android:name="android.permission.READ_SMS" /> 8 9 <!-- End Adding --> 10 <application 11 android:allowBackup="true" 12 android:icon="@mipmap/ic_launcher" 13 android:label="@string/app_name" 14 android:theme="@style/AppTheme" > 15 <activity 16 android:name=".MainActivity" 17 android:label="@string/app_name" > 18 <intent-filter> 19 <action android:name="android.intent.action.MAIN" /> 20 21 <category android:name="android.intent.category.LAUNCHER" /> 22 23 <!-- Adding --> 24 <action android:name="android.intent.action.SEND" /> 25 <action android:name="android.intent.action.SENDTO" /> 26 27 <category android:name="android.intent.category.DEFAULT" /> 28 <category android:name="android.intent.category.BROWSABLE" /> 29 30 <data android:scheme="sms" /> 31 <data android:scheme="smsto" /> 32 <data android:scheme="mms" /> 33 <data android:scheme="mmsto" /> 34 <!-- End Adding --> 35 36 </intent-filter> 37 </activity> 38 39 <!-- Adding --> 40 <!-- BroadcastReceiver that listens for incoming SMS messages --> 41 <receiver 42 android:name=".SmsReceiver" 43 android:permission="android.permission.BROADCAST_SMS" > 44 <intent-filter> 45 <action android:name="android.provider.Telephony.SMS_DELIVER" /> 46 </intent-filter> 47 </receiver> 48 49 <!-- BroadcastReceiver that listens for incoming MMS messages --> 50 <receiver 51 android:name=".MmsReceiver" 52 android:permission="android.permission.BROADCAST_WAP_PUSH" > 53 <intent-filter> 54 <action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" /> 55 56 <data android:mimeType="application/vnd.wap.mms-message" /> 57 </intent-filter> 58 </receiver> 59 60 <!-- Service that delivers messages from the phone "quick response" --> 61 <service 62 android:name=".SmsSendService" 63 android:exported="true" 64 android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE" > 65 <intent-filter> 66 <action android:name="android.intent.action.RESPOND_VIA_MESSAGE" /> 67 68 <category android:name="android.intent.category.DEFAULT" /> 69 70 <data android:scheme="sms" /> 71 <data android:scheme="smsto" /> 72 <data android:scheme="mms" /> 73 <data android:scheme="mmsto" /> 74 </intent-filter> 75 </service> 76 <!-- End Adding --> 77 </application> 78 79 </manifest>
这两个Receiver和Service缺了任何一个,App都无法成为一个默认的消息App。
下面,是今天实现的App的运行效果。第一次打开App,会检测该App是否是默认消息App,如果不是,则弹出对话框让用户决定是否将其设置为默认App,如果用户同意,则可以输入手机号码和短信内容,按一下Insert,手机短信箱里就会多了一条短信,插入成功后会再弹出一个对话框,询问用户是否恢复手机自带的短信App为默认,用户选择Yes,那么回到Message中,就可以看到一条未读短信啦。当然,万一一不小心,在后面这个对话框里选择了是,那么为了能够正常地处理短信,还是到手机的“设置”里面,把原来的App设为默认吧。