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