Refactoring of the screen magnification feature.

1. This patch takes care of the case where a magnified window is covering an unmagnigied
   one. One example is a dialog that covers the IME window.

bug:7634430

2. Ensuring that the UI automator tool can connect and correctly dump the screen.

bug:7694696

3. Removed the partial implementation for multi display magnification. It adds
   unnecessary complexity since it cannot be implemented without support for
   input from multiple screens. We will revisit when necessary.

4. Moved the magnified border window as a surface in the window manager.

5. Moved the mediator APIs on the window manager and the policy methods on the
   WindowManagerPolicy.

6. Implemented batch event processing for the accessibility input filter.

Change-Id: I4ebf68b94fb07201e124794f69611ece388ec116
diff --git a/Android.mk b/Android.mk
index 11c0038..294a2fe 100644
--- a/Android.mk
+++ b/Android.mk
@@ -154,8 +154,7 @@
 	core/java/android/view/accessibility/IAccessibilityManager.aidl \
 	core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
 	core/java/android/view/IApplicationToken.aidl \
-	core/java/android/view/IDisplayMagnificationMediator.aidl \
-	core/java/android/view/IDisplayMagnificationController.aidl \
+	core/java/android/view/IMagnificationCallbacks.aidl \
 	core/java/android/view/IInputFilter.aidl \
 	core/java/android/view/IInputFilterHost.aidl \
 	core/java/android/view/IOnKeyguardExitResult.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 425794a..06f7c54 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -145,6 +145,10 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IDisplayContentChangeListener.P)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IWindowManager.java)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IWindowManager.P)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IDisplayMagnificationController.java)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IDisplayMagnificationController.P)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IDisplayMagnificationMediator.java)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IDisplayMagnificationMediator.P)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/api/current.txt b/api/current.txt
index 57d65ba..8faebfc 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -24753,7 +24753,6 @@
     method public android.graphics.Canvas lockCanvas(android.graphics.Rect) throws java.lang.IllegalArgumentException, android.view.Surface.OutOfResourcesException;
     method public void readFromParcel(android.os.Parcel);
     method public void release();
-    method public static java.lang.String rotationToString(int);
     method public deprecated void unlockCanvas(android.graphics.Canvas);
     method public void unlockCanvasAndPost(android.graphics.Canvas);
     method public void writeToParcel(android.os.Parcel, int);
diff --git a/core/java/android/view/IDisplayMagnificationMediator.aidl b/core/java/android/view/IDisplayMagnificationMediator.aidl
deleted file mode 100644
index aa25dac..0000000
--- a/core/java/android/view/IDisplayMagnificationMediator.aidl
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.view;
-
-import android.view.IDisplayMagnificationController;
-import android.view.MagnificationSpec;
-
-/**
- * {@hide}
- */
-interface IDisplayMagnificationMediator {
-    void addController(int displayId, in IDisplayMagnificationController controller);
-    void removeController(in IDisplayMagnificationController controller);
-    void setMagnificationSpec(in IDisplayMagnificationController controller,
-            in MagnificationSpec spec);
-    MagnificationSpec getCompatibleMagnificationSpec(in IBinder windowToken);
-}
diff --git a/core/java/android/view/IDisplayMagnificationController.aidl b/core/java/android/view/IMagnificationCallbacks.aidl
similarity index 86%
rename from core/java/android/view/IDisplayMagnificationController.aidl
rename to core/java/android/view/IMagnificationCallbacks.aidl
index efe2775..032d073 100644
--- a/core/java/android/view/IDisplayMagnificationController.aidl
+++ b/core/java/android/view/IMagnificationCallbacks.aidl
@@ -16,11 +16,13 @@
 
 package android.view;
 
+import android.graphics.Region;
+
 /**
  * {@hide}
  */
-oneway interface IDisplayMagnificationController {
-    void onMagnifedFrameChanged(int left, int top, int right, int bottom);
+oneway interface IMagnificationCallbacks {
+    void onMagnifedBoundsChanged(in Region bounds);
     void onRectangleOnScreenRequested(int left, int top, int right, int bottom);
     void onRotationChanged(int rotation);
     void onUserContextChanged();
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 17f04e9..1ee2bb3 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -27,7 +27,7 @@
 import android.os.Bundle;
 import android.os.IRemoteCallback;
 import android.view.IApplicationToken;
-import android.view.IDisplayMagnificationMediator;
+import android.view.IMagnificationCallbacks;
 import android.view.IOnKeyguardExitResult;
 import android.view.IRotationWatcher;
 import android.view.IWindowSession;
@@ -226,11 +226,6 @@
     void setInputFilter(in IInputFilter filter);
 
     /**
-     * Gets the display magnification mediator.
-     */
-    IDisplayMagnificationMediator getDisplayMagnificationMediator();
-
-    /**
      * Gets the frame of a window given its token.
      */
     void getWindowFrame(IBinder token, out Rect outFrame);
@@ -245,4 +240,30 @@
      * credentials.
      */
     void showAssistant();
+
+    /**
+     * Sets the display magnification callbacks. These callbacks notify
+     * the client for contextual changes related to display magnification.
+     *
+     * @param callbacks The magnification callbacks.
+     */
+    void setMagnificationCallbacks(IMagnificationCallbacks callbacks);
+
+    /**
+     * Sets the magnification spec to be applied to all windows that can be
+     * magnified.
+     *
+     * @param spec The current magnification spec.
+     */
+    void setMagnificationSpec(in MagnificationSpec spec);
+
+    /**
+     * Gets the magnification spec for a window given its token. If the
+     * window has a compatibility scale it is also folded in the returned
+     * magnification spec.
+     *
+     * @param windowToken The unique window token.
+     * @return The magnification spec if such or null.
+     */
+    MagnificationSpec getCompatibleMagnificationSpecForWindow(in IBinder windowToken);
 }
diff --git a/core/java/android/view/MagnificationSpec.java b/core/java/android/view/MagnificationSpec.java
index 7fb5615..0ee6714 100644
--- a/core/java/android/view/MagnificationSpec.java
+++ b/core/java/android/view/MagnificationSpec.java
@@ -39,6 +39,9 @@
     }
 
     public void initialize(float scale, float offsetX, float offsetY) {
+        if (scale < 1) {
+            throw new IllegalArgumentException("Scale must be greater than or equal to one!");
+        }
         this.scale = scale;
         this.offsetX = offsetX;
         this.offsetY = offsetY;
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index f2c5eac..a972b75 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -812,6 +812,8 @@
      *
      * @param rotation The rotation.
      * @return The rotation symbolic name.
+     *
+     * @hide
      */
     public static String rotationToString(int rotation) {
         switch (rotation) {
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 7bdb44c..47ef638 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1134,4 +1134,23 @@
      * {@link android.content.Intent#ACTION_ASSIST}
      */
     public void showAssistant();
+
+    /**
+     * Returns whether a given window type can be magnified.
+     *
+     * @param windowType The window type.
+     * @return True if the window can be magnified.
+     */
+    public boolean canMagnifyWindow(int windowType);
+
+    /**
+     * Returns whether a given window type is considered a top level one.
+     * A top level window does not have a container, i.e. attached window,
+     * or if it has a container it is laid out as a top-level window, not
+     * as a child of its container.
+     *
+     * @param windowType The window type.
+     * @return True if the window is a top level one.
+     */
+    public boolean isTopLevelWindow(int windowType);
 }
diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java
index 27ea0f0..9773039 100644
--- a/graphics/java/android/graphics/Region.java
+++ b/graphics/java/android/graphics/Region.java
@@ -18,8 +18,15 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Pools.SynchronizedPool;
 
 public class Region implements Parcelable {
+
+    private static final int MAX_POOL_SIZE = 10;
+
+    private static final SynchronizedPool<Region> sPool =
+            new SynchronizedPool<Region>(MAX_POOL_SIZE);
+
     /**
      * @hide
      */
@@ -291,6 +298,39 @@
         return nativeToString(mNativeRegion);
     }
 
+    /**
+     * @return An instance from a pool if such or a new one.
+     *
+     * @hide
+     */
+    public static Region obtain() {
+        Region region = sPool.acquire();
+        return (region != null) ? region : new Region();
+    }
+
+    /**
+     * @return An instance from a pool if such or a new one.
+     *
+     * @param other Region to copy values from for initialization.
+     *
+     * @hide
+     */
+    public static Region obtain(Region other) {
+        Region region = obtain();
+        region.set(other);
+        return region;
+    }
+
+    /**
+     * Recycles an instance.
+     *
+     * @hide
+     */
+    public void recycle() {
+        setEmpty();
+        sPool.release(this);
+    }
+
     //////////////////////////////////////////////////////////////////////////
     
     public static final Parcelable.Creator<Region> CREATOR
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 024c247..2601bd0 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -4544,6 +4544,28 @@
         mKeyguardMediator.showAssistant();
     }
 
+    @Override
+    public boolean canMagnifyWindow(int windowType) {
+        switch (windowType) {
+            case WindowManager.LayoutParams.TYPE_INPUT_METHOD:
+            case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG:
+            case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR:
+            case WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY: {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean isTopLevelWindow(int windowType) {
+        if (windowType >= WindowManager.LayoutParams.FIRST_SUB_WINDOW
+                && windowType <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
+            return (windowType == WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG);
+        }
+        return true;
+    }
+
     /**
      * Returns the human readable name of a window transition.
      *
diff --git a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 45da1e1..9eb6834 100644
--- a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -18,7 +18,9 @@
 
 import android.content.Context;
 import android.os.PowerManager;
+import android.util.Pools.SimplePool;
 import android.util.Slog;
+import android.view.Choreographer;
 import android.view.Display;
 import android.view.InputDevice;
 import android.view.InputEvent;
@@ -27,6 +29,12 @@
 import android.view.WindowManagerPolicy;
 import android.view.accessibility.AccessibilityEvent;
 
+/**
+ * This class is an input filter for implementing accessibility features such
+ * as display magnification and explore by touch.
+ *
+ * NOTE: This class has to be created and poked only from the main thread.
+ */
 class AccessibilityInputFilter extends InputFilter implements EventStreamTransformation {
 
     private static final String TAG = AccessibilityInputFilter.class.getSimpleName();
@@ -49,12 +57,31 @@
      */
     static final int FLAG_FEATURE_TOUCH_EXPLORATION = 0x00000002;
 
+    private final Runnable mProcessBatchedEventsRunnable = new Runnable() {
+        @Override
+        public void run() {
+            final long frameTimeNanos = mChoreographer.getFrameTimeNanos();
+            if (DEBUG) {
+                Slog.i(TAG, "Begin batch processing for frame: " + frameTimeNanos);
+            }
+            processBatchedEvents(frameTimeNanos);
+            if (DEBUG) {
+                Slog.i(TAG, "End batch processing.");
+            }
+            if (mEventQueue != null) {
+                scheduleProcessBatchedEvents();
+            }
+        }
+    };
+
     private final Context mContext;
 
     private final PowerManager mPm;
 
     private final AccessibilityManagerService mAms;
 
+    private final Choreographer mChoreographer;
+
     private int mCurrentDeviceId;
 
     private boolean mInstalled;
@@ -65,11 +92,14 @@
     private ScreenMagnifier mScreenMagnifier;
     private EventStreamTransformation mEventHandler;
 
+    private MotionEventHolder mEventQueue;
+
     AccessibilityInputFilter(Context context, AccessibilityManagerService service) {
         super(context.getMainLooper());
         mContext = context;
         mAms = service;
         mPm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        mChoreographer = Choreographer.getInstance();
     }
 
     @Override
@@ -119,10 +149,61 @@
             }
             mCurrentDeviceId = deviceId;
         }
+        batchMotionEvent((MotionEvent) event, policyFlags);
+    }
+
+    private void scheduleProcessBatchedEvents() {
+        mChoreographer.postCallback(Choreographer.CALLBACK_INPUT,
+                mProcessBatchedEventsRunnable, null);
+    }
+
+    private void batchMotionEvent(MotionEvent event, int policyFlags) {
+        if (DEBUG) {
+            Slog.i(TAG, "Batching event: " + event + ", policyFlags: " + policyFlags);
+        }
+        if (mEventQueue == null) {
+            mEventQueue = MotionEventHolder.obtain(event, policyFlags);
+            scheduleProcessBatchedEvents();
+            return;
+        }
+        if (mEventQueue.event.addBatch(event)) {
+            return;
+        }
+        MotionEventHolder holder = MotionEventHolder.obtain(event, policyFlags);
+        holder.next = mEventQueue;
+        mEventQueue.previous = holder;
+        mEventQueue = holder;
+    }
+
+    private void processBatchedEvents(long frameNanos) {
+        MotionEventHolder current = mEventQueue;
+        while (current.next != null) {
+            current = current.next;
+        }
+        while (true) {
+            if (current == null) {
+                mEventQueue = null;
+                break;
+            }
+            if (current.event.getEventTimeNano() >= frameNanos) {
+                // Finished with this choreographer frame. Do the rest on the next one.
+                current.next = null;
+                break;
+            }
+            handleMotionEvent(current.event, current.policyFlags);
+            MotionEventHolder prior = current;
+            current = current.previous;
+            prior.recycle();
+        }
+    }
+
+    private void handleMotionEvent(MotionEvent event, int policyFlags) {
+        if (DEBUG) {
+            Slog.i(TAG, "Handling batched event: " + event + ", policyFlags: " + policyFlags);
+        }
         mPm.userActivity(event.getEventTime(), false);
-        MotionEvent rawEvent = (MotionEvent) event;
-        MotionEvent transformedEvent = MotionEvent.obtain(rawEvent);
-        mEventHandler.onMotionEvent(transformedEvent, rawEvent, policyFlags);
+        MotionEvent transformedEvent = MotionEvent.obtain(event);
+        mEventHandler.onMotionEvent(transformedEvent, event, policyFlags);
         transformedEvent.recycle();
     }
 
@@ -203,4 +284,34 @@
     public void onDestroy() {
         /* ignore */
     }
+
+    private static class MotionEventHolder {
+        private static final int MAX_POOL_SIZE = 32;
+        private static final SimplePool<MotionEventHolder> sPool =
+                new SimplePool<MotionEventHolder>(MAX_POOL_SIZE);
+
+        public int policyFlags;
+        public MotionEvent event;
+        public MotionEventHolder next;
+        public MotionEventHolder previous;
+
+        public static MotionEventHolder obtain(MotionEvent event, int policyFlags) {
+            MotionEventHolder holder = sPool.acquire();
+            if (holder == null) {
+                holder = new MotionEventHolder();
+            }
+            holder.event = MotionEvent.obtain(event);
+            holder.policyFlags = policyFlags;
+            return holder;
+        }
+
+        public void recycle() {
+            event.recycle();
+            event = null;
+            policyFlags = 0;
+            next = null;
+            previous = null;
+            sPool.release(this);
+        }
+    }
 }
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 1d0e3a3..3e3e7dc 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -65,7 +65,6 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
-import android.view.IDisplayMagnificationMediator;
 import android.view.IWindow;
 import android.view.IWindowManager;
 import android.view.InputDevice;
@@ -159,8 +158,6 @@
 
     private final MainHandler mMainHandler;
 
-    private IDisplayMagnificationMediator mMagnificationMediator;
-
     private Service mUiAutomationService;
 
     private Service mQueryBridge;
@@ -539,7 +536,7 @@
                     accessibilityServiceInfo, true);
             mUiAutomationService.onServiceConnected(componentName, serviceClient.asBinder());
 
-            updateInputFilterLocked(userState);
+            scheduleUpdateInputFilter(userState);
             scheduleSendStateToClientsLocked(userState);
         }
     }
