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