Merge "Optimize RTL properties resolution" into jb-mr2-dev
diff --git a/api/current.txt b/api/current.txt
index 1166546..21c1ef0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6061,8 +6061,6 @@
     field public static final java.lang.String ACTION_GTALK_SERVICE_CONNECTED = "android.intent.action.GTALK_CONNECTED";
     field public static final java.lang.String ACTION_GTALK_SERVICE_DISCONNECTED = "android.intent.action.GTALK_DISCONNECTED";
     field public static final java.lang.String ACTION_HEADSET_PLUG = "android.intent.action.HEADSET_PLUG";
-    field public static final java.lang.String ACTION_IDLE_MAINTENANCE_END = "android.intent.action.ACTION_IDLE_MAINTENANCE_END";
-    field public static final java.lang.String ACTION_IDLE_MAINTENANCE_START = "android.intent.action.ACTION_IDLE_MAINTENANCE_START";
     field public static final java.lang.String ACTION_INPUT_METHOD_CHANGED = "android.intent.action.INPUT_METHOD_CHANGED";
     field public static final java.lang.String ACTION_INSERT = "android.intent.action.INSERT";
     field public static final java.lang.String ACTION_INSERT_OR_EDIT = "android.intent.action.INSERT_OR_EDIT";
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 67bd952..bfc7bf5 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2356,6 +2356,8 @@
      * </p>
      *
      * @see #ACTION_IDLE_MAINTENANCE_END
+     *
+     * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_IDLE_MAINTENANCE_START =
@@ -2383,6 +2385,8 @@
      * by the system.
      *
      * @see #ACTION_IDLE_MAINTENANCE_START
+     *
+     * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_IDLE_MAINTENANCE_END =
diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java
index 7189610..39078ca 100644
--- a/core/java/com/android/internal/view/menu/MenuItemImpl.java
+++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java
@@ -598,11 +598,13 @@
         mActionView = null;
         mActionProvider = actionProvider;
         mMenu.onItemsChanged(true); // Measurement can be changed
-        mActionProvider.setVisibilityListener(new ActionProvider.VisibilityListener() {
-            @Override public void onActionProviderVisibilityChanged(boolean isVisible) {
-                mMenu.onItemVisibleChanged(MenuItemImpl.this);
-            }
-        });
+        if (mActionProvider != null) {
+            mActionProvider.setVisibilityListener(new ActionProvider.VisibilityListener() {
+                @Override public void onActionProviderVisibilityChanged(boolean isVisible) {
+                    mMenu.onItemVisibleChanged(MenuItemImpl.this);
+                }
+            });
+        }
         return this;
     }
 
diff --git a/docs/html/google/google_toc.cs b/docs/html/google/google_toc.cs
index 81982a1..ff2d27a 100644
--- a/docs/html/google/google_toc.cs
+++ b/docs/html/google/google_toc.cs
@@ -40,6 +40,10 @@
           <span class="en">Google Maps</span></a>
       </li>
 
+       <li><a href="<?cs var:toroot?>google/play-services/games.html">
+          <span class="en">Games</span></a>
+      </li>
+
       <li id="gms-tree-list" class="nav-section">
         <div class="nav-section-header">
           <a href="<?cs var:toroot ?>reference/gms-packages.html">
