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