Merge "Enable multi-display support for magnification controller"
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 35098a0..cebe6e12 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -37,6 +37,7 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.view.Display;
 import android.view.KeyEvent;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
@@ -382,7 +383,8 @@
         void init(int connectionId, IBinder windowToken);
         boolean onGesture(int gestureId);
         boolean onKeyEvent(KeyEvent event);
-        void onMagnificationChanged(@NonNull Region region,
+        /** Magnification changed callbacks for different displays */
+        void onMagnificationChanged(int displayId, @NonNull Region region,
                 float scale, float centerX, float centerY);
         void onSoftKeyboardShowModeChanged(int showMode);
         void onPerformGestureResult(int sequence, boolean completedSuccessfully);
@@ -452,7 +454,9 @@
 
     private WindowManager mWindowManager;
 
-    private MagnificationController mMagnificationController;
+    /** List of magnification controllers, mapping from displayId -> MagnificationController. */
+    private final SparseArray<MagnificationController> mMagnificationControllers =
+            new SparseArray<>(0);
     private SoftKeyboardController mSoftKeyboardController;
     private AccessibilityButtonController mAccessibilityButtonController;
 
@@ -483,8 +487,10 @@
      * client code.
      */
     private void dispatchServiceConnected() {
-        if (mMagnificationController != null) {
-            mMagnificationController.onServiceConnected();
+        synchronized (mLock) {
+            for (int i = 0; i < mMagnificationControllers.size(); i++) {
+                mMagnificationControllers.valueAt(i).onServiceConnectedLocked();
+            }
         }
         if (mSoftKeyboardController != null) {
             mSoftKeyboardController.onServiceConnected();
@@ -652,11 +658,34 @@
      */
     @NonNull
     public final MagnificationController getMagnificationController() {
+        return getMagnificationController(Display.DEFAULT_DISPLAY);
+    }
+
+    /**
+     * Returns the magnification controller of specified logical display, which may be used to
+     * query and modify the state of display magnification.
+     * <p>
+     * <strong>Note:</strong> In order to control magnification, your service
+     * must declare the capability by setting the
+     * {@link android.R.styleable#AccessibilityService_canControlMagnification}
+     * property in its meta-data. For more information, see
+     * {@link #SERVICE_META_DATA}.
+     *
+     * @param displayId The logic display id, use {@link Display#DEFAULT_DISPLAY} for
+     *                  default display.
+     * @return the magnification controller
+     *
+     * @hide
+     */
+    @NonNull
+    public final MagnificationController getMagnificationController(int displayId) {
         synchronized (mLock) {
-            if (mMagnificationController == null) {
-                mMagnificationController = new MagnificationController(this, mLock);
+            MagnificationController controller = mMagnificationControllers.get(displayId);
+            if (controller == null) {
+                controller = new MagnificationController(this, mLock, displayId);
+                mMagnificationControllers.put(displayId, controller);
             }
-            return mMagnificationController;
+            return controller;
         }
     }
 
@@ -765,11 +794,14 @@
         }
     }
 
-    private void onMagnificationChanged(@NonNull Region region, float scale,
+    private void onMagnificationChanged(int displayId, @NonNull Region region, float scale,
             float centerX, float centerY) {
-        if (mMagnificationController != null) {
-            mMagnificationController.dispatchMagnificationChanged(
-                    region, scale, centerX, centerY);
+        MagnificationController controller;
+        synchronized (mLock) {
+            controller = mMagnificationControllers.get(displayId);
+        }
+        if (controller != null) {
+            controller.dispatchMagnificationChanged(region, scale, centerX, centerY);
         }
     }
 
@@ -794,6 +826,7 @@
      */
     public static final class MagnificationController {
         private final AccessibilityService mService;
+        private final int mDisplayId;
 
         /**
          * Map of listeners to their handlers. Lazily created when adding the
@@ -802,19 +835,19 @@
         private ArrayMap<OnMagnificationChangedListener, Handler> mListeners;
         private final Object mLock;
 
-        MagnificationController(@NonNull AccessibilityService service, @NonNull Object lock) {
+        MagnificationController(@NonNull AccessibilityService service, @NonNull Object lock,
+                int displayId) {
             mService = service;
             mLock = lock;
+            mDisplayId = displayId;
         }
 
         /**
          * Called when the service is connected.
          */
-        void onServiceConnected() {
-            synchronized (mLock) {
-                if (mListeners != null && !mListeners.isEmpty()) {
-                    setMagnificationCallbackEnabled(true);
-                }
+        void onServiceConnectedLocked() {
+            if (mListeners != null && !mListeners.isEmpty()) {
+                setMagnificationCallbackEnabled(true);
             }
         }
 
@@ -891,7 +924,7 @@
                             mService.mConnectionId);
             if (connection != null) {
                 try {
-                    connection.setMagnificationCallbackEnabled(enabled);
+                    connection.setMagnificationCallbackEnabled(mDisplayId, enabled);
                 } catch (RemoteException re) {
                     throw new RuntimeException(re);
                 }
@@ -952,7 +985,7 @@
                             mService.mConnectionId);
             if (connection != null) {
                 try {
-                    return connection.getMagnificationScale();
+                    return connection.getMagnificationScale(mDisplayId);
                 } catch (RemoteException re) {
                     Log.w(LOG_TAG, "Failed to obtain scale", re);
                     re.rethrowFromSystemServer();
@@ -981,7 +1014,7 @@
                             mService.mConnectionId);
             if (connection != null) {
                 try {
-                    return connection.getMagnificationCenterX();
+                    return connection.getMagnificationCenterX(mDisplayId);
                 } catch (RemoteException re) {
                     Log.w(LOG_TAG, "Failed to obtain center X", re);
                     re.rethrowFromSystemServer();
@@ -1010,7 +1043,7 @@
                             mService.mConnectionId);
             if (connection != null) {
                 try {
-                    return connection.getMagnificationCenterY();
+                    return connection.getMagnificationCenterY(mDisplayId);
                 } catch (RemoteException re) {
                     Log.w(LOG_TAG, "Failed to obtain center Y", re);
                     re.rethrowFromSystemServer();
@@ -1044,7 +1077,7 @@
                             mService.mConnectionId);
             if (connection != null) {
                 try {
-                    return connection.getMagnificationRegion();
+                    return connection.getMagnificationRegion(mDisplayId);
                 } catch (RemoteException re) {
                     Log.w(LOG_TAG, "Failed to obtain magnified region", re);
                     re.rethrowFromSystemServer();
@@ -1073,7 +1106,7 @@
                             mService.mConnectionId);
             if (connection != null) {
                 try {
-                    return connection.resetMagnification(animate);
+                    return connection.resetMagnification(mDisplayId, animate);
                 } catch (RemoteException re) {
                     Log.w(LOG_TAG, "Failed to reset", re);
                     re.rethrowFromSystemServer();
@@ -1101,7 +1134,7 @@
                             mService.mConnectionId);
             if (connection != null) {
                 try {
-                    return connection.setMagnificationScaleAndCenter(
+                    return connection.setMagnificationScaleAndCenter(mDisplayId,
                             scale, Float.NaN, Float.NaN, animate);
                 } catch (RemoteException re) {
                     Log.w(LOG_TAG, "Failed to set scale", re);
@@ -1133,7 +1166,7 @@
                             mService.mConnectionId);
             if (connection != null) {
                 try {
-                    return connection.setMagnificationScaleAndCenter(
+                    return connection.setMagnificationScaleAndCenter(mDisplayId,
                             Float.NaN, centerX, centerY, animate);
                 } catch (RemoteException re) {
                     Log.w(LOG_TAG, "Failed to set center", re);
@@ -1624,9 +1657,10 @@
             }
 
             @Override
-            public void onMagnificationChanged(@NonNull Region region,
+            public void onMagnificationChanged(int displayId, @NonNull Region region,
                     float scale, float centerX, float centerY) {
-                AccessibilityService.this.onMagnificationChanged(region, scale, centerX, centerY);
+                AccessibilityService.this.onMagnificationChanged(displayId, region, scale,
+                        centerX, centerY);
             }
 
             @Override
@@ -1729,13 +1763,15 @@
             mCaller.sendMessage(message);
         }
 
-        public void onMagnificationChanged(@NonNull Region region,
+        /** Magnification changed callbacks for different displays */
+        public void onMagnificationChanged(int displayId, @NonNull Region region,
                 float scale, float centerX, float centerY) {
             final SomeArgs args = SomeArgs.obtain();
             args.arg1 = region;
             args.arg2 = scale;
             args.arg3 = centerX;
             args.arg4 = centerY;
+            args.argi1 = displayId;
 
             final Message message = mCaller.obtainMessageO(DO_ON_MAGNIFICATION_CHANGED, args);
             mCaller.sendMessage(message);
@@ -1865,7 +1901,10 @@
                         final float scale = (float) args.arg2;
                         final float centerX = (float) args.arg3;
                         final float centerY = (float) args.arg4;
-                        mCallback.onMagnificationChanged(region, scale, centerX, centerY);
+                        final int displayId = args.argi1;
+                        args.recycle();
+                        mCallback.onMagnificationChanged(displayId, region, scale,
+                                centerX, centerY);
                     }
                 } return;
 
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index 4e96b8f..1dae4fc 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -41,7 +41,7 @@
 
     void onKeyEvent(in KeyEvent event, int sequence);
 
-    void onMagnificationChanged(in Region region, float scale, float centerX, float centerY);
+    void onMagnificationChanged(int displayId, in Region region, float scale, float centerX, float centerY);
 
     void onSoftKeyboardShowModeChanged(int showMode);
 
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 276131f..8c38fe4 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -70,20 +70,20 @@
 
     oneway void setOnKeyEventResult(boolean handled, int sequence);
 
-    float getMagnificationScale();
+    float getMagnificationScale(int displayId);
 
-    float getMagnificationCenterX();
+    float getMagnificationCenterX(int displayId);
 
-    float getMagnificationCenterY();
+    float getMagnificationCenterY(int displayId);
 
-    Region getMagnificationRegion();
+    Region getMagnificationRegion(int displayId);
 
-    boolean resetMagnification(boolean animate);
+    boolean resetMagnification(int displayId, boolean animate);
 
-    boolean setMagnificationScaleAndCenter(float scale, float centerX, float centerY,
+    boolean setMagnificationScaleAndCenter(int displayId, float scale, float centerX, float centerY,
         boolean animate);
 
-    void setMagnificationCallbackEnabled(boolean enabled);
+    void setMagnificationCallbackEnabled(int displayId, boolean enabled);
 
     boolean setSoftKeyboardShowMode(int showMode);
 
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 3f9627e..a021e3c 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -1240,7 +1240,7 @@
                 }
 
                 @Override
-                public void onMagnificationChanged(@NonNull Region region,
+                public void onMagnificationChanged(int displayId, @NonNull Region region,
                         float scale, float centerX, float centerY) {
                     /* do nothing */
                 }
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 0dd7685..683d16b 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -88,32 +88,32 @@
 
     public void setOnKeyEventResult(boolean handled, int sequence) {}
 
-    public float getMagnificationScale() {
+    public float getMagnificationScale(int displayId) {
         return 0.0f;
     }
 
-    public float getMagnificationCenterX() {
+    public float getMagnificationCenterX(int displayId) {
         return 0.0f;
     }
 
-    public float getMagnificationCenterY() {
+    public float getMagnificationCenterY(int displayId) {
         return 0.0f;
     }
 
-    public Region getMagnificationRegion() {
+    public Region getMagnificationRegion(int displayId) {
         return null;
     }
 
-    public boolean resetMagnification(boolean animate) {
+    public boolean resetMagnification(int displayId, boolean animate) {
         return false;
     }
 
-    public boolean setMagnificationScaleAndCenter(float scale, float centerX, float centerY,
-            boolean animate) {
+    public boolean setMagnificationScaleAndCenter(int displayId, float scale, float centerX,
+            float centerY, boolean animate) {
         return false;
     }
 
-    public void setMagnificationCallbackEnabled(boolean enabled) {}
+    public void setMagnificationCallbackEnabled(int displayId, boolean enabled) {}
 
     public boolean setSoftKeyboardShowMode(int showMode) {
         return false;
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 6eba914..2c075dc 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -53,6 +53,7 @@
 import android.view.accessibility.IAccessibilityInteractionConnection;
 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.DumpUtils;
 import com.android.server.accessibility.AccessibilityManagerService.RemoteAccessibilityConnection;
@@ -751,7 +752,7 @@
     }
 
     @Override
-    public float getMagnificationScale() {
+    public float getMagnificationScale(int displayId) {
         synchronized (mLock) {
             if (!isCalledForCurrentUserLocked()) {
                 return 1.0f;
@@ -759,14 +760,14 @@
         }
         final long identity = Binder.clearCallingIdentity();
         try {
-            return mSystemSupport.getMagnificationController().getScale();
+            return mSystemSupport.getMagnificationController().getScale(displayId);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
     }
 
     @Override
-    public Region getMagnificationRegion() {
+    public Region getMagnificationRegion(int displayId) {
         synchronized (mLock) {
             final Region region = Region.obtain();
             if (!isCalledForCurrentUserLocked()) {
@@ -775,22 +776,22 @@
             MagnificationController magnificationController =
                     mSystemSupport.getMagnificationController();
             boolean registeredJustForThisCall =
-                    registerMagnificationIfNeeded(magnificationController);
+                    registerMagnificationIfNeeded(displayId, magnificationController);
             final long identity = Binder.clearCallingIdentity();
             try {
-                magnificationController.getMagnificationRegion(region);
+                magnificationController.getMagnificationRegion(displayId, region);
                 return region;
             } finally {
                 Binder.restoreCallingIdentity(identity);
                 if (registeredJustForThisCall) {
-                    magnificationController.unregister();
+                    magnificationController.unregister(displayId);
                 }
             }
         }
     }
 
     @Override
-    public float getMagnificationCenterX() {
+    public float getMagnificationCenterX(int displayId) {
         synchronized (mLock) {
             if (!isCalledForCurrentUserLocked()) {
                 return 0.0f;
@@ -798,21 +799,21 @@
             MagnificationController magnificationController =
                     mSystemSupport.getMagnificationController();
             boolean registeredJustForThisCall =
-                    registerMagnificationIfNeeded(magnificationController);
+                    registerMagnificationIfNeeded(displayId, magnificationController);
             final long identity = Binder.clearCallingIdentity();
             try {
-                return magnificationController.getCenterX();
+                return magnificationController.getCenterX(displayId);
             } finally {
                 Binder.restoreCallingIdentity(identity);
                 if (registeredJustForThisCall) {
-                    magnificationController.unregister();
+                    magnificationController.unregister(displayId);
                 }
             }
         }
     }
 
     @Override
-    public float getMagnificationCenterY() {
+    public float getMagnificationCenterY(int displayId) {
         synchronized (mLock) {
             if (!isCalledForCurrentUserLocked()) {
                 return 0.0f;
@@ -820,31 +821,31 @@
             MagnificationController magnificationController =
                     mSystemSupport.getMagnificationController();
             boolean registeredJustForThisCall =
-                    registerMagnificationIfNeeded(magnificationController);
+                    registerMagnificationIfNeeded(displayId, magnificationController);
             final long identity = Binder.clearCallingIdentity();
             try {
-                return magnificationController.getCenterY();
+                return magnificationController.getCenterY(displayId);
             } finally {
                 Binder.restoreCallingIdentity(identity);
                 if (registeredJustForThisCall) {
-                    magnificationController.unregister();
+                    magnificationController.unregister(displayId);
                 }
             }
         }
     }
 
-    private boolean registerMagnificationIfNeeded(
+    private boolean registerMagnificationIfNeeded(int displayId,
             MagnificationController magnificationController) {
-        if (!magnificationController.isRegisteredLocked()
+        if (!magnificationController.isRegistered(displayId)
                 && mSecurityPolicy.canControlMagnification(this)) {
-            magnificationController.register();
+            magnificationController.register(displayId);
             return true;
         }
         return false;
     }
 
     @Override
-    public boolean resetMagnification(boolean animate) {
+    public boolean resetMagnification(int displayId, boolean animate) {
         synchronized (mLock) {
             if (!isCalledForCurrentUserLocked()) {
                 return false;
@@ -857,16 +858,16 @@
         try {
             MagnificationController magnificationController =
                     mSystemSupport.getMagnificationController();
-            return (magnificationController.reset(animate)
-                    || !magnificationController.isMagnifying());
+            return (magnificationController.reset(displayId, animate)
+                    || !magnificationController.isMagnifying(displayId));
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
     }
 
     @Override
-    public boolean setMagnificationScaleAndCenter(float scale, float centerX, float centerY,
-            boolean animate) {
+    public boolean setMagnificationScaleAndCenter(int displayId, float scale, float centerX,
+            float centerY, boolean animate) {
         synchronized (mLock) {
             if (!isCalledForCurrentUserLocked()) {
                 return false;
@@ -878,11 +879,11 @@
             try {
                 MagnificationController magnificationController =
                         mSystemSupport.getMagnificationController();
-                if (!magnificationController.isRegisteredLocked()) {
-                    magnificationController.register();
+                if (!magnificationController.isRegistered(displayId)) {
+                    magnificationController.register(displayId);
                 }
                 return magnificationController
-                        .setScaleAndCenter(scale, centerX, centerY, animate, mId);
+                        .setScaleAndCenter(displayId, scale, centerX, centerY, animate, mId);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -890,12 +891,12 @@
     }
 
     @Override
-    public void setMagnificationCallbackEnabled(boolean enabled) {
-        mInvocationHandler.setMagnificationCallbackEnabled(enabled);
+    public void setMagnificationCallbackEnabled(int displayId, boolean enabled) {
+        mInvocationHandler.setMagnificationCallbackEnabled(displayId, enabled);
     }
 
-    public boolean isMagnificationCallbackEnabled() {
-        return mInvocationHandler.mIsMagnificationCallbackEnabled;
+    public boolean isMagnificationCallbackEnabled(int displayId) {
+        return mInvocationHandler.isMagnificationCallbackEnabled(displayId);
     }
 
     @Override
@@ -1106,10 +1107,10 @@
                 InvocationHandler.MSG_CLEAR_ACCESSIBILITY_CACHE);
     }
 
-    public void notifyMagnificationChangedLocked(@NonNull Region region,
+    public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
             float scale, float centerX, float centerY) {
         mInvocationHandler
-                .notifyMagnificationChangedLocked(region, scale, centerX, centerY);
+                .notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY);
     }
 
     public void notifySoftKeyboardShowModeChangedLocked(int showState) {
@@ -1128,12 +1129,12 @@
      * Called by the invocation handler to notify the service that the
      * state of magnification has changed.
      */
-    private void notifyMagnificationChangedInternal(@NonNull Region region,
+    private void notifyMagnificationChangedInternal(int displayId, @NonNull Region region,
             float scale, float centerX, float centerY) {
         final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
         if (listener != null) {
             try {
-                listener.onMagnificationChanged(region, scale, centerX, centerY);
+                listener.onMagnificationChanged(displayId, region, scale, centerX, centerY);
             } catch (RemoteException re) {
                 Slog.e(LOG_TAG, "Error sending magnification changes to " + mService, re);
             }
@@ -1251,7 +1252,9 @@
         private static final int MSG_ON_ACCESSIBILITY_BUTTON_CLICKED = 7;
         private static final int MSG_ON_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED = 8;
 
-        private boolean mIsMagnificationCallbackEnabled = false;
+        /** List of magnification callback states, mapping from displayId -> Boolean */
+        @GuardedBy("mlock")
+        private final SparseArray<Boolean> mMagnificationCallbackState = new SparseArray<>(0);
         private boolean mIsSoftKeyboardCallbackEnabled = false;
 
         public InvocationHandler(Looper looper) {
@@ -1277,7 +1280,8 @@
                     final float scale = (float) args.arg2;
                     final float centerX = (float) args.arg3;
                     final float centerY = (float) args.arg4;
-                    notifyMagnificationChangedInternal(region, scale, centerX, centerY);
+                    final int displayId = args.argi1;
+                    notifyMagnificationChangedInternal(displayId, region, scale, centerX, centerY);
                     args.recycle();
                 } break;
 
@@ -1301,11 +1305,12 @@
             }
         }
 
-        public void notifyMagnificationChangedLocked(@NonNull Region region, float scale,
-                float centerX, float centerY) {
-            if (!mIsMagnificationCallbackEnabled) {
-                // Callback is disabled, don't bother packing args.
-                return;
+        public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
+                float scale, float centerX, float centerY) {
+            synchronized (mLock) {
+                if (mMagnificationCallbackState.get(displayId) == null) {
+                    return;
+                }
             }
 
             final SomeArgs args = SomeArgs.obtain();
@@ -1313,13 +1318,26 @@
             args.arg2 = scale;
             args.arg3 = centerX;
             args.arg4 = centerY;
+            args.argi1 = displayId;
 
             final Message msg = obtainMessage(MSG_ON_MAGNIFICATION_CHANGED, args);
             msg.sendToTarget();
         }
 
-        public void setMagnificationCallbackEnabled(boolean enabled) {
-            mIsMagnificationCallbackEnabled = enabled;
+        public void setMagnificationCallbackEnabled(int displayId, boolean enabled) {
+            synchronized (mLock) {
+                if (enabled) {
+                    mMagnificationCallbackState.put(displayId, true);
+                } else {
+                    mMagnificationCallbackState.remove(displayId);
+                }
+            }
+        }
+
+        public boolean isMagnificationCallbackEnabled(int displayId) {
+            synchronized (mLock) {
+                return mMagnificationCallbackState.get(displayId) != null;
+            }
         }
 
         public void notifySoftKeyboardShowModeChangedLocked(int showState) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 763c16f..dbe86c1 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -208,6 +208,8 @@
 
     private final WindowManagerInternal mWindowManagerService;
 
+    private final DisplayManager mDisplayManager;
+
     private AppWidgetManagerInternal mAppWidgetService;
 
     private final SecurityPolicy mSecurityPolicy;
@@ -304,10 +306,12 @@
         mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
         mMainHandler = new MainHandler(mContext.getMainLooper());
         mGlobalActionPerformer = new GlobalActionPerformer(mContext, mWindowManagerService);
+        mDisplayManager = mContext.getSystemService(DisplayManager.class);
 
         registerBroadcastReceivers();
         new AccessibilityContentObserver(mMainHandler).register(
                 context.getContentResolver());
+        registerDisplayListener(mMainHandler);
     }
 
     @Override
@@ -523,6 +527,30 @@
         }, UserHandle.ALL, intentFilter, null, null);
     }
 
+    private void registerDisplayListener(Handler handler) {
+        mDisplayManager.registerDisplayListener(new DisplayManager.DisplayListener() {
+            @Override
+            public void onDisplayAdded(int displayId) {
+                synchronized (mLock) {
+                    UserState userState = getCurrentUserStateLocked();
+                    updateMagnificationLocked(userState);
+                }
+            }
+
+            @Override
+            public void onDisplayRemoved(int displayId) {
+                if (mMagnificationController != null) {
+                    mMagnificationController.onDisplayRemoved(displayId);
+                }
+            }
+
+            @Override
+            public void onDisplayChanged(int displayId) {
+                // do nothing
+            }
+        }, handler);
+    }
+
     @Override
     public long addClient(IAccessibilityManagerClient callback, int userId) {
         synchronized (mLock) {
@@ -968,17 +996,18 @@
      * Called by the MagnificationController when the state of display
      * magnification changes.
      *
+     * @param displayId The logical display id.
      * @param region the new magnified region, may be empty if
      *               magnification is not enabled (e.g. scale is 1)
      * @param scale the new scale
      * @param centerX the new screen-relative center X coordinate
      * @param centerY the new screen-relative center Y coordinate
      */
-    public void notifyMagnificationChanged(@NonNull Region region,
+    public void notifyMagnificationChanged(int displayId, @NonNull Region region,
             float scale, float centerX, float centerY) {
         synchronized (mLock) {
             notifyClearAccessibilityCacheLocked();
-            notifyMagnificationChangedLocked(region, scale, centerX, centerY);
+            notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY);
         }
     }
 
@@ -1203,12 +1232,12 @@
         }
     }
 
-    private void notifyMagnificationChangedLocked(@NonNull Region region,
+    private void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
             float scale, float centerX, float centerY) {
         final UserState state = getCurrentUserStateLocked();
         for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
             final AccessibilityServiceConnection service = state.mBoundServices.get(i);
-            service.notifyMagnificationChangedLocked(region, scale, centerX, centerY);
+            service.notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY);
         }
     }
 
@@ -2191,15 +2220,44 @@
             return;
         }
 
-        if (!mUiAutomationManager.suppressingAccessibilityServicesLocked()
-                && (userState.mIsDisplayMagnificationEnabled
-                        || userState.mIsNavBarMagnificationEnabled
-                        || userHasListeningMagnificationServicesLocked(userState))) {
-            // Initialize the magnification controller if necessary
-            getMagnificationController();
-            mMagnificationController.register();
-        } else if (mMagnificationController != null) {
-            mMagnificationController.unregister();
+        if (mUiAutomationManager.suppressingAccessibilityServicesLocked()
+                && mMagnificationController != null) {
+            mMagnificationController.unregisterAll();
+            return;
+        }
+
+        // register all display if global magnification is enabled.
+        final Display[] displays = mDisplayManager.getDisplays();
+        if (userState.mIsDisplayMagnificationEnabled
+                || userState.mIsNavBarMagnificationEnabled) {
+            for (int i = 0; i < displays.length; i++) {
+                final Display display = displays[i];
+                // Overlay display uses overlay window to simulate secondary displays in
+                // one display. It's not a real display and there's no input events for it.
+                // We should ignore it.
+                if (display.getType() == Display.TYPE_OVERLAY) {
+                    continue;
+                }
+                getMagnificationController().register(display.getDisplayId());
+            }
+            return;
+        }
+
+        // register if display has listening magnification services.
+        for (int i = 0; i < displays.length; i++) {
+            final Display display = displays[i];
+            // Overlay display uses overlay window to simulate secondary displays in
+            // one display. It's not a real display and there's no input events for it.
+            // We should ignore it.
+            if (display.getType() == Display.TYPE_OVERLAY) {
+                continue;
+            }
+            final int displayId = display.getDisplayId();
+            if (userHasListeningMagnificationServicesLocked(userState, displayId)) {
+                getMagnificationController().register(displayId);
+            } else if (mMagnificationController != null) {
+                mMagnificationController.unregister(displayId);
+            }
         }
     }
 
@@ -2222,12 +2280,13 @@
      * Returns whether the specified user has any services that are capable of
      * controlling magnification and are actively listening for magnification updates.
      */
-    private boolean userHasListeningMagnificationServicesLocked(UserState userState) {
+    private boolean userHasListeningMagnificationServicesLocked(UserState userState,
+            int displayId) {
         final List<AccessibilityServiceConnection> services = userState.mBoundServices;
         for (int i = 0, count = services.size(); i < count; i++) {
             final AccessibilityServiceConnection service = services.get(i);
             if (mSecurityPolicy.canControlMagnification(service)
-                    && service.isMagnificationCallbackEnabled()) {
+                    && service.isMagnificationCallbackEnabled(displayId)) {
                 return true;
             }
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 86132a8..a19a847 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -109,7 +109,7 @@
         UserState userState = mUserStateWeakReference.get();
         if (userState == null) return;
         userState.removeServiceLocked(this);
-        mSystemSupport.getMagnificationController().resetIfNeeded(mId);
+        mSystemSupport.getMagnificationController().resetAllIfNeeded(mId);
         resetLocked();
     }
 
@@ -256,7 +256,7 @@
                 userState.serviceDisconnectedLocked(this);
             }
             resetLocked();
-            mSystemSupport.getMagnificationController().resetIfNeeded(mId);
+            mSystemSupport.getMagnificationController().resetAllIfNeeded(mId);
             mSystemSupport.onClientChangeLocked(false);
         }
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index 6a97fbb..e784056 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -19,7 +19,6 @@
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -32,6 +31,7 @@
 import android.text.TextUtils;
 import android.util.MathUtils;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.view.Display;
 import android.view.MagnificationSpec;
 import android.view.View;
@@ -39,6 +39,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
 import com.android.server.wm.WindowManagerInternal;
@@ -68,9 +69,7 @@
 
     private final Object mLock;
 
-    private final AccessibilityManagerService mAms;
-
-    private final SettingsBridge mSettingsBridge;
+    private final ControllerContext mControllerCtx;
 
     private final ScreenStateObserver mScreenStateObserver;
 
@@ -78,11 +77,9 @@
 
     private final long mMainThreadId;
 
-    private Handler mHandler;
-
-    private final WindowManagerInternal mWindowManager;
-
-    private final DisplayMagnification mDisplay;
+    /** List of display Magnification, mapping from displayId -> DisplayMagnification. */
+    @GuardedBy("mLock")
+    private final SparseArray<DisplayMagnification> mDisplays = new SparseArray<>(0);
 
     /**
      * This class implements {@link WindowManagerInternal.MagnificationCallbacks} and holds
@@ -107,46 +104,82 @@
         // Flag indicating that we are registered with window manager.
         private boolean mRegistered;
         private boolean mUnregisterPending;
+        private boolean mDeleteAfterUnregister;
 
         private final int mDisplayId;
 
         private static final int INVALID_ID = -1;
         private int mIdOfLastServiceToMagnify = INVALID_ID;
 
-
-        DisplayMagnification(int displayId, SpecAnimationBridge specAnimation) {
+        DisplayMagnification(int displayId) {
             mDisplayId = displayId;
-            mSpecAnimationBridge = specAnimation;
+            mSpecAnimationBridge = new SpecAnimationBridge(mControllerCtx, mLock, mDisplayId);
         }
 
-        void register() {
-            synchronized (mLock) {
-                if (!mRegistered) {
-                    mWindowManager.setMagnificationCallbacks(this);
-                    mSpecAnimationBridge.setEnabled(true);
-                    // Obtain initial state.
-                    mWindowManager.getMagnificationRegion(mMagnificationRegion);
-                    mMagnificationRegion.getBounds(mMagnificationBounds);
-                    mRegistered = true;
-                }
+        /**
+         * Registers magnification callback and get current magnification region from
+         * window manager.
+         *
+         * @return true if callback registers successful.
+         */
+        @GuardedBy("mLock")
+        boolean register() {
+            mRegistered = mControllerCtx.getWindowManager().setMagnificationCallbacks(
+                    mDisplayId, this);
+            if (!mRegistered) {
+                Slog.w(LOG_TAG, "set magnification callbacks fail, displayId:" + mDisplayId);
+                return false;
             }
+            mSpecAnimationBridge.setEnabled(true);
+            // Obtain initial state.
+            mControllerCtx.getWindowManager().getMagnificationRegion(
+                    mDisplayId, mMagnificationRegion);
+            mMagnificationRegion.getBounds(mMagnificationBounds);
+            return true;
         }
 
-        void unregister() {
-            synchronized (mLock) {
-                if (!isMagnifying()) {
-                    unregisterInternalLocked();
-                } else {
-                    mUnregisterPending = true;
-                    reset(true);
-                }
+        /**
+         * Unregisters magnification callback from window manager. Callbacks to
+         * {@link MagnificationController#unregisterCallbackLocked(int, boolean)} after
+         * unregistered.
+         *
+         * @param delete true if this instance should be removed from the SparseArray in
+         *               MagnificationController after unregistered, for example, display removed.
+         */
+        @GuardedBy("mLock")
+        void unregister(boolean delete) {
+            if (mRegistered) {
+                mSpecAnimationBridge.setEnabled(false);
+                mControllerCtx.getWindowManager().setMagnificationCallbacks(
+                        mDisplayId, null);
+                mMagnificationRegion.setEmpty();
+                mRegistered = false;
+                unregisterCallbackLocked(mDisplayId, delete);
             }
+            mUnregisterPending = false;
         }
 
-        boolean isRegisteredLocked() {
+        /**
+         * Reset magnification status with animation enabled. {@link #unregister(boolean)} will be
+         * called after animation finished.
+         *
+         * @param delete true if this instance should be removed from the SparseArray in
+         *               MagnificationController after unregistered, for example, display removed.
+         */
+        @GuardedBy("mLock")
+        void unregisterPending(boolean delete) {
+            mDeleteAfterUnregister = delete;
+            mUnregisterPending = true;
+            reset(true);
+        }
+
+        boolean isRegistered() {
             return mRegistered;
         }
 
+        boolean isMagnifying() {
+            return mCurrentMagnificationSpec.scale > 1.0f;
+        }
 
         float getScale() {
             return mCurrentMagnificationSpec.scale;
@@ -156,18 +189,20 @@
             return mCurrentMagnificationSpec.offsetX;
         }
 
-        float getCenterX() {
-            synchronized (mLock) {
-                return (mMagnificationBounds.width() / 2.0f
-                        + mMagnificationBounds.left - getOffsetX()) / getScale();
-            }
+        float getOffsetY() {
+            return mCurrentMagnificationSpec.offsetY;
         }
 
+        @GuardedBy("mLock")
+        float getCenterX() {
+            return (mMagnificationBounds.width() / 2.0f
+                    + mMagnificationBounds.left - getOffsetX()) / getScale();
+        }
+
+        @GuardedBy("mLock")
         float getCenterY() {
-            synchronized (mLock) {
-                return (mMagnificationBounds.height() / 2.0f
-                        + mMagnificationBounds.top - getOffsetY()) / getScale();
-            }
+            return (mMagnificationBounds.height() / 2.0f
+                    + mMagnificationBounds.top - getOffsetY()) / getScale();
         }
 
         /**
@@ -203,64 +238,35 @@
             return mSpecAnimationBridge.mSentMagnificationSpec.offsetY;
         }
 
-        boolean resetIfNeeded(boolean animate) {
-            synchronized (mLock) {
-                if (isMagnifying()) {
-                    reset(animate);
-                    return true;
-                }
-                return false;
-            }
-        }
-
-        float getOffsetY() {
-            return mCurrentMagnificationSpec.offsetY;
-        }
-
-        boolean isMagnifying() {
-            return mCurrentMagnificationSpec.scale > 1.0f;
-        }
-
-        void unregisterInternalLocked() {
-            if (mRegistered) {
-                mSpecAnimationBridge.setEnabled(false);
-                mWindowManager.setMagnificationCallbacks(null);
-                mMagnificationRegion.setEmpty();
-
-                mRegistered = false;
-            }
-            mUnregisterPending = false;
-        }
-
-
         @Override
         public void onMagnificationRegionChanged(Region magnificationRegion) {
             final Message m = PooledLambda.obtainMessage(
-                    DisplayMagnification.this::updateMagnificationRegion,
+                    DisplayMagnification::updateMagnificationRegion, this,
                     Region.obtain(magnificationRegion));
-            mHandler.sendMessage(m);
+            mControllerCtx.getHandler().sendMessage(m);
         }
 
         @Override
         public void onRectangleOnScreenRequested(int left, int top, int right, int bottom) {
             final Message m = PooledLambda.obtainMessage(
-                    DisplayMagnification.this::requestRectangleOnScreen, left, top, right, bottom);
-            mHandler.sendMessage(m);
+                    DisplayMagnification::requestRectangleOnScreen, this,
+                    left, top, right, bottom);
+            mControllerCtx.getHandler().sendMessage(m);
         }
 
         @Override
         public void onRotationChanged(int rotation) {
             // Treat as context change and reset
-            final Message m = PooledLambda.obtainMessage(DisplayMagnification.this::resetIfNeeded,
-                    true);
-            mHandler.sendMessage(m);
+            final Message m = PooledLambda.obtainMessage(MagnificationController::resetIfNeeded,
+                    MagnificationController.this, mDisplayId, true);
+            mControllerCtx.getHandler().sendMessage(m);
         }
 
         @Override
         public void onUserContextChanged() {
-            final Message m = PooledLambda.obtainMessage(DisplayMagnification.this::resetIfNeeded,
-                    true);
-            mHandler.sendMessage(m);
+            final Message m = PooledLambda.obtainMessage(MagnificationController::resetIfNeeded,
+                    MagnificationController.this, mDisplayId, true);
+            mControllerCtx.getHandler().sendMessage(m);
         }
 
         /**
@@ -298,8 +304,9 @@
                 mSpecAnimationBridge.updateSentSpecMainThread(spec, animate);
             } else {
                 final Message m = PooledLambda.obtainMessage(
-                        this.mSpecAnimationBridge::updateSentSpecMainThread, spec, animate);
-                mHandler.sendMessage(m);
+                        SpecAnimationBridge::updateSentSpecMainThread,
+                        mSpecAnimationBridge, spec, animate);
+                mControllerCtx.getHandler().sendMessage(m);
             }
         }
 
@@ -313,30 +320,26 @@
         }
 
         void onMagnificationChangedLocked() {
-            mAms.notifyMagnificationChanged(mMagnificationRegion,
+            mControllerCtx.getAms().notifyMagnificationChanged(mDisplayId, mMagnificationRegion,
                     getScale(), getCenterX(), getCenterY());
             if (mUnregisterPending && !isMagnifying()) {
-                unregisterInternalLocked();
+                unregister(mDeleteAfterUnregister);
             }
         }
 
+        @GuardedBy("mLock")
         boolean magnificationRegionContains(float x, float y) {
-            synchronized (mLock) {
-                return mMagnificationRegion.contains((int) x, (int) y);
-
-            }
+            return mMagnificationRegion.contains((int) x, (int) y);
         }
 
+        @GuardedBy("mLock")
         void getMagnificationBounds(@NonNull Rect outBounds) {
-            synchronized (mLock) {
-                outBounds.set(mMagnificationBounds);
-            }
+            outBounds.set(mMagnificationBounds);
         }
 
+        @GuardedBy("mLock")
         void getMagnificationRegion(@NonNull Region outRegion) {
-            synchronized (mLock) {
-                outRegion.set(mMagnificationRegion);
-            }
+            outRegion.set(mMagnificationRegion);
         }
 
         void requestRectangleOnScreen(int left, int top, int right, int bottom) {
@@ -392,94 +395,76 @@
             outFrame.scale(1.0f / scale);
         }
 
-        /**
-         * Resets magnification if last magnifying service is disabled.
-         *
-         * @param connectionId the connection ID be disabled.
-         * @return {@code true} on success, {@code false} on failure
-         */
-        boolean resetIfNeeded(int connectionId) {
-            if (mIdOfLastServiceToMagnify == connectionId) {
-                return resetIfNeeded(true /*animate*/);
-            }
-            return false;
-        }
-
+        @GuardedBy("mLock")
         void setForceShowMagnifiableBounds(boolean show) {
             if (mRegistered) {
-                mWindowManager.setForceShowMagnifiableBounds(show);
+                mControllerCtx.getWindowManager().setForceShowMagnifiableBounds(
+                        mDisplayId, show);
             }
         }
 
+        @GuardedBy("mLock")
         boolean reset(boolean animate) {
-            synchronized (mLock) {
-                if (!mRegistered) {
-                    return false;
-                }
-                final MagnificationSpec spec = mCurrentMagnificationSpec;
-                final boolean changed = !spec.isNop();
-                if (changed) {
-                    spec.clear();
-                    onMagnificationChangedLocked();
-                }
-                mIdOfLastServiceToMagnify = INVALID_ID;
-                sendSpecToAnimation(spec, animate);
-                return changed;
+            if (!mRegistered) {
+                return false;
             }
+            final MagnificationSpec spec = mCurrentMagnificationSpec;
+            final boolean changed = !spec.isNop();
+            if (changed) {
+                spec.clear();
+                onMagnificationChangedLocked();
+            }
+            mIdOfLastServiceToMagnify = INVALID_ID;
+            sendSpecToAnimation(spec, animate);
+            return changed;
         }
 
-
+        @GuardedBy("mLock")
         boolean setScale(float scale, float pivotX, float pivotY,
                 boolean animate, int id) {
-
-            synchronized (mLock) {
-                if (!mRegistered) {
-                    return false;
-                }
-                // Constrain scale immediately for use in the pivot calculations.
-                scale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
-
-                final Rect viewport = mTempRect;
-                mMagnificationRegion.getBounds(viewport);
-                final MagnificationSpec spec = mCurrentMagnificationSpec;
-                final float oldScale = spec.scale;
-                final float oldCenterX
-                        = (viewport.width() / 2.0f - spec.offsetX + viewport.left) / oldScale;
-                final float oldCenterY
-                        = (viewport.height() / 2.0f - spec.offsetY + viewport.top) / oldScale;
-                final float normPivotX = (pivotX - spec.offsetX) / oldScale;
-                final float normPivotY = (pivotY - spec.offsetY) / oldScale;
-                final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale);
-                final float offsetY = (oldCenterY - normPivotY) * (oldScale / scale);
-                final float centerX = normPivotX + offsetX;
-                final float centerY = normPivotY + offsetY;
-                mIdOfLastServiceToMagnify = id;
-
-                return setScaleAndCenter(scale, centerX, centerY, animate, id);
+            if (!mRegistered) {
+                return false;
             }
+            // Constrain scale immediately for use in the pivot calculations.
+            scale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
+
+            final Rect viewport = mTempRect;
+            mMagnificationRegion.getBounds(viewport);
+            final MagnificationSpec spec = mCurrentMagnificationSpec;
+            final float oldScale = spec.scale;
+            final float oldCenterX =
+                    (viewport.width() / 2.0f - spec.offsetX + viewport.left) / oldScale;
+            final float oldCenterY =
+                    (viewport.height() / 2.0f - spec.offsetY + viewport.top) / oldScale;
+            final float normPivotX = (pivotX - spec.offsetX) / oldScale;
+            final float normPivotY = (pivotY - spec.offsetY) / oldScale;
+            final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale);
+            final float offsetY = (oldCenterY - normPivotY) * (oldScale / scale);
+            final float centerX = normPivotX + offsetX;
+            final float centerY = normPivotY + offsetY;
+            mIdOfLastServiceToMagnify = id;
+            return setScaleAndCenter(scale, centerX, centerY, animate, id);
         }
 
+        @GuardedBy("mLock")
         boolean setScaleAndCenter(float scale, float centerX, float centerY,
                 boolean animate, int id) {
-
-            synchronized (mLock) {
-                if (!mRegistered) {
-                    return false;
-                }
-                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)) {
-                    mIdOfLastServiceToMagnify = id;
-                }
-                return changed;
+            if (!mRegistered) {
+                return false;
             }
+            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)) {
+                mIdOfLastServiceToMagnify = id;
+            }
+            return changed;
         }
 
         /**
@@ -527,22 +512,21 @@
             return changed;
         }
 
+        @GuardedBy("mLock")
         void offsetMagnifiedRegion(float offsetX, float offsetY, int id) {
-            synchronized (mLock) {
-                if (!mRegistered) {
-                    return;
-                }
-
-                final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX;
-                final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY;
-                if (updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY)) {
-                    onMagnificationChangedLocked();
-                }
-                if (id != INVALID_ID) {
-                    mIdOfLastServiceToMagnify = id;
-                }
-                sendSpecToAnimation(mCurrentMagnificationSpec, false);
+            if (!mRegistered) {
+                return;
             }
+
+            final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX;
+            final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY;
+            if (updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY)) {
+                onMagnificationChangedLocked();
+            }
+            if (id != INVALID_ID) {
+                mIdOfLastServiceToMagnify = id;
+            }
+            sendSpecToAnimation(mCurrentMagnificationSpec, false);
         }
 
         boolean updateCurrentSpecWithOffsetsLocked(float nonNormOffsetX, float nonNormOffsetY) {
@@ -593,44 +577,38 @@
 
         @Override
         public String toString() {
-            return "DisplayMagnification{" +
-                    "mCurrentMagnificationSpec=" + mCurrentMagnificationSpec +
-                    ", mMagnificationRegion=" + mMagnificationRegion +
-                    ", mMagnificationBounds=" + mMagnificationBounds +
-                    ", mDisplayId=" + mDisplayId +
-                    ", mUserId=" + mUserId +
-                    ", mIdOfLastServiceToMagnify=" + mIdOfLastServiceToMagnify +
-                    ", mRegistered=" + mRegistered +
-                    ", mUnregisterPending=" + mUnregisterPending +
-                    '}';
+            return "DisplayMagnification["
+                    + "mCurrentMagnificationSpec=" + mCurrentMagnificationSpec
+                    + ", mMagnificationRegion=" + mMagnificationRegion
+                    + ", mMagnificationBounds=" + mMagnificationBounds
+                    + ", mDisplayId=" + mDisplayId
+                    + ", mIdOfLastServiceToMagnify=" + mIdOfLastServiceToMagnify
+                    + ", mRegistered=" + mRegistered
+                    + ", mUnregisterPending=" + mUnregisterPending
+                    + ']';
         }
-
     }
 
-    public MagnificationController(Context context, AccessibilityManagerService ams, Object lock) {
-        this(context, ams, lock, null, LocalServices.getService(WindowManagerInternal.class),
-                new ValueAnimator(), new SettingsBridge(context.getContentResolver()));
-        mHandler = new Handler(context.getMainLooper());
+    /**
+     * MagnificationController Constructor
+     */
+    public MagnificationController(@NonNull Context context,
+            @NonNull AccessibilityManagerService ams, @NonNull Object lock) {
+        this(new ControllerContext(context, ams,
+                LocalServices.getService(WindowManagerInternal.class),
+                new Handler(context.getMainLooper()),
+                context.getResources().getInteger(R.integer.config_longAnimTime)), lock);
     }
 
-    public MagnificationController(
-            Context context,
-            AccessibilityManagerService ams,
-            Object lock,
-            Handler handler,
-            WindowManagerInternal windowManagerInternal,
-            ValueAnimator valueAnimator,
-            SettingsBridge settingsBridge) {
-        mHandler = handler;
-        mWindowManager = windowManagerInternal;
-        mMainThreadId = context.getMainLooper().getThread().getId();
-        mAms = ams;
-        mScreenStateObserver = new ScreenStateObserver(context, this);
+    /**
+     * Constructor for tests
+     */
+    @VisibleForTesting
+    public MagnificationController(@NonNull ControllerContext ctx, @NonNull Object lock) {
+        mControllerCtx = ctx;
         mLock = lock;
-        mSettingsBridge = settingsBridge;
-        //TODO (multidisplay): Magnification is supported only for the default display.
-        mDisplay =  new DisplayMagnification(Display.DEFAULT_DISPLAY,
-                new SpecAnimationBridge(context, mLock, mWindowManager, valueAnimator));
+        mMainThreadId = mControllerCtx.getContext().getMainLooper().getThread().getId();
+        mScreenStateObserver = new ScreenStateObserver(mControllerCtx.getContext(), this);
     }
 
     /**
@@ -639,54 +617,114 @@
      *
      * This tracking imposes a cost on the system, so we avoid tracking this data unless it's
      * required.
+     *
+     * @param displayId The logical display id.
      */
-    public void register() {
+    public void register(int displayId) {
         synchronized (mLock) {
-            mScreenStateObserver.register();
+            DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                display = new DisplayMagnification(displayId);
+            }
+            if (display.isRegistered()) {
+                return;
+            }
+            if (display.register()) {
+                mDisplays.put(displayId, display);
+                mScreenStateObserver.registerIfNecessary();
+            }
         }
-        mDisplay.register();
     }
 
     /**
      * Stop requiring tracking the magnification region. We may remain registered while we
      * reset magnification.
-     */
-    public void unregister() {
-        synchronized (mLock) {
-            mScreenStateObserver.unregister();
-        }
-        mDisplay.unregister();
-    }
-    
-    /**
-     * Check if we are registered. Note that we may be planning to unregister at any moment.
      *
-     * @return {@code true} if the controller is registered. {@code false} otherwise.
+     * @param displayId The logical display id.
      */
-    public boolean isRegisteredLocked() {
-        return mDisplay.isRegisteredLocked();
+    public void unregister(int displayId) {
+        synchronized (mLock) {
+            unregisterLocked(displayId, false);
+        }
     }
 
     /**
+     * Stop tracking all displays' magnification region.
+     */
+    public void unregisterAll() {
+        synchronized (mLock) {
+            // display will be removed from array after unregister, we need to clone it to
+            // prevent error.
+            final SparseArray<DisplayMagnification> displays = mDisplays.clone();
+            for (int i = 0; i < displays.size(); i++) {
+                unregisterLocked(displays.keyAt(i), false);
+            }
+        }
+    }
+
+    /**
+     * Remove the display magnification with given id.
+     *
+     * @param displayId The logical display id.
+     */
+    public void onDisplayRemoved(int displayId) {
+        synchronized (mLock) {
+            unregisterLocked(displayId, true);
+        }
+    }
+
+    /**
+     * Check if we are registered on specified display. Note that we may be planning to unregister
+     * at any moment.
+     *
+     * @return {@code true} if the controller is registered on specified display.
+     * {@code false} otherwise.
+     *
+     * @param displayId The logical display id.
+     */
+    public boolean isRegistered(int displayId) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return false;
+            }
+            return display.isRegistered();
+        }
+    }
+
+    /**
+     * @param displayId The logical display id.
      * @return {@code true} if magnification is active, e.g. the scale
      *         is > 1, {@code false} otherwise
      */
-    public boolean isMagnifying() {
-        return mDisplay.isMagnifying();
+    public boolean isMagnifying(int displayId) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return false;
+            }
+            return display.isMagnifying();
+        }
     }
 
     /**
      * Returns whether the magnification region contains the specified
      * screen-relative coordinates.
      *
+     * @param displayId The logical display id.
      * @param x the screen-relative X coordinate to check
      * @param y the screen-relative Y coordinate to check
      * @return {@code true} if the coordinate is contained within the
      *         magnified region, or {@code false} otherwise
      */
-    public boolean magnificationRegionContains(float x, float y) {
-        return mDisplay.magnificationRegionContains(x, y);
-
+    public boolean magnificationRegionContains(int displayId, float x, float y) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return false;
+            }
+            return display.magnificationRegionContains(x, y);
+        }
     }
 
     /**
@@ -694,11 +732,18 @@
      * magnification region. If magnification is not enabled, the returned
      * bounds will be empty.
      *
+     * @param displayId The logical display id.
      * @param outBounds rect to populate with the bounds of the magnified
      *                  region
      */
-    public void getMagnificationBounds(@NonNull Rect outBounds) {
-        mDisplay.getMagnificationBounds(outBounds);
+    public void getMagnificationBounds(int displayId, @NonNull Rect outBounds) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return;
+            }
+            display.getMagnificationBounds(outBounds);
+        }
     }
 
     /**
@@ -706,76 +751,122 @@
      * region. If magnification is not enabled, then the returned region
      * will be empty.
      *
+     * @param displayId The logical display id.
      * @param outRegion the region to populate
      */
-    public void getMagnificationRegion(@NonNull Region outRegion) {
-        mDisplay.getMagnificationRegion(outRegion);
+    public void getMagnificationRegion(int displayId, @NonNull Region outRegion) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return;
+            }
+            display.getMagnificationRegion(outRegion);
+        }
     }
 
     /**
      * Returns the magnification scale. If an animation is in progress,
      * this reflects the end state of the animation.
      *
+     * @param displayId The logical display id.
      * @return the scale
      */
-    public float getScale() {
-        return mDisplay.getScale();
+    public float getScale(int displayId) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return 1.0f;
+            }
+            return display.getScale();
+        }
     }
 
     /**
      * Returns the X offset of the magnification viewport. If an animation
      * is in progress, this reflects the end state of the animation.
      *
+     * @param displayId The logical display id.
      * @return the X offset
      */
-    public float getOffsetX() {
-        return mDisplay.getOffsetX();
+    public float getOffsetX(int displayId) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return 0.0f;
+            }
+            return display.getOffsetX();
+        }
     }
 
-
     /**
      * Returns the screen-relative X coordinate of the center of the
      * magnification viewport.
      *
+     * @param displayId The logical display id.
      * @return the X coordinate
      */
-    public float getCenterX() {
-        return mDisplay.getCenterX();
+    public float getCenterX(int displayId) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return 0.0f;
+            }
+            return display.getCenterX();
+        }
     }
 
     /**
      * Returns the Y offset of the magnification viewport. If an animation
      * is in progress, this reflects the end state of the animation.
      *
+     * @param displayId The logical display id.
      * @return the Y offset
      */
-    public float getOffsetY() {
-        return mDisplay.getOffsetY();
+    public float getOffsetY(int displayId) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return 0.0f;
+            }
+            return display.getOffsetY();
+        }
     }
 
     /**
      * Returns the screen-relative Y coordinate of the center of the
      * magnification viewport.
      *
+     * @param displayId The logical display id.
      * @return the Y coordinate
      */
-    public float getCenterY() {
-        return mDisplay.getCenterY();
+    public float getCenterY(int displayId) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return 0.0f;
+            }
+            return display.getCenterY();
+        }
     }
 
     /**
      * Resets the magnification scale and center, optionally animating the
      * transition.
      *
+     * @param displayId The logical display id.
      * @param animate {@code true} to animate the transition, {@code false}
      *                to transition immediately
      * @return {@code true} if the magnification spec changed, {@code false} if
      *         the spec did not change
      */
-    public boolean reset(boolean animate) {
-
-        return mDisplay.reset(animate);
-
+    public boolean reset(int displayId, boolean animate) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return false;
+            }
+            return display.reset(animate);
+        }
     }
 
     /**
@@ -783,6 +874,7 @@
      * optionally animating the transition. If animation is disabled, the
      * transition is immediate.
      *
+     * @param displayId The logical display id.
      * @param scale the target scale, must be >= 1
      * @param pivotX the screen-relative X coordinate around which to scale
      * @param pivotY the screen-relative Y coordinate around which to scale
@@ -792,15 +884,22 @@
      * @return {@code true} if the magnification spec changed, {@code false} if
      *         the spec did not change
      */
-    public boolean setScale(float scale, float pivotX, float pivotY, boolean animate, int id) {
-            return mDisplay.
-                    setScale(scale, pivotX, pivotY, animate, id);
+    public boolean setScale(int displayId, float scale, float pivotX, float pivotY,
+            boolean animate, int id) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return false;
+            }
+            return display.setScale(scale, pivotX, pivotY, animate, id);
+        }
     }
 
     /**
      * Sets the center of the magnified region, optionally animating the
      * transition. If animation is disabled, the transition is immediate.
      *
+     * @param displayId The logical display id.
      * @param centerX the screen-relative X coordinate around which to
      *                center
      * @param centerY the screen-relative Y coordinate around which to
@@ -811,9 +910,14 @@
      * @return {@code true} if the magnification spec changed, {@code false} if
      * the spec did not change
      */
-    public boolean setCenter(float centerX, float centerY, boolean animate, int id) {
-            return mDisplay.
-                    setScaleAndCenter(Float.NaN, centerX, centerY, animate, id);
+    public boolean setCenter(int displayId, float centerX, float centerY, boolean animate, int id) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return false;
+            }
+            return display.setScaleAndCenter(Float.NaN, centerX, centerY, animate, id);
+        }
     }
 
     /**
@@ -821,6 +925,7 @@
      * animating the transition. If animation is disabled, the transition
      * is immediate.
      *
+     * @param displayId The logical display id.
      * @param scale the target scale, or {@link Float#NaN} to leave unchanged
      * @param centerX the screen-relative X coordinate around which to
      *                center and scale, or {@link Float#NaN} to leave unchanged
@@ -832,53 +937,66 @@
      * @return {@code true} if the magnification spec changed, {@code false} if
      *         the spec did not change
      */
-    public boolean setScaleAndCenter(
-            float scale, float centerX, float centerY, boolean animate, int id) {
-        return mDisplay.
-                setScaleAndCenter(scale, centerX, centerY, animate, id);
+    public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY,
+            boolean animate, int id) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return false;
+            }
+            return display.setScaleAndCenter(scale, centerX, centerY, animate, id);
+        }
     }
 
     /**
      * Offsets the magnified region. Note that the offsetX and offsetY values actually move in the
      * opposite direction as the offsets passed in here.
      *
+     * @param displayId The logical display id.
      * @param offsetX the amount in pixels to offset the region in the X direction, in current
      *                screen pixels.
      * @param offsetY the amount in pixels to offset the region in the Y direction, in current
      *                screen pixels.
      * @param id      the ID of the service requesting the change
      */
-    public void offsetMagnifiedRegion(float offsetX, float offsetY, int id) {
-        mDisplay.offsetMagnifiedRegion(offsetX, offsetY,
-                id);
+    public void offsetMagnifiedRegion(int displayId, float offsetX, float offsetY, int id) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return;
+            }
+            display.offsetMagnifiedRegion(offsetX, offsetY, id);
+        }
     }
 
     /**
      * Get the ID of the last service that changed the magnification spec.
      *
+     * @param displayId The logical display id.
      * @return The id
      */
-    public int getIdOfLastServiceToMagnify() {
-        return mDisplay.getIdOfLastServiceToMagnify();
+    public int getIdOfLastServiceToMagnify(int displayId) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return -1;
+            }
+            return display.getIdOfLastServiceToMagnify();
+        }
     }
 
     /**
-     * Persists the current magnification scale to the current user's settings.
+     * Persists the default display magnification scale to the current user's settings.
      */
     public void persistScale() {
-        persistScale(Display.DEFAULT_DISPLAY);
-    }
-    /**
-     * Persists the current magnification scale to the current user's settings.
-     */
-    public void persistScale(int displayId) {
-        final float scale = mDisplay.getScale();
+        // TODO: b/123047354, Need support multi-display?
+        final float scale = getScale(Display.DEFAULT_DISPLAY);
         final int userId = mUserId;
 
         new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
-                mSettingsBridge.putMagnificationScale(scale, displayId, userId);
+                mControllerCtx.putMagnificationScale(scale, userId);
                 return null;
             }
         }.execute();
@@ -892,7 +1010,7 @@
      *         scale if none is available
      */
     public float getPersistedScale() {
-        return mSettingsBridge.getMagnificationScale(Display.DEFAULT_DISPLAY, mUserId);
+        return mControllerCtx.getMagnificationScale(mUserId);
     }
 
     /**
@@ -901,50 +1019,136 @@
      * @param userId the currently active user ID
      */
     public void setUserId(int userId) {
-        if (mUserId != userId) {
-            mUserId = userId;
+        if (mUserId == userId) {
+            return;
+        }
+        mUserId = userId;
+        resetAllIfNeeded(false);
+    }
 
-            synchronized (mLock) {
-                if (isMagnifying()) {
-                    reset(false);
-                }
+    /**
+     * Resets all displays' magnification if last magnifying service is disabled.
+     *
+     * @param connectionId
+     */
+    public void resetAllIfNeeded(int connectionId) {
+        synchronized (mLock) {
+            for (int i = 0; i < mDisplays.size(); i++) {
+                resetIfNeeded(mDisplays.keyAt(i), connectionId);
             }
         }
     }
 
-   /**
+    /**
      * Resets magnification if magnification and auto-update are both enabled.
      *
+     * @param displayId The logical display id.
      * @param animate whether the animate the transition
-     * @return whether was {@link #isMagnifying magnifying}
+     * @return whether was {@link #isMagnifying(int) magnifying}
      */
-    public boolean resetIfNeeded(boolean animate) {
-        return mDisplay.resetIfNeeded(animate);
+    boolean resetIfNeeded(int displayId, boolean animate) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null || !display.isMagnifying()) {
+                return false;
+            }
+            display.reset(animate);
+            return true;
+        }
     }
 
     /**
      * Resets magnification if last magnifying service is disabled.
      *
+     * @param displayId The logical display id.
      * @param connectionId the connection ID be disabled.
      * @return {@code true} on success, {@code false} on failure
      */
-    public boolean resetIfNeeded(int connectionId) {
-        return mDisplay.resetIfNeeded(connectionId);
+    boolean resetIfNeeded(int displayId, int connectionId) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null || !display.isMagnifying()
+                    || connectionId != display.getIdOfLastServiceToMagnify()) {
+                return false;
+            }
+            display.reset(true);
+            return true;
+        }
     }
 
-    void setForceShowMagnifiableBounds(boolean show) {
-        mDisplay.setForceShowMagnifiableBounds(show);
+    void setForceShowMagnifiableBounds(int displayId, boolean show) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return;
+            }
+            display.setForceShowMagnifiableBounds(show);
+        }
     }
 
     private void onScreenTurnedOff() {
         final Message m = PooledLambda.obtainMessage(
-                mDisplay::resetIfNeeded, false);
-        mHandler.sendMessage(m);
+                MagnificationController::resetAllIfNeeded, this, false);
+        mControllerCtx.getHandler().sendMessage(m);
+    }
+
+    private void resetAllIfNeeded(boolean animate) {
+        synchronized (mLock) {
+            for (int i = 0; i < mDisplays.size(); i++) {
+                resetIfNeeded(mDisplays.keyAt(i), animate);
+            }
+        }
+    }
+
+    private void unregisterLocked(int displayId, boolean delete) {
+        final DisplayMagnification display = mDisplays.get(displayId);
+        if (display == null) {
+            return;
+        }
+        if (!display.isRegistered()) {
+            if (delete) {
+                mDisplays.remove(displayId);
+            }
+            return;
+        }
+        if (!display.isMagnifying()) {
+            display.unregister(delete);
+        } else {
+            display.unregisterPending(delete);
+        }
+    }
+
+    /**
+     * Callbacks from DisplayMagnification after display magnification unregistered. It will remove
+     * DisplayMagnification instance if delete is true, and unregister screen state if
+     * there is no registered display magnification.
+     */
+    private void unregisterCallbackLocked(int displayId, boolean delete) {
+        if (delete) {
+            mDisplays.remove(displayId);
+        }
+        // unregister screen state if necessary
+        boolean hasRegister = false;
+        for (int i = 0; i < mDisplays.size(); i++) {
+            final DisplayMagnification display = mDisplays.valueAt(i);
+            hasRegister = display.isRegistered();
+            if (hasRegister) {
+                break;
+            }
+        }
+        if (!hasRegister) {
+            mScreenStateObserver.unregister();
+        }
     }
 
     @Override
     public String toString() {
-        return mDisplay.toString();
+        StringBuilder builder = new StringBuilder();
+        builder.append("MagnificationController[");
+        builder.append("mUserId=").append(mUserId);
+        builder.append(", mDisplays=").append(mDisplays);
+        builder.append("]");
+        return builder.toString();
     }
 
     /**
@@ -952,7 +1156,7 @@
      * updates to the window manager.
      */
     private static class SpecAnimationBridge implements ValueAnimator.AnimatorUpdateListener {
-        private final WindowManagerInternal mWindowManager;
+        private final ControllerContext mControllerCtx;
 
         /**
          * The magnification spec that was sent to the window manager. This should
@@ -973,16 +1177,17 @@
 
         private final Object mLock;
 
+        private final int mDisplayId;
+
         @GuardedBy("mLock")
         private boolean mEnabled = false;
 
-        private SpecAnimationBridge(Context context, Object lock, WindowManagerInternal wm,
-                ValueAnimator animator) {
+        private SpecAnimationBridge(ControllerContext ctx, Object lock, int displayId) {
+            mControllerCtx = ctx;
             mLock = lock;
-            mWindowManager = wm;
-            final long animationDuration = context.getResources().getInteger(
-                    R.integer.config_longAnimTime);
-            mValueAnimator = animator;
+            mDisplayId = displayId;
+            final long animationDuration = mControllerCtx.getAnimationDuration();
+            mValueAnimator = mControllerCtx.newValueAnimator();
             mValueAnimator.setDuration(animationDuration);
             mValueAnimator.setInterpolator(new DecelerateInterpolator(2.5f));
             mValueAnimator.setFloatValues(0.0f, 1.0f);
@@ -999,7 +1204,8 @@
                     mEnabled = enabled;
                     if (!mEnabled) {
                         mSentMagnificationSpec.clear();
-                        mWindowManager.setMagnificationSpec(mSentMagnificationSpec);
+                        mControllerCtx.getWindowManager().setMagnificationSpec(
+                                mDisplayId, mSentMagnificationSpec);
                     }
                 }
             }
@@ -1031,7 +1237,8 @@
                 }
 
                 mSentMagnificationSpec.setTo(spec);
-                mWindowManager.setMagnificationSpec(spec);
+                mControllerCtx.getWindowManager().setMagnificationSpec(
+                        mDisplayId, mSentMagnificationSpec);
             }
         }
 
@@ -1054,9 +1261,7 @@
                     mTmpMagnificationSpec.offsetY = mStartMagnificationSpec.offsetY +
                             (mEndMagnificationSpec.offsetY - mStartMagnificationSpec.offsetY)
                                     * fract;
-                    synchronized (mLock) {
-                        setMagnificationSpecLocked(mTmpMagnificationSpec);
-                    }
+                    setMagnificationSpecLocked(mTmpMagnificationSpec);
                 }
             }
         }
@@ -1072,7 +1277,7 @@
             mController = controller;
         }
 
-        public void register() {
+        public void registerIfNecessary() {
             if (!mRegistered) {
                 mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_SCREEN_OFF));
                 mRegistered = true;
@@ -1092,26 +1297,97 @@
         }
     }
 
-    // Extra class to get settings so tests can mock it
-    public static class SettingsBridge {
-        private final ContentResolver mContentResolver;
+    /**
+     * This class holds resources used between the classes in MagnificationController, and
+     * functions for tests to mock it.
+     */
+    @VisibleForTesting
+    public static class ControllerContext {
+        private final Context mContext;
+        private final AccessibilityManagerService mAms;
+        private final WindowManagerInternal mWindowManager;
+        private final Handler mHandler;
+        private final Long mAnimationDuration;
 
-        public SettingsBridge(ContentResolver contentResolver) {
-            mContentResolver = contentResolver;
+        /**
+         * Constructor for ControllerContext.
+         */
+        public ControllerContext(@NonNull Context context,
+                @NonNull AccessibilityManagerService ams,
+                @NonNull WindowManagerInternal windowManager,
+                @NonNull Handler handler,
+                long animationDuration) {
+            mContext = context;
+            mAms = ams;
+            mWindowManager = windowManager;
+            mHandler = handler;
+            mAnimationDuration = animationDuration;
         }
 
-        public void putMagnificationScale(float value, int displayId, int userId) {
-            Settings.Secure.putFloatForUser(mContentResolver,
-                    Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE + (
-                            Display.DEFAULT_DISPLAY == displayId ? "" : displayId),
-                    value, userId);
+        /**
+         * @return A context.
+         */
+        @NonNull
+        public Context getContext() {
+            return mContext;
         }
 
-        public float getMagnificationScale(int displayId, int userId) {
-            return Settings.Secure.getFloatForUser(mContentResolver,
-                    Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE
-                            + (Display.DEFAULT_DISPLAY == displayId ? "" : displayId),
+        /**
+         * @return AccessibilityManagerService
+         */
+        @NonNull
+        public AccessibilityManagerService getAms() {
+            return mAms;
+        }
+
+        /**
+         * @return WindowManagerInternal
+         */
+        @NonNull
+        public WindowManagerInternal getWindowManager() {
+            return mWindowManager;
+        }
+
+        /**
+         * @return Handler for main looper
+         */
+        @NonNull
+        public Handler getHandler() {
+            return mHandler;
+        }
+
+        /**
+         * Create a new ValueAnimator.
+         *
+         * @return ValueAnimator
+         */
+        @NonNull
+        public ValueAnimator newValueAnimator() {
+            return new ValueAnimator();
+        }
+
+        /**
+         * Write Settings of magnification scale.
+         */
+        public void putMagnificationScale(float value, int userId) {
+            Settings.Secure.putFloatForUser(mContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, value, userId);
+        }
+
+        /**
+         * Get Settings of magnification scale.
+         */
+        public float getMagnificationScale(int userId) {
+            return Settings.Secure.getFloatForUser(mContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
                     DEFAULT_MAGNIFICATION_SCALE, userId);
         }
+
+        /**
+         * @return Configuration of animation duration.
+         */
+        public long getAnimationDuration() {
+            return mAnimationDuration;
+        }
     }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index 80049e8..35abfd4 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -43,6 +43,7 @@
 import android.util.MathUtils;
 import android.util.Slog;
 import android.util.TypedValue;
+import android.view.Display;
 import android.view.GestureDetector;
 import android.view.GestureDetector.SimpleOnGestureListener;
 import android.view.MotionEvent;
@@ -251,14 +252,16 @@
             mScreenStateReceiver.unregister();
         }
         // Check if need to reset when MagnificationGestureHandler is the last magnifying service.
-        mMagnificationController.resetIfNeeded(
+        mMagnificationController.resetAllIfNeeded(
                 AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
         clearAndTransitionToStateDetecting();
     }
 
     void notifyShortcutTriggered() {
         if (mDetectShortcutTrigger) {
-            boolean wasMagnifying = mMagnificationController.resetIfNeeded(/* animate */ true);
+            // TODO: multi-display support for magnification gesture handler
+            boolean wasMagnifying = mMagnificationController.resetIfNeeded(Display.DEFAULT_DISPLAY,
+                    /* animate */ true);
             if (wasMagnifying) {
                 clearAndTransitionToStateDetecting();
             } else {
@@ -419,8 +422,9 @@
                 Slog.i(LOG_TAG, "Panned content by scrollX: " + distanceX
                         + " scrollY: " + distanceY);
             }
-            mMagnificationController.offsetMagnifiedRegion(distanceX, distanceY,
-                    AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+            // TODO: multi-display support for magnification gesture handler
+            mMagnificationController.offsetMagnifiedRegion(Display.DEFAULT_DISPLAY, distanceX,
+                    distanceY, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
             return /* event consumed: */ true;
         }
 
@@ -436,7 +440,8 @@
                 return mScaling;
             }
 
-            final float initialScale = mMagnificationController.getScale();
+            // TODO: multi-display support for magnification gesture handler
+            final float initialScale = mMagnificationController.getScale(Display.DEFAULT_DISPLAY);
             final float targetScale = initialScale * detector.getScaleFactor();
 
             // Don't allow a gesture to move the user further outside the
@@ -458,7 +463,8 @@
             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,
+            // TODO: multi-display support for magnification gesture handler
+            mMagnificationController.setScale(Display.DEFAULT_DISPLAY, scale, pivotX, pivotY, false,
                     AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
             return /* handled: */ true;
         }
@@ -518,8 +524,10 @@
                     }
                     final float eventX = event.getX();
                     final float eventY = event.getY();
-                    if (mMagnificationController.magnificationRegionContains(eventX, eventY)) {
-                        mMagnificationController.setCenter(eventX, eventY,
+                    // TODO: multi-display support for magnification gesture handler
+                    if (mMagnificationController.magnificationRegionContains(
+                            Display.DEFAULT_DISPLAY, eventX, eventY)) {
+                        mMagnificationController.setCenter(Display.DEFAULT_DISPLAY, eventX, eventY,
                                 /* animate */ mLastMoveOutsideMagnifiedRegion,
                                 AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
                         mLastMoveOutsideMagnifiedRegion = false;
@@ -657,8 +665,9 @@
 
                     mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
 
+                    // TODO: multi-display support for magnification gesture handler
                     if (!mMagnificationController.magnificationRegionContains(
-                            event.getX(), event.getY())) {
+                            Display.DEFAULT_DISPLAY, event.getX(), event.getY())) {
 
                         transitionToDelegatingStateAndClear();
 
@@ -671,7 +680,8 @@
                             // If magnified, delay an ACTION_DOWN for mMultiTapMaxDelay
                             // to ensure reachability of
                             // STATE_PANNING_SCALING(triggerable with ACTION_POINTER_DOWN)
-                            || mMagnificationController.isMagnifying()) {
+                            // TODO: multi-display support for magnification gesture handler
+                            || mMagnificationController.isMagnifying(Display.DEFAULT_DISPLAY)) {
 
                         afterMultiTapTimeoutTransitionToDelegatingState();
 
@@ -683,7 +693,8 @@
                 }
                 break;
                 case ACTION_POINTER_DOWN: {
-                    if (mMagnificationController.isMagnifying()) {
+                    // TODO: multi-display support for magnification gesture handler
+                    if (mMagnificationController.isMagnifying(Display.DEFAULT_DISPLAY)) {
                         transitionTo(mPanningScalingState);
                         clear();
                     } else {
@@ -712,8 +723,9 @@
 
                     mHandler.removeMessages(MESSAGE_ON_TRIPLE_TAP_AND_HOLD);
 
+                    // TODO: multi-display support for magnification gesture handler
                     if (!mMagnificationController.magnificationRegionContains(
-                            event.getX(), event.getY())) {
+                            Display.DEFAULT_DISPLAY, event.getX(), event.getY())) {
 
                         transitionToDelegatingStateAndClear();
 
@@ -864,7 +876,8 @@
             clear();
 
             // Toggle zoom
-            if (mMagnificationController.isMagnifying()) {
+            // TODO: multi-display support for magnification gesture handler
+            if (mMagnificationController.isMagnifying(Display.DEFAULT_DISPLAY)) {
                 zoomOff();
             } else {
                 zoomOn(up.getX(), up.getY());
@@ -876,8 +889,9 @@
             if (DEBUG_DETECTING) Slog.i(LOG_TAG, "onTripleTapAndHold()");
             clear();
 
+            // TODO: multi-display support for magnification gesture handler
             mViewportDraggingState.mZoomedInBeforeDrag =
-                    mMagnificationController.isMagnifying();
+                    mMagnificationController.isMagnifying(Display.DEFAULT_DISPLAY);
 
             zoomOn(down.getX(), down.getY());
 
@@ -904,7 +918,8 @@
             if (DEBUG_DETECTING) Slog.i(LOG_TAG, "setShortcutTriggered(" + state + ")");
 
             mShortcutTriggered = state;
-            mMagnificationController.setForceShowMagnifiableBounds(state);
+            // TODO: multi-display support for magnification gesture handler
+            mMagnificationController.setForceShowMagnifiableBounds(Display.DEFAULT_DISPLAY, state);
         }
     }
 
@@ -914,7 +929,8 @@
         final float scale = MathUtils.constrain(
                 mMagnificationController.getPersistedScale(),
                 MIN_SCALE, MAX_SCALE);
-        mMagnificationController.setScaleAndCenter(
+        // TODO: multi-display support for magnification gesture handler
+        mMagnificationController.setScaleAndCenter(Display.DEFAULT_DISPLAY,
                 scale, centerX, centerY,
                 /* animate */ true,
                 AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
@@ -922,8 +938,8 @@
 
     private void zoomOff() {
         if (DEBUG_DETECTING) Slog.i(LOG_TAG, "zoomOff()");
-
-        mMagnificationController.reset(/* animate */ true);
+        // TODO: multi-display support for magnification gesture handler
+        mMagnificationController.reset(Display.DEFAULT_DISPLAY, /* animate */ true);
     }
 
     private static MotionEvent recycleAndNullify(@Nullable MotionEvent event) {
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index caebf15..545b69b 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -49,6 +49,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TypedValue;
+import android.view.Display;
 import android.view.MagnificationSpec;
 import android.view.Surface;
 import android.view.Surface.OutOfResourcesException;
@@ -83,23 +84,36 @@
         mService = service;
     }
 
-    private DisplayMagnifier mDisplayMagnifier;
+    private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>();
 
     private WindowsForAccessibilityObserver mWindowsForAccessibilityObserver;
 
-    public void setMagnificationCallbacksLocked(MagnificationCallbacks callbacks) {
+    public boolean setMagnificationCallbacksLocked(int displayId,
+            MagnificationCallbacks callbacks) {
+        boolean result = false;
         if (callbacks != null) {
-            if (mDisplayMagnifier != null) {
+            if (mDisplayMagnifiers.get(displayId) != null) {
                 throw new IllegalStateException("Magnification callbacks already set!");
             }
-            mDisplayMagnifier = new DisplayMagnifier(mService, callbacks);
+            final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+            if (dc != null) {
+                final Display display = dc.getDisplay();
+                if (display != null && display.getType() != Display.TYPE_OVERLAY) {
+                    mDisplayMagnifiers.put(displayId, new DisplayMagnifier(
+                            mService, dc, display, callbacks));
+                    result = true;
+                }
+            }
         } else {
-            if  (mDisplayMagnifier == null) {
+            final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+            if  (displayMagnifier == null) {
                 throw new IllegalStateException("Magnification callbacks already cleared!");
             }
-            mDisplayMagnifier.destroyLocked();
-            mDisplayMagnifier = null;
+            displayMagnifier.destroyLocked();
+            mDisplayMagnifiers.remove(displayId);
+            result = true;
         }
+        return result;
     }
 
     public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) {
@@ -129,58 +143,72 @@
         }
     }
 
-    public void setMagnificationSpecLocked(MagnificationSpec spec) {
-        if (mDisplayMagnifier != null) {
-            mDisplayMagnifier.setMagnificationSpecLocked(spec);
+    public void setMagnificationSpecLocked(int displayId, MagnificationSpec spec) {
+        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+        if (displayMagnifier != null) {
+            displayMagnifier.setMagnificationSpecLocked(spec);
         }
-        if (mWindowsForAccessibilityObserver != null) {
+        // TODO: support multi-display for windows observer
+        if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) {
             mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
         }
     }
 
-    public void getMagnificationRegionLocked(Region outMagnificationRegion) {
-        if (mDisplayMagnifier != null) {
-            mDisplayMagnifier.getMagnificationRegionLocked(outMagnificationRegion);
+    public void getMagnificationRegionLocked(int displayId, Region outMagnificationRegion) {
+        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+        if (displayMagnifier != null) {
+            displayMagnifier.getMagnificationRegionLocked(outMagnificationRegion);
         }
     }
 
-    public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
-        if (mDisplayMagnifier != null) {
-            mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle);
+    public void onRectangleOnScreenRequestedLocked(int displayId, Rect rectangle) {
+        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+        if (displayMagnifier != null) {
+            displayMagnifier.onRectangleOnScreenRequestedLocked(rectangle);
         }
         // Not relevant for the window observer.
     }
 
-    public void onWindowLayersChangedLocked() {
-        if (mDisplayMagnifier != null) {
-            mDisplayMagnifier.onWindowLayersChangedLocked();
+    public void onWindowLayersChangedLocked(int displayId) {
+        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+        if (displayMagnifier != null) {
+            displayMagnifier.onWindowLayersChangedLocked();
         }
-        if (mWindowsForAccessibilityObserver != null) {
+        // TODO: support multi-display for windows observer
+        if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) {
             mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
         }
     }
 
     public void onRotationChangedLocked(DisplayContent displayContent) {
-        if (mDisplayMagnifier != null) {
-            mDisplayMagnifier.onRotationChangedLocked(displayContent);
+        final int displayId = displayContent.getDisplayId();
+        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+        if (displayMagnifier != null) {
+            displayMagnifier.onRotationChangedLocked(displayContent);
         }
-        if (mWindowsForAccessibilityObserver != null) {
+        // TODO: support multi-display for windows observer
+        if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) {
             mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
         }
     }
 
     public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
-        if (mDisplayMagnifier != null) {
-            mDisplayMagnifier.onAppWindowTransitionLocked(windowState, transition);
+        final int displayId = windowState.getDisplayId();
+        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+        if (displayMagnifier != null) {
+            displayMagnifier.onAppWindowTransitionLocked(windowState, transition);
         }
         // Not relevant for the window observer.
     }
 
     public void onWindowTransitionLocked(WindowState windowState, int transition) {
-        if (mDisplayMagnifier != null) {
-            mDisplayMagnifier.onWindowTransitionLocked(windowState, transition);
+        final int displayId = windowState.getDisplayId();
+        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+        if (displayMagnifier != null) {
+            displayMagnifier.onWindowTransitionLocked(windowState, transition);
         }
-        if (mWindowsForAccessibilityObserver != null) {
+        // TODO: support multi-display for windows observer
+        if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) {
             mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
         }
     }
@@ -197,7 +225,6 @@
         }
     }
 
-
     public void onSomeWindowResizedOrMovedLocked() {
         // Not relevant for the display magnifier.
 
@@ -207,29 +234,34 @@
     }
 
     /** NOTE: This has to be called within a surface transaction. */
-    public void drawMagnifiedRegionBorderIfNeededLocked() {
-        if (mDisplayMagnifier != null) {
-            mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
+    public void drawMagnifiedRegionBorderIfNeededLocked(int displayId) {
+        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+        if (displayMagnifier != null) {
+            displayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
         }
         // Not relevant for the window observer.
     }
 
     public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
-        if (mDisplayMagnifier != null) {
-            return mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState);
+        final int displayId = windowState.getDisplayId();
+        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+        if (displayMagnifier != null) {
+            return displayMagnifier.getMagnificationSpecForWindowLocked(windowState);
         }
         return null;
     }
 
     public boolean hasCallbacksLocked() {
-        return (mDisplayMagnifier != null
+        // TODO: support multi-display for windows observer
+        return (mDisplayMagnifiers.size() > 0
                 || mWindowsForAccessibilityObserver != null);
     }
 
-    public void setForceShowMagnifiableBoundsLocked(boolean show) {
-        if (mDisplayMagnifier != null) {
-            mDisplayMagnifier.setForceShowMagnifiableBoundsLocked(show);
-            mDisplayMagnifier.showMagnificationBoundsIfNeeded();
+    public void setForceShowMagnifiableBoundsLocked(int displayId, boolean show) {
+        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+        if (displayMagnifier != null) {
+            displayMagnifier.setForceShowMagnifiableBoundsLocked(show);
+            displayMagnifier.showMagnificationBoundsIfNeeded();
         }
     }
 
@@ -263,6 +295,8 @@
         private final WindowManagerService mService;
         private final MagnifiedViewport mMagnifedViewport;
         private final Handler mHandler;
+        private final DisplayContent mDisplayContent;
+        private final Display mDisplay;
 
         private final MagnificationCallbacks mCallbacks;
 
@@ -271,10 +305,14 @@
         private boolean mForceShowMagnifiableBounds = false;
 
         public DisplayMagnifier(WindowManagerService windowManagerService,
+                DisplayContent displayContent,
+                Display display,
                 MagnificationCallbacks callbacks) {
             mContext = windowManagerService.mContext;
             mService = windowManagerService;
             mCallbacks = callbacks;
+            mDisplayContent = displayContent;
+            mDisplay = display;
             mHandler = new MyHandler(mService.mH.getLooper());
             mMagnifedViewport = new MagnifiedViewport();
             mLongAnimationDuration = mContext.getResources().getInteger(
@@ -285,7 +323,7 @@
             mMagnifedViewport.updateMagnificationSpecLocked(spec);
             mMagnifedViewport.recomputeBoundsLocked();
 
-            mService.applyMagnificationSpec(spec);
+            mService.applyMagnificationSpecLocked(mDisplay.getDisplayId(), spec);
             mService.scheduleAnimationLocked();
         }
 
@@ -482,7 +520,7 @@
 
                 if (mContext.getResources().getConfiguration().isScreenRound()) {
                     mCircularPath = new Path();
-                    mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
+                    mDisplay.getRealSize(mTempPoint);
                     final int centerXY = mTempPoint.x / 2;
                     mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW);
                 } else {
@@ -512,7 +550,7 @@
             }
 
             public void recomputeBoundsLocked() {
-                mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
+                mDisplay.getRealSize(mTempPoint);
                 final int screenWidth = mTempPoint.x;
                 final int screenHeight = mTempPoint.y;
 
@@ -671,9 +709,8 @@
             }
 
             private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
-                final DisplayContent dc = mService.getDefaultDisplayContentLocked();
                 mTempLayer = 0;
-                dc.forAllWindows((w) -> {
+                mDisplayContent.forAllWindows((w) -> {
                     if (w.isOnScreen() && w.isVisibleLw()
                             && (w.mAttrs.alpha != 0)
                             && !w.mWinAnimator.mEnterAnimationPending) {
@@ -703,8 +740,9 @@
                 public ViewportWindow(Context context) {
                     SurfaceControl surfaceControl = null;
                     try {
-                        mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
-                        surfaceControl = mService.getDefaultDisplayContentLocked().makeOverlay()
+                        mDisplay.getRealSize(mTempPoint);
+                        surfaceControl = mDisplayContent
+                                .makeOverlay()
                                 .setName(SURFACE_TITLE)
                                 .setBufferSize(mTempPoint.x, mTempPoint.y) // not a typo
                                 .setFormat(PixelFormat.TRANSLUCENT)
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 711ca00..9361e7f 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -25,7 +25,6 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
@@ -591,9 +590,7 @@
                     delayed = runningAppAnimation = true;
                 }
                 final WindowState window = findMainWindow();
-                //TODO (multidisplay): Magnification is supported only for the default display.
-                if (window != null && accessibilityController != null
-                        && getDisplayContent().getDisplayId() == DEFAULT_DISPLAY) {
+                if (window != null && accessibilityController != null) {
                     accessibilityController.onAppWindowTransitionLocked(window, transit);
                 }
                 changed = true;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 8026a04..ac1159a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1466,11 +1466,9 @@
             }
         }
 
-        // TODO (multi-display): Magnification is supported only for the default display.
         // Announce rotation only if we will not animate as we already have the
         // windows in final state. Otherwise, we make this call at the rotation end.
-        if (screenRotationAnimation == null && mWmService.mAccessibilityController != null
-                && isDefaultDisplay) {
+        if (screenRotationAnimation == null && mWmService.mAccessibilityController != null) {
             mWmService.mAccessibilityController.onRotationChangedLocked(this);
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index b8a0739..b8db98b 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -167,13 +167,11 @@
                             screenRotationAnimation.kill();
                             displayAnimator.mScreenRotationAnimation = null;
 
-                            //TODO (multidisplay): Accessibility supported only for the default
                             // display.
-                            if (accessibilityController != null && dc.isDefaultDisplay) {
+                            if (accessibilityController != null) {
                                 // We just finished rotation animation which means we did not
                                 // announce the rotation and waited for it to end, announce now.
-                                accessibilityController.onRotationChangedLocked(
-                                        mService.getDefaultDisplayContentLocked());
+                                accessibilityController.onRotationChangedLocked(dc);
                             }
                         }
                     }
@@ -197,9 +195,8 @@
                         screenRotationAnimation.updateSurfaces(mTransaction);
                     }
                     orAnimating(dc.getDockedDividerController().animate(mCurrentTime));
-                    //TODO (multidisplay): Magnification is supported only for the default display.
-                    if (accessibilityController != null && dc.isDefaultDisplay) {
-                        accessibilityController.drawMagnifiedRegionBorderIfNeededLocked();
+                    if (accessibilityController != null) {
+                        accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId);
                     }
                 }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 1691dc0..5267e7e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -212,34 +212,40 @@
      * and has access to the raw window data while the accessibility layer serves
      * as a controller.
      *
+     * @param displayId The logical display id.
      * @param callbacks The callbacks to invoke.
+     * @return {@code false} if display id is not valid.
      */
-    public abstract void setMagnificationCallbacks(@Nullable MagnificationCallbacks callbacks);
+    public abstract boolean setMagnificationCallbacks(int displayId,
+            @Nullable MagnificationCallbacks callbacks);
 
     /**
      * Set by the accessibility layer to specify the magnification and panning to
      * be applied to all windows that should be magnified.
      *
+     * @param displayId The logical display id.
      * @param spec The MagnficationSpec to set.
      *
-     * @see #setMagnificationCallbacks(MagnificationCallbacks)
+     * @see #setMagnificationCallbacks(int, MagnificationCallbacks)
      */
-    public abstract void setMagnificationSpec(MagnificationSpec spec);
+    public abstract void setMagnificationSpec(int displayId, MagnificationSpec spec);
 
     /**
      * Set by the accessibility framework to indicate whether the magnifiable regions of the display
      * should be shown.
      *
+     * @param displayId The logical display id.
      * @param show {@code true} to show magnifiable region bounds, {@code false} to hide
      */
-    public abstract void setForceShowMagnifiableBounds(boolean show);
+    public abstract void setForceShowMagnifiableBounds(int displayId, boolean show);
 
     /**
      * Obtains the magnification regions.
      *
+     * @param displayId The logical display id.
      * @param magnificationRegion the current magnification region
      */
-    public abstract void getMagnificationRegion(@NonNull Region magnificationRegion);
+    public abstract void getMagnificationRegion(int displayId, @NonNull Region magnificationRegion);
 
     /**
      * Gets the magnification and translation applied to a window given its token.
@@ -251,7 +257,7 @@
      *
      * @return The magnification spec for the window.
      *
-     * @see #setMagnificationCallbacks(MagnificationCallbacks)
+     * @see #setMagnificationCallbacks(int, MagnificationCallbacks)
      */
     public abstract MagnificationSpec getCompatibleMagnificationSpecForWindow(
             IBinder windowToken);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index efb38f5..c8c834f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1846,9 +1846,9 @@
         synchronized (mGlobalLock) {
             if (mAccessibilityController != null) {
                 WindowState window = mWindowMap.get(token);
-                //TODO (multidisplay): Magnification is supported only for the default display.
-                if (window != null && window.getDisplayId() == DEFAULT_DISPLAY) {
-                    mAccessibilityController.onRectangleOnScreenRequestedLocked(rectangle);
+                if (window != null) {
+                    mAccessibilityController.onRectangleOnScreenRequestedLocked(
+                            window.getDisplayId(), rectangle);
                 }
             }
         }
@@ -2237,8 +2237,7 @@
             win.mDestroying = true;
             win.destroySurface(false, stopped);
         }
-        // TODO(multidisplay): Magnification is supported only for the default display.
-        if (mAccessibilityController != null && win.getDisplayId() == DEFAULT_DISPLAY) {
+        if (mAccessibilityController != null) {
             mAccessibilityController.onWindowTransitionLocked(win, transit);
         }
 
@@ -6823,10 +6822,10 @@
         }
 
         @Override
-        public void setMagnificationSpec(MagnificationSpec spec) {
+        public void setMagnificationSpec(int displayId, MagnificationSpec spec) {
             synchronized (mGlobalLock) {
                 if (mAccessibilityController != null) {
-                    mAccessibilityController.setMagnificationSpecLocked(spec);
+                    mAccessibilityController.setMagnificationSpecLocked(displayId, spec);
                 } else {
                     throw new IllegalStateException("Magnification callbacks not set!");
                 }
@@ -6837,10 +6836,10 @@
         }
 
         @Override
-        public void setForceShowMagnifiableBounds(boolean show) {
+        public void setForceShowMagnifiableBounds(int displayId, boolean show) {
             synchronized (mGlobalLock) {
                 if (mAccessibilityController != null) {
-                    mAccessibilityController.setForceShowMagnifiableBoundsLocked(show);
+                    mAccessibilityController.setForceShowMagnifiableBoundsLocked(displayId, show);
                 } else {
                     throw new IllegalStateException("Magnification callbacks not set!");
                 }
@@ -6848,10 +6847,11 @@
         }
 
         @Override
-        public void getMagnificationRegion(@NonNull Region magnificationRegion) {
+        public void getMagnificationRegion(int displayId, @NonNull Region magnificationRegion) {
             synchronized (mGlobalLock) {
                 if (mAccessibilityController != null) {
-                    mAccessibilityController.getMagnificationRegionLocked(magnificationRegion);
+                    mAccessibilityController.getMagnificationRegionLocked(displayId,
+                            magnificationRegion);
                 } else {
                     throw new IllegalStateException("Magnification callbacks not set!");
                 }
@@ -6879,16 +6879,19 @@
         }
 
         @Override
-        public void setMagnificationCallbacks(@Nullable MagnificationCallbacks callbacks) {
+        public boolean setMagnificationCallbacks(int displayId,
+                @Nullable MagnificationCallbacks callbacks) {
             synchronized (mGlobalLock) {
                 if (mAccessibilityController == null) {
                     mAccessibilityController = new AccessibilityController(
                             WindowManagerService.this);
                 }
-                mAccessibilityController.setMagnificationCallbacksLocked(callbacks);
+                boolean result = mAccessibilityController.setMagnificationCallbacksLocked(
+                        displayId, callbacks);
                 if (!mAccessibilityController.hasCallbacksLocked()) {
                     mAccessibilityController = null;
                 }
+                return result;
             }
         }
 
@@ -7267,8 +7270,12 @@
         }, false /* traverseTopToBottom */);
     }
 
-    public void applyMagnificationSpec(MagnificationSpec spec) {
-        getDefaultDisplayContentLocked().applyMagnificationSpec(spec);
+    /** Called from Accessibility Controller to apply magnification spec */
+    public void applyMagnificationSpecLocked(int displayId, MagnificationSpec spec) {
+        final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+        if (displayContent != null) {
+            displayContent.applyMagnificationSpec(spec);
+        }
     }
 
     SurfaceControl.Builder makeSurfaceBuilder(SurfaceSession s) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 8f86c00..ce5eb84 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1607,8 +1607,7 @@
                         mWmService.mAccessibilityController;
                 final int winTransit = TRANSIT_EXIT;
                 mWinAnimator.applyAnimationLocked(winTransit, false /* isEntrance */);
-                //TODO (multidisplay): Magnification is supported only for the default
-                if (accessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
+                if (accessibilityController != null) {
                     accessibilityController.onWindowTransitionLocked(this, winTransit);
                 }
             }
@@ -1625,8 +1624,7 @@
 
         if (isVisibleNow()) {
             mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
-            //TODO (multidisplay): Magnification is supported only for the default
-            if (mWmService.mAccessibilityController != null && isDefaultDisplay()) {
+            if (mWmService.mAccessibilityController != null) {
                 mWmService.mAccessibilityController.onWindowTransitionLocked(this, TRANSIT_EXIT);
             }
             changed = true;
@@ -1915,9 +1913,7 @@
                         setDisplayLayoutNeeded();
                         mWmService.requestTraversal();
                     }
-                    //TODO (multidisplay): Magnification is supported only for the default display.
-                    if (mWmService.mAccessibilityController != null
-                            && displayId == DEFAULT_DISPLAY) {
+                    if (mWmService.mAccessibilityController != null) {
                         mWmService.mAccessibilityController.onWindowTransitionLocked(this, transit);
                     }
                 }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index fb5c556..6b4d6d2 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
 import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
@@ -1308,9 +1307,7 @@
             transit = WindowManagerPolicy.TRANSIT_SHOW;
         }
         applyAnimationLocked(transit, true);
-        //TODO (multidisplay): Magnification is supported only for the default display.
-        if (mService.mAccessibilityController != null
-                && mWin.getDisplayId() == DEFAULT_DISPLAY) {
+        if (mService.mAccessibilityController != null) {
             mService.mAccessibilityController.onWindowTransitionLocked(mWin, transit);
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
index feffeef..773b877 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
@@ -20,7 +20,9 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThat;
 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.Matchers.anyObject;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
@@ -36,19 +38,15 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.IntentFilter;
-import android.content.res.Resources;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.Region;
-import android.os.Handler;
 import android.os.Looper;
-import android.os.Message;
 import android.view.MagnificationSpec;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.R;
 import com.android.server.wm.WindowManagerInternal;
 import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
 
@@ -81,48 +79,36 @@
     static final Region OTHER_REGION = new Region(OTHER_MAGNIFICATION_BOUNDS);
     static final int SERVICE_ID_1 = 1;
     static final int SERVICE_ID_2 = 2;
+    static final int DISPLAY_0 = 0;
+    static final int DISPLAY_1 = 1;
+    static final int DISPLAY_COUNT = 2;
+    static final int INVALID_DISPLAY = 2;
 
+    final MagnificationController.ControllerContext mMockControllerCtx =
+            mock(MagnificationController.ControllerContext.class);
     final Context mMockContext = mock(Context.class);
     final AccessibilityManagerService mMockAms = mock(AccessibilityManagerService.class);
     final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class);
-    final MessageCapturingHandler mMessageCapturingHandler =
-            new MessageCapturingHandler(null);
+    final MessageCapturingHandler mMessageCapturingHandler = new MessageCapturingHandler(null);
 
-    final ValueAnimator mMockValueAnimator = mock(ValueAnimator.class);
-    MagnificationController.SettingsBridge mMockSettingsBridge;
+    ValueAnimator mMockValueAnimator;
+    ValueAnimator.AnimatorUpdateListener mTargetAnimationListener;
 
     MagnificationController mMagnificationController;
-    ValueAnimator.AnimatorUpdateListener mTargetAnimationListener;
 
     @Before
     public void setUp() {
         Looper looper = InstrumentationRegistry.getContext().getMainLooper();
         // Pretending ID of the Thread associated with looper as main thread ID in controller
         when(mMockContext.getMainLooper()).thenReturn(looper);
-        Resources mockResources = mock(Resources.class);
-        when(mMockContext.getResources()).thenReturn(mockResources);
-        when(mockResources.getInteger(R.integer.config_longAnimTime))
-                .thenReturn(1000);
-        mMockSettingsBridge = mock(MagnificationController.SettingsBridge.class);
-        mMagnificationController = new MagnificationController(mMockContext, mMockAms, new Object(),
-                mMessageCapturingHandler, mMockWindowManager, mMockValueAnimator,
-                mMockSettingsBridge);
+        when(mMockControllerCtx.getContext()).thenReturn(mMockContext);
+        when(mMockControllerCtx.getAms()).thenReturn(mMockAms);
+        when(mMockControllerCtx.getWindowManager()).thenReturn(mMockWindowManager);
+        when(mMockControllerCtx.getHandler()).thenReturn(mMessageCapturingHandler);
+        when(mMockControllerCtx.getAnimationDuration()).thenReturn(1000L);
+        initMockWindowManager();
 
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocationOnMock) throws Throwable {
-                Object[] args = invocationOnMock.getArguments();
-                Region regionArg = (Region) args[0];
-                regionArg.set(INITIAL_MAGNIFICATION_REGION);
-                return null;
-            }
-        }).when(mMockWindowManager).getMagnificationRegion((Region) anyObject());
-
-        ArgumentCaptor<ValueAnimator.AnimatorUpdateListener> listenerArgumentCaptor =
-                ArgumentCaptor.forClass(ValueAnimator.AnimatorUpdateListener.class);
-        verify(mMockValueAnimator).addUpdateListener(listenerArgumentCaptor.capture());
-        mTargetAnimationListener = listenerArgumentCaptor.getValue();
-        Mockito.reset(mMockValueAnimator); // Ignore other initialization
+        mMagnificationController = new MagnificationController(mMockControllerCtx, new Object());
     }
 
     @After
@@ -133,88 +119,132 @@
 
     @Test
     public void testRegister_WindowManagerAndContextRegisterListeners() {
-        mMagnificationController.register();
+        register(DISPLAY_0);
+        register(DISPLAY_1);
+        register(INVALID_DISPLAY);
         verify(mMockContext).registerReceiver(
                 (BroadcastReceiver) anyObject(), (IntentFilter) anyObject());
-        verify(mMockWindowManager).setMagnificationCallbacks((MagnificationCallbacks) anyObject());
-        assertTrue(mMagnificationController.isRegisteredLocked());
+        verify(mMockWindowManager).setMagnificationCallbacks(
+                eq(DISPLAY_0), (MagnificationCallbacks) anyObject());
+        verify(mMockWindowManager).setMagnificationCallbacks(
+                eq(DISPLAY_1), (MagnificationCallbacks) anyObject());
+        verify(mMockWindowManager).setMagnificationCallbacks(
+                eq(INVALID_DISPLAY), (MagnificationCallbacks) anyObject());
+        assertTrue(mMagnificationController.isRegistered(DISPLAY_0));
+        assertTrue(mMagnificationController.isRegistered(DISPLAY_1));
+        assertFalse(mMagnificationController.isRegistered(INVALID_DISPLAY));
     }
 
     @Test
     public void testRegister_WindowManagerAndContextUnregisterListeners() {
-        mMagnificationController.register();
-        mMagnificationController.unregister();
-
+        register(DISPLAY_0);
+        register(DISPLAY_1);
+        mMagnificationController.unregister(DISPLAY_0);
+        verify(mMockContext, times(0)).unregisterReceiver((BroadcastReceiver) anyObject());
+        mMagnificationController.unregister(DISPLAY_1);
         verify(mMockContext).unregisterReceiver((BroadcastReceiver) anyObject());
-        verify(mMockWindowManager).setMagnificationCallbacks(null);
-        assertFalse(mMagnificationController.isRegisteredLocked());
+        verify(mMockWindowManager).setMagnificationCallbacks(eq(DISPLAY_0), eq(null));
+        verify(mMockWindowManager).setMagnificationCallbacks(eq(DISPLAY_1), eq(null));
+        assertFalse(mMagnificationController.isRegistered(DISPLAY_0));
+        assertFalse(mMagnificationController.isRegistered(DISPLAY_1));
     }
 
     @Test
     public void testInitialState_noMagnificationAndMagnificationRegionReadFromWindowManager() {
-        mMagnificationController.register();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            initialState_noMagnificationAndMagnificationRegionReadFromWindowManager(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void initialState_noMagnificationAndMagnificationRegionReadFromWindowManager(
+            int displayId) {
+        register(displayId);
         MagnificationSpec expectedInitialSpec = getMagnificationSpec(1.0f, 0.0f, 0.0f);
         Region initialMagRegion = new Region();
         Rect initialBounds = new Rect();
 
-        assertEquals(expectedInitialSpec, getCurrentMagnificationSpec());
-        mMagnificationController.getMagnificationRegion(initialMagRegion);
-        mMagnificationController.getMagnificationBounds(initialBounds);
+        assertEquals(expectedInitialSpec, getCurrentMagnificationSpec(displayId));
+        mMagnificationController.getMagnificationRegion(displayId, initialMagRegion);
+        mMagnificationController.getMagnificationBounds(displayId, initialBounds);
         assertEquals(INITIAL_MAGNIFICATION_REGION, initialMagRegion);
         assertEquals(INITIAL_MAGNIFICATION_BOUNDS, initialBounds);
         assertEquals(INITIAL_MAGNIFICATION_BOUNDS.centerX(),
-                mMagnificationController.getCenterX(), 0.0f);
+                mMagnificationController.getCenterX(displayId), 0.0f);
         assertEquals(INITIAL_MAGNIFICATION_BOUNDS.centerY(),
-                mMagnificationController.getCenterY(), 0.0f);
+                mMagnificationController.getCenterY(displayId), 0.0f);
     }
 
     @Test
     public void testNotRegistered_publicMethodsShouldBeBenign() {
-        assertFalse(mMagnificationController.isMagnifying());
-        assertFalse(mMagnificationController.magnificationRegionContains(100, 100));
-        assertFalse(mMagnificationController.reset(true));
-        assertFalse(mMagnificationController.setScale(2, 100, 100, true, 0));
-        assertFalse(mMagnificationController.setCenter(100, 100, false, 1));
-        assertFalse(mMagnificationController.setScaleAndCenter(1.5f, 100, 100, false, 2));
-        assertTrue(mMagnificationController.getIdOfLastServiceToMagnify() < 0);
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            notRegistered_publicMethodsShouldBeBenign(i);
+            resetMockWindowManager();
+        }
+    }
 
-        mMagnificationController.getMagnificationRegion(new Region());
-        mMagnificationController.getMagnificationBounds(new Rect());
-        mMagnificationController.getScale();
-        mMagnificationController.getOffsetX();
-        mMagnificationController.getOffsetY();
-        mMagnificationController.getCenterX();
-        mMagnificationController.getCenterY();
-        mMagnificationController.offsetMagnifiedRegion(50, 50, 1);
-        mMagnificationController.unregister();
+    private void notRegistered_publicMethodsShouldBeBenign(int displayId) {
+        assertFalse(mMagnificationController.isMagnifying(displayId));
+        assertFalse(mMagnificationController.magnificationRegionContains(displayId, 100, 100));
+        assertFalse(mMagnificationController.reset(displayId, true));
+        assertFalse(mMagnificationController.setScale(displayId, 2, 100, 100, true, 0));
+        assertFalse(mMagnificationController.setCenter(displayId, 100, 100, false, 1));
+        assertFalse(mMagnificationController.setScaleAndCenter(displayId,
+                1.5f, 100, 100, false, 2));
+        assertTrue(mMagnificationController.getIdOfLastServiceToMagnify(displayId) < 0);
+
+        mMagnificationController.getMagnificationRegion(displayId, new Region());
+        mMagnificationController.getMagnificationBounds(displayId, new Rect());
+        mMagnificationController.getScale(displayId);
+        mMagnificationController.getOffsetX(displayId);
+        mMagnificationController.getOffsetY(displayId);
+        mMagnificationController.getCenterX(displayId);
+        mMagnificationController.getCenterY(displayId);
+        mMagnificationController.offsetMagnifiedRegion(displayId, 50, 50, 1);
+        mMagnificationController.unregister(displayId);
     }
 
     @Test
     public void testSetScale_noAnimation_shouldGoStraightToWindowManagerAndUpdateState() {
-        mMagnificationController.register();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            setScale_noAnimation_shouldGoStraightToWindowManagerAndUpdateState(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void setScale_noAnimation_shouldGoStraightToWindowManagerAndUpdateState(int displayId) {
+        register(displayId);
         final float scale = 2.0f;
         final PointF center = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
         final PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, center, scale);
         assertTrue(mMagnificationController
-                .setScale(scale, center.x, center.y, false, SERVICE_ID_1));
+                .setScale(displayId, scale, center.x, center.y, false, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
 
         final MagnificationSpec expectedSpec = getMagnificationSpec(scale, offsets);
-        verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedSpec)));
-        assertThat(getCurrentMagnificationSpec(), closeTo(expectedSpec));
-        assertEquals(center.x, mMagnificationController.getCenterX(), 0.0);
-        assertEquals(center.y, mMagnificationController.getCenterY(), 0.0);
+        verify(mMockWindowManager).setMagnificationSpec(
+                eq(displayId), argThat(closeTo(expectedSpec)));
+        assertThat(getCurrentMagnificationSpec(displayId), closeTo(expectedSpec));
+        assertEquals(center.x, mMagnificationController.getCenterX(displayId), 0.0);
+        assertEquals(center.y, mMagnificationController.getCenterY(displayId), 0.0);
         verify(mMockValueAnimator, times(0)).start();
     }
 
     @Test
     public void testSetScale_withPivotAndAnimation_stateChangesAndAnimationHappens() {
-        mMagnificationController.register();
-        MagnificationSpec startSpec = getCurrentMagnificationSpec();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            setScale_withPivotAndAnimation_stateChangesAndAnimationHappens(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void setScale_withPivotAndAnimation_stateChangesAndAnimationHappens(int displayId) {
+        register(displayId);
+        MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
         float scale = 2.0f;
         PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
         assertTrue(mMagnificationController
-                .setScale(scale, pivotPoint.x, pivotPoint.y, true, SERVICE_ID_1));
+                .setScale(displayId, scale, pivotPoint.x, pivotPoint.y, true, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
 
         // New center should be halfway between original center and pivot
@@ -223,467 +253,645 @@
         PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
         MagnificationSpec endSpec = getMagnificationSpec(scale, offsets);
 
-        assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5);
-        assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.5);
-        assertThat(getCurrentMagnificationSpec(), closeTo(endSpec));
+        assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.5);
+        assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.5);
+        assertThat(getCurrentMagnificationSpec(displayId), closeTo(endSpec));
         verify(mMockValueAnimator).start();
 
         // Initial value
         when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f);
         mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
-        verify(mMockWindowManager).setMagnificationSpec(startSpec);
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId), eq(startSpec));
 
         // Intermediate point
         Mockito.reset(mMockWindowManager);
         float fraction = 0.5f;
         when(mMockValueAnimator.getAnimatedFraction()).thenReturn(fraction);
         mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
-        verify(mMockWindowManager).setMagnificationSpec(
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
                 argThat(closeTo(getInterpolatedMagSpec(startSpec, endSpec, fraction))));
 
         // Final value
         Mockito.reset(mMockWindowManager);
         when(mMockValueAnimator.getAnimatedFraction()).thenReturn(1.0f);
         mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
-        verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(endSpec)));
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec)));
     }
 
     @Test
     public void testSetCenter_whileMagnifying_noAnimation_centerMoves() {
-        mMagnificationController.register();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            setCenter_whileMagnifying_noAnimation_centerMoves(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void setCenter_whileMagnifying_noAnimation_centerMoves(int displayId) {
+        register(displayId);
         // First zoom in
         float scale = 2.0f;
-        assertTrue(mMagnificationController.setScale(scale,
+        assertTrue(mMagnificationController.setScale(displayId, scale,
                 INITIAL_MAGNIFICATION_BOUNDS.centerX(), INITIAL_MAGNIFICATION_BOUNDS.centerY(),
                 false, SERVICE_ID_1));
         Mockito.reset(mMockWindowManager);
 
         PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
         assertTrue(mMagnificationController
-                .setCenter(newCenter.x, newCenter.y, false, SERVICE_ID_1));
+                .setCenter(displayId, newCenter.x, newCenter.y, false, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
         PointF expectedOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
         MagnificationSpec expectedSpec = getMagnificationSpec(scale, expectedOffsets);
 
-        verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedSpec)));
-        assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.0);
-        assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.0);
+        verify(mMockWindowManager).setMagnificationSpec(
+                eq(displayId), argThat(closeTo(expectedSpec)));
+        assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.0);
+        assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.0);
         verify(mMockValueAnimator, times(0)).start();
     }
 
     @Test
     public void testSetScaleAndCenter_animated_stateChangesAndAnimationHappens() {
-        mMagnificationController.register();
-        MagnificationSpec startSpec = getCurrentMagnificationSpec();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            setScaleAndCenter_animated_stateChangesAndAnimationHappens(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void setScaleAndCenter_animated_stateChangesAndAnimationHappens(int displayId) {
+        register(displayId);
+        MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
         float scale = 2.5f;
         PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
         PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
         MagnificationSpec endSpec = getMagnificationSpec(scale, offsets);
 
-        assertTrue(mMagnificationController.setScaleAndCenter(scale, newCenter.x, newCenter.y,
-                true, SERVICE_ID_1));
+        assertTrue(mMagnificationController.setScaleAndCenter(displayId, scale, newCenter.x,
+                newCenter.y, true, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
 
-        assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5);
-        assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.5);
-        assertThat(getCurrentMagnificationSpec(), closeTo(endSpec));
-        verify(mMockAms).notifyMagnificationChanged(
+        assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.5);
+        assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.5);
+        assertThat(getCurrentMagnificationSpec(displayId), closeTo(endSpec));
+        verify(mMockAms).notifyMagnificationChanged(displayId,
                 INITIAL_MAGNIFICATION_REGION, scale, newCenter.x, newCenter.y);
         verify(mMockValueAnimator).start();
 
         // Initial value
         when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f);
         mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
-        verify(mMockWindowManager).setMagnificationSpec(startSpec);
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId), eq(startSpec));
 
         // Intermediate point
         Mockito.reset(mMockWindowManager);
         float fraction = 0.33f;
         when(mMockValueAnimator.getAnimatedFraction()).thenReturn(fraction);
         mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
-        verify(mMockWindowManager).setMagnificationSpec(
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
                 argThat(closeTo(getInterpolatedMagSpec(startSpec, endSpec, fraction))));
 
         // Final value
         Mockito.reset(mMockWindowManager);
         when(mMockValueAnimator.getAnimatedFraction()).thenReturn(1.0f);
         mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
-        verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(endSpec)));
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec)));
     }
 
     @Test
     public void testSetScaleAndCenter_scaleOutOfBounds_cappedAtLimits() {
-        mMagnificationController.register();
-        MagnificationSpec startSpec = getCurrentMagnificationSpec();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            setScaleAndCenter_scaleOutOfBounds_cappedAtLimits(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void setScaleAndCenter_scaleOutOfBounds_cappedAtLimits(int displayId) {
+        register(displayId);
+        MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
         PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
         PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter,
                 MagnificationController.MAX_SCALE);
         MagnificationSpec endSpec = getMagnificationSpec(
                 MagnificationController.MAX_SCALE, offsets);
 
-        assertTrue(mMagnificationController.setScaleAndCenter(
+        assertTrue(mMagnificationController.setScaleAndCenter(displayId,
                 MagnificationController.MAX_SCALE + 1.0f,
                 newCenter.x, newCenter.y, false, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
 
-        assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5);
-        assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.5);
-        verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(endSpec)));
+        assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.5);
+        assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.5);
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec)));
         Mockito.reset(mMockWindowManager);
 
         // Verify that we can't zoom below 1x
-        assertTrue(mMagnificationController.setScaleAndCenter(0.5f,
+        assertTrue(mMagnificationController.setScaleAndCenter(displayId, 0.5f,
                 INITIAL_MAGNIFICATION_BOUNDS_CENTER.x, INITIAL_MAGNIFICATION_BOUNDS_CENTER.y,
                 false, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
 
         assertEquals(INITIAL_MAGNIFICATION_BOUNDS_CENTER.x,
-                mMagnificationController.getCenterX(), 0.5);
+                mMagnificationController.getCenterX(displayId), 0.5);
         assertEquals(INITIAL_MAGNIFICATION_BOUNDS_CENTER.y,
-                mMagnificationController.getCenterY(), 0.5);
-        verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(startSpec)));
+                mMagnificationController.getCenterY(displayId), 0.5);
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(startSpec)));
     }
 
     @Test
     public void testSetScaleAndCenter_centerOutOfBounds_cappedAtLimits() {
-        mMagnificationController.register();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            setScaleAndCenter_centerOutOfBounds_cappedAtLimits(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void setScaleAndCenter_centerOutOfBounds_cappedAtLimits(int displayId) {
+        register(displayId);
         float scale = 2.0f;
 
         // Off the edge to the top and left
-        assertTrue(mMagnificationController.setScaleAndCenter(
+        assertTrue(mMagnificationController.setScaleAndCenter(displayId,
                 scale, -100f, -200f, false, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
 
         PointF newCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER;
         PointF newOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
-        assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5);
-        assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.5);
-        verify(mMockWindowManager).setMagnificationSpec(
+        assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.5);
+        assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.5);
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
                 argThat(closeTo(getMagnificationSpec(scale, newOffsets))));
         Mockito.reset(mMockWindowManager);
 
         // Off the edge to the bottom and right
-        assertTrue(mMagnificationController.setScaleAndCenter(scale,
+        assertTrue(mMagnificationController.setScaleAndCenter(displayId, scale,
                 INITIAL_MAGNIFICATION_BOUNDS.right + 1, INITIAL_MAGNIFICATION_BOUNDS.bottom + 1,
                 false, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
         newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
         newOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
-        assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5);
-        assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.5);
-        verify(mMockWindowManager).setMagnificationSpec(
+        assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.5);
+        assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.5);
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
                 argThat(closeTo(getMagnificationSpec(scale, newOffsets))));
     }
 
     @Test
     public void testMagnificationRegionChanged_serviceNotified() {
-        mMagnificationController.register();
-        MagnificationCallbacks callbacks = getMagnificationCallbacks();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            magnificationRegionChanged_serviceNotified(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void magnificationRegionChanged_serviceNotified(int displayId) {
+        register(displayId);
+        MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
         callbacks.onMagnificationRegionChanged(OTHER_REGION);
         mMessageCapturingHandler.sendAllMessages();
-        verify(mMockAms).notifyMagnificationChanged(OTHER_REGION, 1.0f,
+        verify(mMockAms).notifyMagnificationChanged(displayId, OTHER_REGION, 1.0f,
                 OTHER_MAGNIFICATION_BOUNDS.centerX(), OTHER_MAGNIFICATION_BOUNDS.centerY());
     }
 
     @Test
     public void testOffsetMagnifiedRegion_whileMagnifying_offsetsMove() {
-        mMagnificationController.register();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            offsetMagnifiedRegion_whileMagnifying_offsetsMove(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void offsetMagnifiedRegion_whileMagnifying_offsetsMove(int displayId) {
+        register(displayId);
         PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
         float scale = 2.0f;
         PointF startOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, startCenter, scale);
         // First zoom in
         assertTrue(mMagnificationController
-                .setScaleAndCenter(scale, startCenter.x, startCenter.y, false, SERVICE_ID_1));
+                .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, false,
+                        SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
         Mockito.reset(mMockWindowManager);
 
         PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
         PointF newOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
-        mMagnificationController.offsetMagnifiedRegion(
-                startOffsets.x - newOffsets.x, startOffsets.y - newOffsets.y, SERVICE_ID_1);
+        mMagnificationController.offsetMagnifiedRegion(displayId,
+                startOffsets.x - newOffsets.x, startOffsets.y - newOffsets.y,
+                SERVICE_ID_1);
         mMessageCapturingHandler.sendAllMessages();
 
         MagnificationSpec expectedSpec = getMagnificationSpec(scale, newOffsets);
-        verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedSpec)));
-        assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.0);
-        assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.0);
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
+                argThat(closeTo(expectedSpec)));
+        assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.0);
+        assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.0);
         verify(mMockValueAnimator, times(0)).start();
     }
 
     @Test
     public void testOffsetMagnifiedRegion_whileNotMagnifying_hasNoEffect() {
-        mMagnificationController.register();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            offsetMagnifiedRegion_whileNotMagnifying_hasNoEffect(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void offsetMagnifiedRegion_whileNotMagnifying_hasNoEffect(int displayId) {
+        register(displayId);
         Mockito.reset(mMockWindowManager);
-        MagnificationSpec startSpec = getCurrentMagnificationSpec();
-        mMagnificationController.offsetMagnifiedRegion(10, 10, SERVICE_ID_1);
-        assertThat(getCurrentMagnificationSpec(), closeTo(startSpec));
-        mMagnificationController.offsetMagnifiedRegion(-10, -10, SERVICE_ID_1);
-        assertThat(getCurrentMagnificationSpec(), closeTo(startSpec));
+        MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
+        mMagnificationController.offsetMagnifiedRegion(displayId, 10, 10, SERVICE_ID_1);
+        assertThat(getCurrentMagnificationSpec(displayId), closeTo(startSpec));
+        mMagnificationController.offsetMagnifiedRegion(displayId, -10, -10, SERVICE_ID_1);
+        assertThat(getCurrentMagnificationSpec(displayId), closeTo(startSpec));
         verifyNoMoreInteractions(mMockWindowManager);
     }
 
     @Test
     public void testOffsetMagnifiedRegion_whileMagnifyingButAtEdge_hasNoEffect() {
-        mMagnificationController.register();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            offsetMagnifiedRegion_whileMagnifyingButAtEdge_hasNoEffect(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void offsetMagnifiedRegion_whileMagnifyingButAtEdge_hasNoEffect(int displayId) {
+        register(displayId);
         float scale = 2.0f;
 
         // Upper left edges
         PointF ulCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER;
         assertTrue(mMagnificationController
-                .setScaleAndCenter(scale, ulCenter.x, ulCenter.y, false, SERVICE_ID_1));
+                .setScaleAndCenter(displayId, scale, ulCenter.x, ulCenter.y, false,
+                        SERVICE_ID_1));
         Mockito.reset(mMockWindowManager);
-        MagnificationSpec ulSpec = getCurrentMagnificationSpec();
-        mMagnificationController.offsetMagnifiedRegion(-10, -10, SERVICE_ID_1);
-        assertThat(getCurrentMagnificationSpec(), closeTo(ulSpec));
+        MagnificationSpec ulSpec = getCurrentMagnificationSpec(displayId);
+        mMagnificationController.offsetMagnifiedRegion(displayId, -10, -10,
+                SERVICE_ID_1);
+        assertThat(getCurrentMagnificationSpec(displayId), closeTo(ulSpec));
         verifyNoMoreInteractions(mMockWindowManager);
 
         // Lower right edges
         PointF lrCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
         assertTrue(mMagnificationController
-                .setScaleAndCenter(scale, lrCenter.x, lrCenter.y, false, SERVICE_ID_1));
+                .setScaleAndCenter(displayId, scale, lrCenter.x, lrCenter.y, false,
+                        SERVICE_ID_1));
         Mockito.reset(mMockWindowManager);
-        MagnificationSpec lrSpec = getCurrentMagnificationSpec();
-        mMagnificationController.offsetMagnifiedRegion(10, 10, SERVICE_ID_1);
-        assertThat(getCurrentMagnificationSpec(), closeTo(lrSpec));
+        MagnificationSpec lrSpec = getCurrentMagnificationSpec(displayId);
+        mMagnificationController.offsetMagnifiedRegion(displayId, 10, 10,
+                SERVICE_ID_1);
+        assertThat(getCurrentMagnificationSpec(displayId), closeTo(lrSpec));
         verifyNoMoreInteractions(mMockWindowManager);
     }
 
     @Test
     public void testGetIdOfLastServiceToChange_returnsCorrectValue() {
-        mMagnificationController.register();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            getIdOfLastServiceToChange_returnsCorrectValue(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void getIdOfLastServiceToChange_returnsCorrectValue(int displayId) {
+        register(displayId);
         PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
         assertTrue(mMagnificationController
-                .setScale(2.0f, startCenter.x, startCenter.y, false, SERVICE_ID_1));
-        assertEquals(SERVICE_ID_1, mMagnificationController.getIdOfLastServiceToMagnify());
+                .setScale(displayId, 2.0f, startCenter.x, startCenter.y, false,
+                        SERVICE_ID_1));
+        assertEquals(SERVICE_ID_1, mMagnificationController.getIdOfLastServiceToMagnify(displayId));
         assertTrue(mMagnificationController
-                .setScale(1.5f, startCenter.x, startCenter.y, false, SERVICE_ID_2));
-        assertEquals(SERVICE_ID_2, mMagnificationController.getIdOfLastServiceToMagnify());
+                .setScale(displayId, 1.5f, startCenter.x, startCenter.y, false,
+                        SERVICE_ID_2));
+        assertEquals(SERVICE_ID_2, mMagnificationController.getIdOfLastServiceToMagnify(displayId));
     }
 
     @Test
     public void testResetIfNeeded_resetsOnlyIfLastMagnifyingServiceIsDisabled() {
-        mMagnificationController.register();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            resetIfNeeded_resetsOnlyIfLastMagnifyingServiceIsDisabled(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void resetIfNeeded_resetsOnlyIfLastMagnifyingServiceIsDisabled(int displayId) {
+        register(displayId);
         PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
         mMagnificationController
-                .setScale(2.0f, startCenter.x, startCenter.y, false, SERVICE_ID_1);
+                .setScale(displayId, 2.0f, startCenter.x, startCenter.y, false,
+                        SERVICE_ID_1);
         mMagnificationController
-                .setScale(1.5f, startCenter.x, startCenter.y, false, SERVICE_ID_2);
-        assertFalse(mMagnificationController.resetIfNeeded(SERVICE_ID_1));
-        assertTrue(mMagnificationController.isMagnifying());
-        assertTrue(mMagnificationController.resetIfNeeded(SERVICE_ID_2));
-        assertFalse(mMagnificationController.isMagnifying());
+                .setScale(displayId, 1.5f, startCenter.x, startCenter.y, false,
+                        SERVICE_ID_2);
+        assertFalse(mMagnificationController.resetIfNeeded(displayId, SERVICE_ID_1));
+        assertTrue(mMagnificationController.isMagnifying(displayId));
+        assertTrue(mMagnificationController.resetIfNeeded(displayId, SERVICE_ID_2));
+        assertFalse(mMagnificationController.isMagnifying(displayId));
     }
 
     @Test
     public void testSetUserId_resetsOnlyIfIdChanges() {
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            testSetUserId_resetsOnlyIfIdChanges(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void testSetUserId_resetsOnlyIfIdChanges(int displayId) {
         final int userId1 = 1;
         final int userId2 = 2;
 
-        mMagnificationController.register();
+        register(displayId);
         mMagnificationController.setUserId(userId1);
         PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
         float scale = 2.0f;
-        mMagnificationController.setScale(scale, startCenter.x, startCenter.y, false, SERVICE_ID_1);
+        mMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, false,
+                SERVICE_ID_1);
 
         mMagnificationController.setUserId(userId1);
-        assertTrue(mMagnificationController.isMagnifying());
+        assertTrue(mMagnificationController.isMagnifying(displayId));
         mMagnificationController.setUserId(userId2);
-        assertFalse(mMagnificationController.isMagnifying());
+        assertFalse(mMagnificationController.isMagnifying(displayId));
     }
 
     @Test
     public void testResetIfNeeded_doesWhatItSays() {
-        mMagnificationController.register();
-        zoomIn2xToMiddle();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            testResetIfNeeded_doesWhatItSays(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void testResetIfNeeded_doesWhatItSays(int displayId) {
+        register(displayId);
+        zoomIn2xToMiddle(displayId);
         mMessageCapturingHandler.sendAllMessages();
         reset(mMockAms);
-        assertTrue(mMagnificationController.resetIfNeeded(false));
-        verify(mMockAms).notifyMagnificationChanged(
+        assertTrue(mMagnificationController.resetIfNeeded(displayId, false));
+        verify(mMockAms).notifyMagnificationChanged(eq(displayId),
                 eq(INITIAL_MAGNIFICATION_REGION), eq(1.0f), anyFloat(), anyFloat());
-        assertFalse(mMagnificationController.isMagnifying());
-        assertFalse(mMagnificationController.resetIfNeeded(false));
+        assertFalse(mMagnificationController.isMagnifying(displayId));
+        assertFalse(mMagnificationController.resetIfNeeded(displayId, false));
     }
 
     @Test
     public void testTurnScreenOff_resetsMagnification() {
-        mMagnificationController.register();
+        register(DISPLAY_0);
+        register(DISPLAY_1);
         ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
                 ArgumentCaptor.forClass(BroadcastReceiver.class);
         verify(mMockContext).registerReceiver(
                 broadcastReceiverCaptor.capture(), (IntentFilter) anyObject());
         BroadcastReceiver br = broadcastReceiverCaptor.getValue();
-        zoomIn2xToMiddle();
+        zoomIn2xToMiddle(DISPLAY_0);
+        zoomIn2xToMiddle(DISPLAY_1);
         mMessageCapturingHandler.sendAllMessages();
         br.onReceive(mMockContext, null);
         mMessageCapturingHandler.sendAllMessages();
-        assertFalse(mMagnificationController.isMagnifying());
+        assertFalse(mMagnificationController.isMagnifying(DISPLAY_0));
+        assertFalse(mMagnificationController.isMagnifying(DISPLAY_1));
     }
 
     @Test
     public void testUserContextChange_resetsMagnification() {
-        mMagnificationController.register();
-        MagnificationCallbacks callbacks = getMagnificationCallbacks();
-        zoomIn2xToMiddle();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            contextChange_resetsMagnification(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void contextChange_resetsMagnification(int displayId) {
+        register(displayId);
+        MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
+        zoomIn2xToMiddle(displayId);
         mMessageCapturingHandler.sendAllMessages();
         callbacks.onUserContextChanged();
         mMessageCapturingHandler.sendAllMessages();
-        assertFalse(mMagnificationController.isMagnifying());
+        assertFalse(mMagnificationController.isMagnifying(displayId));
     }
 
     @Test
     public void testRotation_resetsMagnification() {
-        mMagnificationController.register();
-        MagnificationCallbacks callbacks = getMagnificationCallbacks();
-        zoomIn2xToMiddle();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            rotation_resetsMagnification(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void rotation_resetsMagnification(int displayId) {
+        register(displayId);
+        MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
+        zoomIn2xToMiddle(displayId);
         mMessageCapturingHandler.sendAllMessages();
-        assertTrue(mMagnificationController.isMagnifying());
+        assertTrue(mMagnificationController.isMagnifying(displayId));
         callbacks.onRotationChanged(0);
         mMessageCapturingHandler.sendAllMessages();
-        assertFalse(mMagnificationController.isMagnifying());
+        assertFalse(mMagnificationController.isMagnifying(displayId));
     }
 
     @Test
     public void testBoundsChange_whileMagnifyingWithCompatibleSpec_noSpecChange() {
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            boundsChange_whileMagnifyingWithCompatibleSpec_noSpecChange(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void boundsChange_whileMagnifyingWithCompatibleSpec_noSpecChange(int displayId) {
         // Going from a small region to a large one leads to no issues
-        mMagnificationController.register();
-        zoomIn2xToMiddle();
+        register(displayId);
+        zoomIn2xToMiddle(displayId);
         mMessageCapturingHandler.sendAllMessages();
-        MagnificationSpec startSpec = getCurrentMagnificationSpec();
-        MagnificationCallbacks callbacks = getMagnificationCallbacks();
+        MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
+        MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
         Mockito.reset(mMockWindowManager);
         callbacks.onMagnificationRegionChanged(OTHER_REGION_COMPAT);
         mMessageCapturingHandler.sendAllMessages();
-        assertThat(getCurrentMagnificationSpec(), closeTo(startSpec));
+        assertThat(getCurrentMagnificationSpec(displayId), closeTo(startSpec));
         verifyNoMoreInteractions(mMockWindowManager);
     }
 
     @Test
     public void testBoundsChange_whileZoomingWithCompatibleSpec_noSpecChange() {
-        mMagnificationController.register();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            boundsChange_whileZoomingWithCompatibleSpec_noSpecChange(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void boundsChange_whileZoomingWithCompatibleSpec_noSpecChange(int displayId) {
+        register(displayId);
         PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
         float scale = 2.0f;
         // setting animate parameter to true is differ from zoomIn2xToMiddle()
-        mMagnificationController.setScale(scale, startCenter.x, startCenter.y, true, SERVICE_ID_1);
-        MagnificationSpec startSpec = getCurrentMagnificationSpec();
-        MagnificationCallbacks callbacks = getMagnificationCallbacks();
+        mMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, true,
+                SERVICE_ID_1);
+        MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
+        MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
         Mockito.reset(mMockWindowManager);
         callbacks.onMagnificationRegionChanged(OTHER_REGION_COMPAT);
         mMessageCapturingHandler.sendAllMessages();
-        assertThat(getCurrentMagnificationSpec(), closeTo(startSpec));
+        assertThat(getCurrentMagnificationSpec(displayId), closeTo(startSpec));
         verifyNoMoreInteractions(mMockWindowManager);
     }
 
     @Test
     public void testBoundsChange_whileMagnifyingWithIncompatibleSpec_offsetsConstrained() {
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            boundsChange_whileMagnifyingWithIncompatibleSpec_offsetsConstrained(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void boundsChange_whileMagnifyingWithIncompatibleSpec_offsetsConstrained(
+            int displayId) {
         // In a large region, pan to the farthest point possible
-        mMagnificationController.register();
-        MagnificationCallbacks callbacks = getMagnificationCallbacks();
+        register(displayId);
+        MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
         callbacks.onMagnificationRegionChanged(OTHER_REGION);
         mMessageCapturingHandler.sendAllMessages();
         PointF startCenter = OTHER_BOUNDS_LOWER_RIGHT_2X_CENTER;
         float scale = 2.0f;
-        mMagnificationController.setScale(scale, startCenter.x, startCenter.y, false, SERVICE_ID_1);
+        mMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, false,
+                SERVICE_ID_1);
         mMessageCapturingHandler.sendAllMessages();
-        MagnificationSpec startSpec = getCurrentMagnificationSpec();
-        verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(startSpec)));
+        MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(startSpec)));
         Mockito.reset(mMockWindowManager);
 
         callbacks.onMagnificationRegionChanged(INITIAL_MAGNIFICATION_REGION);
         mMessageCapturingHandler.sendAllMessages();
 
-        MagnificationSpec endSpec = getCurrentMagnificationSpec();
+        MagnificationSpec endSpec = getCurrentMagnificationSpec(displayId);
         assertThat(endSpec, CoreMatchers.not(closeTo(startSpec)));
         PointF expectedOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS,
                 INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER, scale);
         assertThat(endSpec, closeTo(getMagnificationSpec(scale, expectedOffsets)));
-        verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(endSpec)));
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec)));
     }
 
     @Test
     public void testBoundsChange_whileZoomingWithIncompatibleSpec_jumpsToCompatibleSpec() {
-        mMagnificationController.register();
-        MagnificationCallbacks callbacks = getMagnificationCallbacks();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            boundsChange_whileZoomingWithIncompatibleSpec_jumpsToCompatibleSpec(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void boundsChange_whileZoomingWithIncompatibleSpec_jumpsToCompatibleSpec(
+            int displayId) {
+        register(displayId);
+        MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
         callbacks.onMagnificationRegionChanged(OTHER_REGION);
         mMessageCapturingHandler.sendAllMessages();
         PointF startCenter = OTHER_BOUNDS_LOWER_RIGHT_2X_CENTER;
         float scale = 2.0f;
-        mMagnificationController.setScale(scale, startCenter.x, startCenter.y, true, SERVICE_ID_1);
+        mMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, true,
+                SERVICE_ID_1);
         mMessageCapturingHandler.sendAllMessages();
-        MagnificationSpec startSpec = getCurrentMagnificationSpec();
+        MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
         when(mMockValueAnimator.isRunning()).thenReturn(true);
 
         callbacks.onMagnificationRegionChanged(INITIAL_MAGNIFICATION_REGION);
         mMessageCapturingHandler.sendAllMessages();
         verify(mMockValueAnimator).cancel();
 
-        MagnificationSpec endSpec = getCurrentMagnificationSpec();
+        MagnificationSpec endSpec = getCurrentMagnificationSpec(displayId);
         assertThat(endSpec, CoreMatchers.not(closeTo(startSpec)));
         PointF expectedOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS,
                 INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER, scale);
         assertThat(endSpec, closeTo(getMagnificationSpec(scale, expectedOffsets)));
-        verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(endSpec)));
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec)));
     }
 
     @Test
     public void testRequestRectOnScreen_rectAlreadyOnScreen_doesNothing() {
-        mMagnificationController.register();
-        zoomIn2xToMiddle();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            requestRectOnScreen_rectAlreadyOnScreen_doesNothing(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void requestRectOnScreen_rectAlreadyOnScreen_doesNothing(int displayId) {
+        register(displayId);
+        zoomIn2xToMiddle(displayId);
         mMessageCapturingHandler.sendAllMessages();
-        MagnificationSpec startSpec = getCurrentMagnificationSpec();
-        MagnificationCallbacks callbacks = getMagnificationCallbacks();
+        MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
+        MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
         Mockito.reset(mMockWindowManager);
         int centerX = (int) INITIAL_MAGNIFICATION_BOUNDS_CENTER.x;
         int centerY = (int) INITIAL_MAGNIFICATION_BOUNDS_CENTER.y;
         callbacks.onRectangleOnScreenRequested(centerX - 1, centerY - 1, centerX + 1, centerY - 1);
         mMessageCapturingHandler.sendAllMessages();
-        assertThat(getCurrentMagnificationSpec(), closeTo(startSpec));
+        assertThat(getCurrentMagnificationSpec(displayId), closeTo(startSpec));
         verifyNoMoreInteractions(mMockWindowManager);
     }
 
     @Test
     public void testRequestRectOnScreen_rectCanFitOnScreen_pansToGetRectOnScreen() {
-        mMagnificationController.register();
-        zoomIn2xToMiddle();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            requestRectOnScreen_rectCanFitOnScreen_pansToGetRectOnScreen(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void requestRectOnScreen_rectCanFitOnScreen_pansToGetRectOnScreen(int displayId) {
+        register(displayId);
+        zoomIn2xToMiddle(displayId);
         mMessageCapturingHandler.sendAllMessages();
-        MagnificationCallbacks callbacks = getMagnificationCallbacks();
+        MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
         Mockito.reset(mMockWindowManager);
         callbacks.onRectangleOnScreenRequested(0, 0, 1, 1);
         mMessageCapturingHandler.sendAllMessages();
         MagnificationSpec expectedEndSpec = getMagnificationSpec(2.0f, 0, 0);
-        assertThat(getCurrentMagnificationSpec(), closeTo(expectedEndSpec));
-        verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedEndSpec)));
+        assertThat(getCurrentMagnificationSpec(displayId), closeTo(expectedEndSpec));
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
+                argThat(closeTo(expectedEndSpec)));
     }
 
     @Test
     public void testRequestRectOnScreen_garbageInput_doesNothing() {
-        mMagnificationController.register();
-        zoomIn2xToMiddle();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            requestRectOnScreen_garbageInput_doesNothing(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void requestRectOnScreen_garbageInput_doesNothing(int displayId) {
+        register(displayId);
+        zoomIn2xToMiddle(displayId);
         mMessageCapturingHandler.sendAllMessages();
-        MagnificationSpec startSpec = getCurrentMagnificationSpec();
-        MagnificationCallbacks callbacks = getMagnificationCallbacks();
+        MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
+        MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
         Mockito.reset(mMockWindowManager);
         callbacks.onRectangleOnScreenRequested(0, 0, -50, -50);
         mMessageCapturingHandler.sendAllMessages();
-        assertThat(getCurrentMagnificationSpec(), closeTo(startSpec));
+        assertThat(getCurrentMagnificationSpec(displayId), closeTo(startSpec));
         verifyNoMoreInteractions(mMockWindowManager);
     }
 
     @Test
     public void testRequestRectOnScreen_rectTooWide_pansToGetStartOnScreenBasedOnLocale() {
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            requestRectOnScreen_rectTooWide_pansToGetStartOnScreenBasedOnLocale(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void requestRectOnScreen_rectTooWide_pansToGetStartOnScreenBasedOnLocale(
+            int displayId) {
         Locale.setDefault(new Locale("en", "us"));
-        mMagnificationController.register();
-        zoomIn2xToMiddle();
+        register(displayId);
+        zoomIn2xToMiddle(displayId);
         mMessageCapturingHandler.sendAllMessages();
-        MagnificationCallbacks callbacks = getMagnificationCallbacks();
-        MagnificationSpec startSpec = getCurrentMagnificationSpec();
+        MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
+        MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
         Mockito.reset(mMockWindowManager);
         Rect wideRect = new Rect(0, 50, 100, 51);
         callbacks.onRectangleOnScreenRequested(
                 wideRect.left, wideRect.top, wideRect.right, wideRect.bottom);
         mMessageCapturingHandler.sendAllMessages();
         MagnificationSpec expectedEndSpec = getMagnificationSpec(2.0f, 0, startSpec.offsetY);
-        assertThat(getCurrentMagnificationSpec(), closeTo(expectedEndSpec));
-        verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedEndSpec)));
+        assertThat(getCurrentMagnificationSpec(displayId), closeTo(expectedEndSpec));
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
+                argThat(closeTo(expectedEndSpec)));
         Mockito.reset(mMockWindowManager);
 
         // Repeat with RTL
@@ -692,50 +900,66 @@
                 wideRect.left, wideRect.top, wideRect.right, wideRect.bottom);
         mMessageCapturingHandler.sendAllMessages();
         expectedEndSpec = getMagnificationSpec(2.0f, -100, startSpec.offsetY);
-        assertThat(getCurrentMagnificationSpec(), closeTo(expectedEndSpec));
-        verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedEndSpec)));
+        assertThat(getCurrentMagnificationSpec(displayId), closeTo(expectedEndSpec));
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
+                argThat(closeTo(expectedEndSpec)));
     }
 
     @Test
     public void testRequestRectOnScreen_rectTooTall_pansMinimumToGetTopOnScreen() {
-        mMagnificationController.register();
-        zoomIn2xToMiddle();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            requestRectOnScreen_rectTooTall_pansMinimumToGetTopOnScreen(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void requestRectOnScreen_rectTooTall_pansMinimumToGetTopOnScreen(int displayId) {
+        register(displayId);
+        zoomIn2xToMiddle(displayId);
         mMessageCapturingHandler.sendAllMessages();
-        MagnificationCallbacks callbacks = getMagnificationCallbacks();
-        MagnificationSpec startSpec = getCurrentMagnificationSpec();
+        MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
+        MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
         Mockito.reset(mMockWindowManager);
         Rect tallRect = new Rect(50, 0, 51, 100);
         callbacks.onRectangleOnScreenRequested(
                 tallRect.left, tallRect.top, tallRect.right, tallRect.bottom);
         mMessageCapturingHandler.sendAllMessages();
         MagnificationSpec expectedEndSpec = getMagnificationSpec(2.0f, startSpec.offsetX, 0);
-        assertThat(getCurrentMagnificationSpec(), closeTo(expectedEndSpec));
-        verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedEndSpec)));
+        assertThat(getCurrentMagnificationSpec(displayId), closeTo(expectedEndSpec));
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
+                argThat(closeTo(expectedEndSpec)));
     }
 
     @Test
     public void testChangeMagnification_duringAnimation_animatesToNewValue() {
-        mMagnificationController.register();
-        MagnificationSpec startSpec = getCurrentMagnificationSpec();
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            changeMagnification_duringAnimation_animatesToNewValue(i);
+            resetMockWindowManager();
+        }
+    }
+
+    private void changeMagnification_duringAnimation_animatesToNewValue(int displayId) {
+        register(displayId);
+        MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
         float scale = 2.5f;
         PointF firstCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
         MagnificationSpec firstEndSpec = getMagnificationSpec(
                 scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, firstCenter, scale));
 
