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