對于那些由于視力、聽力或其它身體原因導緻不能友善使用Android智能手機的使用者,Android提供了Accessibility功能和服務幫助這些使用者更加簡單地操作裝置,包括文字轉語音、觸覺回報、手勢操作、軌迹球和搖桿操作。開發者可以搭建自己的Accessibility服務,這可以加強可用性,例如聲音提示,實體回報,和其他可選的操作模式。
随着Android版本的不斷更新,Android Accessibility功能也越來越強大,Android 4.0版本以前,系統輔助服務功能比較單一,僅僅能過單向擷取視窗元素資訊,比如擷取輸入框使用者輸入内容。到Android 4.1版本以後,系統輔助服務增加了與視窗元素的雙向互動,此時可以通過輔助功能服務操作視窗元素,比如點選按鈕等。
由于系統輔助服務能夠實時擷取您目前操作應用的視窗元素資訊,這有可能給你帶來隐私資訊的洩露風險,比如擷取非密碼輸入框的輸入内容等。同時通過輔助功能也可以模拟使用者自動化點選應用内元素,也會帶來一定的安全風險。
本文實作了一種通過系統輔助服務完成應用的自動安裝、解除安裝、強行停止的功能。
1、AndroidManifest.xml檔案配置自己實作的MyAccessibilityService服務。
[html] view plain copy
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5Gcu82Yp9VRE90Qvw1c0V2czF2LcRXZu5ibkN3YuUGZvN2Lc9CX6MHc0RHaiojIsJye.png)
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.jack.accessibility"
- android:versionCode="1"
- android:versionName="1.0" >
- <uses-sdk
- android:minSdkVersion="8"
- android:targetSdkVersion="18" />
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
- <uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@style/AppTheme" >
- <activity
- android:name="com.jack.accessibility.MainActivity"
- android:label="@string/app_name" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <service android:label="@string/acc_service_name" android:name=".MyAccessibilityService" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
- <intent-filter>
- <action android:name="android.accessibilityservice.AccessibilityService" />
- </intent-filter>
- <meta-data android:name="android.accessibilityservice" android:resource="@xml/phone_accessibility" />
- </service>
- </application>
- </manifest>
2、在res/xml/phone_accessibility.xml配置相應的參數資訊。
[html] view plain copy
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5Gcu82Yp9VRE90Qvw1c0V2czF2LcRXZu5ibkN3YuUGZvN2Lc9CX6MHc0RHaiojIsJye.png)
- <?xml version="1.0" encoding="utf-8"?>
- <accessibility-service android:description="@string/accessibility_service_description"
- android:accessibilityEventTypes="typeAllMask"
- android:accessibilityFeedbackType="feedbackGeneric"
- android:notificationTimeout="100"
- android:accessibilityFlags=""
- android:canRetrieveWindowContent="true"
- xmlns:android="http://schemas.android.com/apk/res/android" />
3、MainActivity實作安裝、解除安裝、強行停止動作的發起。
[java] view plain copy
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5Gcu82Yp9VRE90Qvw1c0V2czF2LcRXZu5ibkN3YuUGZvN2Lc9CX6MHc0RHaiojIsJye.png)
- package com.jack.accessibility;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import android.net.Uri;
- import android.os.Bundle;
- import android.os.Environment;
- import android.provider.Settings;
- import android.view.View;
- import android.app.Activity;
- import android.content.Intent;
- public class MainActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- this.findViewById(R.id.activeButton).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View arg0) {
- // TODO Auto-generated method stub
- Intent killIntent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
- startActivity(killIntent);
- }
- });
- this.findViewById(R.id.installButton).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View arg0) {
- // TODO Auto-generated method stub
- MyAccessibilityService.INVOKE_TYPE = MyAccessibilityService.TYPE_INSTALL_APP;
- String fileName = Environment.getExternalStorageDirectory() + "/test.apk";
- File installFile = new File(fileName);
- if(installFile.exists()){
- installFile.delete();
- }
- try {
- installFile.createNewFile();
- FileOutputStream out = new FileOutputStream(installFile);
- byte[] buffer = new byte[512];
- InputStream in = MainActivity.this.getAssets().open("test.apk");
- int count;
- while((count= in.read(buffer))!=-1){
- out.write(buffer, 0, count);
- }
- in.close();
- out.close();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setDataAndType(Uri.fromFile(new File(fileName)), "application/vnd.android.package-archive");
- startActivity(intent);
- }
- });
- this.findViewById(R.id.uninstallButton).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View arg0) {
- // TODO Auto-generated method stub
- MyAccessibilityService.INVOKE_TYPE = MyAccessibilityService.TYPE_UNINSTALL_APP;
- Uri packageURI = Uri.parse("package:com.example.test");
- Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI);
- startActivity(uninstallIntent);
- }
- });
- this.findViewById(R.id.killAppButton).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View arg0) {
- // TODO Auto-generated method stub
- MyAccessibilityService.INVOKE_TYPE = MyAccessibilityService.TYPE_KILL_APP;
- Intent killIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
- Uri packageURI = Uri.parse("package:com.example.test");
- killIntent.setData(packageURI);
- startActivity(killIntent);
- }
- });
- }
- }
4、MyAccessibilityService中通過自動化點選實作應用安裝、解除安裝、強行停止功能。
[java] view plain copy
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5Gcu82Yp9VRE90Qvw1c0V2czF2LcRXZu5ibkN3YuUGZvN2Lc9CX6MHc0RHaiojIsJye.png)
- package com.jack.accessibility;
- import java.util.List;
- import android.accessibilityservice.AccessibilityService;
- import android.annotation.SuppressLint;
- import android.util.Log;
- import android.view.KeyEvent;
- import android.view.accessibility.AccessibilityEvent;
- import android.view.accessibility.AccessibilityNodeInfo;
- @SuppressLint("NewApi")
- public class MyAccessibilityService extends AccessibilityService {
- public static int INVOKE_TYPE = 0;
- public static final int TYPE_KILL_APP = 1;
- public static final int TYPE_INSTALL_APP = 2;
- public static final int TYPE_UNINSTALL_APP = 3;
- public static void reset(){
- INVOKE_TYPE = 0;
- }
- @Override
- public void onAccessibilityEvent(AccessibilityEvent event) {
- // TODO Auto-generated method stub
- this.processAccessibilityEnvent(event);
- }
- private void processAccessibilityEnvent(AccessibilityEvent event) {
- Log.d("test", event.eventTypeToString(event.getEventType()));
- if (event.getSource() == null) {
- Log.d("test", "the source = null");
- } else {
- Log.d("test", "event = " + event.toString());
- switch (INVOKE_TYPE) {
- case TYPE_KILL_APP:
- processKillApplication(event);
- break;
- case TYPE_INSTALL_APP:
- processinstallApplication(event);
- break;
- case TYPE_UNINSTALL_APP:
- processUninstallApplication(event);
- break;
- default:
- break;
- }
- }
- }
- @Override
- protected boolean onKeyEvent(KeyEvent event) {
- // TODO Auto-generated method stub
- return true;
- }
- @Override
- public void onInterrupt() {
- // TODO Auto-generated method stub
- }
- private void processUninstallApplication(AccessibilityEvent event) {
- if (event.getSource() != null) {
- if (event.getPackageName().equals("com.android.packageinstaller")) {
- List<AccessibilityNodeInfo> ok_nodes = event.getSource().findAccessibilityNodeInfosByText("确定");
- if (ok_nodes!=null && !ok_nodes.isEmpty()) {
- AccessibilityNodeInfo node;
- for(int i=0; i<ok_nodes.size(); i++){
- node = ok_nodes.get(i);
- if (node.getClassName().equals("android.widget.Button") && node.isEnabled()) {
- node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
- }
- }
- }
- }
- }
- }
- private void processinstallApplication(AccessibilityEvent event) {
- if (event.getSource() != null) {
- if (event.getPackageName().equals("com.android.packageinstaller")) {
- List<AccessibilityNodeInfo> unintall_nodes = event.getSource().findAccessibilityNodeInfosByText("安裝");
- if (unintall_nodes!=null && !unintall_nodes.isEmpty()) {
- AccessibilityNodeInfo node;
- for(int i=0; i<unintall_nodes.size(); i++){
- node = unintall_nodes.get(i);
- if (node.getClassName().equals("android.widget.Button") && node.isEnabled()) {
- node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
- }
- }
- }
- List<AccessibilityNodeInfo> next_nodes = event.getSource().findAccessibilityNodeInfosByText("下一步");
- if (next_nodes!=null && !next_nodes.isEmpty()) {
- AccessibilityNodeInfo node;
- for(int i=0; i<next_nodes.size(); i++){
- node = next_nodes.get(i);
- if (node.getClassName().equals("android.widget.Button") && node.isEnabled()) {
- node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
- }
- }
- }
- List<AccessibilityNodeInfo> ok_nodes = event.getSource().findAccessibilityNodeInfosByText("打開");
- if (ok_nodes!=null && !ok_nodes.isEmpty()) {
- AccessibilityNodeInfo node;
- for(int i=0; i<ok_nodes.size(); i++){
- node = ok_nodes.get(i);
- if (node.getClassName().equals("android.widget.Button") && node.isEnabled()) {
- node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
- }
- }
- }
- }
- }
- }
- private void processKillApplication(AccessibilityEvent event) {
- if (event.getSource() != null) {
- if (event.getPackageName().equals("com.android.settings")) {
- List<AccessibilityNodeInfo> stop_nodes = event.getSource().findAccessibilityNodeInfosByText("強行停止");
- if (stop_nodes!=null && !stop_nodes.isEmpty()) {
- AccessibilityNodeInfo node;
- for(int i=0; i<stop_nodes.size(); i++){
- node = stop_nodes.get(i);
- if (node.getClassName().equals("android.widget.Button")) {
- if(node.isEnabled()){
- node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
- }
- }
- }
- }
- List<AccessibilityNodeInfo> ok_nodes = event.getSource().findAccessibilityNodeInfosByText("确定");
- if (ok_nodes!=null && !ok_nodes.isEmpty()) {
- AccessibilityNodeInfo node;
- for(int i=0; i<ok_nodes.size(); i++){
- node = ok_nodes.get(i);
- if (node.getClassName().equals("android.widget.Button")) {
- node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
- Log.d("action", "click ok");
- }
- }
- }
- }
- }
- }
- }
完整DEMO下載下傳位址: http://download.csdn.net/detail/jiazhijun/8251277