-        assertTrue(mMagnificationController.setScaleAndCenter(scale, firstCenter.x, firstCenter.y,
-                true, SERVICE_ID_1));
+        assertTrue(mMagnificationController.setScaleAndCenter(displayId,
+                scale, firstCenter.x, firstCenter.y, true, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
 
-        assertEquals(firstCenter.x, mMagnificationController.getCenterX(), 0.5);
-        assertEquals(firstCenter.y, mMagnificationController.getCenterY(), 0.5);
-        assertThat(getCurrentMagnificationSpec(), closeTo(firstEndSpec));
+        assertEquals(firstCenter.x, mMagnificationController.getCenterX(displayId), 0.5);
+        assertEquals(firstCenter.y, mMagnificationController.getCenterY(displayId), 0.5);
+        assertThat(getCurrentMagnificationSpec(displayId), closeTo(firstEndSpec));
         verify(mMockValueAnimator, times(1)).start();
 
         // Initial value
         when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f);
         mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
-        verify(mMockWindowManager).setMagnificationSpec(startSpec);
-        verify(mMockAms).notifyMagnificationChanged(
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId), eq(startSpec));
+        verify(mMockAms).notifyMagnificationChanged(displayId,
                 INITIAL_MAGNIFICATION_REGION, scale, firstCenter.x, firstCenter.y);
         Mockito.reset(mMockWindowManager);
 
