程序锁的原理是一个“看门狗”的服务定时监视顶层activity,如果activity对应的包名是之前上锁的应用程序的,则弹出一个页面要求输入解锁密码,此页面不运行用户按“BACK”键返回,否则便能不输入密码直接进入应用程序了。如果密码输入正确则进入应用程序。
创建一个android component,kind为service,类名为:WatchDogService:
package com.example.mobilesafe.service;
import android.app.ActivityManager;
import android.app.Service;
import android.content.Intent;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import com.example.mobilesafe.EnterPswdActivity;
import com.example.mobilesafe.db.AppLockDao;
import com.example.mobilesafe.engine.IService;
import java.util.ArrayList;
import java.util.List;
/**
* Created by sing on 14-1-16.
* desc:
*/
public class WatchDogService extends Service {
public static final String TAG = "WatchDogService";
boolean flag;
private Intent pswdIntent;
private List<String> lockPacknames;
private List<String> tempStopProtectPacknames;
private AppLockDao dao;
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
flag = false;
getContentResolver().unregisterContentObserver(observer);
observer = null;
super.onDestroy();
}
@Override
public void onCreate() {
//注册内容观察者
Uri uri = Uri.parse("content://com.example.mobilesafe.applock/");
observer = new MyObserver(new Handler());
getContentResolver().registerContentObserver(uri, true, observer);
super.onCreate();
dao = new AppLockDao(this);
flag = true;
lockPacknames = dao.findAll();
tempStopProtectPacknames = new ArrayList<String>();
pswdIntent = new Intent(this, EnterPswdActivity.class);
//服务没有任务栈,如果要开启一个在任务栈中运行的activity,需要为其创建一个任务栈
pswdIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
new Thread() {
@Override
public void run() {
while (flag) {
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
//获取当前正在栈顶运行的activity
ActivityManager.RunningTaskInfo taskInfo = am.getRunningTasks(1).get(0);
String packname = taskInfo.topActivity.getPackageName();
Log.i(TAG, packname);
if (tempStopProtectPacknames.contains(packname)) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
continue;
}
pswdIntent.putExtra("packname", packname);
if (lockPacknames.contains(packname)) {
startActivity(pswdIntent);
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
public void tempStopProtect(String packname) {
tempStopProtectPacknames.add(packname);
}
}
密码输入页面的类EnterPswdActivity:
package com.example.mobilesafe;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.IBinder;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.example.mobilesafe.engine.IService;
import com.example.mobilesafe.service.WatchDogService;
/**
* Created by sing on 14-1-16.
* desc:
*/
public class EnterPswdActivity extends Activity {
public static final String TAG = "EnterPswdActivity";
private ImageView iv_icon;
private TextView tv_name;
private EditText et_pswd;
private Button bt_enter;
private Intent serviceIntent;
private IService iService;
private MyConn conn;
private String packname;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.enterpswd_layout);
iv_icon = (ImageView) findViewById(R.id.iv_icon);
tv_name = (TextView) findViewById(R.id.tv_name);
et_pswd = (EditText) findViewById(R.id.et_pswd);
bt_enter = (Button) findViewById(R.id.bt_enter);
bt_enter.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String pswd = et_pswd.getText().toString().trim();
if (pswd.isEmpty()) {
Toast.makeText(EnterPswdActivity.this,"密码不能为空",0).show();
return;
}
if (pswd.equals("123")) {
iService.callTempStopProtect(packname);
finish();
}else{
Toast.makeText(EnterPswdActivity.this,"密码不正确",0).show();
return;
}
}
});
//获取到激活当前activity的意图,也就是WatchDogService传递的pswdIntent
Intent intent = getIntent();
packname = intent.getStringExtra("packname");
serviceIntent = new Intent(this, WatchDogService.class);
conn = new MyConn();
//绑定服务(非startService),执行WatchDogService中的onCreate-onBing方法
//如果绑定成功,WatchDogService中onBing方法返回一个IBinder给conn.ServiceConnection
bindService(serviceIntent, conn, BIND_AUTO_CREATE);
try {
PackageInfo info = getPackageManager().getPackageInfo(packname, 0);
iv_icon.setImageDrawable(info.applicationInfo.loadIcon(getPackageManager()));
tv_name.setText(info.applicationInfo.loadLabel(getPackageManager()));
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
private class MyConn implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
iService = (IService) iBinder;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//接触绑定
unbindService(conn);
}
/**
* 不允许用户按back键后退
* @param keyCode
* @param event
* @return
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (event.getAction()==KeyEvent.ACTION_DOWN && event.getKeyCode()==KeyEvent.KEYCODE_BACK) {
return true;
}
return super.onKeyDown(keyCode, event);
}
}
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_height="wrap_content"
android:layout_width="match_parent" >
<RelativeLayout style="@style/relativelayout">
<ImageView
android:id="@+id/iv_icon"
android:layout_alignParentLeft="true"
android:layout_height="wrap_content"
android:layout_width="wrap_content"/>
<TextView style="@style/content_text"
android:id="@+id/tv_name"
android:gravity="center"
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/iv_icon"/>
</RelativeLayout>
<EditText
android:id="@+id/et_pswd"
android:layout_height="match_parent"
android:layout_width="match_parent" />
<Button
android:id="@+id/bt_enter"
android:text="确定"
android:gravity="center"
android:layout_height="match_parent"
android:layout_width="match_parent" />
</LinearLayout>
这里EnterPswdActivity类重载了对onKeyDown消息的处理,如果用户按键为KEYCODE_BACK则直接返回,防止用户在密码输入界面通过按“BACK”键进入到被锁定的应用程序界面。
EnterPswdActivity在启动时通过getIntent获取WatchDogService传递来的intent,进而通过getStringExtra("packname")获取被锁定的应用程序包名。进而通过getPackageManager获取包名对应的应用程序信息:图标和应用程序名。将这两个信息显示提示给用户,也即说明即将进入哪个被锁定的应用程序界面。
如图:
最后通过bindService绑定服务,如果绑定成功WatchDogService中onBing方法返回一个IBinder给conn.ServiceConnection:
private class MyConn implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
iService = (IService) iBinder;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
}
接口IService很简单:
package com.example.mobilesafe.engine;
/**
* Created by sing on 14-1-16.
* desc:使看门狗服务停止对packname保护的接口
*/
public interface IService {
public void callTempStopProtect(String packname);
}
绑定服务这一过程是为了让WatchDogService返回一个操作接口,目的是在输入密码成功后调用该接口,使得能够进入被锁定的应用程序界面,临时不再被锁定。WatchDogService需要在onBind函数中返回一个对象:
private MyBinder binder;
public IBinder onBind(Intent intent) {
binder = new MyBinder();
return binder;
}
private class MyBinder extends Binder implements IService {
@Override
public void callTempStopProtect(String packname) {
tempStopProtect(packname);
}
}
WatchDogService在绑定成功时创建了一个实现了IService接口的Binder对象给EnterPswdActivity.MyConn.onServiceConnected,用户正确输入密码后EnterPswdActivity调用该接口函数callTempStopProtect,该函数又调用WatchDogService的tempStopProtect:
private List<String> tempStopProtectPacknames;
public void tempStopProtect(String packname) {
tempStopProtectPacknames.add(packname);
}
将临时解锁的应用程序包名添加到临时停止保护的列表里去,在服务的监控线程中,如果发现应用程序包名已经在此列表里的话,便不再弹出对话框要求输入密码,会认为之前已经成功进入过了。
if (tempStopProtectPacknames.contains(packname)) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
continue;
}