对于那些由于视力、听力或其它身体原因导致不能方便使用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
时间: 2024-10-16 06:25:57