Merge "Send ranking update on importance change."
diff --git a/api/current.txt b/api/current.txt
index 2fd32c5..8e4531e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -34348,6 +34348,7 @@
 package android.service.notification {
 
   public class Condition implements android.os.Parcelable {
+    ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int);
     ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int, int, int);
     method public android.service.notification.Condition copy();
     method public int describeContents();
@@ -34379,7 +34380,6 @@
     method public final void notifyConditions(android.service.notification.Condition...);
     method public android.os.IBinder onBind(android.content.Intent);
     method public abstract void onConnected();
-    method public abstract void onRequestConditions(int);
     method public abstract void onSubscribe(android.net.Uri);
     method public abstract void onUnsubscribe(android.net.Uri);
     field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId";
@@ -43245,7 +43245,7 @@
     method public abstract void setNavigationBarColor(int);
     method public void setReenterTransition(android.transition.Transition);
     method public abstract void setResizingCaptionDrawable(android.graphics.drawable.Drawable);
-    method public final void setRestrictedCaptionAreaListener(android.view.Window.RestrictedCaptionAreaListener);
+    method public final void setRestrictedCaptionAreaListener(android.view.Window.OnRestrictedCaptionAreaChangedListener);
     method public void setReturnTransition(android.transition.Transition);
     method public void setSharedElementEnterTransition(android.transition.Transition);
     method public void setSharedElementExitTransition(android.transition.Transition);
@@ -43332,7 +43332,7 @@
     method public abstract android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
   }
 
-  public static abstract interface Window.RestrictedCaptionAreaListener {
+  public static abstract interface Window.OnRestrictedCaptionAreaChangedListener {
     method public abstract void onRestrictedCaptionAreaChanged(android.graphics.Rect);
   }
 
diff --git a/api/removed.txt b/api/removed.txt
index 2c6729d..0bf6594 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -199,6 +199,14 @@
 
 }
 