diff --git a/docs/html/google/play-services/games.jd b/docs/html/google/play-services/games.jd
new file mode 100644
index 0000000..8555b94
--- /dev/null
+++ b/docs/html/google/play-services/games.jd
@@ -0,0 +1,69 @@
+page.title=Google Play Games
+header.hide=1
+
+@jd:body
+
+<div class="landing-banner">
+        
+<div class="col-6">
+  <img src="{@docRoot}images/google/game_services.png" alt="">
+</div>
+<div class="col-6">
+
+<h1 itemprop="name" style="margin-bottom:0;">Google Play Games Platform Services</h1>
+  <p itemprop="description">
+  Google Play Games Platform Services lets you integrate popular gaming features such as achievements, leaderboards, and real-time multiplayer gameplay in your apps. Players can sign in using their Google+ identities and share their gaming experience with friends.
+  </p>
+  <p>Visit <a class="external-link"
+    href="https://developers.google.com/games/services/">developers.google.com/games/services</a> for more information about integrating game services into your app.
+</p>
+</div>
+</div>
+
+<div class="landing-docs">
+  <div class="col-6 normal-links">
+    <h3 style="clear:left">Key Developer Features</h3>
+    <h4>Drive engagement with leaderboards</h4>
+    <p>Let players compare scores with friends using leaderboards or see how they rank against other players worldwide. Games Platform Services  automatically maintains daily, weekly, and all-time high scores. <a class="external-link" href="https://developers.google.com/games/services/android/leaderboards">Build leaderboards</a>.</p>
+    
+    <h4>Reward players with achievements</h4>
+    <p>Add hidden and incremental achievements to encourage users to explore your game in new and interesting ways. You can use the built-in UI for Android to display achievement progress.
+    <a class="external-link" href="https://developers.google.com/games/services/android/achievements">Add achievements to your game</a>.</p>
+    </a>
+
+    <h4>Create real-time multiplayer games</h4>
+    <p>Make your game more dynamic by letting multiple players compete or cooperate simultaneously. You can use the Games Platform Services API to invite game participants or auto-match players anonymously, and exchange data between game clients. <a class="external-link" href="https://developers.google.com/games/services/android/multiplayer">Develop real-time multiplayer games</a>.</p>
+    
+    <h4>Save game progress to the cloud</h4>
+    <p>Store game data on Google servers using Cloud Save. Synchronize game progress seamlessly across all your users' devices. <a class="external-link" href="https://developers.google.com/games/services/android/cloudsave">Save games in the cloud</a>.</p>
+  </div>
+
+
+  <div class="col-6 normal-links">
+    <h3 style="clear:left">Getting Started</h3>
+    <h4>1. Get the Google Play services SDK</h4>
+    <p>The Games Platform Services API for Android is part of the Google Play services platform.</p>
+    <p>To use game services, <a href="{@docRoot}google/play-services/setup.html">set up</a>
+      the Google Play services SDK. Then, see the <a class="external-link"
+      href="https://developers.google.com/games/services/android/quickstart">
+      Getting Started guide</a> to set up your app.
+    </p>
+            
+    <h4>2. Run the sample</h4>
+    
+    <p>Once you've installed the Google Play services package, <a class="external-link"
+      href="https://developers.google.com/games/services/downloads/">download the game services samples</a> to learn how to use the major components of the Games Platform Services APIs.
+    </p>
+    
+    <h4>3. Read the documentation</h4>
+    
+    <p>Read the <a class="external-link" href="https://developers.google.com/games/services/terms">
+    API Terms of Service</a>.</p> 
+    <p>Detailed documentation for the Games Platform Services is available at <a class="external-link"
+    href="https://developers.google.com/games/services/">developers.google.com/games/services</a>.
+    </p>
+    <p>For quick access while developing your Android apps, the
+      <a href="{@docRoot}reference/com/google/android/gms/games/package-summary.html">Games Platform Services API for Android reference</a> is available here on developer.android.com.</p>
+  </div>
+
+</div>
diff --git a/docs/html/images/google/game_services.png b/docs/html/images/google/game_services.png
new file mode 100644
index 0000000..f62d7f0
--- /dev/null
+++ b/docs/html/images/google/game_services.png
Binary files differ
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index dbffa97..1f2947d 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -236,6 +236,15 @@
         }
     }
 
