Merge "Add support for the new native Builder class on StaticLayout."
diff --git a/api/current.txt b/api/current.txt
index 612d16d..489fd05 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -24062,6 +24062,7 @@
     field public static final java.lang.String CACHED_NUMBER_LABEL = "numberlabel";
     field public static final java.lang.String CACHED_NUMBER_TYPE = "numbertype";
     field public static final java.lang.String CACHED_PHOTO_ID = "photo_id";
+    field public static final java.lang.String CACHED_PHOTO_URI = "photo_uri";
     field public static final android.net.Uri CONTENT_FILTER_URI;
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/calls";
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/calls";
diff --git a/api/system-current.txt b/api/system-current.txt
index ff9809f..7fa8d9b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -25667,6 +25667,7 @@
     field public static final java.lang.String CACHED_NUMBER_LABEL = "numberlabel";
     field public static final java.lang.String CACHED_NUMBER_TYPE = "numbertype";
     field public static final java.lang.String CACHED_PHOTO_ID = "photo_id";
+    field public static final java.lang.String CACHED_PHOTO_URI = "photo_uri";
     field public static final android.net.Uri CONTENT_FILTER_URI;
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/calls";
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/calls";
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4a3650f..e23ffe4 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3681,9 +3681,14 @@
     /**
      * Check whether the current user has been blocked by device policy from uninstalling a package.
      * Requires the caller to be the profile owner if checking a specific admin's policy.
+     * <p>
+     * <strong>Note:</strong> Starting from {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}, the
+     * behavior of this API is changed such that passing <code>null</code> as the <code>admin</code>
+     * parameter will return if any admin has blocked the uninstallation. Before L MR1, passing
+     * <code>null</code> will cause a NullPointerException to be raised.
      *
      * @param admin The name of the admin component whose blocking policy will be checked, or null
-     *        to check if any admin has blocked the uninstallation.
+     *            to check if any admin has blocked the uninstallation.
      * @param packageName package to check.
      * @return true if uninstallation is blocked.
      */
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 2df9dbf..6517f35 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -328,6 +328,14 @@
         public static final String CACHED_PHOTO_ID = "photo_id";
 
         /**
+         * The cached photo URI of the picture associated with the phone number, if it exists.
+         * This value may not be current if the contact information associated with this number
+         * has changed.
+         * <P>Type: TEXT (URI)</P>
+         */
+        public static final String CACHED_PHOTO_URI = "photo_uri";
+
+        /**
          * The cached phone number, formatted with formatting rules based on the country the
          * user was in when the call was made or received.
          * This value is not guaranteed to be present, and may not be current if the contact
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/DisplayListCanvas.java
similarity index 85%
rename from core/java/android/view/GLES20Canvas.java
rename to core/java/android/view/DisplayListCanvas.java
index 06e196d..90e1f86 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.annotation.NonNull;
 import android.graphics.Bitmap;
 import android.graphics.CanvasProperty;
 import android.graphics.NinePatch;
@@ -24,19 +25,50 @@
 import android.graphics.Picture;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.util.Pools.SynchronizedPool;
 
 /**
- * An implementation of Canvas on top of OpenGL ES 2.0.
+ * An implementation of a GL canvas that records drawing operations.
+ * This is intended for use with a DisplayList. This class keeps a list of all the Paint and
+ * Bitmap objects that it draws, preventing the backing memory of Bitmaps from being freed while
+ * the DisplayList is still holding a native reference to the memory.
  */
-class GLES20Canvas extends HardwareCanvas {
+class DisplayListCanvas extends HardwareCanvas {
+    // The recording canvas pool should be large enough to handle a deeply nested
+    // view hierarchy because display lists are generated recursively.
+    private static final int POOL_LIMIT = 25;
+
+    private static final SynchronizedPool<DisplayListCanvas> sPool =
+            new SynchronizedPool<DisplayListCanvas>(POOL_LIMIT);
+
+    RenderNode mNode;
     private int mWidth;
     private int mHeight;
 
-    private float[] mPoint;
-    private float[] mLine;
 
-    private Rect mClipBounds;
-    private RectF mPathBounds;
+    static DisplayListCanvas obtain(@NonNull RenderNode node) {
+        if (node == null) throw new IllegalArgumentException("node cannot be null");
+        DisplayListCanvas canvas = sPool.acquire();
+        if (canvas == null) {
+            canvas = new DisplayListCanvas();
+        }
+        canvas.mNode = node;
+        return canvas;
+    }
+
+    void recycle() {
+        mNode = null;
+        sPool.release(this);
+    }
+
+    long finishRecording() {
+        return nFinishRecording(mNativeCanvasWrapper);
+    }
+
+    @Override
+    public boolean isRecordingFor(Object o) {
+        return o == mNode;
+    }
 
     ///////////////////////////////////////////////////////////////////////////
     // JNI
@@ -53,8 +85,8 @@
     // Constructors
     ///////////////////////////////////////////////////////////////////////////
 
-    // TODO: Merge with GLES20RecordingCanvas
-    protected GLES20Canvas() {
+
+    private DisplayListCanvas() {
         super(nCreateDisplayListRenderer());
     }
 
diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java
deleted file mode 100644
index 6c780c9..0000000
--- a/core/java/android/view/GLES20RecordingCanvas.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.annotation.NonNull;
-import android.util.Pools.SynchronizedPool;
-
-/**
- * An implementation of a GL canvas that records drawing operations.
- * This is intended for use with a DisplayList. This class keeps a list of all the Paint and
- * Bitmap objects that it draws, preventing the backing memory of Bitmaps from being freed while
- * the DisplayList is still holding a native reference to the memory.
- */
-class GLES20RecordingCanvas extends GLES20Canvas {
-    // The recording canvas pool should be large enough to handle a deeply nested
-    // view hierarchy because display lists are generated recursively.
-    private static final int POOL_LIMIT = 25;
-
-    private static final SynchronizedPool<GLES20RecordingCanvas> sPool =
-            new SynchronizedPool<GLES20RecordingCanvas>(POOL_LIMIT);
-
-    RenderNode mNode;
-
-    private GLES20RecordingCanvas() {
-        super();
-    }
-
-    static GLES20RecordingCanvas obtain(@NonNull RenderNode node) {
-        if (node == null) throw new IllegalArgumentException("node cannot be null");
-        GLES20RecordingCanvas canvas = sPool.acquire();
-        if (canvas == null) {
-            canvas = new GLES20RecordingCanvas();
-        }
-        canvas.mNode = node;
-        return canvas;
-    }
-
-    void recycle() {
-        mNode = null;
-        sPool.release(this);
-    }
-
-    long finishRecording() {
-        return nFinishRecording(mNativeCanvasWrapper);
-    }
-
-    @Override
-    public boolean isRecordingFor(Object o) {
-        return o == mNode;
-    }
-}
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index cdb350f..fc2b55b 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -119,6 +119,6 @@
             CanvasProperty<Paint> paint);
 
     public static void setProperty(String name, String value) {
-        GLES20Canvas.setProperty(name, value);
+        DisplayListCanvas.setProperty(name, value);
     }
 }
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 9921be2..afa7f51 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -205,7 +205,7 @@
      *         false otherwise
      */
     public static boolean isAvailable() {
-        return GLES20Canvas.isAvailable();
+        return DisplayListCanvas.isAvailable();
     }
 
     /**
@@ -423,7 +423,7 @@
      */
     static HardwareRenderer create(Context context, boolean translucent) {
         HardwareRenderer renderer = null;
-        if (GLES20Canvas.isAvailable()) {
+        if (DisplayListCanvas.isAvailable()) {
             renderer = new ThreadedRenderer(context, translucent);
         }
         return renderer;
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index 09eb486..38867a8 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -225,7 +225,7 @@
      * @see #isValid()
      */
     public HardwareCanvas start(int width, int height) {
-        HardwareCanvas canvas = GLES20RecordingCanvas.obtain(this);
+        HardwareCanvas canvas = DisplayListCanvas.obtain(this);
         canvas.setViewport(width, height);
         // The dirty rect should always be null for a display list
         canvas.onPreDraw(null);
@@ -241,11 +241,11 @@
      * @see #isValid()
      */
     public void end(HardwareCanvas endCanvas) {
-        if (!(endCanvas instanceof GLES20RecordingCanvas)) {
+        if (!(endCanvas instanceof DisplayListCanvas)) {
             throw new IllegalArgumentException("Passed an invalid canvas to end!");
         }
 
-        GLES20RecordingCanvas canvas = (GLES20RecordingCanvas) endCanvas;
+        DisplayListCanvas canvas = (DisplayListCanvas) endCanvas;
         canvas.onPostDraw();
         long renderNodeData = canvas.finishRecording();
         nSetDisplayListData(mNativeRenderNode, renderNodeData);
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index 7b35a3b..379796d 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -283,10 +283,10 @@
     }
 
     public void setTarget(Canvas canvas) {
-        if (!(canvas instanceof GLES20RecordingCanvas)) {
+        if (!(canvas instanceof DisplayListCanvas)) {
             throw new IllegalArgumentException("Not a GLES20RecordingCanvas");
         }
-        final GLES20RecordingCanvas recordingCanvas = (GLES20RecordingCanvas) canvas;
+        final DisplayListCanvas recordingCanvas = (DisplayListCanvas) canvas;
         setTarget(recordingCanvas.mNode);
     }
 
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 0f99e88..cc44577 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -4887,9 +4887,10 @@
                     text.insert(newTextInsertAt, newText);
                 }
             }
-            // Restore the cursor position.
+            // Restore the cursor position. If there wasn't an old cursor (newCursorPos == -1) then
+            // don't explicitly set it and rely on SpannableStringBuilder to position it.
             // TODO: Select all the text that was undone.
-            if (newCursorPos <= text.length()) {
+            if (0 <= newCursorPos && newCursorPos <= text.length()) {
                 Selection.setSelection(text, newCursorPos);
             }
         }
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index a224f5e..d12739f 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -515,6 +515,23 @@
             }
         }
 
+        // Use the top-start-most laid out view as the baseline. RTL offsets are
+        // applied later, so we can use the left-most edge as the starting edge.
+        View baselineView = null;
+        LayoutParams baselineParams = null;
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+            if (child.getVisibility() != GONE) {
+                final LayoutParams childParams = (LayoutParams) child.getLayoutParams();
+                if (baselineView == null || baselineParams == null
+                        || compareLayoutPosition(childParams, baselineParams) < 0) {
+                    baselineView = child;
+                    baselineParams = childParams;
+                }
+            }
+        }
+        mBaselineView = baselineView;
+
         if (isWrapContentWidth) {
             // Width already has left padding in it since it was calculated by looking at
             // the right of each child view
@@ -616,25 +633,24 @@
             }
         }
 
