Magnification Gestures CTS test

Test: ensure affected CTS tests pass
Change-Id: I2ad9ef57098d1e9343d571e0d59504851ac691eb
diff --git a/api/test-current.txt b/api/test-current.txt
index cb4a6f2..a964aed 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -35400,6 +35400,7 @@
     method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String);
     method public static final deprecated void setLocationProviderEnabled(android.content.ContentResolver, java.lang.String, boolean);
     field public static final java.lang.String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED = "accessibility_display_inversion_enabled";
+    field public static final java.lang.String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled";
     field public static final java.lang.String ACCESSIBILITY_ENABLED = "accessibility_enabled";
     field public static final deprecated java.lang.String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password";
     field public static final deprecated java.lang.String ADB_ENABLED = "adb_enabled";
diff --git a/core/java/android/accessibilityservice/GestureDescription.java b/core/java/android/accessibilityservice/GestureDescription.java
index 92567d7..56f4ae2 100644
--- a/core/java/android/accessibilityservice/GestureDescription.java
+++ b/core/java/android/accessibilityservice/GestureDescription.java
@@ -428,6 +428,18 @@
         }
 
         @Override
+        public String toString() {
+            return "TouchPoint{"
+                    + "mStrokeId=" + mStrokeId
+                    + ", mContinuedStrokeId=" + mContinuedStrokeId
+                    + ", mIsStartOfPath=" + mIsStartOfPath
+                    + ", mIsEndOfPath=" + mIsEndOfPath
+                    + ", mX=" + mX
+                    + ", mY=" + mY
+                    + '}';
+        }
+
+        @Override
         public int describeContents() {
             return 0;
         }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 53e8881..b435074 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5708,6 +5708,7 @@
          *
          * @hide
          */
+        @TestApi
         public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED =
                 "accessibility_display_magnification_enabled";
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 47d21f8..f6fcaae 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -19,6 +19,9 @@
 import android.content.Context;
 import android.os.Handler;
 import android.os.PowerManager;
+import android.util.DebugUtils;
+import android.util.ExceptionUtils;
+import android.util.Log;
 import android.util.Pools.SimplePool;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
@@ -31,6 +34,7 @@
 import android.view.WindowManagerPolicy;
 import android.view.accessibility.AccessibilityEvent;
 
