程序锁的原理是一个“看门狗”的服务定时监视顶层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获取包名对应的应用程序信息:图标和应用程序名。将这两个信息显示提示给用户,也即说明即将进入哪个被锁定的应用程序界面。

如图:

Android 程序是否锁屏 安卓程序锁_android

最后通过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;
                    }