OSDN Git Service

Suppress exceptions for corner-case a11y events.
[android-x86/frameworks-base.git] / core / java / android / view / accessibility / AccessibilityManager.java
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.view.accessibility;
18
19 import android.Manifest;
20 import android.accessibilityservice.AccessibilityServiceInfo;
21 import android.annotation.NonNull;
22 import android.content.Context;
23 import android.content.pm.PackageManager;
24 import android.content.pm.ServiceInfo;
25 import android.os.Binder;
26 import android.os.Handler;
27 import android.os.IBinder;
28 import android.os.Looper;
29 import android.os.Message;
30 import android.os.Process;
31 import android.os.RemoteException;
32 import android.os.ServiceManager;
33 import android.os.SystemClock;
34 import android.os.UserHandle;
35 import android.util.Log;
36 import android.view.IWindow;
37 import android.view.View;
38
39 import java.util.ArrayList;
40 import java.util.Collections;
41 import java.util.List;
42 import java.util.concurrent.CopyOnWriteArrayList;
43
44 /**
45  * System level service that serves as an event dispatch for {@link AccessibilityEvent}s,
46  * and provides facilities for querying the accessibility state of the system.
47  * Accessibility events are generated when something notable happens in the user interface,
48  * for example an {@link android.app.Activity} starts, the focus or selection of a
49  * {@link android.view.View} changes etc. Parties interested in handling accessibility
50  * events implement and register an accessibility service which extends
51  * {@link android.accessibilityservice.AccessibilityService}.
52  * <p>
53  * To obtain a handle to the accessibility manager do the following:
54  * </p>
55  * <p>
56  * <code>
57  * <pre>AccessibilityManager accessibilityManager =
58  *        (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);</pre>
59  * </code>
60  * </p>
61  *
62  * @see AccessibilityEvent
63  * @see AccessibilityNodeInfo
64  * @see android.accessibilityservice.AccessibilityService
65  * @see Context#getSystemService
66  * @see Context#ACCESSIBILITY_SERVICE
67  */
68 public final class AccessibilityManager {
69     private static final boolean DEBUG = false;
70
71     private static final String LOG_TAG = "AccessibilityManager";
72
73     /** @hide */
74     public static final int STATE_FLAG_ACCESSIBILITY_ENABLED = 0x00000001;
75
76     /** @hide */
77     public static final int STATE_FLAG_TOUCH_EXPLORATION_ENABLED = 0x00000002;
78
79     /** @hide */
80     public static final int STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED = 0x00000004;
81
82     /** @hide */
83     public static final int DALTONIZER_DISABLED = -1;
84
85     /** @hide */
86     public static final int DALTONIZER_SIMULATE_MONOCHROMACY = 0;
87
88     /** @hide */
89     public static final int DALTONIZER_CORRECT_DEUTERANOMALY = 12;
90
91     /** @hide */
92     public static final int AUTOCLICK_DELAY_DEFAULT = 600;
93
94     static final Object sInstanceSync = new Object();
95
96     private static AccessibilityManager sInstance;
97
98     private final Object mLock = new Object();
99
100     private IAccessibilityManager mService;
101
102     final int mUserId;
103
104     final Handler mHandler;
105
106     boolean mIsEnabled;
107
108     boolean mIsTouchExplorationEnabled;
109
110     boolean mIsHighTextContrastEnabled;
111
112     private final CopyOnWriteArrayList<AccessibilityStateChangeListener>
113             mAccessibilityStateChangeListeners = new CopyOnWriteArrayList<>();
114
115     private final CopyOnWriteArrayList<TouchExplorationStateChangeListener>
116             mTouchExplorationStateChangeListeners = new CopyOnWriteArrayList<>();
117
118     private final CopyOnWriteArrayList<HighTextContrastChangeListener>
119             mHighTextContrastStateChangeListeners = new CopyOnWriteArrayList<>();
120
121     /**
122      * Listener for the system accessibility state. To listen for changes to the
123      * accessibility state on the device, implement this interface and register
124      * it with the system by calling {@link #addAccessibilityStateChangeListener}.
125      */
126     public interface AccessibilityStateChangeListener {
127
128         /**
129          * Called when the accessibility enabled state changes.
130          *
131          * @param enabled Whether accessibility is enabled.
132          */
133         public void onAccessibilityStateChanged(boolean enabled);
134     }
135
136     /**
137      * Listener for the system touch exploration state. To listen for changes to
138      * the touch exploration state on the device, implement this interface and
139      * register it with the system by calling
140      * {@link #addTouchExplorationStateChangeListener}.
141      */
142     public interface TouchExplorationStateChangeListener {
143
144         /**
145          * Called when the touch exploration enabled state changes.
146          *
147          * @param enabled Whether touch exploration is enabled.
148          */
149         public void onTouchExplorationStateChanged(boolean enabled);
150     }
151
152     /**
153      * Listener for the system high text contrast state. To listen for changes to
154      * the high text contrast state on the device, implement this interface and
155      * register it with the system by calling
156      * {@link #addHighTextContrastStateChangeListener}.
157      *
158      * @hide
159      */
160     public interface HighTextContrastChangeListener {
161
162         /**
163          * Called when the high text contrast enabled state changes.
164          *
165          * @param enabled Whether high text contrast is enabled.
166          */
167         public void onHighTextContrastStateChanged(boolean enabled);
168     }
169
170     private final IAccessibilityManagerClient.Stub mClient =
171             new IAccessibilityManagerClient.Stub() {
172         public void setState(int state) {
173             // We do not want to change this immediately as the applicatoin may
174             // have already checked that accessibility is on and fired an event,
175             // that is now propagating up the view tree, Hence, if accessibility
176             // is now off an exception will be thrown. We want to have the exception
177             // enforcement to guard against apps that fire unnecessary accessibility
178             // events when accessibility is off.
179             mHandler.obtainMessage(MyHandler.MSG_SET_STATE, state, 0).sendToTarget();
180         }
181     };
182
183     /**
184      * Get an AccessibilityManager instance (create one if necessary).
185      *
186      * @param context Context in which this manager operates.
187      *
188      * @hide
189      */
190     public static AccessibilityManager getInstance(Context context) {
191         synchronized (sInstanceSync) {
192             if (sInstance == null) {
193                 final int userId;
194                 if (Binder.getCallingUid() == Process.SYSTEM_UID
195                         || context.checkCallingOrSelfPermission(
196                                 Manifest.permission.INTERACT_ACROSS_USERS)
197                                         == PackageManager.PERMISSION_GRANTED
198                         || context.checkCallingOrSelfPermission(
199                                 Manifest.permission.INTERACT_ACROSS_USERS_FULL)
200                                         == PackageManager.PERMISSION_GRANTED) {
201                     userId = UserHandle.USER_CURRENT;
202                 } else {
203                     userId = UserHandle.myUserId();
204                 }
205                 sInstance = new AccessibilityManager(context, null, userId);
206             }
207         }
208         return sInstance;
209     }
210
211     /**
212      * Create an instance.
213      *
214      * @param context A {@link Context}.
215      * @param service An interface to the backing service.
216      * @param userId User id under which to run.
217      *
218      * @hide
219      */
220     public AccessibilityManager(Context context, IAccessibilityManager service, int userId) {
221         mHandler = new MyHandler(context.getMainLooper());
222         mUserId = userId;
223         synchronized (mLock) {
224             tryConnectToServiceLocked(service);
225         }
226     }
227
228     /**
229      * @hide
230      */
231     public IAccessibilityManagerClient getClient() {
232         return mClient;
233     }
234
235     /**
236      * Returns if the accessibility in the system is enabled.
237      *
238      * @return True if accessibility is enabled, false otherwise.
239      */
240     public boolean isEnabled() {
241         synchronized (mLock) {
242             IAccessibilityManager service = getServiceLocked();
243             if (service == null) {
244                 return false;
245             }
246             return mIsEnabled;
247         }
248     }
249
250     /**
251      * Returns if the touch exploration in the system is enabled.
252      *
253      * @return True if touch exploration is enabled, false otherwise.
254      */
255     public boolean isTouchExplorationEnabled() {
256         synchronized (mLock) {
257             IAccessibilityManager service = getServiceLocked();
258             if (service == null) {
259                 return false;
260             }
261             return mIsTouchExplorationEnabled;
262         }
263     }
264
265     /**
266      * Returns if the high text contrast in the system is enabled.
267      * <p>
268      * <strong>Note:</strong> You need to query this only if you application is
269      * doing its own rendering and does not rely on the platform rendering pipeline.
270      * </p>
271      *
272      * @return True if high text contrast is enabled, false otherwise.
273      *
274      * @hide
275      */
276     public boolean isHighTextContrastEnabled() {
277         synchronized (mLock) {
278             IAccessibilityManager service = getServiceLocked();
279             if (service == null) {
280                 return false;
281             }
282             return mIsHighTextContrastEnabled;
283         }
284     }
285
286     /**
287      * Sends an {@link AccessibilityEvent}.
288      *
289      * @param event The event to send.
290      *
291      * @throws IllegalStateException if accessibility is not enabled.
292      *
293      * <strong>Note:</strong> The preferred mechanism for sending custom accessibility
294      * events is through calling
295      * {@link android.view.ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent)}
296      * instead of this method to allow predecessors to augment/filter events sent by
297      * their descendants.
298      */
299     public void sendAccessibilityEvent(AccessibilityEvent event) {
300         final IAccessibilityManager service;
301         final int userId;
302         synchronized (mLock) {
303             service = getServiceLocked();
304             if (service == null) {
305                 return;
306             }
307             if (!mIsEnabled) {
308                 Looper myLooper = Looper.myLooper();
309                 if (myLooper == Looper.getMainLooper()) {
310                     throw new IllegalStateException(
311                             "Accessibility off. Did you forget to check that?");
312                 } else {
313                     // If we're not running on the thread with the main looper, it's possible for
314                     // the state of accessibility to change between checking isEnabled and
315                     // calling this method. So just log the error rather than throwing the
316                     // exception.
317                     Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled");
318                     return;
319                 }
320             }
321             userId = mUserId;
322         }
323         boolean doRecycle = false;
324         try {
325             event.setEventTime(SystemClock.uptimeMillis());
326             // it is possible that this manager is in the same process as the service but
327             // client using it is called through Binder from another process. Example: MMS
328             // app adds a SMS notification and the NotificationManagerService calls this method
329             long identityToken = Binder.clearCallingIdentity();
330             doRecycle = service.sendAccessibilityEvent(event, userId);
331             Binder.restoreCallingIdentity(identityToken);
332             if (DEBUG) {
333                 Log.i(LOG_TAG, event + " sent");
334             }
335         } catch (RemoteException re) {
336             Log.e(LOG_TAG, "Error during sending " + event + " ", re);
337         } finally {
338             if (doRecycle) {
339                 event.recycle();
340             }
341         }
342     }
343
344     /**
345      * Requests feedback interruption from all accessibility services.
346      */
347     public void interrupt() {
348         final IAccessibilityManager service;
349         final int userId;
350         synchronized (mLock) {
351             service = getServiceLocked();
352             if (service == null) {
353                 return;
354             }
355             if (!mIsEnabled) {
356                 throw new IllegalStateException("Accessibility off. Did you forget to check that?");
357             }
358             userId = mUserId;
359         }
360         try {
361             service.interrupt(userId);
362             if (DEBUG) {
363                 Log.i(LOG_TAG, "Requested interrupt from all services");
364             }
365         } catch (RemoteException re) {
366             Log.e(LOG_TAG, "Error while requesting interrupt from all services. ", re);
367         }
368     }
369
370     /**
371      * Returns the {@link ServiceInfo}s of the installed accessibility services.
372      *
373      * @return An unmodifiable list with {@link ServiceInfo}s.
374      *
375      * @deprecated Use {@link #getInstalledAccessibilityServiceList()}
376      */
377     @Deprecated
378     public List<ServiceInfo> getAccessibilityServiceList() {
379         List<AccessibilityServiceInfo> infos = getInstalledAccessibilityServiceList();
380         List<ServiceInfo> services = new ArrayList<>();
381         final int infoCount = infos.size();
382         for (int i = 0; i < infoCount; i++) {
383             AccessibilityServiceInfo info = infos.get(i);
384             services.add(info.getResolveInfo().serviceInfo);
385         }
386         return Collections.unmodifiableList(services);
387     }
388
389     /**
390      * Returns the {@link AccessibilityServiceInfo}s of the installed accessibility services.
391      *
392      * @return An unmodifiable list with {@link AccessibilityServiceInfo}s.
393      */
394     public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() {
395         final IAccessibilityManager service;
396         final int userId;
397         synchronized (mLock) {
398             service = getServiceLocked();
399             if (service == null) {
400                 return Collections.emptyList();
401             }
402             userId = mUserId;
403         }
404
405         List<AccessibilityServiceInfo> services = null;
406         try {
407             services = service.getInstalledAccessibilityServiceList(userId);
408             if (DEBUG) {
409                 Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
410             }
411         } catch (RemoteException re) {
412             Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
413         }
414         if (services != null) {
415             return Collections.unmodifiableList(services);
416         } else {
417             return Collections.emptyList();
418         }
419     }
420
421     /**
422      * Returns the {@link AccessibilityServiceInfo}s of the enabled accessibility services
423      * for a given feedback type.
424      *
425      * @param feedbackTypeFlags The feedback type flags.
426      * @return An unmodifiable list with {@link AccessibilityServiceInfo}s.
427      *
428      * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE
429      * @see AccessibilityServiceInfo#FEEDBACK_GENERIC
430      * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC
431      * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN
432      * @see AccessibilityServiceInfo#FEEDBACK_VISUAL
433      * @see AccessibilityServiceInfo#FEEDBACK_BRAILLE
434      */
435     public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
436             int feedbackTypeFlags) {
437         final IAccessibilityManager service;
438         final int userId;
439         synchronized (mLock) {
440             service = getServiceLocked();
441             if (service == null) {
442                 return Collections.emptyList();
443             }
444             userId = mUserId;
445         }
446
447         List<AccessibilityServiceInfo> services = null;
448         try {
449             services = service.getEnabledAccessibilityServiceList(feedbackTypeFlags, userId);
450             if (DEBUG) {
451                 Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
452             }
453         } catch (RemoteException re) {
454             Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
455         }
456         if (services != null) {
457             return Collections.unmodifiableList(services);
458         } else {
459             return Collections.emptyList();
460         }
461     }
462
463     /**
464      * Registers an {@link AccessibilityStateChangeListener} for changes in
465      * the global accessibility state of the system.
466      *
467      * @param listener The listener.
468      * @return True if successfully registered.
469      */
470     public boolean addAccessibilityStateChangeListener(
471             @NonNull AccessibilityStateChangeListener listener) {
472         // Final CopyOnWriteArrayList - no lock needed.
473         return mAccessibilityStateChangeListeners.add(listener);
474     }
475
476     /**
477      * Unregisters an {@link AccessibilityStateChangeListener}.
478      *
479      * @param listener The listener.
480      * @return True if successfully unregistered.
481      */
482     public boolean removeAccessibilityStateChangeListener(
483             @NonNull AccessibilityStateChangeListener listener) {
484         // Final CopyOnWriteArrayList - no lock needed.
485         return mAccessibilityStateChangeListeners.remove(listener);
486     }
487
488     /**
489      * Registers a {@link TouchExplorationStateChangeListener} for changes in
490      * the global touch exploration state of the system.
491      *
492      * @param listener The listener.
493      * @return True if successfully registered.
494      */
495     public boolean addTouchExplorationStateChangeListener(
496             @NonNull TouchExplorationStateChangeListener listener) {
497         // Final CopyOnWriteArrayList - no lock needed.
498         return mTouchExplorationStateChangeListeners.add(listener);
499     }
500
501     /**
502      * Unregisters a {@link TouchExplorationStateChangeListener}.
503      *
504      * @param listener The listener.
505      * @return True if successfully unregistered.
506      */
507     public boolean removeTouchExplorationStateChangeListener(
508             @NonNull TouchExplorationStateChangeListener listener) {
509         // Final CopyOnWriteArrayList - no lock needed.
510         return mTouchExplorationStateChangeListeners.remove(listener);
511     }
512
513     /**
514      * Registers a {@link HighTextContrastChangeListener} for changes in
515      * the global high text contrast state of the system.
516      *
517      * @param listener The listener.
518      * @return True if successfully registered.
519      *
520      * @hide
521      */
522     public boolean addHighTextContrastStateChangeListener(
523             @NonNull HighTextContrastChangeListener listener) {
524         // Final CopyOnWriteArrayList - no lock needed.
525         return mHighTextContrastStateChangeListeners.add(listener);
526     }
527
528     /**
529      * Unregisters a {@link HighTextContrastChangeListener}.
530      *
531      * @param listener The listener.
532      * @return True if successfully unregistered.
533      *
534      * @hide
535      */
536     public boolean removeHighTextContrastStateChangeListener(
537             @NonNull HighTextContrastChangeListener listener) {
538         // Final CopyOnWriteArrayList - no lock needed.
539         return mHighTextContrastStateChangeListeners.remove(listener);
540     }
541
542     /**
543      * Sets the current state and notifies listeners, if necessary.
544      *
545      * @param stateFlags The state flags.
546      */
547     private void setStateLocked(int stateFlags) {
548         final boolean enabled = (stateFlags & STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
549         final boolean touchExplorationEnabled =
550                 (stateFlags & STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0;
551         final boolean highTextContrastEnabled =
552                 (stateFlags & STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED) != 0;
553
554         final boolean wasEnabled = mIsEnabled;
555         final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled;
556         final boolean wasHighTextContrastEnabled = mIsHighTextContrastEnabled;
557
558         // Ensure listeners get current state from isZzzEnabled() calls.
559         mIsEnabled = enabled;
560         mIsTouchExplorationEnabled = touchExplorationEnabled;
561         mIsHighTextContrastEnabled = highTextContrastEnabled;
562
563         if (wasEnabled != enabled) {
564             mHandler.sendEmptyMessage(MyHandler.MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED);
565         }
566
567         if (wasTouchExplorationEnabled != touchExplorationEnabled) {
568             mHandler.sendEmptyMessage(MyHandler.MSG_NOTIFY_EXPLORATION_STATE_CHANGED);
569         }
570
571         if (wasHighTextContrastEnabled != highTextContrastEnabled) {
572             mHandler.sendEmptyMessage(MyHandler.MSG_NOTIFY_HIGH_TEXT_CONTRAST_STATE_CHANGED);
573         }
574     }
575
576     /**
577      * Adds an accessibility interaction connection interface for a given window.
578      * @param windowToken The window token to which a connection is added.
579      * @param connection The connection.
580      *
581      * @hide
582      */
583     public int addAccessibilityInteractionConnection(IWindow windowToken,
584             IAccessibilityInteractionConnection connection) {
585         final IAccessibilityManager service;
586         final int userId;
587         synchronized (mLock) {
588             service = getServiceLocked();
589             if (service == null) {
590                 return View.NO_ID;
591             }
592             userId = mUserId;
593         }
594         try {
595             return service.addAccessibilityInteractionConnection(windowToken, connection, userId);
596         } catch (RemoteException re) {
597             Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re);
598         }
599         return View.NO_ID;
600     }
601
602     /**
603      * Removed an accessibility interaction connection interface for a given window.
604      * @param windowToken The window token to which a connection is removed.
605      *
606      * @hide
607      */
608     public void removeAccessibilityInteractionConnection(IWindow windowToken) {
609         final IAccessibilityManager service;
610         synchronized (mLock) {
611             service = getServiceLocked();
612             if (service == null) {
613                 return;
614             }
615         }
616         try {
617             service.removeAccessibilityInteractionConnection(windowToken);
618         } catch (RemoteException re) {
619             Log.e(LOG_TAG, "Error while removing an accessibility interaction connection. ", re);
620         }
621     }
622
623     private  IAccessibilityManager getServiceLocked() {
624         if (mService == null) {
625             tryConnectToServiceLocked(null);
626         }
627         return mService;
628     }
629
630     private void tryConnectToServiceLocked(IAccessibilityManager service) {
631         if (service == null) {
632             IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
633             if (iBinder == null) {
634                 return;
635             }
636             service = IAccessibilityManager.Stub.asInterface(iBinder);
637         }
638
639         try {
640             final int stateFlags = service.addClient(mClient, mUserId);
641             setStateLocked(stateFlags);
642             mService = service;
643         } catch (RemoteException re) {
644             Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
645         }
646     }
647
648     /**
649      * Notifies the registered {@link AccessibilityStateChangeListener}s.
650      */
651     private void handleNotifyAccessibilityStateChanged() {
652         final boolean isEnabled;
653         synchronized (mLock) {
654             isEnabled = mIsEnabled;
655         }
656         // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
657         for (AccessibilityStateChangeListener listener :mAccessibilityStateChangeListeners) {
658             listener.onAccessibilityStateChanged(isEnabled);
659         }
660     }
661
662     /**
663      * Notifies the registered {@link TouchExplorationStateChangeListener}s.
664      */
665     private void handleNotifyTouchExplorationStateChanged() {
666         final boolean isTouchExplorationEnabled;
667         synchronized (mLock) {
668             isTouchExplorationEnabled = mIsTouchExplorationEnabled;
669         }
670         // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
671         for (TouchExplorationStateChangeListener listener :mTouchExplorationStateChangeListeners) {
672             listener.onTouchExplorationStateChanged(isTouchExplorationEnabled);
673         }
674     }
675
676     /**
677      * Notifies the registered {@link HighTextContrastChangeListener}s.
678      */
679     private void handleNotifyHighTextContrastStateChanged() {
680         final boolean isHighTextContrastEnabled;
681         synchronized (mLock) {
682             isHighTextContrastEnabled = mIsHighTextContrastEnabled;
683         }
684         // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
685         for (HighTextContrastChangeListener listener : mHighTextContrastStateChangeListeners) {
686             listener.onHighTextContrastStateChanged(isHighTextContrastEnabled);
687         }
688     }
689
690     private final class MyHandler extends Handler {
691         public static final int MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED = 1;
692         public static final int MSG_NOTIFY_EXPLORATION_STATE_CHANGED = 2;
693         public static final int MSG_NOTIFY_HIGH_TEXT_CONTRAST_STATE_CHANGED = 3;
694         public static final int MSG_SET_STATE = 4;
695
696         public MyHandler(Looper looper) {
697             super(looper, null, false);
698         }
699
700         @Override
701         public void handleMessage(Message message) {
702             switch (message.what) {
703                 case MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED: {
704                     handleNotifyAccessibilityStateChanged();
705                 } break;
706
707                 case MSG_NOTIFY_EXPLORATION_STATE_CHANGED: {
708                     handleNotifyTouchExplorationStateChanged();
709                 } break;
710
711                 case MSG_NOTIFY_HIGH_TEXT_CONTRAST_STATE_CHANGED: {
712                     handleNotifyHighTextContrastStateChanged();
713                 } break;
714
715                 case MSG_SET_STATE: {
716                     // See comment at mClient
717                     final int state = message.arg1;
718                     synchronized (mLock) {
719                         setStateLocked(state);
720                     }
721                 } break;
722             }
723         }
724     }
725 }