diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index 3b0667d1e377d..316a018ef214f 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -22,6 +22,7 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.StringRes; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; @@ -61,6 +62,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.regex.Pattern; /** * This class describes an {@link AccessibilityService}. The system notifies an @@ -552,6 +554,13 @@ public class AccessibilityServiceInfo implements Parcelable { */ private int mHtmlDescriptionRes; + // Used for html description of accessibility service. The src tag must follow the + // prefix rule. e.g. + private static final String IMG_PREFIX = "R.drawable."; + private static final String ANCHOR_TAG = "a"; + private static final List UNSUPPORTED_TAG_LIST = new ArrayList<>( + Collections.singletonList(ANCHOR_TAG)); + /** * Creates a new instance. */ @@ -782,12 +791,10 @@ public class AccessibilityServiceInfo implements Parcelable { } /** - * The animated image resource id. - *

- * Statically set from - * {@link AccessibilityService#SERVICE_META_DATA meta-data}. - *

+ * Gets the animated image resource id. + * * @return The animated image resource id. + * * @hide */ public int getAnimatedImageRes() { @@ -797,10 +804,12 @@ public class AccessibilityServiceInfo implements Parcelable { /** * The animated image drawable. *

+ * Image can not exceed the screen size. * Statically set from * {@link AccessibilityService#SERVICE_META_DATA meta-data}. *

- * @return The animated image drawable. + * @return The animated image drawable, or null if the resource is invalid or the image + * exceed the screen size. */ @Nullable public Drawable loadAnimatedImage(@NonNull Context context) { @@ -808,11 +817,8 @@ public class AccessibilityServiceInfo implements Parcelable { return null; } - final PackageManager packageManager = context.getPackageManager(); - final String packageName = mComponentName.getPackageName(); - final ApplicationInfo applicationInfo = mResolveInfo.serviceInfo.applicationInfo; - - return packageManager.getDrawable(packageName, mAnimatedImageRes, applicationInfo); + return loadSafeAnimatedImage(context, mResolveInfo.serviceInfo.applicationInfo, + mAnimatedImageRes); } /** @@ -924,16 +930,17 @@ public class AccessibilityServiceInfo implements Parcelable { } /** - * The localized html description of the accessibility service. + * The localized and restricted html description of the accessibility service. *

+ * Filters the tag which do not meet the custom specification and the tag. * Statically set from * {@link AccessibilityService#SERVICE_META_DATA meta-data}. *

- * @return The localized html description. + * @return The localized and restricted html description. */ @Nullable public String loadHtmlDescription(@NonNull PackageManager packageManager) { - if (mHtmlDescriptionRes == 0) { + if (mHtmlDescriptionRes == /* invalid */ 0) { return null; } @@ -941,7 +948,7 @@ public class AccessibilityServiceInfo implements Parcelable { final CharSequence htmlDescription = packageManager.getText(serviceInfo.packageName, mHtmlDescriptionRes, serviceInfo.applicationInfo); if (htmlDescription != null) { - return htmlDescription.toString().trim(); + return getFilteredHtmlText(htmlDescription.toString().trim()); } return null; } @@ -1414,4 +1421,103 @@ public class AccessibilityServiceInfo implements Parcelable { return new AccessibilityServiceInfo[size]; } }; + + /** + * Gets the filtered html string for + * {@link android.accessibilityservice.AccessibilityServiceInfo} and + * {@link android.accessibilityservice.AccessibilityShortcutInfo}. It filters + * the tag which do not meet the custom specification and the tag. + * + * @param text the target text is html format. + * @return the filtered html string. + * + * @hide + */ + public static @NonNull String getFilteredHtmlText(@NonNull String text) { + final String replacementStart = ")"; + final String regexEnd = "(?i)"; + text = Pattern.compile(regexStart).matcher(text).replaceAll(replacementStart); + text = Pattern.compile(regexEnd).matcher(text).replaceAll(replacementEnd); + } + + final String regexInvalidImgTag = "(?i) getScreenWidthPixels(context); + final boolean isImageHeightOverScreenLength = + bannerDrawable.getIntrinsicHeight() > getScreenHeightPixels(context); + + return (isImageWidthOverScreenLength || isImageHeightOverScreenLength) + ? null + : bannerDrawable; + } + + /** + * Gets the width of the screen. + * + * @param context the current context. + * @return the width of the screen in term of pixels. + * + * @hide + */ + private static int getScreenWidthPixels(@NonNull Context context) { + final Resources resources = context.getResources(); + final int screenWidthDp = resources.getConfiguration().screenWidthDp; + + return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenWidthDp, + resources.getDisplayMetrics())); + } + + /** + * Gets the height of the screen. + * + * @param context the current context. + * @return the height of the screen in term of pixels. + * + * @hide + */ + private static int getScreenHeightPixels(@NonNull Context context) { + final Resources resources = context.getResources(); + final int screenHeightDp = resources.getConfiguration().screenHeightDp; + + return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenHeightDp, + resources.getDisplayMetrics())); + } } diff --git a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java index 62096792d764d..a812f29a8bc72 100644 --- a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java @@ -18,6 +18,7 @@ package android.accessibilityservice; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.StringRes; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -28,13 +29,19 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; +import android.text.TextUtils; import android.util.AttributeSet; +import android.util.TypedValue; import android.util.Xml; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.regex.Pattern; /** * Activities of interest to users with accessibility needs may request to be targets of the @@ -87,6 +94,13 @@ public final class AccessibilityShortcutInfo { */ private final int mHtmlDescriptionRes; + // Used for html description of accessibility service. The src tag must follow the + // prefix rule. e.g. + private static final String IMG_PREFIX = "R.drawable."; + private static final String ANCHOR_TAG = "a"; + private static final List UNSUPPORTED_TAG_LIST = new ArrayList<>( + Collections.singletonList(ANCHOR_TAG)); + /** * Creates a new instance. * @@ -134,7 +148,7 @@ public final class AccessibilityShortcutInfo { // Gets animated image mAnimatedImageRes = asAttributes.getResourceId( com.android.internal.R.styleable - .AccessibilityShortcutTarget_animatedImageDrawable, 0); + .AccessibilityShortcutTarget_animatedImageDrawable, /* defValue= */ 0); // Gets html description mHtmlDescriptionRes = asAttributes.getResourceId( com.android.internal.R.styleable.AccessibilityShortcutTarget_htmlDescription, @@ -192,7 +206,7 @@ public final class AccessibilityShortcutInfo { } /** - * The animated image resource id of the accessibility shortcut target. + * Gets the animated image resource id. * * @return The animated image resource id. * @@ -205,7 +219,8 @@ public final class AccessibilityShortcutInfo { /** * The animated image drawable of the accessibility shortcut target. * - * @return The animated image drawable. + * @return The animated image drawable, or null if the resource is invalid or the image + * exceed the screen size. */ @Nullable public Drawable loadAnimatedImage(@NonNull Context context) { @@ -213,21 +228,20 @@ public final class AccessibilityShortcutInfo { return null; } - final PackageManager packageManager = context.getPackageManager(); - final String packageName = mComponentName.getPackageName(); - final ApplicationInfo applicationInfo = mActivityInfo.applicationInfo; - - return packageManager.getDrawable(packageName, mAnimatedImageRes, applicationInfo); + return loadSafeAnimatedImage(context, mActivityInfo.applicationInfo, mAnimatedImageRes); } /** - * The localized html description of the accessibility shortcut target. + * The localized and restricted html description of the accessibility shortcut target. + * It filters the tag which do not meet the custom specification and the tag. * - * @return The localized html description. + * @return The localized and restricted html description. */ @Nullable public String loadHtmlDescription(@NonNull PackageManager packageManager) { - return loadResourceString(packageManager, mActivityInfo, mHtmlDescriptionRes); + final String htmlDescription = loadResourceString(packageManager, mActivityInfo, + mHtmlDescriptionRes); + return TextUtils.isEmpty(htmlDescription) ? null : getFilteredHtmlText(htmlDescription); } /** @@ -291,4 +305,103 @@ public final class AccessibilityShortcutInfo { stringBuilder.append("]"); return stringBuilder.toString(); } + + /** + * Gets the filtered html string for + * {@link android.accessibilityservice.AccessibilityServiceInfo} and + * {@link android.accessibilityservice.AccessibilityShortcutInfo}. It filters + * the tag which do not meet the custom specification and the tag. + * + * @param text the target text is html format. + * @return the filtered html string. + * + * @hide + */ + public static @NonNull String getFilteredHtmlText(@NonNull String text) { + final String replacementStart = ")"; + final String regexEnd = "(?i)"; + text = Pattern.compile(regexStart).matcher(text).replaceAll(replacementStart); + text = Pattern.compile(regexEnd).matcher(text).replaceAll(replacementEnd); + } + + final String regexInvalidImgTag = "(?i) getScreenWidthPixels(context); + final boolean isImageHeightOverScreenLength = + bannerDrawable.getIntrinsicHeight() > getScreenHeightPixels(context); + + return (isImageWidthOverScreenLength || isImageHeightOverScreenLength) + ? null + : bannerDrawable; + } + + /** + * Gets the width of the screen. + * + * @param context the current context. + * @return the width of the screen in term of pixels. + * + * @hide + */ + private static int getScreenWidthPixels(@NonNull Context context) { + final Resources resources = context.getResources(); + final int screenWidthDp = resources.getConfiguration().screenWidthDp; + + return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenWidthDp, + resources.getDisplayMetrics())); + } + + /** + * Gets the height of the screen. + * + * @param context the current context. + * @return the height of the screen in term of pixels. + * + * @hide + */ + private static int getScreenHeightPixels(@NonNull Context context) { + final Resources resources = context.getResources(); + final int screenHeightDp = resources.getConfiguration().screenHeightDp; + + return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenHeightDp, + resources.getDisplayMetrics())); + } }