@@ -745,32 +969,34 @@
         mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
         MagnificationSpec intermediateSpec1 =
                 getInterpolatedMagSpec(startSpec, firstEndSpec, fraction);
-        verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(intermediateSpec1)));
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
+                argThat(closeTo(intermediateSpec1)));
         Mockito.reset(mMockWindowManager);
 
         PointF newCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER;
         MagnificationSpec newEndSpec = getMagnificationSpec(
                 scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale));
-        assertTrue(mMagnificationController.setCenter(
+        assertTrue(mMagnificationController.setCenter(displayId,
                 newCenter.x, newCenter.y, true, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
 
         // Animation should have been restarted
         verify(mMockValueAnimator, times(2)).start();
-        verify(mMockAms).notifyMagnificationChanged(
+        verify(mMockAms).notifyMagnificationChanged(displayId,
                 INITIAL_MAGNIFICATION_REGION, scale, newCenter.x, newCenter.y);
 
         // New starting point should be where we left off
         when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f);
         mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
-        verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(intermediateSpec1)));
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
+                argThat(closeTo(intermediateSpec1)));
         Mockito.reset(mMockWindowManager);
 
         // Second intermediate point
         fraction = 0.5f;
         when(mMockValueAnimator.getAnimatedFraction()).thenReturn(fraction);
         mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
-        verify(mMockWindowManager).setMagnificationSpec(
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
                 argThat(closeTo(getInterpolatedMagSpec(intermediateSpec1, newEndSpec, fraction))));
         Mockito.reset(mMockWindowManager);
 
@@ -778,21 +1004,54 @@
         Mockito.reset(mMockWindowManager);
         when(mMockValueAnimator.getAnimatedFraction()).thenReturn(1.0f);
         mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
-        verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(newEndSpec)));
+        verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
+                argThat(closeTo(newEndSpec)));
     }
 