-        // Use the bottom-most laid out view as the baseline.
-        View baselineView = null;
-        int baseline = 0;
-        for (int i = 0; i < count; i++) {
-            final View child = getChildAt(i);
-            if (child.getVisibility() != GONE) {
-                final int childBaseline = child.getBaseline();
-                if (childBaseline >= baseline) {
-                    baselineView = child;
-                    baseline = childBaseline;
-                }
-            }
-        }
-        mBaselineView = baselineView;
-
         setMeasuredDimension(width, height);
     }
 
     /**
+     * @return a negative number if the top of {@code p1} is above the top of
+     *         {@code p2} or if they have identical top values and the left of
+     *         {@code p1} is to the left of {@code p2}, or a positive number
+     *         otherwise
+     */
+    private int compareLayoutPosition(LayoutParams p1, LayoutParams p2) {
+        final int topDiff = p1.mTop - p2.mTop;
+        if (topDiff != 0) {
+            return topDiff;
+        }
+        return p1.mLeft - p2.mLeft;
+    }
+
+    /**
      * Measure a child. The child should have left, top, right and bottom information
      * stored in its LayoutParams. If any of these values is VALUE_NOT_SET it means
      * that the view can extend up to the corresponding edge.
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index a410e45..3ceea9d 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -342,6 +342,9 @@
                         return;
                     }
 
+                    // Do not show the profile switch message anymore.
+                    mProfileSwitchMessageId = -1;
+
                     final Intent intent = intentForDisplayResolveInfo(dri);
                     onIntentSelected(dri.ri, intent, false);
                     finish();
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 97744ea..30a7e68 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -18,6 +18,8 @@
 
 LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
 
+LOCAL_CFLAGS += -DU_USING_ICU_NAMESPACE=0
+
 LOCAL_SRC_FILES:= \
     AndroidRuntime.cpp \
     com_android_internal_content_NativeLibraryHelper.cpp \
@@ -41,25 +43,25 @@
     android_database_SQLiteDebug.cpp \
     android_emoji_EmojiFactory.cpp \
     android_view_DisplayEventReceiver.cpp \
-    android_view_Surface.cpp \
-    android_view_SurfaceControl.cpp \
-    android_view_SurfaceSession.cpp \
-    android_view_TextureView.cpp \
+    android_view_DisplayListCanvas.cpp \
+    android_view_GraphicBuffer.cpp \
+    android_view_HardwareLayer.cpp \
     android_view_InputChannel.cpp \
     android_view_InputDevice.cpp \
     android_view_InputEventReceiver.cpp \
     android_view_InputEventSender.cpp \
     android_view_InputQueue.cpp \
-    android_view_KeyEvent.cpp \
     android_view_KeyCharacterMap.cpp \
-    android_view_GraphicBuffer.cpp \
-    android_view_GLES20Canvas.cpp \
-    android_view_HardwareLayer.cpp \
-    android_view_ThreadedRenderer.cpp \
+    android_view_KeyEvent.cpp \
     android_view_MotionEvent.cpp \
     android_view_PointerIcon.cpp \
     android_view_RenderNode.cpp \
     android_view_RenderNodeAnimator.cpp \
+    android_view_Surface.cpp \
+    android_view_SurfaceControl.cpp \
+    android_view_SurfaceSession.cpp \
+    android_view_TextureView.cpp \
+    android_view_ThreadedRenderer.cpp \
     android_view_VelocityTracker.cpp \
     android_text_AndroidCharacter.cpp \
     android_text_AndroidBidi.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 77afd05..ad52e3f 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -127,16 +127,16 @@
 extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env);
 extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env);
 extern int register_android_view_DisplayEventReceiver(JNIEnv* env);
+extern int register_android_view_DisplayListCanvas(JNIEnv* env);
+extern int register_android_view_GraphicBuffer(JNIEnv* env);
+extern int register_android_view_HardwareLayer(JNIEnv* env);
 extern int register_android_view_RenderNode(JNIEnv* env);
 extern int register_android_view_RenderNodeAnimator(JNIEnv* env);
-extern int register_android_view_GraphicBuffer(JNIEnv* env);
-extern int register_android_view_GLES20Canvas(JNIEnv* env);
-extern int register_android_view_HardwareLayer(JNIEnv* env);
-extern int register_android_view_ThreadedRenderer(JNIEnv* env);
 extern int register_android_view_Surface(JNIEnv* env);
 extern int register_android_view_SurfaceControl(JNIEnv* env);
 extern int register_android_view_SurfaceSession(JNIEnv* env);
 extern int register_android_view_TextureView(JNIEnv* env);
+extern int register_android_view_ThreadedRenderer(JNIEnv* env);
 extern int register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper(JNIEnv *env);
 extern int register_android_database_CursorWindow(JNIEnv* env);
 extern int register_android_database_SQLiteConnection(JNIEnv* env);
@@ -1179,7 +1179,7 @@
     REG_JNI(register_android_view_RenderNode),
     REG_JNI(register_android_view_RenderNodeAnimator),
     REG_JNI(register_android_view_GraphicBuffer),
-    REG_JNI(register_android_view_GLES20Canvas),
+    REG_JNI(register_android_view_DisplayListCanvas),
     REG_JNI(register_android_view_HardwareLayer),
     REG_JNI(register_android_view_ThreadedRenderer),
     REG_JNI(register_android_view_Surface),
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index a6f19b1..e5ae147 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -48,10 +48,10 @@
             delete mBreakIterator;
         }
 
-        void setLocale(const Locale& locale) {
+        void setLocale(const icu::Locale& locale) {
             delete mBreakIterator;
             UErrorCode status = U_ZERO_ERROR;
-            mBreakIterator = BreakIterator::createLineInstance(locale, status);
+            mBreakIterator = icu::BreakIterator::createLineInstance(locale, status);
             // TODO: check status
         }
 
@@ -77,13 +77,13 @@
             }
         }
 
-        BreakIterator* breakIterator() const {
+        icu::BreakIterator* breakIterator() const {
             return mBreakIterator;
         }
 
     private:
         const size_t MAX_TEXT_BUF_RETAIN = 32678;
-        BreakIterator* mBreakIterator = nullptr;
+        icu::BreakIterator* mBreakIterator = nullptr;
         UText mUText = UTEXT_INITIALIZER;
         std::vector<uint16_t>mTextBuf;
 };
@@ -560,9 +560,9 @@
     // TODO: this array access is pretty inefficient, but we'll replace it anyway
     ScopedFloatArrayRO widthsScopedArr(env, widths);
 
-    BreakIterator* breakIterator = b->breakIterator();
+    icu::BreakIterator* breakIterator = b->breakIterator();
     int loc = breakIterator->first();
-    while ((loc = breakIterator->next()) != BreakIterator::DONE) {
+    while ((loc = breakIterator->next()) != icu::BreakIterator::DONE) {
         breaks.push_back(loc);
     }
 
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
similarity index 82%
rename from core/jni/android_view_GLES20Canvas.cpp
rename to core/jni/android_view_DisplayListCanvas.cpp
index 0bee7ae..f2e6c4b 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -50,43 +50,43 @@
 // Setup
 // ----------------------------------------------------------------------------
 
-static void android_view_GLES20Canvas_setViewport(JNIEnv* env, jobject clazz,
+static void android_view_DisplayListCanvas_setViewport(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jint width, jint height) {
     DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     renderer->setViewport(width, height);
 }
 
-static void android_view_GLES20Canvas_setHighContrastText(JNIEnv* env, jobject clazz,
+static void android_view_DisplayListCanvas_setHighContrastText(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jboolean highContrastText) {
     DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     renderer->setHighContrastText(highContrastText);
 }
 
-static void android_view_GLES20Canvas_insertReorderBarrier(JNIEnv* env, jobject clazz,
+static void android_view_DisplayListCanvas_insertReorderBarrier(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jboolean reorderEnable) {
     DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     renderer->insertReorderBarrier(reorderEnable);
 }
 
-static void android_view_GLES20Canvas_prepare(JNIEnv* env, jobject clazz,
+static void android_view_DisplayListCanvas_prepare(JNIEnv* env, jobject clazz,
         jlong rendererPtr) {
     DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     renderer->prepare();
 }
 
-static void android_view_GLES20Canvas_prepareDirty(JNIEnv* env, jobject clazz,
+static void android_view_DisplayListCanvas_prepareDirty(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jint left, jint top, jint right, jint bottom) {
     DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     renderer->prepareDirty(left, top, right, bottom);
 }
 
-static void android_view_GLES20Canvas_finish(JNIEnv* env, jobject clazz,
+static void android_view_DisplayListCanvas_finish(JNIEnv* env, jobject clazz,
         jlong rendererPtr) {
     DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     renderer->finish();
 }
 
-static void android_view_GLES20Canvas_setProperty(JNIEnv* env,
+static void android_view_DisplayListCanvas_setProperty(JNIEnv* env,
         jobject clazz, jstring name, jstring value) {
     if (!Caches::hasInstance()) {
         ALOGW("can't set property, no Caches instance");
@@ -108,7 +108,7 @@
 // Functor
 // ----------------------------------------------------------------------------
 
-static void android_view_GLES20Canvas_callDrawGLFunction(JNIEnv* env, jobject clazz,
+static void android_view_DisplayListCanvas_callDrawGLFunction(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jlong functorPtr) {
     DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     Functor* functor = reinterpret_cast<Functor*>(functorPtr);
@@ -120,11 +120,11 @@
 // Misc
 // ----------------------------------------------------------------------------
 
-static jint android_view_GLES20Canvas_getMaxTextureWidth(JNIEnv* env, jobject clazz) {
+static jint android_view_DisplayListCanvas_getMaxTextureWidth(JNIEnv* env, jobject clazz) {
     return Caches::getInstance().maxTextureSize;
 }
 
-static jint android_view_GLES20Canvas_getMaxTextureHeight(JNIEnv* env, jobject clazz) {
+static jint android_view_DisplayListCanvas_getMaxTextureHeight(JNIEnv* env, jobject clazz) {
     return Caches::getInstance().maxTextureSize;
 }
 
@@ -132,7 +132,7 @@
 // Drawing
 // ----------------------------------------------------------------------------
 
-static void android_view_GLES20Canvas_drawPatch(JNIEnv* env, jobject clazz,
+static void android_view_DisplayListCanvas_drawPatch(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jlong bitmapPtr, jlong patchPtr,
         float left, float top, float right, float bottom, jlong paintPtr) {
     SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);
@@ -143,7 +143,7 @@
     renderer->drawPatch(bitmap, patch, left, top, right, bottom, paint);
 }
 
-static void android_view_GLES20Canvas_drawRoundRectProps(JNIEnv* env, jobject clazz,
+static void android_view_DisplayListCanvas_drawRoundRectProps(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jlong leftPropPtr, jlong topPropPtr, jlong rightPropPtr,
         jlong bottomPropPtr, jlong rxPropPtr, jlong ryPropPtr, jlong paintPropPtr) {
     DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
@@ -157,7 +157,7 @@
     renderer->drawRoundRect(leftProp, topProp, rightProp, bottomProp, rxProp, ryProp, paintProp);
 }
 
-static void android_view_GLES20Canvas_drawCircleProps(JNIEnv* env, jobject clazz,
+static void android_view_DisplayListCanvas_drawCircleProps(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jlong xPropPtr, jlong yPropPtr, jlong radiusPropPtr, jlong paintPropPtr) {
     DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     CanvasPropertyPrimitive* xProp = reinterpret_cast<CanvasPropertyPrimitive*>(xPropPtr);
@@ -167,7 +167,7 @@
     renderer->drawCircle(xProp, yProp, radiusProp, paintProp);
 }
 
-static void android_view_GLES20Canvas_drawRegionAsRects(JNIEnv* env, jobject clazz,
+static void android_view_DisplayListCanvas_drawRegionAsRects(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jlong regionPtr, jlong paintPtr) {
     DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     SkRegion* region = reinterpret_cast<SkRegion*>(regionPtr);
@@ -201,17 +201,17 @@
 // Display lists
 // ----------------------------------------------------------------------------
 
-static jlong android_view_GLES20Canvas_finishRecording(JNIEnv* env,
+static jlong android_view_DisplayListCanvas_finishRecording(JNIEnv* env,
         jobject clazz, jlong rendererPtr) {
     DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     return reinterpret_cast<jlong>(renderer->finishRecording());
 }
 
-static jlong android_view_GLES20Canvas_createDisplayListRenderer(JNIEnv* env, jobject clazz) {
+static jlong android_view_DisplayListCanvas_createDisplayListRenderer(JNIEnv* env, jobject clazz) {
     return reinterpret_cast<jlong>(new DisplayListRenderer);
 }
 
-static void android_view_GLES20Canvas_drawRenderNode(JNIEnv* env,
+static void android_view_DisplayListCanvas_drawRenderNode(JNIEnv* env,
         jobject clazz, jlong rendererPtr, jlong renderNodePtr,
         jint flags) {
     DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
@@ -224,7 +224,7 @@
 // Layers
 // ----------------------------------------------------------------------------
 
-static void android_view_GLES20Canvas_drawLayer(JNIEnv* env, jobject clazz,
+static void android_view_DisplayListCanvas_drawLayer(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jlong layerPtr, jfloat x, jfloat y) {
     DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr);
@@ -235,7 +235,7 @@
 // Common
 // ----------------------------------------------------------------------------
 
-static jboolean android_view_GLES20Canvas_isAvailable(JNIEnv* env, jobject clazz) {
+static jboolean android_view_DisplayListCanvas_isAvailable(JNIEnv* env, jobject clazz) {
     char prop[PROPERTY_VALUE_MAX];
     if (property_get("ro.kernel.qemu", prop, NULL) == 0) {
         // not in the emulator
@@ -261,36 +261,36 @@
 // JNI Glue
 // ----------------------------------------------------------------------------
 
-const char* const kClassPathName = "android/view/GLES20Canvas";
+const char* const kClassPathName = "android/view/DisplayListCanvas";
 
 static JNINativeMethod gMethods[] = {
-    { "nIsAvailable",       "()Z",             (void*) android_view_GLES20Canvas_isAvailable },
-    { "nSetViewport",       "(JII)V",          (void*) android_view_GLES20Canvas_setViewport },
-    { "nSetHighContrastText","(JZ)V",          (void*) android_view_GLES20Canvas_setHighContrastText },
-    { "nInsertReorderBarrier","(JZ)V",         (void*) android_view_GLES20Canvas_insertReorderBarrier },
-    { "nPrepare",           "(J)V",            (void*) android_view_GLES20Canvas_prepare },
-    { "nPrepareDirty",      "(JIIII)V",        (void*) android_view_GLES20Canvas_prepareDirty },
-    { "nFinish",            "(J)V",            (void*) android_view_GLES20Canvas_finish },
+    { "nIsAvailable",       "()Z",             (void*) android_view_DisplayListCanvas_isAvailable },
+    { "nSetViewport",       "(JII)V",          (void*) android_view_DisplayListCanvas_setViewport },
+    { "nSetHighContrastText","(JZ)V",          (void*) android_view_DisplayListCanvas_setHighContrastText },
+    { "nInsertReorderBarrier","(JZ)V",         (void*) android_view_DisplayListCanvas_insertReorderBarrier },
+    { "nPrepare",           "(J)V",            (void*) android_view_DisplayListCanvas_prepare },
+    { "nPrepareDirty",      "(JIIII)V",        (void*) android_view_DisplayListCanvas_prepareDirty },
+    { "nFinish",            "(J)V",            (void*) android_view_DisplayListCanvas_finish },
     { "nSetProperty",       "(Ljava/lang/String;Ljava/lang/String;)V",
-            (void*) android_view_GLES20Canvas_setProperty },
+            (void*) android_view_DisplayListCanvas_setProperty },
 
-    { "nCallDrawGLFunction", "(JJ)V",          (void*) android_view_GLES20Canvas_callDrawGLFunction },
+    { "nCallDrawGLFunction", "(JJ)V",          (void*) android_view_DisplayListCanvas_callDrawGLFunction },
 
-    { "nDrawPatch",         "(JJJFFFFJ)V",     (void*) android_view_GLES20Canvas_drawPatch },
+    { "nDrawPatch",         "(JJJFFFFJ)V",     (void*) android_view_DisplayListCanvas_drawPatch },
 
-    { "nDrawRects",         "(JJJ)V",          (void*) android_view_GLES20Canvas_drawRegionAsRects },
-    { "nDrawRoundRect",     "(JJJJJJJJ)V",     (void*) android_view_GLES20Canvas_drawRoundRectProps },
-    { "nDrawCircle",        "(JJJJJ)V",        (void*) android_view_GLES20Canvas_drawCircleProps },
+    { "nDrawRects",         "(JJJ)V",          (void*) android_view_DisplayListCanvas_drawRegionAsRects },
+    { "nDrawRoundRect",     "(JJJJJJJJ)V",     (void*) android_view_DisplayListCanvas_drawRoundRectProps },
+    { "nDrawCircle",        "(JJJJJ)V",        (void*) android_view_DisplayListCanvas_drawCircleProps },
 
-    { "nFinishRecording",   "(J)J",            (void*) android_view_GLES20Canvas_finishRecording },
-    { "nDrawRenderNode",    "(JJI)V",          (void*) android_view_GLES20Canvas_drawRenderNode },
+    { "nFinishRecording",   "(J)J",            (void*) android_view_DisplayListCanvas_finishRecording },
+    { "nDrawRenderNode",    "(JJI)V",          (void*) android_view_DisplayListCanvas_drawRenderNode },
 
-    { "nCreateDisplayListRenderer", "()J",     (void*) android_view_GLES20Canvas_createDisplayListRenderer },
+    { "nCreateDisplayListRenderer", "()J",     (void*) android_view_DisplayListCanvas_createDisplayListRenderer },
 
-    { "nDrawLayer",               "(JJFF)V",   (void*) android_view_GLES20Canvas_drawLayer },
+    { "nDrawLayer",               "(JJFF)V",   (void*) android_view_DisplayListCanvas_drawLayer },
 
-    { "nGetMaximumTextureWidth",  "()I",       (void*) android_view_GLES20Canvas_getMaxTextureWidth },
-    { "nGetMaximumTextureHeight", "()I",       (void*) android_view_GLES20Canvas_getMaxTextureHeight },
+    { "nGetMaximumTextureWidth",  "()I",       (void*) android_view_DisplayListCanvas_getMaxTextureWidth },
+    { "nGetMaximumTextureHeight", "()I",       (void*) android_view_DisplayListCanvas_getMaxTextureHeight },
 };
 
 static JNINativeMethod gActivityThreadMethods[] = {
@@ -298,7 +298,7 @@
                                                (void*) android_app_ActivityThread_dumpGraphics }
 };
 
-int register_android_view_GLES20Canvas(JNIEnv* env) {
+int register_android_view_DisplayListCanvas(JNIEnv* env) {
     jclass clazz = FindClassOrDie(env, "android/graphics/Rect");
     gRectClassInfo.set = GetMethodIDOrDie(env, clazz, "set", "(IIII)V");
 
diff --git a/docs/html/images/training/geofence.png b/docs/html/images/training/geofence.png
new file mode 100644
index 0000000..2d5d3aa
--- /dev/null
+++ b/docs/html/images/training/geofence.png
Binary files differ
diff --git a/docs/html/images/training/geofence@2x.png b/docs/html/images/training/geofence@2x.png
new file mode 100644
index 0000000..2f83105
--- /dev/null
+++ b/docs/html/images/training/geofence@2x.png
Binary files differ
diff --git a/docs/html/training/location/geofencing.jd b/docs/html/training/location/geofencing.jd
index 748b6ec..59fc4c6 100644
--- a/docs/html/training/location/geofencing.jd
+++ b/docs/html/training/location/geofencing.jd
@@ -9,9 +9,11 @@
 
 <h2>This lesson teaches you to</h2>
 <ol>
-    <li><a href="#RequestGeofences">Request Geofence Monitoring</a></li>
+    <li><a href="#RequestGeofences">Set up for Geofence Monitoring</a></li>
+    <li><a href="#CreateAdd">Create and Add Geofences</a></li>
     <li><a href="#HandleGeofenceTransitions">Handle Geofence Transitions</a></li>
     <li><a href="#StopGeofenceMonitoring">Stop Geofence Monitoring</a></li>
+
 </ol>
 
 <h2>You should also read</h2>
@@ -23,577 +25,148 @@
 
 <h2>Try it out</h2>
 
-<div class="download-box">
-  <a href="http://developer.android.com/shareables/training/GeofenceDetection.zip" class="button">Download the sample</a>
-  <p class="filename">GeofenceDetection.zip</p>
-</div>
+   <ul>
+      <li>
+        <a href="https://github.com/googlesamples/android-play-location/tree/master/Geofencing"
+        class="external-link">Geofencing</a>
+      </li>
+    </ul>
 
 </div>
 </div>
 <p>
-    Geofencing combines awareness of the user's current location with awareness of nearby
-    features, defined as the user's proximity to locations that may be of interest. To mark a
+    Geofencing combines awareness of the user's current location with awareness of the user's
+    proximity to locations that may be of interest. To mark a
     location of interest, you specify its latitude and longitude. To adjust the proximity for the
-    location, you add a radius. The latitude, longitude, and radius define a geofence.
-    You can have multiple active geofences at one time.
+    location, you add a radius. The latitude, longitude, and radius define a geofence, creating a
+    circular area, or fence, around the location of interest.
 </p>
 <p>
-    Location Services treats a geofences as an area rather than as a points and proximity. This
-    allows it to detect when the user enters or exits a geofence. For each geofence, you can ask
-    Location Services to send you entrance events or exit events or both. You can also limit the
-    duration of a geofence by specifying an expiration duration in milliseconds. After the geofence
-    expires, Location Services automatically removes it.
+    You can have multiple active geofences, with a limit of 100 per device user. For each geofence,
+    you can ask Location Services to send you entrance and exit events, or you can specify a
+    duration within the geofence area to wait, or <em>dwell</em>, before triggering an event. You
+    can limit the duration of any geofence by specifying an expiration duration in milliseconds.
+    After the geofence expires, Location Services automatically removes it.
 </p>
-<!--
-    Send geofences to Location Services
- -->
-<h2 id="RequestGeofences">Request Geofence Monitoring</h2>
+
+<img src="{@docRoot}images/training/geofence@2x.png"
+srcset="{@docRoot}images/training/geofence.png 1x, {@docRoot}images/training/geofence@2x.png 2x" alt=""
+  width="400" height="400"/>
+<p>
+    This lesson shows you how to add and remove geofences, and then listen for geofence transitions
+    using an {@link android.app.IntentService}.</p>
+
+<h2 id="RequestGeofences">Set up for Geofence Monitoring</h2>
 <p>
     The first step in requesting geofence monitoring is to request the necessary permission.
     To use geofencing, your app must request
     {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION}. To request this
     permission, add the following element as a child element of the
 <code><a href="{@docRoot}guide/topics/manifest/manifest-element.html">&lt;manifest&gt;</a></code>
-    element:
+    element in your app manifest:
 </p>
 <pre>
 &lt;uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/&gt;
 </pre>
-<!-- Check for Google Play services -->
-<h3>Check for Google Play Services</h3>
-<p>
-    Location Services is part of the Google Play services APK. Since it's hard to anticipate the
-    state of the user's device, you should always check that the APK is installed before you attempt
-    to connect to Location Services. To check that the APK is installed, call
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#isGooglePlayServicesAvailable(android.content.Context)">GooglePlayServicesUtil.isGooglePlayServicesAvailable()</a></code>,
-    which returns one of the
-    integer result codes listed in the API reference documentation. If you encounter an error,
-    call
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#getErrorDialog(int, android.app.Activity, int)">GooglePlayServicesUtil.getErrorDialog()</a></code>
-    to retrieve localized dialog that prompts users to take the correct action, then display
-    the dialog in a {@link android.support.v4.app.DialogFragment}. The dialog may allow the
-    user to correct the problem, in which case Google Play services may send a result back to your
-    activity. To handle this result, override the method
-    {@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()}
 
-</p>
-<p class="note">
-    <strong>Note:</strong> To make your app compatible with
-    platform version 1.6 and later, the activity that displays the
-    {@link android.support.v4.app.DialogFragment} must subclass
-    {@link android.support.v4.app.FragmentActivity} instead of {@link android.app.Activity}. Using
-    {@link android.support.v4.app.FragmentActivity} also allows you to call
-    {@link android.support.v4.app.FragmentActivity#getSupportFragmentManager
-    getSupportFragmentManager()} to display the {@link android.support.v4.app.DialogFragment}.
-</p>
 <p>
-    Since you usually need to check for Google Play services in more than one place in your code,
-    define a method that encapsulates the check, then call the method before each connection
-    attempt. The following snippet contains all of the code required to check for Google
-    Play services:
+    If you want to use an {@link android.app.IntentService} to listen for geofence transitions,
+    add an element specifying the service name. This element must be
+    a child of the <code><a href="{@docRoot}guide/topics/manifest/application-element.html">
+    &lt;application&gt;</a></code> element:
+</p>
+
+<pre>
+&lt;application
+   android:allowBackup=&quot;true&quot;&gt;
+   ...
+   &lt;service android:name=".GeofenceTransitionsIntentService"/&gt;
+&lt;application/&gt;
+</pre>
+
+<p>To access the location APIs, you need to create an instance of the
+  Google Play services API client. To learn how to connect your client, see
+  <a href="{@docRoot}training/location/retrieve-current.html#play-services">Connect
+  to Google Play Services</a>.</p>
+
+<h2 id="CreateAdd">Create and Add Geofences</h2>
+
+<p>Your app needs to create and add geofences using the location API's builder class for
+ creating Geofence objects, and the convenience class for adding them. Also, to handle the
+ intents sent from Location Services when geofence transitions occur, you can define a
+ {@link android.app.PendingIntent} as shown in this section.
+</p>
+
+<h3>Create geofence objects</h3>
+
+<p>
+    First, use <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.Builder.
+    html">Geofence.Builder</a></code> to create a geofence, setting the desired radius, duration, and
+    transition types for the geofence. For example, to populate a list object named
+    {@code mGeofenceList}:
+    </p>
+
+<pre>
+mGeofenceList.add(new Geofence.Builder()
+    // Set the request ID of the geofence. This is a string to identify this
+    // geofence.
+    .setRequestId(entry.getKey())
+
+    .setCircularRegion(
+            entry.getValue().latitude,
+            entry.getValue().longitude,
+            Constants.GEOFENCE_RADIUS_IN_METERS
+    )
+    .setExpirationDuration(Constants.GEOFENCE_EXPIRATION_IN_MILLISECONDS)
+    .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
+            Geofence.GEOFENCE_TRANSITION_EXIT)
+    .build());
+</pre>
+
+<p>This example pulls data from a constants file. In actual practice, apps might
+    dynamically create geofences based on the user's location.</p>
+
+<h3>Specify geofences and initial triggers</h3>
+
+<p>
+    The following snippet uses the <code><a href="{@docRoot}reference/com/google/android/gms/location/GeofencingRequest.html">
+    GeofencingRequest</a></code> class
+    and its nested <code><a href="{@docRoot}reference/com/google/android/gms/location/GeofencingRequest.Builder.html">
+    GeofencingRequestBuilder</a></code> class to
+    specify the geofences to monitor and to set how related geofence events are triggered:
 </p>
 <pre>
-public class MainActivity extends FragmentActivity {
-    ...
-    // Global constants
-    /*
-     * Define a request code to send to Google Play services
-     * This code is returned in Activity.onActivityResult
-     */
-    private final static int
-            CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
-    ...
-    // Define a DialogFragment that displays the error dialog
-    public static class ErrorDialogFragment extends DialogFragment {
-        // Global field to contain the error dialog
-        private Dialog mDialog;
-        ...
-        // Default constructor. Sets the dialog field to null
-        public ErrorDialogFragment() {
-            super();
-            mDialog = null;
-        }
-        ...
-        // Set the dialog to display
-        public void setDialog(Dialog dialog) {
-            mDialog = dialog;
-        }
-        ...
-        // Return a Dialog to the DialogFragment.
-        &#64;Override
-        public Dialog onCreateDialog(Bundle savedInstanceState) {
-            return mDialog;
-        }
-        ...
-    }
-    ...
-    /*
-     * Handle results returned to the FragmentActivity
-     * by Google Play services
-     */
-     &#64;Override
-    protected void onActivityResult(
-            int requestCode, int resultCode, Intent data) {
-        // Decide what to do based on the original request code
-        switch (requestCode) {
-            ...
-            case CONNECTION_FAILURE_RESOLUTION_REQUEST :
-            /*
-             * If the result code is Activity.RESULT_OK, try
-             * to connect again
-             */
-                switch (resultCode) {
-                    ...
-                    case Activity.RESULT_OK :
-                    /*
-                     * Try the request again
-                     */
-                    ...
-                    break;
-                }
-            ...
-        }
-        ...
-    }
-    ...
-    private boolean servicesConnected() {
-        // Check that Google Play services is available
-        int resultCode =
-                GooglePlayServicesUtil.
-                        isGooglePlayServicesAvailable(this);
-        // If Google Play services is available
-        if (ConnectionResult.SUCCESS == resultCode) {
-            // In debug mode, log the status
-            Log.d("Geofence Detection",
-                    "Google Play services is available.");
-            // Continue
-            return true;
-        // Google Play services was not available for some reason
-        } else {
-            // Get the error code
-            int errorCode = connectionResult.getErrorCode();
-            // Get the error dialog from Google Play services
-            Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
-                    errorCode,
-                    this,
-                    CONNECTION_FAILURE_RESOLUTION_REQUEST);
-
-            // If Google Play services can provide an error dialog
-            if (errorDialog != null) {
-                // Create a new DialogFragment for the error dialog
-                ErrorDialogFragment errorFragment =
-                        new ErrorDialogFragment();
-                // Set the dialog in the DialogFragment
-                errorFragment.setDialog(errorDialog);
-                // Show the error dialog in the DialogFragment
-                errorFragment.show(
-                        getSupportFragmentManager(),
-                        "Geofence Detection");
-            }
-        }
-    }
-    ...
+private GeofencingRequest getGeofencingRequest() {
+    GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
+    builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
+    builder.addGeofences(mGeofenceList);
+    return builder.build();
 }
 </pre>
