程序锁的原理是一个“看门狗”的服务定时监视顶层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在启动时通过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); }
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; }