问题
I'm trying to send a system back press event via the AccessibilityService
and this works fine, but only if I'm not in my own app.
I'm always getting true
from performGlobalAction
no matter if I'm in my own app or not, but I only see that the event really is executed if I'm not in my own app but in any other one (in the sense of that the previous activity is shown or similar)
Any ideas why this happens? My app is a sidebar app with an overlay drawn on top in the WindowManager
and everything is working (AccessibilityService
is running and is handling my custom events and the service always returns success messages for my events, but my own app does not react to the back button event).
My service looks like following:
public class MyAccessibilityService extends AccessibilityService {
public static void sendBackIntent(Context context) {
Intent intent = new Intent(context, MyAccessibilityService.class);
intent.putExtra("action", GLOBAL_ACTION_BACK);
context.startService(intent);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Bundle extras = intent.getExtras();
Integer action = null;
if (extras != null) {
action = extras.getInt("action");
}
if (action != null) {
switch (action) {
case GLOBAL_ACTION_BACK:
boolean result = performGlobalAction(action);
L.d("Action %d executed: %b", action, result);
break;
default:
L.e("Unhandled action %d", action);
break;
}
}
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
}
@Override
public void onInterrupt() {
}
}
Edit
To make this clear:
- I do NOT start this service via
MyAccessibilityService.sendBackIntent(context)
, I send the intent like following:if (isAccessibilityserviceRunning) MyAccessibilityService.sendBackIntent(context)
- I start my service via the system service menu by simply enabling it there and let the system start it automatically afterwards
- I've setup everything for the
AccessibilityService
in anaccessibilityservice.xml
and use this to define my services settings and this is working perfectly fine as well, all events I want to receive are received reliably and correct
EDIT 2
Seems like in my case my overlay is still stealing the focus making it focusable and not has timing problems that sometimes make problems. Still, my solution can be improved by using BroadcastReceiver
to communicate with the service, as the startService
call is not safe as discussed in the accepted answer
回答1:
It strikes me that you're doing some very strange things. It appears that you're treating your AccessibilityService
as a normal Service
. The part of this that suggests this is your implementation of the following to methods:
public static void sendBackIntent(Context context);
@Override
public int onStartCommand(Intent intent, int flags, int startId);
Just by the signatures of these two methods and your calling of
context.startService(intent);
Within your static method, I can tell that you don't understand AccessibilityServices
and how they are supposed to perform their jobs. You cannot start your accessibility service, nor interact with it, in the way that you are attempting. Certainly you can use Accessibility Services to perform global actions, but they won't do so accurately and globally, unless you launch them correctly, from the Accessibility Services menu (you know the one where TalkBack shows up).
Your code essentially, isn't running within the Context you think it's running in. So, it runs, and does things. But, AccessibilityServices
and their respective power, is in their ability to attach globally to the Operating System. The android API's won't bind an AccessibilityService properly, when you attempt to launch your service with:
context.startService(intent);
You have to launch your Accessibility Service from the Accessibility Services Settings menu.
Even if your service is already launched such a call is unsafe! There's no guarantee your users are going to start the service prior to opening your Activity. Once you have called context.startService
and attempted to start your AccessibilityService in this way, it will prevent the Accessibility Settings Menu from starting your service and binding to the OS properly. In fact, once in this situation a user would have to: Turn off the Switch for your service in the Accessibility Settings Menu, force stop (perhaps even uninstall) your application, restart their device, start your service and THEN start your activity, in order for the proper behavior to be achieved.
If you don't do so, it will not bind to the OS properly and its behavior is undefined. Right now, you've essentially created a hack in the OS and are running up against said undefined behavior, that could vary WIDELY across version, manufacturer, etc, because it's behavior isn't covered in the AOSP integration tests.
In fact, you explicitly CANNOT launch Accessibility Services using the context.startService()
call. This is a very important security feature of Android, as Accessibility Services can gain access to screen content, and users need fine grain control over the providers and applications they allow this access. So, while you may be getting SOME behavior, it is undefined and dangerous behavior. What you want is something like the following:
With the following service config XML:
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/accessibility_service_description"
android:accessibilityEventTypes="typeWindowContentChanged"
android:accessibilityFlags="flagRequestTouchExplorationMode"
android:canRetrieveWindowContent="true"
android:canRequestTouchExplorationMode="true"
android:accessibilityFeedbackType="feedbackGeneric"
android:notificationTimeout="100"
android:settingsActivity="com.service.SettingsActivity"
/>
And the following accessibility service.
class MyA11yService extends AccessibilityService {
@Override public boolean onGesture(int gestureId) {
switch (gestureId) {
case GESTURE_SWIPE_UP_AND_DOWN:
CLog.d("Performing gesture.");
performGlobalAction(GLOBAL_ACTION_BACK);
return true;
default:
return false;
}
}
}
The performGlobalAction
call works just fine in any Context
. Now, instead of performing this action on the SWIPE_UP_DOWN gesture, what you want to do is set up some sort of inter-process communication with the part of this that you want to be able to trigger the "global back button" action. But, that information is for another question, though if you understand the information in this post, I'm sure how you need to proceed will be clear.
来源:https://stackoverflow.com/questions/46194538/accessibilityservice-performglobalaction-not-working-in-own-app