手机卫士第九天
程序锁功能开发
一、建立APPLock数据库用来存放用户加锁后的程序信息。
package com.example.phoneguard.utils;
import com.example.phoneguard.ui.MainActivity;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.widget.Toast;
public class AppLockOpenHelper extends SQLiteOpenHelper {
private Context context;
// 数据库名称,.db后缀只是给人看的
public static final String DB_NAME = "applock.db";
// 列表的建表语句
public static final String CT_APPLOCKLIST = "CREATE TABLE applock_list ( _id INTEGER PRIMARY KEY AUTOINCREMENT,pkgname VARCHAR(50))";
public static final String APPLOCKLIST_TB_NAME = "applock_list";
public AppLockOpenHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
}
/*
* 自定义的默认的构造器
*/
public AppLockOpenHelper(Context context) {
this(context, DB_NAME, null, 1);
this.context = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
// 通过带工具类得到数据库实例时数据库不存在会执行该方法
Toast.makeText(context, "数据库不存在,执行建表语句创建数据库和相关的数据表", 1).show();
db.execSQL(CT_APPLOCKLIST);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Toast.makeText(context,
"数据库版本变化,新版本:" + newVersion + ",旧版本:" + oldVersion, 1).show();
}
}
二、在软件管理界面注册长按事件,用来对程序进行加/解锁。同时更新列表和数据库的持久化数据,更新数据后,想看门狗服务发送广播,通知其更新程序黑名单列表
lv_list_allapp
.setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent,
View view, int position, long id) {
// 如果长按的不是我们需要的位置
if (position == 0 || position == (userApp.size() + 1)) {
return true;
}
ViewHolder holder = (ViewHolder) view.getTag();
if (position < userApp.size() + 1) {
appInfo = userApp.get(position - 1);
} else {
appInfo = sysApp.get(position - userApp.size() - 2);
}
String pkgName = appInfo.getPackageName();
// 当前的应用已经锁定了,取消锁定
if (lockPkgNames.contains(pkgName)) {
if (daoAppLock.delete(pkgName) > 0) {
// 从锁定的列表中移出
lockPkgNames.remove(pkgName);
holder.app_lock
.setImageResource(R.drawable.unlock);
}
} else {
if (daoAppLock.add(pkgName)) {
// 从锁定的列表中移出
lockPkgNames.add(pkgName);
holder.app_lock
.setImageResource(R.drawable.lock);
}
}
// 发送广播给看门狗服务,通知更新锁定应用名单
Intent intent = new Intent(
"com.example.phoneguard.updateLock");
sendBroadcast(intent);
return true;
}
});
三、建立看门狗服务,间隔很短的时间(30ms),查看最近使用任务栈的包名是否在程序锁黑名单中且不再程序锁白名单中。如果在弹出输入密码的界面
package com.example.phoneguard.service;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.impl.cookie.BrowserCompatSpecFactory;
import com.example.phoneguard.R;
import com.example.phoneguard.dao.DaoAppLock;
import com.example.phoneguard.domain.AppInfo;
import com.example.phoneguard.engine.AppInfoManager;
import com.example.phoneguard.ui.AppLockActivity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.os.IBinder;
import android.widget.Toast;
public class WatcherDogService extends Service {
private boolean flag;
private ActivityManager am;
private List<String> lockList;
private List<String> whiteList;
private WhitePkgNamesUpdateReceiver whiteReceiver;
private LockPkgNamesUpdateReceiver lockReceiver;
private UnLockPkgNamesUpdateReceiver unlockReceiver;
private DaoAppLock daoAppLock;
private PackageManager pm;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
pm = getPackageManager();
daoAppLock = new DaoAppLock(this);
lockList = daoAppLock.list();
whiteList = new ArrayList<String>();
// 代码注册更新锁定集合和白名单集合的广播接收者
lockReceiver = new LockPkgNamesUpdateReceiver();
registerReceiver(lockReceiver, new IntentFilter(
"com.example.phoneguard.updateLock"));
// 屏幕解锁的广播事件
unlockReceiver = new UnLockPkgNamesUpdateReceiver();
registerReceiver(unlockReceiver, new IntentFilter(
Intent.ACTION_USER_PRESENT));
whiteReceiver = new WhitePkgNamesUpdateReceiver();
registerReceiver(whiteReceiver, new IntentFilter(
"com.example.phoneguard.updateWhite"));
watchProcess();
super.onCreate();
}
@Override
public void onDestroy() {
// 取消广播事件的接收
unregisterReceiver(lockReceiver);
unregisterReceiver(whiteReceiver);
unregisterReceiver(unlockReceiver);
unlockReceiver = null;
lockReceiver = null;
whiteReceiver = null;
super.onDestroy();
}
public void watchProcess() {
flag = true;
new Thread(new Runnable() {
@Override
public void run() {
while (flag) {
try {
Thread.sleep(30);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String curPkgName = am.getRunningTasks(1).get(0).baseActivity
.getPackageName();
// 如果锁定名单中存在该应用程序并且忽略名单中没有该应用程序
if (lockList.contains(curPkgName)
&& (!whiteList.contains(curPkgName))) {
// 启动输入密码界面,该Acitivity应该配置成SingleInstance的启动模式(新开任务栈)
// 并且android:exclueFromRecents="true",不显示在最近应用列表
Intent intent = new Intent(getApplicationContext(),
AppLockActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("pkgName", curPkgName);
startActivity(intent);
}
}
}
}).start();
}
private class LockPkgNamesUpdateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 在这个方法里面重新设置锁定程序报名的集合
// pkgLockNames=intent.
// Toast.makeText(getApplicationContext(), "收到了更新应用锁名单的广播",
// 0).show();
lockList = daoAppLock.list();
}
}
private class UnLockPkgNamesUpdateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 在这个方法里面重新设置锁定程序包名的集合
// 屏幕解锁的时候清空白名单
whiteList = new ArrayList<String>();
}
}
private class WhitePkgNamesUpdateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 在这个方法里面添加锁定程序包名白名单的集合
// pkgLockNames=intent.
String packageName = intent.getStringExtra("pkgName");
whiteList.add(packageName);
Toast.makeText(getApplicationContext(),
"收到了更新锁白名单的广播,将包名:" + packageName + "加入到白名单", 0).show();
}
}
public void showActiviy(Class clazz) {
Intent intent = new Intent(getApplicationContext(), clazz);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
四、新建输入密码的布局文件和对应的Activity类。在清单文件时声明需要注意,启动模式为singleInstance,不包含在最近任务列表设为true。
1.布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="60dp"
android:gravity="center"
android:orientation="horizontal" >
<ImageView
android:id="@+id/iv_lockapp_icon"
android:layout_width="40dp"
android:layout_height="40dp" />
<TextView
android:id="@+id/tv_lockapp_name"
android:layout_width="fill_parent"
android:layout_height="40dp"
android:gravity="center_vertical"
android:text="程序名称" />
</LinearLayout>
<EditText
android:id="@+id/et_input_pwd"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="请输入程序锁密码"
android:inputType="numberPassword" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="enterApp"
android:text="进入应用" />
</LinearLayout>
2.Activity类文件
package com.example.phoneguard.ui;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import com.example.phoneguard.R;
import com.example.phoneguard.dao.DaoSMSInfo;
import com.example.phoneguard.domain.AppInfo;
import com.example.phoneguard.domain.SMSInfo;
import com.example.phoneguard.engine.AppInfoManager;
import com.example.phoneguard.utils.DisplayUtil;
import com.example.phoneguard.utils.JsonUtils;
import com.example.phoneguard.utils.MD5Utils;
import com.example.phoneguard.utils.TextUtil;
import com.example.phoneguard.utils.ToastUtils;
import com.google.gson.Gson;
import android.R.color;
import android.app.ActionBar.LayoutParams;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.Cursor;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Looper;
import android.os.StatFs;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.LinearInterpolator;
import android.view.animation.ScaleAnimation;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
public class AppLockActivity extends Activity {
private AppInfo appInfo;
private ImageView iv_lockapp_icon;
private TextView tv_lockapp_name;
private EditText et_input_pwd;
private SharedPreferences sp;
private String pkgName;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.app_lock_activity);
sp = getSharedPreferences("config", MODE_PRIVATE);
iv_lockapp_icon = (ImageView) findViewById(R.id.iv_lockapp_icon);
tv_lockapp_name = (TextView) findViewById(R.id.tv_lockapp_name);
et_input_pwd = (EditText) findViewById(R.id.et_input_pwd);
appInfo = new AppInfo();
pkgName = getIntent().getStringExtra("pkgName");
PackageManager pm = getPackageManager();
try {
ApplicationInfo info = pm.getApplicationInfo(pkgName, 0);
// 不能序列化,传递不过去
appInfo.setIcon(info.loadIcon(pm));
appInfo.setName(info.loadLabel(pm).toString());
} catch (NameNotFoundException e) {
appInfo.setIcon(getResources().getDrawable(
R.drawable.ic_app_default));
appInfo.setName(pkgName);
}
iv_lockapp_icon.setImageDrawable(appInfo.getIcon());
tv_lockapp_name.setText(appInfo.getName());
}
@Override
public void onBackPressed() {
Intent intent = new Intent();
intent.setAction("android.intent.action.MAIN");
intent.addCategory("android.intent.category.HOME");
intent.addCategory("android.intent.category.DEFAULT");
startActivity(intent);
super.onBackPressed();
}
@Override
protected void onStop() {
finish();
super.onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
// 根据clazz值开启Activity
public void showActiviy(Class clazz) {
Intent intent = new Intent(getApplicationContext(), clazz);
startActivity(intent);
}
// 根据clazz文件开启服务
public void startService(Class clazz) {
Intent intent = new Intent(this, clazz);
startService(intent);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
}
public void enterApp(View v) {
String pwd = et_input_pwd.getText().toString().trim();
if (MD5Utils.compareToText(pwd, sp.getString("pwd_in_lostphone", ""))) {
Intent intent = new Intent("com.example.phoneguard.updateWhite");
intent.putExtra("pkgName", pkgName);
sendBroadcast(intent);
finish();
} else {
Toast.makeText(getApplicationContext(), "您输入的程序锁密码有误,请重新输入", 0)
.show();
}
}
}
五、密码输入正确后向看门狗服务发送广播,在白名单中添加该该包名对应的应用。
public void enterApp(View v) {
String pwd = et_input_pwd.getText().toString().trim();
if (MD5Utils.compareToText(pwd, sp.getString("pwd_in_lostphone", ""))) {
Intent intent = new Intent("com.example.phoneguard.updateWhite");
intent.putExtra("pkgName", pkgName);
sendBroadcast(intent);
finish();
} else {
Toast.makeText(getApplicationContext(), "您输入的程序锁密码有误,请重新输入", 0)
.show();
}
}
六、解锁屏幕时晴空白名单。输入密码界面下按回退键返回桌面,界面不可见时销毁界面。
@Override
public void onBackPressed() {
Intent intent = new Intent();
intent.setAction("android.intent.action.MAIN");
intent.addCategory("android.intent.category.HOME");
intent.addCategory("android.intent.category.DEFAULT");
startActivity(intent);
super.onBackPressed();
}
@Override
protected void onStop() {
finish();
super.onStop();
}
流量统计的工作原理
一、lunix系统下用后开机以来的流量信息位于proc目录下
a) proc/
>cpuinfo cpu信息
>meminfo 内存信息
>uid_stat uid状态目录
>uid/tcp_rcv 下行的数据字节数
>uid/tcp_snd 上行的数据字节数
b) uid自增的方式为新安装的应用进行分配的。应用卸载后uid会释放,可能会付给下一个应用程序(uid+pkgnmae)可以用来统计流量信息
二、Android 2.3以后增加了方便开发者获取应用流量信息的API
a) 取得PackageManager (包管理器)pm
b) 使用pm得到所有已经安装的应用程序信息(包括uid)
c) 利用uid通过TrafficStats的静态方法获取流量信息
i. getMobileTxBytes(uid)得到2G/3G上行数据字节数(2G/3G)
ii. getMobileRxBytes(uid)得到2G/3G下行数据字节数(2G/3G)
iii. getTotalTxBytes(uid)得到总的上行数据字节数(2G/3G+WIFI)
iv. getTotalRxBytes(uid)得到总的下行数据字节数(2G/3G+WIFI)
广播接收者使用技巧
一、保证我们写的广播接收者第一个获取广播事件必须保证下面三个条件
a) 应用优先于其他接收同样广播接收者的启动
b) 广播接收者的优先级为整数最大值(214748364)
c) 代码注册广播接收者
二、开机广播接收者的使用
a) 将应用程序所有的关键服务开机启动
b) 如果开机广播接收者没有开启服务很容易就会被回收,应用程序开机后运行出错