-    private void zoomIn2xToMiddle() {
+    private void initMockWindowManager() {
+        for (int i = 0; i < DISPLAY_COUNT; i++) {
+            when(mMockWindowManager.setMagnificationCallbacks(eq(i), any())).thenReturn(true);
+        }
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocationOnMock) throws Throwable {
+                Object[] args = invocationOnMock.getArguments();
+                Region regionArg = (Region) args[1];
+                regionArg.set(INITIAL_MAGNIFICATION_REGION);
+                return null;
+            }
+        }).when(mMockWindowManager).getMagnificationRegion(anyInt(), (Region) anyObject());
+    }
+
+    private void resetMockWindowManager() {
+        Mockito.reset(mMockWindowManager);
+        initMockWindowManager();
+    }
+
+    private void register(int displayId) {
+        mMockValueAnimator = mock(ValueAnimator.class);
+        when(mMockControllerCtx.newValueAnimator()).thenReturn(mMockValueAnimator);
+        mMagnificationController.register(displayId);
+        ArgumentCaptor<ValueAnimator.AnimatorUpdateListener> listenerArgumentCaptor =
+                ArgumentCaptor.forClass(ValueAnimator.AnimatorUpdateListener.class);
+        verify(mMockValueAnimator).addUpdateListener(listenerArgumentCaptor.capture());
+        mTargetAnimationListener = listenerArgumentCaptor.getValue();
+        Mockito.reset(mMockValueAnimator); // Ignore other initialization
+    }
+
+    private void zoomIn2xToMiddle(int displayId) {
         PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
         float scale = 2.0f;
-        mMagnificationController.setScale(scale, startCenter.x, startCenter.y, false, SERVICE_ID_1);
-        assertTrue(mMagnificationController.isMagnifying());
+        mMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, false,
+                SERVICE_ID_1);
+        assertTrue(mMagnificationController.isMagnifying(displayId));
     }
 
