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