AccessibilityServiceInfo.java revision 0f55cc33f658b2793a12d609a0989348077324a4
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 * Creates a new instance. 175 */ 176 public AccessibilityServiceInfo() { 177 /* do nothing */ 178 } 179 180 /** 181 * Creates a new instance. 182 * 183 * @param resolveInfo The service resolve info. 184 * @param context Context for accessing resources. 185 * @throws XmlPullParserException If a XML parsing error occurs. 186 * @throws IOException If a XML parsing error occurs. 187 * 188 * @hide 189 */ 190 public AccessibilityServiceInfo(ResolveInfo resolveInfo, Context context) 191 throws XmlPullParserException, IOException { 192 ServiceInfo serviceInfo = resolveInfo.serviceInfo; 193 mId = new ComponentName(serviceInfo.packageName, serviceInfo.name).flattenToShortString(); 194 mResolveInfo = resolveInfo; 195 196 XmlResourceParser parser = null; 197 198 try { 199 PackageManager packageManager = context.getPackageManager(); 200 parser = serviceInfo.loadXmlMetaData(packageManager, 201 AccessibilityService.SERVICE_META_DATA); 202 if (parser == null) { 203 return; 204 } 205 206 int type = 0; 207 while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) { 208 type = parser.next(); 209 } 210 211 String nodeName = parser.getName(); 212 if (!TAG_ACCESSIBILITY_SERVICE.equals(nodeName)) { 213 throw new XmlPullParserException( "Meta-data does not start with" 214 + TAG_ACCESSIBILITY_SERVICE + " tag"); 215 } 216 217 AttributeSet allAttributes = Xml.asAttributeSet(parser); 218 Resources resources = packageManager.getResourcesForApplication( 219 serviceInfo.applicationInfo); 220 TypedArray asAttributes = resources.obtainAttributes(allAttributes, 221 com.android.internal.R.styleable.AccessibilityService); 222 eventTypes = asAttributes.getInt( 223 com.android.internal.R.styleable.AccessibilityService_accessibilityEventTypes, 224 0); 225 String packageNamez = asAttributes.getString( 226 com.android.internal.R.styleable.AccessibilityService_packageNames); 227 if (packageNamez != null) { 228 packageNames = packageNamez.split("(\\s)*,(\\s)*"); 229 } 230 feedbackType = asAttributes.getInt( 231 com.android.internal.R.styleable.AccessibilityService_accessibilityFeedbackType, 232 0); 233 notificationTimeout = asAttributes.getInt( 234 com.android.internal.R.styleable.AccessibilityService_notificationTimeout, 235 0); 236 flags = asAttributes.getInt( 237 com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0); 238 mSettingsActivityName = asAttributes.getString( 239 com.android.internal.R.styleable.AccessibilityService_settingsActivity); 240 mCanRetrieveWindowContent = asAttributes.getBoolean( 241 com.android.internal.R.styleable.AccessibilityService_canRetrieveWindowContent, 242 false); 243 asAttributes.recycle(); 244 } catch (NameNotFoundException e) { 245 throw new XmlPullParserException( "Unable to create context for: " 246 + serviceInfo.packageName); 247 } finally { 248 if (parser != null) { 249 parser.close(); 250 } 251 } 252 } 253 254 /** 255 * Updates the properties that an AccessibilitySerivice can change dynamically. 256 * 257 * @param other The info from which to update the properties. 258 * 259 * @hide 260 */ 261 public void updateDynamicallyConfigurableProperties(AccessibilityServiceInfo other) { 262 eventTypes = other.eventTypes; 263 packageNames = other.packageNames; 264 feedbackType = other.feedbackType; 265 notificationTimeout = other.notificationTimeout; 266 flags = other.flags; 267 } 268 269 /** 270 * The accessibility service id. 271 * <p> 272 * <strong>Generated by the system.</strong> 273 * </p> 274 * @return The id. 275 */ 276 public String getId() { 277 return mId; 278 } 279 280 /** 281 * The service {@link ResolveInfo}. 282 * <p> 283 * <strong>Generated by the system.</strong> 284 * </p> 285 * @return The info. 286 */ 287 public ResolveInfo getResolveInfo() { 288 return mResolveInfo; 289 } 290 291 /** 292 * The settings activity name. 293 * <p> 294 * <strong>Statically set from 295 * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> 296 * </p> 297 * @return The settings activity name. 298 */ 299 public String getSettingsActivityName() { 300 return mSettingsActivityName; 301 } 302 303 /** 304 * Whether this service can retrieve the current window's content. 305 * <p> 306 * <strong>Statically set from 307 * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> 308 * </p> 309 * @return True window content can be retrieved. 310 */ 311 public boolean getCanRetrieveWindowContent() { 312 return mCanRetrieveWindowContent; 313 } 314 315 /** 316 * {@inheritDoc} 317 */ 318 public int describeContents() { 319 return 0; 320 } 321 322 public void writeToParcel(Parcel parcel, int flagz) { 323 parcel.writeInt(eventTypes); 324 parcel.writeStringArray(packageNames); 325 parcel.writeInt(feedbackType); 326 parcel.writeLong(notificationTimeout); 327 parcel.writeInt(flags); 328 parcel.writeString(mId); 329 parcel.writeParcelable(mResolveInfo, 0); 330 parcel.writeString(mSettingsActivityName); 331 parcel.writeInt(mCanRetrieveWindowContent ? 1 : 0); 332 } 333 334 private void initFromParcel(Parcel parcel) { 335 eventTypes = parcel.readInt(); 336 packageNames = parcel.readStringArray(); 337 feedbackType = parcel.readInt(); 338 notificationTimeout = parcel.readLong(); 339 flags = parcel.readInt(); 340 mId = parcel.readString(); 341 mResolveInfo = parcel.readParcelable(null); 342 mSettingsActivityName = parcel.readString(); 343 mCanRetrieveWindowContent = (parcel.readInt() == 1); 344 } 345 346 @Override 347 public String toString() { 348 StringBuilder stringBuilder = new StringBuilder(); 349 appendEventTypes(stringBuilder, eventTypes); 350 stringBuilder.append(", "); 351 appendPackageNames(stringBuilder, packageNames); 352 stringBuilder.append(", "); 353 appendFeedbackTypes(stringBuilder, feedbackType); 354 stringBuilder.append(", "); 355 stringBuilder.append("notificationTimeout: ").append(notificationTimeout); 356 stringBuilder.append(", "); 357 appendFlags(stringBuilder, flags); 358 stringBuilder.append(", "); 359 stringBuilder.append("id: ").append(mId); 360 stringBuilder.append(", "); 361 stringBuilder.append("resolveInfo: ").append(mResolveInfo); 362 stringBuilder.append(", "); 363 stringBuilder.append("settingsActivityName: ").append(mSettingsActivityName); 364 stringBuilder.append(", "); 365 stringBuilder.append("retrieveScreenContent: ").append(mCanRetrieveWindowContent); 366 return stringBuilder.toString(); 367 } 368 369 private static void appendFeedbackTypes(StringBuilder stringBuilder, int feedbackTypes) { 370 stringBuilder.append("feedbackTypes:"); 371 stringBuilder.append("["); 372 while (feedbackTypes != 0) { 373 final int feedbackTypeBit = (1 << Integer.numberOfTrailingZeros(feedbackTypes)); 374 stringBuilder.append(feedbackTypeToString(feedbackTypeBit)); 375 feedbackTypes &= ~feedbackTypeBit; 376 if (feedbackTypes != 0) { 377 stringBuilder.append(", "); 378 } 379 } 380 stringBuilder.append("]"); 381 } 382 383 private static void appendPackageNames(StringBuilder stringBuilder, String[] packageNames) { 384 stringBuilder.append("packageNames:"); 385 stringBuilder.append("["); 386 if (packageNames != null) { 387 final int packageNameCount = packageNames.length; 388 for (int i = 0; i < packageNameCount; i++) { 389 stringBuilder.append(packageNames[i]); 390 if (i < packageNameCount - 1) { 391 stringBuilder.append(", "); 392 } 393 } 394 } 395 stringBuilder.append("]"); 396 } 397 398 private static void appendEventTypes(StringBuilder stringBuilder, int eventTypes) { 399 stringBuilder.append("eventTypes:"); 400 stringBuilder.append("["); 401 while (eventTypes != 0) { 402 final int eventTypeBit = (1 << Integer.numberOfTrailingZeros(eventTypes)); 403 stringBuilder.append(AccessibilityEvent.eventTypeToString(eventTypeBit)); 404 eventTypes &= ~eventTypeBit; 405 if (eventTypes != 0) { 406 stringBuilder.append(", "); 407 } 408 } 409 stringBuilder.append("]"); 410 } 411 412 private static void appendFlags(StringBuilder stringBuilder, int flags) { 413 stringBuilder.append("flags:"); 414 stringBuilder.append("["); 415 while (flags != 0) { 416 final int flagBit = (1 << Integer.numberOfTrailingZeros(flags)); 417 stringBuilder.append(flagToString(flagBit)); 418 flags &= ~flagBit; 419 if (flags != 0) { 420 stringBuilder.append(", "); 421 } 422 } 423 stringBuilder.append("]"); 424 } 425 426 /** 427 * Returns the string representation of a feedback type. For example, 428 * {@link #FEEDBACK_SPOKEN} is represented by the string FEEDBACK_SPOKEN. 429 * 430 * @param feedbackType The feedback type. 431 * @return The string representation. 432 */ 433 public static String feedbackTypeToString(int feedbackType) { 434 switch (feedbackType) { 435 case FEEDBACK_AUDIBLE: 436 return "FEEDBACK_AUDIBLE"; 437 case FEEDBACK_HAPTIC: 438 return "FEEDBACK_HAPTIC"; 439 case FEEDBACK_GENERIC: 440 return "FEEDBACK_GENERIC"; 441 case FEEDBACK_SPOKEN: 442 return "FEEDBACK_SPOKEN"; 443 case FEEDBACK_VISUAL: 444 return "FEEDBACK_VISUAL"; 445 default: 446 return null; 447 } 448 } 449 450 /** 451 * Returns the string representation of a flag. For example, 452 * {@link #DEFAULT} is represented by the string DEFAULT. 453 * 454 * @param flag The flag. 455 * @return The string representation. 456 */ 457 public static String flagToString(int flag) { 458 switch (flag) { 459 case DEFAULT: 460 return "DEFAULT"; 461 default: 462 return null; 463 } 464 } 465 466 /** 467 * @see Parcelable.Creator 468 */ 469 public static final Parcelable.Creator<AccessibilityServiceInfo> CREATOR = 470 new Parcelable.Creator<AccessibilityServiceInfo>() { 471 public AccessibilityServiceInfo createFromParcel(Parcel parcel) { 472 AccessibilityServiceInfo info = new AccessibilityServiceInfo(); 473 info.initFromParcel(parcel); 474 return info; 475 } 476 477 public AccessibilityServiceInfo[] newArray(int size) { 478 return new AccessibilityServiceInfo[size]; 479 } 480 }; 481} 482