am 87bc3c25: Merge "Improvements to low power mode." into lmp-preview-dev

* commit '87bc3c258d8be516bc0fcedeb6eec34088582061':
  Improvements to low power mode.
diff --git a/Android.mk b/Android.mk
index 642d8b9..573f46a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -232,6 +232,7 @@
 	core/java/android/view/IWindowId.aidl \
 	core/java/android/view/IWindowManager.aidl \
 	core/java/android/view/IWindowSession.aidl \
+	core/java/android/view/IWindowSessionCallback.aidl \
 	core/java/android/speech/IRecognitionListener.aidl \
 	core/java/android/speech/IRecognitionService.aidl \
 	core/java/android/speech/tts/ITextToSpeechCallback.aidl \
diff --git a/api/current.txt b/api/current.txt
index eab14e2..234126b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -21473,12 +21473,14 @@
   public final class PowerManager {
     method public void goToSleep(long);
     method public boolean isInteractive();
+    method public boolean isPowerSaveMode();
     method public deprecated boolean isScreenOn();
     method public android.os.PowerManager.WakeLock newWakeLock(int, java.lang.String);
     method public void reboot(java.lang.String);
     method public void userActivity(long, boolean);
     method public void wakeUp(long);
     field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
+    field public static final java.lang.String ACTION_POWER_SAVE_MODE_CHANGED = "android.os.action.POWER_SAVE_MODE_CHANGED";
     field public static final deprecated int FULL_WAKE_LOCK = 26; // 0x1a
     field public static final int ON_AFTER_RELEASE = 536870912; // 0x20000000
     field public static final int PARTIAL_WAKE_LOCK = 1; // 0x1
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 61194e9..658180b 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -41,6 +41,7 @@
     void goToSleep(long time, int reason, int flags);
     void nap(long time);
     boolean isInteractive();
+    boolean isPowerSaveMode();
 
     void reboot(boolean confirm, String reason, boolean wait);
     void shutdown(boolean confirm, boolean wait);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index d5177e8..92e80a5 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.annotation.SdkConstant;
 import android.content.Context;
 import android.util.Log;
 
@@ -685,6 +686,30 @@
     }
 
     /**
+     * Returns true if the device is currently in power save mode.  When in this mode,
+     * applications should reduce their functionality in order to conserve battery as
+     * much as possible.  You can monitor for changes to this state with
+     * {@link #ACTION_POWER_SAVE_MODE_CHANGED}.
+     *
+     * @return Returns true if currently in low power mode, else false.
+     */
+    public boolean isPowerSaveMode() {
+        try {
+            return mService.isPowerSaveMode();
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Intent that is broadcast when the state of {@link #isPowerSaveMode()} changes.
+     * This broadcast is only sent to registered receivers.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_POWER_SAVE_MODE_CHANGED
+            = "android.os.action.POWER_SAVE_MODE_CHANGED";
+
+    /**
      * A wake lock is a mechanism to indicate that your application needs
      * to have the device stay on.
      * <p>
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index a52ccdf..ae59bbc 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -30,6 +30,7 @@
 import android.view.IOnKeyguardExitResult;
 import android.view.IRotationWatcher;
 import android.view.IWindowSession;
+import android.view.IWindowSessionCallback;
 import android.view.KeyEvent;
 import android.view.InputEvent;
 import android.view.MagnificationSpec;
@@ -56,7 +57,7 @@
     boolean stopViewServer();            // Transaction #2
     boolean isViewServerRunning();       // Transaction #3
 
-    IWindowSession openSession(in IInputMethodClient client,
+    IWindowSession openSession(in IWindowSessionCallback callback, in IInputMethodClient client,
             in IInputContext inputContext);
     boolean inputMethodClientHasFocus(IInputMethodClient client);
 
@@ -130,6 +131,8 @@
     void setAnimationScale(int which, float scale);
     void setAnimationScales(in float[] scales);
 
+    float getCurrentAnimatorScale();
+
     // For testing
     void setInTouchMode(boolean showFocus);
 
diff --git a/core/java/android/view/IWindowSessionCallback.aidl b/core/java/android/view/IWindowSessionCallback.aidl
new file mode 100644
index 0000000..88931ce
--- /dev/null
+++ b/core/java/android/view/IWindowSessionCallback.aidl
@@ -0,0 +1,27 @@
+/*
+** Copyright 2014, 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;
+
+/**
+ * Callback to active sessions of the window manager
+ *
+ * {@hide}
+ */
+oneway interface IWindowSessionCallback
+{
+    void onAnimatorScaleChanged(float scale);
+}
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index b4779f4..0ebf2e1 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -147,9 +147,14 @@
                     InputMethodManager imm = InputMethodManager.getInstance();
                     IWindowManager windowManager = getWindowManagerService();
                     sWindowSession = windowManager.openSession(
+                            new IWindowSessionCallback.Stub() {
+                                @Override
+                                public void onAnimatorScaleChanged(float scale) {
+                                    ValueAnimator.setDurationScale(scale);
+                                }
+                            },
                             imm.getClient(), imm.getInputContext());
-                    float animatorScale = windowManager.getAnimationScale(2);
-                    ValueAnimator.setDurationScale(animatorScale);
+                    ValueAnimator.setDurationScale(windowManager.getCurrentAnimatorScale());
                 } catch (RemoteException e) {
                     Log.e(TAG, "Failed to open window session", e);
                 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index dfe2a58..e4cbf5f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -74,6 +74,8 @@
     <protected-broadcast android:name="android.intent.action.USER_FOREGROUND" />
     <protected-broadcast android:name="android.intent.action.USER_SWITCHED" />
 
+    <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED" />
+
     <protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" />
     <protected-broadcast android:name="android.app.action.EXIT_CAR_MODE" />
     <protected-broadcast android:name="android.app.action.ENTER_DESK_MODE" />
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index cb5946a..fe5c2ef 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -128,6 +128,8 @@
     private int mPlugType;
     private int mLastPlugType = -1; // Extra state so we can detect first run
 
+    private boolean mBatteryLevelLow;
+
     private long mDischargeStartTime;
     private int mDischargeStartLevel;
 
@@ -222,14 +224,30 @@
     }
 
     /**
-     * Returns true if battery level is below the first warning threshold.
+     * Returns whether we currently consider the battery level to be low.
      */
-    public boolean isBatteryLow() {
+    public boolean getBatteryLevelLow() {
         synchronized (mLock) {
-            return mBatteryProps.batteryPresent && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel;
+            return mBatteryLevelLow;
         }
     }
 
+    public boolean isBatteryLowLocked() {
+        final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
+        final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;
+
+        /* The ACTION_BATTERY_LOW broadcast is sent in these situations:
+         * - is just un-plugged (previously was plugged) and battery level is
+         *   less than or equal to WARNING, or
+         * - is not plugged and battery level falls to WARNING boundary
+         *   (becomes <= mLowBatteryWarningLevel).
+         */
+        return !plugged
+                && mBatteryProps.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
+                && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel
+                && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
+    }
+
     /**
      * Returns a non-zero value if an  unsupported charger is attached.
      */
@@ -382,19 +400,7 @@
                 logOutlier = true;
             }
 
-            final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
-            final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;
-
-            /* The ACTION_BATTERY_LOW broadcast is sent in these situations:
-             * - is just un-plugged (previously was plugged) and battery level is
-             *   less than or equal to WARNING, or
-             * - is not plugged and battery level falls to WARNING boundary
-             *   (becomes <= mLowBatteryWarningLevel).
-             */
-            final boolean sendBatteryLow = !plugged
-                    && mBatteryProps.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
-                    && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel
-                    && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
+            mBatteryLevelLow = isBatteryLowLocked();
 
             sendIntentLocked();
 
@@ -422,7 +428,7 @@
                 });
             }
 