-<p>
-    Snippets in the following sections call this method to verify that Google Play services is
-    available.
-</p>
-<p>
-    To use geofencing, start by defining the geofences you want to monitor. Although you usually
-    store geofence data in a local database or download it from the network, you need to send
-    a geofence to Location Services as an instance of
-<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code>,
-    which you create with
-<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.Builder.html">Geofence.Builder</a></code>.
-    Each
-<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code>
-    object contains the following information:
-</p>
-<dl>
-    <dt>Latitude, longitude, and radius</dt>
-    <dd>
-        Define a circular area for the geofence. Use the latitude and longitude to mark a location
-        of interest, and then use the radius to adjust how close the user needs to approach the
-        location before the geofence is detected. The larger the radius, the more likely the
-        user will trigger a geofence transition alert by approaching the geofence. For example,
-        providing a large radius for a geofencing app that turns on lights in the user's house as
-        the user returns home might cause the lights to go on even if the user is simply passing by.
-    </dd>
-    <dt>Expiration time</dt>
-    <dd>
-        How long the geofence should remain active. Once the expiration time is reached, Location
-        Services deletes the geofence. Most of the time, you should specify an expiration time, but
-        you may want to keep permanent geofences for the user's home or place of work.
-    </dd>
-    <dt>Transition type</dt>
-    <dd>
-        Location Services can detect when the user steps within the radius of the geofence ("entry")
-        and when the user steps outside the radius of the geofence ("exit"), or both.
-    </dd>
-    <dt>Geofence ID</dt>
-    <dd>
-        A string that is stored with the geofence. You should make this unique, so that you can
-        use it to remove a geofence from Location Services tracking.
-    </dd>
-</dl>
-<h3>Define geofence storage</h3>
-<p>
-    A geofencing app needs to read and write geofence data to persistent storage. You shouldn't use
-<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code>
-    objects to do this; instead, use storage techniques such as databases that can store groups of
-    related data.
-</p>
-<p>
-    As an example of storing geofence data, the following snippet defines two classes that use
-    the app's {@link android.content.SharedPreferences} instance for persistent storage. The class
-    {@code SimpleGeofence}, analogous to a database record, stores the
-    data for a single
-<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code>
-    object in a "flattened" form. The class {@code SimpleGeofenceStore}, analogous to a database,
-    reads and writes {@code SimpleGeofence} data to the
-    {@link android.content.SharedPreferences} instance.
-</p>
-<pre>
-public class MainActivity extends FragmentActivity {
-    ...
-    /**
-     * A single Geofence object, defined by its center and radius.
-     */
-    public class SimpleGeofence {
-            // Instance variables
-            private final String mId;
-            private final double mLatitude;
-            private final double mLongitude;
-            private final float mRadius;
-            private long mExpirationDuration;
-            private int mTransitionType;
 
-        /**
-         * @param geofenceId The Geofence's request ID
-         * @param latitude Latitude of the Geofence's center.
-         * @param longitude Longitude of the Geofence's center.
-         * @param radius Radius of the geofence circle.
-         * @param expiration Geofence expiration duration
-         * @param transition Type of Geofence transition.
-         */
-        public SimpleGeofence(
-                String geofenceId,
-                double latitude,
-                double longitude,
-                float radius,
-                long expiration,
-                int transition) {
-            // Set the instance fields from the constructor
-            this.mId = geofenceId;
-            this.mLatitude = latitude;
-            this.mLongitude = longitude;
-            this.mRadius = radius;
-            this.mExpirationDuration = expiration;
-            this.mTransitionType = transition;
-        }
-        // Instance field getters
-        public String getId() {
-            return mId;
-        }
-        public double getLatitude() {
-            return mLatitude;
-        }
-        public double getLongitude() {
-            return mLongitude;
-        }
-        public float getRadius() {
-            return mRadius;
-        }
-        public long getExpirationDuration() {
-            return mExpirationDuration;
-        }
-        public int getTransitionType() {
-            return mTransitionType;
-        }
-        /**
-         * Creates a Location Services Geofence object from a
-         * SimpleGeofence.
-         *
-         * @return A Geofence object
-         */
-        public Geofence toGeofence() {
-            // Build a new Geofence object
-            return new Geofence.Builder()
-                    .setRequestId(getId())
-                    .setTransitionTypes(mTransitionType)
-                    .setCircularRegion(
-                            getLatitude(), getLongitude(), getRadius())
-                    .setExpirationDuration(mExpirationDuration)
-                    .build();
-        }
-    }
-    ...
-    /**
-     * Storage for geofence values, implemented in SharedPreferences.
-     */
-    public class SimpleGeofenceStore {
-        // Keys for flattened geofences stored in SharedPreferences
-        public static final String KEY_LATITUDE =
-                "com.example.android.geofence.KEY_LATITUDE";
-        public static final String KEY_LONGITUDE =
-                "com.example.android.geofence.KEY_LONGITUDE";
-        public static final String KEY_RADIUS =
-                "com.example.android.geofence.KEY_RADIUS";
-        public static final String KEY_EXPIRATION_DURATION =
-                "com.example.android.geofence.KEY_EXPIRATION_DURATION";
-        public static final String KEY_TRANSITION_TYPE =
-                "com.example.android.geofence.KEY_TRANSITION_TYPE";
-        // The prefix for flattened geofence keys
-        public static final String KEY_PREFIX =
-                "com.example.android.geofence.KEY";
-        /*
-         * Invalid values, used to test geofence storage when
-         * retrieving geofences
-         */
-        public static final long INVALID_LONG_VALUE = -999l;
-        public static final float INVALID_FLOAT_VALUE = -999.0f;
-        public static final int INVALID_INT_VALUE = -999;
-        // The SharedPreferences object in which geofences are stored
-        private final SharedPreferences mPrefs;
-        // The name of the SharedPreferences
-        private static final String SHARED_PREFERENCES =
-                "SharedPreferences";
-        // Create the SharedPreferences storage with private access only
-        public SimpleGeofenceStore(Context context) {
-            mPrefs =
-                    context.getSharedPreferences(
-                            SHARED_PREFERENCES,
-                            Context.MODE_PRIVATE);
-        }
-        /**
-         * Returns a stored geofence by its id, or returns {@code null}
-         * if it's not found.
-         *
-         * @param id The ID of a stored geofence
-         * @return A geofence defined by its center and radius. See
-         */
-        public SimpleGeofence getGeofence(String id) {
-            /*
-             * Get the latitude for the geofence identified by id, or
-             * INVALID_FLOAT_VALUE if it doesn't exist
-             */
-            double lat = mPrefs.getFloat(
-                    getGeofenceFieldKey(id, KEY_LATITUDE),
-                    INVALID_FLOAT_VALUE);
-            /*
-             * Get the longitude for the geofence identified by id, or
-             * INVALID_FLOAT_VALUE if it doesn't exist
-             */
-            double lng = mPrefs.getFloat(
-                    getGeofenceFieldKey(id, KEY_LONGITUDE),
-                    INVALID_FLOAT_VALUE);
-            /*
-             * Get the radius for the geofence identified by id, or
-             * INVALID_FLOAT_VALUE if it doesn't exist
-             */
-            float radius = mPrefs.getFloat(
-                    getGeofenceFieldKey(id, KEY_RADIUS),
-                    INVALID_FLOAT_VALUE);
-            /*
-             * Get the expiration duration for the geofence identified
-             * by id, or INVALID_LONG_VALUE if it doesn't exist
-             */
-            long expirationDuration = mPrefs.getLong(
-                    getGeofenceFieldKey(id, KEY_EXPIRATION_DURATION),
-                    INVALID_LONG_VALUE);
-            /*
-             * Get the transition type for the geofence identified by
-             * id, or INVALID_INT_VALUE if it doesn't exist
-             */
-            int transitionType = mPrefs.getInt(
-                    getGeofenceFieldKey(id, KEY_TRANSITION_TYPE),
-                    INVALID_INT_VALUE);
-            // If none of the values is incorrect, return the object
-            if (
-                lat != GeofenceUtils.INVALID_FLOAT_VALUE &amp;&amp;
-                lng != GeofenceUtils.INVALID_FLOAT_VALUE &amp;&amp;
-                radius != GeofenceUtils.INVALID_FLOAT_VALUE &amp;&amp;
-                expirationDuration !=
-                        GeofenceUtils.INVALID_LONG_VALUE &amp;&amp;
-                transitionType != GeofenceUtils.INVALID_INT_VALUE) {
-
-                // Return a true Geofence object
-                return new SimpleGeofence(
-                        id, lat, lng, radius, expirationDuration,
-                        transitionType);
-            // Otherwise, return null.
-            } else {
-                return null;
-            }
-        }
-        /**
-         * Save a geofence.
-         * @param geofence The SimpleGeofence containing the
-         * values you want to save in SharedPreferences
-         */
-        public void setGeofence(String id, SimpleGeofence geofence) {
-            /*
-             * Get a SharedPreferences editor instance. Among other
-             * things, SharedPreferences ensures that updates are atomic
-             * and non-concurrent
-             */
-            Editor editor = mPrefs.edit();
-            // Write the Geofence values to SharedPreferences
-            editor.putFloat(
-                    getGeofenceFieldKey(id, KEY_LATITUDE),
-                    (float) geofence.getLatitude());
-            editor.putFloat(
-                    getGeofenceFieldKey(id, KEY_LONGITUDE),
-                    (float) geofence.getLongitude());
-            editor.putFloat(
-                    getGeofenceFieldKey(id, KEY_RADIUS),
-                    geofence.getRadius());
-            editor.putLong(
-                    getGeofenceFieldKey(id, KEY_EXPIRATION_DURATION),
-                    geofence.getExpirationDuration());
-            editor.putInt(
-                    getGeofenceFieldKey(id, KEY_TRANSITION_TYPE),
-                    geofence.getTransitionType());
-            // Commit the changes
-            editor.commit();
-        }
-        public void clearGeofence(String id) {
-            /*
-             * Remove a flattened geofence object from storage by
-             * removing all of its keys
-             */
-            Editor editor = mPrefs.edit();
-            editor.remove(getGeofenceFieldKey(id, KEY_LATITUDE));
-            editor.remove(getGeofenceFieldKey(id, KEY_LONGITUDE));
-            editor.remove(getGeofenceFieldKey(id, KEY_RADIUS));
-            editor.remove(getGeofenceFieldKey(id,
-                    KEY_EXPIRATION_DURATION));
-            editor.remove(getGeofenceFieldKey(id, KEY_TRANSITION_TYPE));
-            editor.commit();
-        }
-        /**
-         * Given a Geofence object's ID and the name of a field
-         * (for example, KEY_LATITUDE), return the key name of the
-         * object's values in SharedPreferences.
-         *
-         * @param id The ID of a Geofence object
-         * @param fieldName The field represented by the key
-         * @return The full key name of a value in SharedPreferences
-         */
-        private String getGeofenceFieldKey(String id,
-                String fieldName) {
-            return KEY_PREFIX + "_" + id + "_" + fieldName;
-        }
-    }
-    ...
-}
-</pre>
-<h3>Create Geofence objects</h3>
 <p>
