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