-    private MagnificationCallbacks getMagnificationCallbacks() {
+    private MagnificationCallbacks getMagnificationCallbacks(int displayId) {
         ArgumentCaptor<MagnificationCallbacks> magnificationCallbacksCaptor =
                 ArgumentCaptor.forClass(MagnificationCallbacks.class);
         verify(mMockWindowManager)
-                .setMagnificationCallbacks(magnificationCallbacksCaptor.capture());
+                .setMagnificationCallbacks(eq(displayId), magnificationCallbacksCaptor.capture());
         return magnificationCallbacksCaptor.getValue();
     }
 
@@ -823,9 +1082,10 @@
         return spec;
     }
 
-    private MagnificationSpec getCurrentMagnificationSpec() {
-        return getMagnificationSpec(mMagnificationController.getScale(),
-                mMagnificationController.getOffsetX(), mMagnificationController.getOffsetY());
+    private MagnificationSpec getCurrentMagnificationSpec(int displayId) {
+        return getMagnificationSpec(mMagnificationController.getScale(displayId),
+                mMagnificationController.getOffsetX(displayId),
+                mMagnificationController.getOffsetY(displayId));
     }
 
     private MagSpecMatcher closeTo(MagnificationSpec spec) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
index 032074a..5083110 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
@@ -26,16 +26,19 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.content.Context;
+import android.os.Handler;
 import android.os.Message;
 import android.util.DebugUtils;
 import android.view.InputDevice;
@@ -104,14 +107,9 @@
     public static final float DEFAULT_X = 301;
     public static final float DEFAULT_Y = 299;
 
+    private static final int DISPLAY_0 = 0;
+
     private Context mContext;
-    final AccessibilityManagerService mMockAms = mock(AccessibilityManagerService.class);
-    final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class);
-    final MessageCapturingHandler mMessageCapturingHandler =
-            new MessageCapturingHandler(null);
-    final ValueAnimator mMockValueAnimator = mock(ValueAnimator.class);
-    MagnificationController.SettingsBridge mMockSettingsBridge =
-            mock(MagnificationController.SettingsBridge.class);
     MagnificationController mMagnificationController;
 
     private OffsettableClock mClock;