+    /**
+     * Returns a non-zero value if an  unsupported charger is attached.
+     */
+    public int getInvalidCharger() {
+        synchronized (mLock) {
+            return mInvalidCharger;
+        }
+    }
+
     private void shutdownIfNoPowerLocked() {
         // shut down gracefully if our battery is critically low and we are not powered.
         // wait until the system has booted before attempting to display the shutdown dialog.
diff --git a/services/java/com/android/server/IdleMaintenanceService.java b/services/java/com/android/server/IdleMaintenanceService.java
index 0c90de4..584d4bc 100644
--- a/services/java/com/android/server/IdleMaintenanceService.java
+++ b/services/java/com/android/server/IdleMaintenanceService.java
@@ -17,11 +17,12 @@
 package com.android.server;
 
 import android.app.Activity;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.os.BatteryManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.PowerManager;
@@ -40,7 +41,6 @@
  * The current implementation is very simple. The start of a maintenance
  * window is announced if: the screen is off or showing a dream AND the
  * battery level is more than twenty percent AND at least one hour passed
- * since the screen went off or a dream started (i.e. since the last user
  * activity).
  *
  * The end of a maintenance window is announced only if: a start was
@@ -48,27 +48,44 @@
  */
 public class IdleMaintenanceService extends BroadcastReceiver {
 
-    private final boolean DEBUG = false;
+    private static final boolean DEBUG = false;
 
     private static final String LOG_TAG = IdleMaintenanceService.class.getSimpleName();
 
     private static final int LAST_USER_ACTIVITY_TIME_INVALID = -1;
 
-    private static final long MILLIS_IN_DAY = 24 * 60 * 60 * 1000;
+    private static final long MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS = 24 * 60 * 60 * 1000; // 1 day
 
     private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING = 30; // percent
 
     private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING = 80; // percent
 
-    private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING = 10; // percent
+    private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING = 20; // percent
 
-    private static final long MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START = 60 * 60 * 1000; // 1 hour
+    private static final long MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START = 71 * 60 * 1000; // 71 min
 
-    private final Intent mIdleMaintenanceStartIntent =
-            new Intent(Intent.ACTION_IDLE_MAINTENANCE_START);
+    private static final long MAX_IDLE_MAINTENANCE_DURATION = 71 * 60 * 1000; // 71 min
 
-    private final Intent mIdleMaintenanceEndIntent =
-            new Intent(Intent.ACTION_IDLE_MAINTENANCE_END);
+    private static final String ACTION_UPDATE_IDLE_MAINTENANCE_STATE =
+        "com.android.server.IdleMaintenanceService.action.UPDATE_IDLE_MAINTENANCE_STATE";
+
+    private static final Intent sIdleMaintenanceStartIntent;
+    static {
+        sIdleMaintenanceStartIntent = new Intent(Intent.ACTION_IDLE_MAINTENANCE_START);
+        sIdleMaintenanceStartIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+    };
+
+    private static final Intent sIdleMaintenanceEndIntent;
+    static {
+        sIdleMaintenanceEndIntent = new Intent(Intent.ACTION_IDLE_MAINTENANCE_END);
+        sIdleMaintenanceEndIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+    }
+
+    private final AlarmManager mAlarmService;
+
+    private final BatteryService mBatteryService;
+
+    private final PendingIntent mUpdateIdleMaintenanceStatePendingIntent;
 
     private final Context mContext;
 
@@ -76,30 +93,37 @@
 
     private final Handler mHandler;
 
-    private long mLastIdleMaintenanceStartTimeMillis = SystemClock.elapsedRealtime();
+    private long mLastIdleMaintenanceStartTimeMillis;
 
     private long mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
 
-    private int mBatteryLevel;
-
-    private boolean mBatteryCharging;
-
     private boolean mIdleMaintenanceStarted;
 
-    public IdleMaintenanceService(Context context) {
+    public IdleMaintenanceService(Context context, BatteryService batteryService) {
         mContext = context;
+        mBatteryService = batteryService;
+
+        mAlarmService = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
 
         PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
 
         mHandler = new Handler(mContext.getMainLooper());
 
+        Intent intent = new Intent(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
+        intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        mUpdateIdleMaintenanceStatePendingIntent = PendingIntent.getBroadcast(mContext, 0,
+                intent, PendingIntent.FLAG_UPDATE_CURRENT);
+
         register(mContext.getMainLooper());
     }
 
     public void register(Looper looper) {
         IntentFilter intentFilter = new IntentFilter();
 
+        // Alarm actions.
+        intentFilter.addAction(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
+
         // Battery actions.
         intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
 
@@ -115,67 +139,117 @@
                 intentFilter, null, new Handler(looper));
     }
 
+    private void scheduleUpdateIdleMaintenanceState(long delayMillis) {
+        final long triggetRealTimeMillis = SystemClock.elapsedRealtime() + delayMillis;
+        mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggetRealTimeMillis,
+                mUpdateIdleMaintenanceStatePendingIntent);
+    }
+
+    private void unscheduleUpdateIdleMaintenanceState() {
+        mAlarmService.cancel(mUpdateIdleMaintenanceStatePendingIntent);
+    }
+
     private void updateIdleMaintenanceState() {
         if (mIdleMaintenanceStarted) {
-            // Idle maintenance can be interrupted only by
-            // a change of the device state.
-            if (!deviceStatePermitsIdleMaintenanceRunning()) {
+            // Idle maintenance can be interrupted by user activity, or duration
+            // time out, or low battery.
+            if (!lastUserActivityPermitsIdleMaintenanceRunning()
+                    || !batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning()) {
+                unscheduleUpdateIdleMaintenanceState();
                 mIdleMaintenanceStarted = false;
                 EventLogTags.writeIdleMaintenanceWindowFinish(SystemClock.elapsedRealtime(),
-                        mLastUserActivityElapsedTimeMillis, mBatteryLevel,
-                        mBatteryCharging ? 1 : 0);
+                        mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
+                        isBatteryCharging() ? 1 : 0);
                 sendIdleMaintenanceEndIntent();
+                // We stopped since we don't have enough battery or timed out but the
+                // user is not using the device, so we should be able to run maintenance
+                // in the next maintenance window since the battery may be charged
+                // without interaction and the min interval between maintenances passed.
+                if (!batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning()) {
+                    scheduleUpdateIdleMaintenanceState(
+                            getNextIdleMaintenanceIntervalStartFromNow());
+                }
             }
         } else if (deviceStatePermitsIdleMaintenanceStart()
                 && lastUserActivityPermitsIdleMaintenanceStart()
                 && lastRunPermitsIdleMaintenanceStart()) {
+            // Now that we started idle maintenance, we should schedule another
+            // update for the moment when the idle maintenance times out.
+            scheduleUpdateIdleMaintenanceState(MAX_IDLE_MAINTENANCE_DURATION);
             mIdleMaintenanceStarted = true;
             EventLogTags.writeIdleMaintenanceWindowStart(SystemClock.elapsedRealtime(),
-                    mLastUserActivityElapsedTimeMillis, mBatteryLevel,
-                    mBatteryCharging ? 1 : 0);
+                    mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
+                    isBatteryCharging() ? 1 : 0);
             mLastIdleMaintenanceStartTimeMillis = SystemClock.elapsedRealtime();
             sendIdleMaintenanceStartIntent();
+        } else if (lastUserActivityPermitsIdleMaintenanceStart()) {
+             if (lastRunPermitsIdleMaintenanceStart()) {
+                // The user does not use the device and we did not run maintenance in more
+                // than the min interval between runs, so schedule an update - maybe the
+                // battery will be charged latter.
+                scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
+             } else {
+                 // The user does not use the device but we have run maintenance in the min
+                 // interval between runs, so schedule an update after the min interval ends.
+                 scheduleUpdateIdleMaintenanceState(
+                         getNextIdleMaintenanceIntervalStartFromNow());
+             }
         }
     }
 
+    private long getNextIdleMaintenanceIntervalStartFromNow() {
+        return mLastIdleMaintenanceStartTimeMillis + MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS
+                - SystemClock.elapsedRealtime();
+    }
+
     private void sendIdleMaintenanceStartIntent() {
-        if (DEBUG) {
-            Log.i(LOG_TAG, "Broadcasting " + Intent.ACTION_IDLE_MAINTENANCE_START);
-        }
         mWakeLock.acquire();
-        mContext.sendOrderedBroadcastAsUser(mIdleMaintenanceStartIntent, UserHandle.ALL,
+        mContext.sendOrderedBroadcastAsUser(sIdleMaintenanceStartIntent, UserHandle.ALL,
                 null, this, mHandler, Activity.RESULT_OK, null, null);
     }
 
     private void sendIdleMaintenanceEndIntent() {
-        if (DEBUG) {
-            Log.i(LOG_TAG, "Broadcasting " + Intent.ACTION_IDLE_MAINTENANCE_END);
-        }
         mWakeLock.acquire();
-        mContext.sendOrderedBroadcastAsUser(mIdleMaintenanceEndIntent, UserHandle.ALL,
+        mContext.sendOrderedBroadcastAsUser(sIdleMaintenanceEndIntent, UserHandle.ALL,
                 null, this, mHandler, Activity.RESULT_OK, null, null);
     }
 
     private boolean deviceStatePermitsIdleMaintenanceStart() {
-        final int minBatteryLevel = mBatteryCharging
+        final int minBatteryLevel = isBatteryCharging()
                 ? MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING
                 : MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING;
         return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
-                && mBatteryLevel > minBatteryLevel);
-    }
-
-    private boolean deviceStatePermitsIdleMaintenanceRunning() {
-        return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
-                && mBatteryLevel > MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING);
+                && mBatteryService.getBatteryLevel() > minBatteryLevel);
     }
 
     private boolean lastUserActivityPermitsIdleMaintenanceStart() {
-        return (SystemClock.elapsedRealtime() - mLastUserActivityElapsedTimeMillis
-                > MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
+        // The last time the user poked the device is above the threshold.
+        return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
+                && SystemClock.elapsedRealtime() - mLastUserActivityElapsedTimeMillis
+                    > MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
     }
 
     private boolean lastRunPermitsIdleMaintenanceStart() {
-        return SystemClock.elapsedRealtime() - mLastIdleMaintenanceStartTimeMillis > MILLIS_IN_DAY;
+        // Enough time passed since the last maintenance run.
+        return SystemClock.elapsedRealtime() - mLastIdleMaintenanceStartTimeMillis
+                > MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS;
+    }
+
+    private boolean lastUserActivityPermitsIdleMaintenanceRunning() {
+        // The user is not using the device.
+        return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID);
+    }
+
+    private boolean batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning() {
+        // Battery not too low and the maintenance duration did not timeout.
+        return (mBatteryService.getBatteryLevel() > MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING
+                && mLastIdleMaintenanceStartTimeMillis + MAX_IDLE_MAINTENANCE_DURATION
+                        > SystemClock.elapsedRealtime());
+    }
+
+    private boolean isBatteryCharging() {
+        return mBatteryService.getPlugType() > 0
+                && mBatteryService.getInvalidCharger() == 0;
     }
 
     @Override
@@ -185,24 +259,38 @@
         }
         String action = intent.getAction();
         if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
