diff options
3 files changed, 154 insertions, 81 deletions
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 4e5dc1dd76fa..fed32e59c846 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -76,7 +76,6 @@ import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;  import static android.view.contentprotection.flags.Flags.createAccessibilityOverlayAppOpEnabled;  import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY; -import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_WEAR_TRIPLE_PRESS_GESTURE;  import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;  import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;  import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED; @@ -97,7 +96,6 @@ import static com.android.server.wm.WindowManagerPolicyProto.SCREEN_ON_FULLY;  import static com.android.server.wm.WindowManagerPolicyProto.WINDOW_MANAGER_DRAW_COMPLETE;  import android.accessibilityservice.AccessibilityService; -import android.accessibilityservice.AccessibilityServiceInfo;  import android.annotation.NonNull;  import android.annotation.Nullable;  import android.annotation.SuppressLint; @@ -124,7 +122,6 @@ import android.content.pm.ActivityInfo;  import android.content.pm.ApplicationInfo;  import android.content.pm.PackageManager;  import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo;  import android.content.res.Configuration;  import android.content.res.Resources;  import android.database.ContentObserver; @@ -204,8 +201,6 @@ import android.widget.Toast;  import com.android.internal.R;  import com.android.internal.accessibility.AccessibilityShortcutController; -import com.android.internal.accessibility.util.AccessibilityStatsLogUtils; -import com.android.internal.accessibility.util.AccessibilityUtils;  import com.android.internal.annotations.VisibleForTesting;  import com.android.internal.app.AssistUtils;  import com.android.internal.display.BrightnessUtils; @@ -385,8 +380,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {      public static final String TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD = "waitForAllWindowsDrawn"; -    private static final String TALKBACK_LABEL = "TalkBack"; -      private static final int POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS = 800;      /** @@ -477,6 +470,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {      /** Controller that supports enabling an AccessibilityService by holding down the volume keys */      private AccessibilityShortcutController mAccessibilityShortcutController; +    private TalkbackShortcutController mTalkbackShortcutController; +      boolean mSafeMode;      // Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key. @@ -1602,19 +1597,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {                  if (DEBUG_INPUT) {                      Slog.d(TAG, "Executing stem primary triple press action behavior.");                  } - -                if (Settings.System.getIntForUser(mContext.getContentResolver(), -                        Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED, -                        /* def= */ 0, UserHandle.USER_CURRENT) == 1) { -                    /** Toggle talkback begin */ -                    ComponentName componentName = getTalkbackComponent(); -                    if (componentName != null && toggleTalkBack(componentName)) { -                        /** log stem triple press telemetry if it's a talkback enabled event */ -                        logStemTriplePressAccessibilityTelemetry(componentName); -                    } -                    performHapticFeedback(HapticFeedbackConstants.CONFIRM, /* always = */ false, -                        /* reason = */ "Stem primary - Triple Press - Toggle Accessibility"); -                    /** Toggle talkback end */ +                mTalkbackShortcutController.toggleTalkback(mCurrentUserId); +                if (mTalkbackShortcutController.isTalkBackShortcutGestureEnabled()) { +                    performHapticFeedback(HapticFeedbackConstants.CONFIRM, /* always = */ +                            false, /* reason = */ +                            "Stem primary - Triple Press - Toggle Accessibility");                  }                  break;          } @@ -1640,61 +1627,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {      }      /** -     * A function that toggles talkback service -     * -     * @return {@code true} if talkback is enabled, {@code false} if talkback is disabled -     */ -    private boolean toggleTalkBack(ComponentName componentName) { -        final Set<ComponentName> enabledServices = -                AccessibilityUtils.getEnabledServicesFromSettings(mContext, mCurrentUserId); - -        boolean isTalkbackAlreadyEnabled = enabledServices.contains(componentName); -        AccessibilityUtils.setAccessibilityServiceState(mContext, componentName, -                !isTalkbackAlreadyEnabled); -        /** if isTalkbackAlreadyEnabled is true, then it's a disabled event so return false -         * and if isTalkbackAlreadyEnabled is false, return true as it's an enabled event */ -        return !isTalkbackAlreadyEnabled; -    } - -    /** -     * A function that logs stem triple press accessibility telemetry -     * If the user setup (Oobe) is not completed, set the -     * WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE -     * setting which will be later logged via Settings Snapshot -     * else, log ACCESSIBILITY_SHORTCUT_REPORTED atom -     */ -    private void logStemTriplePressAccessibilityTelemetry(ComponentName componentName) { -        if (!AccessibilityUtils.isUserSetupCompleted(mContext)) { -            Settings.Secure.putInt(mContext.getContentResolver(), -                    Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE, 1); -        } else { -            AccessibilityStatsLogUtils.logAccessibilityShortcutActivated(mContext, componentName, -                    ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_WEAR_TRIPLE_PRESS_GESTURE, -                    /* serviceEnabled= */ true); -        } -    } - -    private ComponentName getTalkbackComponent() { -        AccessibilityManager accessibilityManager = mContext.getSystemService( -                AccessibilityManager.class); -        List<AccessibilityServiceInfo> serviceInfos = -                accessibilityManager.getInstalledAccessibilityServiceList(); - -        for (AccessibilityServiceInfo service : serviceInfos) { -            final ServiceInfo serviceInfo = service.getResolveInfo().serviceInfo; -            if (isTalkback(serviceInfo)) { -                return new ComponentName(serviceInfo.packageName, serviceInfo.name); -            } -        } -        return null; -    } - -    private boolean isTalkback(ServiceInfo info) { -        String label = info.loadLabel(mPackageManager).toString(); -        return label.equals(TALKBACK_LABEL); -    } - -    /**       * Load most recent task (expect current task) and bring it to the front.       */      void performStemPrimaryDoublePressSwitchToRecentTask() { @@ -1731,12 +1663,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {              case TRIPLE_PRESS_PRIMARY_NOTHING:                  break;              case TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY: -                if (Settings.System.getIntForUser( -                                mContext.getContentResolver(), -                                Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED, -                                /* def= */ 0, -                                UserHandle.USER_CURRENT) -                        == 1) { +                if (mTalkbackShortcutController.isTalkBackShortcutGestureEnabled()) {                      return 3;                  }                  break; @@ -2252,6 +2179,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {          ButtonOverridePermissionChecker getButtonOverridePermissionChecker() {              return new ButtonOverridePermissionChecker();          } + +        TalkbackShortcutController getTalkbackShortcutController() { +            return new TalkbackShortcutController(mContext); +        }      }      /** {@inheritDoc} */ @@ -2515,6 +2446,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {          mKeyguardDrawnTimeout = mContext.getResources().getInteger(                  com.android.internal.R.integer.config_keyguardDrawnTimeout);          mKeyguardDelegate = injector.getKeyguardServiceDelegate(); +        mTalkbackShortcutController = injector.getTalkbackShortcutController();          initKeyCombinationRules();          initSingleKeyGestureRules(injector.getLooper());          mButtonOverridePermissionChecker = injector.getButtonOverridePermissionChecker(); diff --git a/services/core/java/com/android/server/policy/TalkbackShortcutController.java b/services/core/java/com/android/server/policy/TalkbackShortcutController.java new file mode 100644 index 000000000000..906da2f4cdf5 --- /dev/null +++ b/services/core/java/com/android/server/policy/TalkbackShortcutController.java @@ -0,0 +1,118 @@ +/* + * 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.policy; + +import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_WEAR_TRIPLE_PRESS_GESTURE; + +import android.accessibilityservice.AccessibilityServiceInfo; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.os.UserHandle; +import android.provider.Settings; +import android.view.accessibility.AccessibilityManager; + +import com.android.internal.accessibility.util.AccessibilityStatsLogUtils; +import com.android.internal.accessibility.util.AccessibilityUtils; +import com.android.internal.annotations.VisibleForTesting; + +import java.util.List; +import java.util.Set; + +/** + * This class controls talkback shortcut related operations such as toggling, quering and + * logging. + */ +@VisibleForTesting +class TalkbackShortcutController { +    private static final String TALKBACK_LABEL = "TalkBack"; +    private final Context mContext; +    private final PackageManager mPackageManager; + +    TalkbackShortcutController(Context context) { +        mContext = context; +        mPackageManager = mContext.getPackageManager(); +    } + +    /** +     * A function that toggles talkback service. +     * +     * @return talkback state after toggle. {@code true} if talkback is enabled, {@code false} if +     * talkback is disabled +     */ +    boolean toggleTalkback(int userId) { +        final Set<ComponentName> enabledServices = +                AccessibilityUtils.getEnabledServicesFromSettings(mContext, userId); +        ComponentName componentName = getTalkbackComponent(); +        boolean isTalkbackAlreadyEnabled = enabledServices.contains(componentName); + +        if (isTalkBackShortcutGestureEnabled()) { +            isTalkbackAlreadyEnabled = !isTalkbackAlreadyEnabled; +            AccessibilityUtils.setAccessibilityServiceState(mContext, componentName, +                    isTalkbackAlreadyEnabled); + +            // log stem triple press telemetry if it's a talkback enabled event. +            if (componentName != null && isTalkbackAlreadyEnabled) { +                logStemTriplePressAccessibilityTelemetry(componentName); +            } +        } +        return isTalkbackAlreadyEnabled; +    } + +    private ComponentName getTalkbackComponent() { +        AccessibilityManager accessibilityManager = mContext.getSystemService( +                AccessibilityManager.class); +        List<AccessibilityServiceInfo> serviceInfos = +                accessibilityManager.getInstalledAccessibilityServiceList(); + +        for (AccessibilityServiceInfo service : serviceInfos) { +            final ServiceInfo serviceInfo = service.getResolveInfo().serviceInfo; +            if (isTalkback(serviceInfo)) { +                return new ComponentName(serviceInfo.packageName, serviceInfo.name); +            } +        } +        return null; +    } + +    boolean isTalkBackShortcutGestureEnabled() { +        return Settings.System.getIntForUser(mContext.getContentResolver(), +                Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED, +                /* def= */ 0, UserHandle.USER_CURRENT) == 1; +    } + +    /** +     * A function that logs stem triple press accessibility telemetry. If the user setup (Oobe) +     * is not completed, set the WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE setting which +     * will be later logged via Settings Snapshot  else, log ACCESSIBILITY_SHORTCUT_REPORTED atom +     */ +    private void logStemTriplePressAccessibilityTelemetry(ComponentName componentName) { +        if (!AccessibilityUtils.isUserSetupCompleted(mContext)) { +            Settings.Secure.putInt(mContext.getContentResolver(), +                    Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE, 1); +            return; +        } +        AccessibilityStatsLogUtils.logAccessibilityShortcutActivated(mContext, +                componentName, +                ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_WEAR_TRIPLE_PRESS_GESTURE, +                /* serviceEnabled= */ true); +    } + +    private boolean isTalkback(ServiceInfo info) { +        return TALKBACK_LABEL.equals(info.loadLabel(mPackageManager).toString()); +    } +} diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java index d057226836a3..48d3503fe43b 100644 --- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java +++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java @@ -172,6 +172,25 @@ class TestPhoneWindowManager {      private HandlerThread mHandlerThread;      private Handler mHandler; +    private boolean mIsTalkBackEnabled; + +    class TestTalkbackShortcutController extends TalkbackShortcutController { +        TestTalkbackShortcutController(Context context) { +            super(context); +        } + +        @Override +        boolean toggleTalkback(int currentUserId) { +            mIsTalkBackEnabled = !mIsTalkBackEnabled; +            return mIsTalkBackEnabled; +        } + +        @Override +        boolean isTalkBackShortcutGestureEnabled() { +            return true; +        } +    } +      private class TestInjector extends PhoneWindowManager.Injector {          TestInjector(Context context, WindowManagerPolicy.WindowManagerFuncs funcs) {              super(context, funcs, mTestLooper.getLooper()); @@ -197,6 +216,10 @@ class TestPhoneWindowManager {          PhoneWindowManager.ButtonOverridePermissionChecker getButtonOverridePermissionChecker() {              return mButtonOverridePermissionChecker;          } + +        TalkbackShortcutController getTalkbackShortcutController() { +            return new TestTalkbackShortcutController(mContext); +        }      }      TestPhoneWindowManager(Context context, boolean supportSettingsUpdate) {  |