-    The following snippet uses the {@code SimpleGeofence} and {@code SimpleGeofenceStore} classes
-    gets geofence data from the UI, stores it in {@code SimpleGeofence} objects, stores these
-    objects in a {@code SimpleGeofenceStore} object, and then creates
-<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code>
-    objects:
+    This example shows the use of two geofence triggers. The <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_ENTER">
+    GEOFENCE_TRANSITION_ENTER</a></code>
+    transition triggers when a device enters a geofence, and the <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_EXIT">
+    GEOFENCE_TRANSITION_EXIT</a></code>
+    transition triggers when a device exits a geofence. Specifying
+    <code><a href="{@docRoot}reference/com/google/android/gms/location/GeofencingRequest.html#INITIAL_TRIGGER_ENTER">
+        INITIAL_TRIGGER_ENTER</a></code> tells Location services that
+    <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_ENTER">
+        GEOFENCE_TRANSITION_ENTER</a></code>
+    should be triggered if the the device is already inside the geofence.</p>
 </p>
-<pre>
-public class MainActivity extends FragmentActivity {
-    ...
-    /*
-     * Use to set an expiration time for a geofence. After this amount
-     * of time Location Services will stop tracking the geofence.
-     */
-    private static final long SECONDS_PER_HOUR = 60;
-    private static final long MILLISECONDS_PER_SECOND = 1000;
-    private static final long GEOFENCE_EXPIRATION_IN_HOURS = 12;
-    private static final long GEOFENCE_EXPIRATION_TIME =
-            GEOFENCE_EXPIRATION_IN_HOURS *
-            SECONDS_PER_HOUR *
-            MILLISECONDS_PER_SECOND;
-    ...
-    /*
-     * Handles to UI views containing geofence data
-     */
-    // Handle to geofence 1 latitude in the UI
-    private EditText mLatitude1;
-    // Handle to geofence 1 longitude in the UI
-    private EditText mLongitude1;
-    // Handle to geofence 1 radius in the UI
-    private EditText mRadius1;
-    // Handle to geofence 2 latitude in the UI
-    private EditText mLatitude2;
-    // Handle to geofence 2 longitude in the UI
-    private EditText mLongitude2;
-    // Handle to geofence 2 radius in the UI
-    private EditText mRadius2;
-    /*
-     * Internal geofence objects for geofence 1 and 2
-     */
-    private SimpleGeofence mUIGeofence1;
-    private SimpleGeofence mUIGeofence2;
-    ...
-    // Internal List of Geofence objects
-    List&lt;Geofence&gt; mGeofenceList;
-    // Persistent storage for geofences
-    private SimpleGeofenceStore mGeofenceStorage;
-    ...
-    &#64;Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        ...
-        // Instantiate a new geofence storage area
-        mGeofenceStorage = new SimpleGeofenceStore(this);
 
-        // Instantiate the current List of geofences
-        mCurrentGeofences = new ArrayList&lt;Geofence&gt;();
-    }
-    ...
-    /**
-     * Get the geofence parameters for each geofence from the UI
-     * and add them to a List.
-     */
-    public void createGeofences() {
-        /*
-         * Create an internal object to store the data. Set its
-         * ID to "1". This is a "flattened" object that contains
-         * a set of strings
-         */
-        mUIGeofence1 = new SimpleGeofence(
-                "1",
-                Double.valueOf(mLatitude1.getText().toString()),
-                Double.valueOf(mLongitude1.getText().toString()),
-                Float.valueOf(mRadius1.getText().toString()),
-                GEOFENCE_EXPIRATION_TIME,
-                // This geofence records only entry transitions
-                Geofence.GEOFENCE_TRANSITION_ENTER);
-        // Store this flat version
-        mGeofenceStorage.setGeofence("1", mUIGeofence1);
-        // Create another internal object. Set its ID to "2"
-        mUIGeofence2 = new SimpleGeofence(
-                "2",
-                Double.valueOf(mLatitude2.getText().toString()),
-                Double.valueOf(mLongitude2.getText().toString()),
-                Float.valueOf(mRadius2.getText().toString()),
-                GEOFENCE_EXPIRATION_TIME,
-                // This geofence records both entry and exit transitions
-                Geofence.GEOFENCE_TRANSITION_ENTER |
-                Geofence.GEOFENCE_TRANSITION_EXIT);
-        // Store this flat version
-        mGeofenceStorage.setGeofence(2, mUIGeofence2);
-        mGeofenceList.add(mUIGeofence1.toGeofence());
-        mGeofenceList.add(mUIGeofence2.toGeofence());
-    }
-    ...
-}
-</pre>
-<p>
-    In addition to the {@link java.util.List} of
-<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code>
-    objects you want to monitor, you need to provide Location Services with the
-    {@link android.content.Intent} that it sends to your app when it detects geofence
-    transitions.
-<h4>Define a Intent for geofence transitions</h4>
+<p>In many cases, it may be preferable to use instead <code><a href="{@docRoot}reference/com/google/android/gms/location/GeofencingRequest.html#INITIAL_TRIGGER_DWELL">
+    INITIAL_TRIGGER_DWELL</a></code>,
+    which triggers events only when the user stops for a defined duration within a geofence.
+    This approach can help reduce "alert spam" resulting from large numbers notifications when a
+    device briefly enters and exits geofences. Another strategy for getting best results from your
+    geofences is to set a minimum radius of 100 meters. This helps account for the location accuracy
+    of typical WiFi networks, and also helps reduce device power consumption.
+</p>
+
+<h3>Define an Intent for geofence transitions</h3>
 <p>
     The {@link android.content.Intent} sent from Location Services can trigger various actions in
     your app, but you should <i>not</i> have it start an activity or fragment, because components
@@ -601,807 +174,133 @@
     {@link android.app.IntentService} is a good way to handle the intent. An
     {@link android.app.IntentService} can post a notification, do long-running background work,
     send intents to other services, or send a broadcast intent. The following snippet shows how
