Merge "Fix A11y features blocked by IT admin can be bypassed using a11y shortcuts" into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
46be022182
@@ -2116,6 +2116,58 @@ public final class AccessibilityManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the accessibility target is allowed.
|
||||||
|
*
|
||||||
|
* @param packageName The name of the application attempting to perform the operation.
|
||||||
|
* @param uid The user id of the application attempting to perform the operation.
|
||||||
|
* @param userId The id of the user for whom to perform the operation.
|
||||||
|
* @return {@code true} the accessibility target is allowed.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public boolean isAccessibilityTargetAllowed(String packageName, int uid, int userId) {
|
||||||
|
final IAccessibilityManager service;
|
||||||
|
synchronized (mLock) {
|
||||||
|
service = getServiceLocked();
|
||||||
|
if (service == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return service.isAccessibilityTargetAllowed(packageName, uid, userId);
|
||||||
|
} catch (RemoteException re) {
|
||||||
|
Log.e(LOG_TAG, "Error while check accessibility target status", re);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends restricted dialog intent if the accessibility target is disallowed.
|
||||||
|
*
|
||||||
|
* @param packageName The name of the application attempting to perform the operation.
|
||||||
|
* @param uid The user id of the application attempting to perform the operation.
|
||||||
|
* @param userId The id of the user for whom to perform the operation.
|
||||||
|
* @return {@code true} if the restricted dialog is shown.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public boolean sendRestrictedDialogIntent(String packageName, int uid, int userId) {
|
||||||
|
final IAccessibilityManager service;
|
||||||
|
synchronized (mLock) {
|
||||||
|
service = getServiceLocked();
|
||||||
|
if (service == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return service.sendRestrictedDialogIntent(packageName, uid, userId);
|
||||||
|
} catch (RemoteException re) {
|
||||||
|
Log.e(LOG_TAG, "Error while show restricted dialog", re);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private IAccessibilityManager getServiceLocked() {
|
private IAccessibilityManager getServiceLocked() {
|
||||||
if (mService == null) {
|
if (mService == null) {
|
||||||
tryConnectToServiceLocked(null);
|
tryConnectToServiceLocked(null);
|
||||||
|
|||||||
@@ -124,6 +124,9 @@ interface IAccessibilityManager {
|
|||||||
boolean stopFlashNotificationSequence(String opPkg);
|
boolean stopFlashNotificationSequence(String opPkg);
|
||||||
boolean startFlashNotificationEvent(String opPkg, int reason, String reasonPkg);
|
boolean startFlashNotificationEvent(String opPkg, int reason, String reasonPkg);
|
||||||
|
|
||||||
|
boolean isAccessibilityTargetAllowed(String packageName, int uid, int userId);
|
||||||
|
boolean sendRestrictedDialogIntent(String packageName, int uid, int userId);
|
||||||
|
|
||||||
parcelable WindowTransformationSpec {
|
parcelable WindowTransformationSpec {
|
||||||
float[] transformationMatrix;
|
float[] transformationMatrix;
|
||||||
MagnificationSpec magnificationSpec;
|
MagnificationSpec magnificationSpec;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import android.content.Context;
|
|||||||
import android.view.accessibility.AccessibilityManager.ShortcutType;
|
import android.view.accessibility.AccessibilityManager.ShortcutType;
|
||||||
|
|
||||||
import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
|
import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
|
||||||
|
import com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for creating accessibility activity target.
|
* Base class for creating accessibility activity target.
|
||||||
@@ -40,8 +41,25 @@ class AccessibilityActivityTarget extends AccessibilityTarget {
|
|||||||
isShortcutContained(context, shortcutType,
|
isShortcutContained(context, shortcutType,
|
||||||
shortcutInfo.getComponentName().flattenToString()),
|
shortcutInfo.getComponentName().flattenToString()),
|
||||||
shortcutInfo.getComponentName().flattenToString(),
|
shortcutInfo.getComponentName().flattenToString(),
|
||||||
|
shortcutInfo.getActivityInfo().applicationInfo.uid,
|
||||||
shortcutInfo.getActivityInfo().loadLabel(context.getPackageManager()),
|
shortcutInfo.getActivityInfo().loadLabel(context.getPackageManager()),
|
||||||
shortcutInfo.getActivityInfo().loadIcon(context.getPackageManager()),
|
shortcutInfo.getActivityInfo().loadIcon(context.getPackageManager()),
|
||||||
convertToKey(convertToUserType(shortcutType)));
|
convertToKey(convertToUserType(shortcutType)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateActionItem(@NonNull TargetAdapter.ViewHolder holder,
|
||||||
|
@ShortcutMenuMode int shortcutMenuMode) {
|
||||||
|
super.updateActionItem(holder, shortcutMenuMode);
|
||||||
|
|
||||||
|
final boolean isAllowed = AccessibilityTargetHelper.isAccessibilityTargetAllowed(
|
||||||
|
getContext(), getComponentName().getPackageName(), getUid());
|
||||||
|
final boolean isEditMenuMode =
|
||||||
|
shortcutMenuMode == ShortcutMenuMode.EDIT;
|
||||||
|
final boolean enabled = isAllowed || (isEditMenuMode && isShortcutEnabled());
|
||||||
|
holder.mCheckBoxView.setEnabled(enabled);
|
||||||
|
holder.mIconView.setEnabled(enabled);
|
||||||
|
holder.mLabelView.setEnabled(enabled);
|
||||||
|
holder.mStatusView.setEnabled(enabled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import android.content.Context;
|
|||||||
import android.view.accessibility.AccessibilityManager.ShortcutType;
|
import android.view.accessibility.AccessibilityManager.ShortcutType;
|
||||||
|
|
||||||
import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
|
import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
|
||||||
|
import com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for creating accessibility service target with various fragment types related to
|
* Base class for creating accessibility service target with various fragment types related to
|
||||||
@@ -42,8 +43,25 @@ class AccessibilityServiceTarget extends AccessibilityTarget {
|
|||||||
isShortcutContained(context, shortcutType,
|
isShortcutContained(context, shortcutType,
|
||||||
serviceInfo.getComponentName().flattenToString()),
|
serviceInfo.getComponentName().flattenToString()),
|
||||||
serviceInfo.getComponentName().flattenToString(),
|
serviceInfo.getComponentName().flattenToString(),
|
||||||
|
serviceInfo.getResolveInfo().serviceInfo.applicationInfo.uid,
|
||||||
serviceInfo.getResolveInfo().loadLabel(context.getPackageManager()),
|
serviceInfo.getResolveInfo().loadLabel(context.getPackageManager()),
|
||||||
serviceInfo.getResolveInfo().loadIcon(context.getPackageManager()),
|
serviceInfo.getResolveInfo().loadIcon(context.getPackageManager()),
|
||||||
convertToKey(convertToUserType(shortcutType)));
|
convertToKey(convertToUserType(shortcutType)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateActionItem(@NonNull TargetAdapter.ViewHolder holder,
|
||||||
|
@ShortcutMenuMode int shortcutMenuMode) {
|
||||||
|
super.updateActionItem(holder, shortcutMenuMode);
|
||||||
|
|
||||||
|
final boolean isAllowed = AccessibilityTargetHelper.isAccessibilityTargetAllowed(
|
||||||
|
getContext(), getComponentName().getPackageName(), getUid());
|
||||||
|
final boolean isEditMenuMode =
|
||||||
|
shortcutMenuMode == ShortcutMenuMode.EDIT;
|
||||||
|
final boolean enabled = isAllowed || (isEditMenuMode && isShortcutEnabled());
|
||||||
|
holder.mCheckBoxView.setEnabled(enabled);
|
||||||
|
holder.mIconView.setEnabled(enabled);
|
||||||
|
holder.mLabelView.setEnabled(enabled);
|
||||||
|
holder.mStatusView.setEnabled(enabled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,6 +95,13 @@ public class AccessibilityShortcutChooserActivity extends Activity {
|
|||||||
|
|
||||||
private void onTargetSelected(AdapterView<?> parent, View view, int position, long id) {
|
private void onTargetSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
final AccessibilityTarget target = mTargets.get(position);
|
final AccessibilityTarget target = mTargets.get(position);
|
||||||
|
if (target instanceof AccessibilityServiceTarget
|
||||||
|
|| target instanceof AccessibilityActivityTarget) {
|
||||||
|
if (sendRestrictedDialogIntentIfNeeded(target)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
target.onSelected();
|
target.onSelected();
|
||||||
mMenuDialog.dismiss();
|
mMenuDialog.dismiss();
|
||||||
}
|
}
|
||||||
@@ -102,15 +109,41 @@ public class AccessibilityShortcutChooserActivity extends Activity {
|
|||||||
private void onTargetChecked(AdapterView<?> parent, View view, int position, long id) {
|
private void onTargetChecked(AdapterView<?> parent, View view, int position, long id) {
|
||||||
final AccessibilityTarget target = mTargets.get(position);
|
final AccessibilityTarget target = mTargets.get(position);
|
||||||
|
|
||||||
if ((target instanceof AccessibilityServiceTarget) && !target.isShortcutEnabled()) {
|
if (!target.isShortcutEnabled()) {
|
||||||
showPermissionDialogIfNeeded(this, (AccessibilityServiceTarget) target, mTargetAdapter);
|
if (target instanceof AccessibilityServiceTarget
|
||||||
|
|| target instanceof AccessibilityActivityTarget) {
|
||||||
|
if (sendRestrictedDialogIntentIfNeeded(target)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target instanceof AccessibilityServiceTarget) {
|
||||||
|
showPermissionDialogIfNeeded(this, (AccessibilityServiceTarget) target,
|
||||||
|
mTargetAdapter);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
target.onCheckedChanged(!target.isShortcutEnabled());
|
target.onCheckedChanged(!target.isShortcutEnabled());
|
||||||
mTargetAdapter.notifyDataSetChanged();
|
mTargetAdapter.notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends restricted dialog intent if the accessibility target is disallowed.
|
||||||
|
*
|
||||||
|
* @return true if sends restricted dialog intent, otherwise false.
|
||||||
|
*/
|
||||||
|
private boolean sendRestrictedDialogIntentIfNeeded(AccessibilityTarget target) {
|
||||||
|
if (AccessibilityTargetHelper.isAccessibilityTargetAllowed(this,
|
||||||
|
target.getComponentName().getPackageName(), target.getUid())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AccessibilityTargetHelper.sendRestrictedDialogIntent(this,
|
||||||
|
target.getComponentName().getPackageName(), target.getUid());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private void showPermissionDialogIfNeeded(Context context,
|
private void showPermissionDialogIfNeeded(Context context,
|
||||||
AccessibilityServiceTarget serviceTarget, ShortcutTargetAdapter targetAdapter) {
|
AccessibilityServiceTarget serviceTarget, ShortcutTargetAdapter targetAdapter) {
|
||||||
if (mPermissionDialog != null) {
|
if (mPermissionDialog != null) {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import static com.android.internal.accessibility.util.ShortcutUtils.optOutValueF
|
|||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -37,8 +38,11 @@ import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder;
|
|||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base class for creating various target related to accessibility service,
|
* Abstract base class for creating various target related to accessibility service, accessibility
|
||||||
* accessibility activity, and allowlisting feature.
|
* activity, and allowlisting features.
|
||||||
|
*
|
||||||
|
* <p> Disables accessibility features that are not permitted in adding a restricted padlock icon
|
||||||
|
* and showing admin support message dialog.
|
||||||
*/
|
*/
|
||||||
public abstract class AccessibilityTarget implements TargetOperations, OnTargetSelectedListener,
|
public abstract class AccessibilityTarget implements TargetOperations, OnTargetSelectedListener,
|
||||||
OnTargetCheckedChangeListener {
|
OnTargetCheckedChangeListener {
|
||||||
@@ -49,6 +53,8 @@ public abstract class AccessibilityTarget implements TargetOperations, OnTargetS
|
|||||||
private int mFragmentType;
|
private int mFragmentType;
|
||||||
private boolean mShortcutEnabled;
|
private boolean mShortcutEnabled;
|
||||||
private String mId;
|
private String mId;
|
||||||
|
private int mUid;
|
||||||
|
private ComponentName mComponentName;
|
||||||
private CharSequence mLabel;
|
private CharSequence mLabel;
|
||||||
private Drawable mIcon;
|
private Drawable mIcon;
|
||||||
private String mKey;
|
private String mKey;
|
||||||
@@ -57,12 +63,14 @@ public abstract class AccessibilityTarget implements TargetOperations, OnTargetS
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public AccessibilityTarget(Context context, @ShortcutType int shortcutType,
|
public AccessibilityTarget(Context context, @ShortcutType int shortcutType,
|
||||||
@AccessibilityFragmentType int fragmentType, boolean isShortcutSwitched, String id,
|
@AccessibilityFragmentType int fragmentType, boolean isShortcutSwitched, String id,
|
||||||
CharSequence label, Drawable icon, String key) {
|
int uid, CharSequence label, Drawable icon, String key) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mShortcutType = shortcutType;
|
mShortcutType = shortcutType;
|
||||||
mFragmentType = fragmentType;
|
mFragmentType = fragmentType;
|
||||||
mShortcutEnabled = isShortcutSwitched;
|
mShortcutEnabled = isShortcutSwitched;
|
||||||
mId = id;
|
mId = id;
|
||||||
|
mUid = uid;
|
||||||
|
mComponentName = ComponentName.unflattenFromString(id);
|
||||||
mLabel = label;
|
mLabel = label;
|
||||||
mIcon = icon;
|
mIcon = icon;
|
||||||
mKey = key;
|
mKey = key;
|
||||||
@@ -71,9 +79,14 @@ public abstract class AccessibilityTarget implements TargetOperations, OnTargetS
|
|||||||
@Override
|
@Override
|
||||||
public void updateActionItem(@NonNull ViewHolder holder,
|
public void updateActionItem(@NonNull ViewHolder holder,
|
||||||
@ShortcutConstants.ShortcutMenuMode int shortcutMenuMode) {
|
@ShortcutConstants.ShortcutMenuMode int shortcutMenuMode) {
|
||||||
|
// Resetting the enable state of the item to avoid the previous wrong state of RecyclerView.
|
||||||
|
holder.mCheckBoxView.setEnabled(true);
|
||||||
|
holder.mIconView.setEnabled(true);
|
||||||
|
holder.mLabelView.setEnabled(true);
|
||||||
|
holder.mStatusView.setEnabled(true);
|
||||||
|
|
||||||
final boolean isEditMenuMode =
|
final boolean isEditMenuMode =
|
||||||
shortcutMenuMode == ShortcutConstants.ShortcutMenuMode.EDIT;
|
shortcutMenuMode == ShortcutConstants.ShortcutMenuMode.EDIT;
|
||||||
|
|
||||||
holder.mCheckBoxView.setChecked(isEditMenuMode && isShortcutEnabled());
|
holder.mCheckBoxView.setChecked(isEditMenuMode && isShortcutEnabled());
|
||||||
holder.mCheckBoxView.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
|
holder.mCheckBoxView.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
|
||||||
holder.mIconView.setImageDrawable(getIcon());
|
holder.mIconView.setImageDrawable(getIcon());
|
||||||
@@ -145,6 +158,14 @@ public abstract class AccessibilityTarget implements TargetOperations, OnTargetS
|
|||||||
return mId;
|
return mId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getUid() {
|
||||||
|
return mUid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ComponentName getComponentName() {
|
||||||
|
return mComponentName;
|
||||||
|
}
|
||||||
|
|
||||||
public CharSequence getLabel() {
|
public CharSequence getLabel() {
|
||||||
return mLabel;
|
return mLabel;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import android.app.ActivityManager;
|
|||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.UserHandle;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.text.BidiFormatter;
|
import android.text.BidiFormatter;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -202,12 +203,14 @@ public final class AccessibilityTargetHelper {
|
|||||||
private static List<AccessibilityTarget> getAllowListingFeatureTargets(Context context,
|
private static List<AccessibilityTarget> getAllowListingFeatureTargets(Context context,
|
||||||
@ShortcutType int shortcutType) {
|
@ShortcutType int shortcutType) {
|
||||||
final List<AccessibilityTarget> targets = new ArrayList<>();
|
final List<AccessibilityTarget> targets = new ArrayList<>();
|
||||||
|
final int uid = context.getApplicationInfo().uid;
|
||||||
|
|
||||||
final InvisibleToggleAllowListingFeatureTarget magnification =
|
final InvisibleToggleAllowListingFeatureTarget magnification =
|
||||||
new InvisibleToggleAllowListingFeatureTarget(context,
|
new InvisibleToggleAllowListingFeatureTarget(context,
|
||||||
shortcutType,
|
shortcutType,
|
||||||
isShortcutContained(context, shortcutType, MAGNIFICATION_CONTROLLER_NAME),
|
isShortcutContained(context, shortcutType, MAGNIFICATION_CONTROLLER_NAME),
|
||||||
MAGNIFICATION_CONTROLLER_NAME,
|
MAGNIFICATION_CONTROLLER_NAME,
|
||||||
|
uid,
|
||||||
context.getString(R.string.accessibility_magnification_chooser_text),
|
context.getString(R.string.accessibility_magnification_chooser_text),
|
||||||
context.getDrawable(R.drawable.ic_accessibility_magnification),
|
context.getDrawable(R.drawable.ic_accessibility_magnification),
|
||||||
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
|
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
|
||||||
@@ -219,6 +222,7 @@ public final class AccessibilityTargetHelper {
|
|||||||
isShortcutContained(context, shortcutType,
|
isShortcutContained(context, shortcutType,
|
||||||
DALTONIZER_COMPONENT_NAME.flattenToString()),
|
DALTONIZER_COMPONENT_NAME.flattenToString()),
|
||||||
DALTONIZER_COMPONENT_NAME.flattenToString(),
|
DALTONIZER_COMPONENT_NAME.flattenToString(),
|
||||||
|
uid,
|
||||||
context.getString(R.string.color_correction_feature_name),
|
context.getString(R.string.color_correction_feature_name),
|
||||||
context.getDrawable(R.drawable.ic_accessibility_color_correction),
|
context.getDrawable(R.drawable.ic_accessibility_color_correction),
|
||||||
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED);
|
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED);
|
||||||
@@ -230,6 +234,7 @@ public final class AccessibilityTargetHelper {
|
|||||||
isShortcutContained(context, shortcutType,
|
isShortcutContained(context, shortcutType,
|
||||||
COLOR_INVERSION_COMPONENT_NAME.flattenToString()),
|
COLOR_INVERSION_COMPONENT_NAME.flattenToString()),
|
||||||
COLOR_INVERSION_COMPONENT_NAME.flattenToString(),
|
COLOR_INVERSION_COMPONENT_NAME.flattenToString(),
|
||||||
|
uid,
|
||||||
context.getString(R.string.color_inversion_feature_name),
|
context.getString(R.string.color_inversion_feature_name),
|
||||||
context.getDrawable(R.drawable.ic_accessibility_color_inversion),
|
context.getDrawable(R.drawable.ic_accessibility_color_inversion),
|
||||||
Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
|
Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
|
||||||
@@ -242,6 +247,7 @@ public final class AccessibilityTargetHelper {
|
|||||||
isShortcutContained(context, shortcutType,
|
isShortcutContained(context, shortcutType,
|
||||||
ONE_HANDED_COMPONENT_NAME.flattenToString()),
|
ONE_HANDED_COMPONENT_NAME.flattenToString()),
|
||||||
ONE_HANDED_COMPONENT_NAME.flattenToString(),
|
ONE_HANDED_COMPONENT_NAME.flattenToString(),
|
||||||
|
uid,
|
||||||
context.getString(R.string.one_handed_mode_feature_name),
|
context.getString(R.string.one_handed_mode_feature_name),
|
||||||
context.getDrawable(R.drawable.ic_accessibility_one_handed),
|
context.getDrawable(R.drawable.ic_accessibility_one_handed),
|
||||||
Settings.Secure.ONE_HANDED_MODE_ACTIVATED);
|
Settings.Secure.ONE_HANDED_MODE_ACTIVATED);
|
||||||
@@ -254,6 +260,7 @@ public final class AccessibilityTargetHelper {
|
|||||||
isShortcutContained(context, shortcutType,
|
isShortcutContained(context, shortcutType,
|
||||||
REDUCE_BRIGHT_COLORS_COMPONENT_NAME.flattenToString()),
|
REDUCE_BRIGHT_COLORS_COMPONENT_NAME.flattenToString()),
|
||||||
REDUCE_BRIGHT_COLORS_COMPONENT_NAME.flattenToString(),
|
REDUCE_BRIGHT_COLORS_COMPONENT_NAME.flattenToString(),
|
||||||
|
uid,
|
||||||
context.getString(R.string.reduce_bright_colors_feature_name),
|
context.getString(R.string.reduce_bright_colors_feature_name),
|
||||||
context.getDrawable(R.drawable.ic_accessibility_reduce_bright_colors),
|
context.getDrawable(R.drawable.ic_accessibility_reduce_bright_colors),
|
||||||
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED);
|
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED);
|
||||||
@@ -265,6 +272,7 @@ public final class AccessibilityTargetHelper {
|
|||||||
isShortcutContained(context, shortcutType,
|
isShortcutContained(context, shortcutType,
|
||||||
ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString()),
|
ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString()),
|
||||||
ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString(),
|
ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString(),
|
||||||
|
uid,
|
||||||
context.getString(R.string.hearing_aids_feature_name),
|
context.getString(R.string.hearing_aids_feature_name),
|
||||||
context.getDrawable(R.drawable.ic_accessibility_hearing_aid),
|
context.getDrawable(R.drawable.ic_accessibility_hearing_aid),
|
||||||
/* key= */ null);
|
/* key= */ null);
|
||||||
@@ -327,4 +335,21 @@ public final class AccessibilityTargetHelper {
|
|||||||
final Locale locale = context.getResources().getConfiguration().getLocales().get(0);
|
final Locale locale = context.getResources().getConfiguration().getLocales().get(0);
|
||||||
return BidiFormatter.getInstance(locale).unicodeWrap(label);
|
return BidiFormatter.getInstance(locale).unicodeWrap(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the{@link AccessibilityTarget} is allowed.
|
||||||
|
*/
|
||||||
|
public static boolean isAccessibilityTargetAllowed(Context context, String packageName,
|
||||||
|
int uid) {
|
||||||
|
final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
|
||||||
|
return am.isAccessibilityTargetAllowed(packageName, uid, UserHandle.myUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends restricted dialog intent if the accessibility target is disallowed.
|
||||||
|
*/
|
||||||
|
public static boolean sendRestrictedDialogIntent(Context context, String packageName, int uid) {
|
||||||
|
final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
|
||||||
|
return am.sendRestrictedDialogIntent(packageName, uid, UserHandle.myUserId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,8 +29,9 @@ import com.android.internal.accessibility.common.ShortcutConstants.Accessibility
|
|||||||
class InvisibleToggleAllowListingFeatureTarget extends AccessibilityTarget {
|
class InvisibleToggleAllowListingFeatureTarget extends AccessibilityTarget {
|
||||||
|
|
||||||
InvisibleToggleAllowListingFeatureTarget(Context context, @ShortcutType int shortcutType,
|
InvisibleToggleAllowListingFeatureTarget(Context context, @ShortcutType int shortcutType,
|
||||||
boolean isShortcutSwitched, String id, CharSequence label, Drawable icon, String key) {
|
boolean isShortcutSwitched, String id, int uid, CharSequence label, Drawable icon,
|
||||||
super(context, shortcutType, AccessibilityFragmentType.INVISIBLE_TOGGLE,
|
String key) {
|
||||||
isShortcutSwitched, id, label, icon, key);
|
super(context, shortcutType, AccessibilityFragmentType.INVISIBLE_TOGGLE, isShortcutSwitched,
|
||||||
|
id, uid, label, icon, key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,12 +29,22 @@ import com.android.internal.accessibility.common.ShortcutConstants.Accessibility
|
|||||||
import com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode;
|
import com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode;
|
||||||
import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder;
|
import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extension for {@link AccessibilityServiceTarget} with {@link AccessibilityFragmentType#TOGGLE}
|
* Extension for {@link AccessibilityServiceTarget} with {@link AccessibilityFragmentType#TOGGLE}
|
||||||
* type.
|
* type.
|
||||||
*/
|
*/
|
||||||
class ToggleAccessibilityServiceTarget extends AccessibilityServiceTarget {
|
class ToggleAccessibilityServiceTarget extends AccessibilityServiceTarget {
|
||||||
|
|
||||||
|
/** Float enum for view alpha setting. */
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@interface StatusViewAlphaScale {
|
||||||
|
float OPAQUE = 1.0f;
|
||||||
|
float DISABLED = 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
ToggleAccessibilityServiceTarget(Context context, @ShortcutType int shortcutType,
|
ToggleAccessibilityServiceTarget(Context context, @ShortcutType int shortcutType,
|
||||||
@NonNull AccessibilityServiceInfo serviceInfo) {
|
@NonNull AccessibilityServiceInfo serviceInfo) {
|
||||||
super(context,
|
super(context,
|
||||||
@@ -53,9 +63,13 @@ class ToggleAccessibilityServiceTarget extends AccessibilityServiceTarget {
|
|||||||
@ShortcutMenuMode int shortcutMenuMode) {
|
@ShortcutMenuMode int shortcutMenuMode) {
|
||||||
super.updateActionItem(holder, shortcutMenuMode);
|
super.updateActionItem(holder, shortcutMenuMode);
|
||||||
|
|
||||||
|
final boolean isAllowed = AccessibilityTargetHelper.isAccessibilityTargetAllowed(
|
||||||
|
getContext(), getComponentName().getPackageName(), getUid());
|
||||||
final boolean isEditMenuMode =
|
final boolean isEditMenuMode =
|
||||||
shortcutMenuMode == ShortcutMenuMode.EDIT;
|
shortcutMenuMode == ShortcutMenuMode.EDIT;
|
||||||
holder.mStatusView.setVisibility(isEditMenuMode ? View.GONE : View.VISIBLE);
|
holder.mStatusView.setVisibility(isEditMenuMode ? View.GONE : View.VISIBLE);
|
||||||
holder.mStatusView.setText(getStateDescription());
|
holder.mStatusView.setText(getStateDescription());
|
||||||
|
holder.mStatusView.setAlpha(isAllowed
|
||||||
|
? StatusViewAlphaScale.OPAQUE : StatusViewAlphaScale.DISABLED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,9 +35,10 @@ import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder;
|
|||||||
class ToggleAllowListingFeatureTarget extends AccessibilityTarget {
|
class ToggleAllowListingFeatureTarget extends AccessibilityTarget {
|
||||||
|
|
||||||
ToggleAllowListingFeatureTarget(Context context, @ShortcutType int shortcutType,
|
ToggleAllowListingFeatureTarget(Context context, @ShortcutType int shortcutType,
|
||||||
boolean isShortcutSwitched, String id, CharSequence label, Drawable icon, String key) {
|
boolean isShortcutSwitched, String id, int uid, CharSequence label, Drawable icon,
|
||||||
super(context, shortcutType, AccessibilityFragmentType.TOGGLE,
|
String key) {
|
||||||
isShortcutSwitched, id, label, icon, key);
|
super(context, shortcutType, AccessibilityFragmentType.TOGGLE, isShortcutSwitched, id,
|
||||||
|
uid, label, icon, key);
|
||||||
|
|
||||||
final int statusResId = isFeatureEnabled()
|
final int statusResId = isFeatureEnabled()
|
||||||
? R.string.accessibility_shortcut_menu_item_status_on
|
? R.string.accessibility_shortcut_menu_item_status_on
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ import static org.hamcrest.Matchers.allOf;
|
|||||||
import static org.hamcrest.Matchers.endsWith;
|
import static org.hamcrest.Matchers.endsWith;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||||
@@ -54,6 +57,7 @@ import androidx.test.runner.AndroidJUnit4;
|
|||||||
import com.android.internal.R;
|
import com.android.internal.R;
|
||||||
import com.android.internal.accessibility.dialog.AccessibilityShortcutChooserActivity;
|
import com.android.internal.accessibility.dialog.AccessibilityShortcutChooserActivity;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@@ -71,6 +75,7 @@ public class AccessibilityShortcutChooserActivityTest {
|
|||||||
private static final String ONE_HANDED_MODE = "One-Handed mode";
|
private static final String ONE_HANDED_MODE = "One-Handed mode";
|
||||||
private static final String TEST_LABEL = "TEST_LABEL";
|
private static final String TEST_LABEL = "TEST_LABEL";
|
||||||
private static final ComponentName TEST_COMPONENT_NAME = new ComponentName("package", "class");
|
private static final ComponentName TEST_COMPONENT_NAME = new ComponentName("package", "class");
|
||||||
|
private TestAccessibilityShortcutChooserActivity mActivity;
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
@@ -85,10 +90,22 @@ public class AccessibilityShortcutChooserActivityTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private IAccessibilityManager mAccessibilityManagerService;
|
private IAccessibilityManager mAccessibilityManagerService;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
when(mAccessibilityServiceInfo.getResolveInfo()).thenReturn(mResolveInfo);
|
||||||
|
mResolveInfo.serviceInfo = mServiceInfo;
|
||||||
|
mServiceInfo.applicationInfo = mApplicationInfo;
|
||||||
|
when(mResolveInfo.loadLabel(any(PackageManager.class))).thenReturn(TEST_LABEL);
|
||||||
|
when(mAccessibilityServiceInfo.getComponentName()).thenReturn(TEST_COMPONENT_NAME);
|
||||||
|
when(mAccessibilityManagerService.getInstalledAccessibilityServiceList(
|
||||||
|
anyInt())).thenReturn(Collections.singletonList(mAccessibilityServiceInfo));
|
||||||
|
when(mAccessibilityManagerService.isAccessibilityTargetAllowed(
|
||||||
|
anyString(), anyInt(), anyInt())).thenReturn(true);
|
||||||
|
TestAccessibilityShortcutChooserActivity.setupForTesting(mAccessibilityManagerService);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void doubleClickTestServiceAndClickDenyButton_permissionDialogDoesNotExist()
|
public void doubleClickTestServiceAndClickDenyButton_permissionDialogDoesNotExist() {
|
||||||
throws Exception {
|
|
||||||
configureTestService();
|
|
||||||
final ActivityScenario<TestAccessibilityShortcutChooserActivity> scenario =
|
final ActivityScenario<TestAccessibilityShortcutChooserActivity> scenario =
|
||||||
ActivityScenario.launch(TestAccessibilityShortcutChooserActivity.class);
|
ActivityScenario.launch(TestAccessibilityShortcutChooserActivity.class);
|
||||||
scenario.moveToState(Lifecycle.State.CREATED);
|
scenario.moveToState(Lifecycle.State.CREATED);
|
||||||
@@ -101,12 +118,36 @@ public class AccessibilityShortcutChooserActivityTest {
|
|||||||
onView(withText(TEST_LABEL)).perform(scrollTo(), doubleClick());
|
onView(withText(TEST_LABEL)).perform(scrollTo(), doubleClick());
|
||||||
onView(withId(R.id.accessibility_permission_enable_deny_button)).perform(scrollTo(),
|
onView(withId(R.id.accessibility_permission_enable_deny_button)).perform(scrollTo(),
|
||||||
click());
|
click());
|
||||||
|
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||||
|
|
||||||
onView(withId(R.id.accessibility_permissionDialog_title)).inRoot(isDialog()).check(
|
onView(withId(R.id.accessibility_permissionDialog_title)).inRoot(isDialog()).check(
|
||||||
doesNotExist());
|
doesNotExist());
|
||||||
scenario.moveToState(Lifecycle.State.DESTROYED);
|
scenario.moveToState(Lifecycle.State.DESTROYED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void clickServiceTarget_notPermittedByAdmin_sendRestrictedDialogIntent()
|
||||||
|
throws Exception {
|
||||||
|
when(mAccessibilityManagerService.isAccessibilityTargetAllowed(
|
||||||
|
eq(TEST_COMPONENT_NAME.getPackageName()), anyInt(), anyInt())).thenReturn(false);
|
||||||
|
final ActivityScenario<TestAccessibilityShortcutChooserActivity> scenario =
|
||||||
|
ActivityScenario.launch(TestAccessibilityShortcutChooserActivity.class);
|
||||||
|
scenario.onActivity(activity -> mActivity = activity);
|
||||||
|
scenario.moveToState(Lifecycle.State.CREATED);
|
||||||
|
scenario.moveToState(Lifecycle.State.STARTED);
|
||||||
|
scenario.moveToState(Lifecycle.State.RESUMED);
|
||||||
|
|
||||||
|
onView(withText(R.string.accessibility_select_shortcut_menu_title)).inRoot(
|
||||||
|
isDialog()).check(matches(isDisplayed()));
|
||||||
|
onView(withText(R.string.edit_accessibility_shortcut_menu_button)).perform(click());
|
||||||
|
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||||
|
|
||||||
|
onView(withText(TEST_LABEL)).perform(scrollTo(), click());
|
||||||
|
verify(mAccessibilityManagerService).sendRestrictedDialogIntent(
|
||||||
|
eq(TEST_COMPONENT_NAME.getPackageName()), anyInt(), anyInt());
|
||||||
|
scenario.moveToState(Lifecycle.State.DESTROYED);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void popEditShortcutMenuList_oneHandedModeEnabled_shouldBeInListView() {
|
public void popEditShortcutMenuList_oneHandedModeEnabled_shouldBeInListView() {
|
||||||
TestUtils.setOneHandedModeEnabled(this, /* enabled= */ true);
|
TestUtils.setOneHandedModeEnabled(this, /* enabled= */ true);
|
||||||
@@ -145,18 +186,6 @@ public class AccessibilityShortcutChooserActivityTest {
|
|||||||
scenario.moveToState(Lifecycle.State.DESTROYED);
|
scenario.moveToState(Lifecycle.State.DESTROYED);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void configureTestService() throws Exception {
|
|
||||||
when(mAccessibilityServiceInfo.getResolveInfo()).thenReturn(mResolveInfo);
|
|
||||||
mResolveInfo.serviceInfo = mServiceInfo;
|
|
||||||
mServiceInfo.applicationInfo = mApplicationInfo;
|
|
||||||
when(mResolveInfo.loadLabel(any(PackageManager.class))).thenReturn(TEST_LABEL);
|
|
||||||
when(mAccessibilityServiceInfo.getComponentName()).thenReturn(TEST_COMPONENT_NAME);
|
|
||||||
when(mAccessibilityManagerService.getInstalledAccessibilityServiceList(
|
|
||||||
anyInt())).thenReturn(Collections.singletonList(mAccessibilityServiceInfo));
|
|
||||||
|
|
||||||
TestAccessibilityShortcutChooserActivity.setupForTesting(mAccessibilityManagerService);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for testing.
|
* Used for testing.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import static com.android.internal.accessibility.util.AccessibilityStatsLogUtils
|
|||||||
import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
|
import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
|
||||||
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
|
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
|
||||||
import static com.android.server.accessibility.AccessibilityUserState.doesShortcutTargetsStringContain;
|
import static com.android.server.accessibility.AccessibilityUserState.doesShortcutTargetsStringContain;
|
||||||
|
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.accessibilityservice.AccessibilityGestureEvent;
|
import android.accessibilityservice.AccessibilityGestureEvent;
|
||||||
@@ -54,8 +55,10 @@ import android.annotation.RequiresPermission;
|
|||||||
import android.annotation.UserIdInt;
|
import android.annotation.UserIdInt;
|
||||||
import android.app.ActivityOptions;
|
import android.app.ActivityOptions;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
|
import android.app.AppOpsManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.RemoteAction;
|
import android.app.RemoteAction;
|
||||||
|
import android.app.admin.DevicePolicyManager;
|
||||||
import android.appwidget.AppWidgetManagerInternal;
|
import android.appwidget.AppWidgetManagerInternal;
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
@@ -156,6 +159,7 @@ import com.android.server.pm.UserManagerInternal;
|
|||||||
import com.android.server.policy.WindowManagerPolicy;
|
import com.android.server.policy.WindowManagerPolicy;
|
||||||
import com.android.server.wm.ActivityTaskManagerInternal;
|
import com.android.server.wm.ActivityTaskManagerInternal;
|
||||||
import com.android.server.wm.WindowManagerInternal;
|
import com.android.server.wm.WindowManagerInternal;
|
||||||
|
import com.android.settingslib.RestrictedLockUtils;
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
@@ -3871,6 +3875,51 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@RequiresPermission(anyOf = {
|
||||||
|
android.Manifest.permission.MANAGE_USERS,
|
||||||
|
android.Manifest.permission.QUERY_ADMIN_POLICY})
|
||||||
|
public boolean isAccessibilityTargetAllowed(String packageName, int uid, int userId) {
|
||||||
|
final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
|
||||||
|
final List<String> permittedServices = dpm.getPermittedAccessibilityServices(userId);
|
||||||
|
|
||||||
|
// permittedServices null means all accessibility services are allowed.
|
||||||
|
boolean allowed = permittedServices == null || permittedServices.contains(packageName);
|
||||||
|
if (allowed) {
|
||||||
|
final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
|
||||||
|
final int mode = appOps.noteOpNoThrow(
|
||||||
|
AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
|
||||||
|
uid, packageName, /* attributionTag= */ null, /* message= */ null);
|
||||||
|
final boolean ecmEnabled = mContext.getResources().getBoolean(
|
||||||
|
R.bool.config_enhancedConfirmationModeEnabled);
|
||||||
|
return !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@RequiresPermission(anyOf = {
|
||||||
|
android.Manifest.permission.MANAGE_USERS,
|
||||||
|
android.Manifest.permission.QUERY_ADMIN_POLICY})
|
||||||
|
public boolean sendRestrictedDialogIntent(String packageName, int uid, int userId) {
|
||||||
|
// The accessibility service is allowed. Don't show the restricted dialog.
|
||||||
|
if (isAccessibilityTargetAllowed(packageName, uid, userId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final EnforcedAdmin admin =
|
||||||
|
RestrictedLockUtilsInternal.checkIfAccessibilityServiceDisallowed(
|
||||||
|
mContext, packageName, userId);
|
||||||
|
if (admin != null) {
|
||||||
|
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, admin);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RestrictedLockUtils.sendShowRestrictedSettingDialogIntent(mContext,
|
||||||
|
packageName, uid);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
|
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
|
||||||
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
|
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
|
||||||
|
|||||||
@@ -24,9 +24,7 @@ import android.accessibilityservice.AccessibilityService;
|
|||||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.annotation.UserIdInt;
|
|
||||||
import android.app.AppOpsManager;
|
import android.app.AppOpsManager;
|
||||||
import android.app.admin.DevicePolicyManager;
|
|
||||||
import android.appwidget.AppWidgetManagerInternal;
|
import android.appwidget.AppWidgetManagerInternal;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -46,7 +44,6 @@ import android.view.inputmethod.InputMethodInfo;
|
|||||||
|
|
||||||
import com.android.internal.util.ArrayUtils;
|
import com.android.internal.util.ArrayUtils;
|
||||||
import com.android.server.inputmethod.InputMethodManagerInternal;
|
import com.android.server.inputmethod.InputMethodManagerInternal;
|
||||||
import com.android.settingslib.RestrictedLockUtils;
|
|
||||||
|
|
||||||
import libcore.util.EmptyArray;
|
import libcore.util.EmptyArray;
|
||||||
|
|
||||||
@@ -420,7 +417,7 @@ public class AccessibilitySecurityPolicy {
|
|||||||
|
|
||||||
// TODO(b/207697949, b/208872785): Add cts test for managed device.
|
// TODO(b/207697949, b/208872785): Add cts test for managed device.
|
||||||
// Use RestrictedLockUtilsInternal in AccessibilitySecurityPolicy
|
// Use RestrictedLockUtilsInternal in AccessibilitySecurityPolicy
|
||||||
if (checkIfInputMethodDisallowed(
|
if (RestrictedLockUtilsInternal.checkIfInputMethodDisallowed(
|
||||||
mContext, inputMethodInfo.getPackageName(), callingUserId) != null) {
|
mContext, inputMethodInfo.getPackageName(), callingUserId) != null) {
|
||||||
return ENABLE_IME_FAIL_BY_ADMIN;
|
return ENABLE_IME_FAIL_BY_ADMIN;
|
||||||
}
|
}
|
||||||
@@ -428,72 +425,6 @@ public class AccessibilitySecurityPolicy {
|
|||||||
return ENABLE_IME_SUCCESS;
|
return ENABLE_IME_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the UserHandle for a userId. Return null for USER_NULL
|
|
||||||
*/
|
|
||||||
private static UserHandle getUserHandleOf(@UserIdInt int userId) {
|
|
||||||
if (userId == UserHandle.USER_NULL) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
return UserHandle.of(userId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int getManagedProfileId(Context context, int userId) {
|
|
||||||
UserManager um = context.getSystemService(UserManager.class);
|
|
||||||
List<UserInfo> userProfiles = um.getProfiles(userId);
|
|
||||||
for (UserInfo uInfo : userProfiles) {
|
|
||||||
if (uInfo.id == userId) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (uInfo.isManagedProfile()) {
|
|
||||||
return uInfo.id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return UserHandle.USER_NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static RestrictedLockUtils.EnforcedAdmin checkIfInputMethodDisallowed(Context context,
|
|
||||||
String packageName, int userId) {
|
|
||||||
DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
|
|
||||||
if (dpm == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
RestrictedLockUtils.EnforcedAdmin admin =
|
|
||||||
RestrictedLockUtils.getProfileOrDeviceOwner(context, getUserHandleOf(userId));
|
|
||||||
boolean permitted = true;
|
|
||||||
if (admin != null) {
|
|
||||||
permitted = dpm.isInputMethodPermittedByAdmin(admin.component,
|
|
||||||
packageName, userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean permittedByParentAdmin = true;
|
|
||||||
RestrictedLockUtils.EnforcedAdmin profileAdmin = null;
|
|
||||||
int managedProfileId = getManagedProfileId(context, userId);
|
|
||||||
if (managedProfileId != UserHandle.USER_NULL) {
|
|
||||||
profileAdmin = RestrictedLockUtils.getProfileOrDeviceOwner(
|
|
||||||
context, getUserHandleOf(managedProfileId));
|
|
||||||
// If the device is an organization-owned device with a managed profile, the
|
|
||||||
// managedProfileId will be used instead of the affected userId. This is because
|
|
||||||
// isInputMethodPermittedByAdmin is called on the parent DPM instance, which will
|
|
||||||
// return results affecting the personal profile.
|
|
||||||
if (profileAdmin != null && dpm.isOrganizationOwnedDeviceWithManagedProfile()) {
|
|
||||||
DevicePolicyManager parentDpm = dpm.getParentProfileInstance(
|
|
||||||
UserManager.get(context).getUserInfo(managedProfileId));
|
|
||||||
permittedByParentAdmin = parentDpm.isInputMethodPermittedByAdmin(
|
|
||||||
profileAdmin.component, packageName, managedProfileId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!permitted && !permittedByParentAdmin) {
|
|
||||||
return RestrictedLockUtils.EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
|
|
||||||
} else if (!permitted) {
|
|
||||||
return admin;
|
|
||||||
} else if (!permittedByParentAdmin) {
|
|
||||||
return profileAdmin;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the parent userId of the profile according to the specified userId.
|
* Returns the parent userId of the profile according to the specified userId.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.server.accessibility;
|
||||||
|
|
||||||
|
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
||||||
|
|
||||||
|
import android.annotation.UserIdInt;
|
||||||
|
import android.app.admin.DevicePolicyManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.UserInfo;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.os.UserManager;
|
||||||
|
|
||||||
|
import com.android.settingslib.RestrictedLockUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class to host methods usable to return {@link EnforcedAdmin} instances based on device
|
||||||
|
* admin policy.
|
||||||
|
*/
|
||||||
|
public class RestrictedLockUtilsInternal {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables accessibility service that are not permitted.
|
||||||
|
*/
|
||||||
|
public static EnforcedAdmin checkIfAccessibilityServiceDisallowed(Context context,
|
||||||
|
String packageName, int userId) {
|
||||||
|
final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
|
||||||
|
if (dpm == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final EnforcedAdmin admin =
|
||||||
|
RestrictedLockUtils.getProfileOrDeviceOwner(context, getUserHandleOf(userId));
|
||||||
|
boolean permitted = true;
|
||||||
|
if (admin != null) {
|
||||||
|
permitted = dpm.isAccessibilityServicePermittedByAdmin(admin.component,
|
||||||
|
packageName, userId);
|
||||||
|
}
|
||||||
|
int managedProfileId = getManagedProfileId(context, userId);
|
||||||
|
final EnforcedAdmin profileAdmin = RestrictedLockUtils.getProfileOrDeviceOwner(context,
|
||||||
|
getUserHandleOf(managedProfileId));
|
||||||
|
boolean permittedByProfileAdmin = true;
|
||||||
|
if (profileAdmin != null) {
|
||||||
|
permittedByProfileAdmin = dpm.isAccessibilityServicePermittedByAdmin(
|
||||||
|
profileAdmin.component, packageName, managedProfileId);
|
||||||
|
}
|
||||||
|
if (!permitted && !permittedByProfileAdmin) {
|
||||||
|
return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
|
||||||
|
} else if (!permitted) {
|
||||||
|
return admin;
|
||||||
|
} else if (!permittedByProfileAdmin) {
|
||||||
|
return profileAdmin;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables input method that are not permitted.
|
||||||
|
*/
|
||||||
|
public static EnforcedAdmin checkIfInputMethodDisallowed(Context context, String packageName,
|
||||||
|
int userId) {
|
||||||
|
final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
|
||||||
|
if (dpm == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final EnforcedAdmin admin =
|
||||||
|
RestrictedLockUtils.getProfileOrDeviceOwner(context, getUserHandleOf(userId));
|
||||||
|
boolean permitted = true;
|
||||||
|
if (admin != null) {
|
||||||
|
permitted = dpm.isInputMethodPermittedByAdmin(admin.component,
|
||||||
|
packageName, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean permittedByParentAdmin = true;
|
||||||
|
EnforcedAdmin profileAdmin = null;
|
||||||
|
int managedProfileId = getManagedProfileId(context, userId);
|
||||||
|
if (managedProfileId != UserHandle.USER_NULL) {
|
||||||
|
profileAdmin = RestrictedLockUtils.getProfileOrDeviceOwner(
|
||||||
|
context, getUserHandleOf(managedProfileId));
|
||||||
|
// If the device is an organization-owned device with a managed profile, the
|
||||||
|
// managedProfileId will be used instead of the affected userId. This is because
|
||||||
|
// isInputMethodPermittedByAdmin is called on the parent DPM instance, which will
|
||||||
|
// return results affecting the personal profile.
|
||||||
|
if (profileAdmin != null && dpm.isOrganizationOwnedDeviceWithManagedProfile()) {
|
||||||
|
final DevicePolicyManager parentDpm = dpm.getParentProfileInstance(
|
||||||
|
UserManager.get(context).getUserInfo(managedProfileId));
|
||||||
|
permittedByParentAdmin = parentDpm.isInputMethodPermittedByAdmin(
|
||||||
|
profileAdmin.component, packageName, managedProfileId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!permitted && !permittedByParentAdmin) {
|
||||||
|
return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
|
||||||
|
} else if (!permitted) {
|
||||||
|
return admin;
|
||||||
|
} else if (!permittedByParentAdmin) {
|
||||||
|
return profileAdmin;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getManagedProfileId(Context context, int userId) {
|
||||||
|
final UserManager um = context.getSystemService(UserManager.class);
|
||||||
|
final List<UserInfo> userProfiles = um.getProfiles(userId);
|
||||||
|
for (UserInfo uInfo : userProfiles) {
|
||||||
|
if (uInfo.id == userId) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (uInfo.isManagedProfile()) {
|
||||||
|
return uInfo.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return UserHandle.USER_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the UserHandle for a userId. Return null for USER_NULL
|
||||||
|
*/
|
||||||
|
private static UserHandle getUserHandleOf(@UserIdInt int userId) {
|
||||||
|
if (userId == UserHandle.USER_NULL) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return UserHandle.of(userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user