+package android.service.notification {
+
+  public abstract class ConditionProviderService extends android.app.Service {
+    method public void onRequestConditions(int);
+  }
+
+}
+
 package android.test.mock {
 
   public deprecated class MockPackageManager extends android.content.pm.PackageManager {
diff --git a/api/system-current.txt b/api/system-current.txt
index 5b640cd..b34182f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -36528,6 +36528,7 @@
 package android.service.notification {
 
   public class Condition implements android.os.Parcelable {
+    ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int);
     ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int, int, int);
     method public android.service.notification.Condition copy();
     method public int describeContents();
@@ -36559,7 +36560,7 @@
     method public final void notifyConditions(android.service.notification.Condition...);
     method public android.os.IBinder onBind(android.content.Intent);
     method public abstract void onConnected();
-    method public abstract void onRequestConditions(int);
+    method public void onRequestConditions(int);
     method public abstract void onSubscribe(android.net.Uri);
     method public abstract void onUnsubscribe(android.net.Uri);
     field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId";
@@ -45663,7 +45664,7 @@
     method public abstract void setNavigationBarColor(int);
     method public void setReenterTransition(android.transition.Transition);
     method public abstract void setResizingCaptionDrawable(android.graphics.drawable.Drawable);
-    method public final void setRestrictedCaptionAreaListener(android.view.Window.RestrictedCaptionAreaListener);
+    method public final void setRestrictedCaptionAreaListener(android.view.Window.OnRestrictedCaptionAreaChangedListener);
     method public void setReturnTransition(android.transition.Transition);
     method public void setSharedElementEnterTransition(android.transition.Transition);
     method public void setSharedElementExitTransition(android.transition.Transition);
@@ -45750,7 +45751,7 @@
     method public abstract android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
   }
 
-  public static abstract interface Window.RestrictedCaptionAreaListener {
+  public static abstract interface Window.OnRestrictedCaptionAreaChangedListener {
     method public abstract void onRestrictedCaptionAreaChanged(android.graphics.Rect);
   }
 
diff --git a/api/test-current.txt b/api/test-current.txt
index 47eec48..6a5917d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -34363,6 +34363,7 @@
 package android.service.notification {
 
   public class Condition implements android.os.Parcelable {
+    ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int);
     ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int, int, int);
     method public android.service.notification.Condition copy();
     method public int describeContents();
@@ -34394,7 +34395,6 @@
     method public final void notifyConditions(android.service.notification.Condition...);
     method public android.os.IBinder onBind(android.content.Intent);
     method public abstract void onConnected();
-    method public abstract void onRequestConditions(int);
     method public abstract void onSubscribe(android.net.Uri);
     method public abstract void onUnsubscribe(android.net.Uri);
     field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId";
@@ -43262,7 +43262,7 @@
     method public abstract void setNavigationBarColor(int);
     method public void setReenterTransition(android.transition.Transition);
     method public abstract void setResizingCaptionDrawable(android.graphics.drawable.Drawable);
-    method public final void setRestrictedCaptionAreaListener(android.view.Window.RestrictedCaptionAreaListener);
+    method public final void setRestrictedCaptionAreaListener(android.view.Window.OnRestrictedCaptionAreaChangedListener);
     method public void setReturnTransition(android.transition.Transition);
     method public void setSharedElementEnterTransition(android.transition.Transition);
     method public void setSharedElementExitTransition(android.transition.Transition);
@@ -43349,7 +43349,7 @@
     method public abstract android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
   }
 
-  public static abstract interface Window.RestrictedCaptionAreaListener {
+  public static abstract interface Window.OnRestrictedCaptionAreaChangedListener {
     method public abstract void onRestrictedCaptionAreaChanged(android.graphics.Rect);
   }
 
diff --git a/api/test-removed.txt b/api/test-removed.txt
index 2c6729d..0bf6594 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -199,6 +199,14 @@
 
 }
 
+package android.service.notification {
+
+  public abstract class ConditionProviderService extends android.app.Service {
+    method public void onRequestConditions(int);
+  }
+
+}
+
 package android.test.mock {
 
   public deprecated class MockPackageManager extends android.content.pm.PackageManager {
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 2c6b604..593fe2a 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -452,6 +452,9 @@
      */
     boolean performDexOptIfNeeded(String packageName, String instructionSet);
 
+    boolean performDexOpt(String packageName, String instructionSet, boolean useProfiles,
+            boolean extractOnly, boolean force);
+
     void forceDexOpt(String packageName);
 
     /**
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index 2c7bf65..11737c6 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -16,6 +16,7 @@
 
 package android.service.notification;
 
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.net.Uri;
 import android.os.Parcel;
@@ -24,7 +25,8 @@
 import java.util.Objects;
 
 /**
- * Condition information from condition providers.
+ * Condition information from condition providers. Used to tell the system to enter Do Not Disturb
+ * mode and request that the system exit Do Not Disturb mode.
  */
 public class Condition implements Parcelable {
 
@@ -38,14 +40,47 @@
     public static final int FLAG_RELEVANT_NOW = 1 << 0;
     public static final int FLAG_RELEVANT_ALWAYS = 1 << 1;
 
+    /**
+     * The URI representing the condition being updated.
+     * See {@link android.app.AutomaticZenRule#getConditionId()}.
+     */
     public final Uri id;
-    public final String summary;
-    public final String line1;
-    public final String line2;
-    public final int icon;
-    public final int state;
-    public final int flags;
 
+    /**
+     * A summary of what the rule encoded in {@link #id} means when it is enabled. User visible
+     * if the state of the condition is {@link #STATE_TRUE}.
+     */
+    public final String summary;
+
+    /**
+     * Additional information about what the rule encoded in {@link #id} means when it is enabled.
+     * User visible if the state of the condition is {@link #STATE_TRUE}.
+     */
+    public final String line1;
+
+    /**
+     * Additional information about what the rule encoded in {@link #id} means when it is enabled.
+     * User visible if the state of the condition is {@link #STATE_TRUE}.
+     */
+    public final String line2;
+
+    /**
+     * The state of this condition. {@link #STATE_TRUE} will enable Do Not Disturb mode. Any other
+     * state will turn Do Not Disturb off for this rule. Note that Do Not Disturb might still be
+     * enabled globally if other conditions are in a {@link #STATE_TRUE} state.
+     */
+    public final int state;
+
+    @SystemApi
+    public final int flags;
+    @SystemApi
+    public final int icon;
+
+    public Condition(Uri id, String summary, String line1, String line2, int state) {
+        this(id, summary, line1, line2, -1, state, FLAG_RELEVANT_ALWAYS);
+    }
+
+    @SystemApi
     public Condition(Uri id, String summary, String line1, String line2, int icon,
             int state, int flags) {
         if (id == null) throw new IllegalArgumentException("id is required");
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index eff09d6..adcc9d6 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -17,6 +17,7 @@
 package android.service.notification;
 
 import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
 import android.app.INotificationManager;
 import android.app.Service;
 import android.content.ComponentName;
@@ -102,13 +103,10 @@
     abstract public void onConnected();
 
     /**
-     * Called when the system wants to know the state of Conditions managed by this provider.
-     *
-     * Implementations should evaluate the state of all subscribed conditions, and provide updates
-     * by calling {@link #notifyCondition(Condition)} or {@link #notifyConditions(Condition...)}.
-     * @param relevance
+     * @removed
      */
-    abstract public void onRequestConditions(int relevance);
+    @SystemApi
+    public void onRequestConditions(int relevance) {}
 
     /**
      * Called by the system when there is a new {@link Condition} to be managed by this provider.
@@ -131,7 +129,11 @@
     }
 
     /**
-     * Informs the notification manager that the state of a Condition has changed.
+     * Informs the notification manager that the state of a Condition has changed. Use this method
+     * to put the system into Do Not Disturb mode or request that it exits Do Not Disturb mode. This
+     * call will be ignored unless there is an enabled {@link android.app.AutomaticZenRule} owned by
+     * service that has an {@link android.app.AutomaticZenRule#getConditionId()} equal to this
+     * {@link Condition#id}.
      * @param condition the condition that has changed.
      */
     public final void notifyCondition(Condition condition) {
@@ -140,7 +142,8 @@
     }
 
     /**
-     * Informs the notification manager that the state of one or more Conditions has changed.
+     * Informs the notification manager that the state of one or more Conditions has changed. See
+     * {@link #notifyCondition(Condition)} for restrictions.
      * @param conditions the changed conditions.
      */
     public final void notifyConditions(Condition... conditions) {
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 96b63d1..c68a740 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -34,7 +34,6 @@
 import android.media.session.MediaController;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemProperties;
@@ -273,7 +272,7 @@
     private Callback mCallback;
     private OnWindowDismissedCallback mOnWindowDismissedCallback;
     private WindowControllerCallback mWindowControllerCallback;
-    private RestrictedCaptionAreaListener mRestrictedCaptionAreaListener;
+    private OnRestrictedCaptionAreaChangedListener mOnRestrictedCaptionAreaChangedListener;
     private Rect mRestrictedCaptionAreaRect;
     private WindowManager mWindowManager;
     private IBinder mAppToken;
@@ -596,7 +595,7 @@
     /**
      * Callback for clients that want to be aware of where caption draws content.
      */
-    public interface RestrictedCaptionAreaListener {
+    public interface OnRestrictedCaptionAreaChangedListener {
         /**
          * Called when the area where caption draws content changes.
          *
@@ -856,8 +855,8 @@
      *
      * @param listener Callback that will be called when the area changes.
      */
-    public final void setRestrictedCaptionAreaListener(RestrictedCaptionAreaListener listener) {
-        mRestrictedCaptionAreaListener = listener;
+    public final void setRestrictedCaptionAreaListener(OnRestrictedCaptionAreaChangedListener listener) {
+        mOnRestrictedCaptionAreaChangedListener = listener;
         mRestrictedCaptionAreaRect = listener != null ? new Rect() : null;
     }
 
@@ -2126,17 +2125,20 @@
 
     /** @hide */
     public void notifyRestrictedCaptionAreaCallback(int left, int top, int right, int bottom) {
-        if (mRestrictedCaptionAreaListener != null) {
+        if (mOnRestrictedCaptionAreaChangedListener != null) {
             mRestrictedCaptionAreaRect.set(left, top, right, bottom);
-            mRestrictedCaptionAreaListener.onRestrictedCaptionAreaChanged(
+            mOnRestrictedCaptionAreaChangedListener.onRestrictedCaptionAreaChanged(
                     mRestrictedCaptionAreaRect);
         }
     }
 
     /**
      * Set what color should the caption controls be. By default the system will try to determine
-     * the color from the theme. You can overwrite this by using {@link #DECOR_CAPTION_SHADE_DARK}
-     * or {@link #DECOR_CAPTION_SHADE_DARK}.
+     * the color from the theme. You can overwrite this by using {@link #DECOR_CAPTION_SHADE_DARK},
+     * {@link #DECOR_CAPTION_SHADE_LIGHT}, or {@link #DECOR_CAPTION_SHADE_AUTO}.
+     * @see #DECOR_CAPTION_SHADE_DARK
+     * @see #DECOR_CAPTION_SHADE_LIGHT
+     * @see #DECOR_CAPTION_SHADE_AUTO
      */
     public abstract void setDecorCaptionShade(int decorCaptionShade);
 
diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
index c4ec714..478a56d 100644
--- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
+++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.app;
 
+import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -165,6 +166,14 @@
                     localized.setVisibility(View.VISIBLE);
                     text.setTextLocale(Locale.getDefault());
                 }
+                if (mCountryMode) {
+                    int layoutDir = TextUtils.getLayoutDirectionFromLocale(item.getParent());
+                    //noinspection ResourceType
+                    convertView.setLayoutDirection(layoutDir);
+                    text.setTextDirection(layoutDir == View.LAYOUT_DIRECTION_RTL
+                            ? View.TEXT_DIRECTION_RTL
+                            : View.TEXT_DIRECTION_LTR);
+                }
         }
         return convertView;
     }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
index 71e87cb..eea91a0 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
@@ -129,9 +129,11 @@
 
                     @Override
                     public void onChanged() {
-                        // TODO: This is causing b/22765812
-                        mSelection.clear();
                         mModelIds = mAdapter.getModelIds();
+
+                        // Update the selection to remove any disappeared IDs.
+                        mSelection.cancelProvisionalSelection();
+                        mSelection.intersect(mModelIds);
                     }
 
                     @Override
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java b/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java
index 467d97e..5df4dca 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java
@@ -105,6 +105,19 @@
         return createDocument(root.documentId, mimeType, name);
     }
 
+    public Uri createDocumentWithFlags(String documentId, String mimeType, String name, int flags)
+            throws RemoteException {
+        Bundle in = new Bundle();
+        in.putInt(StubProvider.EXTRA_FLAGS, flags);
+        in.putString(StubProvider.EXTRA_PARENT_ID, documentId);
+        in.putString(Document.COLUMN_MIME_TYPE, mimeType);
+        in.putString(Document.COLUMN_DISPLAY_NAME, name);
+
+        Bundle out = mClient.call("createDocumentWithFlags", null, in);
+        Uri uri = out.getParcelable(DocumentsContract.EXTRA_URI);
+        return uri;
+    }
+
     public Uri createFolder(Uri parentUri, String name) {
         return createDocument(parentUri, Document.MIME_TYPE_DIR, name);
     }
@@ -286,4 +299,5 @@
 
         return DocumentsContract.buildDocumentUri(mAuthority, documentId);
     }
+
 }
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/DownloadsActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/DownloadsActivityUiTest.java
index 737a8b6..0a91427 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/DownloadsActivityUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/DownloadsActivityUiTest.java
@@ -85,7 +85,7 @@
         mDevice.wait(Until.hasObject(By.pkg(TARGET_PKG).depth(0)), TIMEOUT);
         mDevice.waitForIdle();
 
-        mBot = new UiBot(mDevice, TIMEOUT);
+        mBot = new UiBot(mDevice, mContext, TIMEOUT);
 
         resetStorage();  // Just in case a test failed and tearDown didn't happen.
     }
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
index f0dbfe9..379300b 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
@@ -82,7 +82,7 @@
         mDevice.wait(Until.hasObject(By.pkg(TARGET_PKG).depth(0)), TIMEOUT);
         mDevice.waitForIdle();
 
-        mBot = new UiBot(mDevice, TIMEOUT);
+        mBot = new UiBot(mDevice, mContext, TIMEOUT);
 
         resetStorage();  // Just incase a test failed and tearDown didn't happen.
     }
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java
new file mode 100644
index 0000000..5c6254f
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import static com.android.documentsui.StubProvider.ROOT_0_ID;
+import static com.android.documentsui.UiTestEnvironment.TIMEOUT;
+
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+
+@LargeTest
+public class RenameDocumentUiTest extends InstrumentationTestCase {
+
+    private static final String TAG = "RenamDocumentUiTest";
+
+    private final String newName = "kitties.log";
+
+    private UiTestEnvironment mHelper;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mHelper = new UiTestEnvironment(getInstrumentation());
+        mHelper.launch();
+        mHelper.initTestFiles();
+        mHelper.bot().openRoot(ROOT_0_ID);
+    }
+
+    public void testRenameEnabled_SingleSelection() throws Exception {
+        mHelper.bot().selectDocument(UiTestEnvironment.fileName1);
+        mHelper.bot().openOverflowMenu();
+        mHelper.bot().assertMenuEnabled(R.string.menu_rename, true);
+
+        // Dismiss more options window
+        mHelper.device().pressBack();
+    }
+
+    public void testNoRenameSupport_SingleSelection() throws Exception {
+        mHelper.bot().selectDocument(UiTestEnvironment.fileNameNoRename);
+        mHelper.bot().openOverflowMenu();
+        mHelper.bot().assertMenuEnabled(R.string.menu_rename, false);
+
+        // Dismiss more options window
+        mHelper.device().pressBack();
+    }
+
+    public void testOneHasRenameSupport_MultipleSelection() throws Exception {
+        mHelper.bot().selectDocument(UiTestEnvironment.fileName1);
+        mHelper.bot().selectDocument(UiTestEnvironment.fileNameNoRename);
+        mHelper.bot().openOverflowMenu();
+        mHelper.bot().assertMenuEnabled(R.string.menu_rename, false);
+
+        // Dismiss more options window
+        mHelper.device().pressBack();
+    }
+
+    public void testRenameDisabled_MultipleSelection() throws Exception {
+        mHelper.bot().selectDocument(UiTestEnvironment.fileName1);
+        mHelper.bot().selectDocument(UiTestEnvironment.fileName2);
+        mHelper.bot().openOverflowMenu();
+        mHelper.bot().assertMenuEnabled(R.string.menu_rename, false);
+
+        // Dismiss more options window
+        mHelper.device().pressBack();
+    }
+
+    public void testRenameFile_OkButton() throws Exception {
+        mHelper.bot().selectDocument(UiTestEnvironment.fileName1);
+        mHelper.bot().openOverflowMenu();
+        mHelper.bot().openDialog(R.string.menu_rename);
+        mHelper.bot().setDialogText(newName);
+        mHelper.bot().dismissKeyboardIfPresent();
+
+        mHelper.device().waitForIdle(TIMEOUT);
+        mHelper.bot().findRenameDialogOkButton().click();
+        mHelper.device().waitForIdle(TIMEOUT);
+
+        mHelper.bot().assertDocument(UiTestEnvironment.fileName1, false);
+        mHelper.bot().assertDocument(newName, true);
+        mHelper.bot().assertDocumentsCount(mHelper.getDocumentsCountDir0());
+    }
+
+    public void testRenameFile_Enter() throws Exception {
+        mHelper.bot().selectDocument(UiTestEnvironment.fileName1);
+        mHelper.bot().openOverflowMenu();
+        mHelper.bot().openDialog(R.string.menu_rename);
+        mHelper.bot().setDialogText(newName);
+
+        pressEnter();
+
+        mHelper.bot().assertDocument(UiTestEnvironment.fileName1, false);
+        mHelper.bot().assertDocument(newName, true);
+        mHelper.bot().assertDocumentsCount(mHelper.getDocumentsCountDir0());
+    }
+
+    public void testRenameFile_Cancel() throws Exception {
+        mHelper.bot().selectDocument(UiTestEnvironment.fileName1);
+        mHelper.bot().openOverflowMenu();
+        mHelper.bot().openDialog(R.string.menu_rename);
+        mHelper.bot().setDialogText(newName);
+        mHelper.bot().dismissKeyboardIfPresent();
+
+        mHelper.device().waitForIdle(TIMEOUT);
+        mHelper.bot().findRenameDialogCancelButton().click();
+        mHelper.device().waitForIdle(TIMEOUT);
+
+        mHelper.bot().assertDocument(UiTestEnvironment.fileName1, true);
+        mHelper.bot().assertDocument(newName, false);
+        mHelper.bot().assertDocumentsCount(mHelper.getDocumentsCountDir0());
+    }
+
+    public void testRenameDir() throws Exception {
+        String oldName = "Dir1";
+        String newName = "Dir123";
+
+        mHelper.bot().selectDocument(oldName);
+        mHelper.bot().openOverflowMenu();
+        mHelper.bot().openDialog(R.string.menu_rename);
+        mHelper.bot().setDialogText(newName);
+
+        pressEnter();
+
+        mHelper.bot().assertDocument(oldName, false);
+        mHelper.bot().assertDocument(newName, true);
+        mHelper.bot().assertDocumentsCount(mHelper.getDocumentsCountDir0());
+    }
+
+    public void testRename_NameExists() throws Exception {
+        // Check that document with the new name exists
+        mHelper.bot().assertDocument(UiTestEnvironment.fileName2, true);
+        mHelper.bot().selectDocument(UiTestEnvironment.fileName1);
+        mHelper.bot().openOverflowMenu();
+        mHelper.bot().openDialog(R.string.menu_rename);
+        mHelper.bot().setDialogText(UiTestEnvironment.fileName2);
+
+        pressEnter();
+
+        mHelper.bot().assertSnackbar(R.string.rename_error);
+        mHelper.bot().assertDocument(UiTestEnvironment.fileName1, true);
+        mHelper.bot().assertDocument(UiTestEnvironment.fileName2, true);
+        mHelper.bot().assertDocumentsCount(mHelper.getDocumentsCountDir0());
+    }
+
+    private void pressEnter() {
+        mHelper.device().waitForIdle(TIMEOUT);
+        mHelper.device().pressEnter();
+        mHelper.device().waitForIdle(TIMEOUT);
+    }
+
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java
index 6c9c5d9..ce54725 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java
@@ -16,278 +16,139 @@
 
 package com.android.documentsui;
 
-import static com.android.documentsui.StubProvider.DEFAULT_AUTHORITY;
 import static com.android.documentsui.StubProvider.ROOT_0_ID;
 import static com.android.documentsui.StubProvider.ROOT_1_ID;
 
-import android.app.Instrumentation;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.RemoteException;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.Configurator;
-import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject;
 import android.support.test.uiautomator.UiObjectNotFoundException;
-import android.support.test.uiautomator.Until;
 import android.test.InstrumentationTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
-import android.view.MotionEvent;
-
-import com.android.documentsui.model.RootInfo;
 
 @LargeTest
 public class SearchViewUiTest extends InstrumentationTestCase {
 
-    private static final int TIMEOUT = 5000;
     private static final String TAG = "SearchViewUiTest";
-    private static final String TARGET_PKG = "com.android.documentsui";
-    private static final String LAUNCHER_PKG = "com.android.launcher";
 
-    private UiBot mBot;
-    private UiDevice mDevice;
-    private Context mContext;
-    private ContentResolver mResolver;
-    private DocumentsProviderHelper mDocsHelper;
-    private ContentProviderClient mClient;
-    private RootInfo mRoot_0;
-    private RootInfo mRoot_1;
+    private UiTestEnvironment mEnv;
 
-    private UiObject mSearchView;
-    private UiObject mSearchTextField;
     private UiObject mDocsList;
     private UiObject mMessageTextView;
-    private UiObject mSearchIcon;
 
+    @Override
     public void setUp() throws Exception {
-        // Initialize UiDevice instance.
-        Instrumentation instrumentation = getInstrumentation();
+        super.setUp();
+        mEnv = new UiTestEnvironment(getInstrumentation());
+        mEnv.launch();
 
-        mDevice = UiDevice.getInstance(instrumentation);
-
-        Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_MOUSE);
-
-        // Start from the home screen.
-        mDevice.pressHome();
-        mDevice.wait(Until.hasObject(By.pkg(LAUNCHER_PKG).depth(0)), TIMEOUT);
-
-        // NOTE: Must be the "target" context, else security checks in content provider will fail.
-        mContext = instrumentation.getTargetContext();
-        mResolver = mContext.getContentResolver();
-
-        mClient = mResolver.acquireUnstableContentProviderClient(DEFAULT_AUTHORITY);
-        mDocsHelper = new DocumentsProviderHelper(DEFAULT_AUTHORITY, mClient);
-
-        // Launch app.
-        Intent intent = mContext.getPackageManager().getLaunchIntentForPackage(TARGET_PKG);
-        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
-        mContext.startActivity(intent);
-        // Wait for the app to appear.
-        mDevice.wait(Until.hasObject(By.pkg(TARGET_PKG).depth(0)), TIMEOUT);
-        mDevice.waitForIdle();
-
-        mBot = new UiBot(mDevice, TIMEOUT);
-
-        resetStorage(); // Just incase a test failed and tearDown didn't happen.
-
-        initUiObjects();
     }
 
     @Override
     protected void tearDown() throws Exception {
+        mEnv.device().pressBack();
         super.tearDown();
-        mDevice.pressBack();
-        resetStorage();
-        mClient.release();
     }
 
-    private void resetStorage() throws RemoteException {
-        mClient.call("clear", null, null);
-        // TODO: Would be nice to have an event to wait on here.
-        mDevice.waitForIdle();
+    public void testSearchView_ExpandsOnClick() throws Exception {
+        mEnv.bot().openSearchView();
+        mEnv.bot().assertSearchTextFiledAndIcon(true, false);
     }
 
-    private void initTestFiles() throws RemoteException {
-        mRoot_0 = mDocsHelper.getRoot(ROOT_0_ID);
-        mRoot_1 = mDocsHelper.getRoot(ROOT_1_ID);
+    public void testSearchView_CollapsesOnBack() throws Exception {
+        mEnv.bot().openSearchView();
 
-        mDocsHelper.createDocument(mRoot_0, "text/plain", "file10.log");
-        mDocsHelper.createDocument(mRoot_0, "image/png", "file1.png");
-        mDocsHelper.createDocument(mRoot_0, "text/csv", "file2.csv");
+        mEnv.device().pressBack();
 
-        mDocsHelper.createDocument(mRoot_1, "text/plain", "anotherFile0.log");
-        mDocsHelper.createDocument(mRoot_1, "text/plain", "poodles.text");
+        mEnv.bot().assertSearchTextFiledAndIcon(false, true);
     }
 
-    private void initUiObjects() {
-        mSearchView = mBot.findSearchView();
-        mSearchTextField = mBot.findSearchViewTextField();
-        mDocsList = mBot.findDocumentsList();
-        mMessageTextView = mBot.findMessageTextView();
-        mSearchIcon = mBot.findSearchViewIcon();
-    }
-
-    public void testSearchViewExpandsOnClick() throws Exception {
-        assertTrue(mSearchIcon.exists());
-        assertFalse(mSearchTextField.exists());
-
-        mSearchView.click();
-
-        assertTrue(mSearchTextField.exists());
-        assertTrue(mSearchTextField.isFocused());
-        assertFalse(mSearchIcon.exists());
-    }
-
-    public void testSearchViewCollapsesOnBack() throws Exception {
-        assertTrue(mSearchIcon.exists());
-        assertFalse(mSearchTextField.exists());
-
-        mSearchView.click();
-
-        mDevice.pressBack();
-
-        assertTrue(mSearchIcon.exists());
-        assertFalse(mSearchTextField.exists());
-    }
-
-    public void testSearchViewClearsTextOnBack() throws Exception {
-        assertTrue(mSearchIcon.exists());
-        assertFalse(mSearchTextField.exists());
-
+    public void testSearchView_ClearsTextOnBack() throws Exception {
         String query = "file2";
-        mSearchView.click();
-        mSearchTextField.setText(query);
+        mEnv.bot().openSearchView();
+        mEnv.bot().setSearchQuery(query);
 
-        assertSearchTextField(true, query);
+        mEnv.device().pressBack();
 
-        mDevice.pressBack();
-
-        assertTrue(mSearchIcon.exists());
-        assertFalse(mSearchTextField.exists());
+        mEnv.bot().assertSearchTextFiledAndIcon(false, true);
     }
 
-    public void testSearchFound() throws Exception {
-        initTestFiles();
-
-        mBot.openRoot(ROOT_0_ID);
-
-        assertDefaultTestDir0();
+    public void testSearch_ResultsFound() throws Exception {
+        mEnv.initTestFiles();
+        mEnv.bot().openRoot(ROOT_0_ID);
+        mEnv.assertDefaultContentOfTestDir0();
 
         String query = "file1";
-        mSearchView.click();
-        mSearchTextField.setText(query);
+        mEnv.bot().openSearchView();
+        mEnv.bot().setSearchQuery(query);
+        mEnv.bot().assertSearchTextField(true, query);
 
-        assertTrue(mDocsList.exists());
-        assertSearchTextField(true, query);
+        mEnv.device().pressEnter();
 
-        mDevice.pressEnter();
-
-        assertTrue(mDocsList.exists());
-        assertEquals(2, mDocsList.getChildCount());
-        mBot.assertHasDocuments("file1.png", "file10.log");
-        assertSearchTextField(false, query);
+        mEnv.bot().assertDocumentsCountOnList(true, 2);
+        mEnv.bot().assertHasDocuments(UiTestEnvironment.fileName1, UiTestEnvironment.fileName2);
+        mEnv.bot().assertSearchTextField(false, query);
     }
 
-    public void testSearchFoundClearsOnBack() throws Exception {
-        initTestFiles();
+    public void testSearchResultsFound_ClearsOnBack() throws Exception {
+        mEnv.initTestFiles();
+        mEnv.bot().openRoot(ROOT_0_ID);
+        mEnv.assertDefaultContentOfTestDir0();
 
-        mBot.openRoot(ROOT_0_ID);
+        String query = UiTestEnvironment.fileName1;
+        mEnv.bot().openSearchView();
+        mEnv.bot().setSearchQuery(query);
 
-        assertDefaultTestDir0();
-
-        String query = "file1";
-        mSearchView.click();
-        mSearchTextField.setText(query);
-
-        mDevice.pressEnter();
-        mDevice.pressBack();
-
-        assertDefaultTestDir0();
+        mEnv.device().pressEnter();
+        mEnv.device().pressBack();
+        mEnv.assertDefaultContentOfTestDir0();
     }
 
-    public void testSearchNoResults() throws Exception {
-        initTestFiles();
-
-        mBot.openRoot(ROOT_0_ID);
-
-        assertDefaultTestDir0();
+    public void testSearch_NoResults() throws Exception {
+        mEnv.initTestFiles();
+        mEnv.bot().openRoot(ROOT_0_ID);
+        mEnv.assertDefaultContentOfTestDir0();
 
         String query = "chocolate";
-        mSearchView.click();
-        mSearchTextField.setText(query);
+        mEnv.bot().openSearchView();
+        mEnv.bot().setSearchQuery(query);
 
-        mDevice.pressEnter();
+        mEnv.device().pressEnter();
 
-        assertFalse(mDocsList.exists());
-        assertTrue(mMessageTextView.exists());
-        String msg = String.valueOf(mContext.getString(R.string.no_results));
-        assertEquals(String.format(msg, "TEST_ROOT_0"), mMessageTextView.getText());
-        assertSearchTextField(false, query);
+        mEnv.bot().assertDocumentsCountOnList(false, 0);
+
+        String msg = String.valueOf(mEnv.context().getString(R.string.no_results));
+        mEnv.bot().assertMessageTextView(String.format(msg, "TEST_ROOT_0"));
+        mEnv.bot().assertSearchTextField(false, query);
     }
 
-    public void testSearchNoResultsClearsOnBack() throws Exception {
-        initTestFiles();
-
-        mBot.openRoot(ROOT_0_ID);
-
-        assertDefaultTestDir0();
+    public void testSearchNoResults_ClearsOnBack() throws Exception {
+        mEnv.initTestFiles();
+        mEnv.bot().openRoot(ROOT_0_ID);
+        mEnv.assertDefaultContentOfTestDir0();
 
         String query = "chocolate";
-        mSearchView.click();
-        mSearchTextField.setText(query);
+        mEnv.bot().openSearchView();
+        mEnv.bot().setSearchQuery(query);
 
-        mDevice.pressEnter();
-        mDevice.pressBack();
-
-        assertDefaultTestDir0();
+        mEnv.device().pressEnter();
+        mEnv.device().pressBack();
+        mEnv.assertDefaultContentOfTestDir0();
     }
 
-    public void testSearchFoundClearsDirectoryChange() throws Exception {
-        initTestFiles();
+    public void testSearchResultsFound_ClearsOnDirectoryChange() throws Exception {
+        mEnv.initTestFiles();
+        mEnv.bot().openRoot(ROOT_0_ID);
+        mEnv.assertDefaultContentOfTestDir0();
 
-        mBot.openRoot(ROOT_0_ID);
+        String query = UiTestEnvironment.fileName1;
+        mEnv.bot().openSearchView();
+        mEnv.bot().setSearchQuery(query);
 
-        assertDefaultTestDir0();
+        mEnv.device().pressEnter();
 
-        String query = "file1";
-        mSearchView.click();
-        mSearchTextField.setText(query);
+        mEnv.bot().openRoot(ROOT_1_ID);
+        mEnv.assertDefaultContentOfTestDir1();
 
-        mDevice.pressEnter();
-
-        mBot.openRoot(ROOT_1_ID);
-
-         assertDefaultTestDir1();
-
-         mBot.openRoot(ROOT_0_ID);
-
-         assertDefaultTestDir0();
-    }
-
-    private void assertDefaultTestDir0() throws UiObjectNotFoundException {
-        assertTrue(mSearchIcon.exists());
-        assertTrue(mDocsList.exists());
-        assertFalse(mSearchTextField.exists());
-        assertEquals(3, mDocsList.getChildCount());
-        mBot.assertHasDocuments("file2.csv", "file1.png", "file10.log");
-    }
-
-    private void assertDefaultTestDir1() throws UiObjectNotFoundException {
-        assertTrue(mSearchIcon.exists());
-        assertFalse(mSearchTextField.exists());
-        assertTrue(mDocsList.exists());
-        assertEquals(2, mDocsList.getChildCount());
-        mBot.assertHasDocuments("anotherFile0.log", "poodles.text");
-    }
-
-    private void assertSearchTextField(boolean isFocused, String query)
-            throws UiObjectNotFoundException {
-        assertFalse(mSearchIcon.exists());
-        assertTrue(mSearchTextField.exists());
-        assertEquals(isFocused, mSearchTextField.isFocused());
-        assertEquals(query, mSearchTextField.getText());
+        mEnv.bot().openRoot(ROOT_0_ID);
+        mEnv.assertDefaultContentOfTestDir0();
     }
 }
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
index cc48786..980627b 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
@@ -16,6 +16,10 @@
 
 package com.android.documentsui;
 
+import static com.android.documentsui.Shared.TAG;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.pm.ProviderInfo;
@@ -34,6 +38,7 @@
 import android.provider.DocumentsContract.Root;
 import android.provider.DocumentsProvider;
 import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
 import android.util.Log;
 
 import libcore.io.IoUtils;
@@ -66,10 +71,13 @@
             = "com.android.documentsui.stubprovider.STREAM_TYPES";
     public static final String EXTRA_CONTENT = "com.android.documentsui.stubprovider.CONTENT";
 
+    public static final String EXTRA_FLAGS = "com.android.documentsui.stubprovider.FLAGS";
+    public static final String EXTRA_PARENT_ID = "com.android.documentsui.stubprovider.PARENT";
+
     private static final String TAG = "StubProvider";
 
     private static final String STORAGE_SIZE_KEY = "documentsui.stubprovider.size";
-    private static int DEFAULT_ROOT_SIZE = 1024 * 1024 * 100;  // 100 MB.
+    private static int DEFAULT_ROOT_SIZE = 1024 * 1024 * 100; // 100 MB.
 
     private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
             Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID,
@@ -176,44 +184,8 @@
     @Override
     public String createDocument(String parentId, String mimeType, String displayName)
             throws FileNotFoundException {
-
-        final StubDocument parent = mStorage.get(parentId);
-        if (parent == null) {
-            throw new IllegalArgumentException(
-                    "Can't create file " + displayName + " in null parent.");
-        }
-        if (!parent.file.isDirectory()) {
-            throw new IllegalArgumentException(
-                    "Can't create file " + displayName + " inside non-directory parent "
-                    + parent.file.getName());
-        }
-
-        final File file = new File(parent.file, displayName);
-        if (file.exists()) {
-            throw new FileNotFoundException(
-                    "Duplicate file names not supported for " + file);
-        }
-
-        if (mimeType.equals(Document.MIME_TYPE_DIR)) {
-            if (!file.mkdirs()) {
-                throw new FileNotFoundException(
-                        "Failed to create directory(s): " + file);
-            }
-            Log.i(TAG, "Created new directory: " + file);
-        } else {
-            boolean created = false;
-            try {
-                created = file.createNewFile();
-            } catch (IOException e) {
-                // We'll throw an FNF exception later :)
-                Log.e(TAG, "createNewFile operation failed for file: " + file, e);
-            }
-            if (!created) {
-                throw new FileNotFoundException(
-                        "createNewFile operation failed for: " + file);
-            }
-            Log.i(TAG, "Created new file: " + file);
-        }
+        StubDocument parent = mStorage.get(parentId);
+        File file = createFile(parent, mimeType, displayName);
 
         final StubDocument document = StubDocument.createRegularDocument(file, mimeType, parent);
         mStorage.put(document.documentId, document);
@@ -303,6 +275,45 @@
     }
 
     @Override
+    public String renameDocument(String documentId, String displayName)
+            throws FileNotFoundException {
+
+        StubDocument oldDoc = mStorage.get(documentId);
+
+        File before = oldDoc.file;
+        File after = new File(before.getParentFile(), displayName);
+
+        if (after.exists()) {
+            throw new IllegalStateException("Already exists " + after);
+        }
+
+        boolean result = before.renameTo(after);
+
+        if (!result) {
+            throw new IllegalStateException("Failed to rename to " + after);
+        }
+
+        StubDocument newDoc = StubDocument.createRegularDocument(after, oldDoc.mimeType,
+                mStorage.get(oldDoc.parentId));
+
+        mStorage.remove(documentId);
+        notifyParentChanged(oldDoc.parentId);
+        getContext().getContentResolver().notifyChange(
+                DocumentsContract.buildDocumentUri(mAuthority, oldDoc.documentId), null, false);
+
+        mStorage.put(newDoc.documentId, newDoc);
+        notifyParentChanged(newDoc.parentId);
+        getContext().getContentResolver().notifyChange(
+                DocumentsContract.buildDocumentUri(mAuthority, newDoc.documentId), null, false);
+
+        if (!TextUtils.equals(documentId, newDoc.documentId)) {
+            return newDoc.documentId;
+        } else {
+            return null;
+        }
+    }
+
+    @Override
     public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal)
             throws FileNotFoundException {
 
@@ -468,6 +479,9 @@
             case "simulateReadErrorsForFile":
                 simulateReadErrorsForFile(arg);
                 return null;
+            case "createDocumentWithFlags":
+                Bundle bundle = dispatchCreateDocumentWithFlags(extras);
+                return bundle;
             default:
                 return super.call(method, arg, extras);
         }
@@ -493,6 +507,81 @@
         return null;
     }
 
+    private Bundle dispatchCreateDocumentWithFlags(Bundle extras) {
+        String rootId = extras.getString(EXTRA_PARENT_ID);
+        String mimeType = extras.getString(Document.COLUMN_MIME_TYPE);
+        String name = extras.getString(Document.COLUMN_DISPLAY_NAME);
+        int flags = extras.getInt(EXTRA_FLAGS);
+
+        Bundle out = new Bundle();
+        String documentId = null;
+        try {
+            documentId = createDocument(rootId, mimeType, name, flags);
+            Uri uri = DocumentsContract.buildDocumentUri(mAuthority, documentId);
+            out.putParcelable(DocumentsContract.EXTRA_URI, uri);
+        } catch (FileNotFoundException e) {
+            Log.d(TAG, "Cretaing document with flags failed" + name);
+        }
+        return out;
+    }
+
+    public String createDocument(String parentId, String mimeType, String displayName, int flags)
+            throws FileNotFoundException {
+
+        StubDocument parent = mStorage.get(parentId);
+        File file = createFile(parent, mimeType, displayName);
+
+        final StubDocument document = StubDocument.createDocumentWithFlags(file, mimeType, parent,
+                flags);
+        mStorage.put(document.documentId, document);
+        Log.d(TAG, "Created document " + document.documentId);
+        notifyParentChanged(document.parentId);
+        getContext().getContentResolver().notifyChange(
+                DocumentsContract.buildDocumentUri(mAuthority, document.documentId),
+                null, false);
+
+        return document.documentId;
+    }
+
+    private File createFile(StubDocument parent, String mimeType, String displayName)
+            throws FileNotFoundException {
+        if (parent == null) {
+            throw new IllegalArgumentException(
+                    "Can't create file " + displayName + " in null parent.");
+        }
+        if (!parent.file.isDirectory()) {
+            throw new IllegalArgumentException(
+                    "Can't create file " + displayName + " inside non-directory parent "
+                            + parent.file.getName());
+        }
+
+        final File file = new File(parent.file, displayName);
+        if (file.exists()) {
+            throw new FileNotFoundException(
+                    "Duplicate file names not supported for " + file);
+        }
+
+        if (mimeType.equals(Document.MIME_TYPE_DIR)) {
+            if (!file.mkdirs()) {
+                throw new FileNotFoundException("Failed to create directory(s): " + file);
+            }
+            Log.i(TAG, "Created new directory: " + file);
+        } else {
+            boolean created = false;
+            try {
+                created = file.createNewFile();
+            } catch (IOException e) {
+                // We'll throw an FNF exception later :)
+                Log.e(TAG, "createNewFile operation failed for file: " + file, e);
+            }
+            if (!created) {
+                throw new FileNotFoundException("createNewFile operation failed for: " + file);
+            }
+            Log.i(TAG, "Created new file: " + file);
+        }
+        return file;
+    }
+
     private void configure(String arg, Bundle extras) {
         Log.d(TAG, "Configure " + arg);
         String rootName = extras.getString(EXTRA_ROOT, ROOT_0_ID);
@@ -557,7 +646,7 @@
         }
         final StubDocument document = StubDocument.createRegularDocument(file, mimeType, parent);
         mStorage.put(document.documentId, document);
-        return DocumentsContract.buildDocumentUri(mAuthority,  document.documentId);
+        return DocumentsContract.buildDocumentUri(mAuthority, document.documentId);
     }
 
     @VisibleForTesting
@@ -573,7 +662,7 @@
         final StubDocument document = StubDocument.createVirtualDocument(
                 file, mimeType, streamTypes, parent);
         mStorage.put(document.documentId, document);
-        return DocumentsContract.buildDocumentUri(mAuthority,  document.documentId);
+        return DocumentsContract.buildDocumentUri(mAuthority, document.documentId);
     }
 
     @VisibleForTesting
@@ -643,9 +732,8 @@
         public final String parentId;
         public final RootInfo rootInfo;
 
-        private StubDocument(
-                 File file, String mimeType, List<String> streamTypes, int flags,
-                 StubDocument parent) {
+        private StubDocument(File file, String mimeType, List<String> streamTypes, int flags,
+                StubDocument parent) {
             this.file = file;
             this.documentId = getDocumentIdForFile(file);
             this.mimeType = mimeType;
@@ -671,7 +759,7 @@
 
         public static StubDocument createRegularDocument(
                 File file, String mimeType, StubDocument parent) {
-            int flags = Document.FLAG_SUPPORTS_DELETE;
+            int flags = Document.FLAG_SUPPORTS_DELETE | Document.FLAG_SUPPORTS_RENAME;
             if (file.isDirectory()) {
                 flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
             } else {
@@ -680,6 +768,11 @@
             return new StubDocument(file, mimeType, new ArrayList<String>(), flags, parent);
         }
 
+        public static StubDocument createDocumentWithFlags(
+                File file, String mimeType, StubDocument parent, int flags) {
+            return new StubDocument(file, mimeType, new ArrayList<String>(), flags, parent);
+        }
+
         public static StubDocument createVirtualDocument(
                 File file, String mimeType, List<String> streamTypes, StubDocument parent) {
             int flags = Document.FLAG_SUPPORTS_DELETE | Document.FLAG_SUPPORTS_WRITE
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
index c4def8f..f12ae10 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
@@ -17,7 +17,11 @@
 package com.android.documentsui;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.assertFalse;
 
+import android.content.Context;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.BySelector;
 import android.support.test.uiautomator.UiDevice;
@@ -28,11 +32,13 @@
 import android.support.test.uiautomator.UiSelector;
 import android.support.test.uiautomator.Until;
 import android.util.Log;
+import android.view.inputmethod.InputMethodManager;
 
 import junit.framework.Assert;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Iterator;
 import java.util.List;
 import java.util.regex.Pattern;
 
@@ -48,10 +54,12 @@
             By.desc(Pattern.compile("^Deleting [0-9]+ file.+"));
 
     private UiDevice mDevice;
+    private Context mContext;
     private int mTimeout;
 
-    public UiBot(UiDevice device, int timeout) {
+    public UiBot(UiDevice device, Context context, int timeout) {
         mDevice = device;
+        mContext = context;
         mTimeout = timeout;
     }
 
@@ -109,25 +117,40 @@
         }
     }
 
-    UiObject findDocument(String label) throws UiObjectNotFoundException {
-        final UiSelector docList = new UiSelector().resourceId(
-                "com.android.documentsui:id/container_directory").childSelector(
-                        new UiSelector().resourceId("com.android.documentsui:id/list"));
-
-        // Wait for the first list item to appear
-        new UiObject(docList.childSelector(new UiSelector())).waitForExists(mTimeout);
-
-        // new UiScrollable(docList).scrollIntoView(new UiSelector().text(label));
-        return mDevice.findObject(docList.childSelector(new UiSelector().text(label)));
+    void assertMenuEnabled(int id, boolean enabled) {
+        UiObject2 menu= findMenuWithName(mContext.getString(id));
+        assertNotNull(menu);
+        assertEquals(enabled, menu.isEnabled());
     }
 
-    boolean hasDocuments(String... labels) throws UiObjectNotFoundException {
-        for (String label : labels) {
-            if (!findDocument(label).exists()) {
-                return false;
-            }
+    void assertDocumentsCount(int count) throws UiObjectNotFoundException {
+        UiObject docsList = findDocumentsList();
+        assertEquals(count, docsList.getChildCount());
+    }
+
+    void assertDocumentsCount(String dir, int count) throws UiObjectNotFoundException {
+        openRoot(dir);
+        UiObject docsList = findDocumentsList();
+        assertEquals(count, docsList.getChildCount());
+    }
+
+    void assertSearchTextField(boolean isFocused, String query)
+            throws UiObjectNotFoundException {
+        UiObject textField = findSearchViewTextField();
+        UiObject searchIcon = findSearchViewIcon();
+
+        assertFalse(searchIcon.exists());
+        assertTrue(textField.exists());
+        assertEquals(isFocused, textField.isFocused());
+        if(query != null) {
+            assertEquals(query, textField.getText());
         }
-        return true;
+    }
+
+    void assertSearchTextFiledAndIcon(boolean searchTextFieldExists, boolean searchIconExists) {
+        assertEquals(searchTextFieldExists, findSearchViewTextField().exists());
+        assertEquals(searchIconExists, findSearchViewIcon().exists());
+
     }
 
     void assertHasDocuments(String... labels) throws UiObjectNotFoundException {
@@ -143,10 +166,77 @@
         }
     }
 
+    void assertDocument(String name, boolean exists) throws UiObjectNotFoundException {
+        UiObject doc = findDocument(name);
+        assertEquals(exists, doc.exists());
+    }
+
+    void assertDocumentsCountOnList(boolean exists, int count) throws UiObjectNotFoundException {
+        UiObject docsList = findDocumentsList();
+        assertEquals(exists, docsList.exists());
+        if(docsList.exists()) {
+            assertEquals(count, docsList.getChildCount());
+        }
+    }
+
+    void assertMessageTextView(String message) throws UiObjectNotFoundException {
+        UiObject messageTextView = findMessageTextView();
+        assertTrue(messageTextView.exists());
+
+        String msg = String.valueOf(message);
+        assertEquals(String.format(msg, "TEST_ROOT_0"), messageTextView.getText());
+
+    }
+    void assertSnackbar(int id) {
+        assertNotNull(getSnackbar(mContext.getString(id)));
+    }
+
     void clickDocument(String label) throws UiObjectNotFoundException {
         findDocument(label).click();
     }
 
+    void openSearchView() throws UiObjectNotFoundException {
+        UiObject searchView = findSearchView();
+        searchView.click();
+        assertTrue(searchView.exists());
+    }
+
+    void setSearchQuery(String query) throws UiObjectNotFoundException {
+        UiObject searchView = findSearchView();
+        assertTrue(searchView.exists());
+        UiObject searchTextField = findSearchViewTextField();
+        searchTextField.setText(query);
+        assertSearchTextField(true, query);
+    }
+
+    UiObject openOverflowMenu() throws UiObjectNotFoundException {
+        UiObject obj = findMenuMoreOptions();
+        obj.click();
+        mDevice.waitForIdle(mTimeout);
+        return obj;
+    }
+
+    void openDialog(int id) {
+        UiObject2 menu= findMenuWithName(mContext.getString(id));
+        assertNotNull(menu);
+        assertEquals(true, menu.isEnabled());
+        menu.click();
+    }
+
+    void setDialogText(String text) throws UiObjectNotFoundException {
+        findDialogEditText().setText(text);
+    }
+
+    UiObject selectDocument(String label) throws UiObjectNotFoundException {
+        UiObject doc = findDocument(label);
+        doc.longClick();
+        return doc;
+    }
+
+    UiObject2 getSnackbar(String message) {
+        return mDevice.wait(Until.findObject(By.text(message)), mTimeout);
+    }
+
     void waitForDeleteSnackbar() {
         mDevice.wait(Until.findObject(SNACK_DELETE), mTimeout);
     }
@@ -200,6 +290,27 @@
         return mDevice.findObject(selector);
     }
 
+    UiObject findDocument(String label) throws UiObjectNotFoundException {
+        final UiSelector docList = new UiSelector().resourceId(
+                "com.android.documentsui:id/container_directory").childSelector(
+                        new UiSelector().resourceId("com.android.documentsui:id/list"));
+
+        // Wait for the first list item to appear
+        new UiObject(docList.childSelector(new UiSelector())).waitForExists(mTimeout);
+
+        // new UiScrollable(docList).scrollIntoView(new UiSelector().text(label));
+        return mDevice.findObject(docList.childSelector(new UiSelector().text(label)));
+    }
+
+    boolean hasDocuments(String... labels) throws UiObjectNotFoundException {
+        for (String label : labels) {
+            if (!findDocument(label).exists()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     UiObject findDocumentsList() {
         return findObject(
                 "com.android.documentsui:id/container_directory",
@@ -224,4 +335,60 @@
                 "com.android.documentsui:id/message");
     }
 
+    UiObject findActionModeBar() {
+        return findObject("android:id/action_mode_bar");
+    }
+
+    UiObject findDialogEditText() {
+        return findObject("android:id/content", "android:id/text1");
+    }
+
+    UiObject findRenameDialogOkButton() {
+        return findObject("android:id/content", "android:id/button1");
+    }
+
+    UiObject findRenameDialogCancelButton() {
+        return findObject("android:id/content", "android:id/button2");
+    }
+
+    UiObject findMenuLabelWithName(String label) {
+        UiSelector selector = new UiSelector().text(label);
+        return mDevice.findObject(selector);
+    }
+
+    UiObject2 findMenuWithName(String label) {
+        List<UiObject2> menuItems = mDevice.findObjects(By.clazz("android.widget.LinearLayout"));
+        Iterator<UiObject2> it = menuItems.iterator();
+
+        UiObject2 menuItem = null;
+        while(it.hasNext()) {
+            menuItem = it.next();
+            UiObject2 text = menuItem.findObject(By.text(label));
+            if(text != null) {
+                break;
+            }
+        }
+        return menuItem;
+    }
+
+    UiObject findMenuMoreOptions() {
+        UiSelector selector = new UiSelector().className("android.widget.ImageButton")
+                .descriptionContains("More options");
+        //TODO: use the system string ? android.R.string.action_menu_overflow_description
+        return mDevice.findObject(selector);
+    }
+
+    // Indirect way to detect the keyboard.
+    boolean isKeyboardPresent() {
+        InputMethodManager inputManager = (InputMethodManager) mContext
+                .getSystemService(Context.INPUT_METHOD_SERVICE);
+        return inputManager.isAcceptingText();
+    }
+
+    void dismissKeyboardIfPresent() {
+        if(isKeyboardPresent()) {
+            mDevice.pressBack();
+        }
+    }
+
 }
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/UiTestEnvironment.java b/packages/DocumentsUI/tests/src/com/android/documentsui/UiTestEnvironment.java
new file mode 100644
index 0000000..9e30589
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/UiTestEnvironment.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import static com.android.documentsui.StubProvider.DEFAULT_AUTHORITY;
+import static com.android.documentsui.StubProvider.ROOT_0_ID;
+import static com.android.documentsui.StubProvider.ROOT_1_ID;
+
+import android.app.Instrumentation;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.provider.DocumentsContract.Document;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.Configurator;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.Until;
+import android.view.MotionEvent;
+
+import com.android.documentsui.model.RootInfo;
+
+/**
+ * Provides basic test environment for UI tests:
+ * - Launches activity
+ * - Creates and gives access to test root directories and test files
+ * - Cleans up the test environment
+ */
+class UiTestEnvironment {
+
+    public static final int TIMEOUT = 5000;
+    public static final String NO_RENAME = "NO_RENAME";
+
+    public static final String dirName1 = "Dir1";
+    public static final String fileName1 = "file1.log";
+    public static final String fileName2 = "file12.png";
+    public static final String fileName3 = "anotherFile0.log";
+    public static final String fileName4 = "poodles.text";
+    public static final String fileNameNoRename = NO_RENAME + "file.txt";
+
+    private static final String TARGET_PKG = "com.android.documentsui";
+    private static final String LAUNCHER_PKG = "com.android.launcher";
+
+    private final UiBot mBot;
+    private final UiDevice mDevice;
+    private final Context mContext;
+
+    private  RootInfo mRootDir0;
+    private  RootInfo mRootDir1;
+    private int mDocsCountDir0;
+    private int mDocsCountDir1;
+
+    private ContentResolver mResolver;
+    private DocumentsProviderHelper mDocsHelper;
+    private ContentProviderClient mClient;
+
+    private final Instrumentation mInstrumentation;
+
+    public UiTestEnvironment(Instrumentation instrumentation) {
+        mInstrumentation = instrumentation;
+        mDevice = UiDevice.getInstance(mInstrumentation);
+        // NOTE: Must be the "target" context, else security checks in content provider will fail.
+        mContext = mInstrumentation.getTargetContext();
+        mBot = new UiBot(mDevice, mContext, TIMEOUT);
+    }
+
+/**
+ * Launches default activity and waits for the application to appear.
+ * @throws Exception
+ */
+    public void launch() throws Exception {
+        Intent intent = mContext.getPackageManager().getLaunchIntentForPackage(TARGET_PKG);
+        launch(intent);
+    }
+
+    /**
+     * Launches activity specified by intent and waits for the application to appear.
+     * @param intent Intent describing activity to launch.
+     * @throws Exception
+     */
+    public void launch(Intent intent) throws Exception {
+        Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_MOUSE);
+        // Start from the home screen.
+        mDevice.pressHome();
+        mDevice.wait(Until.hasObject(By.pkg(LAUNCHER_PKG).depth(0)), TIMEOUT);
+
+        mResolver = mContext.getContentResolver();
+        mClient = mResolver.acquireUnstableContentProviderClient(DEFAULT_AUTHORITY);
+        mDocsHelper = new DocumentsProviderHelper(DEFAULT_AUTHORITY, mClient);
+
+        // Launch app.
+        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(intent);
+        // Wait for the app to appear.
+        mDevice.wait(Until.hasObject(By.pkg(TARGET_PKG).depth(0)), TIMEOUT);
+        mDevice.waitForIdle();
+
+        resetStorage(); // Just incase a test failed and tearDown didn't happen.
+    }
+
+    public void cleanUp() throws Exception {
+        resetStorage();
+        mClient.release();
+    }
+
+    public void resetStorage() throws RemoteException {
+        mClient.call("clear", null, null);
+        mDevice.waitForIdle();
+    }
+
+    public void initTestFiles() throws RemoteException {
+        mRootDir0 = mDocsHelper.getRoot(ROOT_0_ID);
+        mRootDir1 = mDocsHelper.getRoot(ROOT_1_ID);
+
+        mDocsHelper.createFolder(mRootDir0, dirName1);
+        mDocsHelper.createDocument(mRootDir0, "text/plain", fileName1);
+        mDocsHelper.createDocument(mRootDir0, "image/png", fileName2);
+        mDocsHelper.createDocumentWithFlags(mRootDir0.documentId, "text/plain", fileNameNoRename,
+                Document.FLAG_SUPPORTS_WRITE);
+        mDocsCountDir0 = 4;
+
+        mDocsHelper.createDocument(mRootDir1, "text/plain", fileName3);
+        mDocsHelper.createDocument(mRootDir1, "text/plain", fileName4);
+        mDocsCountDir1 = 2;
+    }
+
+    public void assertDefaultContentOfTestDir0() throws UiObjectNotFoundException {
+        bot().assertDocumentsCount(ROOT_0_ID, getDocumentsCountDir0());
+        bot().assertHasDocuments(UiTestEnvironment.fileName1, UiTestEnvironment.fileName2,
+                UiTestEnvironment.dirName1, UiTestEnvironment.fileNameNoRename);
+    }
+
+    public void assertDefaultContentOfTestDir1() throws UiObjectNotFoundException {
+        bot().assertDocumentsCount(ROOT_1_ID, getDocumentsCountDir1());
+        bot().assertHasDocuments(UiTestEnvironment.fileName3, UiTestEnvironment.fileName4);
+    }
+
+    public UiBot bot() {
+        return mBot;
+    }
+
+    public UiDevice device() {
+        return mDevice;
+    }
+
+    public Context context() {
+        return mContext;
+    }
+
+    public RootInfo getRootDir0() {
+        return mRootDir0;
+    }
+
+    public int getDocumentsCountDir0() {
+        return mDocsCountDir0;
+    }
+
+    public int getDocumentsCountDir1() {
+        return mDocsCountDir1;
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index ed26e0c..3ad6b83 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -654,7 +654,6 @@
                     _intent.getCategories().size() == 1 &&
                     _intent.getData() == null &&
                     _intent.getType() == null &&
-                    (intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
                     !isResolverActivity()) {
                 // This sure looks like a home activity!
                 mActivityType = HOME_ACTIVITY_TYPE;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 577ca48..0e970b3 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1673,11 +1673,37 @@
             TaskRecord task, ActivityRecord r) {
         if (r.fullscreen) {
             if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
-                    + " stackInvisible=" + stackInvisible
-                    + " behindFullscreenActivity=" + behindFullscreenActivity);
+                        + " stackInvisible=" + stackInvisible
+                        + " behindFullscreenActivity=" + behindFullscreenActivity);
             // At this point, nothing else needs to be shown in this task.
             behindFullscreenActivity = true;
-        } else if (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()) {
+        } else if (isHomeStack()) {
+            if (r.isHomeActivity()) {
+                if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home activity: at " + r
+                        + " stackInvisible=" + stackInvisible
+                        + " behindFullscreenActivity=" + behindFullscreenActivity);
+                // No other activity in the home stack should be visible behind the home activity.
+                // Home activities is usually a translucent activity with the wallpaper behind them.
+                // However, when they don't have the wallpaper behind them, we want to show
+                // activities in the next application stack behind them vs. another activity in the
+                // home stack like recents.
+                behindFullscreenActivity = true;
+            } else if (r.isRecentsActivity()
+                    && task.getTaskToReturnTo() == APPLICATION_ACTIVITY_TYPE) {
+                if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
+                        "Recents activity returning to app: at " + r
+                        + " stackInvisible=" + stackInvisible
+                        + " behindFullscreenActivity=" + behindFullscreenActivity);
+                // We don't want any other activities in the home stack visible if the recents
+                // activity is going to be returning to an application activity type.
+                // We do this to preserve the visible order the user used to get into the recents
+                // activity. The recents activity is normally translucent and if it doesn't have
+                // the wallpaper behind it the next activity in the home stack shouldn't be visible
+                // when the home stack is brought to the front to display the recents activity from
+                // an app.
+                behindFullscreenActivity = true;
+            }
+        } else if (r.frontOfTask && task.isOverHomeStack()) {
             if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Showing home: at " + r
                     + " stackInvisible=" + stackInvisible
                     + " behindFullscreenActivity=" + behindFullscreenActivity);
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index b8d94eb..98eebea 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -48,6 +48,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityManagerService.ANIMATE;
+import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
@@ -941,12 +942,8 @@
                 Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
                 return START_RETURN_LOCK_TASK_MODE_VIOLATION;
             }
-            if (!mMovedHome
-                    && (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
-                    == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
-                // Caller wants to appear on home activity, so before starting
-                // their own activity we will bring home to the front.
-                mStartActivity.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+            if (!mMovedHome) {
+                updateTaskReturnToType(mStartActivity.task, mLaunchFlags, topStack);
             }
         } else if (mSourceRecord != null) {
             if (mSupervisor.isLockTaskModeViolation(mSourceRecord.task)) {
@@ -1287,11 +1284,7 @@
                             mOptions, mStartActivity.appTimeTracker, "bringingFoundTaskToFront");
                     mMovedToFront = true;
                 }
-                if ((mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
-                        == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
-                    // Caller wants to appear on home activity.
-                    intentActivity.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
-                }
+                updateTaskReturnToType(intentActivity.task, mLaunchFlags, focusStack);
                 mOptions = null;
             }
         }
@@ -1308,6 +1301,23 @@
         return intentActivity;
     }
 
+    private void updateTaskReturnToType(
+            TaskRecord task, int launchFlags, ActivityStack focusedStack) {
+        if ((launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
+                == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
+            // Caller wants to appear on home activity.
+            task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+            return;
+        } else if (focusedStack == null || focusedStack.mStackId == HOME_STACK_ID) {
+            // Task will be launched over the home stack, so return home.
+            task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+            return;
+        }
+
+        // Else we are coming from an application stack so return to an application.
+        task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
+    }
+
     private void setTaskFromIntentActivity(ActivityRecord intentActivity) {
         if ((mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
                 == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 272f26f..f39ab2f 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -128,8 +128,6 @@
 
     private static final String TASK_THUMBNAIL_SUFFIX = "_task_thumbnail";
 
-    static final boolean IGNORE_RETURN_TO_RECENTS = true;
-
     static final int INVALID_TASK_ID = -1;
 
     final int taskId;       // Unique identifier for this task.
@@ -459,8 +457,8 @@
     }
 
     void setTaskToReturnTo(int taskToReturnTo) {
-        mTaskToReturnTo = (IGNORE_RETURN_TO_RECENTS && taskToReturnTo == RECENTS_ACTIVITY_TYPE)
-            ? HOME_ACTIVITY_TYPE : taskToReturnTo;
+        mTaskToReturnTo = (taskToReturnTo == RECENTS_ACTIVITY_TYPE)
+                ? HOME_ACTIVITY_TYPE : taskToReturnTo;
     }
 
     int getTaskToReturnTo() {
diff --git a/services/core/java/com/android/server/notification/CountdownConditionProvider.java b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
index 07903e9..c1a7ac6 100644
--- a/services/core/java/com/android/server/notification/CountdownConditionProvider.java
+++ b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
@@ -107,11 +107,6 @@
     }
 
     @Override
-    public void onRequestConditions(int relevance) {
-        // by convention
-    }
-
-    @Override
     public void onSubscribe(Uri conditionId) {
         if (DEBUG) Slog.d(TAG, "onSubscribe " + conditionId);
         mTime = ZenModeConfig.tryParseCountdownConditionId(conditionId);
diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java
index a4d5bce..ab3cb83 100644
--- a/services/core/java/com/android/server/notification/EventConditionProvider.java
+++ b/services/core/java/com/android/server/notification/EventConditionProvider.java
@@ -134,12 +134,6 @@
     }
 
     @Override
-    public void onRequestConditions(int relevance) {
-        if (DEBUG) Slog.d(TAG, "onRequestConditions relevance=" + relevance);
-        // does not advertise conditions
-    }
-
-    @Override
     public void onSubscribe(Uri conditionId) {
         if (DEBUG) Slog.d(TAG, "onSubscribe " + conditionId);
         if (!ZenModeConfig.isValidEventConditionId(conditionId)) {
diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
index 44448cc..c9b0ebb 100644
--- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
+++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
@@ -107,12 +107,6 @@
     }
 
     @Override
-    public void onRequestConditions(int relevance) {
-        if (DEBUG) Slog.d(TAG, "onRequestConditions relevance=" + relevance);
-        // does not advertise conditions
-    }
-
-    @Override
     public void onSubscribe(Uri conditionId) {
         if (DEBUG) Slog.d(TAG, "onSubscribe " + conditionId);
         if (!ZenModeConfig.isValidScheduleConditionId(conditionId)) {
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 89e89b0..0eacd13 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -94,7 +94,7 @@
                         continue;
                     }
                     if (!pm.performDexOpt(pkg, /* instruction set */ null, useJitProfiles,
-                            /* extractOnly */ false)) {
+                            /* extractOnly */ false, /* force */ false)) {
                         // there was a problem running dexopt,
                         // remember this so we do not keep retrying.
                         sFailedPackageNames.add(pkg);
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 4d66e10..fe0f141 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -81,7 +81,7 @@
      * {@link PackageManagerService#mInstallLock}.
      */
     int performDexOpt(PackageParser.Package pkg, String[] instructionSets,
-            boolean inclDependencies, boolean useProfiles, boolean extractOnly) {
+            boolean inclDependencies, boolean useProfiles, boolean extractOnly, boolean force) {
         ArraySet<String> done;
         if (inclDependencies && (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null)) {
             done = new ArraySet<String>();
@@ -97,7 +97,7 @@
             }
             try {
                 return performDexOptLI(pkg, instructionSets, done, useProfiles,
-                        extractOnly);
+                        extractOnly, force);
             } finally {
                 if (useLock) {
                     mDexoptWakeLock.release();
@@ -107,7 +107,7 @@
     }
 
     private int performDexOptLI(PackageParser.Package pkg, String[] targetInstructionSets,
-            ArraySet<String> done, boolean useProfiles, boolean extractOnly) {
+            ArraySet<String> done, boolean useProfiles, boolean extractOnly, boolean force) {
         final String[] instructionSets = targetInstructionSets != null ?
                 targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
 
@@ -128,34 +128,38 @@
         final boolean vmSafeMode = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0;
         final boolean debuggable = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
 
+        if (useProfiles) {
+            // If we do a profile guided compilation then we might recompile
+            // the same package if more profile information is available.
+            force = true;
+        }
+
         final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
         boolean performedDexOpt = false;
         final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
         for (String dexCodeInstructionSet : dexCodeInstructionSets) {
-            if (!useProfiles && pkg.mDexOptPerformed.contains(dexCodeInstructionSet)) {
-                // Skip only if we do not use profiles since they might trigger a recompilation.
+            if (!force && pkg.mDexOptPerformed.contains(dexCodeInstructionSet)) {
                 continue;
             }
 
             for (String path : paths) {
                 int dexoptNeeded;
-                try {
-                    dexoptNeeded = DexFile.getDexOptNeeded(path, pkg.packageName,
-                            dexCodeInstructionSet, /* defer */false);
-                } catch (IOException ioe) {
-                    Slog.w(TAG, "IOException reading apk: " + path, ioe);
-                    return DEX_OPT_FAILED;
+
+                if (force) {
+                    dexoptNeeded = DexFile.DEX2OAT_NEEDED;
+                } else {
+                    try {
+                        dexoptNeeded = DexFile.getDexOptNeeded(path, pkg.packageName,
+                                dexCodeInstructionSet, /* defer */false);
+                    } catch (IOException ioe) {
+                        Slog.w(TAG, "IOException reading apk: " + path, ioe);
+                        return DEX_OPT_FAILED;
+                    }
                 }
 
                 if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) {
-                    if (useProfiles) {
-                        // If we do a profile guided compilation then we might recompile the same
-                        // package if more profile information is available.
-                        dexoptNeeded = DexFile.DEX2OAT_NEEDED;
-                    } else {
-                        // No dexopt needed and we don't use profiles. Nothing to do.
-                        continue;
-                    }
+                    // No dexopt needed and we don't use profiles. Nothing to do.
+                    continue;
                 }
                 final String dexoptType;
                 String oatDir = null;
@@ -252,7 +256,7 @@
                 // TODO: Analyze and investigate if we (should) profile libraries.
                 // Currently this will do a full compilation of the library.
                 performDexOptLI(libPkg, instructionSets, done, /*useProfiles*/ false,
-                        /* extractOnly */ false);
+                        /* extractOnly */ false, /* force */ false);
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 45455cf..b6905b4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6639,7 +6639,7 @@
             if (pkgs != null) {
                 for (String pkg : pkgs) {
                     performDexOpt(pkg, null /* instructionSet */, false /* useProfiles */,
-                            true /* extractOnly */);
+                            true /* extractOnly */, false /* force */);
                 }
             }
         }
@@ -6676,26 +6676,28 @@
     @Override
     public boolean performDexOptIfNeeded(String packageName, String instructionSet) {
         return performDexOptTraced(packageName, instructionSet, false /* useProfiles */,
-                false /* extractOnly */);
+                false /* extractOnly */, false /* force */);
     }
 
+    @Override
     public boolean performDexOpt(String packageName, String instructionSet, boolean useProfiles,
-            boolean extractOnly) {
-        return performDexOptTraced(packageName, instructionSet, useProfiles, extractOnly);
+            boolean extractOnly, boolean force) {
+        return performDexOptTraced(packageName, instructionSet, useProfiles, extractOnly, force);
     }
 
     private boolean performDexOptTraced(String packageName, String instructionSet,
-                boolean useProfiles, boolean extractOnly) {
+                boolean useProfiles, boolean extractOnly, boolean force) {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
         try {
-            return performDexOptInternal(packageName, instructionSet, useProfiles, extractOnly);
+            return performDexOptInternal(packageName, instructionSet, useProfiles, extractOnly,
+                    force);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
     }
 
     private boolean performDexOptInternal(String packageName, String instructionSet,
-                boolean useProfiles, boolean extractOnly) {
+                boolean useProfiles, boolean extractOnly, boolean force) {
         PackageParser.Package p;
         final String targetInstructionSet;
         synchronized (mPackages) {
@@ -6707,7 +6709,7 @@
 
             targetInstructionSet = instructionSet != null ? instructionSet :
                     getPrimaryInstructionSet(p.applicationInfo);
-            if (!useProfiles && p.mDexOptPerformed.contains(targetInstructionSet)) {
+            if (!force && !useProfiles && p.mDexOptPerformed.contains(targetInstructionSet)) {
                 // Skip only if we do not use profiles since they might trigger a recompilation.
                 return false;
             }
@@ -6717,7 +6719,7 @@
             synchronized (mInstallLock) {
                 final String[] instructionSets = new String[] { targetInstructionSet };
                 int result = mPackageDexOptimizer.performDexOpt(p, instructionSets,
-                        true /* inclDependencies */, useProfiles, extractOnly);
+                        true /* inclDependencies */, useProfiles, extractOnly, force);
                 return result == PackageDexOptimizer.DEX_OPT_PERFORMED;
             }
         } finally {
@@ -6763,7 +6765,7 @@
             // Don't use profiles since that may cause compilation to be skipped.
             final int res = mPackageDexOptimizer.performDexOpt(pkg, instructionSets,
                     true /* inclDependencies */, false /* useProfiles */,
-                    false /* extractOnly */);
+                    false /* extractOnly */, true /* force */);
 
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
@@ -13031,7 +13033,7 @@
                 // method because `pkg` is not in `mPackages` yet.
                 int result = mPackageDexOptimizer.performDexOpt(pkg, null /* instructionSets */,
                         false /* inclDependencies */, false /* useProfiles */,
-                        true /* extractOnly */);
+                        true /* extractOnly */, false /* force */);
                 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                 if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
                     String msg = "Extracking package failed for " + pkgName;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 43e4b84..d8845d8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -44,6 +44,7 @@
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ShellCommand;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.PrintWriterPrinter;
@@ -96,6 +97,8 @@
                     return runInstallCreate();
                 case "install-write":
                     return runInstallWrite();
+                case "compile":
+                    return runCompile();
                 case "list":
                     return runList();
                 case "uninstall":
@@ -227,6 +230,67 @@
         return doWriteSession(sessionId, path, sizeBytes, splitName, true /*logSuccess*/);
     }
 
+    private int runCompile() throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        boolean useJitProfiles = false;
+        boolean extractOnly = false;
+        boolean forceCompilation = false;
+        String compilationMode = "default";
+
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "-m":
+                    compilationMode = getNextArgRequired();
+                    break;
+                case "-f":
+                    forceCompilation = true;
+                    break;
+                default:
+                    pw.println("Error: Unknown option: " + opt);
+                    return 1;
+            }
+        }
+
+        switch (compilationMode) {
+            case "default":
+                useJitProfiles = SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
+                extractOnly = false;
+                break;
+            case "all":
+                useJitProfiles = false;
+                extractOnly = false;
+                break;
+            case "profile":
+                useJitProfiles = true;
+                extractOnly = false;
+                break;
+            case "extract":
+                useJitProfiles = false;
+                extractOnly = true;
+                break;
+            default:
+                pw.println("Error: Unknown compilation mode: " + compilationMode);
+                return 1;
+        }
+
+        String packageName = getNextArg();
+        if (packageName == null) {
+            pw.println("Error: package name not specified");
+            return 1;
+        }
+
+        boolean success = mInterface.performDexOpt(packageName, null /* instructionSet */,
+                useJitProfiles, extractOnly, forceCompilation);
+        if (success) {
+            pw.println("Success");
+            return 0;
+        } else {
+            pw.println("Failure: package " + packageName + " could not be compiled");
+            return 1;
+        }
+    }
+
     private int runList() throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
         final String type = getNextArg();
@@ -1069,6 +1133,12 @@
         pw.println("  help");
         pw.println("    Print this help text.");
         pw.println("");
+        pw.println("  compile [-m MODE] [-f] TARGET-PACKAGE");
+        pw.println("    Trigger compilation of TARGET-PACKAGE.");
+        pw.println("    Options:");
+        pw.println("      -m: select compilation mode");
+        pw.println("          MODE can be one of \"default\", \"all\", \"profile\", and \"extract\"");
+        pw.println("      -f: force compilation even if not needed");
         pw.println("  list features");
         pw.println("    Prints all features of the system.");
         pw.println("  list instrumentation [-f] [TARGET-PACKAGE]");