@@ -570,7 +567,7 @@
             userState.mTouchExplorationGrantedServices.add(service);
             // Update the internal state.
             performServiceManagementLocked(userState);
-            updateInputFilterLocked(userState);
+            scheduleUpdateInputFilter(userState);
             scheduleSendStateToClientsLocked(userState);
         }
     }
@@ -736,7 +733,7 @@
                 mTempStateChangeForCurrentUserMemento.clear();
                 // Update the internal state.
                 performServiceManagementLocked(userState);
-                updateInputFilterLocked(userState);
+                scheduleUpdateInputFilter(userState);
                 scheduleSendStateToClientsLocked(userState);
             }
         }
@@ -890,7 +887,7 @@
             service.linkToOwnDeath();
             userState.mServices.add(service);
             userState.mComponentNameToServiceMap.put(service.mComponentName, service);
-            updateInputFilterLocked(userState);
+            scheduleUpdateInputFilter(userState);
             tryEnableTouchExplorationLocked(service);
         } catch (RemoteException e) {
             /* do nothing */
@@ -912,7 +909,7 @@
         userState.mComponentNameToServiceMap.remove(service.mComponentName);
         service.unlinkToOwnDeath();
         service.dispose();
-        updateInputFilterLocked(userState);
+        scheduleUpdateInputFilter(userState);
         tryDisableTouchExplorationLocked(service);
         return removed;
     }
@@ -1090,7 +1087,11 @@
         }
     }
 
-    private void updateInputFilterLocked(UserState userState) {
+    private void scheduleUpdateInputFilter(UserState userState) {
+        mMainHandler.obtainMessage(MainHandler.MSG_UPDATE_INPUT_FILTER, userState).sendToTarget();
+    }
+
+    private void updateInputFilter(UserState userState) {
         boolean setInputFilter = false;
         AccessibilityInputFilter inputFilter = null;
         synchronized (mLock) {
@@ -1200,7 +1201,7 @@
         handleAccessibilityEnabledSettingChangedLocked(userState);
 
         performServiceManagementLocked(userState);
-        updateInputFilterLocked(userState);
+        scheduleUpdateInputFilter(userState);
         scheduleSendStateToClientsLocked(userState);
     }
 
@@ -1355,6 +1356,7 @@
         public static final int MSG_SEND_RECREATE_INTERNAL_STATE = 4;
         public static final int MSG_UPDATE_ACTIVE_WINDOW = 5;
         public static final int MSG_ANNOUNCE_NEW_USER_IF_NEEDED = 6;
+        public static final int MSG_UPDATE_INPUT_FILTER = 7;
 
         public MainHandler(Looper looper) {
             super(looper);
@@ -1398,6 +1400,10 @@
                 case MSG_ANNOUNCE_NEW_USER_IF_NEEDED: {
                     announceNewUserIfNeeded();
                 } break;
+                case MSG_UPDATE_INPUT_FILTER: {
+                    UserState userState = (UserState) msg.obj;
+                    updateInputFilter(userState);                        
+                } break;
             }
         }
 
@@ -2220,16 +2226,13 @@
 
         private MagnificationSpec getCompatibleMagnificationSpec(int windowId) {
             try {
-                if (mMagnificationMediator == null) {
-                    mMagnificationMediator = mWindowManagerService
-                            .getDisplayMagnificationMediator();
-                }
                 IBinder windowToken = mGlobalWindowTokens.get(windowId);
                 if (windowToken == null) {
                     windowToken = getCurrentUserStateLocked().mWindowTokens.get(windowId);
                 }                    
                 if (windowToken != null) {
-                    return mMagnificationMediator.getCompatibleMagnificationSpec(windowToken);
+                    return mWindowManagerService.getCompatibleMagnificationSpecForWindow(
+                            windowToken);
                 }
             } catch (RemoteException re) {
                 /* ignore */
@@ -2581,7 +2584,7 @@
                         UserState userState = getCurrentUserStateLocked();
                         handleAccessibilityEnabledSettingChangedLocked(userState);
                         performServiceManagementLocked(userState);
-                        updateInputFilterLocked(userState);
+                        scheduleUpdateInputFilter(userState);
                         scheduleSendStateToClientsLocked(userState);
                     }
                 }
@@ -2591,7 +2594,7 @@
                     if (mUiAutomationService == null) {
                         UserState userState = getCurrentUserStateLocked();
                         handleTouchExplorationEnabledSettingChangedLocked(userState);
-                        updateInputFilterLocked(userState);
+                        scheduleUpdateInputFilter(userState);
                         scheduleSendStateToClientsLocked(userState);
                     }
                 }
@@ -2601,7 +2604,7 @@
                     if (mUiAutomationService == null) {
                         UserState userState = getCurrentUserStateLocked();
                         handleDisplayMagnificationEnabledSettingChangedLocked(userState);
-                        updateInputFilterLocked(userState);
+                        scheduleUpdateInputFilter(userState);
                         scheduleSendStateToClientsLocked(userState);
                     }
                 }
diff --git a/services/java/com/android/server/accessibility/ScreenMagnifier.java b/services/java/com/android/server/accessibility/ScreenMagnifier.java
index e5f0924..1bf2c42 100644
--- a/services/java/com/android/server/accessibility/ScreenMagnifier.java
+++ b/services/java/com/android/server/accessibility/ScreenMagnifier.java
@@ -16,25 +16,17 @@
 
 package com.android.server.accessibility;
 
-import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
 import android.animation.ObjectAnimator;
 import android.animation.TypeEvaluator;
 import android.animation.ValueAnimator;
-import android.app.Service;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.PixelFormat;
-import android.graphics.PorterDuff.Mode;
 import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManager.DisplayListener;
+import android.graphics.Region;
 import android.os.AsyncTask;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.Message;
 import android.os.RemoteException;
@@ -44,13 +36,9 @@
 import android.text.TextUtils;
 import android.util.Property;
 import android.util.Slog;
-import android.view.Display;
-import android.view.DisplayInfo;
 import android.view.GestureDetector;
 import android.view.GestureDetector.SimpleOnGestureListener;
-import android.view.Gravity;
-import android.view.IDisplayMagnificationController;
-import android.view.IDisplayMagnificationMediator;
+import android.view.IMagnificationCallbacks;
 import android.view.IWindowManager;
 import android.view.MagnificationSpec;
 import android.view.MotionEvent;
@@ -60,13 +48,9 @@
 import android.view.ScaleGestureDetector.OnScaleGestureListener;
 import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
 
-import com.android.internal.R;
 import com.android.internal.os.SomeArgs;
 
 import java.util.Locale;
@@ -110,7 +94,7 @@
  *
  * 6. The magnification scale will be persisted in settings and in the cloud.
  */
-public final class ScreenMagnifier extends IDisplayMagnificationController.Stub
+public final class ScreenMagnifier extends IMagnificationCallbacks.Stub
         implements EventStreamTransformation {
 
     private static final String LOG_TAG = ScreenMagnifier.class.getSimpleName();
@@ -121,7 +105,6 @@
     private static final boolean DEBUG_PANNING = false;
     private static final boolean DEBUG_SCALING = false;
     private static final boolean DEBUG_MAGNIFICATION_CONTROLLER = false;
-    private static final boolean DEBUG_VIEWPORT_WINDOW = false;
 
     private static final int STATE_DELEGATING = 1;
     private static final int STATE_DETECTING = 2;
@@ -131,22 +114,22 @@
     private static final float DEFAULT_MAGNIFICATION_SCALE = 2.0f;
     private static final int MULTI_TAP_TIME_SLOP_ADJUSTMENT = 50;
 
-    private static final int MESSAGE_ON_MAGNIFIED_FRAME_CHANGED = 1;
+    private static final int MESSAGE_ON_MAGNIFIED_BOUNDS_CHANGED = 1;
     private static final int MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED = 2;
     private static final int MESSAGE_ON_USER_CONTEXT_CHANGED = 3;
     private static final int MESSAGE_ON_ROTATION_CHANGED = 4;
-    private static final int MESSAGE_SHOW_VIEWPORT_FRAME = 5;
 
     private static final int DEFAULT_SCREEN_MAGNIFICATION_AUTO_UPDATE = 1;
-    private static final float DEFAULT_WINDOW_ANIMATION_SCALE = 1.0f;
+
+    private static final int MY_PID = android.os.Process.myPid();
 
     private final Rect mTempRect = new Rect();
     private final Rect mTempRect1 = new Rect();
 
     private final Context mContext;
+    private final IWindowManager mWindowManager;
     private final MagnificationController mMagnificationController;
     private final ScreenStateObserver mScreenStateObserver;
-    private final ViewportWindow mViewportWindow;
 
     private final DetectingStateHandler mDetectingStateHandler;
     private final MagnifiedContentInteractonStateHandler mMagnifiedContentInteractonStateHandler;
@@ -161,11 +144,8 @@
     private final int mMultiTapDistanceSlop;
 
     private final long mLongAnimationDuration;
-    private final float mWindowAnimationScale;
 
-    private final Rect mMagnifiedFrame = new Rect();
-
-    private IDisplayMagnificationMediator mDisplayMagnifier;
+    private final Region mMagnifiedBounds = new Region();
 
     private EventStreamTransformation mNext;
 
@@ -178,18 +158,16 @@
 
     private long mDelegatingStateDownTime;
 
+    private boolean mUpdateMagnificationSpecOnNextBoundsChange;
+
     private final Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message message) {
             switch (message.what) {
-                case MESSAGE_ON_MAGNIFIED_FRAME_CHANGED: {
-                    SomeArgs args = (SomeArgs) message.obj;
-                    final int left = args.argi1;
-                    final int top = args.argi2;
-                    final int right = args.argi3;
-                    final int bottom = args.argi4;
-                    handleOnMagnifiedFrameChanged(left, top, right, bottom);
-                    args.recycle();
+                case MESSAGE_ON_MAGNIFIED_BOUNDS_CHANGED: {
+                    Region bounds = (Region) message.obj;
+                    handleOnMagnifiedBoundsChanged(bounds);
+                    bounds.recycle();
                 } break;
                 case MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED: {
                     SomeArgs args = (SomeArgs) message.obj;
@@ -207,23 +185,20 @@
                     final int rotation = message.arg1;
                     handleOnRotationChanged(rotation);
                 } break;
-                case MESSAGE_SHOW_VIEWPORT_FRAME: {
-                    mViewportWindow.setShown(true, true);
-                } break;
             }
         }
     };
 
     public ScreenMagnifier(Context context, int displayId, AccessibilityManagerService service) {
         mContext = context;
+        mWindowManager = IWindowManager.Stub.asInterface(
+                ServiceManager.getService("window"));
         mAms = service;
 
         mLongAnimationDuration = context.getResources().getInteger(
                 com.android.internal.R.integer.config_longAnimTime);
         mTapDistanceSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         mMultiTapDistanceSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
-        mWindowAnimationScale = Settings.Global.getFloat(context.getContentResolver(),
-                Settings.Global.WINDOW_ANIMATION_SCALE, DEFAULT_WINDOW_ANIMATION_SCALE);
 
         mDetectingStateHandler = new DetectingStateHandler();
         mStateViewportDraggingHandler = new StateViewportDraggingHandler();
@@ -231,15 +206,10 @@
                 context);
 
         mMagnificationController = new MagnificationController(mLongAnimationDuration);
-        mViewportWindow = new ViewportWindow(context, displayId);
-        mScreenStateObserver = new ScreenStateObserver(context, mMagnificationController,
-                mViewportWindow);
+        mScreenStateObserver = new ScreenStateObserver(context, mMagnificationController);
 
         try {
-            IWindowManager windowManagerService = IWindowManager.Stub.asInterface(
-                    ServiceManager.getService("window"));
-            mDisplayMagnifier = windowManagerService.getDisplayMagnificationMediator();
-            mDisplayMagnifier.addController(Display.DEFAULT_DISPLAY, this);
+            mWindowManager.setMagnificationCallbacks(this);
         } catch (RemoteException re) {
             /* ignore */
         }
