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;