-            final int maxBatteryLevel = intent.getExtras().getInt(BatteryManager.EXTRA_SCALE);
-            final int currBatteryLevel = intent.getExtras().getInt(BatteryManager.EXTRA_LEVEL);
-            mBatteryLevel = (int) (((float) maxBatteryLevel / 100) * currBatteryLevel);
-            final int pluggedState = intent.getExtras().getInt(BatteryManager.EXTRA_PLUGGED);
-            final int chargerState = intent.getExtras().getInt(
-                    BatteryManager.EXTRA_INVALID_CHARGER, 0);
-            mBatteryCharging = (pluggedState > 0 && chargerState == 0);
+            // We care about battery only if maintenance is in progress so we can
+            // stop it if battery is too low. Note that here we assume that the
+            // maintenance clients are properly holding a wake lock. We will
+            // refactor the maintenance to use services instead of intents for the
+            // next release. The only client for this for now is internal an holds
+            // a wake lock correctly.
+            if (mIdleMaintenanceStarted) {
+                updateIdleMaintenanceState();
+            }
         } else if (Intent.ACTION_SCREEN_ON.equals(action)
                 || Intent.ACTION_DREAMING_STOPPED.equals(action)) {
             mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
+            // Unschedule any future updates since we already know that maintenance
+            // cannot be performed since the user is back.
+            unscheduleUpdateIdleMaintenanceState();
+            // If the screen went on/stopped dreaming, we know the user is using the
+            // device which means that idle maintenance should be stopped if running.
+            updateIdleMaintenanceState();
         } else if (Intent.ACTION_SCREEN_OFF.equals(action)
                 || Intent.ACTION_DREAMING_STARTED.equals(action)) {
             mLastUserActivityElapsedTimeMillis = SystemClock.elapsedRealtime();
+            // If screen went off/started dreaming, we may be able to start idle maintenance
+            // after the minimal user inactivity elapses. We schedule an alarm for when
+            // this timeout elapses since the device may go to sleep by then.
+            scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
+        } else if (ACTION_UPDATE_IDLE_MAINTENANCE_STATE.equals(action)) {
+            updateIdleMaintenanceState();
         } else if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)
                 || Intent.ACTION_IDLE_MAINTENANCE_END.equals(action)) {
+            // We were holding a wake lock while broadcasting the idle maintenance
+            // intents but now that we finished the broadcast release the wake lock.
             mWakeLock.release();
-            return;
         }
