Home | History | Annotate | Download | only in accessibilityservice
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.accessibilityservice;
     18 
     19 import com.android.internal.os.HandlerCaller;
     20 
     21 import android.app.Service;
     22 import android.content.Intent;
     23 import android.os.IBinder;
     24 import android.os.Message;
     25 import android.os.RemoteException;
     26 import android.util.Log;
     27 import android.view.accessibility.AccessibilityEvent;
     28 import android.view.accessibility.AccessibilityNodeInfo;
     29 
     30 /**
     31  * An accessibility service runs in the background and receives callbacks by the system
     32  * when {@link AccessibilityEvent}s are fired. Such events denote some state transition
     33  * in the user interface, for example, the focus has changed, a button has been clicked,
     34  * etc. Such a service can optionally request the capability for querying the content
     35  * of the active window. Development of an accessibility service requires extending this
     36  * class and implementing its abstract methods.
     37  * <h3>Lifecycle</h3>
     38  * <p>
     39  * The lifecycle of an accessibility service is managed exclusively by the system and
     40  * follows the established service life cycle. Additionally, starting or stopping an
     41  * accessibility service is triggered exclusively by an explicit user action through
     42  * enabling or disabling it in the device settings. After the system binds to a service it
     43  * calls {@link AccessibilityService#onServiceConnected()}. This method can be
     44  * overriden by clients that want to perform post binding setup.
     45  * </p>
     46  * <h3>Declaration</h3>
     47  * <p>
     48  * An accessibility is declared as any other service in an AndroidManifest.xml but it
     49  * must also specify that it handles the "android.accessibilityservice.AccessibilityService"
     50  * {@link android.content.Intent}. Failure to declare this intent will cause the system to
     51  * ignore the accessibility service. Following is an example declaration:
     52  * </p>
     53  * <pre> &lt;service android:name=".MyAccessibilityService"&gt;
     54  *     &lt;intent-filter&gt;
     55  *         &lt;action android:name="android.accessibilityservice.AccessibilityService" /&gt;
     56  *     &lt;/intent-filter&gt;
     57  *     . . .
     58  * &lt;/service&gt;</pre>
     59  * <h3>Configuration</h3>
     60  * <p>
     61  * An accessibility service can be configured to receive specific types of accessibility events,
     62  * listen only to specific packages, get events from each type only once in a given time frame,
     63  * retrieve window content, specify a settings activity, etc.
     64  * </p>
     65  * <p>
     66  * There are two approaches for configuring an accessibility service:
     67  * </p>
     68  * <ul>
     69  * <li>
     70  * Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring
     71  * the service. A service declaration with a meta-data tag is presented below:
     72  * <pre> &lt;service android:name=".MyAccessibilityService"&gt;
     73  *     &lt;intent-filter&gt;
     74  *         &lt;action android:name="android.accessibilityservice.AccessibilityService" /&gt;
     75  *     &lt;/intent-filter&gt;
     76  *     &lt;meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice" /&gt;
     77  * &lt;/service&gt;</pre>
     78  * <p class="note">
     79  * <strong>Note:</strong> This approach enables setting all properties.
     80  * </p>
     81  * <p>
     82  * For more details refer to {@link #SERVICE_META_DATA} and
     83  * <code>&lt;{@link android.R.styleable#AccessibilityService accessibility-service}&gt;</code>.
     84  * </p>
     85  * </li>
     86  * <li>
     87  * Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note
     88  * that this method can be called any time to dynamically change the service configuration.
     89  * <p class="note">
     90  * <strong>Note:</strong> This approach enables setting only dynamically configurable properties:
     91  * {@link AccessibilityServiceInfo#eventTypes},
     92  * {@link AccessibilityServiceInfo#feedbackType},
     93  * {@link AccessibilityServiceInfo#flags},
     94  * {@link AccessibilityServiceInfo#notificationTimeout},
     95  * {@link AccessibilityServiceInfo#packageNames}
     96  * </p>
     97  * <p>
     98  * For more details refer to {@link AccessibilityServiceInfo}.
     99  * </p>
    100  * </li>
    101  * </ul>
    102  * <h3>Retrieving window content</h3>
    103  * <p>
    104  * An service can specify in its declaration that it can retrieve the active window
    105  * content which is represented as a tree of {@link AccessibilityNodeInfo}. Note that
    106  * declaring this capability requires that the service declares its configuration via
    107  * an XML resource referenced by {@link #SERVICE_META_DATA}.
    108  * </p>
    109  * <p>
    110  * For security purposes an accessibility service can retrieve only the content of the
    111  * currently active window. The currently active window is defined as the window from
    112  * which was fired the last event of the following types:
    113  * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED},
    114  * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER},
    115  * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT},
    116  * In other words, the last window that was shown or the last window that the user has touched
    117  * during touch exploration.
    118  * </p>
    119  * <p>
    120  * The entry point for retrieving window content is through calling
    121  * {@link AccessibilityEvent#getSource() AccessibilityEvent.getSource()} of the last received
    122  * event of the above types or a previous event from the same window
    123  * (see {@link AccessibilityEvent#getWindowId() AccessibilityEvent.getWindowId()}). Invoking
    124  * this method will return an {@link AccessibilityNodeInfo} that can be used to traverse the
    125  * window content which represented as a tree of such objects.
    126  * </p>
    127  * <p class="note">
    128  * <strong>Note</strong> An accessibility service may have requested to be notified for
    129  * a subset of the event types, thus be unaware that the active window has changed. Therefore
    130  * accessibility service that would like to retrieve window content should:
    131  * <ul>
    132  * <li>
    133  * Register for all event types with no notification timeout and keep track for the active
    134  * window by calling {@link AccessibilityEvent#getWindowId()} of the last received event and
    135  * compare this with the {@link AccessibilityNodeInfo#getWindowId()} before calling retrieval
    136  * methods on the latter.
    137  * </li>
    138  * <li>
    139  * Prepare that a retrieval method on {@link AccessibilityNodeInfo} may fail since the
    140  * active window has changed and the service did not get the accessibility event yet. Note
    141  * that it is possible to have a retrieval method failing even adopting the strategy
    142  * specified in the previous bullet because the accessibility event dispatch is asynchronous
    143  * and crosses process boundaries.
    144  * </li>
    145  * </ul>
    146  * </p>
    147  * <h3>Notification strategy</h3>
    148  * <p>
    149  * For each feedback type only one accessibility service is notified. Services are notified
    150  * in the order of registration. Hence, if two services are registered for the same
    151  * feedback type in the same package the first one wins. It is possible however, to
    152  * register a service as the default one for a given feedback type. In such a case this
    153  * service is invoked if no other service was interested in the event. In other words, default
    154  * services do not compete with other services and are notified last regardless of the
    155  * registration order. This enables "generic" accessibility services that work reasonably
    156  * well with most applications to coexist with "polished" ones that are targeted for
    157  * specific applications.
    158  * </p>
    159  * <p class="note">
    160  * <strong>Note:</strong> The event notification timeout is useful to avoid propagating
    161  * events to the client too frequently since this is accomplished via an expensive
    162  * interprocess call. One can think of the timeout as a criteria to determine when
    163  * event generation has settled down.</p>
    164  * <h3>Event types</h3>
    165  * <ul>
    166  * <li>{@link AccessibilityEvent#TYPE_VIEW_CLICKED}
    167  * <li>{@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}
    168  * <li>{@link AccessibilityEvent#TYPE_VIEW_FOCUSED}
    169  * <li>{@link AccessibilityEvent#TYPE_VIEW_SELECTED}
    170  * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}
    171  * <li>{@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}
    172  * <li>{@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}
    173  * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}
    174  * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}
    175  * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}
    176  * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}
    177  * <li>{@link AccessibilityEvent#TYPE_VIEW_SCROLLED}
    178  * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}
    179  * <li>{@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}
    180  * </ul>
    181  * <h3>Feedback types</h3>
    182  * <ul>
    183  * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}
    184  * <li>{@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}
    185  * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}
    186  * <li>{@link AccessibilityServiceInfo#FEEDBACK_VISUAL}
    187  * <li>{@link AccessibilityServiceInfo#FEEDBACK_GENERIC}
    188  * </ul>
    189  * @see AccessibilityEvent
    190  * @see AccessibilityServiceInfo
    191  * @see android.view.accessibility.AccessibilityManager
    192  */
    193 public abstract class AccessibilityService extends Service {
    194     /**
    195      * The {@link Intent} that must be declared as handled by the service.
    196      */
    197     public static final String SERVICE_INTERFACE =
    198         "android.accessibilityservice.AccessibilityService";
    199 
    200     /**
    201      * Name under which an AccessibilityService component publishes information
    202      * about itself. This meta-data must reference an XML resource containing an
    203      * <code>&lt;{@link android.R.styleable#AccessibilityService accessibility-service}&gt;</code>
    204      * tag. This is a a sample XML file configuring an accessibility service:
    205      * <pre> &lt;accessibility-service
    206      *     android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
    207      *     android:packageNames="foo.bar, foo.baz"
    208      *     android:accessibilityFeedbackType="feedbackSpoken"
    209      *     android:notificationTimeout="100"
    210      *     android:accessibilityFlags="flagDefault"
    211      *     android:settingsActivity="foo.bar.TestBackActivity"
    212      *     android:canRetrieveWindowContent="true"
    213      *     . . .
    214      * /&gt;</pre>
    215      */
    216     public static final String SERVICE_META_DATA = "android.accessibilityservice";
    217 
    218     private static final String LOG_TAG = "AccessibilityService";
    219 
    220     private AccessibilityServiceInfo mInfo;
    221 
    222     IAccessibilityServiceConnection mConnection;
    223 
    224     /**
    225      * Callback for {@link android.view.accessibility.AccessibilityEvent}s.
    226      *
    227      * @param event An event.
    228      */
    229     public abstract void onAccessibilityEvent(AccessibilityEvent event);
    230 
    231     /**
    232      * Callback for interrupting the accessibility feedback.
    233      */
    234     public abstract void onInterrupt();
    235 
    236     /**
    237      * This method is a part of the {@link AccessibilityService} lifecycle and is
    238      * called after the system has successfully bound to the service. If is
    239      * convenient to use this method for setting the {@link AccessibilityServiceInfo}.
    240      *
    241      * @see AccessibilityServiceInfo
    242      * @see #setServiceInfo(AccessibilityServiceInfo)
    243      */
    244     protected void onServiceConnected() {
    245 
    246     }
    247 
    248     /**
    249      * Sets the {@link AccessibilityServiceInfo} that describes this service.
    250      * <p>
    251      * Note: You can call this method any time but the info will be picked up after
    252      *       the system has bound to this service and when this method is called thereafter.
    253      *
    254      * @param info The info.
    255      */
    256     public final void setServiceInfo(AccessibilityServiceInfo info) {
    257         mInfo = info;
    258         sendServiceInfo();
    259     }
    260 
    261     /**
    262      * Sets the {@link AccessibilityServiceInfo} for this service if the latter is
    263      * properly set and there is an {@link IAccessibilityServiceConnection} to the
    264      * AccessibilityManagerService.
    265      */
    266     private void sendServiceInfo() {
    267         if (mInfo != null && mConnection != null) {
    268             try {
    269                 mConnection.setServiceInfo(mInfo);
    270             } catch (RemoteException re) {
    271                 Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re);
    272             }
    273         }
    274     }
    275 
    276     /**
    277      * Implement to return the implementation of the internal accessibility
    278      * service interface.
    279      */
    280     @Override
    281     public final IBinder onBind(Intent intent) {
    282         return new IEventListenerWrapper(this);
    283     }
    284 
    285     /**
    286      * Implements the internal {@link IEventListener} interface to convert
    287      * incoming calls to it back to calls on an {@link AccessibilityService}.
    288      */
    289     class IEventListenerWrapper extends IEventListener.Stub
    290             implements HandlerCaller.Callback {
    291 
    292         private static final int DO_SET_SET_CONNECTION = 10;
    293         private static final int DO_ON_INTERRUPT = 20;
    294         private static final int DO_ON_ACCESSIBILITY_EVENT = 30;
    295 
    296         private final HandlerCaller mCaller;
    297 
    298         private final AccessibilityService mTarget;
    299 
    300         public IEventListenerWrapper(AccessibilityService context) {
    301             mTarget = context;
    302             mCaller = new HandlerCaller(context, this);
    303         }
    304 
    305         public void setConnection(IAccessibilityServiceConnection connection) {
    306             Message message = mCaller.obtainMessageO(DO_SET_SET_CONNECTION, connection);
    307             mCaller.sendMessage(message);
    308         }
    309 
    310         public void onInterrupt() {
    311             Message message = mCaller.obtainMessage(DO_ON_INTERRUPT);
    312             mCaller.sendMessage(message);
    313         }
    314 
    315         public void onAccessibilityEvent(AccessibilityEvent event) {
    316             Message message = mCaller.obtainMessageO(DO_ON_ACCESSIBILITY_EVENT, event);
    317             mCaller.sendMessage(message);
    318         }
    319 
    320         public void executeMessage(Message message) {
    321             switch (message.what) {
    322                 case DO_ON_ACCESSIBILITY_EVENT :
    323                     AccessibilityEvent event = (AccessibilityEvent) message.obj;
    324                     if (event != null) {
    325                         mTarget.onAccessibilityEvent(event);
    326                         event.recycle();
    327                     }
    328                     return;
    329                 case DO_ON_INTERRUPT :
    330                     mTarget.onInterrupt();
    331                     return;
    332                 case DO_SET_SET_CONNECTION :
    333                     mConnection = ((IAccessibilityServiceConnection) message.obj);
    334                     mTarget.onServiceConnected();
    335                     return;
    336                 default :
    337                     Log.w(LOG_TAG, "Unknown message type " + message.what);
    338             }
    339         }
    340     }
    341 }
    342