+import com.android.internal.util.BitUtils;
 import com.android.server.LocalServices;
 
 /**
@@ -188,6 +192,7 @@
         }
 
         if (mEventHandler == null) {
+            if (DEBUG) Slog.d(TAG, "mEventHandler == null for event " + event);
             super.onInputEvent(event, policyFlags);
             return;
         }
@@ -339,6 +344,8 @@
             MotionEvent transformedEvent = MotionEvent.obtain(event);
             mEventHandler.onMotionEvent(transformedEvent, event, policyFlags);
             transformedEvent.recycle();
+        } else {
+            if (DEBUG) Slog.d(TAG, "mEventHandler == null for " + event);
         }
     }
 
@@ -376,6 +383,10 @@
     }
 
     void setUserAndEnabledFeatures(int userId, int enabledFeatures) {
+        if (DEBUG) {
+            Slog.i(TAG, "setUserAndEnabledFeatures(userId = " + userId + ", enabledFeatures = 0x"
+                    + Integer.toHexString(enabledFeatures) + ")");
+        }
         if (mEnabledFeatures == enabledFeatures && mUserId == userId) {
             return;
         }
@@ -402,6 +413,8 @@
     }
 
     private void enableFeatures() {
+        if (DEBUG) Slog.i(TAG, "enableFeatures()");
+
         resetStreamState();
 
         if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
@@ -448,7 +461,7 @@
      */
     private void addFirstEventHandler(EventStreamTransformation handler) {
         if (mEventHandler != null) {
-           handler.setNext(mEventHandler);
+            handler.setNext(mEventHandler);
         } else {
             handler.setNext(this);
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index 98b8e6b..a10b7a2 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -56,6 +56,7 @@
  * constraints.
  */
 class MagnificationController implements Handler.Callback {
+    private static final boolean DEBUG = false;
     private static final String LOG_TAG = "MagnificationController";
 
     public static final float MIN_SCALE = 1.0f;
@@ -509,6 +510,12 @@
 
     private boolean setScaleAndCenterLocked(float scale, float centerX, float centerY,
             boolean animate, int id) {
+        if (DEBUG) {
+            Slog.i(LOG_TAG,
+                    "setScaleAndCenterLocked(scale = " + scale + ", centerX = " + centerX
+                            + ", centerY = " + centerY + ", animate = " + animate + ", id = " + id
+                            + ")");
+        }
         final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY);
         sendSpecToAnimation(mCurrentMagnificationSpec, animate);
         if (isMagnifying() && (id != INVALID_ID)) {
@@ -535,7 +542,9 @@
 
             final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX;
             final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY;
-            updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY);
+            if (updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY)) {
+                onMagnificationChangedLocked();
+            }
             if (id != INVALID_ID) {
                 mIdOfLastServiceToMagnify = id;
             }
@@ -633,6 +642,11 @@
     }
 
     private boolean updateCurrentSpecWithOffsetsLocked(float nonNormOffsetX, float nonNormOffsetY) {
+        if (DEBUG) {
+            Slog.i(LOG_TAG,
+                    "updateCurrentSpecWithOffsetsLocked(nonNormOffsetX = " + nonNormOffsetX
+                            + ", nonNormOffsetY = " + nonNormOffsetY + ")");
+        }
         boolean changed = false;
         final float offsetX = MathUtils.constrain(nonNormOffsetX, getMinOffsetXLocked(), 0);
         if (Float.compare(mCurrentMagnificationSpec.offsetX, offsetX) != 0) {
@@ -750,6 +764,9 @@
     }
 
     private void sendSpecToAnimation(MagnificationSpec spec, boolean animate) {
+        if (DEBUG) {
+            Slog.i(LOG_TAG, "sendSpecToAnimation(spec = " + spec + ", animate = " + animate + ")");
+        }
         if (Thread.currentThread().getId() == mMainThreadId) {
             mSpecAnimationBridge.updateSentSpecMainThread(spec, animate);
         } else {
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index 969f5b0..9b2b4eb 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -101,21 +101,12 @@
  */
 @SuppressWarnings("WeakerAccess")
 class MagnificationGestureHandler extends BaseEventStreamTransformation {
-    private static final String LOG_TAG = "MagnificationEventHandler";
+    private static final String LOG_TAG = "MagnificationGestureHandler";
 
     private static final boolean DEBUG_ALL = false;
     private static final boolean DEBUG_STATE_TRANSITIONS = false || DEBUG_ALL;
     private static final boolean DEBUG_DETECTING = false || DEBUG_ALL;
-    private static final boolean DEBUG_PANNING = false || DEBUG_ALL;
-
-    /** @see DelegatingState */
-    @VisibleForTesting static final int STATE_DELEGATING = 1;
-    /** @see DetectingState */
-    @VisibleForTesting static final int STATE_DETECTING = 2;
-    /** @see ViewportDraggingState */
-    @VisibleForTesting static final int STATE_VIEWPORT_DRAGGING = 3;
-    /** @see PanningScalingState */
-    @VisibleForTesting static final int STATE_PANNING_SCALING = 4;
+    private static final boolean DEBUG_PANNING_SCALING = false || DEBUG_ALL;
 
     private static final float MIN_SCALE = 2.0f;
     private static final float MAX_SCALE = 5.0f;
@@ -184,6 +175,8 @@
 
     @Override
     public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        if (DEBUG_ALL) Slog.i(LOG_TAG, "onMotionEvent(" + event + ")");
+
         if ((!mDetectTripleTap && !mDetectShortcutTrigger)
                 || !event.isFromSource(SOURCE_TOUCHSCREEN)) {
             dispatchTransformedEvent(event, rawEvent, policyFlags);
@@ -213,6 +206,11 @@
 
     @Override
     public void onDestroy() {
+        if (DEBUG_STATE_TRANSITIONS) {
+            Slog.i(LOG_TAG, "onDestroy(); delayed = "
+                    + MotionEventInfo.toString(mDetectingState.mDelayedEventQueue));
+        }
+
         if (mScreenStateReceiver != null) {
             mScreenStateReceiver.unregister();
         }
@@ -239,6 +237,8 @@
 
     private void dispatchTransformedEvent(MotionEvent event, MotionEvent rawEvent,
             int policyFlags) {
+        if (DEBUG_ALL) Slog.i(LOG_TAG, "dispatchTransformedEvent(event = " + event + ")");
+
         // If the touchscreen event is within the magnified portion of the screen we have
         // to change its location to be where the user thinks he is poking the
         // UI which may have been magnified and panned.
@@ -332,8 +332,6 @@
      * magnification level.
      * This makes it the preferred mode for one-off adjustments, due to its precision and ease of
      * triggering.
-     *
-     * @see #STATE_PANNING_SCALING
      */
     final class PanningScalingState extends SimpleOnGestureListener
             implements OnScaleGestureListener, State {
@@ -385,7 +383,7 @@
             if (mCurrentState != mPanningScalingState) {
                 return true;
             }
-            if (DEBUG_PANNING) {
+            if (DEBUG_PANNING_SCALING) {
                 Slog.i(LOG_TAG, "Panned content by scrollX: " + distanceX
                         + " scrollY: " + distanceY);
             }
@@ -402,12 +400,8 @@
                     return false;
                 }
                 final float deltaScale = detector.getScaleFactor() - mInitialScaleFactor;
-                if (abs(deltaScale) > mScalingThreshold) {
-                    mScaling = true;
-                    return true;
-                } else {
-                    return false;
-                }
+                mScaling = abs(deltaScale) > mScalingThreshold;
+                return mScaling;
             }
 
             final float initialScale = mMagnificationController.getScale();
@@ -431,6 +425,7 @@
 
             final float pivotX = detector.getFocusX();
             final float pivotY = detector.getFocusY();
+            if (DEBUG_PANNING_SCALING) Slog.i(LOG_TAG, "Scaled content to: " + scale + "x");
             mMagnificationController.setScale(scale, pivotX, pivotY, false,
                     AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
             return /* handled: */ true;
@@ -469,8 +464,6 @@
      * Unlike when {@link PanningScalingState panning}, the viewport moves in the opposite direction
      * of the finger, and any part of the screen is reachable without lifting the finger.
      * This makes it the preferable mode for tasks like reading text spanning full screen width.
-     *
-     * @see #STATE_VIEWPORT_DRAGGING
      */
     final class ViewportDraggingState implements State {
 
@@ -534,7 +527,7 @@
 
     final class DelegatingState implements State {
         /**
-         * Time of last {@link MotionEvent#ACTION_DOWN} while in {@link #STATE_DELEGATING}
+         * Time of last {@link MotionEvent#ACTION_DOWN} while in {@link DelegatingState}
          */
         public long mLastDelegatedDownEventTime;
 
@@ -563,8 +556,6 @@
     /**
      * This class handles motion events when the event dispatch has not yet
      * determined what the user is doing. It watches for various tap events.
-     *
-     * @see #STATE_DETECTING
      */
     final class DetectingState implements State, Handler.Callback {
 
@@ -737,14 +728,14 @@
             return MotionEventInfo.countOf(mDelayedEventQueue, ACTION_UP);
         }
 
-        /** -> {@link #STATE_DELEGATING} */
+        /** -> {@link DelegatingState} */
         public void afterMultiTapTimeoutTransitionToDelegatingState() {
             mHandler.sendEmptyMessageDelayed(
                     MESSAGE_TRANSITION_TO_DELEGATING_STATE,
                     mMultiTapMaxDelay);
         }
 
-        /** -> {@link #STATE_VIEWPORT_DRAGGING} */
+        /** -> {@link ViewportDraggingState} */
         public void afterLongTapTimeoutTransitionToDraggingState(MotionEvent event) {
             mHandler.sendMessageDelayed(
                     mHandler.obtainMessage(MESSAGE_ON_TRIPLE_TAP_AND_HOLD, event),
@@ -865,6 +856,8 @@
     }
 
     private void zoomOn(float centerX, float centerY) {
+        if (DEBUG_DETECTING) Slog.i(LOG_TAG, "zoomOn(" + centerX + ", " + centerY + ")");
+
         final float scale = MathUtils.constrain(
                 mMagnificationController.getPersistedScale(),
                 MIN_SCALE, MAX_SCALE);
@@ -875,6 +868,8 @@
     }
 
     private void zoomOff() {
+        if (DEBUG_DETECTING) Slog.i(LOG_TAG, "zoomOff()");
+
         mMagnificationController.reset(/* animate */ true);
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
index 7925510..b6b7812 100644
--- a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
+++ b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
@@ -36,6 +36,7 @@
 import com.android.internal.os.SomeArgs;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -241,17 +242,24 @@
                 int continuedPointerId = mStrokeIdToPointerId
                         .get(touchPoint.mContinuedStrokeId, -1);
                 if (continuedPointerId == -1) {
+                    Slog.w(LOG_TAG, "Can't continue gesture due to unknown continued stroke id in "
+                            + touchPoint);
                     return false;
                 }
                 mStrokeIdToPointerId.put(touchPoint.mStrokeId, continuedPointerId);
                 int lastPointIndex = findPointByStrokeId(
                         mLastTouchPoints, mNumLastTouchPoints, touchPoint.mContinuedStrokeId);
                 if (lastPointIndex < 0) {
+                    Slog.w(LOG_TAG, "Can't continue gesture due continued gesture id of "
+                            + touchPoint + " not matching any previous strokes in "
+                            + Arrays.asList(mLastTouchPoints));
                     return false;
                 }
                 if (mLastTouchPoints[lastPointIndex].mIsEndOfPath
                         || (mLastTouchPoints[lastPointIndex].mX != touchPoint.mX)
                         || (mLastTouchPoints[lastPointIndex].mY != touchPoint.mY)) {
+                    Slog.w(LOG_TAG, "Can't continue gesture due to points mismatch between "
+                            + mLastTouchPoints[lastPointIndex] + " and " + touchPoint);
                     return false;
                 }
                 // Update the last touch point to match the continuation, so the gestures will