@@ -123,18 +121,26 @@
     @Before
     public void setUp() {
         mContext = InstrumentationRegistry.getContext();
-        mMagnificationController = new MagnificationController(mContext, mMockAms, new Object(),
-                mMessageCapturingHandler, mMockWindowManager, mMockValueAnimator,
-                mMockSettingsBridge) {
+        final MagnificationController.ControllerContext mockController =
+                mock(MagnificationController.ControllerContext.class);
+        final WindowManagerInternal mockWindowManager = mock(WindowManagerInternal.class);
+        when(mockController.getContext()).thenReturn(mContext);
+        when(mockController.getAms()).thenReturn(mock(AccessibilityManagerService.class));
+        when(mockController.getWindowManager()).thenReturn(mockWindowManager);
+        when(mockController.getHandler()).thenReturn(new Handler(mContext.getMainLooper()));
+        when(mockController.newValueAnimator()).thenReturn(new ValueAnimator());
+        when(mockController.getAnimationDuration()).thenReturn(1000L);
+        when(mockWindowManager.setMagnificationCallbacks(eq(DISPLAY_0), any())).thenReturn(true);
+        mMagnificationController = new MagnificationController(mockController, new Object()) {
             @Override
-            public boolean magnificationRegionContains(float x, float y) {
+            public boolean magnificationRegionContains(int displayId, float x, float y) {
                 return true;
             }
 
             @Override
-            void setForceShowMagnifiableBounds(boolean show) {}
+            void setForceShowMagnifiableBounds(int displayId, boolean show) {}
         };
-        mMagnificationController.register();
+        mMagnificationController.register(DISPLAY_0);
         mClock = new OffsettableClock.Stopped();
 
         boolean detectTripleTap = true;
@@ -144,7 +150,7 @@
 
     @After
     public void tearDown() {
-        mMagnificationController.unregister();
+        mMagnificationController.unregister(DISPLAY_0);
     }
 
     @NonNull
@@ -509,7 +515,7 @@
     }
 
     private boolean isZoomed() {
-        return mMgh.mMagnificationController.isMagnifying();
+        return mMgh.mMagnificationController.isMagnifying(DISPLAY_0);
     }
 
     private int tapCount() {