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