Merge "Fine-tune the public APIs of allow rich content in Accessibility Settings for security." into rvc-dev
This commit is contained in:
@@ -22,6 +22,7 @@ import android.annotation.IntDef;
|
|||||||
import android.annotation.IntRange;
|
import android.annotation.IntRange;
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
|
import android.annotation.StringRes;
|
||||||
import android.compat.annotation.ChangeId;
|
import android.compat.annotation.ChangeId;
|
||||||
import android.compat.annotation.EnabledAfter;
|
import android.compat.annotation.EnabledAfter;
|
||||||
import android.compat.annotation.UnsupportedAppUsage;
|
import android.compat.annotation.UnsupportedAppUsage;
|
||||||
@@ -61,6 +62,7 @@ import java.lang.annotation.RetentionPolicy;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class describes an {@link AccessibilityService}. The system notifies an
|
* This class describes an {@link AccessibilityService}. The system notifies an
|
||||||
@@ -552,6 +554,13 @@ public class AccessibilityServiceInfo implements Parcelable {
|
|||||||
*/
|
*/
|
||||||
private int mHtmlDescriptionRes;
|
private int mHtmlDescriptionRes;
|
||||||
|
|
||||||
|
// Used for html description of accessibility service. The <img> src tag must follow the
|
||||||
|
// prefix rule. e.g. <img src="R.drawable.fileName"/>
|
||||||
|
private static final String IMG_PREFIX = "R.drawable.";
|
||||||
|
private static final String ANCHOR_TAG = "a";
|
||||||
|
private static final List<String> UNSUPPORTED_TAG_LIST = new ArrayList<>(
|
||||||
|
Collections.singletonList(ANCHOR_TAG));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
*/
|
*/
|
||||||
@@ -782,12 +791,10 @@ public class AccessibilityServiceInfo implements Parcelable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The animated image resource id.
|
* Gets the animated image resource id.
|
||||||
* <p>
|
*
|
||||||
* <strong>Statically set from
|
|
||||||
* {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
|
|
||||||
* </p>
|
|
||||||
* @return The animated image resource id.
|
* @return The animated image resource id.
|
||||||
|
*
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public int getAnimatedImageRes() {
|
public int getAnimatedImageRes() {
|
||||||
@@ -797,10 +804,12 @@ public class AccessibilityServiceInfo implements Parcelable {
|
|||||||
/**
|
/**
|
||||||
* The animated image drawable.
|
* The animated image drawable.
|
||||||
* <p>
|
* <p>
|
||||||
|
* Image can not exceed the screen size.
|
||||||
* <strong>Statically set from
|
* <strong>Statically set from
|
||||||
* {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
|
* {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
|
||||||
* </p>
|
* </p>
|
||||||
* @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
|
@Nullable
|
||||||
public Drawable loadAnimatedImage(@NonNull Context context) {
|
public Drawable loadAnimatedImage(@NonNull Context context) {
|
||||||
@@ -808,11 +817,8 @@ public class AccessibilityServiceInfo implements Parcelable {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final PackageManager packageManager = context.getPackageManager();
|
return loadSafeAnimatedImage(context, mResolveInfo.serviceInfo.applicationInfo,
|
||||||
final String packageName = mComponentName.getPackageName();
|
mAnimatedImageRes);
|
||||||
final ApplicationInfo applicationInfo = mResolveInfo.serviceInfo.applicationInfo;
|
|
||||||
|
|
||||||
return packageManager.getDrawable(packageName, mAnimatedImageRes, applicationInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -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.
|
||||||
* <p>
|
* <p>
|
||||||
|
* Filters the <img> tag which do not meet the custom specification and the <a> tag.
|
||||||
* <strong>Statically set from
|
* <strong>Statically set from
|
||||||
* {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
|
* {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
|
||||||
* </p>
|
* </p>
|
||||||
* @return The localized html description.
|
* @return The localized and restricted html description.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public String loadHtmlDescription(@NonNull PackageManager packageManager) {
|
public String loadHtmlDescription(@NonNull PackageManager packageManager) {
|
||||||
if (mHtmlDescriptionRes == 0) {
|
if (mHtmlDescriptionRes == /* invalid */ 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -941,7 +948,7 @@ public class AccessibilityServiceInfo implements Parcelable {
|
|||||||
final CharSequence htmlDescription = packageManager.getText(serviceInfo.packageName,
|
final CharSequence htmlDescription = packageManager.getText(serviceInfo.packageName,
|
||||||
mHtmlDescriptionRes, serviceInfo.applicationInfo);
|
mHtmlDescriptionRes, serviceInfo.applicationInfo);
|
||||||
if (htmlDescription != null) {
|
if (htmlDescription != null) {
|
||||||
return htmlDescription.toString().trim();
|
return getFilteredHtmlText(htmlDescription.toString().trim());
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1414,4 +1421,103 @@ public class AccessibilityServiceInfo implements Parcelable {
|
|||||||
return new AccessibilityServiceInfo[size];
|
return new AccessibilityServiceInfo[size];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the filtered html string for
|
||||||
|
* {@link android.accessibilityservice.AccessibilityServiceInfo} and
|
||||||
|
* {@link android.accessibilityservice.AccessibilityShortcutInfo}. It filters
|
||||||
|
* the <img> tag which do not meet the custom specification and the <a> 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 = "<invalidtag ";
|
||||||
|
final String replacementEnd = "</invalidtag>";
|
||||||
|
|
||||||
|
for (String tag : UNSUPPORTED_TAG_LIST) {
|
||||||
|
final String regexStart = "(?i)<" + tag + "(\\s+|>)";
|
||||||
|
final String regexEnd = "(?i)</" + tag + "\\s*>";
|
||||||
|
text = Pattern.compile(regexStart).matcher(text).replaceAll(replacementStart);
|
||||||
|
text = Pattern.compile(regexEnd).matcher(text).replaceAll(replacementEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String regexInvalidImgTag = "(?i)<img\\s+(?!src\\s*=\\s*\"(?-i)" + IMG_PREFIX + ")";
|
||||||
|
text = Pattern.compile(regexInvalidImgTag).matcher(text).replaceAll(
|
||||||
|
replacementStart);
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the animated image for
|
||||||
|
* {@link android.accessibilityservice.AccessibilityServiceInfo} and
|
||||||
|
* {@link android.accessibilityservice.AccessibilityShortcutInfo}. It checks the resource
|
||||||
|
* whether to exceed the screen size.
|
||||||
|
*
|
||||||
|
* @param context the current context.
|
||||||
|
* @param applicationInfo the current application.
|
||||||
|
* @param resId the animated image resource id.
|
||||||
|
* @return the animated image which is safe.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static Drawable loadSafeAnimatedImage(@NonNull Context context,
|
||||||
|
@NonNull ApplicationInfo applicationInfo, @StringRes int resId) {
|
||||||
|
if (resId == /* invalid */ 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final PackageManager packageManager = context.getPackageManager();
|
||||||
|
final String packageName = applicationInfo.packageName;
|
||||||
|
final Drawable bannerDrawable = packageManager.getDrawable(packageName, resId,
|
||||||
|
applicationInfo);
|
||||||
|
if (bannerDrawable == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean isImageWidthOverScreenLength =
|
||||||
|
bannerDrawable.getIntrinsicWidth() > 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()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package android.accessibilityservice;
|
|||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
|
import android.annotation.StringRes;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -28,13 +29,19 @@ import android.content.res.Resources;
|
|||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.content.res.XmlResourceParser;
|
import android.content.res.XmlResourceParser;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import android.util.TypedValue;
|
||||||
import android.util.Xml;
|
import android.util.Xml;
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
import java.io.IOException;
|
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
|
* 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;
|
private final int mHtmlDescriptionRes;
|
||||||
|
|
||||||
|
// Used for html description of accessibility service. The <img> src tag must follow the
|
||||||
|
// prefix rule. e.g. <img src="R.drawable.fileName"/>
|
||||||
|
private static final String IMG_PREFIX = "R.drawable.";
|
||||||
|
private static final String ANCHOR_TAG = "a";
|
||||||
|
private static final List<String> UNSUPPORTED_TAG_LIST = new ArrayList<>(
|
||||||
|
Collections.singletonList(ANCHOR_TAG));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
*
|
*
|
||||||
@@ -134,7 +148,7 @@ public final class AccessibilityShortcutInfo {
|
|||||||
// Gets animated image
|
// Gets animated image
|
||||||
mAnimatedImageRes = asAttributes.getResourceId(
|
mAnimatedImageRes = asAttributes.getResourceId(
|
||||||
com.android.internal.R.styleable
|
com.android.internal.R.styleable
|
||||||
.AccessibilityShortcutTarget_animatedImageDrawable, 0);
|
.AccessibilityShortcutTarget_animatedImageDrawable, /* defValue= */ 0);
|
||||||
// Gets html description
|
// Gets html description
|
||||||
mHtmlDescriptionRes = asAttributes.getResourceId(
|
mHtmlDescriptionRes = asAttributes.getResourceId(
|
||||||
com.android.internal.R.styleable.AccessibilityShortcutTarget_htmlDescription,
|
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.
|
* @return The animated image resource id.
|
||||||
*
|
*
|
||||||
@@ -205,7 +219,8 @@ public final class AccessibilityShortcutInfo {
|
|||||||
/**
|
/**
|
||||||
* The animated image drawable of the accessibility shortcut target.
|
* 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
|
@Nullable
|
||||||
public Drawable loadAnimatedImage(@NonNull Context context) {
|
public Drawable loadAnimatedImage(@NonNull Context context) {
|
||||||
@@ -213,21 +228,20 @@ public final class AccessibilityShortcutInfo {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final PackageManager packageManager = context.getPackageManager();
|
return loadSafeAnimatedImage(context, mActivityInfo.applicationInfo, mAnimatedImageRes);
|
||||||
final String packageName = mComponentName.getPackageName();
|
|
||||||
final ApplicationInfo applicationInfo = mActivityInfo.applicationInfo;
|
|
||||||
|
|
||||||
return packageManager.getDrawable(packageName, mAnimatedImageRes, applicationInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The localized html description of the accessibility shortcut target.
|
* The localized and restricted html description of the accessibility shortcut target.
|
||||||
|
* It filters the <img> tag which do not meet the custom specification and the <a> tag.
|
||||||
*
|
*
|
||||||
* @return The localized html description.
|
* @return The localized and restricted html description.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public String loadHtmlDescription(@NonNull PackageManager packageManager) {
|
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("]");
|
stringBuilder.append("]");
|
||||||
return stringBuilder.toString();
|
return stringBuilder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the filtered html string for
|
||||||
|
* {@link android.accessibilityservice.AccessibilityServiceInfo} and
|
||||||
|
* {@link android.accessibilityservice.AccessibilityShortcutInfo}. It filters
|
||||||
|
* the <img> tag which do not meet the custom specification and the <a> 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 = "<invalidtag ";
|
||||||
|
final String replacementEnd = "</invalidtag>";
|
||||||
|
|
||||||
|
for (String tag : UNSUPPORTED_TAG_LIST) {
|
||||||
|
final String regexStart = "(?i)<" + tag + "(\\s+|>)";
|
||||||
|
final String regexEnd = "(?i)</" + tag + "\\s*>";
|
||||||
|
text = Pattern.compile(regexStart).matcher(text).replaceAll(replacementStart);
|
||||||
|
text = Pattern.compile(regexEnd).matcher(text).replaceAll(replacementEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String regexInvalidImgTag = "(?i)<img\\s+(?!src\\s*=\\s*\"(?-i)" + IMG_PREFIX + ")";
|
||||||
|
text = Pattern.compile(regexInvalidImgTag).matcher(text).replaceAll(
|
||||||
|
replacementStart);
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the animated image for
|
||||||
|
* {@link android.accessibilityservice.AccessibilityServiceInfo} and
|
||||||
|
* {@link android.accessibilityservice.AccessibilityShortcutInfo}. It checks the resource
|
||||||
|
* whether to exceed the screen size.
|
||||||
|
*
|
||||||
|
* @param context the current context.
|
||||||
|
* @param applicationInfo the current application.
|
||||||
|
* @param resId the animated image resource id.
|
||||||
|
* @return the animated image which is safe.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static Drawable loadSafeAnimatedImage(@NonNull Context context,
|
||||||
|
@NonNull ApplicationInfo applicationInfo, @StringRes int resId) {
|
||||||
|
if (resId == /* invalid */ 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final PackageManager packageManager = context.getPackageManager();
|
||||||
|
final String packageName = applicationInfo.packageName;
|
||||||
|
final Drawable bannerDrawable = packageManager.getDrawable(packageName, resId,
|
||||||
|
applicationInfo);
|
||||||
|
if (bannerDrawable == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean isImageWidthOverScreenLength =
|
||||||
|
bannerDrawable.getIntrinsicWidth() > 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()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user