AccessibilityServiceInfo.java revision 80943d8daa6ab31ab5c486d57aea406aa0730d58
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.TypedValue; 33import android.util.Xml; 34import android.view.View; 35import android.view.accessibility.AccessibilityEvent; 36import android.view.accessibility.AccessibilityNodeInfo; 37 38import org.xmlpull.v1.XmlPullParser; 39import org.xmlpull.v1.XmlPullParserException; 40 41import java.io.IOException; 42 43/** 44 * This class describes an {@link AccessibilityService}. The system notifies an 45 * {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s 46 * according to the information encapsulated in this class. 47 * 48 * <div class="special reference"> 49 * <h3>Developer Guides</h3> 50 * <p>For more information about creating AccessibilityServices, read the 51 * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a> 52 * developer guide.</p> 53 * </div> 54 * 55 * @see AccessibilityService 56 * @see android.view.accessibility.AccessibilityEvent 57 * @see android.view.accessibility.AccessibilityManager 58 */ 59public class AccessibilityServiceInfo implements Parcelable { 60 61 private static final String TAG_ACCESSIBILITY_SERVICE = "accessibility-service"; 62 63 /** 64 * Denotes spoken feedback. 65 */ 66 public static final int FEEDBACK_SPOKEN = 0x0000001; 67 68 /** 69 * Denotes haptic feedback. 70 */ 71 public static final int FEEDBACK_HAPTIC = 0x0000002; 72 73 /** 74 * Denotes audible (not spoken) feedback. 75 */ 76 public static final int FEEDBACK_AUDIBLE = 0x0000004; 77 78 /** 79 * Denotes visual feedback. 80 */ 81 public static final int FEEDBACK_VISUAL = 0x0000008; 82 83 /** 84 * Denotes generic feedback. 85 */ 86 public static final int FEEDBACK_GENERIC = 0x0000010; 87 88 /** 89 * Denotes braille feedback. 90 */ 91 public static final int FEEDBACK_BRAILLE = 0x0000020; 92 93 /** 94 * Mask for all feedback types. 95 * 96 * @see #FEEDBACK_SPOKEN 97 * @see #FEEDBACK_HAPTIC 98 * @see #FEEDBACK_AUDIBLE 99 * @see #FEEDBACK_VISUAL 100 * @see #FEEDBACK_GENERIC 101 * @see #FEEDBACK_BRAILLE 102 */ 103 public static final int FEEDBACK_ALL_MASK = 0xFFFFFFFF; 104 105 /** 106 * If an {@link AccessibilityService} is the default for a given type. 107 * Default service is invoked only if no package specific one exists. In case of 108 * more than one package specific service only the earlier registered is notified. 109 */ 110 public static final int DEFAULT = 0x0000001; 111 112 /** 113 * If this flag is set the system will regard views that are not important 114 * for accessibility in addition to the ones that are important for accessibility. 115 * That is, views that are marked as not important for accessibility via 116 * {@link View#IMPORTANT_FOR_ACCESSIBILITY_NO} and views that are marked as 117 * potentially important for accessibility via 118 * {@link View#IMPORTANT_FOR_ACCESSIBILITY_AUTO} for which the system has determined 119 * that are not important for accessibility, are both reported while querying the 120 * window content and also the accessibility service will receive accessibility events 121 * from them. 122 * <p> 123 * <strong>Note:</strong> For accessibility services targeting API version 124 * {@link Build.VERSION_CODES#JELLY_BEAN} or higher this flag has to be explicitly 125 * set for the system to regard views that are not important for accessibility. For 126 * accessibility services targeting API version lower than 127 * {@link Build.VERSION_CODES#JELLY_BEAN} this flag is ignored and all views are 128 * regarded for accessibility purposes. 129 * </p> 130 * <p> 131 * Usually views not important for accessibility are layout managers that do not 132 * react to user actions, do not draw any content, and do not have any special 133 * semantics in the context of the screen content. For example, a three by three 134 * grid can be implemented as three horizontal linear layouts and one vertical, 135 * or three vertical linear layouts and one horizontal, or one grid layout, etc. 136 * In this context the actual layout mangers used to achieve the grid configuration 137 * are not important, rather it is important that there are nine evenly distributed 138 * elements. 139 * </p> 140 */ 141 public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x0000002; 142 143 /** 144 * This flag requests that the system gets into touch exploration mode. 145 * In this mode a single finger moving on the screen behaves as a mouse 146 * pointer hovering over the user interface. The system will also detect 147 * certain gestures performed on the touch screen and notify this service. 148 * The system will enable touch exploration mode if there is at least one 149 * accessibility service that has this flag set. Hence, clearing this 150 * flag does not guarantee that the device will not be in touch exploration 151 * mode since there may be another enabled service that requested it. 152 */ 153 public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE= 0x0000004; 154 155 /** 156 * This flag requests that the {@link AccessibilityNodeInfo}s obtained 157 * by an {@link AccessibilityService} contain the id of the source view. 158 * The source view id will be a fully qualified resource name of the 159 * form "package:id/name", for example "foo.bar:id/my_list", and it is 160 * useful for UI test automation. This flag is not set by default. 161 */ 162 public static final int FLAG_REPORT_VIEW_IDS = 0x00000008; 163 164 /** 165 * The event types an {@link AccessibilityService} is interested in. 166 * <p> 167 * <strong>Can be dynamically set at runtime.</strong> 168 * </p> 169 * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_CLICKED 170 * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_LONG_CLICKED 171 * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_FOCUSED 172 * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SELECTED 173 * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED 174 * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED 175 * @see android.view.accessibility.AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED 176 * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START 177 * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END 178 * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_ENTER 179 * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_EXIT 180 * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SCROLLED 181 * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED 182 * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED 183 */ 184 public int eventTypes; 185 186 /** 187 * The package names an {@link AccessibilityService} is interested in. Setting 188 * to <code>null</code> is equivalent to all packages. 189 * <p> 190 * <strong>Can be dynamically set at runtime.</strong> 191 * </p> 192 */ 193 public String[] packageNames; 194 195 /** 196 * The feedback type an {@link AccessibilityService} provides. 197 * <p> 198 * <strong>Can be dynamically set at runtime.</strong> 199 * </p> 200 * @see #FEEDBACK_AUDIBLE 201 * @see #FEEDBACK_GENERIC 202 * @see #FEEDBACK_HAPTIC 203 * @see #FEEDBACK_SPOKEN 204 * @see #FEEDBACK_VISUAL 205 * @see #FEEDBACK_BRAILLE 206 */ 207 public int feedbackType; 208 209 /** 210 * The timeout after the most recent event of a given type before an 211 * {@link AccessibilityService} is notified. 212 * <p> 213 * <strong>Can be dynamically set at runtime.</strong>. 214 * </p> 215 * <p> 216 * <strong>Note:</strong> The event notification timeout is useful to avoid propagating 217 * events to the client too frequently since this is accomplished via an expensive 218 * interprocess call. One can think of the timeout as a criteria to determine when 219 * event generation has settled down. 220 */ 221 public long notificationTimeout; 222 223 /** 224 * This field represents a set of flags used for configuring an 225 * {@link AccessibilityService}. 226 * <p> 227 * <strong>Can be dynamically set at runtime.</strong> 228 * </p> 229 * @see #DEFAULT 230 * @see #FLAG_INCLUDE_NOT_IMPORTANT_VIEWS 231 * @see #FLAG_REQUEST_TOUCH_EXPLORATION_MODE 232 */ 233 public int flags; 234 235 /** 236 * The unique string Id to identify the accessibility service. 237 */ 238 private String mId; 239 240 /** 241 * The Service that implements this accessibility service component. 242 */ 243 private ResolveInfo mResolveInfo; 244 245 /** 246 * The accessibility service setting activity's name, used by the system 247 * settings to launch the setting activity of this accessibility service. 248 */ 249 private String mSettingsActivityName; 250 251 /** 252 * Flag whether this accessibility service can retrieve window content. 253 */ 254 private boolean mCanRetrieveWindowContent; 255 256 /** 257 * Resource id of the description of the accessibility service. 258 */ 259 private int mDescriptionResId; 260 261 /** 262 * Non localized description of the accessibility service. 263 */ 264 private String mNonLocalizedDescription; 265 266 /** 267 * Creates a new instance. 268 */ 269 public AccessibilityServiceInfo() { 270 /* do nothing */ 271 } 272 273 /** 274 * Creates a new instance. 275 * 276 * @param resolveInfo The service resolve info. 277 * @param context Context for accessing resources. 278 * @throws XmlPullParserException If a XML parsing error occurs. 279 * @throws IOException If a XML parsing error occurs. 280 * 281 * @hide 282 */ 283 public AccessibilityServiceInfo(ResolveInfo resolveInfo, Context context) 284 throws XmlPullParserException, IOException { 285 ServiceInfo serviceInfo = resolveInfo.serviceInfo; 286 mId = new ComponentName(serviceInfo.packageName, serviceInfo.name).flattenToShortString(); 287 mResolveInfo = resolveInfo; 288 289 XmlResourceParser parser = null; 290 291 try { 292 PackageManager packageManager = context.getPackageManager(); 293 parser = serviceInfo.loadXmlMetaData(packageManager, 294 AccessibilityService.SERVICE_META_DATA); 295 if (parser == null) { 296 return; 297 } 298 299 int type = 0; 300 while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) { 301 type = parser.next(); 302 } 303 304 String nodeName = parser.getName(); 305 if (!TAG_ACCESSIBILITY_SERVICE.equals(nodeName)) { 306 throw new XmlPullParserException( "Meta-data does not start with" 307 + TAG_ACCESSIBILITY_SERVICE + " tag"); 308 } 309 310 AttributeSet allAttributes = Xml.asAttributeSet(parser); 311 Resources resources = packageManager.getResourcesForApplication( 312 serviceInfo.applicationInfo); 313 TypedArray asAttributes = resources.obtainAttributes(allAttributes, 314 com.android.internal.R.styleable.AccessibilityService); 315 eventTypes = asAttributes.getInt( 316 com.android.internal.R.styleable.AccessibilityService_accessibilityEventTypes, 317 0); 318 String packageNamez = asAttributes.getString( 319 com.android.internal.R.styleable.AccessibilityService_packageNames); 320 if (packageNamez != null) { 321 packageNames = packageNamez.split("(\\s)*,(\\s)*"); 322 } 323 feedbackType = asAttributes.getInt( 324 com.android.internal.R.styleable.AccessibilityService_accessibilityFeedbackType, 325 0); 326 notificationTimeout = asAttributes.getInt( 327 com.android.internal.R.styleable.AccessibilityService_notificationTimeout, 328 0); 329 flags = asAttributes.getInt( 330 com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0); 331 mSettingsActivityName = asAttributes.getString( 332 com.android.internal.R.styleable.AccessibilityService_settingsActivity); 333 mCanRetrieveWindowContent = asAttributes.getBoolean( 334 com.android.internal.R.styleable.AccessibilityService_canRetrieveWindowContent, 335 false); 336 TypedValue peekedValue = asAttributes.peekValue( 337 com.android.internal.R.styleable.AccessibilityService_description); 338 if (peekedValue != null) { 339 mDescriptionResId = peekedValue.resourceId; 340 CharSequence nonLocalizedDescription = peekedValue.coerceToString(); 341 if (nonLocalizedDescription != null) { 342 mNonLocalizedDescription = nonLocalizedDescription.toString().trim(); 343 } 344 } 345 asAttributes.recycle(); 346 } catch (NameNotFoundException e) { 347 throw new XmlPullParserException( "Unable to create context for: " 348 + serviceInfo.packageName); 349 } finally { 350 if (parser != null) { 351 parser.close(); 352 } 353 } 354 } 355 356 /** 357 * Updates the properties that an AccessibilitySerivice can change dynamically. 358 * 359 * @param other The info from which to update the properties. 360 * 361 * @hide 362 */ 363 public void updateDynamicallyConfigurableProperties(AccessibilityServiceInfo other) { 364 eventTypes = other.eventTypes; 365 packageNames = other.packageNames; 366 feedbackType = other.feedbackType; 367 notificationTimeout = other.notificationTimeout; 368 flags = other.flags; 369 } 370 371 /** 372 * The accessibility service id. 373 * <p> 374 * <strong>Generated by the system.</strong> 375 * </p> 376 * @return The id. 377 */ 378 public String getId() { 379 return mId; 380 } 381 382 /** 383 * The service {@link ResolveInfo}. 384 * <p> 385 * <strong>Generated by the system.</strong> 386 * </p> 387 * @return The info. 388 */ 389 public ResolveInfo getResolveInfo() { 390 return mResolveInfo; 391 } 392 393 /** 394 * The settings activity name. 395 * <p> 396 * <strong>Statically set from 397 * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> 398 * </p> 399 * @return The settings activity name. 400 */ 401 public String getSettingsActivityName() { 402 return mSettingsActivityName; 403 } 404 405 /** 406 * Whether this service can retrieve the current window's content. 407 * <p> 408 * <strong>Statically set from 409 * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> 410 * </p> 411 * @return True if window content can be retrieved. 412 */ 413 public boolean getCanRetrieveWindowContent() { 414 return mCanRetrieveWindowContent; 415 } 416 417 /** 418 * Gets the non-localized description of the accessibility service. 419 * <p> 420 * <strong>Statically set from 421 * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> 422 * </p> 423 * @return The description. 424 * 425 * @deprecated Use {@link #loadDescription(PackageManager)}. 426 */ 427 public String getDescription() { 428 return mNonLocalizedDescription; 429 } 430 431 /** 432 * The localized description of the accessibility service. 433 * <p> 434 * <strong>Statically set from 435 * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> 436 * </p> 437 * @return The localized description. 438 */ 439 public String loadDescription(PackageManager packageManager) { 440 if (mDescriptionResId == 0) { 441 return mNonLocalizedDescription; 442 } 443 ServiceInfo serviceInfo = mResolveInfo.serviceInfo; 444 CharSequence description = packageManager.getText(serviceInfo.packageName, 445 mDescriptionResId, serviceInfo.applicationInfo); 446 if (description != null) { 447 return description.toString().trim(); 448 } 449 return null; 450 } 451 452 /** 453 * {@inheritDoc} 454 */ 455 public int describeContents() { 456 return 0; 457 } 458 459 public void writeToParcel(Parcel parcel, int flagz) { 460 parcel.writeInt(eventTypes); 461 parcel.writeStringArray(packageNames); 462 parcel.writeInt(feedbackType); 463 parcel.writeLong(notificationTimeout); 464 parcel.writeInt(flags); 465 parcel.writeString(mId); 466 parcel.writeParcelable(mResolveInfo, 0); 467 parcel.writeString(mSettingsActivityName); 468 parcel.writeInt(mCanRetrieveWindowContent ? 1 : 0); 469 parcel.writeInt(mDescriptionResId); 470 parcel.writeString(mNonLocalizedDescription); 471 } 472 473 private void initFromParcel(Parcel parcel) { 474 eventTypes = parcel.readInt(); 475 packageNames = parcel.readStringArray(); 476 feedbackType = parcel.readInt(); 477 notificationTimeout = parcel.readLong(); 478 flags = parcel.readInt(); 479 mId = parcel.readString(); 480 mResolveInfo = parcel.readParcelable(null); 481 mSettingsActivityName = parcel.readString(); 482 mCanRetrieveWindowContent = (parcel.readInt() == 1); 483 mDescriptionResId = parcel.readInt(); 484 mNonLocalizedDescription = parcel.readString(); 485 } 486 487 @Override 488 public String toString() { 489 StringBuilder stringBuilder = new StringBuilder(); 490 appendEventTypes(stringBuilder, eventTypes); 491 stringBuilder.append(", "); 492 appendPackageNames(stringBuilder, packageNames); 493 stringBuilder.append(", "); 494 appendFeedbackTypes(stringBuilder, feedbackType); 495 stringBuilder.append(", "); 496 stringBuilder.append("notificationTimeout: ").append(notificationTimeout); 497 stringBuilder.append(", "); 498 appendFlags(stringBuilder, flags); 499 stringBuilder.append(", "); 500 stringBuilder.append("id: ").append(mId); 501 stringBuilder.append(", "); 502 stringBuilder.append("resolveInfo: ").append(mResolveInfo); 503 stringBuilder.append(", "); 504 stringBuilder.append("settingsActivityName: ").append(mSettingsActivityName); 505 stringBuilder.append(", "); 506 stringBuilder.append("retrieveScreenContent: ").append(mCanRetrieveWindowContent); 507 return stringBuilder.toString(); 508 } 509 510 private static void appendFeedbackTypes(StringBuilder stringBuilder, int feedbackTypes) { 511 stringBuilder.append("feedbackTypes:"); 512 stringBuilder.append("["); 513 while (feedbackTypes != 0) { 514 final int feedbackTypeBit = (1 << Integer.numberOfTrailingZeros(feedbackTypes)); 515 stringBuilder.append(feedbackTypeToString(feedbackTypeBit)); 516 feedbackTypes &= ~feedbackTypeBit; 517 if (feedbackTypes != 0) { 518 stringBuilder.append(", "); 519 } 520 } 521 stringBuilder.append("]"); 522 } 523 524 private static void appendPackageNames(StringBuilder stringBuilder, String[] packageNames) { 525 stringBuilder.append("packageNames:"); 526 stringBuilder.append("["); 527 if (packageNames != null) { 528 final int packageNameCount = packageNames.length; 529 for (int i = 0; i < packageNameCount; i++) { 530 stringBuilder.append(packageNames[i]); 531 if (i < packageNameCount - 1) { 532 stringBuilder.append(", "); 533 } 534 } 535 } 536 stringBuilder.append("]"); 537 } 538 539 private static void appendEventTypes(StringBuilder stringBuilder, int eventTypes) { 540 stringBuilder.append("eventTypes:"); 541 stringBuilder.append("["); 542 while (eventTypes != 0) { 543 final int eventTypeBit = (1 << Integer.numberOfTrailingZeros(eventTypes)); 544 stringBuilder.append(AccessibilityEvent.eventTypeToString(eventTypeBit)); 545 eventTypes &= ~eventTypeBit; 546 if (eventTypes != 0) { 547 stringBuilder.append(", "); 548 } 549 } 550 stringBuilder.append("]"); 551 } 552 553 private static void appendFlags(StringBuilder stringBuilder, int flags) { 554 stringBuilder.append("flags:"); 555 stringBuilder.append("["); 556 while (flags != 0) { 557 final int flagBit = (1 << Integer.numberOfTrailingZeros(flags)); 558 stringBuilder.append(flagToString(flagBit)); 559 flags &= ~flagBit; 560 if (flags != 0) { 561 stringBuilder.append(", "); 562 } 563 } 564 stringBuilder.append("]"); 565 } 566 567 /** 568 * Returns the string representation of a feedback type. For example, 569 * {@link #FEEDBACK_SPOKEN} is represented by the string FEEDBACK_SPOKEN. 570 * 571 * @param feedbackType The feedback type. 572 * @return The string representation. 573 */ 574 public static String feedbackTypeToString(int feedbackType) { 575 StringBuilder builder = new StringBuilder(); 576 builder.append("["); 577 while (feedbackType != 0) { 578 final int feedbackTypeFlag = 1 << Integer.numberOfTrailingZeros(feedbackType); 579 feedbackType &= ~feedbackTypeFlag; 580 switch (feedbackTypeFlag) { 581 case FEEDBACK_AUDIBLE: 582 if (builder.length() > 1) { 583 builder.append(", "); 584 } 585 builder.append("FEEDBACK_AUDIBLE"); 586 break; 587 case FEEDBACK_HAPTIC: 588 if (builder.length() > 1) { 589 builder.append(", "); 590 } 591 builder.append("FEEDBACK_HAPTIC"); 592 break; 593 case FEEDBACK_GENERIC: 594 if (builder.length() > 1) { 595 builder.append(", "); 596 } 597 builder.append("FEEDBACK_GENERIC"); 598 break; 599 case FEEDBACK_SPOKEN: 600 if (builder.length() > 1) { 601 builder.append(", "); 602 } 603 builder.append("FEEDBACK_SPOKEN"); 604 break; 605 case FEEDBACK_VISUAL: 606 if (builder.length() > 1) { 607 builder.append(", "); 608 } 609 builder.append("FEEDBACK_VISUAL"); 610 break; 611 case FEEDBACK_BRAILLE: 612 if (builder.length() > 1) { 613 builder.append(", "); 614 } 615 builder.append("FEEDBACK_BRAILLE"); 616 break; 617 } 618 } 619 builder.append("]"); 620 return builder.toString(); 621 } 622 623 /** 624 * Returns the string representation of a flag. For example, 625 * {@link #DEFAULT} is represented by the string DEFAULT. 626 * 627 * @param flag The flag. 628 * @return The string representation. 629 */ 630 public static String flagToString(int flag) { 631 switch (flag) { 632 case DEFAULT: 633 return "DEFAULT"; 634 case FLAG_INCLUDE_NOT_IMPORTANT_VIEWS: 635 return "FLAG_INCLUDE_NOT_IMPORTANT_VIEWS"; 636 case FLAG_REQUEST_TOUCH_EXPLORATION_MODE: 637 return "FLAG_REQUEST_TOUCH_EXPLORATION_MODE"; 638 default: 639 return null; 640 } 641 } 642 643 /** 644 * @see Parcelable.Creator 645 */ 646 public static final Parcelable.Creator<AccessibilityServiceInfo> CREATOR = 647 new Parcelable.Creator<AccessibilityServiceInfo>() { 648 public AccessibilityServiceInfo createFromParcel(Parcel parcel) { 649 AccessibilityServiceInfo info = new AccessibilityServiceInfo(); 650 info.initFromParcel(parcel); 651 return info; 652 } 653 654 public AccessibilityServiceInfo[] newArray(int size) { 655 return new AccessibilityServiceInfo[size]; 656 } 657 }; 658} 659