-            if (sendBatteryLow) {
+            if (mBatteryLevelLow) {
                 mSentLowBatteryBroadcast = true;
                 mHandler.post(new Runnable() {
                     @Override
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 06dd3ed..fdaf55e 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -28,6 +28,7 @@
 import android.os.Handler;
 import android.os.IVibratorService;
 import android.os.PowerManager;
+import android.os.PowerManagerInternal;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.IBinder;
@@ -64,6 +65,7 @@
     private final PowerManager.WakeLock mWakeLock;
     private final IAppOpsService mAppOpsService;
     private final IBatteryStats mBatteryStatsService;
+    private PowerManagerInternal mPowerManagerInternal;
     private InputManager mIm;
 
     volatile VibrateThread mThread;
@@ -169,14 +171,19 @@
         mIm = (InputManager)mContext.getSystemService(Context.INPUT_SERVICE);
         mSettingObserver = new SettingsObserver(mH);
 
+        mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
+        mPowerManagerInternal.registerLowPowerModeObserver(
+                new PowerManagerInternal.LowPowerModeListener() {
+            @Override
+            public void onLowPowerModeChanged(boolean enabled) {
+                updateInputDeviceVibrators();
+            }
+        });
+
         mContext.getContentResolver().registerContentObserver(
                 Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES),
                 true, mSettingObserver, UserHandle.USER_ALL);
 
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE), false,
-                mSettingObserver, UserHandle.USER_ALL);
-
         mContext.registerReceiver(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
@@ -448,8 +455,7 @@
                 } catch (SettingNotFoundException snfe) {
                 }
 
-                mLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(),
-                         Settings.Global.LOW_POWER_MODE, 0) != 0;
+                mLowPowerMode = mPowerManagerInternal.getLowPowerModeEnabled();
 
                 if (mVibrateInputDevicesSetting) {
                     if (!mInputDeviceListenerRegistered) {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index aca17bf..d8671d9 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -406,6 +406,12 @@
     // If true, the device is in low power mode.
     private boolean mLowPowerModeEnabled;
 
+    // Current state of the low power mode setting.
+    private boolean mLowPowerModeSetting;
+
+    // True if the battery level is currently considered low.
+    private boolean mBatteryLevelLow;
+
     private final ArrayList<PowerManagerInternal.LowPowerModeListener> mLowPowerModeListeners
             = new ArrayList<PowerManagerInternal.LowPowerModeListener>();
 
@@ -502,6 +508,7 @@
             // Register for broadcasts from other components of the system.
             IntentFilter filter = new IntentFilter();
             filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+            filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
             mContext.registerReceiver(new BatteryReceiver(), filter, null, mHandler);
 
             filter = new IntentFilter();
@@ -638,7 +645,18 @@
 
         final boolean lowPowerModeEnabled = Settings.Global.getInt(resolver,
                 Settings.Global.LOW_POWER_MODE, 0) != 0;
-        if (lowPowerModeEnabled != mLowPowerModeEnabled) {
+        if (lowPowerModeEnabled != mLowPowerModeSetting) {
+            mLowPowerModeSetting = lowPowerModeEnabled;
+            updateLowPowerModeLocked();
+        }
+
+        mDirty |= DIRTY_SETTINGS;
+    }
+
+    void updateLowPowerModeLocked() {
+        final boolean lowPowerModeEnabled = mLowPowerModeSetting || mBatteryLevelLow;
+        if (mLowPowerModeEnabled != lowPowerModeEnabled) {
+            mLowPowerModeEnabled = lowPowerModeEnabled;
             powerHintInternal(POWER_HINT_LOW_POWER_MODE, lowPowerModeEnabled ? 1 : 0);
             mLowPowerModeEnabled = lowPowerModeEnabled;
             BackgroundThread.getHandler().post(new Runnable() {
@@ -652,11 +670,12 @@
                     for (int i=0; i<listeners.size(); i++) {
                         listeners.get(i).onLowPowerModeChanged(lowPowerModeEnabled);
                     }
+                    Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
+                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                    mContext.sendBroadcast(intent);
                 }
             });
         }
-
-        mDirty |= DIRTY_SETTINGS;
     }
 
     private void handleSettingsChangedLocked() {
@@ -1137,9 +1156,11 @@
         if ((dirty & DIRTY_BATTERY_STATE) != 0) {
             final boolean wasPowered = mIsPowered;
             final int oldPlugType = mPlugType;
+            final boolean oldLevelLow = mBatteryLevelLow;
             mIsPowered = mBatteryService.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
             mPlugType = mBatteryService.getPlugType();
             mBatteryLevel = mBatteryService.getBatteryLevel();
+            mBatteryLevelLow = mBatteryService.getBatteryLevelLow();
 
             if (DEBUG_SPEW) {
                 Slog.d(TAG, "updateIsPoweredLocked: wasPowered=" + wasPowered
@@ -1175,6 +1196,10 @@
                     mNotifier.onWirelessChargingStarted();
                 }
             }
+
+            if (oldLevelLow != mBatteryLevelLow) {
+                updateLowPowerModeLocked();
+            }
         }
     }
 
@@ -1896,6 +1921,12 @@
         }
     }
 
