am 7741e620: Merge "Making sure the sticky widget logic works with music" into jb-mr1-lockscreen-dev

* commit '7741e6209ab4c6425ee4156631f54fe9bfce5fc3':
  Making sure the sticky widget logic works with music
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 6624eb8..a300776 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -3495,25 +3495,29 @@
         }
 
         /**
-         * Returns true if video snapshot is supported. That is, applications
+         * <p>Returns true if video snapshot is supported. That is, applications
          * can call {@link #takePicture(Camera.ShutterCallback,
-         * Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)}
-         * during recording. Applications do not need to call {@link
-         * #startPreview()} after taking a picture. The preview will be still
-         * active. Other than that, taking a picture during recording is
-         * identical to taking a picture normally. All settings and methods
-         * related to takePicture work identically. Ex: {@link
-         * #getPictureSize()}, {@link #getSupportedPictureSizes()}, {@link
-         * #setJpegQuality(int)}, {@link #setRotation(int)}, and etc. The
-         * picture will have an EXIF header. {@link #FLASH_MODE_AUTO} and {@link
-         * #FLASH_MODE_ON} also still work, but the video will record the flash.
+         * Camera.PictureCallback, Camera.PictureCallback,
+         * Camera.PictureCallback)} during recording. Applications do not need
+         * to call {@link #startPreview()} after taking a picture. The preview
+         * will be still active. Other than that, taking a picture during
+         * recording is identical to taking a picture normally. All settings and
+         * methods related to takePicture work identically. Ex:
+         * {@link #getPictureSize()}, {@link #getSupportedPictureSizes()},
+         * {@link #setJpegQuality(int)}, {@link #setRotation(int)}, and etc. The
+         * picture will have an EXIF header. {@link #FLASH_MODE_AUTO} and
+         * {@link #FLASH_MODE_ON} also still work, but the video will record the
+         * flash.</p>
          *
-         * Applications can set shutter callback as null to avoid the shutter
+         * <p>Applications can set shutter callback as null to avoid the shutter
          * sound. It is also recommended to set raw picture and post view
-         * callbacks to null to avoid the interrupt of preview display.
+         * callbacks to null to avoid the interrupt of preview display.</p>
          *
-         * Field-of-view of the recorded video may be different from that of the
-         * captured pictures.
+         * <p>Field-of-view of the recorded video may be different from that of the
+         * captured pictures. The maximum size of a video snapshot may be
+         * smaller than that for regular still captures. If the current picture
+         * size is set higher than can be supported by video snapshot, the
+         * picture will be captured at the maximum supported size instead.</p>
          *
          * @return true if video snapshot is supported.
          */
diff --git a/core/java/android/net/DhcpStateMachine.java b/core/java/android/net/DhcpStateMachine.java
index 874e80a..8dc900e 100644
--- a/core/java/android/net/DhcpStateMachine.java
+++ b/core/java/android/net/DhcpStateMachine.java
@@ -351,6 +351,8 @@
         DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal();
 
         if (dhcpAction == DhcpAction.START) {
+            /* Stop any existing DHCP daemon before starting new */
+            NetworkUtils.stopDhcp(mInterfaceName);
             if (DBG) Log.d(TAG, "DHCP request on " + mInterfaceName);
             success = NetworkUtils.runDhcp(mInterfaceName, dhcpInfoInternal);
             mDhcpInfo = dhcpInfoInternal;
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 5397eb6..b1a44c5 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -289,13 +289,7 @@
     public void setError(CharSequence error, Drawable icon) {
         mError = TextUtils.stringOrSpannedString(error);
         mErrorWasChanged = true;
-        final Drawables dr = mTextView.mDrawables;
-        if (dr != null) {
-            mTextView.setCompoundDrawables(dr.mDrawableLeft, dr.mDrawableTop, icon,
-                    dr.mDrawableBottom);
-        } else {
-            mTextView.setCompoundDrawables(null, null, icon, null);
-        }
+
         if (mError == null) {
             if (mErrorPopup != null) {
                 if (mErrorPopup.isShowing()) {
@@ -304,10 +298,21 @@
 
                 mErrorPopup = null;
             }
+
+            setErrorIcon(null);
+        } else if (mTextView.isFocused()) {
+            showError();
+            setErrorIcon(icon);
+        }
+    }
+
+    private void setErrorIcon(Drawable icon) {
+        final Drawables dr = mTextView.mDrawables;
+        if (dr != null) {
+            mTextView.setCompoundDrawables(dr.mDrawableLeft, dr.mDrawableTop, icon,
+                    dr.mDrawableBottom);
         } else {
-            if (mTextView.isFocused()) {
-                showError();
-            }
+            mTextView.setCompoundDrawables(null, null, icon, null);
         }
     }
 
@@ -316,6 +321,8 @@
             if (mErrorPopup.isShowing()) {
                 mErrorPopup.dismiss();
             }
+
+            setErrorIcon(null);
         }
 
         mShowErrorAfterAttach = false;
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index e7c4c23..9537ac4 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -122,9 +122,9 @@
     return (jboolean)(::wifi_start_supplicant(p2pSupported) == 0);
 }
 