-    how to define a {@link android.app.PendingIntent} that starts an
-    {@link android.app.IntentService}:
+    to define a {@link android.app.PendingIntent} that starts an {@link android.app.IntentService}:
 </p>
 <pre>
 public class MainActivity extends FragmentActivity {
     ...
-    /*
-     * Create a PendingIntent that triggers an IntentService in your
-     * app when a geofence transition occurs.
-     */
-    private PendingIntent getTransitionPendingIntent() {
-        // Create an explicit Intent
-        Intent intent = new Intent(this,
-                ReceiveTransitionsIntentService.class);
-        /*
-         * Return the PendingIntent
-         */
-        return PendingIntent.getService(
-                this,
-                0,
-                intent,
-                PendingIntent.FLAG_UPDATE_CURRENT);
-    }
-    ...
-}
-</pre>
-<p>
-    Now you have all the code you need to send a request to monitor geofences to Location
-    Services.
-</p>
-<!-- Send the monitoring request -->
-<h3 id="requestmonitoring">Send the monitoring request</h3>
-<p>
-    Sending the monitoring request requires two asynchronous operations. The first operation gets a
-    location client for the request, and the second makes the request using the client. In both
-    cases, Location Services invokes a callback method when it finishes the operation. The best way
-    to handle these operations is to chain together the method calls. The following snippets
-    demonstrate how to set up an activity, define the methods, and call them in the proper order.
-</p>
-<p>
-     First, modify the activity's class definition to implement the necessary callback interfaces.
-     Add the following interfaces:
-</p>
-<dl>
-    <dt>
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html">ConnectionCallbacks</a></code>
-    </dt>
-    <dd>
-        Specifies methods that Location Services calls when a location client is connected or
-        disconnected.
-    </dd>
-    <dt>
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html">OnConnectionFailedListener</a></code>
-    </dt>
-    <dd>
-        Specifies a method that Location Services calls if an error occurs while attempting to
-        connect the location client.
-    </dd>
-    <dt>
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnAddGeofencesResultListener.html">OnAddGeofencesResultListener</a></code>
-    </dt>
-    <dd>
-        Specifies a method that Location Services calls once it has added the geofences.
-    </dd>
-</dl>
-<p>
-    For example:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
-        ConnectionCallbacks,
-        OnConnectionFailedListener,
-        OnAddGeofencesResultListener {
-    ...
-}
-</pre>
-<h4>Start the request process</h4>
-<p>
-    Next, define a method that starts the request process by connecting to Location Services.
-    Mark this as a request to add a geofence by setting a global variable. This allows you to
-    use the callback
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">ConnectionCallbacks.onConnected()</a></code>
-    to add geofences and to remove them, as described in succeeding sections.
-</p>
-<p>
-<p>
-    To guard against race conditions that might arise if your app tries to start another request
-    before the first one finishes, define a boolean flag that tracks the state of the current
-    request:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
-        ConnectionCallbacks,
-        OnConnectionFailedListener,
-        OnAddGeofencesResultListener {
-    ...
-    // Holds the location client
-    private LocationClient mLocationClient;
-    // Stores the PendingIntent used to request geofence monitoring
-    private PendingIntent mGeofenceRequestIntent;
-    // Defines the allowable request types.
-    public enum REQUEST_TYPE = {ADD}
-    private REQUEST_TYPE mRequestType;
-    // Flag that indicates if a request is underway.
-    private boolean mInProgress;
-    ...
-    &#64;Override
-    protected void onCreate(Bundle savedInstanceState) {
-        ...
-        // Start with the request flag set to false
-        mInProgress = false;
-        ...
-    }
-    ...
-    /**
-     * Start a request for geofence monitoring by calling
-     * LocationClient.connect().
-     */
-    public void addGeofences() {
-        // Start a request to add geofences
-        mRequestType = ADD;
-        /*
-         * Test for Google Play services after setting the request type.
-         * If Google Play services isn't present, the proper request
-         * can be restarted.
-         */
-        if (!servicesConnected()) {
-            return;
+    private PendingIntent getGeofencePendingIntent() {
+        // Reuse the PendingIntent if we already have it.
+        if (mGeofencePendingIntent != null) {
+            return mGeofencePendingIntent;
         }
-        /*
-         * Create a new location client object. Since the current
-         * activity class implements ConnectionCallbacks and
-         * OnConnectionFailedListener, pass the current activity object
-         * as the listener for both parameters
-         */
-        mLocationClient = new LocationClient(this, this, this)
-        // If a request is not already underway
-        if (!mInProgress) {
-            // Indicate that a request is underway
-            mInProgress = true;
-            // Request a connection from the client to Location Services
-            mLocationClient.connect();
-        } else {
-            /*
-             * A request is already underway. You can handle
-             * this situation by disconnecting the client,
-             * re-setting the flag, and then re-trying the
-             * request.
-             */
-        }
+        Intent intent = new Intent(this, GeofenceTransitionsIntentService.class);
+        // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when
+        // calling addGeofences() and removeGeofences().
+        return PendingIntent.getService(this, 0, intent, PendingIntent.
+                FLAG_UPDATE_CURRENT);
     }
-    ...
-}
 </pre>
-<h4>Send a request to add the geofences</h4>
+
+<h3>Add geofences</h3>
+
 <p>
-    In your implementation of
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">ConnectionCallbacks.onConnected()</a></code>,
-    call
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#addGeofences(java.util.List<com.google.android.gms.location.Geofence>, android.app.PendingIntent, com.google.android.gms.location.LocationClient.OnAddGeofencesResultListener)">LocationClient.addGeofences()</a></code>.
-    Notice that if the connection fails,
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected()</a></code>
-    isn't called, and the request stops.
+    To add geofences, use the <code><a href="{@docRoot}reference/com/google/android/gms/location/GeofencingApi.html#addGeofences(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.location.GeofencingRequest, android.app.PendingIntent)">{@code GeoencingApi.addGeofences()}</a></code> method.
+    Provide the Google API client, the <code><a href="{@docRoot}reference/com/google/android/gms/location/GeofencingRequest">
+    GeofencingRequest</a></code> object, and the {@link android.app.PendingIntent}.
+    The following snippet, which processes the results in <code><a href="{@docRoot}reference/com/google/android/gms/common/api/ResultCallback.html#onResult(R)">
+    onResult()</a></code>, assumes that the main activity implements <code><a href="{@docRoot}reference/com/google/android/gms/common/api/ResultCallback.html">
+    ResultCallback</a></code>:
 </p>
 <pre>
-public class MainActivity extends FragmentActivity implements
-        ConnectionCallbacks,
-        OnConnectionFailedListener,
-        OnAddGeofencesResultListener {
+public class MainActivity extends FragmentActivity {
     ...
-    /*
-     * Provide the implementation of ConnectionCallbacks.onConnected()
-     * Once the connection is available, send a request to add the
-     * Geofences
-     */
-    &#64;Override
-    private void onConnected(Bundle dataBundle) {
-        ...
-        switch (mRequestType) {
-            case ADD :
-                // Get the PendingIntent for the request
-                mTransitionPendingIntent =
-                        getTransitionPendingIntent();
-                // Send a request to add the current geofences
-                mLocationClient.addGeofences(
-                        mCurrentGeofences, pendingIntent, this);
-            ...
-        }
-    }
-    ...
-}
+    LocationServices.GeofencingApi.addGeofences(
+                mGoogleApiClient,
+                getGeofencingRequest(),
+                getGeofencePendingIntent()
+        ).setResultCallback(this);
 </pre>
-<p>
-    Notice that
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#addGeofences(java.util.List<com.google.android.gms.location.Geofence>, android.app.PendingIntent, com.google.android.gms.location.LocationClient.OnAddGeofencesResultListener)">addGeofences()</a></code>
-    returns immediately, but the status of the request is indeterminate until Location Services
-    calls
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnAddGeofencesResultListener.html#onAddGeofencesResult(int, java.lang.String[])">onAddGeofencesResult()</a></code>
-    Once this method is called, you can determine if the request was successful or not.
-</p>
-<h4>Check the result returned by Location Services</h4>
-<p>
-    When Location Services invokes your implementation of the callback method
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnAddGeofencesResultListener.html#onAddGeofencesResult(int, java.lang.String[])">onAddGeofencesResult()</a></code>,
-    indicating that the request is complete, examine the incoming status code. If the request
-    was successful, the geofences you requested are active. If the request was unsuccessful,
-    the geofences aren't active, and you need to re-try the request or report an error. For example:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
-        ConnectionCallbacks,
-        OnConnectionFailedListener,
-        OnAddGeofencesResultListener {
-        ...
-    /*
-     * Provide the implementation of
-     * OnAddGeofencesResultListener.onAddGeofencesResult.
-     * Handle the result of adding the geofences
-     *
-     */
-    &#64;Override
-    public void onAddGeofencesResult(
-            int statusCode, String[] geofenceRequestIds) {
-        // If adding the geofences was successful
-        if (LocationStatusCodes.SUCCESS == statusCode) {
-            /*
-             * Handle successful addition of geofences here.
-             * You can send out a broadcast intent or update the UI.
-             * geofences into the Intent's extended data.
-             */
-        } else {
-        // If adding the geofences failed
-            /*
-             * Report errors here.
-             * You can log the error using Log.e() or update
-             * the UI.
-             */
-        }
-        // Turn off the in progress flag and disconnect the client
-        mInProgress = false;
-        mLocationClient.disconnect();
-    }
-    ...
-}
-</pre>
-<!-- Handle disconnections -->
-<h3>Handle disconnections</h3>
-<p>
-    In some cases, Location Services may disconnect from the activity recognition client before
-    you call
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#disconnect()">disconnect()</a></code>.
-    To handle this situation, implement <code>
-<a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onDisconnected()">onDisconnected()</a></code>.
-    In this method, set the request flag to indicate that a request is not in progress, and
-    delete the client:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
-        ConnectionCallbacks,
-        OnConnectionFailedListener,
-        OnAddGeofencesResultListener {
-    ...
-    /*
-     * Implement ConnectionCallbacks.onDisconnected()
-     * Called by Location Services once the location client is
-     * disconnected.
-     */
-    &#64;Override
-    public void onDisconnected() {
-        // Turn off the request flag
-        mInProgress = false;
-        // Destroy the current location client
-        mLocationClient = null;
-    }
-    ...
-}
-</pre>
-<!-- Handle connection errors -->
-<h3>Handle connection errors</h3>
-<p>
-    Besides handling the normal callbacks from Location Services, you have to provide a callback
-    method that Location Services calls if a connection error occurs. This callback method
-    can re-use the {@link android.support.v4.app.DialogFragment} class that you defined to
-    handle the check for Google Play services. It can also re-use the override you defined
-    for {@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()} that
-    receives any Google Play services results that occur when the user interacts with the
-    error dialog. The following snippet shows you a sample implementation of the callback method:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
-        ConnectionCallbacks,
-        OnConnectionFailedListener,
-        OnAddGeofencesResultListener {
-    ...
-    // Implementation of OnConnectionFailedListener.onConnectionFailed
-    &#64;Override
-    public void onConnectionFailed(ConnectionResult connectionResult) {
-        // Turn off the request flag
-        mInProgress = false;
-        /*
-         * If the error has a resolution, start a Google Play services
-         * activity to resolve it.
-         */
-        if (connectionResult.hasResolution()) {
-            try {
-                connectionResult.startResolutionForResult(
-                        this,
-                        CONNECTION_FAILURE_RESOLUTION_REQUEST);
-            } catch (SendIntentException e) {
-                // Log the error
-                e.printStackTrace();
-            }
-        // If no resolution is available, display an error dialog
-        } else {
-            // Get the error code
-            int errorCode = connectionResult.getErrorCode();
-            // Get the error dialog from Google Play services
-            Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
-                    errorCode,
-                    this,
-                    CONNECTION_FAILURE_RESOLUTION_REQUEST);
-            // If Google Play services can provide an error dialog
-            if (errorDialog != null) {
-                // Create a new DialogFragment for the error dialog
-                ErrorDialogFragment errorFragment =
-                        new ErrorDialogFragment();
-                // Set the dialog in the DialogFragment
-                errorFragment.setDialog(errorDialog);
-                // Show the error dialog in the DialogFragment
-                errorFragment.show(
-                        getSupportFragmentManager(),
-                        "Geofence Detection");
-            }
-        }
-    }
-    ...
-}
-</pre>
-<!--
-    Handle Geofence Transitions
- -->
+
+
 <h2 id="HandleGeofenceTransitions">Handle Geofence Transitions</h2>
 <p>
     When Location Services detects that the user has entered or exited a geofence, it
     sends out the {@link android.content.Intent} contained in the {@link android.app.PendingIntent}
-    you included in the request to add geofences. This {@link android.content.Intent} is
+    you included in the request to add geofences. This {@link android.content.Intent} is received
+    by a service like <code>GeofenceTransitionsIntentService</code>,
+    which obtains the geofencing event from the intent, determines the type of Geofence transition(s),
+    and determines which of the defined geofences was triggered. It then sends a notification as
+    the output.
 </p>
-<h3>Define an IntentService</h3>
 <p>
     The following snippet shows how to define an {@link android.app.IntentService} that posts a
     notification when a geofence transition occurs. When the user clicks the notification, the
     app's main activity appears:
 </p>
 <pre>
-public class ReceiveTransitionsIntentService extends IntentService {
-    ...
-    /**
-     * Sets an identifier for the service
-     */
-    public ReceiveTransitionsIntentService() {
-        super("ReceiveTransitionsIntentService");
-    }
-    /**
-     * Handles incoming intents
-     *&#64;param intent The Intent sent by Location Services. This
-     * Intent is provided
-     * to Location Services (inside a PendingIntent) when you call
-     * addGeofences()
-     */
-    &#64;Override
+public class GeofenceTransitionsIntentService extends IntentService {
+   ...
     protected void onHandleIntent(Intent intent) {
-        // First check for errors
-        if (LocationClient.hasError(intent)) {
-            // Get the error code with a static method
-            int errorCode = LocationClient.getErrorCode(intent);
-            // Log the error
-            Log.e("ReceiveTransitionsIntentService",
-                    "Location Services error: " +
-                    Integer.toString(errorCode));
-            /*
-             * You can also send the error code to an Activity or
-             * Fragment with a broadcast Intent
-             */
-        /*
-         * If there's no error, get the transition type and the IDs
-         * of the geofence or geofences that triggered the transition
-         */
-        } else {
-            // Get the type of transition (entry or exit)
-            int transitionType =
-                    LocationClient.getGeofenceTransition(intent);
-            // Test that a valid transition was reported
-            if (
-                (transitionType == Geofence.GEOFENCE_TRANSITION_ENTER)
-                 ||
-                (transitionType == Geofence.GEOFENCE_TRANSITION_EXIT)
-               ) {
-                List &lt;Geofence&gt; triggerList =
-                        getTriggeringGeofences(intent);
+        GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
+        if (geofencingEvent.hasError()) {
+            String errorMessage = GeofenceErrorMessages.getErrorString(this,
+                    geofencingEvent.getErrorCode());
+            Log.e(TAG, errorMessage);
+            return;
+        }
 
-                String[] triggerIds = new String[geofenceList.size()];
+        // Get the transition type.
+        int geofenceTransition = geofencingEvent.getGeofenceTransition();
 
-                for (int i = 0; i &lt; triggerIds.length; i++) {
-                    // Store the Id of each geofence
-                    triggerIds[i] = triggerList.get(i).getRequestId();
-                }
-                /*
-                 * At this point, you can store the IDs for further use
-                 * display them, or display the details associated with
-                 * them.
-                 */
-            }
-        // An invalid transition was reported
+        // Test that the reported transition was of interest.
+        if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
+                geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {
+
+            // Get the geofences that were triggered. A single event can trigger
+            // multiple geofences.
+            List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();
+
+            // Get the transition details as a String.
+            String geofenceTransitionDetails = getGeofenceTransitionDetails(
+                    this,
+                    geofenceTransition,
+                    triggeringGeofences
+            );
+
+            // Send notification and log the transition details.
+            sendNotification(geofenceTransitionDetails);
+            Log.i(TAG, geofenceTransitionDetails);
         } else {
-            Log.e("ReceiveTransitionsIntentService",
-                    "Geofence transition error: " +
-                    Integer.toString()transitionType));
+            // Log the error.
+            Log.e(TAG, getString(R.string.geofence_transition_invalid_type,
+                    geofenceTransition));
         }
     }
-    ...
-}
 </pre>
-<!-- Specify the IntentService in the manifest -->
-<h3>Specify the IntentService in the manifest</h3>
-<p>
-    To identify the {@link android.app.IntentService} to the system, add a
-    <code><a href="{@docRoot}guide/topics/manifest/service-element.html">&lt;service&gt;</a></code>
-    element to the app manifest. For example:
-</p>
-<pre>
-&lt;service
-    android:name="com.example.android.location.ReceiveTransitionsIntentService"
-    android:label="&#64;string/app_name"
-    android:exported="false"&gt;
-&lt;/service&gt;
-</pre>
-<p>
-    Notice that you don't have to specify intent filters for the service, because it only receives
-    explicit intents. How the incoming geofence transition intents are created is described in the
-    section <a href="#requestmonitoring">Send the monitoring request</a>.
-</p>
+
+<p>After detecting the transition event via the {@link android.app.PendingIntent},
+    this {@link android.app.IntentService} gets the geofence transition type and tests whether
+    it is one of the events the app uses to trigger notifications -- either
+    <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_ENTER">GEOFENCE_TRANSITION_ENTER</a></code>
+     or <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_EXIT">GEOFENCE_TRANSITION_EXIT</a></code>
+    in this case. The service then sends a notification and logs the transition details.</p>
 <!--
     Remove Geofences
  -->
 <h2 id="StopGeofenceMonitoring">Stop Geofence Monitoring</h2>
-<p>
-    To stop geofence monitoring, you remove the geofences themselves. You can remove a specific
-    set of geofences or all the geofences associated with a {@link android.app.PendingIntent}. The
-    procedure is similar to adding geofences. The first operation gets a location
-    client for the removal request, and the second makes the request using the client.
+
+<p>Stopping geofence monitoring when it is no longer needed or desired can help save battery
+    power and CPU cycles on the device. You can stop geofence monitoring
+    in the main activity used to add and remove geofences; removing a geofence stops it
+    immediately. The API provides methods to
+    remove geofences either by request IDs, or by removing geofences associated with a given
+    {@link android.app.PendingIntent}.
 </p>
 <p>
-    The callback methods that Location Services invokes when it has finished removing geofences
-    are defined in the interface
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnRemoveGeofencesResultListener.html">LocationClient.OnRemoveGeofencesResultListener</a></code>. Declare
-    this interface as part of your class definition, and then add definitions for its two methods:
-</p>
-<dl>
-    <dt>
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnRemoveGeofencesResultListener.html#onRemoveGeofencesByPendingIntentResult(int, android.app.PendingIntent)">onRemoveGeofencesByPendingIntentResult()</a></code>
-    </dt>
-    <dd>
-        Callback invoked when Location Services finishes a request to remove all geofences made
-        by the method
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#removeGeofences(android.app.PendingIntent, com.google.android.gms.location.LocationClient.OnRemoveGeofencesResultListener)">removeGeofences(PendingIntent, LocationClient.OnRemoveGeofencesResultListener)</a></code>.
-    </dd>
-    <dt>
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnRemoveGeofencesResultListener.html#onRemoveGeofencesByRequestIdsResult(int, java.lang.String[])">onRemoveGeofencesByRequestIdsResult(List&lt;String&gt;, LocationClient.OnRemoveGeofencesResultListener)</a></code>
-    </dt>
-    <dd>
-        Callback invoked when Location Services finished a request to remove a set of geofences,
-        specified by their geofence IDs, by the method
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#removeGeofences(java.util.List<java.lang.String>, com.google.android.gms.location.LocationClient.OnRemoveGeofencesResultListener)">removeGeofences(List&lt;String&gt;, LocationClient.OnRemoveGeofencesResultListener)</a></code>.
-    </dd>
-</dl>
-<p>
-    Examples of implementing these methods are shown in the next snippets.
-</p>
-<h3>Remove all geofences</h3>
-<p>
-    Since removing geofences uses some of the methods you use to add geofences, start by defining
-    another request type:
+    The following snippet removes geofences by {@link android.app.PendingIntent}, stopping all
+    further notification when the device enters or exits previously added geofences:
 </p>
 <pre>
-public class MainActivity extends FragmentActivity implements
-        ConnectionCallbacks,
-        OnConnectionFailedListener,
-        OnAddGeofencesResultListener {
-    ...
-    // Enum type for controlling the type of removal requested
-    public enum REQUEST_TYPE = {ADD, REMOVE_INTENT}
-    ...
+LocationServices.GeofencingApi.removeGeofences(
+            mGoogleApiClient,
+            // This is the same pending intent that was used in addGeofences().
+            getGeofencePendingIntent()
+    ).setResultCallback(this); // Result processed in onResult().
 }
 </pre>
+
 <p>
-    Start the removal request by getting a connection to Location Services. If the connection fails,
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected()</a></code> isn't called,
-    and the request stops. The following snippet shows how to start the request:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
-        ConnectionCallbacks,
-        OnConnectionFailedListener,
-        OnAddGeofencesResultListener {
-    ...
-    /**
-     * Start a request to remove geofences by calling
-     * LocationClient.connect()
-     */
-    public void removeGeofences(PendingIntent requestIntent) {
-        // Record the type of removal request
-        mRequestType = REMOVE_INTENT;
-        /*
-         * Test for Google Play services after setting the request type.
-         * If Google Play services isn't present, the request can be
-         * restarted.
-         */
-        if (!servicesConnected()) {
-            return;
-        }
-        // Store the PendingIntent
-        mGeofenceRequestIntent = requestIntent;
-        /*
-         * Create a new location client object. Since the current
-         * activity class implements ConnectionCallbacks and
-         * OnConnectionFailedListener, pass the current activity object
-         * as the listener for both parameters
-         */
-        mLocationClient = new LocationClient(this, this, this);
-        // If a request is not already underway
-        if (!mInProgress) {
-            // Indicate that a request is underway
-            mInProgress = true;
-            // Request a connection from the client to Location Services
-            mLocationClient.connect();
-        } else {
-            /*
-             * A request is already underway. You can handle
-             * this situation by disconnecting the client,
-             * re-setting the flag, and then re-trying the
-             * request.
-             */
-        }
-    }
-    ...
-}
-</pre>
-<p>
-   When Location Services invokes the callback method indicating that the connection is open,
-   make the request to remove all geofences. Disconnect the client after making the request.
-   For example:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
-        ConnectionCallbacks,
-        OnConnectionFailedListener,
-        OnAddGeofencesResultListener {
-    ...
-    /**
-     * Once the connection is available, send a request to remove the
-     * Geofences. The method signature used depends on which type of
-     * remove request was originally received.
-     */
-    private void onConnected(Bundle dataBundle) {
-        /*
-         * Choose what to do based on the request type set in
-         * removeGeofences
-         */
-        switch (mRequestType) {
-            ...
-            case REMOVE_INTENT :
-                mLocationClient.removeGeofences(
-                        mGeofenceRequestIntent, this);
-                break;
-            ...
-        }
-    }
-    ...
-}
-</pre>
-<p>
-    Although the call to
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#removeGeofences(android.app.PendingIntent, com.google.android.gms.location.LocationClient.OnRemoveGeofencesResultListener)">removeGeofences(PendingIntent, LocationClient.OnRemoveGeofencesResultListener)</a></code>    Services calls
-    returns immediately, the result of the removal request is indeterminate until Location Services
-    calls
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnRemoveGeofencesResultListener.html#onRemoveGeofencesByPendingIntentResult(int, android.app.PendingIntent)">onRemoveGeofencesByPendingIntentResult()</a></code>.
-    The following snippet shows how to define this method:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
-        ConnectionCallbacks,
-        OnConnectionFailedListener,
-        OnAddGeofencesResultListener {
-    ...
-    /**
-     * When the request to remove geofences by PendingIntent returns,
-     * handle the result.
-     *
-     *&#64;param statusCode the code returned by Location Services
-     *&#64;param requestIntent The Intent used to request the removal.
-     */
-    &#64;Override
-    public void onRemoveGeofencesByPendingIntentResult(int statusCode,
-            PendingIntent requestIntent) {
-        // If removing the geofences was successful
-        if (statusCode == LocationStatusCodes.SUCCESS) {
-            /*
-             * Handle successful removal of geofences here.
-             * You can send out a broadcast intent or update the UI.
-             * geofences into the Intent's extended data.
-             */
-        } else {
-        // If adding the geocodes failed
-            /*
-             * Report errors here.
-             * You can log the error using Log.e() or update
-             * the UI.
-             */
-        }
-        /*
-         * Disconnect the location client regardless of the
-         * request status, and indicate that a request is no
-         * longer in progress
-         */
-        mInProgress = false;
-        mLocationClient.disconnect();
-    }
-    ...
-}
-</pre>
-<h3>Remove individual geofences</h3>
-<p>
-    The procedure for removing an individual geofence or set of geofences is similar to the
-    removal of all geofences. To specify the geofences you want remove, add their geofence ID
-    values to a {@link java.util.List} of String objects. Pass this {@link java.util.List} to a
-    different definition of {@code removeGeofences} with the appropriate signature. This method
-    then starts the removal process.
-</p>
-<p>
-    Start by adding a request type for removing geofences by a list, and also add a global variable
-    for storing the list of geofences:
-</p>
-<pre>
-    ...
-    // Enum type for controlling the type of removal requested
-    public enum REQUEST_TYPE = {ADD, REMOVE_INTENT, REMOVE_LIST}
-    // Store the list of geofence Ids to remove
-    String&lt;List&gt; mGeofencesToRemove;
-</pre>
-<p>
-    Next, define a list of geofences you want to remove. For example, this snippet removes the
-<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code>
-    defined by the geofence ID "1":
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
-        ConnectionCallbacks,
-        OnConnectionFailedListener,
-        OnAddGeofencesResultListener {
-    ...
-        List&lt;String&gt; listOfGeofences =
-                Collections.singletonList("1");
-        removeGeofences(listOfGeofences);
-    ...
-}
-</pre>
-<p>
-    The following snippet defines the {@code removeGeofences()} method:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
-        ConnectionCallbacks,
-        OnConnectionFailedListener,
-        OnAddGeofencesResultListener {
-    ...
-    /**
-     * Start a request to remove monitoring by
-     * calling LocationClient.connect()
-     *
-     */
-    public void removeGeofences(List&lt;String&gt; geofenceIds) {
-        // If Google Play services is unavailable, exit
-        // Record the type of removal request
-        mRequestType = REMOVE_LIST;
-        /*
-         * Test for Google Play services after setting the request type.
-         * If Google Play services isn't present, the request can be
-         * restarted.
-         */
-        if (!servicesConnected()) {
-            return;
-        }
-        // Store the list of geofences to remove
-        mGeofencesToRemove = geofenceIds;
-        /*
-         * Create a new location client object. Since the current
-         * activity class implements ConnectionCallbacks and
-         * OnConnectionFailedListener, pass the current activity object
-         * as the listener for both parameters
-         */
-        mLocationClient = new LocationClient(this, this, this);
-        // If a request is not already underway
-        if (!mInProgress) {
-            // Indicate that a request is underway
-            mInProgress = true;
-            // Request a connection from the client to Location Services
-            mLocationClient.connect();
-        } else {
-            /*
-             * A request is already underway. You can handle
-             * this situation by disconnecting the client,
-             * re-setting the flag, and then re-trying the
-             * request.
-             */
-        }
-    }
-    ...
-}
-</pre>
-<p>
-   When Location Services invokes the callback method indicating that the connection is open,
-   make the request to remove the list of geofences. Disconnect the client after making the request.
-   For example:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
-        ConnectionCallbacks,
-        OnConnectionFailedListener,
-        OnAddGeofencesResultListener {
-    ...
-    private void onConnected(Bundle dataBundle) {
-        ...
-        switch (mRequestType) {
-        ...
-        // If removeGeofencesById was called
-            case REMOVE_LIST :
-                mLocationClient.removeGeofences(
-                        mGeofencesToRemove, this);
-                break;
-        ...
-        }
-        ...
-    }
-    ...
-}
-</pre>
-<p>
-    Define an implementation of
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnRemoveGeofencesResultListener.html#onRemoveGeofencesByRequestIdsResult(int, java.lang.String[])">onRemoveGeofencesByRequestIdsResult()</a></code>.
-    Location Services invokes this callback method to indicate that the request to remove a list of
-    geofences is complete. In this method, examine the incoming status code and take the
-    appropriate action:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
-        ConnectionCallbacks,
-        OnConnectionFailedListener,
-        OnAddGeofencesResultListener {
-    ...
-    /**
-     * When the request to remove geofences by IDs returns, handle the
-     * result.
-     *
-     * &#64;param statusCode The code returned by Location Services
-     * &#64;param geofenceRequestIds The IDs removed
-     */
-    &#64;Override
-    public void onRemoveGeofencesByRequestIdsResult(
-            int statusCode, String[] geofenceRequestIds) {
-        // If removing the geocodes was successful
-        if (LocationStatusCodes.SUCCESS == statusCode) {
-            /*
-             * Handle successful removal of geofences here.
-             * You can send out a broadcast intent or update the UI.
-             * geofences into the Intent's extended data.
-             */
-        } else {
-        // If removing the geofences failed
-            /*
-             * Report errors here.
-             * You can log the error using Log.e() or update
-             * the UI.
-             */
-        }
-        // Indicate that a request is no longer in progress
-        mInProgress = false;
-        // Disconnect the location client
-        mLocationClient.disconnect();
-    }
-    ...
-}
-</pre>
-<p>
-    You can combine geofencing with other location-aware features, such as periodic location updates
-    or activity recognition, which are described in other lessons in this class.
-</p>
-<p>
-    The next lesson,
-    <a href="activity-recognition.html">Recognizing the User's Current Activity</a>, shows you how
-    to request and receive activity updates. At regular intervals, Location Services can send you
-    information about the user's current physical activity. Based on this information, you can
-    change your app's behavior; for example, you can switch to a longer update interval if you
-    detect that the user is walking instead of driving.
+    You can combine geofencing with other location-aware features, such as periodic location updates.
+    For more information, see the other lessons in this class.
 </p>
diff --git a/docs/html/training/location/index.jd b/docs/html/training/location/index.jd
index 35e177f..c4dec99 100644
--- a/docs/html/training/location/index.jd
+++ b/docs/html/training/location/index.jd
@@ -81,4 +81,10 @@
     Learn how to convert a location's latitude and longitude into an address
     (reverse geocoding).
   </dd>
+  <dt>
+    <b><a href="geofencing.html">Creating and Monitoring Geofences</a></b>
+  </dt> <dd>
+    Learn how to define one or more geographic areas as locations of interest,
+    called geofences, and detect when the user is close to or inside a geofence.
+  </dd>
 </dl>
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 89e72f1..11ae1a6 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -736,6 +736,10 @@
             Displaying a Location Address
           </a>
           </li>
+          <li><a href="<?cs var:toroot ?>training/location/geofencing.html">
+            Creating and Monitoring Geofences
+          </a>
+          </li>
         </ul>
       </li>
     </ul>
diff --git a/docs/html/wear/images/partners/acer.png b/docs/html/wear/images/partners/acer.png
new file mode 100644
index 0000000..439de80
--- /dev/null
+++ b/docs/html/wear/images/partners/acer.png
Binary files differ
diff --git a/docs/html/wear/images/partners/arm.png b/docs/html/wear/images/partners/arm.png
new file mode 100644
index 0000000..3e2f642
--- /dev/null
+++ b/docs/html/wear/images/partners/arm.png
Binary files differ
diff --git a/docs/html/wear/images/partners/huawei.png b/docs/html/wear/images/partners/huawei.png
new file mode 100644
index 0000000..9099ed4
--- /dev/null
+++ b/docs/html/wear/images/partners/huawei.png
Binary files differ
diff --git a/docs/html/wear/index.jd b/docs/html/wear/index.jd
index 27e8098..316f5ca 100644
--- a/docs/html/wear/index.jd
+++ b/docs/html/wear/index.jd
@@ -202,6 +202,12 @@
 
           <div class="landing-partners cols">
             <div class="col-4">
+              <img src="/wear/images/partners/acer.png" alt="Acer">
+            </div>
+            <div class="col-4">
+              <img src="/wear/images/partners/arm.png" alt="ARM">
+            </div>
+            <div class="col-4">
               <img src="/wear/images/partners/asus.png" alt="Asus">
             </div>
             <div class="col-4">
@@ -214,6 +220,9 @@
               <img src="/wear/images/partners/htc.png" alt="HTC">
             </div>
             <div class="col-4">
+              <img src="/wear/images/partners/huawei.png" alt="Huawei">
+            </div>
+            <div class="col-4">
               <img src="/wear/images/partners/intel.png" alt="Intel">
             </div>
             <div class="col-4">
diff --git a/graphics/java/android/graphics/drawable/RippleComponent.java b/graphics/java/android/graphics/drawable/RippleComponent.java
index fd3e06c..79407f7 100644
--- a/graphics/java/android/graphics/drawable/RippleComponent.java
+++ b/graphics/java/android/graphics/drawable/RippleComponent.java
@@ -69,7 +69,6 @@
 
         mDensity = density;
 
-        onSetup();
         onTargetRadiusChanged(mTargetRadius);
     }
 
@@ -82,7 +81,10 @@
         cancel();
 
         mSoftwareAnimator = createSoftwareEnter(fast);
-        mSoftwareAnimator.start();
+
+        if (mSoftwareAnimator != null) {
+            mSoftwareAnimator.start();
+        }
     }
 
     /**
@@ -250,14 +252,6 @@
         // Stub.
     }
 
-    /**
-     * Called during ripple setup, which occurs before the first enter
-     * animation.
-     */
-    protected void onSetup() {
-        // Stub.
-    }
-
     protected abstract Animator createSoftwareEnter(boolean fast);
 
     protected abstract Animator createSoftwareExit();
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index bc8c7d2..66160c0 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -564,7 +564,9 @@
                 x = mHotspotBounds.exactCenterX();
                 y = mHotspotBounds.exactCenterY();
             }