+    private boolean isLowPowerModeInternal() {
+        synchronized (mLock) {
+            return mLowPowerModeEnabled;
+        }
+    }
+
     private void handleBatteryStateChangedLocked() {
         mDirty |= DIRTY_BATTERY_STATE;
         updatePowerStateLocked();
@@ -2764,6 +2795,16 @@
             }
         }
 
+        @Override // Binder call
+        public boolean isPowerSaveMode() {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                return isLowPowerModeInternal();
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
         /**
          * Reboots the device.
          *
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 35b7f99..cfd09e5 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -541,7 +541,7 @@
                 if (isMagnifyingLocked()) {
                     setMagnifiedRegionBorderShownLocked(false, false);
                     final long delay = (long) (mLongAnimationDuration
-                            * mWindowManagerService.mWindowAnimationScale);
+                            * mWindowManagerService.getWindowAnimationScaleLocked());
                     Message message = mHandler.obtainMessage(
                             MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
                     mHandler.sendMessageDelayed(message, delay);
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 7fe895b..63ae98e 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -87,7 +87,7 @@
             anim.initialize(width, height, width, height);
         }
         anim.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
-        anim.scaleCurrentDuration(mService.mTransitionAnimationScale);
+        anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
         int zorder = anim.getZAdjustment();
         int adj = 0;
         if (zorder == Animation.ZORDER_TOP) {
@@ -227,7 +227,8 @@
                 if (!animating) {
                     if (WindowManagerService.DEBUG_ANIM) Slog.v(
                         TAG, "Starting animation in " + mAppToken +
-                        " @ " + currentTime + " scale=" + mService.mTransitionAnimationScale
+                        " @ " + currentTime + " scale="
+                        + mService.getTransitionAnimationScaleLocked()
                         + " allDrawn=" + mAppToken.allDrawn + " animating=" + animating);
                     animation.setStartTime(currentTime);
                     animating = true;
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index ca9076f..3200b54 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import android.view.IWindowId;
+import android.view.IWindowSessionCallback;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
 import com.android.internal.view.IInputMethodManager;
@@ -54,6 +55,7 @@
 final class Session extends IWindowSession.Stub
         implements IBinder.DeathRecipient {
     final WindowManagerService mService;
+    final IWindowSessionCallback mCallback;
     final IInputMethodClient mClient;
     final IInputContext mInputContext;
     final int mUid;
@@ -62,14 +64,17 @@
     SurfaceSession mSurfaceSession;
     int mNumWindow = 0;
     boolean mClientDead = false;
+    float mLastReportedAnimatorScale;
 
-    public Session(WindowManagerService service, IInputMethodClient client,
-            IInputContext inputContext) {
+    public Session(WindowManagerService service, IWindowSessionCallback callback,
+            IInputMethodClient client, IInputContext inputContext) {
         mService = service;
+        mCallback = callback;
         mClient = client;
         mInputContext = inputContext;
         mUid = Binder.getCallingUid();
         mPid = Binder.getCallingPid();
+        mLastReportedAnimatorScale = service.getCurrentAnimatorScale();
         StringBuilder sb = new StringBuilder();
         sb.append("Session{");
         sb.append(Integer.toHexString(System.identityHashCode(this)));
@@ -464,6 +469,9 @@
             if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
                     WindowManagerService.TAG, "  NEW SURFACE SESSION " + mSurfaceSession);
             mService.mSessions.add(this);
+            if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
+                mService.dispatchNewAnimatorScaleLocked(this);
+            }
         }
         mNumWindow++;
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 616db42..2d15dab 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -24,6 +24,7 @@
 import android.util.TimeUtils;
 import android.view.IWindowId;
 
+import android.view.IWindowSessionCallback;
 import android.view.WindowContentFrameStats;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.policy.PolicyManager;
@@ -339,7 +340,7 @@
     /**
      * All currently active sessions with clients.
      */
-    final HashSet<Session> mSessions = new HashSet<Session>();
+    final ArraySet<Session> mSessions = new ArraySet<Session>();
 
     /**
      * Mapping from an IWindow IBinder to the server's Window object.
@@ -562,9 +563,10 @@
     PowerManager mPowerManager;
     PowerManagerInternal mPowerManagerInternal;
 
-    float mWindowAnimationScale = 1.0f;
-    float mTransitionAnimationScale = 1.0f;
-    float mAnimatorDurationScale = 1.0f;
+    float mWindowAnimationScaleSetting = 1.0f;
+    float mTransitionAnimationScaleSetting = 1.0f;
+    float mAnimatorDurationScaleSetting = 1.0f;
+    boolean mAnimationsDisabled = false;
 
     final InputManagerService mInputManager;
     final DisplayManagerInternal mDisplayManagerInternal;
@@ -780,6 +782,19 @@
         mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
         mPowerManagerInternal.setPolicy(mPolicy); // TODO: register as local service instead
+        mPowerManagerInternal.registerLowPowerModeObserver(
+                new PowerManagerInternal.LowPowerModeListener() {
+            @Override
+            public void onLowPowerModeChanged(boolean enabled) {
+                synchronized (mWindowMap) {
+                    if (mAnimationsDisabled != enabled) {
+                        mAnimationsDisabled = enabled;
+                        dispatchNewAnimatorScaleLocked(null);
+                    }
+                }
+            }
+        });
+        mAnimationsDisabled = mPowerManagerInternal.getLowPowerModeEnabled();
         mScreenFrozenLock = mPowerManager.newWakeLock(
                 PowerManager.PARTIAL_WAKE_LOCK, "SCREEN_FROZEN");
         mScreenFrozenLock.setReferenceCounted(false);
@@ -799,12 +814,12 @@
         );
 
         // Get persisted window scale setting
-        mWindowAnimationScale = Settings.Global.getFloat(context.getContentResolver(),
-                Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScale);
-        mTransitionAnimationScale = Settings.Global.getFloat(context.getContentResolver(),
-                Settings.Global.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale);
+        mWindowAnimationScaleSetting = Settings.Global.getFloat(context.getContentResolver(),
+                Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScaleSetting);
+        mTransitionAnimationScaleSetting = Settings.Global.getFloat(context.getContentResolver(),
+                Settings.Global.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScaleSetting);
         setAnimatorDurationScale(Settings.Global.getFloat(context.getContentResolver(),
-                Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorDurationScale));
+                Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorDurationScaleSetting));
 
         // Track changes to DevicePolicyManager state so we can enable/disable keyguard.
         IntentFilter filter = new IntentFilter();
@@ -5188,9 +5203,9 @@
 
         scale = fixScale(scale);
         switch (which) {
-            case 0: mWindowAnimationScale = scale; break;
-            case 1: mTransitionAnimationScale = scale; break;
-            case 2: mAnimatorDurationScale = scale; break;
+            case 0: mWindowAnimationScaleSetting = scale; break;
+            case 1: mTransitionAnimationScaleSetting = scale; break;
+            case 2: mAnimatorDurationScaleSetting = scale; break;
         }
 
         // Persist setting
@@ -5206,13 +5221,14 @@
 
         if (scales != null) {
             if (scales.length >= 1) {
-                mWindowAnimationScale = fixScale(scales[0]);
+                mWindowAnimationScaleSetting = fixScale(scales[0]);
             }
             if (scales.length >= 2) {
-                mTransitionAnimationScale = fixScale(scales[1]);
+                mTransitionAnimationScaleSetting = fixScale(scales[1]);
             }
             if (scales.length >= 3) {
-                setAnimatorDurationScale(fixScale(scales[2]));
+                mAnimatorDurationScaleSetting = fixScale(scales[2]);
+                dispatchNewAnimatorScaleLocked(null);
             }
         }
 
@@ -5221,24 +5237,43 @@
     }
 
     private void setAnimatorDurationScale(float scale) {
-        mAnimatorDurationScale = scale;
+        mAnimatorDurationScaleSetting = scale;
         ValueAnimator.setDurationScale(scale);
     }
 
+    public float getWindowAnimationScaleLocked() {
+        return mAnimationsDisabled ? 0 : mWindowAnimationScaleSetting;
+    }
+
+    public float getTransitionAnimationScaleLocked() {
+        return mAnimationsDisabled ? 0 : mTransitionAnimationScaleSetting;
+    }
+
     @Override
     public float getAnimationScale(int which) {
         switch (which) {
-            case 0: return mWindowAnimationScale;
-            case 1: return mTransitionAnimationScale;
-            case 2: return mAnimatorDurationScale;
+            case 0: return mWindowAnimationScaleSetting;
+            case 1: return mTransitionAnimationScaleSetting;
+            case 2: return mAnimatorDurationScaleSetting;
         }
         return 0;
     }
 
     @Override
     public float[] getAnimationScales() {
-        return new float[] { mWindowAnimationScale, mTransitionAnimationScale,
-                mAnimatorDurationScale };
+        return new float[] { mWindowAnimationScaleSetting, mTransitionAnimationScaleSetting,
+                mAnimatorDurationScaleSetting };
+    }
+
+    @Override
+    public float getCurrentAnimatorScale() {
+        synchronized(mWindowMap) {
+            return mAnimationsDisabled ? 0 : mAnimatorDurationScaleSetting;
+        }
+    }
+
+    void dispatchNewAnimatorScaleLocked(Session session) {
+        mH.obtainMessage(H.NEW_ANIMATOR_SCALE, session).sendToTarget();
     }
 
     @Override
@@ -6096,7 +6131,7 @@
                     && screenRotationAnimation.hasScreenshot()) {
                 if (screenRotationAnimation.setRotationInTransaction(
                         rotation, mFxSession,
-                        MAX_ANIMATION_DURATION, mTransitionAnimationScale,
+                        MAX_ANIMATION_DURATION, getTransitionAnimationScaleLocked(),
                         displayInfo.logicalWidth, displayInfo.logicalHeight)) {
                     scheduleAnimationLocked();
                 }
@@ -7181,6 +7216,8 @@
         public static final int SHOW_DISPLAY_MASK = 33;
         public static final int ALL_WINDOWS_DRAWN = 34;
 
+        public static final int NEW_ANIMATOR_SCALE = 35;
+
         @Override
         public void handleMessage(Message msg) {
             if (DEBUG_WINDOW_TRACE) {
@@ -7431,11 +7468,12 @@
 
                 case PERSIST_ANIMATION_SCALE: {
                     Settings.Global.putFloat(mContext.getContentResolver(),
-                            Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScale);
+                            Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScaleSetting);
                     Settings.Global.putFloat(mContext.getContentResolver(),
-                            Settings.Global.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale);
+                            Settings.Global.TRANSITION_ANIMATION_SCALE,
+                            mTransitionAnimationScaleSetting);
                     Settings.Global.putFloat(mContext.getContentResolver(),
-                            Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorDurationScale);
+                            Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorDurationScaleSetting);
                     break;
                 }
 
@@ -7638,6 +7676,33 @@
                         }
                     }
                 }
+                case NEW_ANIMATOR_SCALE: {
+                    float scale = getCurrentAnimatorScale();
+                    ValueAnimator.setDurationScale(scale);
+                    Session session = (Session)msg.obj;
+                    if (session != null) {
+                        try {
+                            session.mCallback.onAnimatorScaleChanged(scale);
+                        } catch (RemoteException e) {
+                        }
+                    } else {
+                        ArrayList<IWindowSessionCallback> callbacks
+                                = new ArrayList<IWindowSessionCallback>();
+                        synchronized (mWindowMap) {
+                            for (int i=0; i<mSessions.size(); i++) {
+                                callbacks.add(mSessions.valueAt(i).mCallback);
+                            }
+
+                        }
+                        for (int i=0; i<callbacks.size(); i++) {
+                            try {
+                                callbacks.get(i).onAnimatorScaleChanged(scale);
+                            } catch (RemoteException e) {
+                            }
+                        }
+                    }
+                }
+                break;
             }
             if (DEBUG_WINDOW_TRACE) {
                 Slog.v(TAG, "handleMessage: exit");
@@ -7650,11 +7715,11 @@
     // -------------------------------------------------------------
 
     @Override
-    public IWindowSession openSession(IInputMethodClient client,
+    public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
             IInputContext inputContext) {
         if (client == null) throw new IllegalArgumentException("null client");
         if (inputContext == null) throw new IllegalArgumentException("null inputContext");
-        Session session = new Session(this, client, inputContext);
+        Session session = new Session(this, callback, client, inputContext);
         return session;
     }
 
@@ -8764,7 +8829,7 @@
                             displayInfo.appWidth, displayInfo.appHeight, transit);
                     appAnimator.thumbnailAnimation = anim;
                     anim.restrictDuration(MAX_ANIMATION_DURATION);
-                    anim.scaleCurrentDuration(mTransitionAnimationScale);
+                    anim.scaleCurrentDuration(getTransitionAnimationScaleLocked());
                     Point p = new Point();
                     mAppTransition.getStartingPoint(p);
                     appAnimator.thumbnailX = p.x;
@@ -10072,7 +10137,7 @@
                 mExitAnimId = mEnterAnimId = 0;
             }
             if (screenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION,
-                    mTransitionAnimationScale, displayInfo.logicalWidth,
+                    getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
                         displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {
                 scheduleAnimationLocked();
             } else {
@@ -10407,13 +10472,10 @@
 
     void dumpSessionsLocked(PrintWriter pw, boolean dumpAll) {
         pw.println("WINDOW MANAGER SESSIONS (dumpsys window sessions)");
-        if (mSessions.size() > 0) {
-            Iterator<Session> it = mSessions.iterator();
-            while (it.hasNext()) {
-                Session s = it.next();
-                pw.print("  Session "); pw.print(s); pw.println(':');
-                s.dump(pw, "    ");
-            }
+        for (int i=0; i<mSessions.size(); i++) {
+            Session s = mSessions.valueAt(i);
+            pw.print("  Session "); pw.print(s); pw.println(':');
+            s.dump(pw, "    ");
         }
     }
 
@@ -10617,9 +10679,10 @@
             pw.print("  mLastWindowForcedOrientation="); pw.print(mLastWindowForcedOrientation);
                     pw.print(" mForcedAppOrientation="); pw.println(mForcedAppOrientation);
             pw.print("  mDeferredRotationPauseCount="); pw.println(mDeferredRotationPauseCount);
-            pw.print("  mWindowAnimationScale="); pw.print(mWindowAnimationScale);
-                    pw.print(" mTransitionWindowAnimationScale="); pw.print(mTransitionAnimationScale);
-                    pw.print(" mAnimatorDurationScale="); pw.println(mAnimatorDurationScale);
+            pw.print("  Animation settings: disabled="); pw.print(mAnimationsDisabled);
+                    pw.print(" window="); pw.print(mWindowAnimationScaleSetting);
+                    pw.print(" transition="); pw.print(mTransitionAnimationScaleSetting);
+                    pw.print(" animator="); pw.println(mAnimatorDurationScaleSetting);
             pw.print("  mTraversalScheduled="); pw.println(mTraversalScheduled);
             pw.print("  mStartingIconInTransition="); pw.print(mStartingIconInTransition);
                     pw.print(" mSkipAppTransitionAnimation="); pw.println(mSkipAppTransitionAnimation);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index e257ebc..bda10de 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -207,7 +207,7 @@
         mLocalAnimating = false;
         mAnimation = anim;
         mAnimation.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
-        mAnimation.scaleCurrentDuration(mService.mWindowAnimationScale);
+        mAnimation.scaleCurrentDuration(mService.getWindowAnimationScaleLocked());
         // Start out animation gone if window is gone, or visible if window is visible.
         mTransformation.clear();
         mTransformation.setAlpha(mLastHidden ? 0 : 1);
@@ -283,7 +283,7 @@
                         " @ " + currentTime + ": ww=" + mWin.mFrame.width() +
                         " wh=" + mWin.mFrame.height() +
                         " dw=" + mAnimDw + " dh=" + mAnimDh +
-                        " scale=" + mService.mWindowAnimationScale);
+                        " scale=" + mService.getWindowAnimationScaleLocked());
                     mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(),
                             mAnimDw, mAnimDh);
                     final DisplayInfo displayInfo = displayContent.getDisplayInfo();
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index cfe8e15..2f40003 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -195,8 +195,8 @@
     }
 
     @Override
-    public IWindowSession openSession(IInputMethodClient arg0, IInputContext arg1)
-            throws RemoteException {
+    public IWindowSession openSession(IWindowSessionCallback argn1, IInputMethodClient arg0,
+            IInputContext arg1) throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
@@ -276,6 +276,11 @@
     }
 
     @Override
+    public float getCurrentAnimatorScale() throws RemoteException {
+        return 0;
+    }
+
+    @Override
     public void setAppGroupId(IBinder arg0, int arg1) throws RemoteException {
         // TODO Auto-generated method stub