-static jboolean android_net_wifi_killSupplicant(JNIEnv* env, jobject)
+static jboolean android_net_wifi_killSupplicant(JNIEnv* env, jobject, jboolean p2pSupported)
 {
-    return (jboolean)(::wifi_stop_supplicant() == 0);
+    return (jboolean)(::wifi_stop_supplicant(p2pSupported) == 0);
 }
 
 static jboolean android_net_wifi_connectToSupplicant(JNIEnv* env, jobject, jstring jIface)
@@ -204,7 +204,7 @@
     { "isDriverLoaded", "()Z",  (void *)android_net_wifi_isDriverLoaded },
     { "unloadDriver", "()Z",  (void *)android_net_wifi_unloadDriver },
     { "startSupplicant", "(Z)Z",  (void *)android_net_wifi_startSupplicant },
-    { "killSupplicant", "()Z",  (void *)android_net_wifi_killSupplicant },
+    { "killSupplicant", "(Z)Z",  (void *)android_net_wifi_killSupplicant },
     { "connectToSupplicant", "(Ljava/lang/String;)Z",
             (void *)android_net_wifi_connectToSupplicant },
     { "closeSupplicantConnection", "(Ljava/lang/String;)V",
diff --git a/core/res/res/drawable-hdpi/ic_coins_l.png b/core/res/res/drawable-hdpi/ic_coins_l.png
new file mode 100644
index 0000000..e1e3e2a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_coins_l.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_coins_l.png b/core/res/res/drawable-mdpi/ic_coins_l.png
new file mode 100644
index 0000000..a6d7abb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_coins_l.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_coins_l.png b/core/res/res/drawable-xhdpi/ic_coins_l.png
new file mode 100644
index 0000000..84e7e72
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_coins_l.png
Binary files differ
diff --git a/core/res/res/layout/sms_short_code_confirmation_dialog.xml b/core/res/res/layout/sms_short_code_confirmation_dialog.xml
index ec39d97..d82f560 100644
--- a/core/res/res/layout/sms_short_code_confirmation_dialog.xml
+++ b/core/res/res/layout/sms_short_code_confirmation_dialog.xml
@@ -30,9 +30,9 @@
         style="?android:attr/textAppearanceMedium"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:paddingLeft="16dip"
-        android:paddingRight="16dip"
-        android:paddingTop="8dip"
+        android:paddingLeft="20dip"
+        android:paddingRight="20dip"
+        android:paddingTop="16dip"
         android:paddingBottom="16dip" />
 
     <TableLayout android:id="@+id/sms_short_code_detail_layout"
@@ -51,7 +51,7 @@
                 android:layout_height="wrap_content"
                 android:paddingLeft="8dip"
                 android:paddingRight="8dip"
-                android:src="@null" />
+                android:src="@drawable/ic_coins_l" />
             <TextView android:id="@+id/sms_short_code_detail_message"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content" />
@@ -60,14 +60,19 @@
         <TableRow
             android:layout_width="wrap_content"
             android:layout_height="wrap_content" >
-
-            <CheckBox android:id="@+id/sms_short_code_remember_choice_checkbox"
-                android:layout_width="wrap_content"
+            <RelativeLayout android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:paddingRight="8dip" />
+                android:paddingTop="12dip"
+                android:paddingLeft="8dip" >
+            <CheckBox android:id="@+id/sms_short_code_remember_choice_checkbox"
+                android:paddingTop="11dip"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" />
+            </RelativeLayout>
             <TextView android:id="@+id/sms_short_code_remember_choice_text"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:paddingTop="18dip"
                 android:text="@string/sms_short_code_remember_choice" />
         </TableRow>
 
@@ -77,6 +82,7 @@
 
             <Space android:layout_gravity="fill" />
             <TextView android:id="@+id/sms_short_code_remember_undo_instruction"
+                android:paddingTop="10dip"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content" />
         </TableRow>
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_10/land_back.png b/docs/html/distribute/promote/device-art-resources/nexus_10/land_back.png
index 71bb52d..d082d50 100644
--- a/docs/html/distribute/promote/device-art-resources/nexus_10/land_back.png
+++ b/docs/html/distribute/promote/device-art-resources/nexus_10/land_back.png
Binary files differ
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 406f644..5397a29 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -3168,8 +3168,7 @@
 
         if (lidOpen) {
             if (keyguardIsShowingTq()) {
-                mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(
-                        KeyEvent.KEYCODE_POWER, mDockMode != Intent.EXTRA_DOCK_STATE_UNDOCKED);
+                mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(KeyEvent.KEYCODE_POWER);
             } else {
                 mPowerManager.wakeUp(SystemClock.uptimeMillis());
             }
@@ -3388,11 +3387,10 @@
             // When the screen is off and the key is not injected, determine whether
             // to wake the device but don't pass the key to the application.
             result = 0;
-            if (down && isWakeKey) {
+            if (down && isWakeKey && isWakeKeyWhenScreenOff(keyCode)) {
                 if (keyguardActive) {
-                    // If the keyguard is showing, let it decide what to do with the wake key.
-                    mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode,
-                            mDockMode != Intent.EXTRA_DOCK_STATE_UNDOCKED);
+                    // If the keyguard is showing, let it wake the device when ready.
+                    mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode);
                 } else {
                     // Otherwise, wake the device ourselves.
                     result |= ACTION_WAKE_UP;
@@ -3614,6 +3612,40 @@
         return result;
     }
 
+    /**
+     * When the screen is off we ignore some keys that might otherwise typically
+     * be considered wake keys.  We filter them out here.
+     *
+     * {@link KeyEvent#KEYCODE_POWER} is notably absent from this list because it
+     * is always considered a wake key.
+     */
+    private boolean isWakeKeyWhenScreenOff(int keyCode) {
+        switch (keyCode) {
+            // ignore volume keys unless docked
+            case KeyEvent.KEYCODE_VOLUME_UP:
+            case KeyEvent.KEYCODE_VOLUME_DOWN:
+            case KeyEvent.KEYCODE_VOLUME_MUTE:
+                return mDockMode != Intent.EXTRA_DOCK_STATE_UNDOCKED;
+
+            // ignore media and camera keys
+            case KeyEvent.KEYCODE_MUTE:
+            case KeyEvent.KEYCODE_HEADSETHOOK:
+            case KeyEvent.KEYCODE_MEDIA_PLAY:
+            case KeyEvent.KEYCODE_MEDIA_PAUSE:
+            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+            case KeyEvent.KEYCODE_MEDIA_STOP:
+            case KeyEvent.KEYCODE_MEDIA_NEXT:
+            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+            case KeyEvent.KEYCODE_MEDIA_REWIND:
+            case KeyEvent.KEYCODE_MEDIA_RECORD:
+            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+            case KeyEvent.KEYCODE_CAMERA:
+                return false;
+        }
+        return true;
+    }
+
+
     /** {@inheritDoc} */
     @Override
     public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags) {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
index 3648d99..5e19271 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
@@ -175,7 +175,7 @@
      * Does not turn on screen, held while a call to {@link KeyguardViewManager#wakeWhenReadyTq(int)}
      * is called to make sure the device doesn't sleep before it has a chance to poke
      * the wake lock.
-     * @see #wakeWhenReadyLocked(int)
+     * @see #wakeWhenReady(int)
      */
     private PowerManager.WakeLock mWakeAndHandOff;
 
@@ -935,8 +935,8 @@
      * @see #handleWakeWhenReady
      * @see #onWakeKeyWhenKeyguardShowingTq(int)
      */
-    private void wakeWhenReadyLocked(int keyCode) {
-        if (DBG_WAKE) Log.d(TAG, "wakeWhenReadyLocked(" + keyCode + ")");
+    private void wakeWhenReady(int keyCode) {
+        if (DBG_WAKE) Log.d(TAG, "wakeWhenReady(" + keyCode + ")");
 
         /**
          * acquire the handoff lock that will keep the cpu running.  this will
@@ -1014,54 +1014,14 @@
      * action should be posted to a handler.
      *
      * @param keyCode The keycode of the key that woke the device
-     * @param isDocked True if the device is in the dock
-     * @return Whether we poked the wake lock (and turned the screen on)
      */
-    public boolean onWakeKeyWhenKeyguardShowingTq(int keyCode, boolean isDocked) {
+    public void onWakeKeyWhenKeyguardShowingTq(int keyCode) {
         if (DEBUG) Log.d(TAG, "onWakeKeyWhenKeyguardShowing(" + keyCode + ")");
 
-        if (isWakeKeyWhenKeyguardShowing(keyCode, isDocked)) {
-            // give the keyguard view manager a chance to adjust the state of the
-            // keyguard based on the key that woke the device before poking
-            // the wake lock
-            wakeWhenReadyLocked(keyCode);
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * When the keyguard is showing we ignore some keys that might otherwise typically
-     * be considered wake keys.  We filter them out here.
-     *
-     * {@link KeyEvent#KEYCODE_POWER} is notably absent from this list because it
-     * is always considered a wake key.
-     */
-    private boolean isWakeKeyWhenKeyguardShowing(int keyCode, boolean isDocked) {
-        switch (keyCode) {
-            // ignore volume keys unless docked
-            case KeyEvent.KEYCODE_VOLUME_UP:
-            case KeyEvent.KEYCODE_VOLUME_DOWN:
-            case KeyEvent.KEYCODE_VOLUME_MUTE:
-                return isDocked;
-
-            // ignore media and camera keys
-            case KeyEvent.KEYCODE_MUTE:
-            case KeyEvent.KEYCODE_HEADSETHOOK:
-            case KeyEvent.KEYCODE_MEDIA_PLAY:
-            case KeyEvent.KEYCODE_MEDIA_PAUSE:
-            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
-            case KeyEvent.KEYCODE_MEDIA_STOP:
-            case KeyEvent.KEYCODE_MEDIA_NEXT:
-            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
-            case KeyEvent.KEYCODE_MEDIA_REWIND:
-            case KeyEvent.KEYCODE_MEDIA_RECORD:
-            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
-            case KeyEvent.KEYCODE_CAMERA:
-                return false;
-        }
-        return true;
+        // give the keyguard view manager a chance to adjust the state of the
+        // keyguard based on the key that woke the device before poking
+        // the wake lock
+        wakeWhenReady(keyCode);
     }
 
     /**
@@ -1073,17 +1033,14 @@
      * The 'Tq' suffix is per the documentation in {@link WindowManagerPolicy}.
      * Be sure not to take any action that takes a long time; any significant
      * action should be posted to a handler.
-     *
-     * @return Whether we poked the wake lock (and turned the screen on)
      */
-    public boolean onWakeMotionWhenKeyguardShowingTq() {
+    public void onWakeMotionWhenKeyguardShowingTq() {
         if (DEBUG) Log.d(TAG, "onWakeMotionWhenKeyguardShowing()");
 
         // give the keyguard view manager a chance to adjust the state of the
         // keyguard based on the key that woke the device before poking
         // the wake lock
-        wakeWhenReadyLocked(KeyEvent.KEYCODE_UNKNOWN);
-        return true;
+        wakeWhenReady(KeyEvent.KEYCODE_UNKNOWN);
     }
 
     public void keyguardDone(boolean authenticated, boolean wakeup) {
@@ -1350,7 +1307,7 @@
     }
 
     /**
-     * Handle message sent by {@link #wakeWhenReadyLocked(int)}
+     * Handle message sent by {@link #wakeWhenReady(int)}
      * @param keyCode The key that woke the device.
      * @see #WAKE_WHEN_READY
      */
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
index 274e12b..9f0c5bd 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
@@ -180,6 +180,22 @@
         }
     }
 
+    private void updateWidgetFramesImportantForAccessibility() {
+        final int pageCount = getPageCount();
+        for (int i = 0; i < pageCount; i++) {
+            KeyguardWidgetFrame frame = getWidgetPageAt(i);
+            updateWidgetFrameImportantForAccessibility(frame);
+        }
+    }
+
+    private void updateWidgetFrameImportantForAccessibility(KeyguardWidgetFrame frame) {
+        if (frame.getContentAlpha() <= 0) {
+            frame.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+        } else {
+            frame.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+        }
+    }
+
     private void userActivity() {
         if (mCallbacks != null) {
             mCallbacks.onUserActivityTimeoutChanged();
@@ -312,6 +328,7 @@
                 content.getContentDescription());
             frame.setContentDescription(contentDescription);
         }
+        updateWidgetFrameImportantForAccessibility(frame);
     }
 
     /**
@@ -559,6 +576,12 @@
     }
 
     @Override
+    void setCurrentPage(int currentPage) {
+        super.setCurrentPage(currentPage);
+        updateWidgetFramesImportantForAccessibility();
+    }
+
+    @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
         mHasMeasure = false;
@@ -669,6 +692,7 @@
                     }
                     mWidgetToResetAfterFadeOut = -1;
                 }
+                updateWidgetFramesImportantForAccessibility();
             }
         });
         mChildrenOutlineFadeAnimation.start();
diff --git a/services/java/com/android/server/location/GeofenceManager.java b/services/java/com/android/server/location/GeofenceManager.java
index d04d2f3..f9be719 100644
--- a/services/java/com/android/server/location/GeofenceManager.java
+++ b/services/java/com/android/server/location/GeofenceManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 20012 The Android Open Source Project
+ * 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.
@@ -21,7 +21,6 @@
 import java.util.LinkedList;
 import java.util.List;
 
-import android.Manifest.permission;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
@@ -31,10 +30,11 @@
 import android.location.LocationManager;
 import android.location.LocationRequest;
 import android.os.Bundle;
-import android.os.Looper;
+import android.os.Handler;
+import android.os.Message;
 import android.os.PowerManager;
 import android.os.SystemClock;
-import android.util.Log;
+import android.util.Slog;
 
 import com.android.server.LocationManagerService;
 
@@ -42,6 +42,8 @@
     private static final String TAG = "GeofenceManager";
     private static final boolean D = LocationManagerService.D;
 
+    private static final int MSG_UPDATE_FENCES = 1;
+
     /**
      * Assume a maximum land speed, as a heuristic to throttle location updates.
      * (Air travel should result in an airplane mode toggle which will
@@ -49,37 +51,77 @@
      */
     private static final int MAX_SPEED_M_S = 100;  // 360 km/hr (high speed train)
 
+    /**
+     * Maximum age after which a location is no longer considered fresh enough to use.
+     */
+    private static final long MAX_AGE_NANOS = 5 * 60 * 1000000000L; // five minutes
+
+    /**
+     * Most frequent update interval allowed.
+     */
+    private static final long MIN_INTERVAL_MS = 1 * 60 * 1000; // one minute
+
+    /**
+     * Least frequent update interval allowed.
+     */
+    private static final long MAX_INTERVAL_MS = 2 * 60 * 60 * 1000; // two hours
+
     private final Context mContext;
     private final LocationManager mLocationManager;
     private final PowerManager.WakeLock mWakeLock;
-    private final Looper mLooper;  // looper thread to take location updates on
+    private final GeofenceHandler mHandler;
     private final LocationBlacklist mBlacklist;
 
     private Object mLock = new Object();
 
     // access to members below is synchronized on mLock
+    /**
+     * A list containing all registered geofences.
+     */
     private List<GeofenceState> mFences = new LinkedList<GeofenceState>();
 
+    /**
+     * This is set true when we have an active request for {@link Location} updates via
+     * {@link LocationManager#requestLocationUpdates(LocationRequest, LocationListener,
+     * android.os.Looper).
+     */
+    private boolean mReceivingLocationUpdates;
+
+    /**
+     * The update interval component of the current active {@link Location} update request.
+     */
+    private long mLocationUpdateInterval;
+
+    /**
+     * The {@link Location} most recently received via {@link #onLocationChanged(Location)}.
+     */
+    private Location mLastLocationUpdate;
+
+    /**
+     * This is set true when a {@link Location} is received via
+     * {@link #onLocationChanged(Location)} or {@link #scheduleUpdateFencesLocked()}, and cleared
+     * when that Location has been processed via {@link #updateFences()}
+     */
+    private boolean mPendingUpdate;
+
     public GeofenceManager(Context context, LocationBlacklist blacklist) {
         mContext = context;
         mLocationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
         PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-        mLooper = Looper.myLooper();
+        mHandler = new GeofenceHandler();
         mBlacklist = blacklist;
-
-        LocationRequest request = new LocationRequest()
-                .setQuality(LocationRequest.POWER_NONE)
-                .setFastestInterval(0);
-        mLocationManager.requestLocationUpdates(request, this, Looper.myLooper());
     }
 
-    public void addFence(LocationRequest request, Geofence geofence, PendingIntent intent, int uid,
-            String packageName) {
-        Location lastLocation = mLocationManager.getLastLocation();
-        GeofenceState state = new GeofenceState(geofence, lastLocation,
-                request.getExpireAt(), packageName, intent);
+    public void addFence(LocationRequest request, Geofence geofence, PendingIntent intent,
+            int uid, String packageName) {
+        if (D) {
+            Slog.d(TAG, "addFence: request=" + request + ", geofence=" + geofence
+                    + ", intent=" + intent + ", uid=" + uid + ", packageName=" + packageName);
+        }
 
+        GeofenceState state = new GeofenceState(geofence,
+                request.getExpireAt(), packageName, intent);
         synchronized (mLock) {
             // first make sure it doesn't already exist
             for (int i = mFences.size() - 1; i >= 0; i--) {
@@ -91,11 +133,15 @@
                 }
             }
             mFences.add(state);
-            updateProviderRequirementsLocked();
+            scheduleUpdateFencesLocked();
         }
     }
 
     public void removeFence(Geofence fence, PendingIntent intent) {
+        if (D) {
+            Slog.d(TAG, "removeFence: fence=" + fence + ", intent=" + intent);
+        }
+
         synchronized (mLock) {
             Iterator<GeofenceState> iter = mFences.iterator();
             while (iter.hasNext()) {
@@ -103,7 +149,7 @@
                 if (state.mIntent.equals(intent)) {
 
                     if (fence == null) {
-                        // alwaus remove
+                        // always remove
                         iter.remove();
                     } else {
                         // just remove matching fences
@@ -113,11 +159,15 @@
                     }
                 }
             }
-            updateProviderRequirementsLocked();
+            scheduleUpdateFencesLocked();
         }
     }
 
     public void removeFence(String packageName) {
+        if (D) {
+            Slog.d(TAG, "removeFence: packageName=" + packageName);
+        }
+
         synchronized (mLock) {
             Iterator<GeofenceState> iter = mFences.iterator();
             while (iter.hasNext()) {
@@ -126,7 +176,7 @@
                     iter.remove();
                 }
             }
-            updateProviderRequirementsLocked();
+            scheduleUpdateFencesLocked();
         }
     }
 
@@ -141,29 +191,133 @@
         }
     }
 
-    private void processLocation(Location location) {
+    private void scheduleUpdateFencesLocked() {
+        if (!mPendingUpdate) {
+            mPendingUpdate = true;
+            mHandler.sendEmptyMessage(MSG_UPDATE_FENCES);
+        }
+    }
+
+    /**
+     * Returns the location received most recently from {@link #onLocationChanged(Location)},
+     * or consult {@link LocationManager#getLastLocation()} if none has arrived. Does not return
+     * either if the location would be too stale to be useful.
+     *
+     * @return a fresh, valid Location, or null if none is available
+     */
+    private Location getFreshLocationLocked() {
+        // Prefer mLastLocationUpdate to LocationManager.getLastLocation().
+        Location location = mReceivingLocationUpdates ? mLastLocationUpdate : null;
+        if (location == null && !mFences.isEmpty()) {
+            location = mLocationManager.getLastLocation();
+        }
+
+        // Early out for null location.
+        if (location == null) {
+            return null;
+        }
+
+        // Early out for stale location.
+        long now = SystemClock.elapsedRealtimeNanos();
+        if (now - location.getElapsedRealtimeNanos() > MAX_AGE_NANOS) {
+            return null;
+        }
+
+        // Made it this far? Return our fresh, valid location.
+        return location;
+    }
+
+    /**
+     * The geofence update loop. This function removes expired fences, then tests the most
+     * recently-received {@link Location} against each registered {@link GeofenceState}, sending
+     * {@link Intent}s for geofences that have been tripped. It also adjusts the active location
+     * update request with {@link LocationManager} as appropriate for any active geofences.
+     */
+    // Runs on the handler.
+    private void updateFences() {
         List<PendingIntent> enterIntents = new LinkedList<PendingIntent>();
         List<PendingIntent> exitIntents = new LinkedList<PendingIntent>();
 
         synchronized (mLock) {
+            mPendingUpdate = false;
+
+            // Remove expired fences.
             removeExpiredFencesLocked();
 
+            // Get a location to work with, either received via onLocationChanged() or
+            // via LocationManager.getLastLocation().
+            Location location = getFreshLocationLocked();
+
+            // Update all fences.
+            // Keep track of the distance to the nearest fence.
+            double minFenceDistance = Double.MAX_VALUE;
+            boolean needUpdates = false;
             for (GeofenceState state : mFences) {
                 if (mBlacklist.isBlacklisted(state.mPackageName)) {
-                    if (D) Log.d(TAG, "skipping geofence processing for blacklisted app: " +
-                            state.mPackageName);
+                    if (D) {
+                        Slog.d(TAG, "skipping geofence processing for blacklisted app: "
+                                + state.mPackageName);
+                    }
                     continue;
                 }
 
-                int event = state.processLocation(location);
-                if ((event & GeofenceState.FLAG_ENTER) != 0) {
-                    enterIntents.add(state.mIntent);
-                }
-                if ((event & GeofenceState.FLAG_EXIT) != 0) {
-                    exitIntents.add(state.mIntent);
+                needUpdates = true;
+                if (location != null) {
+                    int event = state.processLocation(location);
+                    if ((event & GeofenceState.FLAG_ENTER) != 0) {
+                        enterIntents.add(state.mIntent);
+                    }
+                    if ((event & GeofenceState.FLAG_EXIT) != 0) {
+                        exitIntents.add(state.mIntent);
+                    }
+
+                    // FIXME: Ideally this code should take into account the accuracy of the
+                    // location fix that was used to calculate the distance in the first place.
+                    double fenceDistance = state.getDistanceToBoundary(); // MAX_VALUE if unknown
+                    if (fenceDistance < minFenceDistance) {
+                        minFenceDistance = fenceDistance;
+                    }
                 }
             }
-            updateProviderRequirementsLocked();
+
+            // Request or cancel location updates if needed.
+            if (needUpdates) {
+                // Request location updates.
+                // Compute a location update interval based on the distance to the nearest fence.
+                long intervalMs;
+                if (location != null && Double.compare(minFenceDistance, Double.MAX_VALUE) != 0) {
+                    intervalMs = (long)Math.min(MAX_INTERVAL_MS, Math.max(MIN_INTERVAL_MS,
+                            minFenceDistance * 1000 / MAX_SPEED_M_S));
+                } else {
+                    intervalMs = MIN_INTERVAL_MS;
+                }
+                if (!mReceivingLocationUpdates || mLocationUpdateInterval != intervalMs) {
+                    mReceivingLocationUpdates = true;
+                    mLocationUpdateInterval = intervalMs;
+                    mLastLocationUpdate = location;
+
+                    LocationRequest request = new LocationRequest();
+                    request.setInterval(intervalMs).setFastestInterval(0);
+                    mLocationManager.requestLocationUpdates(request, this, mHandler.getLooper());
+                }
+            } else {
+                // Cancel location updates.
+                if (mReceivingLocationUpdates) {
+                    mReceivingLocationUpdates = false;
+                    mLocationUpdateInterval = 0;
+                    mLastLocationUpdate = null;
+
+                    mLocationManager.removeUpdates(this);
+                }
+            }
+
+            if (D) {
+                Slog.d(TAG, "updateFences: location=" + location
+                        + ", mFences.size()=" + mFences.size()
+                        + ", mReceivingLocationUpdates=" + mReceivingLocationUpdates
+                        + ", mLocationUpdateInterval=" + mLocationUpdateInterval
+                        + ", mLastLocationUpdate=" + mLastLocationUpdate);
+            }
         }
 
         // release lock before sending intents
@@ -176,55 +330,54 @@
     }
 
     private void sendIntentEnter(PendingIntent pendingIntent) {
+        if (D) {
+            Slog.d(TAG, "sendIntentEnter: pendingIntent=" + pendingIntent);
+        }
+
         Intent intent = new Intent();
         intent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
         sendIntent(pendingIntent, intent);
     }
 
     private void sendIntentExit(PendingIntent pendingIntent) {
+        if (D) {
+            Slog.d(TAG, "sendIntentExit: pendingIntent=" + pendingIntent);
+        }
+
         Intent intent = new Intent();
         intent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
         sendIntent(pendingIntent, intent);
     }
 
     private void sendIntent(PendingIntent pendingIntent, Intent intent) {
+        mWakeLock.acquire();
         try {
-            mWakeLock.acquire();
-            pendingIntent.send(mContext, 0, intent, this, null, permission.ACCESS_FINE_LOCATION);
+            pendingIntent.send(mContext, 0, intent, this, null,
+                    android.Manifest.permission.ACCESS_FINE_LOCATION);
         } catch (PendingIntent.CanceledException e) {
             removeFence(null, pendingIntent);
             mWakeLock.release();
         }
+        // ...otherwise, mWakeLock.release() gets called by onSendFinished()
     }
 
-    private void updateProviderRequirementsLocked() {
-        double minDistance = Double.MAX_VALUE;
-        for (GeofenceState state : mFences) {
-            if (state.getDistance() < minDistance) {
-                minDistance = state.getDistance();
-            }
-        }
-
-        if (minDistance == Double.MAX_VALUE) {
-            disableLocationLocked();
-        } else {
-            int intervalMs = (int)(minDistance * 1000) / MAX_SPEED_M_S;
-            requestLocationLocked(intervalMs);
-        }
-    }
-
-    private void requestLocationLocked(int intervalMs) {
-        mLocationManager.requestLocationUpdates(new LocationRequest().setInterval(intervalMs), this,
-                mLooper);
-    }
-
-    private void disableLocationLocked() {
-        mLocationManager.removeUpdates(this);
-    }
-
+    // Runs on the handler (which was passed into LocationManager.requestLocationUpdates())
     @Override
     public void onLocationChanged(Location location) {
-        processLocation(location);
+        synchronized (mLock) {
+            if (mReceivingLocationUpdates) {
+                mLastLocationUpdate = location;
+            }
+
+            // Update the fences immediately before returning in
+            // case the caller is holding a wakelock.
+            if (mPendingUpdate) {
+                mHandler.removeMessages(MSG_UPDATE_FENCES);
+            } else {
+                mPendingUpdate = true;
+            }
+        }
+        updateFences();
     }
 
     @Override
@@ -253,4 +406,20 @@
             pw.append("\n");
         }
     }
+
+    private final class GeofenceHandler extends Handler {
+        public GeofenceHandler() {
+            super(true /*async*/);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_UPDATE_FENCES: {
+                    updateFences();
+                    break;
+                }
+            }
+        }
+    }
 }
diff --git a/services/java/com/android/server/location/GeofenceState.java b/services/java/com/android/server/location/GeofenceState.java
index 1fd737f..11705ff 100644
--- a/services/java/com/android/server/location/GeofenceState.java
+++ b/services/java/com/android/server/location/GeofenceState.java
@@ -39,11 +39,12 @@
     public final PendingIntent mIntent;
 
     int mState;  // current state
-    double mDistance;  // current distance to center of fence
+    double mDistanceToCenter;  // current distance to center of fence
 
-    public GeofenceState(Geofence fence, Location prevLocation, long expireAt,
+    public GeofenceState(Geofence fence, long expireAt,
             String packageName, PendingIntent intent) {
         mState = STATE_UNKNOWN;
+        mDistanceToCenter = Double.MAX_VALUE;
 
         mFence = fence;
         mExpireAt = expireAt;
@@ -53,10 +54,6 @@
         mLocation = new Location("");
         mLocation.setLatitude(fence.getLatitude());
         mLocation.setLongitude(fence.getLongitude());
-
-        if (prevLocation != null) {
-            processLocation(prevLocation);
-        }
     }
 
     /**
@@ -64,26 +61,35 @@
      * @return FLAG_ENTER or FLAG_EXIT if the fence was crossed, 0 otherwise
      */
     public int processLocation(Location location) {
-        mDistance = mLocation.distanceTo(location);
+        mDistanceToCenter = mLocation.distanceTo(location);
 
         int prevState = mState;
         //TODO: inside/outside detection could be made more rigorous
-        boolean inside = mDistance <= Math.max(mFence.getRadius(), location.getAccuracy());
+        boolean inside = mDistanceToCenter <= Math.max(mFence.getRadius(), location.getAccuracy());
         if (inside) {
             mState = STATE_INSIDE;
+            if (prevState != STATE_INSIDE) {
+                return FLAG_ENTER; // return enter if previously exited or unknown
+            }
         } else {
             mState = STATE_OUTSIDE;
-        }
-
-        if (prevState != 0 && mState != prevState) {
-            if (mState == STATE_INSIDE) return FLAG_ENTER;
-            if (mState == STATE_OUTSIDE) return FLAG_EXIT;
+            if (prevState == STATE_INSIDE) {
+                return FLAG_EXIT; // return exit only if previously entered
+            }
         }
         return 0;
     }
 
-    public double getDistance() {
-        return mDistance;
+    /**
+     * Gets the distance from the current location to the fence's boundary.
+     * @return The distance or {@link Double#MAX_VALUE} if unknown.
+     */
+    public double getDistanceToBoundary() {
+        if (Double.compare(mDistanceToCenter, Double.MAX_VALUE) == 0) {
+            return Double.MAX_VALUE;
+        } else {
+            return Math.abs(mFence.getRadius() - mDistanceToCenter);
+        }
     }
 
     @Override
@@ -99,6 +105,6 @@
             default:
                 state = "?";
         }
-        return String.format("%s d=%.0f %s", mFence.toString(), mDistance, state);
+        return String.format("%s d=%.0f %s", mFence.toString(), mDistanceToCenter, state);
     }
 }
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 4c5fc5d..5e25623 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -61,7 +61,7 @@
 
     /* Sends a kill signal to supplicant. To be used when we have lost connection
        or when the supplicant is hung */
-    public native static boolean killSupplicant();
+    public native static boolean killSupplicant(boolean p2pSupported);
 
     private native boolean connectToSupplicant(String iface);
 
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 040ff24..dafa8e8 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -1944,6 +1944,7 @@
                 case CMD_STOP_DRIVER:
                 case CMD_DELAYED_STOP_DRIVER:
                 case CMD_DRIVER_START_TIMED_OUT:
+                case CMD_CAPTIVE_CHECK_COMPLETE:
                 case CMD_START_AP:
                 case CMD_START_AP_SUCCESS:
                 case CMD_START_AP_FAILURE:
@@ -2189,6 +2190,13 @@
                         loge("Unable to change interface settings: " + ie);
                     }
 
