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