From e19cdef47561b16dedb572425eb4496b1d1c62ef Mon Sep 17 00:00:00 2001 From: Jacky Kao Date: Wed, 13 Jan 2021 11:03:03 +0800 Subject: [PATCH] Logs magnification feature behavior. (1/2) Uses the atom MagnificationUsageReported in westworld to log the activated mode and its duration of the magnification. The timing of logging the MagnificationUsageReported is when the magnification feature is deactivated. Bug: 154021596 Test: a11y CTS & unit tests Test: make statsd_testdrive && ./out/host/linux-x86/bin/statsd_testdrive 345 Change-Id: I708e9731da23658774e462968a6b9df648b0ed19 --- .../util/AccessibilityStatsLogUtils.java | 34 +++++++++++++ .../FullScreenMagnificationController.java | 17 +++++++ .../MagnificationController.java | 49 +++++++++++++++++-- .../WindowMagnificationManager.java | 38 +++++++++++--- ...FullScreenMagnificationControllerTest.java | 24 +++++++++ .../MagnificationControllerTest.java | 25 ++++++++++ .../WindowMagnificationManagerTest.java | 17 +++++++ 7 files changed, 194 insertions(+), 10 deletions(-) diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java index c2e1426bc4fec..7baa53bcd56d2 100644 --- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java +++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java @@ -16,6 +16,9 @@ package com.android.internal.accessibility.util; +import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL; +import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; +import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; @@ -28,6 +31,10 @@ import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT 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 static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_ALL; +import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_FULL_SCREEN; +import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_UNKNOWN_MODE; +import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_WINDOW; import android.content.ComponentName; import android.view.accessibility.AccessibilityManager; @@ -113,6 +120,19 @@ public final class AccessibilityStatsLogUtils { UNKNOWN_STATUS); } + /** + * Logs the magnification activated mode and its duration of the usage. + * Calls this when the magnification is disabled. + * + * @param mode The activated magnification mode. + * @param duration The duration in milliseconds during the magnification is activated. + */ + public static void logMagnificationUsageState(int mode, long duration) { + FrameworkStatsLog.write(FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED, + convertToLoggingMagnificationMode(mode), + duration); + } + private static int convertToLoggingShortcutType(@ShortcutType int shortcutType) { switch (shortcutType) { case ACCESSIBILITY_BUTTON: @@ -127,4 +147,18 @@ public final class AccessibilityStatsLogUtils { return enabled ? ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__ENABLED : ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__DISABLED; } + + private static int convertToLoggingMagnificationMode(int mode) { + switch (mode) { + case ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN: + return MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_FULL_SCREEN; + case ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW: + return MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_WINDOW; + case ACCESSIBILITY_MAGNIFICATION_MODE_ALL: + return MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_ALL; + + default: + return MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_UNKNOWN_MODE; + } + } } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java index c7f0efa18568d..3d07da5fbb303 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java @@ -118,6 +118,7 @@ public class FullScreenMagnificationController { private static final int INVALID_ID = -1; private int mIdOfLastServiceToMagnify = INVALID_ID; + private boolean mMagnificationActivated = false; DisplayMagnification(int displayId) { mDisplayId = displayId; @@ -322,6 +323,13 @@ public class FullScreenMagnificationController { mSpecAnimationBridge, spec, animationCallback); mControllerCtx.getHandler().sendMessage(m); } + + final boolean lastMagnificationActivated = mMagnificationActivated; + mMagnificationActivated = spec.scale > 1.0f; + if (mMagnificationActivated != lastMagnificationActivated) { + mMagnificationRequestObserver.onFullScreenMagnificationActivationState( + mMagnificationActivated); + } } /** @@ -1506,5 +1514,14 @@ public class FullScreenMagnificationController { * @param serviceId the ID of the service requesting the change */ void onRequestMagnificationSpec(int displayId, int serviceId); + + /** + * Called when the state of the magnification activation is changed. + * It is for the logging data of the magnification activation state. + * + * @param activated {@code true} if the magnification is activated, otherwise {@code false}. + */ + @GuardedBy("mLock") + void onFullScreenMagnificationActivationState(boolean activated); } } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java index 2a65b64231581..17a7d393f3692 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java @@ -16,6 +16,7 @@ package com.android.server.accessibility.magnification; +import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; @@ -25,11 +26,13 @@ import android.content.Context; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.Region; +import android.os.SystemClock; import android.provider.Settings; import android.util.Slog; import android.util.SparseArray; import android.view.accessibility.MagnificationAnimationCallback; +import com.android.internal.accessibility.util.AccessibilityStatsLogUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.server.accessibility.AccessibilityManagerService; @@ -51,7 +54,8 @@ import com.android.server.accessibility.AccessibilityManagerService; * */ public class MagnificationController implements WindowMagnificationManager.Callback, - MagnificationGestureHandler.Callback { + MagnificationGestureHandler.Callback, + FullScreenMagnificationController.MagnificationRequestObserver { private static final boolean DEBUG = false; private static final String TAG = "MagnificationController"; @@ -66,6 +70,9 @@ public class MagnificationController implements WindowMagnificationManager.Callb private WindowMagnificationManager mWindowMagnificationMgr; private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; + private long mWindowModeEnabledTime = 0; + private long mFullScreenModeEnabledTime = 0; + /** * A callback to inform the magnification transition result. */ @@ -187,7 +194,8 @@ public class MagnificationController implements WindowMagnificationManager.Callb setDisableMagnificationCallbackLocked(displayId, animationEndCallback); } - void onRequestMagnificationSpec(int displayId, int serviceId) { + @Override + public void onRequestMagnificationSpec(int displayId, int serviceId) { synchronized (mLock) { if (serviceId == AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID) { return; @@ -200,6 +208,39 @@ public class MagnificationController implements WindowMagnificationManager.Callb } } + // TODO : supporting multi-display (b/182227245). + @Override + public void onWindowMagnificationActivationState(boolean activated) { + if (activated) { + mWindowModeEnabledTime = SystemClock.uptimeMillis(); + } else { + logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, + SystemClock.uptimeMillis() - mWindowModeEnabledTime); + } + } + + @Override + public void onFullScreenMagnificationActivationState(boolean activated) { + if (activated) { + mFullScreenModeEnabledTime = SystemClock.uptimeMillis(); + } else { + logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, + SystemClock.uptimeMillis() - mFullScreenModeEnabledTime); + } + } + + /** + * Wrapper method of logging the magnification activated mode and its duration of the usage + * when the magnification is disabled. + * + * @param mode The activated magnification mode. + * @param duration The duration in milliseconds during the magnification is activated. + */ + @VisibleForTesting + public void logMagnificationUsageState(int mode, long duration) { + AccessibilityStatsLogUtils.logMagnificationUsageState(mode, duration); + } + /** * Updates the active user ID of {@link FullScreenMagnificationController} and {@link * WindowMagnificationManager}. @@ -260,7 +301,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb synchronized (mLock) { if (mFullScreenMagnificationController == null) { mFullScreenMagnificationController = new FullScreenMagnificationController(mContext, - mAms, mLock, this::onRequestMagnificationSpec); + mAms, mLock, this); mFullScreenMagnificationController.setUserId(mAms.getCurrentUserIdLocked()); } } @@ -340,7 +381,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb mTransitionCallBack = transitionCallBack; mDisplayId = displayId; mTargetMode = targetMode; - mCurrentMode = mTargetMode ^ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL; + mCurrentMode = mTargetMode ^ ACCESSIBILITY_MAGNIFICATION_MODE_ALL; mCurrentScale = scale; mCurrentCenter.set(currentCenter); } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java index 40668d8df568b..ded601e70ff12 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java @@ -93,6 +93,13 @@ public class WindowMagnificationManager implements * @param scale the target scale, or {@link Float#NaN} to leave unchanged */ void onPerformScaleAction(int displayId, float scale); + + /** + * Called when the state of the magnification activation is changed. + * + * @param activated {@code true} if the magnification is activated, otherwise {@code false}. + */ + void onWindowMagnificationActivationState(boolean activated); } private final Callback mCallback; @@ -264,6 +271,7 @@ public class WindowMagnificationManager implements */ void enableWindowMagnification(int displayId, float scale, float centerX, float centerY, @Nullable MagnificationAnimationCallback animationCallback) { + final boolean enabled; synchronized (mLock) { if (mConnectionWrapper == null) { return; @@ -272,9 +280,13 @@ public class WindowMagnificationManager implements if (magnifier == null) { magnifier = createWindowMagnifier(displayId); } - magnifier.enableWindowMagnificationInternal(scale, centerX, centerY, + enabled = magnifier.enableWindowMagnificationInternal(scale, centerX, centerY, animationCallback); } + + if (enabled) { + mCallback.onWindowMagnificationActivationState(true); + } } /** @@ -296,16 +308,21 @@ public class WindowMagnificationManager implements */ void disableWindowMagnification(int displayId, boolean clear, MagnificationAnimationCallback animationCallback) { + final boolean disabled; synchronized (mLock) { WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); if (magnifier == null || mConnectionWrapper == null) { return; } - magnifier.disableWindowMagnificationInternal(animationCallback); + disabled = magnifier.disableWindowMagnificationInternal(animationCallback); if (clear) { mWindowMagnifiers.delete(displayId); } } + + if (disabled) { + mCallback.onWindowMagnificationActivationState(false); + } } /** @@ -560,26 +577,35 @@ public class WindowMagnificationManager implements } @GuardedBy("mLock") - void enableWindowMagnificationInternal(float scale, float centerX, float centerY, + boolean enableWindowMagnificationInternal(float scale, float centerX, float centerY, @Nullable MagnificationAnimationCallback animationCallback) { if (mEnabled) { - return; + return false; } final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE); if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale, centerX, centerY, animationCallback)) { mScale = normScale; mEnabled = true; + + return true; } + return false; } @GuardedBy("mLock") - void disableWindowMagnificationInternal( + boolean disableWindowMagnificationInternal( @Nullable MagnificationAnimationCallback animationResultCallback) { - if (mEnabled && mWindowMagnificationManager.disableWindowMagnificationInternal( + if (!mEnabled) { + return false; + } + if (mWindowMagnificationManager.disableWindowMagnificationInternal( mDisplayId, animationResultCallback)) { mEnabled = false; + + return true; } + return false; } @GuardedBy("mLock") diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java index 44b9f44fc8431..872b955486550 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java @@ -1114,6 +1114,30 @@ public class FullScreenMagnificationControllerTest { argThat(closeTo(newEndSpec))); } + @Test + public void testSetScale_toMagnifying_shouldNotifyActivatedState() { + setScaleToMagnifying(); + + verify(mRequestObserver).onFullScreenMagnificationActivationState(eq(true)); + } + + @Test + public void testReset_afterMagnifying_shouldNotifyDeactivatedState() { + setScaleToMagnifying(); + + mFullScreenMagnificationController.reset(DISPLAY_0, mAnimationCallback); + verify(mRequestObserver).onFullScreenMagnificationActivationState(eq(false)); + } + + private void setScaleToMagnifying() { + register(DISPLAY_0); + float scale = 2.0f; + PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + + mFullScreenMagnificationController.setScale(DISPLAY_0, scale, pivotPoint.x, pivotPoint.y, + false, SERVICE_ID_1); + } + private void initMockWindowManager() { for (int i = 0; i < DISPLAY_COUNT; i++) { when(mMockWindowManager.setMagnificationCallbacks(eq(i), any())).thenReturn(true); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java index 0c3640c4eeb0d..84c76b77018de 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java @@ -16,6 +16,9 @@ package com.android.server.accessibility.magnification; +import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; +import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; + import static com.android.server.accessibility.AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID; import static org.junit.Assert.assertEquals; @@ -24,6 +27,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doAnswer; @@ -282,6 +286,27 @@ public class MagnificationControllerTest { verify(mWindowMagnificationManager).persistScale(eq(TEST_DISPLAY)); } + @Test + public void onWindowMagnificationActivationState_windowActivated_logWindowDuration() { + mMagnificationController.onWindowMagnificationActivationState(true); + + mMagnificationController.onWindowMagnificationActivationState(false); + + verify(mMagnificationController).logMagnificationUsageState( + eq(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW), anyLong()); + } + + @Test + public void + onFullScreenMagnificationActivationState_fullScreenActivated_logFullScreenDuration() { + mMagnificationController.onFullScreenMagnificationActivationState(true); + + mMagnificationController.onFullScreenMagnificationActivationState(false); + + verify(mMagnificationController).logMagnificationUsageState( + eq(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN), anyLong()); + } + @Test public void onTouchInteractionStart_fullScreenAndCapabilitiesAll_showMagnificationButton() throws RemoteException { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java index f26c86c695874..955217c7d93f8 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java @@ -392,6 +392,23 @@ public class WindowMagnificationManagerTest { assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY)); } + @Test + public void onWindowMagnificationActivationState_magnifierEnabled_notifyActivatedState() { + mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN); + + verify(mMockCallback).onWindowMagnificationActivationState(eq(true)); + } + + @Test + public void onWindowMagnificationActivationState_magnifierDisabled_notifyDeactivatedState() { + mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN); + mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, true); + + verify(mMockCallback).onWindowMagnificationActivationState(eq(false)); + } + private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) { final int len = pointersLocation.length;