-            mRipple = new RippleForeground(this, mHotspotBounds, x, y);
+
+            final boolean isBounded = !isProjected();
+            mRipple = new RippleForeground(this, mHotspotBounds, x, y, isBounded);
         }
 
         mRipple.setup(mState.mMaxRadius, mDensity);
diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java
index 2023f04..334122d 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -44,9 +44,16 @@
     private static final float WAVE_TOUCH_UP_ACCELERATION = 3400;
     private static final float WAVE_OPACITY_DECAY_VELOCITY = 3;
 
+    // Bounded ripple animation properties.
+    private static final int BOUNDED_ORIGIN_EXIT_DURATION = 300;
+    private static final int BOUNDED_RADIUS_EXIT_DURATION = 800;
+    private static final int BOUNDED_OPACITY_EXIT_DURATION = 400;
+    private static final float MAX_BOUNDED_RADIUS = 350;
+
     private static final int RIPPLE_ENTER_DELAY = 80;
     private static final int OPACITY_ENTER_DURATION_FAST = 120;
 
+    // Parent-relative values for starting position.
     private float mStartingX;
     private float mStartingY;
     private float mClampedStartingX;
@@ -58,30 +65,41 @@
     private CanvasProperty<Float> mPropX;
     private CanvasProperty<Float> mPropY;
 
+    // Target values for tween animations.
+    private float mTargetX = 0;
+    private float mTargetY = 0;
+
+    /** Ripple target radius used when bounded. Not used for clamping. */
+    private float mBoundedRadius = 0;
+
     // Software rendering properties.
     private float mOpacity = 1;
-    private float mOuterX;
-    private float mOuterY;
 
     // Values used to tween between the start and end positions.
     private float mTweenRadius = 0;
     private float mTweenX = 0;
     private float mTweenY = 0;
 
+    /** Whether this ripple is bounded. */
+    private boolean mIsBounded;
+
     /** Whether this ripple has finished its exit animation. */
     private boolean mHasFinishedExit;
 
