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 android.content.ComponentName; 20 import android.content.Context; 21 import android.content.pm.PackageManager; 22 import android.content.pm.PackageManager.NameNotFoundException; 23 import android.content.pm.ResolveInfo; 24 import android.content.pm.ServiceInfo; 25 import android.content.res.Resources; 26 import android.content.res.TypedArray; 27 import android.content.res.XmlResourceParser; 28 import android.os.Build; 29 import android.os.Parcel; 30 import android.os.Parcelable; 31 import android.util.AttributeSet; 32 import android.util.SparseArray; 33 import android.util.TypedValue; 34 import android.util.Xml; 35 import android.view.View; 36 import android.view.accessibility.AccessibilityEvent; 37 import android.view.accessibility.AccessibilityNodeInfo; 38 39 import org.xmlpull.v1.XmlPullParser; 40 import org.xmlpull.v1.XmlPullParserException; 41 42 import com.android.internal.R; 43 44 import java.io.IOException; 45 import java.util.ArrayList; 46 import java.util.Collections; 47 import java.util.List; 48 49 /** 50 * This class describes an {@link AccessibilityService}. The system notifies an 51 * {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s 52 * according to the information encapsulated in this class. 53 * 54 * <div class="special reference"> 55 * <h3>Developer Guides</h3> 56 * <p>For more information about creating AccessibilityServices, read the 57 * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a> 58 * developer guide.</p> 59 * </div> 60 * 61 * @attr ref android.R.styleable#AccessibilityService_accessibilityEventTypes 62 * @attr ref android.R.styleable#AccessibilityService_accessibilityFeedbackType 63 * @attr ref android.R.styleable#AccessibilityService_accessibilityFlags 64 * @attr ref android.R.styleable#AccessibilityService_canRequestEnhancedWebAccessibility 65 * @attr ref android.R.styleable#AccessibilityService_canRequestFilterKeyEvents 66 * @attr ref android.R.styleable#AccessibilityService_canRequestTouchExplorationMode 67 * @attr ref android.R.styleable#AccessibilityService_canRetrieveWindowContent 68 * @attr ref android.R.styleable#AccessibilityService_description 69 * @attr ref android.R.styleable#AccessibilityService_notificationTimeout 70 * @attr ref android.R.styleable#AccessibilityService_packageNames 71 * @attr ref android.R.styleable#AccessibilityService_settingsActivity 72 * 73 * @see AccessibilityService 74 * @see android.view.accessibility.AccessibilityEvent 75 * @see android.view.accessibility.AccessibilityManager 76 */ 77 public class AccessibilityServiceInfo implements Parcelable { 78 79 private static final String TAG_ACCESSIBILITY_SERVICE = "accessibility-service"; 80 81 /** 82 * Capability: This accessibility service can retrieve the active window content. 83 * @see android.R.styleable#AccessibilityService_canRetrieveWindowContent 84 */ 85 public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 0x00000001; 86 87 /** 88 * Capability: This accessibility service can request touch exploration mode in which 89 * touched items are spoken aloud and the UI can be explored via gestures. 90 * @see android.R.styleable#AccessibilityService_canRequestTouchExplorationMode 91 */ 92 public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 0x00000002; 93 94 /** 95 * Capability: This accessibility service can request enhanced web accessibility 96 * enhancements. For example, installing scripts to make app content more accessible. 97 * @see android.R.styleable#AccessibilityService_canRequestEnhancedWebAccessibility 98 */ 99 public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x00000004; 100 101 /** 102 * Capability: This accessibility service can request to filter the key event stream. 103 * @see android.R.styleable#AccessibilityService_canRequestFilterKeyEvents 104 */ 105 public static final int CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS = 0x00000008; 106 107 private static final SparseArray<CapabilityInfo> sAvailableCapabilityInfos = 108 new SparseArray<CapabilityInfo>(); 109 static { 110 sAvailableCapabilityInfos.put(CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT, 111 new CapabilityInfo(CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT, 112 R.string.capability_title_canRetrieveWindowContent, 113 R.string.capability_desc_canRetrieveWindowContent)); 114 sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION, 115 new CapabilityInfo(CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION, 116 R.string.capability_title_canRequestTouchExploration, 117 R.string.capability_desc_canRequestTouchExploration)); 118 sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY, 119 new CapabilityInfo(CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY, 120 R.string.capability_title_canRequestEnhancedWebAccessibility, 121 R.string.capability_desc_canRequestEnhancedWebAccessibility)); 122 sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS, 123 new CapabilityInfo(CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS, 124 R.string.capability_title_canRequestFilterKeyEvents, 125 R.string.capability_desc_canRequestFilterKeyEvents)); 126 } 127 128 /** 129 * Denotes spoken feedback. 130 */ 131 public static final int FEEDBACK_SPOKEN = 0x0000001; 132 133 /** 134 * Denotes haptic feedback. 135 */ 136 public static final int FEEDBACK_HAPTIC = 0x0000002; 137 138 /** 139 * Denotes audible (not spoken) feedback. 140 */ 141 public static final int FEEDBACK_AUDIBLE = 0x0000004; 142 143 /** 144 * Denotes visual feedback. 145 */ 146 public static final int FEEDBACK_VISUAL = 0x0000008; 147 148 /** 149 * Denotes generic feedback. 150 */ 151 public static final int FEEDBACK_GENERIC = 0x0000010; 152 153 /** 154 * Denotes braille feedback. 155 */ 156 public static final int FEEDBACK_BRAILLE = 0x0000020; 157 158 /** 159 * Mask for all feedback types. 160 * 161 * @see #FEEDBACK_SPOKEN 162 * @see #FEEDBACK_HAPTIC 163 * @see #FEEDBACK_AUDIBLE 164 * @see #FEEDBACK_VISUAL 165 * @see #FEEDBACK_GENERIC 166 * @see #FEEDBACK_BRAILLE 167 */ 168 public static final int FEEDBACK_ALL_MASK = 0xFFFFFFFF; 169 170 /** 171 * If an {@link AccessibilityService} is the default for a given type. 172 * Default service is invoked only if no package specific one exists. In case of 173 * more than one package specific service only the earlier registered is notified. 174 */ 175 public static final int DEFAULT = 0x0000001; 176 177 /** 178 * If this flag is set the system will regard views that are not important 179 * for accessibility in addition to the ones that are important for accessibility. 180 * That is, views that are marked as not important for accessibility via 181 * {@link View#IMPORTANT_FOR_ACCESSIBILITY_NO} or 182 * {@link View#IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS} and views that are 183 * marked as potentially important for accessibility via 184 * {@link View#IMPORTANT_FOR_ACCESSIBILITY_AUTO} for which the system has determined 185 * that are not important for accessibility, are reported while querying the window 186 * content and also the accessibility service will receive accessibility events from 187 * them. 188 * <p> 189 * <strong>Note:</strong> For accessibility services targeting API version 190 * {@link Build.VERSION_CODES#JELLY_BEAN} or higher this flag has to be explicitly 191 * set for the system to regard views that are not important for accessibility. For 192 * accessibility services targeting API version lower than 193 * {@link Build.VERSION_CODES#JELLY_BEAN} this flag is ignored and all views are 194 * regarded for accessibility purposes. 195 * </p> 196 * <p> 197 * Usually views not important for accessibility are layout managers that do not 198 * react to user actions, do not draw any content, and do not have any special 199 * semantics in the context of the screen content. For example, a three by three 200 * grid can be implemented as three horizontal linear layouts and one vertical, 201 * or three vertical linear layouts and one horizontal, or one grid layout, etc. 202 * In this context the actual layout mangers used to achieve the grid configuration 203 * are not important, rather it is important that there are nine evenly distributed 204 * elements. 205 * </p> 206 */ 207 public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x0000002; 208 209 /** 210 * This flag requests that the system gets into touch exploration mode. 211 * In this mode a single finger moving on the screen behaves as a mouse 212 * pointer hovering over the user interface. The system will also detect 213 * certain gestures performed on the touch screen and notify this service. 214 * The system will enable touch exploration mode if there is at least one 215 * accessibility service that has this flag set. Hence, clearing this 216 * flag does not guarantee that the device will not be in touch exploration 217 * mode since there may be another enabled service that requested it. 218 * <p> 219 * For accessibility services targeting API version higher than 220 * {@link Build.VERSION_CODES#JELLY_BEAN_MR1} that want to set 221 * this flag have to declare this capability in their meta-data by setting 222 * the attribute {@link android.R.attr#canRequestTouchExplorationMode 223 * canRequestTouchExplorationMode} to true, otherwise this flag will 224 * be ignored. For how to declare the meta-data of a service refer to 225 * {@value AccessibilityService#SERVICE_META_DATA}. 226 * </p> 227 * <p> 228 * Services targeting API version equal to or lower than 229 * {@link Build.VERSION_CODES#JELLY_BEAN_MR1} will work normally, i.e. 230 * the first time they are run, if this flag is specified, a dialog is 231 * shown to the user to confirm enabling explore by touch. 232 * </p> 233 * @see android.R.styleable#AccessibilityService_canRequestTouchExplorationMode 234 */ 235 public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 0x0000004; 236 237 /** 238 * This flag requests from the system to enable web accessibility enhancing 239 * extensions. Such extensions aim to provide improved accessibility support 240 * for content presented in a {@link android.webkit.WebView}. An example of such 241 * an extension is injecting JavaScript from a secure source. The system will enable 242 * enhanced web accessibility if there is at least one accessibility service 243 * that has this flag set. Hence, clearing this flag does not guarantee that the 244 * device will not have enhanced web accessibility enabled since there may be 245 * another enabled service that requested it. 246 * <p> 247 * Services that want to set this flag have to declare this capability 248 * in their meta-data by setting the attribute {@link android.R.attr 249 * #canRequestEnhancedWebAccessibility canRequestEnhancedWebAccessibility} to 250 * true, otherwise this flag will be ignored. For how to declare the meta-data 251 * of a service refer to {@value AccessibilityService#SERVICE_META_DATA}. 252 * </p> 253 * @see android.R.styleable#AccessibilityService_canRequestEnhancedWebAccessibility 254 */ 255 public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x00000008; 256 257 /** 258 * This flag requests that the {@link AccessibilityNodeInfo}s obtained 259 * by an {@link AccessibilityService} contain the id of the source view. 260 * The source view id will be a fully qualified resource name of the 261 * form "package:id/name", for example "foo.bar:id/my_list", and it is 262 * useful for UI test automation. This flag is not set by default. 263 */ 264 public static final int FLAG_REPORT_VIEW_IDS = 0x00000010; 265 266 /** 267 * This flag requests from the system to filter key events. If this flag 268 * is set the accessibility service will receive the key events before 269 * applications allowing it implement global shortcuts. Setting this flag 270 * does not guarantee that this service will filter key events since only 271 * one service can do so at any given time. This avoids user confusion due 272 * to behavior change in case different key filtering services are enabled. 273 * If there is already another key filtering service enabled, this one will 274 * not receive key events. 275 * <p> 276 * Services that want to set this flag have to declare this capability 277 * in their meta-data by setting the attribute {@link android.R.attr 278 * #canRequestFilterKeyEvents canRequestFilterKeyEvents} to true, 279 * otherwise this flag will be ignored. For how to declare the meta-data 280 * of a service refer to {@value AccessibilityService#SERVICE_META_DATA}. 281 * </p> 282 * @see android.R.styleable#AccessibilityService_canRequestFilterKeyEvents 283 */ 284 public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 0x00000020; 285 286 /** 287 * The event types an {@link AccessibilityService} is interested in. 288 * <p> 289 * <strong>Can be dynamically set at runtime.</strong> 290 * </p> 291 * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_CLICKED 292 * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_LONG_CLICKED 293 * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_FOCUSED 294 * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SELECTED 295 * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED 296 * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED 297 * @see android.view.accessibility.AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED 298 * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START 299 * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END 300 * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_ENTER 301 * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_EXIT 302 * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SCROLLED 303 * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED 304 * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED 305 */ 306 public int eventTypes; 307 308 /** 309 * The package names an {@link AccessibilityService} is interested in. Setting 310 * to <code>null</code> is equivalent to all packages. 311 * <p> 312 * <strong>Can be dynamically set at runtime.</strong> 313 * </p> 314 */ 315 public String[] packageNames; 316 317 /** 318 * The feedback type an {@link AccessibilityService} provides. 319 * <p> 320 * <strong>Can be dynamically set at runtime.</strong> 321 * </p> 322 * @see #FEEDBACK_AUDIBLE 323 * @see #FEEDBACK_GENERIC 324 * @see #FEEDBACK_HAPTIC 325 * @see #FEEDBACK_SPOKEN 326 * @see #FEEDBACK_VISUAL 327 * @see #FEEDBACK_BRAILLE 328 */ 329 public int feedbackType; 330 331 /** 332 * The timeout after the most recent event of a given type before an 333 * {@link AccessibilityService} is notified. 334 * <p> 335 * <strong>Can be dynamically set at runtime.</strong>. 336 * </p> 337 * <p> 338 * <strong>Note:</strong> The event notification timeout is useful to avoid propagating 339 * events to the client too frequently since this is accomplished via an expensive 340 * interprocess call. One can think of the timeout as a criteria to determine when 341 * event generation has settled down. 342 */ 343 public long notificationTimeout; 344 345 /** 346 * This field represents a set of flags used for configuring an 347 * {@link AccessibilityService}. 348 * <p> 349 * <strong>Can be dynamically set at runtime.</strong> 350 * </p> 351 * @see #DEFAULT 352 * @see #FLAG_INCLUDE_NOT_IMPORTANT_VIEWS 353 * @see #FLAG_REQUEST_TOUCH_EXPLORATION_MODE 354 * @see #FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY 355 * @see #FLAG_REQUEST_FILTER_KEY_EVENTS 356 * @see #FLAG_REPORT_VIEW_IDS 357 */ 358 public int flags; 359 360 /** 361 * The unique string Id to identify the accessibility service. 362 */ 363 private String mId; 364 365 /** 366 * The Service that implements this accessibility service component. 367 */ 368 private ResolveInfo mResolveInfo; 369 370 /** 371 * The accessibility service setting activity's name, used by the system 372 * settings to launch the setting activity of this accessibility service. 373 */ 374 private String mSettingsActivityName; 375 376 /** 377 * Bit mask with capabilities of this service. 378 */ 379 private int mCapabilities; 380 381 /** 382 * Resource id of the description of the accessibility service. 383 */ 384 private int mDescriptionResId; 385 386 /** 387 * Non localized description of the accessibility service. 388 */ 389 private String mNonLocalizedDescription; 390 391 /** 392 * Creates a new instance. 393 */ 394 public AccessibilityServiceInfo() { 395 /* do nothing */ 396 } 397 398 /** 399 * Creates a new instance. 400 * 401 * @param resolveInfo The service resolve info. 402 * @param context Context for accessing resources. 403 * @throws XmlPullParserException If a XML parsing error occurs. 404 * @throws IOException If a XML parsing error occurs. 405 * 406 * @hide 407 */ 408 public AccessibilityServiceInfo(ResolveInfo resolveInfo, Context context) 409 throws XmlPullParserException, IOException { 410 ServiceInfo serviceInfo = resolveInfo.serviceInfo; 411 mId = new ComponentName(serviceInfo.packageName, serviceInfo.name).flattenToShortString(); 412 mResolveInfo = resolveInfo; 413 414 XmlResourceParser parser = null; 415 416 try { 417 PackageManager packageManager = context.getPackageManager(); 418 parser = serviceInfo.loadXmlMetaData(packageManager, 419 AccessibilityService.SERVICE_META_DATA); 420 if (parser == null) { 421 return; 422 } 423 424 int type = 0; 425 while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) { 426 type = parser.next(); 427 } 428 429 String nodeName = parser.getName(); 430 if (!TAG_ACCESSIBILITY_SERVICE.equals(nodeName)) { 431 throw new XmlPullParserException( "Meta-data does not start with" 432 + TAG_ACCESSIBILITY_SERVICE + " tag"); 433 } 434 435 AttributeSet allAttributes = Xml.asAttributeSet(parser); 436 Resources resources = packageManager.getResourcesForApplication( 437 serviceInfo.applicationInfo); 438 TypedArray asAttributes = resources.obtainAttributes(allAttributes, 439 com.android.internal.R.styleable.AccessibilityService); 440 eventTypes = asAttributes.getInt( 441 com.android.internal.R.styleable.AccessibilityService_accessibilityEventTypes, 442 0); 443 String packageNamez = asAttributes.getString( 444 com.android.internal.R.styleable.AccessibilityService_packageNames); 445 if (packageNamez != null) { 446 packageNames = packageNamez.split("(\\s)*,(\\s)*"); 447 } 448 feedbackType = asAttributes.getInt( 449 com.android.internal.R.styleable.AccessibilityService_accessibilityFeedbackType, 450 0); 451 notificationTimeout = asAttributes.getInt( 452 com.android.internal.R.styleable.AccessibilityService_notificationTimeout, 453 0); 454 flags = asAttributes.getInt( 455 com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0); 456 mSettingsActivityName = asAttributes.getString( 457 com.android.internal.R.styleable.AccessibilityService_settingsActivity); 458 if (asAttributes.getBoolean(com.android.internal.R.styleable 459 .AccessibilityService_canRetrieveWindowContent, false)) { 460 mCapabilities |= CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT; 461 } 462 if (asAttributes.getBoolean(com.android.internal.R.styleable 463 .AccessibilityService_canRequestTouchExplorationMode, false)) { 464 mCapabilities |= CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION; 465 } 466 if (asAttributes.getBoolean(com.android.internal.R.styleable 467 .AccessibilityService_canRequestEnhancedWebAccessibility, false)) { 468 mCapabilities |= CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY; 469 } 470 if (asAttributes.getBoolean(com.android.internal.R.styleable 471 .AccessibilityService_canRequestFilterKeyEvents, false)) { 472 mCapabilities |= CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS; 473 } 474 TypedValue peekedValue = asAttributes.peekValue( 475 com.android.internal.R.styleable.AccessibilityService_description); 476 if (peekedValue != null) { 477 mDescriptionResId = peekedValue.resourceId; 478 CharSequence nonLocalizedDescription = peekedValue.coerceToString(); 479 if (nonLocalizedDescription != null) { 480 mNonLocalizedDescription = nonLocalizedDescription.toString().trim(); 481 } 482 } 483 asAttributes.recycle(); 484 } catch (NameNotFoundException e) { 485 throw new XmlPullParserException( "Unable to create context for: " 486 + serviceInfo.packageName); 487 } finally { 488 if (parser != null) { 489 parser.close(); 490 } 491 } 492 } 493 494 /** 495 * Updates the properties that an AccessibilitySerivice can change dynamically. 496 * 497 * @param other The info from which to update the properties. 498 * 499 * @hide 500 */ 501 public void updateDynamicallyConfigurableProperties(AccessibilityServiceInfo other) { 502 eventTypes = other.eventTypes; 503 packageNames = other.packageNames; 504 feedbackType = other.feedbackType; 505 notificationTimeout = other.notificationTimeout; 506 flags = other.flags; 507 } 508 509 /** 510 * @hide 511 */ 512 public void setComponentName(ComponentName component) { 513 mId = component.flattenToShortString(); 514 } 515 516 /** 517 * The accessibility service id. 518 * <p> 519 * <strong>Generated by the system.</strong> 520 * </p> 521 * @return The id. 522 */ 523 public String getId() { 524 return mId; 525 } 526 527 /** 528 * The service {@link ResolveInfo}. 529 * <p> 530 * <strong>Generated by the system.</strong> 531 * </p> 532 * @return The info. 533 */ 534 public ResolveInfo getResolveInfo() { 535 return mResolveInfo; 536 } 537 538 /** 539 * The settings activity name. 540 * <p> 541 * <strong>Statically set from 542 * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> 543 * </p> 544 * @return The settings activity name. 545 */ 546 public String getSettingsActivityName() { 547 return mSettingsActivityName; 548 } 549 550 /** 551 * Whether this service can retrieve the current window's content. 552 * <p> 553 * <strong>Statically set from 554 * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> 555 * </p> 556 * @return True if window content can be retrieved. 557 * 558 * @deprecated Use {@link #getCapabilities()}. 559 */ 560 public boolean getCanRetrieveWindowContent() { 561 return (mCapabilities & CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0; 562 } 563 564 /** 565 * Returns the bit mask of capabilities this accessibility service has such as 566 * being able to retrieve the active window content, etc. 567 * 568 * @return The capability bit mask. 569 * 570 * @see #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT 571 * @see #CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION 572 * @see #CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY 573 * @see #CAPABILITY_FILTER_KEY_EVENTS 574 */ 575 public int getCapabilities() { 576 return mCapabilities; 577 } 578 579 /** 580 * Sets the bit mask of capabilities this accessibility service has such as 581 * being able to retrieve the active window content, etc. 582 * 583 * @param capabilities The capability bit mask. 584 * 585 * @see #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT 586 * @see #CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION 587 * @see #CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY 588 * @see #CAPABILITY_FILTER_KEY_EVENTS 589 * 590 * @hide 591 */ 592 public void setCapabilities(int capabilities) { 593 mCapabilities = capabilities; 594 } 595 596 /** 597 * Gets the non-localized description of the accessibility service. 598 * <p> 599 * <strong>Statically set from 600 * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> 601 * </p> 602 * @return The description. 603 * 604 * @deprecated Use {@link #loadDescription(PackageManager)}. 605 */ 606 public String getDescription() { 607 return mNonLocalizedDescription; 608 } 609 610 /** 611 * The localized description of the accessibility service. 612 * <p> 613 * <strong>Statically set from 614 * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> 615 * </p> 616 * @return The localized description. 617 */ 618 public String loadDescription(PackageManager packageManager) { 619 if (mDescriptionResId == 0) { 620 return mNonLocalizedDescription; 621 } 622 ServiceInfo serviceInfo = mResolveInfo.serviceInfo; 623 CharSequence description = packageManager.getText(serviceInfo.packageName, 624 mDescriptionResId, serviceInfo.applicationInfo); 625 if (description != null) { 626 return description.toString().trim(); 627 } 628 return null; 629 } 630 631 /** 632 * {@inheritDoc} 633 */ 634 public int describeContents() { 635 return 0; 636 } 637 638 public void writeToParcel(Parcel parcel, int flagz) { 639 parcel.writeInt(eventTypes); 640 parcel.writeStringArray(packageNames); 641 parcel.writeInt(feedbackType); 642 parcel.writeLong(notificationTimeout); 643 parcel.writeInt(flags); 644 parcel.writeString(mId); 645 parcel.writeParcelable(mResolveInfo, 0); 646 parcel.writeString(mSettingsActivityName); 647 parcel.writeInt(mCapabilities); 648 parcel.writeInt(mDescriptionResId); 649 parcel.writeString(mNonLocalizedDescription); 650 } 651 652 private void initFromParcel(Parcel parcel) { 653 eventTypes = parcel.readInt(); 654 packageNames = parcel.readStringArray(); 655 feedbackType = parcel.readInt(); 656 notificationTimeout = parcel.readLong(); 657 flags = parcel.readInt(); 658 mId = parcel.readString(); 659 mResolveInfo = parcel.readParcelable(null); 660 mSettingsActivityName = parcel.readString(); 661 mCapabilities = parcel.readInt(); 662 mDescriptionResId = parcel.readInt(); 663 mNonLocalizedDescription = parcel.readString(); 664 } 665 666 @Override 667 public int hashCode() { 668 return 31 * 1 + ((mId == null) ? 0 : mId.hashCode()); 669 } 670 671 @Override 672 public boolean equals(Object obj) { 673 if (this == obj) { 674 return true; 675 } 676 if (obj == null) { 677 return false; 678 } 679 if (getClass() != obj.getClass()) { 680 return false; 681 } 682 AccessibilityServiceInfo other = (AccessibilityServiceInfo) obj; 683 if (mId == null) { 684 if (other.mId != null) { 685 return false; 686 } 687 } else if (!mId.equals(other.mId)) { 688 return false; 689 } 690 return true; 691 } 692 693 @Override 694 public String toString() { 695 StringBuilder stringBuilder = new StringBuilder(); 696 appendEventTypes(stringBuilder, eventTypes); 697 stringBuilder.append(", "); 698 appendPackageNames(stringBuilder, packageNames); 699 stringBuilder.append(", "); 700 appendFeedbackTypes(stringBuilder, feedbackType); 701 stringBuilder.append(", "); 702 stringBuilder.append("notificationTimeout: ").append(notificationTimeout); 703 stringBuilder.append(", "); 704 appendFlags(stringBuilder, flags); 705 stringBuilder.append(", "); 706 stringBuilder.append("id: ").append(mId); 707 stringBuilder.append(", "); 708 stringBuilder.append("resolveInfo: ").append(mResolveInfo); 709 stringBuilder.append(", "); 710 stringBuilder.append("settingsActivityName: ").append(mSettingsActivityName); 711 stringBuilder.append(", "); 712 appendCapabilities(stringBuilder, mCapabilities); 713 return stringBuilder.toString(); 714 } 715 716 private static void appendFeedbackTypes(StringBuilder stringBuilder, int feedbackTypes) { 717 stringBuilder.append("feedbackTypes:"); 718 stringBuilder.append("["); 719 while (feedbackTypes != 0) { 720 final int feedbackTypeBit = (1 << Integer.numberOfTrailingZeros(feedbackTypes)); 721 stringBuilder.append(feedbackTypeToString(feedbackTypeBit)); 722 feedbackTypes &= ~feedbackTypeBit; 723 if (feedbackTypes != 0) { 724 stringBuilder.append(", "); 725 } 726 } 727 stringBuilder.append("]"); 728 } 729 730 private static void appendPackageNames(StringBuilder stringBuilder, String[] packageNames) { 731 stringBuilder.append("packageNames:"); 732 stringBuilder.append("["); 733 if (packageNames != null) { 734 final int packageNameCount = packageNames.length; 735 for (int i = 0; i < packageNameCount; i++) { 736 stringBuilder.append(packageNames[i]); 737 if (i < packageNameCount - 1) { 738 stringBuilder.append(", "); 739 } 740 } 741 } 742 stringBuilder.append("]"); 743 } 744 745 private static void appendEventTypes(StringBuilder stringBuilder, int eventTypes) { 746 stringBuilder.append("eventTypes:"); 747 stringBuilder.append("["); 748 while (eventTypes != 0) { 749 final int eventTypeBit = (1 << Integer.numberOfTrailingZeros(eventTypes)); 750 stringBuilder.append(AccessibilityEvent.eventTypeToString(eventTypeBit)); 751 eventTypes &= ~eventTypeBit; 752 if (eventTypes != 0) { 753 stringBuilder.append(", "); 754 } 755 } 756 stringBuilder.append("]"); 757 } 758 759 private static void appendFlags(StringBuilder stringBuilder, int flags) { 760 stringBuilder.append("flags:"); 761 stringBuilder.append("["); 762 while (flags != 0) { 763 final int flagBit = (1 << Integer.numberOfTrailingZeros(flags)); 764 stringBuilder.append(flagToString(flagBit)); 765 flags &= ~flagBit; 766 if (flags != 0) { 767 stringBuilder.append(", "); 768 } 769 } 770 stringBuilder.append("]"); 771 } 772 773 private static void appendCapabilities(StringBuilder stringBuilder, int capabilities) { 774 stringBuilder.append("capabilities:"); 775 stringBuilder.append("["); 776 while (capabilities != 0) { 777 final int capabilityBit = (1 << Integer.numberOfTrailingZeros(capabilities)); 778 stringBuilder.append(capabilityToString(capabilityBit)); 779 capabilities &= ~capabilityBit; 780 if (capabilities != 0) { 781 stringBuilder.append(", "); 782 } 783 } 784 stringBuilder.append("]"); 785 } 786 787 /** 788 * Returns the string representation of a feedback type. For example, 789 * {@link #FEEDBACK_SPOKEN} is represented by the string FEEDBACK_SPOKEN. 790 * 791 * @param feedbackType The feedback type. 792 * @return The string representation. 793 */ 794 public static String feedbackTypeToString(int feedbackType) { 795 StringBuilder builder = new StringBuilder(); 796 builder.append("["); 797 while (feedbackType != 0) { 798 final int feedbackTypeFlag = 1 << Integer.numberOfTrailingZeros(feedbackType); 799 feedbackType &= ~feedbackTypeFlag; 800 switch (feedbackTypeFlag) { 801 case FEEDBACK_AUDIBLE: 802 if (builder.length() > 1) { 803 builder.append(", "); 804 } 805 builder.append("FEEDBACK_AUDIBLE"); 806 break; 807 case FEEDBACK_HAPTIC: 808 if (builder.length() > 1) { 809 builder.append(", "); 810 } 811 builder.append("FEEDBACK_HAPTIC"); 812 break; 813 case FEEDBACK_GENERIC: 814 if (builder.length() > 1) { 815 builder.append(", "); 816 } 817 builder.append("FEEDBACK_GENERIC"); 818 break; 819 case FEEDBACK_SPOKEN: 820 if (builder.length() > 1) { 821 builder.append(", "); 822 } 823 builder.append("FEEDBACK_SPOKEN"); 824 break; 825 case FEEDBACK_VISUAL: 826 if (builder.length() > 1) { 827 builder.append(", "); 828 } 829 builder.append("FEEDBACK_VISUAL"); 830 break; 831 case FEEDBACK_BRAILLE: 832 if (builder.length() > 1) { 833 builder.append(", "); 834 } 835 builder.append("FEEDBACK_BRAILLE"); 836 break; 837 } 838 } 839 builder.append("]"); 840 return builder.toString(); 841 } 842 843 /** 844 * Returns the string representation of a flag. For example, 845 * {@link #DEFAULT} is represented by the string DEFAULT. 846 * 847 * @param flag The flag. 848 * @return The string representation. 849 */ 850 public static String flagToString(int flag) { 851 switch (flag) { 852 case DEFAULT: 853 return "DEFAULT"; 854 case FLAG_INCLUDE_NOT_IMPORTANT_VIEWS: 855 return "FLAG_INCLUDE_NOT_IMPORTANT_VIEWS"; 856 case FLAG_REQUEST_TOUCH_EXPLORATION_MODE: 857 return "FLAG_REQUEST_TOUCH_EXPLORATION_MODE"; 858 case FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY: 859 return "FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY"; 860 case FLAG_REPORT_VIEW_IDS: 861 return "FLAG_REPORT_VIEW_IDS"; 862 case FLAG_REQUEST_FILTER_KEY_EVENTS: 863 return "FLAG_REQUEST_FILTER_KEY_EVENTS"; 864 default: 865 return null; 866 } 867 } 868 869 /** 870 * Returns the string representation of a capability. For example, 871 * {@link #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT} is represented 872 * by the string CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT. 873 * 874 * @param capability The capability. 875 * @return The string representation. 876 */ 877 public static String capabilityToString(int capability) { 878 switch (capability) { 879 case CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT: 880 return "CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT"; 881 case CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION: 882 return "CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION"; 883 case CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY: 884 return "CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY"; 885 case CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS: 886 return "CAPABILITY_CAN_FILTER_KEY_EVENTS"; 887 default: 888 return "UNKNOWN"; 889 } 890 } 891 892 /** 893 * @hide 894 * @return The list of {@link CapabilityInfo} objects. 895 */ 896 public List<CapabilityInfo> getCapabilityInfos() { 897 if (mCapabilities == 0) { 898 return Collections.emptyList(); 899 } 900 int capabilities = mCapabilities; 901 List<CapabilityInfo> capabilityInfos = new ArrayList<CapabilityInfo>(); 902 while (capabilities != 0) { 903 final int capabilityBit = 1 << Integer.numberOfTrailingZeros(capabilities); 904 capabilities &= ~capabilityBit; 905 CapabilityInfo capabilityInfo = sAvailableCapabilityInfos.get(capabilityBit); 906 if (capabilityInfo != null) { 907 capabilityInfos.add(capabilityInfo); 908 } 909 } 910 return capabilityInfos; 911 } 912 913 /** 914 * @hide 915 */ 916 public static final class CapabilityInfo { 917 public final int capability; 918 public final int titleResId; 919 public final int descResId; 920 921 public CapabilityInfo(int capability, int titleResId, int descResId) { 922 this.capability = capability; 923 this.titleResId = titleResId; 924 this.descResId = descResId; 925 } 926 } 927 928 /** 929 * @see Parcelable.Creator 930 */ 931 public static final Parcelable.Creator<AccessibilityServiceInfo> CREATOR = 932 new Parcelable.Creator<AccessibilityServiceInfo>() { 933 public AccessibilityServiceInfo createFromParcel(Parcel parcel) { 934 AccessibilityServiceInfo info = new AccessibilityServiceInfo(); 935 info.initFromParcel(parcel); 936 return info; 937 } 938 939 public AccessibilityServiceInfo[] newArray(int size) { 940 return new AccessibilityServiceInfo[size]; 941 } 942 }; 943 } 944