diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java index be66d0c238cc6..891d53527f961 100644 --- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java @@ -73,8 +73,11 @@ public class AccessibilityShortcutController { new ComponentName("com.android.server.accessibility", "ColorInversion"); public static final ComponentName DALTONIZER_COMPONENT_NAME = new ComponentName("com.android.server.accessibility", "Daltonizer"); + // TODO(b/147990389): Use MAGNIFICATION_COMPONENT_NAME to replace. public static final String MAGNIFICATION_CONTROLLER_NAME = "com.android.server.accessibility.MagnificationController"; + public static final ComponentName MAGNIFICATION_COMPONENT_NAME = + new ComponentName("com.android.server.accessibility", "Magnification"); private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityButtonChooserActivity.java index 7c4df52d14950..7eb09e59601b0 100644 --- a/core/java/com/android/internal/accessibility/dialog/AccessibilityButtonChooserActivity.java +++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityButtonChooserActivity.java @@ -19,10 +19,14 @@ package com.android.internal.accessibility.dialog; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; +import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME; +import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets; +import static com.android.internal.accessibility.util.AccessibilityStatsLogUtils.logAccessibilityButtonLongPressStatus; import android.annotation.Nullable; import android.app.Activity; +import android.content.ComponentName; import android.os.Bundle; import android.provider.Settings; import android.text.TextUtils; @@ -87,6 +91,12 @@ public class AccessibilityButtonChooserActivity extends Activity { gridview.setAdapter(new ButtonTargetAdapter(mTargets)); gridview.setOnItemClickListener((parent, view, position, id) -> { final String key = Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT; + String name = mTargets.get(position).getId(); + if (name.equals(MAGNIFICATION_CONTROLLER_NAME)) { + name = MAGNIFICATION_COMPONENT_NAME.flattenToString(); + } + final ComponentName componentName = ComponentName.unflattenFromString(name); + logAccessibilityButtonLongPressStatus(componentName); Settings.Secure.putString(getContentResolver(), key, mTargets.get(position).getId()); finish(); }); diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java new file mode 100644 index 0000000000000..c2e1426bc4fec --- /dev/null +++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2020 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.internal.accessibility.util; + +import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; +import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; + +import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME; +import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__DISABLED; +import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__ENABLED; +import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__UNKNOWN; +import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON; +import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON_LONG_PRESS; +import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TRIPLE_TAP; +import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE; +import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY; + +import android.content.ComponentName; +import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityManager.ShortcutType; + +import com.android.internal.util.FrameworkStatsLog; + +/** Methods for logging accessibility states. */ +public final class AccessibilityStatsLogUtils { + private static final int UNKNOWN_STATUS = + ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__UNKNOWN; + + private AccessibilityStatsLogUtils() {} + + /** + * Logs accessibility feature name that is assigned to the shortcut also its shortcut type. + * Calls this when clicking the shortcut {@link AccessibilityManager#ACCESSIBILITY_BUTTON} or + * {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY} + * + * @param componentName component name of the accessibility feature + * @param shortcutType accessibility shortcut type {@link ShortcutType} + */ + public static void logAccessibilityShortcutActivated(ComponentName componentName, + @ShortcutType int shortcutType) { + logAccessibilityShortcutActivated(componentName, shortcutType, UNKNOWN_STATUS); + } + + /** + * Logs accessibility feature name that is assigned to the shortcut also its shortcut type and + * enabled status. Calls this when clicking the shortcut + * {@link AccessibilityManager#ACCESSIBILITY_BUTTON} + * or {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY} + * + * @param componentName component name of the accessibility feature + * @param shortcutType accessibility shortcut type + * @param serviceEnabled {@code true} if the service is enabled + */ + public static void logAccessibilityShortcutActivated(ComponentName componentName, + @ShortcutType int shortcutType, boolean serviceEnabled) { + logAccessibilityShortcutActivated(componentName, shortcutType, + convertToLoggingServiceStatus(serviceEnabled)); + } + + /** + * Logs accessibility feature name that is assigned to the shortcut also its shortcut type and + * status code. Calls this when clicking the shortcut + * {@link AccessibilityManager#ACCESSIBILITY_BUTTON} + * or {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY} + * + * @param componentName component name of the accessibility feature + * @param shortcutType accessibility shortcut type {@link ShortcutType} + * @param serviceStatus The service status code. 0 denotes unknown_status, 1 denotes enabled, 2 + * denotes disabled. + */ + private static void logAccessibilityShortcutActivated(ComponentName componentName, + @ShortcutType int shortcutType, int serviceStatus) { + FrameworkStatsLog.write(FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED, + componentName.flattenToString(), convertToLoggingShortcutType(shortcutType), + serviceStatus); + } + + /** + * Logs magnification that is assigned to the triple tap shortcut. Calls this when triggering + * the magnification triple tap shortcut. + */ + public static void logMagnificationTripleTap(boolean enabled) { + FrameworkStatsLog.write(FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED, + MAGNIFICATION_COMPONENT_NAME.flattenToString(), + ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TRIPLE_TAP, + convertToLoggingServiceStatus(enabled)); + } + + /** + * Logs accessibility feature name that is assigned to the long pressed accessibility button + * shortcut. Calls this when clicking the long pressed accessibility button shortcut. + * + * @param componentName The component name of the accessibility feature. + */ + public static void logAccessibilityButtonLongPressStatus(ComponentName componentName) { + FrameworkStatsLog.write(FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED, + componentName.flattenToString(), + ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON_LONG_PRESS, + UNKNOWN_STATUS); + } + + private static int convertToLoggingShortcutType(@ShortcutType int shortcutType) { + switch (shortcutType) { + case ACCESSIBILITY_BUTTON: + return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON; + case ACCESSIBILITY_SHORTCUT_KEY: + return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY; + } + return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE; + } + + private static int convertToLoggingServiceStatus(boolean enabled) { + return enabled ? ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__ENABLED + : ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__DISABLED; + } +} diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 02ab60b05bca4..6d848d17eb70a 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -20,8 +20,10 @@ import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTT import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; import static android.view.accessibility.AccessibilityManager.ShortcutType; +import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME; import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; +import static com.android.internal.accessibility.util.AccessibilityStatsLogUtils.logAccessibilityShortcutActivated; import static com.android.internal.util.FunctionalUtils.ignoreRemoteException; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import static com.android.server.accessibility.AccessibilityUserState.doesShortcutTargetsStringContain; @@ -2422,6 +2424,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } // In case user assigned magnification to the given shortcut. if (targetName.equals(MAGNIFICATION_CONTROLLER_NAME)) { + final boolean enabled = !getMagnificationController().isMagnifying(displayId); + logAccessibilityShortcutActivated(MAGNIFICATION_COMPONENT_NAME, shortcutType, enabled); sendAccessibilityButtonToInputFilter(displayId); return; } @@ -2431,11 +2435,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return; } // In case user assigned an accessibility framework feature to the given shortcut. - if (performAccessibilityFrameworkFeature(targetComponentName)) { + if (performAccessibilityFrameworkFeature(targetComponentName, shortcutType)) { return; } // In case user assigned an accessibility shortcut target to the given shortcut. if (performAccessibilityShortcutTargetActivity(displayId, targetComponentName)) { + logAccessibilityShortcutActivated(targetComponentName, shortcutType); return; } // in case user assigned an accessibility service to the given shortcut. @@ -2445,7 +2450,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private boolean performAccessibilityFrameworkFeature(ComponentName assignedTarget) { + private boolean performAccessibilityFrameworkFeature(ComponentName assignedTarget, + @ShortcutType int shortcutType) { final Map frameworkFeatureMap = AccessibilityShortcutController.getFrameworkShortcutFeaturesMap(); if (!frameworkFeatureMap.containsKey(assignedTarget)) { @@ -2457,8 +2463,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub featureInfo.getSettingKey(), mCurrentUserId); // Assuming that the default state will be to have the feature off if (!TextUtils.equals(featureInfo.getSettingOnValue(), setting.read())) { + logAccessibilityShortcutActivated(assignedTarget, shortcutType, /* serviceEnabled= */ + true); setting.write(featureInfo.getSettingOnValue()); } else { + logAccessibilityShortcutActivated(assignedTarget, shortcutType, /* serviceEnabled= */ + false); setting.write(featureInfo.getSettingOffValue()); } return true; @@ -2520,8 +2530,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if ((targetSdk <= Build.VERSION_CODES.Q && shortcutType == ACCESSIBILITY_SHORTCUT_KEY) || (targetSdk > Build.VERSION_CODES.Q && !requestA11yButton)) { if (serviceConnection == null) { + logAccessibilityShortcutActivated(assignedTarget, + shortcutType, /* serviceEnabled= */ true); enableAccessibilityServiceLocked(assignedTarget, mCurrentUserId); + } else { + logAccessibilityShortcutActivated(assignedTarget, + shortcutType, /* serviceEnabled= */ false); disableAccessibilityServiceLocked(assignedTarget, mCurrentUserId); } return true; @@ -2541,6 +2556,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub + assignedTarget); return false; } + // ServiceConnection means service enabled. + logAccessibilityShortcutActivated(assignedTarget, shortcutType, /* serviceEnabled= */ + true); serviceConnection.notifyAccessibilityButtonClickedLocked(displayId); return true; } diff --git a/services/accessibility/java/com/android/server/accessibility/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/FullScreenMagnificationGestureHandler.java index 9e4fd80c9e6ba..afe6238ca38f1 100644 --- a/services/accessibility/java/com/android/server/accessibility/FullScreenMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/FullScreenMagnificationGestureHandler.java @@ -24,6 +24,7 @@ import static android.view.MotionEvent.ACTION_POINTER_DOWN; import static android.view.MotionEvent.ACTION_POINTER_UP; import static android.view.MotionEvent.ACTION_UP; +import static com.android.internal.accessibility.util.AccessibilityStatsLogUtils.logMagnificationTripleTap; import static com.android.server.accessibility.gestures.GestureUtils.distance; import static java.lang.Math.abs; @@ -759,10 +760,17 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler // Shortcut acts as the 2 initial taps if (mShortcutTriggered) return tapCount() + 2 >= numTaps; - return mDetectTripleTap + final boolean multitapTriggered = mDetectTripleTap && tapCount() >= numTaps && isMultiTap(mPreLastDown, mLastDown) && isMultiTap(mPreLastUp, mLastUp); + + // Only log the triple tap event, use numTaps to filter. + if (multitapTriggered && numTaps > 2) { + final boolean enabled = mMagnificationController.isMagnifying(mDisplayId); + logMagnificationTripleTap(enabled); + } + return multitapTriggered; } private boolean isMultiTap(MotionEvent first, MotionEvent second) { @@ -885,7 +893,6 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler } private void onTripleTap(MotionEvent up) { - if (DEBUG_DETECTING) { Slog.i(LOG_TAG, "onTripleTap(); delayed: " + MotionEventInfo.toString(mDelayedEventQueue)); @@ -908,6 +915,10 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler mViewportDraggingState.mZoomedInBeforeDrag = mMagnificationController.isMagnifying(mDisplayId); + // Triple tap and hold also belongs to triple tap event. + final boolean enabled = !mViewportDraggingState.mZoomedInBeforeDrag; + logMagnificationTripleTap(enabled); + zoomOn(down.getX(), down.getY()); transitionTo(mViewportDraggingState);