-    public RippleForeground(RippleDrawable owner, Rect bounds, float startingX, float startingY) {
+    public RippleForeground(RippleDrawable owner, Rect bounds, float startingX, float startingY,
+            boolean isBounded) {
         super(owner, bounds);
 
+        mIsBounded = isBounded;
         mStartingX = startingX;
         mStartingY = startingY;
-    }
 
-    @Override
-    public void onSetup() {
-        mOuterX = 0;
-        mOuterY = 0;
+        if (isBounded) {
+            mBoundedRadius = MAX_BOUNDED_RADIUS * 0.9f
+                    + (float) (MAX_BOUNDED_RADIUS * Math.random() * 0.1);
+        } else {
+            mBoundedRadius = 0;
+        }
     }
 
     @Override
@@ -95,12 +113,10 @@
 
         final int origAlpha = p.getAlpha();
         final int alpha = (int) (origAlpha * mOpacity + 0.5f);
-        final float radius = MathUtils.lerp(0, mTargetRadius, mTweenRadius);
+        final float radius = getCurrentRadius();
         if (alpha > 0 && radius > 0) {
-            final float x = MathUtils.lerp(
-                    mClampedStartingX - mBounds.exactCenterX(), mOuterX, mTweenX);
-            final float y = MathUtils.lerp(
-                    mClampedStartingY - mBounds.exactCenterY(), mOuterY, mTweenY);
+            final float x = getCurrentX();
+            final float y = getCurrentY();
             p.setAlpha(alpha);
             c.drawCircle(x, y, radius, p);
             p.setAlpha(origAlpha);
@@ -120,8 +136,8 @@
      * Returns the maximum bounds of the ripple relative to the ripple center.
      */
     public void getBounds(Rect bounds) {
-        final int outerX = (int) mOuterX;
-        final int outerY = (int) mOuterY;
+        final int outerX = (int) mTargetX;
+        final int outerY = (int) mTargetY;
         final int r = (int) mTargetRadius + 1;
         bounds.set(outerX - r, outerY - r, outerX + r, outerY + r);
     }
@@ -146,14 +162,25 @@
 
     @Override
     protected Animator createSoftwareEnter(boolean fast) {
+        // Bounded ripples don't have enter animations.
+        if (mIsBounded) {
+            return null;
+        }
+
         final int duration = (int)
                 (1000 * Math.sqrt(mTargetRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensity) + 0.5);
 
-        final ObjectAnimator tweenAll = ObjectAnimator.ofFloat(this, TWEEN_ALL, 1);
-        tweenAll.setAutoCancel(true);
-        tweenAll.setDuration(duration);
-        tweenAll.setInterpolator(LINEAR_INTERPOLATOR);
-        tweenAll.setStartDelay(RIPPLE_ENTER_DELAY);
+        final ObjectAnimator tweenRadius = ObjectAnimator.ofFloat(this, TWEEN_RADIUS, 1);
+        tweenRadius.setAutoCancel(true);
+        tweenRadius.setDuration(duration);
+        tweenRadius.setInterpolator(LINEAR_INTERPOLATOR);
+        tweenRadius.setStartDelay(RIPPLE_ENTER_DELAY);
+
+        final ObjectAnimator tweenOrigin = ObjectAnimator.ofFloat(this, TWEEN_ORIGIN, 1);
+        tweenOrigin.setAutoCancel(true);
+        tweenOrigin.setDuration(duration);
+        tweenOrigin.setInterpolator(LINEAR_INTERPOLATOR);
+        tweenOrigin.setStartDelay(RIPPLE_ENTER_DELAY);
 
         final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 1);
         opacity.setAutoCancel(true);
@@ -161,31 +188,68 @@
         opacity.setInterpolator(LINEAR_INTERPOLATOR);
 
         final AnimatorSet set = new AnimatorSet();
-        set.play(tweenAll).with(opacity);
+        set.play(tweenOrigin).with(tweenRadius).with(opacity);
 
         return set;
     }
 
+    private float getCurrentX() {
+        return MathUtils.lerp(mClampedStartingX - mBounds.exactCenterX(), mTargetX, mTweenX);
+    }
+
+    private float getCurrentY() {
+        return MathUtils.lerp(mClampedStartingY - mBounds.exactCenterY(), mTargetY, mTweenY);
+    }
+
     private int getRadiusExitDuration() {
-        final float radius = MathUtils.lerp(0, mTargetRadius, mTweenRadius);
-        final float remaining = mTargetRadius - radius;
-        return (int) (1000 * Math.sqrt(remaining / (WAVE_TOUCH_UP_ACCELERATION
+        final float remainingRadius = mTargetRadius - getCurrentRadius();
+        return (int) (1000 * Math.sqrt(remainingRadius / (WAVE_TOUCH_UP_ACCELERATION
                 + WAVE_TOUCH_DOWN_ACCELERATION) * mDensity) + 0.5);
     }
 
+    private float getCurrentRadius() {
+        return MathUtils.lerp(0, mTargetRadius, mTweenRadius);
+    }
+
     private int getOpacityExitDuration() {
         return (int) (1000 * mOpacity / WAVE_OPACITY_DECAY_VELOCITY + 0.5f);
     }
 
+    /**
+     * Compute target values that are dependent on bounding.
+     */
+    private void computeBoundedTargetValues() {
+        mTargetX = (mClampedStartingX - mBounds.exactCenterX()) * .7f;
+        mTargetY = (mClampedStartingY - mBounds.exactCenterY()) * .7f;
+        mTargetRadius = mBoundedRadius;
+    }
+
     @Override
     protected Animator createSoftwareExit() {
-        final int radiusDuration = getRadiusExitDuration();
-        final int opacityDuration = getOpacityExitDuration();
+        final int radiusDuration;
+        final int originDuration;
+        final int opacityDuration;
+        if (mIsBounded) {
+            computeBoundedTargetValues();
 
-        final ObjectAnimator tweenAll = ObjectAnimator.ofFloat(this, TWEEN_ALL, 1);
-        tweenAll.setAutoCancel(true);
-        tweenAll.setDuration(radiusDuration);
-        tweenAll.setInterpolator(DECELERATE_INTERPOLATOR);
+            radiusDuration = BOUNDED_RADIUS_EXIT_DURATION;
+            originDuration = BOUNDED_ORIGIN_EXIT_DURATION;
+            opacityDuration = BOUNDED_OPACITY_EXIT_DURATION;
+        } else {
+            radiusDuration = getRadiusExitDuration();
+            originDuration = radiusDuration;
+            opacityDuration = getOpacityExitDuration();
+        }
+
+        final ObjectAnimator tweenRadius = ObjectAnimator.ofFloat(this, TWEEN_RADIUS, 1);
+        tweenRadius.setAutoCancel(true);
+        tweenRadius.setDuration(radiusDuration);
+        tweenRadius.setInterpolator(DECELERATE_INTERPOLATOR);
+
+        final ObjectAnimator tweenOrigin = ObjectAnimator.ofFloat(this, TWEEN_ORIGIN, 1);
+        tweenOrigin.setAutoCancel(true);
+        tweenOrigin.setDuration(originDuration);
+        tweenOrigin.setInterpolator(DECELERATE_INTERPOLATOR);
 
         final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 0);
         opacity.setAutoCancel(true);
@@ -193,7 +257,7 @@
         opacity.setInterpolator(LINEAR_INTERPOLATOR);
 
         final AnimatorSet set = new AnimatorSet();
-        set.play(tweenAll).with(opacity);
+        set.play(tweenOrigin).with(tweenRadius).with(opacity);
         set.addListener(mAnimationListener);
 
         return set;
@@ -201,15 +265,25 @@
 
     @Override
     protected RenderNodeAnimatorSet createHardwareExit(Paint p) {
-        final int radiusDuration = getRadiusExitDuration();
-        final int opacityDuration = getOpacityExitDuration();
+        final int radiusDuration;
+        final int originDuration;
+        final int opacityDuration;
+        if (mIsBounded) {
+            computeBoundedTargetValues();
 
-        final float startX = MathUtils.lerp(
-                mClampedStartingX - mBounds.exactCenterX(), mOuterX, mTweenX);
-        final float startY = MathUtils.lerp(
-                mClampedStartingY - mBounds.exactCenterY(), mOuterY, mTweenY);
+            radiusDuration = BOUNDED_RADIUS_EXIT_DURATION;
+            originDuration = BOUNDED_ORIGIN_EXIT_DURATION;
+            opacityDuration = BOUNDED_OPACITY_EXIT_DURATION;
+        } else {
+            radiusDuration = getRadiusExitDuration();
+            originDuration = radiusDuration;
+            opacityDuration = getOpacityExitDuration();
+        }
 
-        final float startRadius = MathUtils.lerp(0, mTargetRadius, mTweenRadius);
+        final float startX = getCurrentX();
+        final float startY = getCurrentY();
+        final float startRadius = getCurrentRadius();
+
         p.setAlpha((int) (p.getAlpha() * mOpacity + 0.5f));
 
         mPropPaint = CanvasProperty.createPaint(p);
@@ -221,12 +295,12 @@
         radius.setDuration(radiusDuration);
         radius.setInterpolator(DECELERATE_INTERPOLATOR);
 
-        final RenderNodeAnimator x = new RenderNodeAnimator(mPropX, mOuterX);
-        x.setDuration(radiusDuration);
+        final RenderNodeAnimator x = new RenderNodeAnimator(mPropX, mTargetX);
+        x.setDuration(originDuration);
         x.setInterpolator(DECELERATE_INTERPOLATOR);
 
-        final RenderNodeAnimator y = new RenderNodeAnimator(mPropY, mOuterY);
-        y.setDuration(radiusDuration);
+        final RenderNodeAnimator y = new RenderNodeAnimator(mPropY, mTargetY);
+        y.setDuration(originDuration);
         y.setInterpolator(DECELERATE_INTERPOLATOR);
 
         final RenderNodeAnimator opacity = new RenderNodeAnimator(mPropPaint,
@@ -307,16 +381,13 @@
     }
 
     /**
-     * Property for animating radius, center X, and center Y between their
-     * initial and target values.
+     * Property for animating radius between its initial and target values.
      */
-    private static final FloatProperty<RippleForeground> TWEEN_ALL =
-            new FloatProperty<RippleForeground>("tweenAll") {
+    private static final FloatProperty<RippleForeground> TWEEN_RADIUS =
+            new FloatProperty<RippleForeground>("tweenRadius") {
         @Override
         public void setValue(RippleForeground object, float value) {
             object.mTweenRadius = value;
-            object.mTweenX = value;
-            object.mTweenY = value;
             object.invalidateSelf();
         }
 
@@ -327,6 +398,24 @@
     };
 
     /**
+     * Property for animating origin between its initial and target values.
+     */
+    private static final FloatProperty<RippleForeground> TWEEN_ORIGIN =
+            new FloatProperty<RippleForeground>("tweenOrigin") {
+                @Override
+                public void setValue(RippleForeground object, float value) {
+                    object.mTweenX = value;
+                    object.mTweenY = value;
+                    object.invalidateSelf();
+                }
+
+                @Override
+                public Float get(RippleForeground object) {
+                    return object.mTweenX;
+                }
+            };
+
+    /**
      * Property for animating opacity between 0 and its target value.
      */
     private static final FloatProperty<RippleForeground> OPACITY =
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index 1d07951..a9ce7f4 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -208,7 +208,8 @@
 // Fill
 ////////////////////////////////////////////////////////////////////////////////
 
-void GlopBuilder::setFill(int color, float alphaScale, SkXfermode::Mode mode,
+void GlopBuilder::setFill(int color, float alphaScale,
+        SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage,
         const SkShader* shader, const SkColorFilter* colorFilter) {
     if (mode != SkXfermode::kClear_Mode) {
         float alpha = (SkColorGetA(color) / 255.0f) * alphaScale;
@@ -226,7 +227,6 @@
     } else {
         mOutGlop->fill.color = { 0, 0, 0, 1 };
     }
-    const bool SWAP_SRC_DST = false;
 
     mOutGlop->blend = { GL_ZERO, GL_ZERO };
     if (mOutGlop->fill.color.a < 1.0f
@@ -237,7 +237,7 @@
             || PaintUtils::isBlendedColorFilter(colorFilter)
             || mode != SkXfermode::kSrcOver_Mode) {
         if (CC_LIKELY(mode <= SkXfermode::kScreen_Mode)) {
-            Blend::getFactors(mode, SWAP_SRC_DST,
+            Blend::getFactors(mode, modeUsage,
                     &mOutGlop->blend.src, &mOutGlop->blend.dst);
         } else {
             // These blend modes are not supported by OpenGL directly and have
@@ -247,11 +247,11 @@
             // back to the default SrcOver blend mode instead
             if (CC_UNLIKELY(mCaches.extensions().hasFramebufferFetch())) {
                 mDescription.framebufferMode = mode;
-                mDescription.swapSrcDst = SWAP_SRC_DST;
+                mDescription.swapSrcDst = (modeUsage == Blend::ModeOrderSwap::Swap);
                 // blending in shader, don't enable
             } else {
                 // unsupported
-                Blend::getFactors(SkXfermode::kSrcOver_Mode, SWAP_SRC_DST,
+                Blend::getFactors(SkXfermode::kSrcOver_Mode, modeUsage,
                         &mOutGlop->blend.src, &mOutGlop->blend.dst);
             }
         }
@@ -317,17 +317,17 @@
             color |= 0x00FFFFFF;
             shader = nullptr;
         }
-        setFill(color, alphaScale, PaintUtils::getXfermode(paint->getXfermode()),
+        setFill(color, alphaScale,
+                PaintUtils::getXfermode(paint->getXfermode()), Blend::ModeOrderSwap::NoSwap,
                 shader, paint->getColorFilter());
     } else {
         mOutGlop->fill.color = { alphaScale, alphaScale, alphaScale, alphaScale };
 
-        const bool SWAP_SRC_DST = false;
         if (alphaScale < 1.0f
                 || (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::kAlpha)
                 || texture.blend
                 || mOutGlop->roundRectClipState) {
-            Blend::getFactors(SkXfermode::kSrcOver_Mode, SWAP_SRC_DST,
+            Blend::getFactors(SkXfermode::kSrcOver_Mode, Blend::ModeOrderSwap::NoSwap,
                     &mOutGlop->blend.src, &mOutGlop->blend.dst);
         } else {
             mOutGlop->blend = { GL_ZERO, GL_ZERO };
@@ -349,7 +349,8 @@
 
     mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
 
-    setFill(paint.getColor(), alphaScale, PaintUtils::getXfermode(paint.getXfermode()),
+    setFill(paint.getColor(), alphaScale,
+            PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap,
             paint.getShader(), paint.getColorFilter());
     mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
     return *this;
@@ -363,7 +364,8 @@
     //specify invalid filter/clamp, since these are always static for PathTextures
     mOutGlop->fill.texture = { &texture, GL_TEXTURE_2D, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
 
-    setFill(paint.getColor(), alphaScale, PaintUtils::getXfermode(paint.getXfermode()),
+    setFill(paint.getColor(), alphaScale,
+            PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap,
             paint.getShader(), paint.getColorFilter());
 
     mDescription.hasAlpha8Texture = true;
@@ -386,7 +388,8 @@
         shadowColor &= paint.getColor() | COLOR_BITMASK;
     }
 
-    setFill(shadowColor, alphaScale, PaintUtils::getXfermode(paint.getXfermode()),
+    setFill(shadowColor, alphaScale,
+            PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap,
             paint.getShader(), paint.getColorFilter());
 
     mDescription.hasAlpha8Texture = true;
@@ -399,7 +402,8 @@
     REQUIRE_STAGES(kMeshStage);
 
     mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
-    setFill(SK_ColorBLACK, 1.0f, SkXfermode::kSrcOver_Mode, nullptr, nullptr);
+    setFill(SK_ColorBLACK, 1.0f, SkXfermode::kSrcOver_Mode, Blend::ModeOrderSwap::NoSwap,
+            nullptr, nullptr);
     return *this;
 }
 
@@ -408,12 +412,13 @@
     REQUIRE_STAGES(kMeshStage);
 
     mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
-    setFill(SK_ColorBLACK, 1.0f, SkXfermode::kClear_Mode, nullptr, nullptr);
+    setFill(SK_ColorBLACK, 1.0f, SkXfermode::kClear_Mode, Blend::ModeOrderSwap::NoSwap,
+            nullptr, nullptr);
     return *this;
 }
 
 GlopBuilder& GlopBuilder::setFillLayer(Texture& texture, const SkColorFilter* colorFilter,
-        float alpha, SkXfermode::Mode mode) {
+        float alpha, SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage) {
     TRIGGER_STAGE(kFillStage);
     REQUIRE_STAGES(kMeshStage);
 
@@ -421,7 +426,7 @@
             GL_TEXTURE_2D, GL_LINEAR, GL_CLAMP_TO_EDGE, nullptr };
     mOutGlop->fill.color = { alpha, alpha, alpha, alpha };
 
-    setFill(SK_ColorWHITE, alpha, mode, nullptr, colorFilter);
+    setFill(SK_ColorWHITE, alpha, mode, modeUsage, nullptr, colorFilter);
 
     mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
     return *this;
@@ -435,7 +440,8 @@
             layer.getRenderTarget(), GL_LINEAR, GL_CLAMP_TO_EDGE, &layer.getTexTransform() };
     mOutGlop->fill.color = { alpha, alpha, alpha, alpha };
 
-    setFill(SK_ColorWHITE, alpha, layer.getMode(), nullptr, layer.getColorFilter());
+    setFill(SK_ColorWHITE, alpha, layer.getMode(), Blend::ModeOrderSwap::NoSwap,
+            nullptr, layer.getColorFilter());
 
     mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
     mDescription.hasTextureTransform = true;
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index 74d4889..b335a2c 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -18,6 +18,7 @@
 
 #include "OpenGLRenderer.h"
 #include "Program.h"
+#include "renderstate/Blend.h"
 #include "utils/Macros.h"
 
 class SkPaint;
@@ -65,7 +66,7 @@
     GlopBuilder& setFillBlack();
     GlopBuilder& setFillClear();
     GlopBuilder& setFillLayer(Texture& texture, const SkColorFilter* colorFilter,
-            float alpha, SkXfermode::Mode mode);
+            float alpha, SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage);
     GlopBuilder& setFillTextureLayer(Layer& layer, float alpha);
 
     GlopBuilder& setTransform(const Matrix4& ortho, const Matrix4& transform, bool fudgingOffset);
@@ -94,7 +95,8 @@
 
     void build();
 private:
-    void setFill(int color, float alphaScale, SkXfermode::Mode mode,
+    void setFill(int color, float alphaScale,
+            SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage,
             const SkShader* shader, const SkColorFilter* colorFilter);
 
     enum StageFlags {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index a6f6e18..622b570 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -908,15 +908,35 @@
         resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
     } else {
         EVENT_LOGD("composeHardwareLayerRect");
+
+        if (USE_GLOPS) {
+            Blend::ModeOrderSwap modeUsage = swap ?
+                    Blend::ModeOrderSwap::Swap : Blend::ModeOrderSwap::NoSwap;
+            const Matrix4& transform = swap ? Matrix4::identity() : *currentTransform();
+            bool snap = !swap
+                    && layer->getWidth() == static_cast<uint32_t>(rect.getWidth())
+                    && layer->getHeight() == static_cast<uint32_t>(rect.getHeight());
+            Glop glop;
+            GlopBuilder(mRenderState, mCaches, &glop)
+                    .setMeshTexturedUvQuad(nullptr, layer->texCoords)
+                    .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), modeUsage)
+                    .setTransform(currentSnapshot()->getOrthoMatrix(), transform, false)
+                    .setModelViewMapUnitToRectOptionalSnap(snap, rect)
+                    .setRoundRectClipState(currentSnapshot()->roundRectClipState)
+                    .build();
+            renderGlop(glop);
+            return;
+        }
+
         const Rect& texCoords = layer->texCoords;
         resetDrawTextureTexCoords(texCoords.left, texCoords.top,
                 texCoords.right, texCoords.bottom);
 
         float x = rect.left;
         float y = rect.top;
-        bool simpleTransform = currentTransform()->isPureTranslate() &&
-                layer->getWidth() == (uint32_t) rect.getWidth() &&
-                layer->getHeight() == (uint32_t) rect.getHeight();
+        bool simpleTransform = currentTransform()->isPureTranslate()
+                && layer->getWidth() == (uint32_t) rect.getWidth()
+                && layer->getHeight() == (uint32_t) rect.getHeight();
 
         if (simpleTransform) {
             // When we're swapping, the layer is already in screen coordinates
@@ -1053,11 +1073,42 @@
         rects = safeRegion.getArray(&count);
     }
 
-    const float alpha = getLayerAlpha(layer);
     const float texX = 1.0f / float(layer->getWidth());
     const float texY = 1.0f / float(layer->getHeight());
     const float height = rect.getHeight();
 
+    if (USE_GLOPS) {
+        TextureVertex quadVertices[count * 4];
+        //std::unique_ptr<TextureVertex[]> quadVertices(new TextureVertex[count * 4]);
+        TextureVertex* mesh = &quadVertices[0];
+        for (size_t i = 0; i < count; i++) {
+            const android::Rect* r = &rects[i];
+
+            const float u1 = r->left * texX;
+            const float v1 = (height - r->top) * texY;
+            const float u2 = r->right * texX;
+            const float v2 = (height - r->bottom) * texY;
+
+            // TODO: Reject quads outside of the clip
+            TextureVertex::set(mesh++, r->left, r->top, u1, v1);
+            TextureVertex::set(mesh++, r->right, r->top, u2, v1);
+            TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
+            TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
+        }
+        Glop glop;
+        GlopBuilder(mRenderState, mCaches, &glop)
+                .setMeshTexturedIndexedQuads(&quadVertices[0], count * 6)
+                .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap)
+                .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
+                .setModelViewOffsetRectSnap(0, 0, rect)
+                .setRoundRectClipState(currentSnapshot()->roundRectClipState)
+                .build();
+        DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, renderGlop(glop));
+        return;
+    }
+
+    const float alpha = getLayerAlpha(layer);
+
     setupDraw();
 
     // We must get (and therefore bind) the region mesh buffer
@@ -3110,7 +3161,7 @@
                 Glop glop;
                 GlopBuilder(mRenderState, mCaches, &glop)
                         .setMeshTexturedIndexedQuads(layer->mesh, layer->meshElementCount)
-                        .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode())
+                        .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap)
                         .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
                         .setModelViewOffsetRectSnap(x, y, Rect(0, 0, layer->layer.getWidth(), layer->layer.getHeight()))
                         .setRoundRectClipState(currentSnapshot()->roundRectClipState)
@@ -3596,7 +3647,8 @@
                 mode = SkXfermode::kSrcOver_Mode;
             }
         }
-        mRenderState.blend().enable(mode, swapSrcDst);
+        mRenderState.blend().enable(mode,
+                swapSrcDst ? Blend::ModeOrderSwap::Swap : Blend::ModeOrderSwap::NoSwap);
     } else {
         mRenderState.blend().disable();
     }
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index c6fdd3f..e009451 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -139,7 +139,7 @@
 
 void RenderNode::prepareLayer(TreeInfo& info, uint32_t dirtyMask) {
     LayerType layerType = properties().layerProperties().type();
-    if (CC_UNLIKELY(layerType == kLayerTypeRenderLayer)) {
+    if (CC_UNLIKELY(layerType == LayerType::RenderLayer)) {
         // Damage applied so far needs to affect our parent, but does not require
         // the layer to be updated. So we pop/push here to clear out the current
         // damage and get a clean state for display list or children updates to
@@ -156,7 +156,7 @@
     LayerType layerType = properties().layerProperties().type();
     // If we are not a layer OR we cannot be rendered (eg, view was detached)
     // we need to destroy any Layers we may have had previously
-    if (CC_LIKELY(layerType != kLayerTypeRenderLayer) || CC_UNLIKELY(!isRenderable())) {
+    if (CC_LIKELY(layerType != LayerType::RenderLayer) || CC_UNLIKELY(!isRenderable())) {
         if (CC_UNLIKELY(mLayer)) {
             LayerRenderer::destroyLayer(mLayer);
             mLayer = nullptr;
@@ -384,7 +384,7 @@
             renderer.concatMatrix(*properties().getTransformMatrix());
         }
     }
-    const bool isLayer = properties().layerProperties().type() != kLayerTypeNone;
+    const bool isLayer = properties().layerProperties().type() != LayerType::None;
     int clipFlags = properties().getClippingFlags();
     if (properties().getAlpha() < 1) {
         if (isLayer) {
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index bb6d087..9f1ceed 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -33,14 +33,12 @@
 namespace android {
 namespace uirenderer {
 
-LayerProperties::LayerProperties()
-        : mType(kLayerTypeNone)
-        , mColorFilter(nullptr) {
+LayerProperties::LayerProperties() {
     reset();
 }
 
 LayerProperties::~LayerProperties() {
-    setType(kLayerTypeNone);
+    setType(LayerType::None);
 }
 
 void LayerProperties::reset() {
@@ -146,7 +144,7 @@
         }
     }
 
-    const bool isLayer = layerProperties().type() != kLayerTypeNone;
+    const bool isLayer = layerProperties().type() != LayerType::None;
     int clipFlags = getClippingFlags();
     if (mPrimitiveFields.mAlpha < 1) {
         if (isLayer) {
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index f0e22d6..61e98d2 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -49,12 +49,12 @@
 #define RP_SET_AND_DIRTY(a, b) RP_SET(a, b, mPrimitiveFields.mMatrixOrPivotDirty = true)
 
 // Keep in sync with View.java:LAYER_TYPE_*
-enum LayerType {
-    kLayerTypeNone = 0,
+enum class LayerType {
+    None = 0,
     // Although we cannot build the software layer directly (must be done at
     // record time), this information is used when applying alpha.
-    kLayerTypeSoftware = 1,
-    kLayerTypeRenderLayer = 2,
+    Software = 1,
+    RenderLayer = 2,
     // TODO: LayerTypeSurfaceTexture? Maybe?
 };
 
@@ -124,12 +124,12 @@
 
     friend class RenderProperties;
 
-    LayerType mType;
+    LayerType mType = LayerType::None;
     // Whether or not that Layer's content is opaque, doesn't include alpha
     bool mOpaque;
     uint8_t mAlpha;
     SkXfermode::Mode mMode;
-    SkColorFilter* mColorFilter;
+    SkColorFilter* mColorFilter = nullptr;
 };
 
 /*
diff --git a/libs/hwui/renderstate/Blend.cpp b/libs/hwui/renderstate/Blend.cpp
index 789f6cc..29927ed 100644
--- a/libs/hwui/renderstate/Blend.cpp
+++ b/libs/hwui/renderstate/Blend.cpp
@@ -78,10 +78,10 @@
     // gl blending off by default
 }
 
-void Blend::enable(SkXfermode::Mode mode, bool swapSrcDst) {
+void Blend::enable(SkXfermode::Mode mode, ModeOrderSwap modeUsage) {
     GLenum srcMode;
     GLenum dstMode;
-    getFactors(mode, swapSrcDst, &srcMode, &dstMode);
+    getFactors(mode, modeUsage, &srcMode, &dstMode);
     setFactors(srcMode, dstMode);
 }
 
@@ -105,9 +105,9 @@
     }
 }
 
-void Blend::getFactors(SkXfermode::Mode mode, bool swapSrcDst, GLenum* outSrc, GLenum* outDst) {
-    *outSrc = swapSrcDst ? kBlendsSwap[mode].src : kBlends[mode].src;
-    *outDst = swapSrcDst ? kBlendsSwap[mode].dst : kBlends[mode].dst;
+void Blend::getFactors(SkXfermode::Mode mode, ModeOrderSwap modeUsage, GLenum* outSrc, GLenum* outDst) {
+    *outSrc = (modeUsage == ModeOrderSwap::Swap) ? kBlendsSwap[mode].src : kBlends[mode].src;
+    *outDst = (modeUsage == ModeOrderSwap::Swap) ? kBlendsSwap[mode].dst : kBlends[mode].dst;
 }
 
 void Blend::setFactors(GLenum srcMode, GLenum dstMode) {
diff --git a/libs/hwui/renderstate/Blend.h b/libs/hwui/renderstate/Blend.h
index 6d0c115c..dcc681d 100644
--- a/libs/hwui/renderstate/Blend.h
+++ b/libs/hwui/renderstate/Blend.h
@@ -29,11 +29,18 @@
 class Blend {
     friend class RenderState;
 public:
-    void enable(SkXfermode::Mode mode, bool swapSrcDst);
+    // dictates whether to swap src/dst
+    enum class ModeOrderSwap {
+        NoSwap,
+        Swap,
+    };
+
+    void enable(SkXfermode::Mode mode, ModeOrderSwap modeUsage);
     void disable();
     void syncEnabled();
 
-    static void getFactors(SkXfermode::Mode mode, bool swapSrcDst, GLenum* outSrc, GLenum* outDst);
+    static void getFactors(SkXfermode::Mode mode, ModeOrderSwap modeUsage,
+            GLenum* outSrc, GLenum* outDst);
     void setFactors(GLenum src, GLenum dst);
 
     void dump();
diff --git a/libs/hwui/renderstate/MeshState.cpp b/libs/hwui/renderstate/MeshState.cpp
index 6b00020..0521f65 100644
--- a/libs/hwui/renderstate/MeshState.cpp
+++ b/libs/hwui/renderstate/MeshState.cpp
@@ -37,7 +37,7 @@
 
     mCurrentBuffer = mUnitQuadBuffer;
 
-    std::unique_ptr<uint16_t[]> regionIndices(new uint16_t[kMaxNumberOfQuads * 6]);
+    uint16_t regionIndices[kMaxNumberOfQuads * 6];
     for (uint32_t i = 0; i < kMaxNumberOfQuads; i++) {
         uint16_t quad = i * 4;
         int index = i * 6;
@@ -50,8 +50,7 @@
     }
     glGenBuffers(1, &mQuadListIndices);
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mQuadListIndices);
-    glBufferData(GL_ELEMENT_ARRAY_BUFFER, kMaxNumberOfQuads * 6 * sizeof(uint16_t),
-            regionIndices.get(), GL_STATIC_DRAW);
+    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(regionIndices), regionIndices, GL_STATIC_DRAW);
     mCurrentIndicesBuffer = mQuadListIndices;
 
     // position attribute always enabled
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index 925d17e..84544ff 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -33,7 +33,7 @@
     private static Method sPropertyMethod;
     static {
         try {
-            Class<?> c = Class.forName("android.view.GLES20Canvas");
+            Class<?> c = Class.forName("android.view.DisplayListCanvas");
             sPropertyMethod = c.getDeclaredMethod("setProperty", String.class, String.class);
             if (!sPropertyMethod.isAccessible()) sPropertyMethod.setAccessible(true);
         } catch (ClassNotFoundException e) {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 5363968..b5036db 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -808,6 +808,7 @@
             }
             if (typeChanged) {
                 mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this);
+                mHandler.post(MessageHandler.MSG_UPDATE_VOLUME);
             }
         }
 
@@ -822,6 +823,7 @@
             }
             if (typeChanged) {
                 mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this);
+                mHandler.post(MessageHandler.MSG_UPDATE_VOLUME);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7e69e87..a4f3f23 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -496,7 +496,12 @@
     int mLastDisplayFreezeDuration = 0;
     Object mLastFinishedFreezeSource = null;
     boolean mWaitingForConfig = false;
-    boolean mWindowsFreezingScreen = false;
+
+    final static int WINDOWS_FREEZING_SCREENS_NONE = 0;
+    final static int WINDOWS_FREEZING_SCREENS_ACTIVE = 1;
+    final static int WINDOWS_FREEZING_SCREENS_TIMEOUT = 2;
+    private int mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
+
     boolean mClientFreezingScreen = false;
     int mAppsFreezingScreen = 0;
     int mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -4699,7 +4704,7 @@
                 if (mAppsFreezingScreen == 1) {
                     startFreezingDisplayLocked(false, 0, 0);
                     mH.removeMessages(H.APP_FREEZE_TIMEOUT);
-                    mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 5000);
+                    mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000);
                 }
             }
             final int N = wtoken.allAppWindows.size();
@@ -6415,7 +6420,7 @@
         mAltOrientation = altOrientation;
         mPolicy.setRotationLw(mRotation);
 
-        mWindowsFreezingScreen = true;
+        mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
         mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
         mH.sendEmptyMessageDelayed(H.WINDOW_FREEZE_TIMEOUT, WINDOW_FREEZE_TIMEOUT_DURATION);
         mWaitingForConfig = true;
@@ -7784,6 +7789,7 @@
                     // TODO(multidisplay): Can non-default displays rotate?
                     synchronized (mWindowMap) {
                         Slog.w(TAG, "Window freeze timeout expired.");
+                        mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
                         final WindowList windows = getDefaultWindowListLocked();
                         int i = windows.size();
                         while (i > 0) {
@@ -7851,6 +7857,7 @@
                 case APP_FREEZE_TIMEOUT: {
                     synchronized (mWindowMap) {
                         Slog.w(TAG, "App freeze timeout expired.");
+                        mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
                         final int numStacks = mStackIdToStack.size();
                         for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
                             final TaskStack stack = mStackIdToStack.valueAt(stackNdx);
@@ -8914,8 +8921,8 @@
             w.mOrientationChanging = true;
             w.mLastFreezeDuration = 0;
             mInnerFields.mOrientationChangeComplete = false;
-            if (!mWindowsFreezingScreen) {
-                mWindowsFreezingScreen = true;
+            if (mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_NONE) {
+                mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
                 // XXX should probably keep timeout from
                 // when we first froze the display.
                 mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
@@ -9905,8 +9912,8 @@
                 "With display frozen, orientationChangeComplete="
                 + mInnerFields.mOrientationChangeComplete);
         if (mInnerFields.mOrientationChangeComplete) {
-            if (mWindowsFreezingScreen) {
-                mWindowsFreezingScreen = false;
+            if (mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
+                mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
                 mLastFinishedFreezeSource = mInnerFields.mLastWindowFreezeSource;
                 mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
             }
@@ -10188,7 +10195,7 @@
         } else {
             mInnerFields.mOrientationChangeComplete = true;
             mInnerFields.mLastWindowFreezeSource = mAnimator.mLastWindowFreezeSource;
-            if (mWindowsFreezingScreen) {
+            if (mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
                 doRequest = true;
             }
         }
@@ -10524,7 +10531,8 @@
             return;
         }
 
-        if (mWaitingForConfig || mAppsFreezingScreen > 0 || mWindowsFreezingScreen
+        if (mWaitingForConfig || mAppsFreezingScreen > 0
+                || mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_ACTIVE
                 || mClientFreezingScreen) {
             if (DEBUG_ORIENTATION) Slog.d(TAG,
                 "stopFreezingDisplayLocked: Returning mWaitingForConfig=" + mWaitingForConfig