+                    /* Stop a running supplicant after a runtime restart
+                     * Avoids issues with drivers that do not handle interface down
+                     * on a running supplicant properly.
+                     */
+                    if (DBG) log("Kill any running supplicant");
+                    mWifiNative.killSupplicant(mP2pSupported);
+
                     if(mWifiNative.startSupplicant(mP2pSupported)) {
                         if (DBG) log("Supplicant start successful");
                         mWifiMonitor.startMonitoring();
@@ -2384,7 +2392,7 @@
                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
                     if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
                         loge("Failed to setup control channel, restart supplicant");
-                        mWifiNative.killSupplicant();
+                        mWifiNative.killSupplicant(mP2pSupported);
                         transitionTo(mDriverLoadedState);
                         sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
                     } else {
@@ -2451,7 +2459,7 @@
                     break;
                 case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant connection lost */
                     loge("Connection lost, restart supplicant");
-                    mWifiNative.killSupplicant();
+                    mWifiNative.killSupplicant(mP2pSupported);
                     mWifiNative.closeSupplicantConnection();
                     mNetworkInfo.setIsAvailable(false);
                     handleNetworkDisconnect();
@@ -2605,14 +2613,14 @@
                     /* Socket connection can be lost when we do a graceful shutdown
                      * or when the driver is hung. Ensure supplicant is stopped here.
                      */
-                    mWifiNative.killSupplicant();
+                    mWifiNative.killSupplicant(mP2pSupported);
                     mWifiNative.closeSupplicantConnection();
                     transitionTo(mDriverLoadedState);
                     break;
                 case CMD_STOP_SUPPLICANT_FAILED:
                     if (message.arg1 == mSupplicantStopFailureToken) {
                         loge("Timed out on a supplicant stop, kill and proceed");
-                        mWifiNative.killSupplicant();
+                        mWifiNative.killSupplicant(mP2pSupported);
                         mWifiNative.closeSupplicantConnection();
                         transitionTo(mDriverLoadedState);
                     }