phonewm: refactor Talkback shortcut logic into a controller

Wrap talkback shortcut related logic into one
`TalkbackShortcutController` class. This makes it easier to test
triple press logic since its using talkback shortcut.

Note: this is pure refactor. No behavioral change.

Test: atest WmTests:StemKeyGestureTests
BUG: 311006348
Change-Id: Icbd83958406e30a7bf85e5352bcbdfdcb68d8eaa
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4e5dc1d..fed32e5 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.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.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.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 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 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 @@
     /** 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 @@
                 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 @@
     }
 
     /**
-     * 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 @@
             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 @@
         ButtonOverridePermissionChecker getButtonOverridePermissionChecker() {
             return new ButtonOverridePermissionChecker();
         }
+
+        TalkbackShortcutController getTalkbackShortcutController() {
+            return new TalkbackShortcutController(mContext);
+        }
     }
 
     /** {@inheritDoc} */
@@ -2515,6 +2446,7 @@
         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 0000000..906da2f
--- /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 d057226..48d3503 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 @@
     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 @@
         PhoneWindowManager.ButtonOverridePermissionChecker getButtonOverridePermissionChecker() {
             return mButtonOverridePermissionChecker;
         }
+
+        TalkbackShortcutController getTalkbackShortcutController() {
+            return new TestTalkbackShortcutController(mContext);
+        }
     }
 
     TestPhoneWindowManager(Context context, boolean supportSettingsUpdate) {