@@ -248,18 +218,30 @@
     }
 
     @Override
-    public void onMagnifedFrameChanged(int left, int top, int right, int bottom) {
-        SomeArgs args = SomeArgs.obtain();
-        args.argi1 = left;
-        args.argi2 = top;
-        args.argi3 = right;
-        args.argi4 = bottom;
-        mHandler.obtainMessage(MESSAGE_ON_MAGNIFIED_FRAME_CHANGED, args).sendToTarget();
+    public void onMagnifedBoundsChanged(Region bounds) {
+        Region newBounds = Region.obtain(bounds);
+        mHandler.obtainMessage(MESSAGE_ON_MAGNIFIED_BOUNDS_CHANGED, newBounds).sendToTarget();
+        if (MY_PID != Binder.getCallingPid()) {
+            bounds.recycle();
+        }
     }
 
-    private void handleOnMagnifiedFrameChanged(int left, int top, int right, int bottom) {
-        mMagnifiedFrame.set(left, top, right, bottom);
-        mViewportWindow.setBounds(mMagnifiedFrame, mMagnificationController.isMagnifying());
+    private void handleOnMagnifiedBoundsChanged(Region bounds) {
+        // If there was a rotation we have to update the center of the magnified
+        // region since the old offset X/Y may be out of its acceptable range for
+        // the new display width and height.
+        if (mUpdateMagnificationSpecOnNextBoundsChange) {
+            mUpdateMagnificationSpecOnNextBoundsChange = false;
+            MagnificationSpec spec = mMagnificationController.getMagnificationSpec();
+            Rect magnifiedFrame = mTempRect;
+            mMagnifiedBounds.getBounds(magnifiedFrame);
+            final float scale = spec.scale;
+            final float centerX = (-spec.offsetX + magnifiedFrame.width() / 2) / scale;
+            final float centerY = (-spec.offsetY + magnifiedFrame.height() / 2) / scale;
+            mMagnificationController.setScaleAndMagnifiedRegionCenter(scale, centerX,
+                    centerY, false);
+        }
+        mMagnifiedBounds.set(bounds);
         mAms.onMagnificationStateChanged();
     }
 
@@ -274,35 +256,35 @@
     }
 
     private void handleOnRectangleOnScreenRequested(int left, int top, int right, int bottom) {
-        Rect rectangle = mTempRect;
-        rectangle.set(left, top, right, bottom);
-        if (!Rect.intersects(rectangle, mMagnifiedFrame)) {
+        Rect magnifiedFrame = mTempRect;
+        mMagnifiedBounds.getBounds(magnifiedFrame);
+        if (!magnifiedFrame.intersects(left, top, right, bottom)) {
             return;
         }
         Rect magnifFrameInScreenCoords = mTempRect1;
         getMagnifiedFrameInContentCoords(magnifFrameInScreenCoords);
         final float scrollX;
         final float scrollY;
-        if (rectangle.width() > magnifFrameInScreenCoords.width()) {
+        if (right - left > magnifFrameInScreenCoords.width()) {
             final int direction = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault());
             if (direction == View.LAYOUT_DIRECTION_LTR) {
-                scrollX = rectangle.left - magnifFrameInScreenCoords.left;
+                scrollX = left - magnifFrameInScreenCoords.left;
             } else {
-                scrollX = rectangle.right - magnifFrameInScreenCoords.right;
+                scrollX = right - magnifFrameInScreenCoords.right;
             }
-        } else if (rectangle.left < magnifFrameInScreenCoords.left) {
-            scrollX = rectangle.left - magnifFrameInScreenCoords.left;
-        } else if (rectangle.right > magnifFrameInScreenCoords.right) {
-            scrollX = rectangle.right - magnifFrameInScreenCoords.right;
+        } else if (left < magnifFrameInScreenCoords.left) {
+            scrollX = left - magnifFrameInScreenCoords.left;
+        } else if (right > magnifFrameInScreenCoords.right) {
+            scrollX = right - magnifFrameInScreenCoords.right;
         } else {
             scrollX = 0;
         }
-        if (rectangle.height() > magnifFrameInScreenCoords.height()) {
-            scrollY = rectangle.top - magnifFrameInScreenCoords.top;
-        } else if (rectangle.top < magnifFrameInScreenCoords.top) {
-            scrollY = rectangle.top - magnifFrameInScreenCoords.top;
-        } else if (rectangle.bottom > magnifFrameInScreenCoords.bottom) {
-            scrollY = rectangle.bottom - magnifFrameInScreenCoords.bottom;
+        if (bottom - top > magnifFrameInScreenCoords.height()) {
+            scrollY = top - magnifFrameInScreenCoords.top;
+        } else if (top < magnifFrameInScreenCoords.top) {
+            scrollY = top - magnifFrameInScreenCoords.top;
+        } else if (bottom > magnifFrameInScreenCoords.bottom) {
+            scrollY = bottom - magnifFrameInScreenCoords.bottom;
         } else {
             scrollY = 0;
         }
@@ -310,13 +292,6 @@
         mMagnificationController.offsetMagnifiedRegionCenter(scrollX * scale, scrollY * scale);
     }
 
-    private void getMagnifiedFrameInContentCoords(Rect rect) {
-        MagnificationSpec spec = mMagnificationController.getMagnificationSpec();
-        rect.set(mMagnifiedFrame);
-        rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
-        rect.scale(1.0f / spec.scale);
-    }
-
     @Override
     public void onRotationChanged(int rotation) {
         mHandler.obtainMessage(MESSAGE_ON_ROTATION_CHANGED, rotation, 0).sendToTarget();
@@ -324,12 +299,8 @@
 
     private void handleOnRotationChanged(int rotation) {
         resetMagnificationIfNeeded();
-        mViewportWindow.setShown(false, false);
-        mViewportWindow.rotationChanged();
         if (mMagnificationController.isMagnifying()) {
-            final long delay = (long) (2 * mLongAnimationDuration * mWindowAnimationScale);
-            Message message = mHandler.obtainMessage(MESSAGE_SHOW_VIEWPORT_FRAME);
-            mHandler.sendMessageDelayed(message, delay);
+            mUpdateMagnificationSpecOnNextBoundsChange = true;
         }
     }
 
@@ -342,11 +313,17 @@
         resetMagnificationIfNeeded();
     }
 
+    private void getMagnifiedFrameInContentCoords(Rect rect) {
+        MagnificationSpec spec = mMagnificationController.getMagnificationSpec();
+        mMagnifiedBounds.getBounds(rect);
+        rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
+        rect.scale(1.0f / spec.scale);
+    }
+
     private void resetMagnificationIfNeeded() {
         if (mMagnificationController.isMagnifying()
                 && isScreenMagnificationAutoUpdateEnabled(mContext)) {
             mMagnificationController.reset(true);
-            mViewportWindow.setShown(false, true);
         }
     }
 
@@ -401,7 +378,11 @@
     @Override
     public void onDestroy() {
         mScreenStateObserver.destroy();
-        mViewportWindow.destroy();
+        try {
+            mWindowManager.setMagnificationCallbacks(null);
+        } catch (RemoteException re) {
+            /* ignore */
+        }
     }
 
     private void handleMotionEventStateDelegating(MotionEvent event,
@@ -423,7 +404,7 @@
             final float eventX = event.getX();
             final float eventY = event.getY();
             if (mMagnificationController.isMagnifying()
-                    && mMagnifiedFrame.contains((int) eventX, (int) eventY)) {
+                    && mMagnifiedBounds.contains((int) eventX, (int) eventY)) {
                 final float scale = mMagnificationController.getScale();
                 final float scaledOffsetX = mMagnificationController.getOffsetX();
                 final float scaledOffsetY = mMagnificationController.getOffsetY();
@@ -619,7 +600,7 @@
                     }
                     final float eventX = event.getX();
                     final float eventY = event.getY();
-                    if (mMagnifiedFrame.contains((int) eventX, (int) eventY)) {
+                    if (mMagnifiedBounds.contains((int) eventX, (int) eventY)) {
                         if (mLastMoveOutsideMagnifiedRegion) {
                             mLastMoveOutsideMagnifiedRegion = false;
                             mMagnificationController.setMagnifiedRegionCenter(eventX,
@@ -635,7 +616,6 @@
                 case MotionEvent.ACTION_UP: {
                     if (!mTranslationEnabledBeforePan) {
                         mMagnificationController.reset(true);
-                        mViewportWindow.setShown(false, true);
                     }
                     clear();
                     transitionToState(STATE_DETECTING);
@@ -693,7 +673,7 @@
             switch (action) {
                 case MotionEvent.ACTION_DOWN: {
                     mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
-                    if (!mMagnifiedFrame.contains((int) event.getX(),
+                    if (!mMagnifiedBounds.contains((int) event.getX(),
                             (int) event.getY())) {
                         transitionToDelegatingStateAndClear();
                         return;
@@ -735,7 +715,7 @@
                         return;
                     }
                     mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD);
-                    if (!mMagnifiedFrame.contains((int) event.getX(), (int) event.getY())) {
+                    if (!mMagnifiedBounds.contains((int) event.getX(), (int) event.getY())) {
                          transitionToDelegatingStateAndClear();
                          return;
                     }
@@ -861,10 +841,8 @@
             if (!mMagnificationController.isMagnifying()) {
                 mMagnificationController.setScaleAndMagnifiedRegionCenter(getPersistedScale(),
                         up.getX(), up.getY(), true);
-                mViewportWindow.setShown(true, true);
             } else {
                 mMagnificationController.reset(true);
-                mViewportWindow.setShown(false, true);
             }
         }
 
@@ -876,7 +854,6 @@
             mTranslationEnabledBeforePan = mMagnificationController.isMagnifying();
             mMagnificationController.setScaleAndMagnifiedRegionCenter(getPersistedScale(),
                     down.getX(), down.getY(), true);
-            mViewportWindow.setShown(true, true);
             transitionToState(STATE_VIEWPORT_DRAGGING);
         }
     }
@@ -1043,10 +1020,12 @@
         }
 
         public void setScale(float scale, float pivotX, float pivotY, boolean animate) {
+            Rect magnifiedFrame = mTempRect;
+            mMagnifiedBounds.getBounds(magnifiedFrame);
             MagnificationSpec spec = mCurrentMagnificationSpec;
             final float oldScale = spec.scale;
-            final float oldCenterX = (-spec.offsetX + mMagnifiedFrame.width() / 2) / oldScale;
-            final float oldCenterY = (-spec.offsetY + mMagnifiedFrame.height() / 2) / oldScale;
+            final float oldCenterX = (-spec.offsetX + magnifiedFrame.width() / 2) / oldScale;
+            final float oldCenterY = (-spec.offsetY + magnifiedFrame.height() / 2) / oldScale;
             final float normPivotX = (-spec.offsetX + pivotX) / oldScale;
             final float normPivotY = (-spec.offsetY + pivotY) / oldScale;
             final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale);
@@ -1099,24 +1078,30 @@
 
         public void updateMagnificationSpec(float scale, float magnifiedCenterX,
                 float magnifiedCenterY) {
+            Rect magnifiedFrame = mTempRect;
+            mMagnifiedBounds.getBounds(magnifiedFrame);
             mCurrentMagnificationSpec.scale = scale;
-            final int viewportWidth = mMagnifiedFrame.width();
+            final int viewportWidth = magnifiedFrame.width();
             final float nonNormOffsetX = viewportWidth / 2 - magnifiedCenterX * scale;
             mCurrentMagnificationSpec.offsetX = Math.min(Math.max(nonNormOffsetX,
                     getMinOffsetX()), 0);
-            final int viewportHeight = mMagnifiedFrame.height();
+            final int viewportHeight = magnifiedFrame.height();
             final float nonNormOffsetY = viewportHeight / 2 - magnifiedCenterY * scale;
             mCurrentMagnificationSpec.offsetY = Math.min(Math.max(nonNormOffsetY,
                     getMinOffsetY()), 0);
         }
 
         private float getMinOffsetX() {
-            final float viewportWidth = mMagnifiedFrame.width();
+            Rect magnifiedFrame = mTempRect;
+            mMagnifiedBounds.getBounds(magnifiedFrame);
+            final float viewportWidth = magnifiedFrame.width();
             return viewportWidth - viewportWidth * mCurrentMagnificationSpec.scale;
         }
 
         private float getMinOffsetY() {
-            final float viewportHeight = mMagnifiedFrame.height();
+            Rect magnifiedFrame = mTempRect;
+            mMagnifiedBounds.getBounds(magnifiedFrame);
+            final float viewportHeight = magnifiedFrame.height();
             return viewportHeight - viewportHeight * mCurrentMagnificationSpec.scale;
         }
 
@@ -1138,7 +1123,7 @@
                 mSentMagnificationSpec.scale = spec.scale;
                 mSentMagnificationSpec.offsetX = spec.offsetX;
                 mSentMagnificationSpec.offsetY = spec.offsetY;
-                mDisplayMagnifier.setMagnificationSpec(ScreenMagnifier.this,
+                mWindowManager.setMagnificationSpec(
                         MagnificationSpec.obtain(spec));
             } catch (RemoteException re) {
                 /* ignore */
@@ -1146,12 +1131,11 @@
         }
     }
 
-    private static final class ScreenStateObserver extends BroadcastReceiver {
+    private final class ScreenStateObserver extends BroadcastReceiver {
         private static final int MESSAGE_ON_SCREEN_STATE_CHANGE = 1;
 
         private final Context mContext;
         private final MagnificationController mMagnificationController;
-        private final ViewportWindow mViewportWindow;
 
         private final Handler mHandler = new Handler() {
             @Override
@@ -1166,10 +1150,9 @@
         };
 
         public ScreenStateObserver(Context context,
-                MagnificationController magnificationController, ViewportWindow viewportWindow) {
+                MagnificationController magnificationController) {
             mContext = context;
             mMagnificationController = magnificationController;
-            mViewportWindow = viewportWindow;
             mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_SCREEN_OFF));
         }
 
@@ -1181,282 +1164,13 @@
         public void onReceive(Context context, Intent intent) {
             mHandler.obtainMessage(MESSAGE_ON_SCREEN_STATE_CHANGE,
                     intent.getAction()).sendToTarget();
-            }
+        }
 
         private void handleOnScreenStateChange(String action) {
             if (mMagnificationController.isMagnifying()
                     && isScreenMagnificationAutoUpdateEnabled(mContext)) {
                 mMagnificationController.reset(false);
-                mViewportWindow.setShown(false, false);
             }
         }
     }
-
-    private static final class ViewportWindow implements DisplayListener {
-        private static final String WINDOW_TITLE = "Magnification Overlay";
-
-        private static final String PROPERTY_NAME_ALPHA = "alpha";
-        private static final String PROPERTY_NAME_BOUNDS = "bounds";
-
-        private static final int MIN_ALPHA = 0;
-        private static final int MAX_ALPHA = 255;
-
-        private final Rect mBounds = new Rect();
-
-        private final ValueAnimator mResizeFrameAnimator;
-        private final ValueAnimator mShowHideFrameAnimator;
-
-        private final WindowManager mWindowManager;
-        private final DisplayManager mDisplayManager;
-        private final Display mDisplay;
-        private final DisplayInfo mDisplayInfo = new DisplayInfo();
-        private final int mDisplayId;
-
-        private final ContentView mWindowContent;
-        private final WindowManager.LayoutParams mWindowParams;
-
-        private boolean mShown;
-        private int mAlpha;
-
-        public ViewportWindow(Context context, int displayId) {
-            mWindowManager = (WindowManager) context.getSystemService(Service.WINDOW_SERVICE);
-
-            mDisplayId = displayId;
-            mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
-            mDisplay = mDisplayManager.getDisplay(displayId);
-            mDisplayManager.registerDisplayListener(this, null);
-            updateDisplayInfo();
-
-            ViewGroup.LayoutParams contentParams = new ViewGroup.LayoutParams(
-                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
-            mWindowContent = new ContentView(context);
-            mWindowContent.setLayoutParams(contentParams);
-            mWindowContent.setBackgroundColor(R.color.transparent);
-
-            mWindowParams = new WindowManager.LayoutParams(
-                    WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY);
-            mWindowParams.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                    | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-            mWindowParams.setTitle(WINDOW_TITLE);
-            mWindowParams.gravity = Gravity.CENTER;
-            mWindowParams.width = mDisplayInfo.logicalWidth;
-            mWindowParams.height = mDisplayInfo.logicalHeight;
-            mWindowParams.format = PixelFormat.TRANSLUCENT;
-
-            Interpolator interpolator = new DecelerateInterpolator(2.5f);
-            final long longAnimationDuration = context.getResources().getInteger(
-                    com.android.internal.R.integer.config_longAnimTime);
-
-            mShowHideFrameAnimator = ObjectAnimator.ofInt(this, PROPERTY_NAME_ALPHA,
-                    MIN_ALPHA, MAX_ALPHA);
-            mShowHideFrameAnimator.setInterpolator(interpolator);
-            mShowHideFrameAnimator.setDuration(longAnimationDuration);
-            mShowHideFrameAnimator.addListener(new AnimatorListener() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (mShowHideFrameAnimator.getAnimatedValue().equals(MIN_ALPHA)) {
-                        hide();
-                    }
-                }
-
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    /* do nothing - stub */
-                }
-
-                @Override
-                public void onAnimationCancel(Animator animation) {
-                    /* do nothing - stub */
-                }
-
-                @Override
-                public void onAnimationRepeat(Animator animation) {
-                    /* do nothing - stub */
-                }
-            });
-
-            Property<ViewportWindow, Rect> property = Property.of(ViewportWindow.class,
-                    Rect.class, PROPERTY_NAME_BOUNDS);
-            TypeEvaluator<Rect> evaluator = new TypeEvaluator<Rect>() {
-                private final Rect mReusableResultRect = new Rect();
-
-                @Override
-                public Rect evaluate(float fraction, Rect fromFrame, Rect toFrame) {
-                    Rect result = mReusableResultRect;
-                    result.left = (int) (fromFrame.left
-                            + (toFrame.left - fromFrame.left) * fraction);
-                    result.top = (int) (fromFrame.top
-                            + (toFrame.top - fromFrame.top) * fraction);
-                    result.right = (int) (fromFrame.right
-                            + (toFrame.right - fromFrame.right) * fraction);
-                    result.bottom = (int) (fromFrame.bottom
-                            + (toFrame.bottom - fromFrame.bottom) * fraction);
-                    return result;
-                }
-            };
-            mResizeFrameAnimator = ObjectAnimator.ofObject(this, property,
-                    evaluator, mBounds, mBounds);
-            mResizeFrameAnimator.setDuration(longAnimationDuration);
-            mResizeFrameAnimator.setInterpolator(interpolator);
-        }
-
-        public boolean isShown() {
-            return mShown;
-        }
-
-        public void setShown(boolean shown, boolean animate) {
-            if (isShown() == shown) {
-                return;
-            }
-            if (animate) {
-                if (mShowHideFrameAnimator.isRunning()) {
-                    mShowHideFrameAnimator.reverse();
-                } else {
-                    if (shown) {
-                        show();
-                        mShowHideFrameAnimator.start();
-                    } else {
-                        mShowHideFrameAnimator.reverse();
-                    }
-                }
-            } else {
-                mShowHideFrameAnimator.cancel();
-                if (shown) {
-                    show();
-                } else {
-                    hide();
-                }
-            }
-        }
-
-        private void show() {
-            if (mShown) {
-                return;
-            }
-            mShown = true;
-            mWindowManager.addView(mWindowContent, mWindowParams);
-            if (DEBUG_VIEWPORT_WINDOW) {
-                Slog.i(LOG_TAG, "ViewportWindow shown.");
-            }
-        }
-
-        public void setBounds(Rect bounds, boolean animate) {
-            if (getBounds().equals(bounds)) {
-                return;
-            }
-            if (animate) {
-                if (mResizeFrameAnimator.isRunning()) {
-                    mResizeFrameAnimator.cancel();
-                }
-                mResizeFrameAnimator.setObjectValues(getBounds(), bounds);
-                mResizeFrameAnimator.start();
-            } else {
-                setBounds(bounds);
-            }
-        }
-
-        private void hide() {
-            if (!mShown) {
-                return;
-            }
-            mShown = false;
-            mWindowManager.removeView(mWindowContent);
-            if (DEBUG_VIEWPORT_WINDOW) {
-                Slog.i(LOG_TAG, "ViewportWindow hidden.");
-            }
-        }
-
-        @SuppressWarnings("unused")
-        // Called reflectively from an animator.
-        public int getAlpha() {
-            return mAlpha;
-        }
-
-        @SuppressWarnings("unused")
-        // Called reflectively from an animator.
-        public void setAlpha(int alpha) {
-            if (mAlpha == alpha) {
-                return;
-            }
-            mAlpha = alpha;
-            if (mShown) {
-                mWindowContent.invalidate();
-            }
-            if (DEBUG_VIEWPORT_WINDOW) {
-                Slog.i(LOG_TAG, "ViewportFrame set alpha: " + alpha);
-            }
-        }
-
-        public Rect getBounds() {
-            return mBounds;
-        }
-
-        public void setBounds(Rect bounds) {
-            if (mBounds.equals(bounds)) {
-                return;
-            }
-            mBounds.set(bounds);
-            if (mShown) {
-                mWindowContent.invalidate();
-            }
-            if (DEBUG_VIEWPORT_WINDOW) {
-                Slog.i(LOG_TAG, "ViewportFrame set bounds: " + bounds);
-            }
-        }
-
-        public void rotationChanged() {
-            mWindowParams.width = mDisplayInfo.logicalWidth;
-            mWindowParams.height = mDisplayInfo.logicalHeight;
-            if (mShown) {
-                mWindowManager.updateViewLayout(mWindowContent, mWindowParams);
-            }
-        }
-
-        private final class ContentView extends View {
-            private final Drawable mHighlightFrame;
-
-            public ContentView(Context context) {
-                super(context);
-                mHighlightFrame = context.getResources().getDrawable(
-                        R.drawable.magnified_region_frame);
-            }
-
-            @Override
-            public void onDraw(Canvas canvas) {
-                canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
-                mHighlightFrame.setBounds(mBounds);
-                mHighlightFrame.setAlpha(mAlpha);
-                mHighlightFrame.draw(canvas);
-            }
-        }
-
-        private void updateDisplayInfo() {
-            if (!mDisplay.getDisplayInfo(mDisplayInfo)) {
-                Slog.e(LOG_TAG, "Display is not valid.");
-            }
-        }
-
-        public void destroy() {
-            setShown(false, true);
-            mDisplayManager.unregisterDisplayListener(this);
-        }
-
-        @Override
-        public void onDisplayChanged(int displayId) {
-            if (mDisplayId == displayId) {
-                updateDisplayInfo();
-            }
-        }
-
-        @Override
-        public void onDisplayAdded(int displayId) {
-            /* do nothing */
-        }
-
-        @Override
-        public void onDisplayRemoved(int displayId) {
-            /* do nothing */
-        }
-    }
 }
diff --git a/services/java/com/android/server/wm/DisplayMagnificationMediator.java b/services/java/com/android/server/wm/DisplayMagnificationMediator.java
deleted file mode 100644
index 0e3a6e1..0000000
--- a/services/java/com/android/server/wm/DisplayMagnificationMediator.java
+++ /dev/null
@@ -1,614 +0,0 @@
-package com.android.server.wm;
-
-import android.Manifest;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManager.DisplayListener;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Process;
-import android.os.RemoteException;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.Pools.SynchronizedPool;
-import android.view.DisplayInfo;
-import android.view.IDisplayMagnificationController;
-import android.view.IDisplayMagnificationMediator;
-import android.view.MagnificationSpec;
-import android.view.Surface;
-import android.view.WindowManager;
-import android.view.WindowManagerPolicy;
-
-import com.android.internal.os.SomeArgs;
-import com.android.internal.policy.impl.PhoneWindowManager;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-
-final class DisplayMagnificationMediator extends IDisplayMagnificationMediator.Stub
-        implements DisplayListener {
-    private static final String LOG_TAG = DisplayMagnificationMediator.class.getSimpleName();
-
-    private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
-    private static final boolean DEBUG_ROTATION = false;
-    private static final boolean DEBUG_LAYERS = false;
-    private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
-
-    private static final int MESSAGE_NOTIFY_MAGNIFIED_FRAME_CHANGED = 1;
-    private static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
-    private static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
-    private static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
-
-    private static final String METHOD_SIGNATURE_ADD_CONTROLLER =
-            "addController(int, IDisplayMagnificationController)";
-    private static final String METHOD_SIGNATURE_REMOVE_CONTROLLER =
-            "removeController(IDisplayMagnificationController, int)";
-    private static final String METHOD_SIGNATURE_SET_MAGNIFICATION_SPEC =
-            "setMagnificationSpec(IDisplayMagnificationController, MagnificationSpec)";
-
-    private final Rect mTempRect = new Rect();
-    private final Rect mTempRect1 = new Rect();
-    private final Region mTempRegion = new Region();
-
-    private final SparseArray<DisplayState> mDisplayStates =
-            new SparseArray<DisplayMagnificationMediator.DisplayState>();
-
-    private final Context mContext;
-    private final WindowManagerService mWindowManagerService;
-
-    private final Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message message) {
-            switch (message.what) {
-                case MESSAGE_NOTIFY_MAGNIFIED_FRAME_CHANGED: {
-                    SomeArgs args = (SomeArgs) message.obj;
-                    IDisplayMagnificationController client =
-                            (IDisplayMagnificationController) args.arg1;
-                    final int left = args.argi1;
-                    final int top = args.argi2;
-                    final int right = args.argi3;
-                    final int bottom = args.argi4;
-                    try {
-                        client.onMagnifedFrameChanged(left, top, right, bottom);
-                    } catch (RemoteException re) {
-                        /* ignore */
-                    } finally {
-                        args.recycle();
-                    }
-                } break;
-                case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
-                    SomeArgs args = (SomeArgs) message.obj;
-                    IDisplayMagnificationController client =
-                            (IDisplayMagnificationController) args.arg1;
-                    final int left = args.argi1;
-                    final int top = args.argi2;
-                    final int right = args.argi3;
-                    final int bottom = args.argi4;
-                    try {
-                        client.onRectangleOnScreenRequested(left, top, right, bottom);
-                    } catch (RemoteException re) {
-                        /* ignore */
-                    } finally {
-                        args.recycle();
-                    }
-                } break;
-                case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
-                    IDisplayMagnificationController client =
-                            (IDisplayMagnificationController) message.obj;
-                    try {
-                        client.onUserContextChanged();
-                    } catch (RemoteException re) {
-                        /* ignore */
-                    }
-                } break;
-                case MESSAGE_NOTIFY_ROTATION_CHANGED: {
-                    IDisplayMagnificationController client =
-                            (IDisplayMagnificationController) message.obj;
-                    final int rotation = message.arg1;
-                    try {
-                        client.onRotationChanged(rotation);
-                    } catch (RemoteException re) {
-                        /* ignore */
-                    }
-                } break;
-            }
-        }
-    };
-
-    public DisplayMagnificationMediator(WindowManagerService windowManagerService) {
-        mContext = windowManagerService.mContext;
-        mWindowManagerService = windowManagerService;
-        DisplayManager displayManager = (DisplayManager)
-                mContext.getSystemService(Context.DISPLAY_SERVICE);
-        displayManager.registerDisplayListener(this, mHandler);
-    }
-
-    @Override
-    public void addController(int displayId, IDisplayMagnificationController controller) {
-        enforceCallingPermission(Manifest.permission.MAGNIFY_DISPLAY,
-                METHOD_SIGNATURE_ADD_CONTROLLER);
-        synchronized (mWindowManagerService.mWindowMap) {
-            DisplayState displayState = mDisplayStates.get(displayId);
-            if (displayState != null) {
-                displayState.clearLw();
-            }
-            mDisplayStates.remove(displayId);
-            mDisplayStates.put(displayId, new DisplayState(displayId, controller));
-        }
-    }
-
-    @Override
-    public void removeController(IDisplayMagnificationController controller) {
-        enforceCallingPermission(Manifest.permission.MAGNIFY_DISPLAY,
-                METHOD_SIGNATURE_REMOVE_CONTROLLER);
-        synchronized (mWindowManagerService.mWindowMap) {
-            final int displayStateCount = mDisplayStates.size();
-            for (int i = 0; i < displayStateCount; i++) {
-                DisplayState displayState = mDisplayStates.valueAt(i);
-                if (displayState.mClient.asBinder() == controller.asBinder()) {
-                    displayState.clearLw();
-                    mDisplayStates.removeAt(i);
-                    return;
-                }
-            }
-        }
-    }
-
-    @Override
-    public void setMagnificationSpec(IDisplayMagnificationController controller,
-            MagnificationSpec spec) {
-        enforceCallingPermission(Manifest.permission.MAGNIFY_DISPLAY,
-                METHOD_SIGNATURE_SET_MAGNIFICATION_SPEC);
-        synchronized (mWindowManagerService.mWindowMap) {
-            DisplayState displayState = null;
-            final int displayStateCount = mDisplayStates.size();
-            for (int i = 0; i < displayStateCount; i++) {
-                DisplayState candidate = mDisplayStates.valueAt(i);
-                if (candidate.mClient.asBinder() == controller.asBinder()) {
-                    displayState = candidate;
-                    break;
-                }
-            }
-            if (displayState == null) {
-                Slog.e(LOG_TAG, "Setting magnification spec for unregistered controller "
-                        + controller);
-                return;
-            }
-            displayState.mMagnificationSpec.initialize(spec.scale, spec.offsetX,
-                    spec.offsetY);
-            spec.recycle();
-            mWindowManagerService.scheduleAnimationLocked();
-        }
-    }
-
-    @Override
-    public MagnificationSpec getCompatibleMagnificationSpec(IBinder windowToken) {
-        synchronized (mWindowManagerService.mWindowMap) {
-            WindowState windowState = mWindowManagerService.mWindowMap.get(windowToken);
-            if (windowState == null) {
-                return null;
-            }
-            MagnificationSpec spec = getMagnificationSpecLw(windowState);
-            if ((spec == null || spec.isNop()) && windowState.mGlobalScale == 1.0f) {
-                return null;
-            }
-            if (spec == null) {
-                spec = MagnificationSpec.obtain();
-            } else {
-                spec = MagnificationSpec.obtain(spec);
-            }
-            spec.scale *= windowState.mGlobalScale;
-            return spec;
-        }
-    }
-
-    @Override
-    public void onDisplayAdded(int displayId) {
-        /* do nothing */
-    }
-
-    @Override
-    public void onDisplayRemoved(int displayId) {
-        synchronized (mWindowManagerService.mWindowMap) {
-            DisplayState displayState = mDisplayStates.get(displayId);
-            if (displayState != null) {
-                displayState.clearLw();
-                mDisplayStates.remove(displayId);
-            }
-        }
-    }
-
-    @Override
-    public void onDisplayChanged(int displayId) {
-        /* do nothing */
-    }
-
-    public void onRectangleOnScreenRequestedLw(WindowState windowState, Rect rectangle,
-            boolean immediate) {
-        DisplayState displayState = mDisplayStates.get(windowState.getDisplayId());
-        if (displayState == null) {
-            return;
-        }
-        if (DEBUG_RECTANGLE_REQUESTED) {
-            Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle
-                    + " displayId: " + windowState.getDisplayId());
-        }
-        if (!displayState.isMagnifyingLw()) {
-            return;
-        }
-        Rect magnifiedRegionBounds = mTempRect1;
-        displayState.getMagnifiedFrameInContentCoordsLw(magnifiedRegionBounds);
-        if (magnifiedRegionBounds.contains(rectangle)) {
-            return;
-        }
-        SomeArgs args = SomeArgs.obtain();
-        args.arg1 = displayState.mClient;
-        args.argi1 = rectangle.left;
-        args.argi2 = rectangle.top;
-        args.argi3 = rectangle.right;
-        args.argi4 = rectangle.bottom;
-        mHandler.obtainMessage(MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
-                args).sendToTarget();
-    }
-
-    public void onWindowLayersChangedLw(int displayId) {
-        DisplayState displayState = mDisplayStates.get(displayId);
-        if (displayState == null) {
-            return;
-        }
-        if (DEBUG_LAYERS) {
-            Slog.i(LOG_TAG, "Layers changed displayId: " + displayId);
-        }
-        displayState.mViewport.recomputeBoundsLw();
-    }
-
-    public void onRotationChangedLw(int displayId, int rotation) {
-        DisplayState displayState = mDisplayStates.get(displayId);
-        if (displayState == null) {
-            return;
-        }
-        if (DEBUG_ROTATION) {
-            Slog.i(LOG_TAG, "Rotaton: " + Surface.rotationToString(rotation)
-                    + " displayId: " + displayId);
-        }
-        displayState.mViewport.recomputeBoundsLw();
-        mHandler.obtainMessage(MESSAGE_NOTIFY_ROTATION_CHANGED, rotation, 0,
-                displayState.mClient).sendToTarget();
-    }
-
-    public void onWindowTransitionLw(WindowState windowState, int transition) {
-        DisplayState displayState = mDisplayStates.get(windowState.getDisplayId());
-        if (displayState == null) {
-            return;
-        }
-        if (DEBUG_WINDOW_TRANSITIONS) {
-            Slog.i(LOG_TAG, "Window transition: "
-                    + PhoneWindowManager.windowTransitionToString(transition)
-                    + " displayId: " + windowState.getDisplayId());
-        }
-        final boolean magnifying = displayState.isMagnifyingLw();
-        if (magnifying) {
-            switch (transition) {
-                case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN:
-                case WindowManagerPolicy.TRANSIT_TASK_OPEN:
-                case WindowManagerPolicy.TRANSIT_TASK_TO_FRONT:
-                case WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN:
-                case WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE:
-                case WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN: {
-                    mHandler.obtainMessage(MESSAGE_NOTIFY_USER_CONTEXT_CHANGED,
-                            displayState.mClient).sendToTarget();
-                }
-            }
-        }
-        final int type = windowState.mAttrs.type;
-        if (type == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
-                || type == WindowManager.LayoutParams.TYPE_INPUT_METHOD
-                || type == WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG
-                || type == WindowManager.LayoutParams.TYPE_KEYGUARD
-                || type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) {
-            switch (transition) {
-                case WindowManagerPolicy.TRANSIT_ENTER:
-                case WindowManagerPolicy.TRANSIT_SHOW:
-                case WindowManagerPolicy.TRANSIT_EXIT:
-                case WindowManagerPolicy.TRANSIT_HIDE: {
-                    displayState.mViewport.recomputeBoundsLw();
-                } break;
-            }
-        }
-        switch (transition) {
-            case WindowManagerPolicy.TRANSIT_ENTER:
-            case WindowManagerPolicy.TRANSIT_SHOW: {
-                if (!magnifying) {
-                    break;
-                }
-                switch (type) {
-                    case WindowManager.LayoutParams.TYPE_APPLICATION:
-                    case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
-                    case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
-                    case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
-                    case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
-                    case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
-                    case WindowManager.LayoutParams.TYPE_PHONE:
-                    case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
-                    case WindowManager.LayoutParams.TYPE_TOAST:
-                    case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
-                    case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
-                    case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
-                    case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
-                    case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
-                    case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
-                    case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL:
-                    case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: {
-                        Rect magnifiedRegionBounds = mTempRect1;
-                        displayState.getMagnifiedFrameInContentCoordsLw(magnifiedRegionBounds);
-                        Rect touchableRegionBounds = mTempRect;
-                        windowState.getTouchableRegion(mTempRegion);
-                        mTempRegion.getBounds(touchableRegionBounds);
-                        if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
-                            SomeArgs args = SomeArgs.obtain();
-                            args.arg1 = displayState.mClient;
-                            args.argi1 = touchableRegionBounds.left;
-                            args.argi2 = touchableRegionBounds.top;
-                            args.argi3 = touchableRegionBounds.right;
-                            args.argi4 = touchableRegionBounds.bottom;
-                            mHandler.obtainMessage(MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
-                                    args).sendToTarget();
-                        }
-                    } break;
-                } break;
-            }
-        }
-    }
-
-    public MagnificationSpec getMagnificationSpecLw(WindowState windowState) {
-        DisplayState displayState = mDisplayStates.get(windowState.getDisplayId());
-        if (displayState == null) {
-            return null;
-        }
-        MagnificationSpec spec = displayState.mMagnificationSpec;
-        if (spec != null && !spec.isNop()) {
-            if (windowState.mAttachedWindow != null) {
-                if (!canMagnifyWindow(windowState.mAttachedWindow.mAttrs.type)) {
-                    return null;
-                }
-            }
-            if (!canMagnifyWindow(windowState.mAttrs.type)) {
-                return null;
-            }
-        }
-        return spec;
-    }
-
-    private void enforceCallingPermission(String permission, String function) {
-        if (Process.myPid() == Binder.getCallingPid()) {
-            return;
-        }
-        if (mContext.checkCallingPermission(permission) != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("The caller does not have " + permission
-                    + " required to call " + function);
-        }
-    }
-
-    private static boolean canMagnifyWindow(int type) {
-        switch (type) {
-            case WindowManager.LayoutParams.TYPE_INPUT_METHOD:
-            case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG:
-            case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR:
-            case WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY: {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private static final class Viewport {
-
-        private final ArrayList<WindowStateInfo> mTempWindowStateInfoList =
-                new ArrayList<WindowStateInfo>();
-
-        private final Rect mTempRect1 = new Rect();
-        private final Rect mTempRect2 = new Rect();
-        private final Rect mTempRect3 = new Rect();
-
-        private final Rect mBounds = new Rect();
-        private final Handler mHandler;
-        private final IDisplayMagnificationController mClient;
-        private final WindowManagerService mWindowManagerService;
-        private final DisplayInfo mDisplayInfo;
-
-        private final int mDisplayId;
-
-        public Viewport(Context context, int displayId, Handler handler,
-                IDisplayMagnificationController client, WindowManagerService windowManagerService) {
-            mDisplayId = displayId;
-            mHandler = handler;
-            mWindowManagerService = windowManagerService;
-            mDisplayInfo = mWindowManagerService.getDisplayContentLocked(displayId)
-                    .getDisplayInfo();
-            mClient = client;
-            recomputeBoundsLw();
-        }
-
-        private final Comparator<WindowStateInfo> mWindowInfoInverseComparator =
-                new Comparator<WindowStateInfo>() {
-            @Override
-            public int compare(WindowStateInfo lhs, WindowStateInfo rhs) {
-                if (lhs.mWindowState.mLayer != rhs.mWindowState.mLayer) {
-                    return rhs.mWindowState.mLayer - lhs.mWindowState.mLayer;
-                }
-                if (lhs.mTouchableRegion.top != rhs.mTouchableRegion.top) {
-                    return rhs.mTouchableRegion.top - lhs.mTouchableRegion.top;
-                }
-                if (lhs.mTouchableRegion.left != rhs.mTouchableRegion.left) {
-                    return rhs.mTouchableRegion.left - lhs.mTouchableRegion.left;
-                }
-                if (lhs.mTouchableRegion.right != rhs.mTouchableRegion.right) {
-                    return rhs.mTouchableRegion.right - lhs.mTouchableRegion.right;
-                }
-                if (lhs.mTouchableRegion.bottom != rhs.mTouchableRegion.bottom) {
-                    return rhs.mTouchableRegion.bottom - lhs.mTouchableRegion.bottom;
-                }
-                return 0;
-            }
-        };
-
-        public void recomputeBoundsLw() {
-            Rect magnifiedFrame = mBounds;
-            magnifiedFrame.set(0, 0, 0, 0);
-
-            Rect oldmagnifiedFrame = mTempRect3;
-            oldmagnifiedFrame.set(magnifiedFrame);
-
-            Rect availableFrame = mTempRect1;
-            availableFrame.set(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
-
-            ArrayList<WindowStateInfo> visibleWindows = mTempWindowStateInfoList;
-            visibleWindows.clear();
-            getVisibleWindowsLw(visibleWindows);
-
-            Collections.sort(visibleWindows, mWindowInfoInverseComparator);
-
-            final int visibleWindowCount = visibleWindows.size();
-            for (int i = 0; i < visibleWindowCount; i++) {
-                WindowStateInfo info = visibleWindows.get(i);
-                if (info.mWindowState.mAttrs.type == WindowManager
-                        .LayoutParams.TYPE_MAGNIFICATION_OVERLAY) {
-                    continue;
-                }
-                Rect windowFrame = mTempRect2;
-                windowFrame.set(info.mTouchableRegion);
-                if (canMagnifyWindow(info.mWindowState.mAttrs.type)) {
-                    magnifiedFrame.union(windowFrame);
-                    magnifiedFrame.intersect(availableFrame);
-                } else {
-                    subtract(windowFrame, magnifiedFrame);
-                    subtract(availableFrame, windowFrame);
-                }
-                if (availableFrame.equals(magnifiedFrame)) {
-                    break;
-                }
-            }
-            for (int i = visibleWindowCount - 1; i >= 0; i--) {
-                visibleWindows.remove(i).recycle();
-            }
-
-            final int displayWidth = mDisplayInfo.logicalWidth;
-            final int displayHeight = mDisplayInfo.logicalHeight;
-            magnifiedFrame.intersect(0, 0, displayWidth, displayHeight);
-
-            if (!oldmagnifiedFrame.equals(magnifiedFrame)) {
-                SomeArgs args = SomeArgs.obtain();
-                args.arg1 = mClient;
-                args.argi1 = magnifiedFrame.left;
-                args.argi2 = magnifiedFrame.top;
-                args.argi3 = magnifiedFrame.right;
-                args.argi4 = magnifiedFrame.bottom;
-                mHandler.obtainMessage(MESSAGE_NOTIFY_MAGNIFIED_FRAME_CHANGED, args)
-                        .sendToTarget();
-            }
-        }
-
-        private void getVisibleWindowsLw(ArrayList<WindowStateInfo> outWindowStates) {
-            DisplayContent displayContent = mWindowManagerService.getDisplayContentLocked(
-                    mDisplayId);
-            WindowList windowList = displayContent.getWindowList();
-            final int windowCount = windowList.size();
-            for (int i = 0; i < windowCount; i++) {
-                WindowState windowState = windowList.get(i);
-                if (windowState.isVisibleLw() || windowState.mAttrs.type == WindowManager
-                        .LayoutParams.TYPE_UNIVERSE_BACKGROUND) {
-                    outWindowStates.add(WindowStateInfo.obtain(windowState));
-                }
-            }
-        }
-
-        public Rect getBoundsLw() {
-            return mBounds;
-        }
-
-        private static boolean subtract(Rect lhs, Rect rhs) {
-            if (lhs.right < rhs.left || lhs.left  > rhs.right
-                    || lhs.bottom < rhs.top || lhs.top > rhs.bottom) {
-                return false;
-            }
-            if (lhs.left < rhs.left) {
-                lhs.right = rhs.left;
-            }
-            if (lhs.top < rhs.top) {
-                lhs.bottom = rhs.top;
-            }
-            if (lhs.right > rhs.right) {
-                lhs.left = rhs.right;
-            }
-            if (lhs.bottom > rhs.bottom) {
-                lhs.top = rhs.bottom;
-            }
-            return true;
-        }
-
-        private static final class WindowStateInfo {
-            private static final int MAX_POOL_SIZE = 30;
-
-            private static final SynchronizedPool<WindowStateInfo> sPool =
-                    new SynchronizedPool<WindowStateInfo>(MAX_POOL_SIZE);
-
-            private static final Region mTempRegion = new Region();
-
-            public WindowState mWindowState;
-            public final Rect mTouchableRegion = new Rect();
-
-            public static WindowStateInfo obtain(WindowState windowState) {
-                WindowStateInfo info = sPool.acquire();
-                if (info == null) {
-                    info = new WindowStateInfo();
-                }
-                info.mWindowState = windowState;
-                windowState.getTouchableRegion(mTempRegion);
-                mTempRegion.getBounds(info.mTouchableRegion);
-                return info;
-            }
-
-            public void recycle() {
-                mWindowState = null;
-                mTouchableRegion.setEmpty();
-                sPool.release(this);
-            }
-        }
-    }
-
-    private final class DisplayState {
-        final int mDisplayId;
-        final MagnificationSpec mMagnificationSpec;
-        final Viewport mViewport;
-        final IDisplayMagnificationController mClient;
-
-        DisplayState(int displayId, IDisplayMagnificationController client) {
-            mDisplayId = displayId;
-            mClient = client;
-            mMagnificationSpec = MagnificationSpec.obtain();
-            mViewport = new Viewport(mContext, mDisplayId, mHandler,
-                    mClient, mWindowManagerService);
-        }
-
-        public boolean isMagnifyingLw() {
-            return mMagnificationSpec.scale > 1.0f;
-        }
-
-        private void getMagnifiedFrameInContentCoordsLw(Rect rect) {
-            MagnificationSpec spec = mMagnificationSpec;
-            rect.set(mViewport.getBoundsLw());
-            rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
-            rect.scale(1.0f / spec.scale);
-        }
-
-        public void clearLw() {
-            mMagnificationSpec.recycle();
-        }
-    }
-}
diff --git a/services/java/com/android/server/wm/DisplayMagnifier.java b/services/java/com/android/server/wm/DisplayMagnifier.java
new file mode 100644
index 0000000..cd5ae4b
--- /dev/null
+++ b/services/java/com/android/server/wm/DisplayMagnifier.java
@@ -0,0 +1,732 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.app.Service;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Pools.SimplePool;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TypedValue;
+import android.view.IMagnificationCallbacks;
+import android.view.MagnificationSpec;
+import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
+import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import com.android.internal.R;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.policy.impl.PhoneWindowManager;
+
+/**
+ * This class is a part of the window manager and encapsulates the
+ * functionality related to display magnification.
+ */
+final class DisplayMagnifier {
+    private static final String LOG_TAG = DisplayMagnifier.class.getSimpleName();
+
+    private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
+    private static final boolean DEBUG_ROTATION = false;
+    private static final boolean DEBUG_LAYERS = false;
+    private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
+    private static final boolean DEBUG_VIEWPORT_WINDOW = false;
+
+    private final Rect mTempRect1 = new Rect();
+    private final Rect mTempRect2 = new Rect();
+
+    private final Region mTempRegion1 = new Region();
+    private final Region mTempRegion2 = new Region();
+    private final Region mTempRegion3 = new Region();
+    private final Region mTempRegion4 = new Region();
+
+    private final Context mContext;
+    private final WindowManagerService mWindowManagerService;
+    private final MagnifiedViewport mMagnifedViewport;
+    private final Handler mHandler;
+
+    private final IMagnificationCallbacks mCallbacks;
+
+    private final long mLongAnimationDuration;
+
+    public DisplayMagnifier(WindowManagerService windowManagerService,
+            IMagnificationCallbacks callbacks) {
+        mContext = windowManagerService.mContext;
+        mWindowManagerService = windowManagerService;
+        mCallbacks = callbacks;
+        mMagnifedViewport = new MagnifiedViewport();
+        mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
+        mLongAnimationDuration = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_longAnimTime);
+    }
+
+    public void setMagnificationSpecLocked(MagnificationSpec spec) {
+        mMagnifedViewport.updateMagnificationSpecLocked(spec);
+        mMagnifedViewport.recomputeBoundsLocked();
+        mWindowManagerService.scheduleAnimationLocked();
+    }
+
+    public void onRectangleOnScreenRequestedLocked(Rect rectangle, boolean immediate) {
+        if (DEBUG_RECTANGLE_REQUESTED) {
+            Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
+        }
+        if (!mMagnifedViewport.isMagnifyingLocked()) {
+            return;
+        }
+        Rect magnifiedRegionBounds = mTempRect2;
+        mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
+        if (magnifiedRegionBounds.contains(rectangle)) {
+            return;
+        }
+        SomeArgs args = SomeArgs.obtain();
+        args.argi1 = rectangle.left;
+        args.argi2 = rectangle.top;
+        args.argi3 = rectangle.right;
+        args.argi4 = rectangle.bottom;
+        mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
+                args).sendToTarget();
+    }
+
+    public void onWindowLayersChangedLocked() {
+        if (DEBUG_LAYERS) {
+            Slog.i(LOG_TAG, "Layers changed.");
+        }
+        mMagnifedViewport.recomputeBoundsLocked();
+        mWindowManagerService.scheduleAnimationLocked();
+    }
+
+    public void onRotationChangedLocked(DisplayContent displayContent, int rotation) {
+        if (DEBUG_ROTATION) {
+            Slog.i(LOG_TAG, "Rotaton: " + Surface.rotationToString(rotation)
+                    + " displayId: " + displayContent.getDisplayId());
+        }
+        mMagnifedViewport.onRotationChangedLocked();
+        mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
+    }
+
+    public void onWindowTransitionLocked(WindowState windowState, int transition) {
+        if (DEBUG_WINDOW_TRANSITIONS) {
+            Slog.i(LOG_TAG, "Window transition: "
+                    + PhoneWindowManager.windowTransitionToString(transition)
+                    + " displayId: " + windowState.getDisplayId());
+        }
+        final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
+        if (magnifying) {
+            switch (transition) {
+                case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN:
+                case WindowManagerPolicy.TRANSIT_TASK_OPEN:
+                case WindowManagerPolicy.TRANSIT_TASK_TO_FRONT:
+                case WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN:
+                case WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE:
+                case WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN: {
+                    mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
+                }
+            }
+        }
+        final int type = windowState.mAttrs.type;
+        switch (transition) {
+            case WindowManagerPolicy.TRANSIT_ENTER:
+            case WindowManagerPolicy.TRANSIT_SHOW: {
+                if (!magnifying) {
+                    break;
+                }
+                switch (type) {
+                    case WindowManager.LayoutParams.TYPE_APPLICATION:
+                    case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
+                    case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
+                    case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
+                    case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
+                    case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
+                    case WindowManager.LayoutParams.TYPE_PHONE:
+                    case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
+                    case WindowManager.LayoutParams.TYPE_TOAST:
+                    case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
+                    case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
+                    case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
+                    case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
+                    case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
+                    case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
+                    case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL:
+                    case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: {
+                        Rect magnifiedRegionBounds = mTempRect2;
+                        mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
+                                magnifiedRegionBounds);
+                        Rect touchableRegionBounds = mTempRect1;
+                        windowState.getTouchableRegion(mTempRegion1);
+                        mTempRegion1.getBounds(touchableRegionBounds);
+                        if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
+                            try {
+                                mCallbacks.onRectangleOnScreenRequested(
+                                        touchableRegionBounds.left,
+                                        touchableRegionBounds.top,
+                                        touchableRegionBounds.right,
+                                        touchableRegionBounds.bottom);
+                            } catch (RemoteException re) {
+                                /* ignore */
+                            }
+                        }
+                    } break;
+                } break;
+            }
+        }
+    }
+
+    public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
+        MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
+        if (spec != null && !spec.isNop()) {
+            WindowManagerPolicy policy = mWindowManagerService.mPolicy;
+            final int windowType = windowState.mAttrs.type;
+            if (!policy.isTopLevelWindow(windowType) && windowState.mAttachedWindow != null
+                    && !policy.canMagnifyWindow(windowType)) {
+                return null;
+            }
+            if (!policy.canMagnifyWindow(windowState.mAttrs.type)) {
+                return null;
+            }
+        }
+        return spec;
+    }
+
+    /** NOTE: This has to be called within a surface transaction. */
+    public void drawMagnifiedRegionBorderIfNeededLocked() {
+        mMagnifedViewport.drawWindowIfNeededLocked();
+    }
+
+    private final class MagnifiedViewport {
+
+        private static final int DEFAUTLT_BORDER_WIDTH_DIP = 5;
+
+        private final SparseArray<WindowStateInfo> mTempWindowStateInfos =
+                new SparseArray<WindowStateInfo>();
+
+        private final float[] mTempFloats = new float[9];
+
+        private final RectF mTempRectF = new RectF();
+
+        private final Point mTempPoint = new Point();
+
+        private final Matrix mTempMatrix = new Matrix();
+
+        private final Region mMagnifiedBounds = new Region();
+        private final Region mOldMagnifiedBounds = new Region();
+
+        private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain();
+
+        private final WindowManager mWindowManager;
+
+        private final int mBorderWidth;
+        private final int mHalfBorderWidth;
+
+        private ViewportWindow mWindow;
+
+        private boolean mFullRedrawNeeded;
+
+        public MagnifiedViewport() {
+            mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
+            mBorderWidth = (int) TypedValue.applyDimension(
+                    TypedValue.COMPLEX_UNIT_DIP, DEFAUTLT_BORDER_WIDTH_DIP,
+                            mContext.getResources().getDisplayMetrics());
+            mHalfBorderWidth = (int) (mBorderWidth + 0.5) / 2;
+            mWindow = new ViewportWindow(mContext);
+        }
+
+        public void updateMagnificationSpecLocked(MagnificationSpec spec) {
+            if (spec != null) {
+                mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
+            } else {
+                mMagnificationSpec.clear();
+            }
+            // If this message is pending we are in a rotation animation and do not want
+            // to show the border. We will do so when the pending message is handled.
+            if (!mHandler.hasMessages(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
+                setMagnifiedRegionBorderShownLocked(isMagnifyingLocked(), true);
+            }
+        }
+
+        public void recomputeBoundsLocked() {
+            mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
+            final int screenWidth = mTempPoint.x;
+            final int screenHeight = mTempPoint.y;
+
+            Region magnifiedBounds = mMagnifiedBounds;
+            magnifiedBounds.set(0, 0, 0, 0);
+
+            Region availableBounds = mTempRegion1;
+            availableBounds.set(0, 0, screenWidth, screenHeight);
+
+            Region nonMagnifiedBounds = mTempRegion4;
+            nonMagnifiedBounds.set(0,  0,  0,  0);
+
+            SparseArray<WindowStateInfo> visibleWindows = mTempWindowStateInfos;
+            visibleWindows.clear();
+            getWindowsOnScreenLocked(visibleWindows);
+
+            final int visibleWindowCount = visibleWindows.size();
+            for (int i = visibleWindowCount - 1; i >= 0; i--) {
+                WindowStateInfo info = visibleWindows.valueAt(i);
+                if (info.mWindowState.mAttrs.type == WindowManager
+                        .LayoutParams.TYPE_MAGNIFICATION_OVERLAY) {
+                    continue;
+                }
+
+                Region windowBounds = mTempRegion2;
+                Matrix matrix = mTempMatrix;
+                populateTransformationMatrix(info.mWindowState, matrix);
+                RectF windowFrame = mTempRectF;
+
+                if (mWindowManagerService.mPolicy.canMagnifyWindow(info.mWindowState.mAttrs.type)) {
+                    windowFrame.set(info.mWindowState.mFrame);
+                    windowFrame.offset(-windowFrame.left, -windowFrame.top);
+                    matrix.mapRect(windowFrame);
+                    windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
+                            (int) windowFrame.right, (int) windowFrame.bottom);
+                    magnifiedBounds.op(windowBounds, Region.Op.UNION);
+                    magnifiedBounds.op(availableBounds, Region.Op.INTERSECT);
+                } else {
+                    windowFrame.set(info.mTouchableRegion);
+                    windowFrame.offset(-info.mWindowState.mFrame.left,
+                            -info.mWindowState.mFrame.top);
+                    matrix.mapRect(windowFrame);
+                    windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
+                            (int) windowFrame.right, (int) windowFrame.bottom);
+                    nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
+                    windowBounds.op(magnifiedBounds, Region.Op.DIFFERENCE);
+                    availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
+                }
+
+                Region accountedBounds = mTempRegion2;
+                accountedBounds.set(magnifiedBounds);
+                accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
+                accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
+
+                if (accountedBounds.isRect()) {
+                    Rect accountedFrame = mTempRect1;
+                    accountedBounds.getBounds(accountedFrame);
+                    if (accountedFrame.width() == screenWidth
+                            && accountedFrame.height() == screenHeight) {
+                        break;
+                    }
+                }
+            }
+
+            for (int i = visibleWindowCount - 1; i >= 0; i--) {
+                WindowStateInfo info = visibleWindows.valueAt(i);
+                info.recycle();
+                visibleWindows.removeAt(i);
+            }
+
+            magnifiedBounds.op(mHalfBorderWidth, mHalfBorderWidth,
+                    screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth,
+                    Region.Op.INTERSECT);
+
+            if (!mOldMagnifiedBounds.equals(magnifiedBounds)) {
+                Region bounds = Region.obtain();
+                bounds.set(magnifiedBounds);
+                mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED,
+                        bounds).sendToTarget();
+
+                mWindow.setBounds(magnifiedBounds);
+                Rect dirtyRect = mTempRect1;
+                if (mFullRedrawNeeded) {
+                    mFullRedrawNeeded = false;
+                    dirtyRect.set(mHalfBorderWidth, mHalfBorderWidth,
+                            screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth);
+                    mWindow.invalidate(dirtyRect);
+                } else {
+                    Region dirtyRegion = mTempRegion3;
+                    dirtyRegion.set(magnifiedBounds);
+                    dirtyRegion.op(mOldMagnifiedBounds, Region.Op.UNION);
+                    dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
+                    dirtyRegion.getBounds(dirtyRect);
+                    mWindow.invalidate(dirtyRect);
+                }
+
+                mOldMagnifiedBounds.set(magnifiedBounds);
+            }
+        }
+
+        private void populateTransformationMatrix(WindowState windowState, Matrix outMatrix) {
+            mTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx;
+            mTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx;
+            mTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDsDy;
+            mTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDtDy;
+            mTempFloats[Matrix.MTRANS_X] = windowState.mShownFrame.left;
+            mTempFloats[Matrix.MTRANS_Y] = windowState.mShownFrame.top;
+            mTempFloats[Matrix.MPERSP_0] = 0;
+            mTempFloats[Matrix.MPERSP_1] = 0;
+            mTempFloats[Matrix.MPERSP_2] = 1;
+            outMatrix.setValues(mTempFloats);
+        }
+
+        private void getWindowsOnScreenLocked(SparseArray<WindowStateInfo> outWindowStates) {
+            DisplayContent displayContent = mWindowManagerService.getDefaultDisplayContentLocked();
+            WindowList windowList = displayContent.getWindowList();
+            final int windowCount = windowList.size();
+            for (int i = 0; i < windowCount; i++) {
+                WindowState windowState = windowList.get(i);
+                if ((windowState.isOnScreen() || windowState.mAttrs.type == WindowManager
+                        .LayoutParams.TYPE_UNIVERSE_BACKGROUND)
+                        && !windowState.mWinAnimator.mEnterAnimationPending) {
+                    outWindowStates.put(windowState.mLayer, WindowStateInfo.obtain(windowState));
+                }
+            }
+        }
+
+        public void onRotationChangedLocked() {
+            // If we are magnifying, hide the magnified border window immediately so
+            // the user does not see strange artifacts during rotation. The screenshot
+            // used for rotation has already the border. After the rotation is complete
+            // we will show the border.
+            if (isMagnifyingLocked()) {
+                setMagnifiedRegionBorderShownLocked(false, false);
+                final long delay = (long) (mLongAnimationDuration
+                        * mWindowManagerService.mWindowAnimationScale);
+                Message message = mHandler.obtainMessage(
+                        MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
+                mHandler.sendMessageDelayed(message, delay);
+            }
+            recomputeBoundsLocked();
+            mWindow.updateSize();
+        }
+
+        public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
+            if (shown) {
+                mFullRedrawNeeded = true;
+                mOldMagnifiedBounds.set(0,  0,  0,  0);
+            }
+            mWindow.setShown(shown, animate);
+        }
+
+        public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
+            MagnificationSpec spec = mMagnificationSpec;
+            mMagnifiedBounds.getBounds(rect);
+            rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
+            rect.scale(1.0f / spec.scale);
+        }
+
+        public boolean isMagnifyingLocked() {
+            return mMagnificationSpec.scale > 1.0f;
+        }
+
+        public MagnificationSpec getMagnificationSpecLocked() {
+            return mMagnificationSpec;
+        }
+
+        /** NOTE: This has to be called within a surface transaction. */
+        public void drawWindowIfNeededLocked() {
+            recomputeBoundsLocked();
+            mWindow.drawIfNeeded();
+        }
+
+        private final class ViewportWindow {
+            private static final String SURFACE_TITLE = "Magnification Overlay";
+
+            private static final String PROPERTY_NAME_ALPHA = "alpha";
+
+            private static final int MIN_ALPHA = 0;
+            private static final int MAX_ALPHA = 255;
+
+            private final Point mTempPoint = new Point();
+            private final Region mBounds = new Region();
+            private final Rect mDirtyRect = new Rect();
+            private final Paint mPaint = new Paint();
+
+            private final ValueAnimator mShowHideFrameAnimator;
+            private final Surface mSurface;
+
+            private boolean mShown;
+            private int mAlpha;
+
+            private boolean mInvalidated;
+
+            public ViewportWindow(Context context) {
+                Surface surface = null;
+                try {
+                    mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
+                    surface = new Surface(mWindowManagerService.mFxSession, SURFACE_TITLE,
+                            mTempPoint.x, mTempPoint.y, PixelFormat.TRANSLUCENT, Surface.HIDDEN);
+                } catch (OutOfResourcesException oore) {
+                    /* ignore */
+                }
+                mSurface = surface;
+                mSurface.setLayerStack(mWindowManager.getDefaultDisplay().getLayerStack());
+                mSurface.setLayer(mWindowManagerService.mPolicy.windowTypeToLayerLw(
+                        WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY)
+                        * WindowManagerService.TYPE_LAYER_MULTIPLIER);
+                mSurface.setPosition(0, 0);
+
+                TypedValue typedValue = new TypedValue();
+                context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
+                        typedValue, true);
+                final int borderColor = context.getResources().getColor(typedValue.resourceId);
+
+                mPaint.setStyle(Paint.Style.STROKE);
+                mPaint.setStrokeWidth(mBorderWidth);
+                mPaint.setColor(borderColor);
+
+                Interpolator interpolator = new DecelerateInterpolator(2.5f);
+                final long longAnimationDuration = context.getResources().getInteger(
+                        com.android.internal.R.integer.config_longAnimTime);
+
+                mShowHideFrameAnimator = ObjectAnimator.ofInt(this, PROPERTY_NAME_ALPHA,
+                        MIN_ALPHA, MAX_ALPHA);
+                mShowHideFrameAnimator.setInterpolator(interpolator);
+                mShowHideFrameAnimator.setDuration(longAnimationDuration);
+                mInvalidated = true;
+            }
+
+            public void setShown(boolean shown, boolean animate) {
+                synchronized (mWindowManagerService.mWindowMap) {
+                    if (mShown == shown) {
+                        return;
+                    }
+                    mShown = shown;
+                    if (animate) {
+                        if (mShowHideFrameAnimator.isRunning()) {
+                            mShowHideFrameAnimator.reverse();
+                        } else {
+                            if (shown) {
+                                mShowHideFrameAnimator.start();
+                            } else {
+                                mShowHideFrameAnimator.reverse();
+                            }
+                        }
+                    } else {
+                        mShowHideFrameAnimator.cancel();
+                        if (shown) {
+                            setAlpha(MAX_ALPHA);
+                        } else {
+                            setAlpha(MIN_ALPHA);
+                        }
+                    }
+                    if (DEBUG_VIEWPORT_WINDOW) {
+                        Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
+                    }
+                }
+            }
+
+            @SuppressWarnings("unused")
+            // Called reflectively from an animator.
+            public int getAlpha() {
+                synchronized (mWindowManagerService.mWindowMap) {
+                    return mAlpha;
+                }
+            }
+
+            public void setAlpha(int alpha) {
+                synchronized (mWindowManagerService.mWindowMap) {
+                    if (mAlpha == alpha) {
+                        return;
+                    }
+                    mAlpha = alpha;
+                    invalidate(null);
+                    if (DEBUG_VIEWPORT_WINDOW) {
+                        Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha);
+                    }
+                }
+            }
+
+            public void setBounds(Region bounds) {
+                synchronized (mWindowManagerService.mWindowMap) {
+                    if (mBounds.equals(bounds)) {
+                        return;
+                    }
+                    mBounds.set(bounds);
+                    invalidate(mDirtyRect);
+                    if (DEBUG_VIEWPORT_WINDOW) {
+                        Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
+                    }
+                }
+            }
+
+            public void updateSize() {
+                synchronized (mWindowManagerService.mWindowMap) {
+                    mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
+                    mSurface.setSize(mTempPoint.x, mTempPoint.y);
+                    invalidate(mDirtyRect);
+                }
+            }
+
+            public void invalidate(Rect dirtyRect) {
+                if (dirtyRect != null) {
+                    mDirtyRect.set(dirtyRect);
+                } else {
+                    mDirtyRect.setEmpty();
+                }
+                mInvalidated = true;
+                mWindowManagerService.scheduleAnimationLocked();
+            }
+
+            /** NOTE: This has to be called within a surface transaction. */
+            public void drawIfNeeded() {
+                synchronized (mWindowManagerService.mWindowMap) {
+                    if (!mInvalidated) {
+                        return;
+                    }
+                    mInvalidated = false;
+                    Canvas canvas = null;
+                    try {
+                        // Empty dirty rectangle means unspecified.
+                        if (mDirtyRect.isEmpty()) {
+                            mBounds.getBounds(mDirtyRect);
+                        }
+                        mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth);
+                        canvas = mSurface.lockCanvas(mDirtyRect);
+                        if (DEBUG_VIEWPORT_WINDOW) {
+                            Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
+                        }
+                    } catch (IllegalArgumentException iae) {
+                        /* ignore */
+                    } catch (OutOfResourcesException oore) {
+                        /* ignore */
+                    }
+                    if (canvas == null) {
+                        return;
+                    }
+                    if (DEBUG_VIEWPORT_WINDOW) {
+                        Slog.i(LOG_TAG, "Bounds: " + mBounds);
+                    }
+                    canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
+                    mPaint.setAlpha(mAlpha);
+                    Path path = mBounds.getBoundaryPath();
+                    canvas.drawPath(path, mPaint);
+
+                    mSurface.unlockCanvasAndPost(canvas);
+
+                    if (mAlpha > 0) {
+                        mSurface.show();
+                    } else {
+                        mSurface.hide();
+                    }
+                }
+            }
+        }
+    }
+
+    private static final class WindowStateInfo {
+        private static final int MAX_POOL_SIZE = 30;
+
+        private static final SimplePool<WindowStateInfo> sPool =
+                new SimplePool<WindowStateInfo>(MAX_POOL_SIZE);
+
+        private static final Region mTempRegion = new Region();
+
+        public WindowState mWindowState;
+        public final Rect mTouchableRegion = new Rect();
+
+        public static WindowStateInfo obtain(WindowState windowState) {
+            WindowStateInfo info = sPool.acquire();
+            if (info == null) {
+                info = new WindowStateInfo();
+            }
+            info.mWindowState = windowState;
+            windowState.getTouchableRegion(mTempRegion);
+            mTempRegion.getBounds(info.mTouchableRegion);
+            return info;
+        }
+
+        public void recycle() {
+            mWindowState = null;
+            mTouchableRegion.setEmpty();
+            sPool.release(this);
+        }
+    }
+
+    private class MyHandler extends Handler {
+        public static final int MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED = 1;
+        public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
+        public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
+        public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
+        public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
+
+        public MyHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message message) {
+            switch (message.what) {
+                case MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED: {
+                    Region bounds = (Region) message.obj;
+                    try {
+                        mCallbacks.onMagnifedBoundsChanged(bounds);
+                    } catch (RemoteException re) {
+                        /* ignore */
+                    } finally {
+                        bounds.recycle();
+                    }
+                } break;
+                case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
+                    SomeArgs args = (SomeArgs) message.obj;
+                    final int left = args.argi1;
+                    final int top = args.argi2;
+                    final int right = args.argi3;
+                    final int bottom = args.argi4;
+                    try {
+                        mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom);
+                    } catch (RemoteException re) {
+                        /* ignore */
+                    } finally {
+                        args.recycle();
+                    }
+                } break;
+                case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
+                    try {
+                        mCallbacks.onUserContextChanged();
+                    } catch (RemoteException re) {
+                        /* ignore */
+                    }
+                } break;
+                case MESSAGE_NOTIFY_ROTATION_CHANGED: {
+                    final int rotation = message.arg1;
+                    try {
+                        mCallbacks.onRotationChanged(rotation);
+                    } catch (RemoteException re) {
+                        /* ignore */
+                    }
+                } break;
+                case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
+                    synchronized (mWindowManagerService.mWindowMap) {
+                        if (mMagnifedViewport.isMagnifyingLocked()) {
+                            mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
+                            mWindowManagerService.scheduleAnimationLocked();
+                        }
+                    }
+                } break;
+            }
+        }
+    }
+}
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 74f51d7..a9f3c0f 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -570,6 +570,10 @@
                     mAnimating |= dimAnimator.updateSurface(isDimmingLocked(displayId),
                             mCurrentTime, !mService.okToDisplay());
                 }
