1. 程式人生 > >Android Accessibility(輔助功能) --實現Android應用自動安裝、解除安裝

Android Accessibility(輔助功能) --實現Android應用自動安裝、解除安裝

     對於那些由於視力、聽力或其它身體原因導致不能方便使用Android智慧手機的使用者,Android提供了Accessibility功能和服務幫助這些使用者更加簡單地操作裝置,包括文字轉語音、觸覺反饋、手勢操作、軌跡球和手柄操作。開發者可以搭建自己的Accessibility服務,這可以加強可用性,例如聲音提示,物理反饋,和其他可選的操作模式。

     隨著Android版本的不斷升級,Android Accessibility功能也越來越強大,Android 4.0版本以前,系統輔助服務功能比較單一,僅僅能過單向獲取視窗元素資訊,比如獲取輸入框使用者輸入內容。到Android 4.1版本以後,系統輔助服務增加了與視窗元素的雙向互動,此時可以通過輔助功能服務操作視窗元素,比如點選按鈕等。

    由於系統輔助服務能夠實時獲取您當前操作應用的視窗元素資訊,這有可能給你帶來隱私資訊的洩露風險,比如獲取非密碼輸入框的輸入內容等。同時通過輔助功能也可以模擬使用者自動化點選應用內元素,也會帶來一定的安全風險。

    本文實現了一種通過系統輔助服務完成應用的自動安裝、解除安裝、強行停止的功能。

1、AndroidManifest.xml檔案配置自己實現的MyAccessibilityService服務。

<?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配置相應的引數資訊。

<?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實現安裝、解除安裝、強行停止動作的發起。

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中通過自動化點選實現應用安裝、解除安裝、強行停止功能。

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