-        updateIdleMaintenanceState();
     }
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 7994b56..9455017 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -745,7 +745,7 @@
 
             try {
                 Slog.i(TAG, "IdleMaintenanceService");
-                new IdleMaintenanceService(context);
+                new IdleMaintenanceService(context, battery);
             } catch (Throwable e) {
                 reportWtf("starting IdleMaintenanceService", e);
             }
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 138c51b..4ae9eb5 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1711,6 +1711,8 @@
 
         final KeyEventDispatcher mKeyEventDispatcher = new KeyEventDispatcher();
 
+        boolean mWasConnectedAndDied;
+
         // Handler only for dispatching accessibility events since we use event
         // types as message types allowing us to remove messages per event type.
         public Handler mEventDispatchHandler = new Handler(mMainHandler.getLooper()) {
@@ -1865,8 +1867,9 @@
                 mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service);
                 UserState userState = getUserStateLocked(mUserId);
                 addServiceLocked(this, userState);
-                if (userState.mBindingServices.contains(mComponentName)) {
+                if (userState.mBindingServices.contains(mComponentName) || mWasConnectedAndDied) {
                     userState.mBindingServices.remove(mComponentName);
+                    mWasConnectedAndDied = false;
                     try {
                        mServiceInterface.setConnection(this, mId);
                        onUserStateChangedLocked(userState);
@@ -2220,7 +2223,7 @@
             mServiceInterface = null;
         }
 
-        public boolean isInitializedLocked() {
+        public boolean isConnectedLocked() {
             return (mService != null);
         }
 
@@ -2230,9 +2233,10 @@
                 // whose handling the death recipient is unlinked and still get a call
                 // on binderDied since the call was made before we unlink but was
                 // waiting on the lock we held during the force stop handling.
-                if (!isInitializedLocked()) {
+                if (!isConnectedLocked()) {
                     return;
                 }
+                mWasConnectedAndDied = true;
                 mKeyEventDispatcher.flush();
                 UserState userState = getUserStateLocked(mUserId);
                 // The death recipient is unregistered in removeServiceLocked
@@ -2245,7 +2249,6 @@
                     userState.mEnabledServices.remove(mComponentName);
                     userState.destroyUiAutomationService();
                 }
-                onUserStateChangedLocked(userState);
             }
         }
 
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index fccaab5..9fdd293 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -371,15 +371,15 @@
                         return;
                     }
                     try {
-                        if (foregroundNoti.icon == 0) {
+                        if (localForegroundNoti.icon == 0) {
                             // It is not correct for the caller to supply a notification
                             // icon, but this used to be able to slip through, so for
                             // those dirty apps give it the app's icon.
-                            foregroundNoti.icon = appInfo.icon;
+                            localForegroundNoti.icon = appInfo.icon;
 
                             // Do not allow apps to present a sneaky invisible content view either.
-                            foregroundNoti.contentView = null;
-                            foregroundNoti.bigContentView = null;
+                            localForegroundNoti.contentView = null;
+                            localForegroundNoti.bigContentView = null;
                             CharSequence appName = appInfo.loadLabel(
                                     ams.mContext.getPackageManager());
                             if (appName == null) {
@@ -395,7 +395,7 @@
                                         appInfo.packageName, null));
                                 PendingIntent pi = PendingIntent.getActivity(ams.mContext, 0,
                                         runningIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-                                foregroundNoti.setLatestEventInfo(ctx,
+                                localForegroundNoti.setLatestEventInfo(ctx,
                                         ams.mContext.getString(
                                                 com.android.internal.R.string
                                                         .app_running_notification_title,
@@ -406,10 +406,10 @@
                                                 appName),
                                         pi);
                             } catch (PackageManager.NameNotFoundException e) {
-                                foregroundNoti.icon = 0;
+                                localForegroundNoti.icon = 0;
                             }
                         }
-                        if (foregroundNoti.icon == 0) {
+                        if (localForegroundNoti.icon == 0) {
                             // Notifications whose icon is 0 are defined to not show
                             // a notification, silently ignoring it.  We don't want to
                             // just ignore it, we want to prevent the service from