+                //TODO (multidisplay): Magnification is supported only for the default display.
+                if (mService.mDisplayMagnifier != null && displayId == Display.DEFAULT_DISPLAY) {
+                    mService.mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
+                }
             }
 
             if (mService.mWatermark != null) {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 653f075..1640cac 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -115,8 +115,8 @@
 import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.IApplicationToken;
-import android.view.IDisplayMagnificationMediator;
 import android.view.IInputFilter;
+import android.view.IMagnificationCallbacks;
 import android.view.IOnKeyguardExitResult;
 import android.view.IRotationWatcher;
 import android.view.IWindow;
@@ -127,6 +127,7 @@
 import android.view.InputEvent;
 import android.view.InputEventReceiver;
 import android.view.KeyEvent;
+import android.view.MagnificationSpec;
 import android.view.MotionEvent;
 import android.view.Surface;
 import android.view.SurfaceSession;
@@ -424,7 +425,7 @@
 
     IInputMethodManager mInputMethodManager;
 
-    DisplayMagnificationMediator mMagnificationMediator;
+    DisplayMagnifier mDisplayMagnifier;
 
     final SurfaceSession mFxSession;
     Watermark mWatermark;
@@ -2323,8 +2324,10 @@
                 if (win.mWinAnimator.applyAnimationLocked(transit, false)) {
                     win.mExiting = true;
                 }
-                if (mMagnificationMediator != null) {
-                    mMagnificationMediator.onWindowTransitionLw(win, transit);
+                //TODO (multidisplay): Magnification is supported only for the default display.
+                if (mDisplayMagnifier != null
+                        && win.getDisplayId() == Display.DEFAULT_DISPLAY) {
+                    mDisplayMagnifier.onWindowTransitionLocked(win, transit);
                 }
             }
             if (win.mExiting || win.mWinAnimator.isAnimating()) {
@@ -2619,11 +2622,11 @@
 
     public void onRectangleOnScreenRequested(IBinder token, Rect rectangle, boolean immediate) {
         synchronized (mWindowMap) {
-            if (mMagnificationMediator != null) {
+            if (mDisplayMagnifier != null) {
                 WindowState window = mWindowMap.get(token);
-                if (window != null) {
-                    mMagnificationMediator.onRectangleOnScreenRequestedLw(window, rectangle,
-                            immediate);
+                //TODO (multidisplay): Magnification is supported only for the default display.
+                if (window != null && window.getDisplayId() == Display.DEFAULT_DISPLAY) {
+                    mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle, immediate);
                 }
             }
         }
@@ -2850,8 +2853,10 @@
                             }
                             winAnimator.destroySurfaceLocked();
                         }
-                        if (mMagnificationMediator != null) {
-                            mMagnificationMediator.onWindowTransitionLw(win, transit);
+                        //TODO (multidisplay): Magnification is supported only for the default
+                        if (mDisplayMagnifier != null
+                                && win.getDisplayId() == Display.DEFAULT_DISPLAY) {
+                            mDisplayMagnifier.onWindowTransitionLocked(win, transit);
                         }
                     }
                 }
@@ -3004,15 +3009,64 @@
     }
 
     @Override
-    public IDisplayMagnificationMediator getDisplayMagnificationMediator() {
+    public void setMagnificationSpec(MagnificationSpec spec) {
         if (!checkCallingPermission(android.Manifest.permission.MAGNIFY_DISPLAY,
-                "getDisplayMagnificationMediator()")) {
-            throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission.");
+                "setMagnificationSpec()")) {
+            throw new SecurityException("Requires MAGNIFY_DISPLAY permission.");
         }
-        if (mMagnificationMediator == null) {
-            mMagnificationMediator = new DisplayMagnificationMediator(this);
+        synchronized (mWindowMap) {
+            if (mDisplayMagnifier != null) {
+                mDisplayMagnifier.setMagnificationSpecLocked(spec);
+            } else {
+                throw new IllegalStateException("Magnification callbacks not set!");
+            }
         }
-        return mMagnificationMediator;
+        if (Binder.getCallingPid() != android.os.Process.myPid()) {
+            spec.recycle();
+        }
+    }
+
+    @Override
+    public MagnificationSpec getCompatibleMagnificationSpecForWindow(IBinder windowToken) {
+        if (!checkCallingPermission(android.Manifest.permission.MAGNIFY_DISPLAY,
+                "getCompatibleMagnificationSpecForWindow()")) {
+            throw new SecurityException("Requires MAGNIFY_DISPLAY permission.");
+        }
+        synchronized (mWindowMap) {
+            WindowState windowState = mWindowMap.get(windowToken);
+            if (windowState == null) {
+                return null;
+            }
+            MagnificationSpec spec = null;
+            if (mDisplayMagnifier != null) {
+                spec = mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState);
+            }
+            if ((spec == null || spec.isNop()) && windowState.mGlobalScale == 1.0f) {
+                return null;
+            }
+            spec = (spec == null) ? MagnificationSpec.obtain() : MagnificationSpec.obtain(spec);
+            spec.scale *= windowState.mGlobalScale;
+            return spec;
+        }
+    }
+
+    @Override
+    public void setMagnificationCallbacks(IMagnificationCallbacks callbacks) {
+        if (!checkCallingPermission(android.Manifest.permission.MAGNIFY_DISPLAY,
+                "setMagnificationCallbacks()")) {
+            throw new SecurityException("Requires MAGNIFY_DISPLAY permission.");
+        }
+        synchronized (mWindowMap) {
+            if (mDisplayMagnifier == null) {
+                mDisplayMagnifier = new DisplayMagnifier(this, callbacks);
+            } else {
+                if (callbacks == null) {
+                    mDisplayMagnifier = null;
+                } else {
+                    throw new IllegalStateException("Magnification callbacks already set!");
+                }
+            }
+        }
     }
 
     private boolean applyAnimationLocked(AppWindowToken atoken,
@@ -3156,8 +3210,10 @@
                         if (win.isVisibleNow()) {
                             win.mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_EXIT,
                                     false);
-                            if (mMagnificationMediator != null) {
-                                mMagnificationMediator.onWindowTransitionLw(win,
+                            //TODO (multidisplay): Magnification is supported only for the default
+                            if (mDisplayMagnifier != null
+                                    && win.getDisplayId() == Display.DEFAULT_DISPLAY) {
+                                mDisplayMagnifier.onWindowTransitionLocked(win,
                                         WindowManagerPolicy.TRANSIT_EXIT);
                             }
                             changed = true;
@@ -3912,8 +3968,10 @@
                     delayed = runningAppAnimation = true;
                 }
                 WindowState window = wtoken.findMainWindow();
-                if (window != null && mMagnificationMediator != null) {
-                    mMagnificationMediator.onWindowTransitionLw(window, transit);
+                //TODO (multidisplay): Magnification is supported only for the default display.
+                if (window != null && mDisplayMagnifier != null
+                        && window.getDisplayId() == Display.DEFAULT_DISPLAY) {
+                    mDisplayMagnifier.onWindowTransitionLocked(window, transit);
                 }
                 changed = true;
             }
@@ -3932,8 +3990,10 @@
                         if (!runningAppAnimation) {
                             win.mWinAnimator.applyAnimationLocked(
                                     WindowManagerPolicy.TRANSIT_ENTER, true);
-                            if (mMagnificationMediator != null) {
-                                mMagnificationMediator.onWindowTransitionLw(win,
+                            //TODO (multidisplay): Magnification is supported only for the default
+                            if (mDisplayMagnifier != null
+                                    && win.getDisplayId() == Display.DEFAULT_DISPLAY) {
+                                mDisplayMagnifier.onWindowTransitionLocked(win,
                                         WindowManagerPolicy.TRANSIT_ENTER);
                             }
                         }
@@ -3944,8 +4004,10 @@
                     if (!runningAppAnimation) {
                         win.mWinAnimator.applyAnimationLocked(
                                 WindowManagerPolicy.TRANSIT_EXIT, false);
-                        if (mMagnificationMediator != null) {
-                            mMagnificationMediator.onWindowTransitionLw(win,
+                        //TODO (multidisplay): Magnification is supported only for the default
+                        if (mDisplayMagnifier != null
+                                && win.getDisplayId() == Display.DEFAULT_DISPLAY) {
+                            mDisplayMagnifier.onWindowTransitionLocked(win,
                                     WindowManagerPolicy.TRANSIT_EXIT);
                         }
                     }
@@ -5571,8 +5633,10 @@
             }
         }
 
-        if (mMagnificationMediator != null) {
-            mMagnificationMediator.onRotationChangedLw(Display.DEFAULT_DISPLAY, rotation);
+        //TODO (multidisplay): Magnification is supported only for the default display.
+        if (mDisplayMagnifier != null
+                && displayContent.getDisplayId() == Display.DEFAULT_DISPLAY) {
+            mDisplayMagnifier.onRotationChangedLocked(getDefaultDisplayContentLocked(), rotation);
         }
 
         return true;
@@ -7467,9 +7531,10 @@
             //    "Assigned layer " + curLayer + " to " + w.mClient.asBinder());
         }
 
-        if (mMagnificationMediator != null && anyLayerChanged) {
-            mMagnificationMediator.onWindowLayersChangedLw(
-                    windows.get(windows.size() - 1).getDisplayId());
+        //TODO (multidisplay): Magnification is supported only for the default display.
+        if (mDisplayMagnifier != null && anyLayerChanged
+                && windows.get(windows.size() - 1).getDisplayId() == Display.DEFAULT_DISPLAY) {
+            mDisplayMagnifier.onWindowLayersChangedLocked();
         }
     }
 
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
index e03e40d..9acdd49 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -15,6 +15,7 @@
 import android.graphics.Region;
 import android.os.Debug;
 import android.util.Slog;
+import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.MagnificationSpec;
 import android.view.Surface;
@@ -919,9 +920,11 @@
             if (screenAnimation) {
                 tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix());
             }
-            if (mService.mMagnificationMediator != null) {
-                MagnificationSpec spec = mService.mMagnificationMediator
-                        .getMagnificationSpecLw(mWin);
+            //TODO (multidisplay): Magnification is supported only for the default display.
+            if (mService.mDisplayMagnifier != null
+                    && mWin.getDisplayId() == Display.DEFAULT_DISPLAY) {
+                MagnificationSpec spec = mService.mDisplayMagnifier
+                        .getMagnificationSpecForWindowLocked(mWin);
                 if (spec != null && !spec.isNop()) {
                     tmpMatrix.postScale(spec.scale, spec.scale);
                     tmpMatrix.postTranslate(spec.offsetX, spec.offsetY);
@@ -997,8 +1000,11 @@
         final boolean applyUniverseTransformation = (mAnimator.mUniverseBackground != null
                 && mWin.mAttrs.type != WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND
                 && mWin.mBaseLayer < mAnimator.mAboveUniverseLayer);
-        MagnificationSpec spec = (mService.mMagnificationMediator != null)
-                ? mService.mMagnificationMediator.getMagnificationSpecLw(mWin) : null;
+        MagnificationSpec spec = null;
+        //TODO (multidisplay): Magnification is supported only for the default display.
+        if (mService.mDisplayMagnifier != null && mWin.getDisplayId() == Display.DEFAULT_DISPLAY) {
+            spec = mService.mDisplayMagnifier.getMagnificationSpecForWindowLocked(mWin);
+        }
         if (applyUniverseTransformation || spec != null) {
             final Rect frame = mWin.mFrame;
             final float tmpFloats[] = mService.mTmpFloats;
@@ -1498,8 +1504,10 @@
             transit = WindowManagerPolicy.TRANSIT_SHOW;
         }
         applyAnimationLocked(transit, true);
-        if (mService.mMagnificationMediator != null) {
-            mService.mMagnificationMediator.onWindowTransitionLw(mWin, transit);
+        //TODO (multidisplay): Magnification is supported only for the default display.
+        if (mService.mDisplayMagnifier != null
+                && mWin.getDisplayId() == Display.DEFAULT_DISPLAY) {
+            mService.mDisplayMagnifier.onWindowTransitionLocked(mWin, transit);
         }
     }