Merge "Revert "Redact Account info from getCurrentSyncs""
diff --git a/Android.mk b/Android.mk
index 282b2af..f58caac 100644
--- a/Android.mk
+++ b/Android.mk
@@ -42,6 +42,7 @@
 
 # EventLogTags files.
 LOCAL_SRC_FILES += \
+       core/java/android/auditing/SecurityLogTags.logtags \
        core/java/android/content/EventLogTags.logtags \
        core/java/android/speech/tts/EventLogTags.logtags \
        core/java/android/webkit/EventLogTags.logtags \
diff --git a/api/current.txt b/api/current.txt
index ee4a572..8cd83fd 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6477,6 +6477,30 @@
 
 }
 
+package android.auditing {
+
+  public class SecurityLog {
+    ctor public SecurityLog();
+    field public static final int TAG_ADB_SHELL_CMD = 210002; // 0x33452
+    field public static final int TAG_ADB_SHELL_INTERACTIVE = 210001; // 0x33451
+    field public static final int TAG_APP_PROCESS_START = 210005; // 0x33455
+    field public static final int TAG_DEVICE_LOCKED = 210007; // 0x33457
+    field public static final int TAG_DEVICE_UNLOCK_ATTEMPT = 210006; // 0x33456
+    field public static final int TAG_SYNC_RECV_FILE = 210003; // 0x33453
+    field public static final int TAG_SYNC_SEND_FILE = 210004; // 0x33454
+  }
+
+  public static class SecurityLog.SecurityEvent implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.lang.Object getData();
+    method public int getTag();
+    method public long getTimeNanos();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.auditing.SecurityLog.SecurityEvent> CREATOR;
+  }
+
+}
+
 package android.bluetooth {
 
   public final class BluetoothA2dp implements android.bluetooth.BluetoothProfile {
diff --git a/api/system-current.txt b/api/system-current.txt
index 187fa6b..e6ae46a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6701,6 +6701,30 @@
 
 }
 
+package android.auditing {
+
+  public class SecurityLog {
+    ctor public SecurityLog();
+    field public static final int TAG_ADB_SHELL_CMD = 210002; // 0x33452
+    field public static final int TAG_ADB_SHELL_INTERACTIVE = 210001; // 0x33451
+    field public static final int TAG_APP_PROCESS_START = 210005; // 0x33455
+    field public static final int TAG_DEVICE_LOCKED = 210007; // 0x33457
+    field public static final int TAG_DEVICE_UNLOCK_ATTEMPT = 210006; // 0x33456
+    field public static final int TAG_SYNC_RECV_FILE = 210003; // 0x33453
+    field public static final int TAG_SYNC_SEND_FILE = 210004; // 0x33454
+  }
+
+  public static class SecurityLog.SecurityEvent implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.lang.Object getData();
+    method public int getTag();
+    method public long getTimeNanos();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.auditing.SecurityLog.SecurityEvent> CREATOR;
+  }
+
+}
+
 package android.bluetooth {
 
   public final class BluetoothA2dp implements android.bluetooth.BluetoothProfile {
diff --git a/api/test-current.txt b/api/test-current.txt
index 915fbee..4824f68 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -6479,6 +6479,30 @@
 
 }
 
+package android.auditing {
+
+  public class SecurityLog {
+    ctor public SecurityLog();
+    field public static final int TAG_ADB_SHELL_CMD = 210002; // 0x33452
+    field public static final int TAG_ADB_SHELL_INTERACTIVE = 210001; // 0x33451
+    field public static final int TAG_APP_PROCESS_START = 210005; // 0x33455
+    field public static final int TAG_DEVICE_LOCKED = 210007; // 0x33457
+    field public static final int TAG_DEVICE_UNLOCK_ATTEMPT = 210006; // 0x33456
+    field public static final int TAG_SYNC_RECV_FILE = 210003; // 0x33453
+    field public static final int TAG_SYNC_SEND_FILE = 210004; // 0x33454
+  }
+
+  public static class SecurityLog.SecurityEvent implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.lang.Object getData();
+    method public int getTag();
+    method public long getTimeNanos();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.auditing.SecurityLog.SecurityEvent> CREATOR;
+  }
+
+}
+
 package android.bluetooth {
 
   public final class BluetoothA2dp implements android.bluetooth.BluetoothProfile {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index a7d3669..93122dd 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4325,6 +4325,21 @@
 
         r.activity.mChangingConfigurations = true;
 
+        // If we are preserving the main window across relaunches we would also like to preserve
+        // the children. However the client side view system does not support preserving
+        // the child views so we notify the window manager to expect these windows to
+        // be replaced and defer requests to destroy or hide them. This way we can achieve
+        // visual continuity. It's important that we do this here prior to pause and destroy
+        // as that is when we may hide or remove the child views.
+        try {
+            if (r.mPreserveWindow) {
+                WindowManagerGlobal.getWindowSession().prepareToReplaceChildren(r.token);
+            }
+        } catch (RemoteException e) {
+            // If the system process has died, it's game over for everyone.
+        }
+
+
         // Need to ensure state is saved.
         if (!r.paused) {
             performPauseActivity(r.token, false, r.isPreHoneycomb());
diff --git a/core/java/android/auditing/SecurityLog.java b/core/java/android/auditing/SecurityLog.java
new file mode 100644
index 0000000..87dc1d8
--- /dev/null
+++ b/core/java/android/auditing/SecurityLog.java
@@ -0,0 +1,210 @@
+/*
+ * 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 android.auditing;
+
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemProperties;
+import android.util.EventLog.Event;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
+
+public class SecurityLog {
+
+    private static final String PROPERTY_LOGGING_ENABLED = "persist.logd.security";
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({TAG_ADB_SHELL_INTERACTIVE, TAG_ADB_SHELL_CMD, TAG_SYNC_RECV_FILE, TAG_SYNC_SEND_FILE,
+        TAG_APP_PROCESS_START, TAG_DEVICE_UNLOCK_ATTEMPT, TAG_DEVICE_LOCKED})
+    public @interface SECURITY_LOG_TAG {}
+
+    /**
+     * Indicate that an ADB interactive shell was opened via "adb shell".
+     * There is no extra payload in the log event.
+     */
+    public static final int TAG_ADB_SHELL_INTERACTIVE =
+            SecurityLogTags.SECURITY_ADB_SHELL_INTERACTIVE;
+    /**
+     * Indicate that an shell command was issued over ADB via "adb shell command"
+     * The log entry contains a string data of the shell command, accessible via
+     * {@link SecurityEvent#getData()}
+     */
+    public static final int TAG_ADB_SHELL_CMD = SecurityLogTags.SECURITY_ADB_SHELL_COMMAND;
+    /**
+     * Indicate that a file was pulled from the device via the adb daemon, for example via
+     * "adb pull". The log entry contains a string data of the path of the pulled file,
+     * accessible via {@link SecurityEvent#getData()}
+     */
+    public static final int TAG_SYNC_RECV_FILE = SecurityLogTags.SECURITY_ADB_SYNC_RECV;
+    /**
+     * Indicate that a file was pushed to the device via the adb daemon, for example via
+     * "adb push". The log entry contains a string data of the destination path of the
+     * pushed file, accessible via {@link SecurityEvent#getData()}
+     */
+    public static final int TAG_SYNC_SEND_FILE = SecurityLogTags.SECURITY_ADB_SYNC_SEND;
+    /**
+     * Indicate that an app process was started. The log entry contains the following
+     * information about the process in order, accessible via {@link SecurityEvent#getData()}}:
+     * process name (String), exact start time (long), app Uid (integer), app Pid (integer),
+     * seinfo tag (String), SHA-256 hash of the APK in hexadecimal (String)
+     */
+    public static final int TAG_APP_PROCESS_START = SecurityLogTags.SECURITY_APP_PROCESS_START;
+
+    /**
+     * Indicate that there has been an attempt to unlock the device. The log entry contains the
+     * following information about the attempt in order, accessible via
+     * {@link SecurityEvent#getData()}}: unlock result (integer, 1 for successful unlock, 0 for
+     * unsuccessful), unlock method (String)
+     */
+    public static final int TAG_DEVICE_UNLOCK_ATTEMPT =
+            SecurityLogTags.SECURITY_DEVICE_UNLOCK_ATTEMPT;
+
+    /**
+     * Indicate that the device has been locked, either by user or by timeout.
+     */
+    public static final int TAG_DEVICE_LOCKED = SecurityLogTags.SECURITY_DEVICE_LOCKED;
+
+
+    /**
+     * Returns if device logging is enabled. Log producers should only write new logs if this is
+     * true. Under the hood this is the logical AND of whether device owner exists and whether
+     * it enables logging by setting the system property {@link #PROPERTY_LOGGING_ENABLED}.
+     * @hide
+     */
+    public static native boolean isLoggingEnabled();
+
+    /**
+     * @hide
+     */
+    public static void setLoggingEnabledProperty(boolean enabled) {
+        SystemProperties.set(PROPERTY_LOGGING_ENABLED, enabled ? "true" : "false");
+    }
+
+    /**
+     * @hide
+     */
+    public static boolean getLoggingEnabledProperty() {
+        return SystemProperties.getBoolean(PROPERTY_LOGGING_ENABLED, false);
+    }
+
+    /**
+     * A class representing a security event log entry.
+     */
+    public static class SecurityEvent implements Parcelable {
+        private Event mEvent;
+
+        /** @hide */
+        /*package*/ SecurityEvent(byte[] data) {
+            mEvent = Event.fromBytes(data);
+        }
+
+        /**
+         * Returns the timestamp in nano seconds when this event was logged.
+         */
+        public long getTimeNanos() {
+            return mEvent.getTimeNanos();
+        }
+
+        /**
+         * Returns the tag of this log entry, which specifies entry's semantics.
+         * Could be one of {@link SecurityLog#TAG_SYNC_RECV_FILE},
+         * {@link SecurityLog#TAG_SYNC_SEND_FILE}, {@link SecurityLog#TAG_ADB_SHELL_CMD},
+         * {@link SecurityLog#TAG_ADB_SHELL_INTERACTIVE}, {@link SecurityLog#TAG_APP_PROCESS_START}.
+         */
+        public @SECURITY_LOG_TAG int getTag() {
+            return mEvent.getTag();
+        }
+
+        /**
+         * Returns the payload contained in this log. Each call to this method will
+         * retrieve the next payload item. If no more payload exists, it returns {@code null}.
+         */
+        public Object getData() {
+            return mEvent.getData();
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeByteArray(mEvent.getBytes());
+        }
+
+        public static final Parcelable.Creator<SecurityEvent> CREATOR =
+                new Parcelable.Creator<SecurityEvent>() {
+            @Override
+            public SecurityEvent createFromParcel(Parcel source) {
+                return new SecurityEvent(source.createByteArray());
+            }
+
+            @Override
+            public SecurityEvent[] newArray(int size) {
+                return new SecurityEvent[size];
+            }
+        };
+    }
+    /**
+     * Retrieve all security logs and return immediately.
+     * @hide
+     */
+    public static native void readEvents(Collection<SecurityEvent> output) throws IOException;
+
+    /**
+     * Retrieve all security logs since the given timestamp in nanoseconds and return immediately.
+     * @hide
+     */
+    public static native void readEventsSince(long timestamp, Collection<SecurityEvent> output)
+            throws IOException;
+
+    /**
+     * Retrieve all security logs before the last reboot. May return corrupted data due to
+     * unreliable pstore.
+     * @hide
+     */
+    public static native void readPreviousEvents(Collection<SecurityEvent> output)
+            throws IOException;
+
+    /**
+     * Retrieve all security logs whose timestamp (in nanosceonds) is equal to or greater than the
+     * given timestamp. This method will block until either the last log earlier than the given
+     * timestamp is about to be pruned, or after a 2-hour timeout has passed.
+     * @hide
+     */
+    public static native void readEventsOnWrapping(long timestamp, Collection<SecurityEvent> output)
+            throws IOException;
+
+    /**
+     * Write a log entry to the underlying storage, with a string payload.
+     * @hide
+     */
+    public static native int writeEvent(int tag, String str);
+
+    /**
+     * Write a log entry to the underlying storage, with several payloads.
+     * Supported types of payload are: integer, long, float, string plus array of supported types.
+     * @hide
+     */
+    public static native int writeEvent(int tag, Object... payloads);
+}
diff --git a/core/java/android/auditing/SecurityLogTags.logtags b/core/java/android/auditing/SecurityLogTags.logtags
new file mode 100644
index 0000000..455acff
--- /dev/null
+++ b/core/java/android/auditing/SecurityLogTags.logtags
@@ -0,0 +1,11 @@
+# See system/core/logcat/event.logtags for a description of the format of this file.
+
+option java_package android.auditing
+
+210001 security_adb_shell_interactive
+210002 security_adb_shell_command        (command|3)
+210003 security_adb_sync_recv            (path|3)
+210004 security_adb_sync_send            (path|3)
+210005 security_app_process_start        (process|3),(start_time|2|3),(uid|1),(pid|1),(seinfo|3),(sha256|3)
+210006 security_device_unlock_attempt    (success|1),(method|3)
+210007 security_device_locked
\ No newline at end of file
diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java
index 6bda83d2..6196a97 100644
--- a/core/java/android/util/EventLog.java
+++ b/core/java/android/util/EventLog.java
@@ -23,6 +23,7 @@
 import java.nio.BufferUnderflowException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.regex.Matcher;
@@ -161,6 +162,17 @@
                 throw new IllegalArgumentException("Unknown entry type: " + type);
             }
         }
+
+        /** @hide */
+        public static Event fromBytes(byte[] data) {
+            return new Event(data);
+        }
+
+        /** @hide */
+        public byte[] getBytes() {
+            byte[] bytes = mBuffer.array();
+            return Arrays.copyOf(bytes, bytes.length);
+        }
     }
 
     // We assume that the native methods deal with any concurrency issues.
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index bea36c0..1a5de7e 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -123,6 +123,14 @@
     void repositionChild(IWindow childWindow, int left, int top, int right, int bottom,
             long deferTransactionUntilFrame, out Rect outFrame);
 
+    /*
+     * Notify the window manager that an application is relaunching and
+     * child windows should be prepared for replacement.
+     *
+     * @param appToken The application
+     */
+    void prepareToReplaceChildren(IBinder appToken);
+
     /**
      * If a call to relayout() asked to have the surface destroy deferred,
      * it must call this once it is okay to destroy that surface.
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 9647345..0ed2299 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2203,6 +2203,17 @@
      * by changing its extra value. The different subtype won't get affected by the stored past
      * status. (You may want to take a look at {@link InputMethodSubtype#hashCode()} to refer
      * to the current implementation.)
+     *
+     * <p>NOTE: If the same subtype exists in both the manifest XML file and additional subtypes
+     * specified by {@code subtypes}, those multiple instances are automatically merged into one
+     * instance.</p>
+     *
+     * <p>CAVEAT: In API Level 23 and prior, the system may do nothing if an empty
+     * {@link InputMethodSubtype} is specified in {@code subtypes}, which prevents you from removing
+     * the last one entry of additional subtypes. If your IME statically defines one or more
+     * subtypes in the manifest XML file, you may be able to work around this limitation by
+     * specifying one of those statically defined subtypes in {@code subtypes}.</p>
+     *
      * @param imiId Id of InputMethodInfo which additional input method subtypes will be added to.
      * @param subtypes subtypes will be added as additional subtypes of the current input method.
      */
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 30593f2..a79183d 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -33,6 +33,7 @@
     com_google_android_gles_jni_EGLImpl.cpp \
     com_google_android_gles_jni_GLImpl.cpp.arm \
     android_app_NativeActivity.cpp \
+    android_auditing_SecurityLog.cpp \
     android_opengl_EGL14.cpp \
     android_opengl_EGLExt.cpp \
     android_opengl_GLES10.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 8e11e06..321440c 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -108,6 +108,7 @@
  * JNI-based registration functions.  Note these are properly contained in
  * namespace android.
  */
+extern int register_android_auditing_SecurityLog(JNIEnv* env);
 extern int register_android_content_AssetManager(JNIEnv* env);
 extern int register_android_util_EventLog(JNIEnv* env);
 extern int register_android_util_Log(JNIEnv* env);
@@ -1249,6 +1250,7 @@
     REG_JNI(register_android_util_EventLog),
     REG_JNI(register_android_util_Log),
     REG_JNI(register_android_util_PathParser),
+    REG_JNI(register_android_auditing_SecurityLog),
     REG_JNI(register_android_content_AssetManager),
     REG_JNI(register_android_content_StringBlock),
     REG_JNI(register_android_content_XmlBlock),
diff --git a/core/jni/android_auditing_SecurityLog.cpp b/core/jni/android_auditing_SecurityLog.cpp
new file mode 100644
index 0000000..78f04cd
--- /dev/null
+++ b/core/jni/android_auditing_SecurityLog.cpp
@@ -0,0 +1,312 @@
+/*
+ * 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.
+ */
+
+#include <fcntl.h>
+
+#include "JNIHelp.h"
+#include "core_jni_helpers.h"
+#include "jni.h"
+#include "log/logger.h"
+
+// The size of the tag number comes out of the payload size.
+#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
+
+namespace android {
+
+static jclass gCollectionClass;
+static jmethodID gCollectionAddID;
+
+static jclass gEventClass;
+static jmethodID gEventInitID;
+
+static jclass gIntegerClass;
+static jfieldID gIntegerValueID;
+
+static jclass gLongClass;
+static jfieldID gLongValueID;
+
+static jclass gFloatClass;
+static jfieldID gFloatValueID;
+
+static jclass gStringClass;
+
+
+static jboolean android_auditing_SecurityLog_isLoggingEnabled(JNIEnv* env,
+                                                    jobject /* clazz */) {
+    return (bool)__android_log_security();
+}
+
+static jint android_auditing_SecurityLog_writeEvent_String(JNIEnv* env,
+                                                    jobject /* clazz */,
+                                                    jint tag, jstring value) {
+    uint8_t buf[MAX_EVENT_PAYLOAD];
+
+    // Don't throw NPE -- I feel like it's sort of mean for a logging function
+    // to be all crashy if you pass in NULL -- but make the NULL value explicit.
+    const char *str = value != NULL ? env->GetStringUTFChars(value, NULL) : "NULL";
+    uint32_t len = strlen(str);
+    size_t max = sizeof(buf) - sizeof(len) - 2;  // Type byte, final newline
+    if (len > max) len = max;
+
+    buf[0] = EVENT_TYPE_STRING;
+    memcpy(&buf[1], &len, sizeof(len));
+    memcpy(&buf[1 + sizeof(len)], str, len);
+    buf[1 + sizeof(len) + len] = '\n';
+
+    if (value != NULL) env->ReleaseStringUTFChars(value, str);
+    return __android_log_security_bwrite(tag, buf, 2 + sizeof(len) + len);
+}
+
+static jint android_auditing_SecurityLog_writeEvent_Array(JNIEnv* env, jobject clazz,
+                                                   jint tag, jobjectArray value) {
+    if (value == NULL) {
+        return android_auditing_SecurityLog_writeEvent_String(env, clazz, tag, NULL);
+    }
+
+    uint8_t buf[MAX_EVENT_PAYLOAD];
+    const size_t max = sizeof(buf) - 1;  // leave room for final newline
+    size_t pos = 2;  // Save room for type tag & array count
+
+    jsize copied = 0, num = env->GetArrayLength(value);
+    for (; copied < num && copied < 255; ++copied) {
+        jobject item = env->GetObjectArrayElement(value, copied);
+        if (item == NULL || env->IsInstanceOf(item, gStringClass)) {
+            if (pos + 1 + sizeof(jint) > max) break;
+            const char *str = item != NULL ? env->GetStringUTFChars((jstring) item, NULL) : "NULL";
+            jint len = strlen(str);
+            if (pos + 1 + sizeof(len) + len > max) len = max - pos - 1 - sizeof(len);
+            buf[pos++] = EVENT_TYPE_STRING;
+            memcpy(&buf[pos], &len, sizeof(len));
+            memcpy(&buf[pos + sizeof(len)], str, len);
+            pos += sizeof(len) + len;
+            if (item != NULL) env->ReleaseStringUTFChars((jstring) item, str);
+        } else if (env->IsInstanceOf(item, gIntegerClass)) {
+            jint intVal = env->GetIntField(item, gIntegerValueID);
+            if (pos + 1 + sizeof(intVal) > max) break;
+            buf[pos++] = EVENT_TYPE_INT;
+            memcpy(&buf[pos], &intVal, sizeof(intVal));
+            pos += sizeof(intVal);
+        } else if (env->IsInstanceOf(item, gLongClass)) {
+            jlong longVal = env->GetLongField(item, gLongValueID);
+            if (pos + 1 + sizeof(longVal) > max) break;
+            buf[pos++] = EVENT_TYPE_LONG;
+            memcpy(&buf[pos], &longVal, sizeof(longVal));
+            pos += sizeof(longVal);
+        } else if (env->IsInstanceOf(item, gFloatClass)) {
+            jfloat floatVal = env->GetFloatField(item, gFloatValueID);
+            if (pos + 1 + sizeof(floatVal) > max) break;
+            buf[pos++] = EVENT_TYPE_FLOAT;
+            memcpy(&buf[pos], &floatVal, sizeof(floatVal));
+            pos += sizeof(floatVal);
+        } else {
+            jniThrowException(env,
+                    "java/lang/IllegalArgumentException",
+                    "Invalid payload item type");
+            return -1;
+        }
+        env->DeleteLocalRef(item);
+    }
+
+    buf[0] = EVENT_TYPE_LIST;
+    buf[1] = copied;
+    buf[pos++] = '\n';
+    return __android_log_security_bwrite(tag, buf, pos);
+}
+
+static void readEvents(JNIEnv* env, int loggerMode, jlong startTime, jobject out) {
+    struct logger_list *logger_list;
+    if (startTime) {
+        logger_list = android_logger_list_alloc_time(loggerMode,
+                log_time(startTime / NS_PER_SEC, startTime % NS_PER_SEC), 0);
+    } else {
+        logger_list = android_logger_list_alloc(loggerMode, 0, 0);
+    }
+    if (!logger_list) {
+        jniThrowIOException(env, errno);
+        return;
+    }
+
+    if (!android_logger_open(logger_list, LOG_ID_SECURITY)) {
+        jniThrowIOException(env, errno);
+        android_logger_list_free(logger_list);
+        return;
+    }
+
+    while (1) {
+        log_msg log_msg;
+        int ret = android_logger_list_read(logger_list, &log_msg);
+
+        if (ret == 0) {
+            break;
+        }
+        if (ret < 0) {
+            if (ret == -EINTR) {
+                continue;
+            }
+            if (ret == -EINVAL) {
+                jniThrowException(env, "java/io/IOException", "Event too short");
+            } else if (ret != -EAGAIN) {
+                jniThrowIOException(env, -ret);  // Will throw on return
+            }
+            break;
+        }
+
+        if (log_msg.id() != LOG_ID_SECURITY) {
+            continue;
+        }
+
+        jsize len = ret;
+        jbyteArray array = env->NewByteArray(len);
+        if (array == NULL) {
+            break;
+        }
+
+        jbyte *bytes = env->GetByteArrayElements(array, NULL);
+        memcpy(bytes, log_msg.buf, len);
+        env->ReleaseByteArrayElements(array, bytes, 0);
+
+        jobject event = env->NewObject(gEventClass, gEventInitID, array);
+        if (event == NULL) {
+            break;
+        }
+
+        env->CallBooleanMethod(out, gCollectionAddID, event);
+        env->DeleteLocalRef(event);
+        env->DeleteLocalRef(array);
+    }
+
+    android_logger_list_close(logger_list);
+}
+
+static void android_auditing_SecurityLog_readEvents(JNIEnv* env, jobject /* clazz */,
+                                             jobject out) {
+
+    if (out == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return;
+    }
+    readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, out);
+}
+
+static void android_auditing_SecurityLog_readEventsSince(JNIEnv* env, jobject /* clazz */,
+                                             jlong timestamp,
+                                             jobject out) {
+
+    if (out == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return;
+    }
+    readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, timestamp, out);
+}
+
+static void android_auditing_SecurityLog_readPreviousEvents(JNIEnv* env, jobject /* clazz */,
+                                             jobject out) {
+
+    if (out == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return;
+    }
+    readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_PSTORE, 0, out);
+}
+
+static void android_auditing_SecurityLog_readEventsOnWrapping(JNIEnv* env, jobject /* clazz */,
+                                             jlong timestamp,
+                                             jobject out) {
+    if (out == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return;
+    }
+    readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP, timestamp, out);
+}
+
+/*
+ * JNI registration.
+ */
+static const JNINativeMethod gRegisterMethods[] = {
+    /* name, signature, funcPtr */
+    { "isLoggingEnabled",
+      "()Z",
+      (void*) android_auditing_SecurityLog_isLoggingEnabled
+    },
+    { "writeEvent",
+      "(ILjava/lang/String;)I",
+      (void*) android_auditing_SecurityLog_writeEvent_String
+    },
+    { "writeEvent",
+      "(I[Ljava/lang/Object;)I",
+      (void*) android_auditing_SecurityLog_writeEvent_Array
+    },
+    { "readEvents",
+      "(Ljava/util/Collection;)V",
+      (void*) android_auditing_SecurityLog_readEvents
+    },
+    { "readEventsSince",
+      "(JLjava/util/Collection;)V",
+      (void*) android_auditing_SecurityLog_readEventsSince
+    },
+    { "readPreviousEvents",
+      "(Ljava/util/Collection;)V",
+      (void*) android_auditing_SecurityLog_readPreviousEvents
+    },
+    { "readEventsOnWrapping",
+      "(JLjava/util/Collection;)V",
+      (void*) android_auditing_SecurityLog_readEventsOnWrapping
+    },
+};
+
+static struct { const char *name; jclass *clazz; } gClasses[] = {
+    { "android/auditing/SecurityLog$SecurityEvent", &gEventClass },
+    { "java/lang/Integer", &gIntegerClass },
+    { "java/lang/Long", &gLongClass },
+    { "java/lang/Float", &gFloatClass },
+    { "java/lang/String", &gStringClass },
+    { "java/util/Collection", &gCollectionClass },
+};
+
+static struct { jclass *c; const char *name, *ft; jfieldID *id; } gFields[] = {
+    { &gIntegerClass, "value", "I", &gIntegerValueID },
+    { &gLongClass, "value", "J", &gLongValueID },
+    { &gFloatClass, "value", "F", &gFloatValueID },
+};
+
+static struct { jclass *c; const char *name, *mt; jmethodID *id; } gMethods[] = {
+    { &gEventClass, "<init>", "([B)V", &gEventInitID },
+    { &gCollectionClass, "add", "(Ljava/lang/Object;)Z", &gCollectionAddID },
+};
+
+int register_android_auditing_SecurityLog(JNIEnv* env) {
+    for (int i = 0; i < NELEM(gClasses); ++i) {
+        jclass clazz = FindClassOrDie(env, gClasses[i].name);
+        *gClasses[i].clazz = MakeGlobalRefOrDie(env, clazz);
+    }
+
+    for (int i = 0; i < NELEM(gFields); ++i) {
+        *gFields[i].id = GetFieldIDOrDie(env,
+                *gFields[i].c, gFields[i].name, gFields[i].ft);
+    }
+
+    for (int i = 0; i < NELEM(gMethods); ++i) {
+        *gMethods[i].id = GetMethodIDOrDie(env,
+                *gMethods[i].c, gMethods[i].name, gMethods[i].mt);
+    }
+
+    return RegisterMethodsOrDie(
+            env,
+            "android/auditing/SecurityLog",
+            gRegisterMethods, NELEM(gRegisterMethods));
+}
+
+}; // namespace android
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index be54496..0fb8daf 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -38,24 +38,21 @@
 import android.provider.DocumentsContract.Root;
 import android.support.annotation.LayoutRes;
 import android.support.annotation.Nullable;
-import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnFocusChangeListener;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemSelectedListener;
 import android.widget.BaseAdapter;
 import android.widget.ImageView;
-import android.widget.SearchView;
-import android.widget.SearchView.OnQueryTextListener;
 import android.widget.TextView;
 
 import com.android.documentsui.RecentsProvider.ResumeColumns;
+import com.android.documentsui.SearchManager;
+import com.android.documentsui.SearchManager.SearchManagerListener;
 import com.android.documentsui.dirlist.DirectoryFragment;
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.DocumentStack;
@@ -72,7 +69,7 @@
 import java.util.List;
 import java.util.concurrent.Executor;
 
-public abstract class BaseActivity extends Activity {
+public abstract class BaseActivity extends Activity implements SearchManagerListener {
 
     static final String EXTRA_STATE = "state";
 
@@ -91,7 +88,7 @@
     public abstract void onDocumentsPicked(List<DocumentInfo> docs);
 
     abstract void onTaskFinished(Uri... uris);
-    abstract void onDirectoryChanged(int anim);
+    abstract void refreshDirectory(int anim);
     abstract void updateActionBar();
     abstract void saveStackBlocking();
     abstract State buildState();
@@ -121,7 +118,7 @@
                     }
                 });
         mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
-        mSearchManager = new SearchManager();
+        mSearchManager = new SearchManager(this);
 
         // Base classes must update result in their onCreate.
         setResult(Activity.RESULT_CANCELED);
@@ -213,7 +210,7 @@
         // Otherwise we delegate loading data from disk to a task
         // to ensure a responsive ui.
         if (mRoots.isRecentsRoot(root)) {
-            onCurrentDirectoryChanged(ANIM_SIDE);
+            refreshCurrentRootAndDirectory(ANIM_SIDE);
         } else {
             new PickRootTask(root, true).executeOnExecutor(getExecutorForCurrentDirectory());
         }
@@ -319,19 +316,19 @@
     void openContainerDocument(DocumentInfo doc) {
         checkArgument(doc.isContainer());
         mState.pushDocument(doc);
-        onCurrentDirectoryChanged(ANIM_DOWN);
+        refreshCurrentRootAndDirectory(ANIM_DOWN);
     }
 
     /**
-     * Call this when directory changes. Prior to root fragment update
-     * the (abstract) directoryChanged method will be called.
+     * Refreshes the content of the director and the menu/action bar.
+     * The current directory name and selection will get updated.
      * @param anim
      */
-    // TODO: Refactor the usage of the method - now it is called not only when the directory
-    // changed, but also to refresh the content of the directory while searching
-    final void onCurrentDirectoryChanged(int anim) {
+    final void refreshCurrentRootAndDirectory(int anim) {
+        mSearchManager.cancelSearch();
+
         mDirectoryContainer.setDrawDisappearingFirst(anim == ANIM_DOWN);
-        onDirectoryChanged(anim);
+        refreshDirectory(anim);
 
         final RootsFragment roots = RootsFragment.get(getFragmentManager());
         if (roots != null) {
@@ -340,10 +337,28 @@
 
         updateActionBar();
 
-        // Prevents searchView from being recreated while searching
-        if (!mSearchManager.isSearching()) {
-            invalidateOptionsMenu();
-        }
+        invalidateOptionsMenu();
+    }
+
+    /**
+     * Called when search results changed.
+     * Refreshes the content of the directory. It doesn't refresh elements on the action bar.
+     * e.g. The current directory name displayed on the action bar won't get updated.
+     */
+    @Override
+    public void onSearchChanged() {
+        mDirectoryContainer.setDrawDisappearingFirst(false);
+        refreshDirectory(ANIM_NONE);
+    }
+
+    /**
+     * Called when search query changed.
+     * Updates the state object.
+     * @param query - New query
+     */
+    @Override
+    public void onSearchQueryChanged(String query) {
+        mState.currentSearch = query;
     }
 
     final List<String> getExcludedAuthorities() {
@@ -479,7 +494,7 @@
             mDrawer.setOpen(false);
         } else if (size > 1) {
             mState.stack.pop();
-            onCurrentDirectoryChanged(ANIM_UP);
+            refreshCurrentRootAndDirectory(ANIM_UP);
         } else {
             super.onBackPressed();
         }
@@ -490,7 +505,7 @@
             // Update the restored stack to ensure we have freshest data
             stack.updateDocuments(getContentResolver());
             mState.setStack(stack);
-            onCurrentDirectoryChanged(ANIM_SIDE);
+            refreshCurrentRootAndDirectory(ANIM_SIDE);
 
         } catch (FileNotFoundException e) {
             Log.w(mTag, "Failed to restore stack: " + e);
@@ -579,7 +594,7 @@
         protected void onPostExecute(Void result) {
             if (isDestroyed()) return;
             mState.restored = true;
-            onCurrentDirectoryChanged(ANIM_NONE);
+            refreshCurrentRootAndDirectory(ANIM_NONE);
             onStackRestored(mRestoredStack, mExternal);
         }
     }
@@ -659,7 +674,7 @@
             while (mState.stack.size() > position + 1) {
                 mState.popDocument();
             }
-            onCurrentDirectoryChanged(ANIM_UP);
+            refreshCurrentRootAndDirectory(ANIM_UP);
         }
 
         @Override
@@ -732,170 +747,6 @@
     }
 
     /**
-     * Facade over the various search parts in the menu.
-     */
-    final class SearchManager implements
-            SearchView.OnCloseListener, OnQueryTextListener, OnClickListener, OnFocusChangeListener,
-            DocumentsToolBar.OnActionViewCollapsedListener {
-
-        private boolean mSearchExpanded;
-        private boolean mIgnoreNextClose;
-        private boolean mIgnoreNextCollapse;
-
-        private DocumentsToolBar mActionBar;
-        private MenuItem mMenu;
-        private SearchView mView;
-
-        public void install(DocumentsToolBar actionBar) {
-            assert(mActionBar == null);
-            mActionBar = actionBar;
-            mMenu = actionBar.getSearchMenu();
-            mView = (SearchView) mMenu.getActionView();
-
-            mActionBar.setOnActionViewCollapsedListener(this);
-            mView.setOnQueryTextListener(this);
-            mView.setOnCloseListener(this);
-            mView.setOnSearchClickListener(this);
-            mView.setOnQueryTextFocusChangeListener(this);
-        }
-
-        /**
-         * @param root Info about the current directory.
-         */
-        void update(RootInfo root) {
-            if (mMenu == null) {
-                Log.d(mTag, "update called before Search MenuItem installed.");
-                return;
-            }
-
-            if (mState.currentSearch != null) {
-                mMenu.expandActionView();
-
-                mView.setIconified(false);
-                mView.clearFocus();
-                mView.setQuery(mState.currentSearch, false);
-            } else {
-                mView.clearFocus();
-                if (!mView.isIconified()) {
-                    mIgnoreNextClose = true;
-                    mView.setIconified(true);
-                }
-
-                if (mMenu.isActionViewExpanded()) {
-                    mIgnoreNextCollapse = true;
-                    mMenu.collapseActionView();
-                }
-            }
-
-            showMenu(root != null
-                    && ((root.flags & Root.FLAG_SUPPORTS_SEARCH) != 0));
-        }
-
-        void showMenu(boolean visible) {
-            if (mMenu == null) {
-                Log.d(mTag, "showMenu called before Search MenuItem installed.");
-                return;
-            }
-
-            mMenu.setVisible(visible);
-            if (!visible) {
-                mState.currentSearch = null;
-            }
-        }
-
-        /**
-         * Cancels current search operation.
-         * @return True if it cancels search. False if it does not operate
-         *     search currently.
-         */
-        boolean cancelSearch() {
-            if (isExpanded() || isSearching()) {
-                // If the query string is not empty search view won't get iconified
-                mView.setQuery("", false);
-                mView.setIconified(true);
-                return true;
-            }
-            return false;
-        }
-
-        boolean isSearching() {
-            return mState.currentSearch != null;
-        }
-
-        boolean isExpanded() {
-            return mSearchExpanded;
-        }
-
-        /**
-         * Clears the search.
-         * @return True if the default behavior of clearing/dismissing SearchView should be
-         *      overridden. False otherwise.
-         */
-        @Override
-        public boolean onClose() {
-            mSearchExpanded = false;
-            if (mIgnoreNextClose) {
-                mIgnoreNextClose = false;
-                return false;
-            }
-
-            mView.setBackgroundColor(
-                    getResources().getColor(android.R.color.transparent, null));
-
-            // Refresh the directory if a search was done
-            if(mState.currentSearch != null) {
-                mState.currentSearch = null;
-                onCurrentDirectoryChanged(ANIM_NONE);
-            }
-
-            return false;
-        }
-
-        /**
-         * Sets mSearchExpanded.
-         * Called when search icon is clicked to start search.
-         * Used to detect when the view expanded instead of onMenuItemActionExpand, because
-         * SearchView has showAsAction set to always and onMenuItemAction* methods are not called.
-         */
-        @Override
-        public void onClick (View v) {
-            mSearchExpanded = true;
-            mView.setBackgroundColor(
-                    getResources().getColor(R.color.menu_search_background, null));
-        }
-
-        @Override
-        public boolean onQueryTextSubmit(String query) {
-            mState.currentSearch = query;
-            mView.clearFocus();
-            onCurrentDirectoryChanged(ANIM_NONE);
-            return true;
-        }
-
-        @Override
-        public boolean onQueryTextChange(String newText) {
-            return false;
-        }
-
-        @Override
-        public void onFocusChange(View v, boolean hasFocus) {
-            if(!hasFocus) {
-                if(mState.currentSearch == null) {
-                    mView.setIconified(true);
-                }
-                else if(TextUtils.isEmpty(mView.getQuery())) {
-                    cancelSearch();
-                }
-            }
-        }
-
-        @Override
-        public void onActionViewCollapsed() {
-            updateActionBar();
-        }
-    }
-
-    /**
      * Interface providing access to current view of documents
      * even when all documents are not homed to the same parent.
      */
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 223af89..6dcd472 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -120,7 +120,7 @@
             setTitle("");
             new RestoreStackTask().execute();
         } else {
-            onCurrentDirectoryChanged(ANIM_NONE);
+            refreshCurrentRootAndDirectory(ANIM_NONE);
         }
     }
 
@@ -332,7 +332,7 @@
     }
 
     @Override
-    void onDirectoryChanged(int anim) {
+    void refreshDirectory(int anim) {
         final FragmentManager fm = getFragmentManager();
         final RootInfo root = getCurrentRoot();
         final DocumentInfo cwd = getCurrentDirectory();
@@ -353,12 +353,12 @@
                 mState.derivedMode = mState.userMode;
             }
         } else {
-            if (mState.currentSearch != null) {
+            if (mSearchManager.isSearching()) {
                 // Ongoing search
                 DirectoryFragment.showSearch(fm, root, mState.currentSearch, anim);
             } else {
                 // Normal boring directory
-                DirectoryFragment.showNormal(fm, root, cwd, anim);
+                DirectoryFragment.showDirectory(fm, root, cwd, anim);
             }
         }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java
index b806ced..3302da9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java
@@ -92,7 +92,7 @@
             final Uri rootUri = getIntent().getData();
             new RestoreRootTask(rootUri).executeOnExecutor(getExecutorForCurrentDirectory());
         } else {
-            onCurrentDirectoryChanged(ANIM_NONE);
+            refreshCurrentRootAndDirectory(ANIM_NONE);
         }
     }
 
@@ -164,7 +164,7 @@
     }
 
     @Override
-    void onDirectoryChanged(int anim) {
+    void refreshDirectory(int anim) {
         final FragmentManager fm = getFragmentManager();
         final RootInfo root = getCurrentRoot();
         final DocumentInfo cwd = getCurrentDirectory();
@@ -178,7 +178,7 @@
             DirectoryFragment.showSearch(fm, root, mState.currentSearch, anim);
         } else {
             // Normal boring directory
-            DirectoryFragment.showNormal(fm, root, cwd, anim);
+            DirectoryFragment.showDirectory(fm, root, cwd, anim);
         }
     }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index 0bd09f6..58537ee 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -95,7 +95,7 @@
 
         if (mState.restored) {
             if (DEBUG) Log.d(TAG, "Stack already resolved for uri: " + intent.getData());
-            onCurrentDirectoryChanged(ANIM_NONE);
+            refreshCurrentRootAndDirectory(ANIM_NONE);
         } else if (!mState.stack.isEmpty()) {
             // If a non-empty stack is present in our state it was read (presumably)
             // from EXTRA_STACK intent extra. In this case, we'll skip other means of
@@ -106,7 +106,7 @@
             // don't specify a real content target.
             if (DEBUG) Log.d(TAG, "Launching with non-empty stack.");
             checkState(uri == null || LauncherActivity.isLaunchUri(uri));
-            onCurrentDirectoryChanged(ANIM_NONE);
+            refreshCurrentRootAndDirectory(ANIM_NONE);
         } else if (DocumentsContract.isRootUri(this, uri)) {
             if (DEBUG) Log.d(TAG, "Launching with root URI.");
             // If we've got a specific root to display, restore that root using a dedicated
@@ -288,7 +288,7 @@
     }
 
     @Override
-    void onDirectoryChanged(int anim) {
+    void refreshDirectory(int anim) {
         final FragmentManager fm = getFragmentManager();
         final RootInfo root = getCurrentRoot();
         final DocumentInfo cwd = getCurrentDirectory();
@@ -307,7 +307,7 @@
                 DirectoryFragment.showSearch(fm, root, mState.currentSearch, anim);
             } else {
                 // Normal boring directory
-                DirectoryFragment.showNormal(fm, root, cwd, anim);
+                DirectoryFragment.showDirectory(fm, root, cwd, anim);
             }
         }
     }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SearchManager.java b/packages/DocumentsUI/src/com/android/documentsui/SearchManager.java
new file mode 100644
index 0000000..fb585a6
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/SearchManager.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2013 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 android.provider.DocumentsContract.Root;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnFocusChangeListener;
+import android.widget.SearchView;
+import android.widget.SearchView.OnQueryTextListener;
+
+import com.android.documentsui.model.RootInfo;
+
+/**
+ * Manages searching UI behavior.
+ */
+final class SearchManager implements
+        SearchView.OnCloseListener, OnQueryTextListener, OnClickListener, OnFocusChangeListener {
+
+    public interface SearchManagerListener {
+        void onSearchChanged();
+
+        void onSearchQueryChanged(String query);
+    }
+
+    public static final String TAG = "SearchManger";
+
+    private SearchManagerListener mListener;
+    private String currentSearch;
+    private boolean mSearchExpanded;
+    private boolean mIgnoreNextClose;
+
+    private DocumentsToolBar mActionBar;
+    private MenuItem mMenu;
+    private SearchView mView;
+
+    public SearchManager(SearchManagerListener listener) {
+        mListener = listener;
+    }
+
+    public void setSearchMangerListener(SearchManagerListener listener) {
+        mListener = listener;
+    }
+
+    public void install(DocumentsToolBar actionBar) {
+        assert (mActionBar == null);
+        mActionBar = actionBar;
+        mMenu = actionBar.getSearchMenu();
+        mView = (SearchView) mMenu.getActionView();
+
+        mView.setOnQueryTextListener(this);
+        mView.setOnCloseListener(this);
+        mView.setOnSearchClickListener(this);
+        mView.setOnQueryTextFocusChangeListener(this);
+    }
+
+    /**
+     * @param root Info about the current directory.
+     */
+    void update(RootInfo root) {
+        if (mMenu == null) {
+            Log.d(TAG, "update called before Search MenuItem installed.");
+            return;
+        }
+
+        if (currentSearch != null) {
+            mMenu.expandActionView();
+
+            mView.setIconified(false);
+            mView.clearFocus();
+            mView.setQuery(currentSearch, false);
+        } else {
+            mView.clearFocus();
+            if (!mView.isIconified()) {
+                mIgnoreNextClose = true;
+                mView.setIconified(true);
+            }
+
+            if (mMenu.isActionViewExpanded()) {
+                mMenu.collapseActionView();
+            }
+        }
+
+        showMenu(root != null
+                && ((root.flags & Root.FLAG_SUPPORTS_SEARCH) != 0));
+    }
+
+    void showMenu(boolean visible) {
+        if (mMenu == null) {
+            Log.d(TAG, "showMenu called before Search MenuItem installed.");
+            return;
+        }
+
+        mMenu.setVisible(visible);
+        if (!visible) {
+            currentSearch = null;
+            if (mListener != null) {
+                mListener.onSearchQueryChanged(currentSearch);
+            }
+        }
+    }
+
+    /**
+     * Cancels current search operation. Triggers clearing and collapsing the SearchView.
+     *
+     * @return True if it cancels search. False if it does not operate search currently.
+     */
+    boolean cancelSearch() {
+        if (isExpanded() || isSearching()) {
+            // If the query string is not empty search view won't get iconified
+            mView.setQuery("", false);
+            // Causes calling onClose(). onClose() is triggering directory content update.
+            mView.setIconified(true);
+            return true;
+        }
+        return false;
+    }
+
+    boolean isSearching() {
+        return currentSearch != null;
+    }
+
+    boolean isExpanded() {
+        return mSearchExpanded;
+    }
+
+    /**
+     * Clears the search. Clears the SearchView background color. Triggers refreshing of the
+     * directory content.
+     * @return True if the default behavior of clearing/dismissing SearchView should be overridden.
+     *         False otherwise.
+     */
+    @Override
+    public boolean onClose() {
+        mSearchExpanded = false;
+        if (mIgnoreNextClose) {
+            mIgnoreNextClose = false;
+            return false;
+        }
+
+        mView.setBackgroundColor(
+                mView.getResources().getColor(android.R.color.transparent, null));
+
+        // Refresh the directory if a search was done
+        if (currentSearch != null) {
+            currentSearch = null;
+            if (mListener != null) {
+                mListener.onSearchQueryChanged(currentSearch);
+                mListener.onSearchChanged();
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Sets mSearchExpanded. Called when search icon is clicked to start search. Used to detect when
+     * the view expanded instead of onMenuItemActionExpand, because SearchView has showAsAction set
+     * to always and onMenuItemAction* methods are not called.
+     */
+    @Override
+    public void onClick(View v) {
+        mSearchExpanded = true;
+        mView.setBackgroundColor(
+                mView.getResources().getColor(R.color.menu_search_background, null));
+    }
+
+    @Override
+    public boolean onQueryTextSubmit(String query) {
+        currentSearch = query;
+        mView.clearFocus();
+        if (mListener != null) {
+            mListener.onSearchQueryChanged(currentSearch);
+            mListener.onSearchChanged();
+        }
+        return true;
+    }
+
+    @Override
+    public void onFocusChange(View v, boolean hasFocus) {
+        if (!hasFocus) {
+            if (currentSearch == null) {
+                mView.setIconified(true);
+            } else if (TextUtils.isEmpty(mView.getQuery())) {
+                cancelSearch();
+            }
+        }
+    }
+
+    @Override
+    public boolean onQueryTextChange(String newText) {
+        return false;
+    }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 22e81c6..580e2d8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -163,7 +163,7 @@
     private MessageBar mMessageBar;
     private View mProgressBar;
 
-    public static void showNormal(FragmentManager fm, RootInfo root, DocumentInfo doc, int anim) {
+    public static void showDirectory(FragmentManager fm, RootInfo root, DocumentInfo doc, int anim) {
         show(fm, TYPE_NORMAL, root, doc, null, anim);
     }
 
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java
index baa7a2e..1b8bd4e 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java
@@ -259,12 +259,11 @@
 
         mBot.openRoot(ROOT_1_ID);
 
-        // This assert is failing right now - fix will come with SearchManager refactoring
-        // assertDefaultTestDir1();
-        //
-        // mBot.openRoot(ROOT_0_ID);
-        //
-        // assertDefaultTestDir0();
+         assertDefaultTestDir1();
+
+         mBot.openRoot(ROOT_0_ID);
+
+         assertDefaultTestDir0();
     }
 
     private void assertDefaultTestDir0() throws UiObjectNotFoundException {
@@ -280,7 +279,7 @@
         assertFalse(mSearchTextField.exists());
         assertTrue(mDocsList.exists());
         assertEquals(2, mDocsList.getChildCount());
-        mBot.assertHasDocuments("anotherFile0.log", "poodles.txt");
+        mBot.assertHasDocuments("anotherFile0.log", "poodles.text");
     }
 
     private void assertSearchTextField(boolean isFocused, String query)
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index fabc7b7..d28da41 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -135,7 +135,7 @@
 
     private ObjectAnimator createTranslationAnimation(View v, float newPos) {
         ObjectAnimator anim = ObjectAnimator.ofFloat(v,
-                mSwipeDirection == X ? "translationX" : "translationY", newPos);
+                mSwipeDirection == X ? View.TRANSLATION_X : View.TRANSLATION_Y, newPos);
         return anim;
     }
 
@@ -401,9 +401,17 @@
                 mCallback.onChildSnappedBack(animView);
             }
         });
+        updateSnapBackAnimation(anim);
         anim.start();
     }
 
+    /**
+     * Called to update the snap back animation.
+     */
+    protected void updateSnapBackAnimation(Animator anim) {
+        // Do nothing
+    }
+
     public boolean onTouchEvent(MotionEvent ev) {
         if (mLongPressSent) {
             return true;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index 33f116b..f3c4cc3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -21,10 +21,16 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
+import android.util.ArraySet;
 import android.util.IntProperty;
 import android.util.Property;
 import android.view.View;
 import android.view.ViewParent;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.views.TaskViewTransform;
+
+import java.util.Collections;
+import java.util.List;
 
 /* Common code */
 public class Utilities {
@@ -71,6 +77,28 @@
         return null;
     }
 
+    /**
+     * Initializes the {@param setOut} with the given object.
+     */
+    public static <T> ArraySet<T> objectToSet(T obj, ArraySet<T> setOut) {
+        setOut.clear();
+        if (obj != null) {
+            setOut.add(obj);
+        }
+        return setOut;
+    }
+
+    /**
+     * Replaces the contents of {@param setOut} with the contents of the {@param array}.
+     */
+    public static <T> ArraySet<T> arrayToSet(T[] array, ArraySet<T> setOut) {
+        setOut.clear();
+        if (array != null) {
+            Collections.addAll(setOut, array);
+        }
+        return setOut;
+    }
+
     /** Scales a rect about its centroid */
     public static void scaleRectAboutCenter(RectF r, float scale) {
         if (scale != 1.0f) {
@@ -127,4 +155,22 @@
             animator.cancel();
         }
     }
+
+    /**
+     * Updates {@param transforms} to be the same size as {@param tasks}.
+     */
+    public static void matchTaskListSize(List<Task> tasks, List<TaskViewTransform> transforms) {
+        // We can reuse the task transforms where possible to reduce object allocation
+        int taskTransformCount = transforms.size();
+        int taskCount = tasks.size();
+        if (taskTransformCount < taskCount) {
+            // If there are less transforms than tasks, then add as many transforms as necessary
+            for (int i = taskTransformCount; i < taskCount; i++) {
+                transforms.add(new TaskViewTransform());
+            }
+        } else if (taskTransformCount > taskCount) {
+            // If there are more transforms than tasks, then just subset the transform list
+            transforms.subList(taskCount, taskTransformCount).clear();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java
index 15f6b0a..2109376 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java
@@ -98,7 +98,6 @@
         int taskCount = mTaskKeys.size();
         mFrontMostTaskKey = mTaskKeys.get(mTaskKeys.size() - 1);
         mTaskKeyIndices.clear();
-        mTaskKeyIndices.ensureCapacity(taskCount);
         for (int i = 0; i < taskCount; i++) {
             Task.TaskKey k = mTaskKeys.get(i);
             mTaskKeyIndices.put(k, i);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 21d0bb6..327cdf8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -193,7 +193,6 @@
     private void updateFilteredTaskIndices() {
         int taskCount = mFilteredTasks.size();
         mTaskIndices.clear();
-        mTaskIndices.ensureCapacity(taskCount);
         for (int i = 0; i < taskCount; i++) {
             Task t = mFilteredTasks.get(i);
             mTaskIndices.put(t.key, i);
@@ -841,7 +840,6 @@
             ArrayMap<Task.TaskKey, Task> tasksMap = new ArrayMap<>();
             ArrayList<Task> tasks = mStackTaskList.getTasks();
             int taskCount = tasks.size();
-            tasksMap.ensureCapacity(taskCount);
             for (int i = 0; i < taskCount; i++) {
                 Task t = tasks.get(i);
                 TaskGrouping group;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
index b8bbf51..5a72897 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
@@ -58,6 +58,7 @@
     void setAlpha(float alpha) {
         if (Float.compare(alpha, mAlpha) != 0) {
             mAlpha = alpha;
+            // TODO, If both clip and alpha change in the same frame, only invalidate once
             mSourceView.invalidateOutline();
         }
     }
@@ -79,6 +80,7 @@
                 mSourceView.getHeight() - Math.max(0, mClipRect.bottom));
         if (!mLastClipBounds.equals(mClipBounds)) {
             mSourceView.setClipBounds(mClipBounds);
+            // TODO, If both clip and alpha change in the same frame, only invalidate once
             mSourceView.invalidateOutline();
             mLastClipBounds.set(mClipBounds);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
index d3a1e91..491c4c2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
@@ -48,7 +48,6 @@
     public void update(List<Task> freeformTasks, TaskStackLayoutAlgorithm stackLayout) {
         Collections.reverse(freeformTasks);
         mTaskRectMap.clear();
-        mTaskRectMap.ensureCapacity(freeformTasks.size());
 
         int numFreeformTasks = stackLayout.mNumFreeformTasks;
         if (!freeformTasks.isEmpty()) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index c77a2f9..e727652 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -59,6 +59,7 @@
 import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
 import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
 import com.android.systemui.stackdivider.WindowManagerProxy;
@@ -66,7 +67,6 @@
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
@@ -312,7 +312,7 @@
     @Override
     protected void onAttachedToWindow() {
         EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
-        EventBus.getDefault().register(mTouchHandler, RecentsActivity.EVENT_BUS_PRIORITY + 1);
+        EventBus.getDefault().register(mTouchHandler, RecentsActivity.EVENT_BUS_PRIORITY + 2);
         super.onAttachedToWindow();
     }
 
@@ -665,10 +665,8 @@
     private void updateVisibleDockRegions(TaskStack.DockState[] newDockStates,
             boolean isDefaultDockState, int overrideAlpha, boolean animateAlpha,
             boolean animateBounds) {
-        ArraySet<TaskStack.DockState> newDockStatesSet = new ArraySet<>();
-        if (newDockStates != null) {
-            Collections.addAll(newDockStatesSet, newDockStates);
-        }
+        ArraySet<TaskStack.DockState> newDockStatesSet = Utilities.arrayToSet(newDockStates,
+                new ArraySet<TaskStack.DockState>());
         ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates();
         for (int i = visDockStates.size() - 1; i >= 0; i--) {
             TaskStack.DockState dockState = visDockStates.get(i);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 2fa99ce..68ff63c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -408,7 +408,6 @@
         // Put each of the tasks in the progress map at a fixed index (does not need to actually
         // map to a scroll position, just by index)
         int taskCount = stackTasks.size();
-        mTaskIndexMap.ensureCapacity(taskCount);
         for (int i = 0; i < taskCount; i++) {
             Task task = stackTasks.get(i);
             mTaskIndexMap.put(task.key, i);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 713cfc3..7583a19 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -357,15 +357,7 @@
         int backMostVisibleIndex = -1;
 
         // We can reuse the task transforms where possible to reduce object allocation
-        if (taskTransformCount < taskCount) {
-            // If there are less transforms than tasks, then add as many transforms as necessary
-            for (int i = taskTransformCount; i < taskCount; i++) {
-                taskTransforms.add(new TaskViewTransform());
-            }
-        } else if (taskTransformCount > taskCount) {
-            // If there are more transforms than tasks, then just subset the transform list
-            taskTransforms.subList(0, taskCount);
-        }
+        Utilities.matchTaskListSize(tasks, taskTransforms);
 
         // Update the stack transforms
         TaskViewTransform frontTransform = null;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index a0bb0ef..4813c19 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -16,11 +16,11 @@
 
 package com.android.systemui.recents.views;
 
+import android.animation.Animator;
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Rect;
-import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.InputDevice;
 import android.view.MotionEvent;
@@ -95,6 +95,11 @@
             protected float getSize(View v) {
                 return mSv.getWidth();
             }
+
+            @Override
+            protected void updateSnapBackAnimation(Animator anim) {
+                anim.setInterpolator(mSv.mFastOutSlowInInterpolator);
+            }
         };
         mSwipeHelper.setDisableHardwareLayers(true);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index db4db63..9b72702 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -260,17 +260,23 @@
             if (Float.compare(getTaskProgress(), toTransform.p) != 0) {
                 setTaskProgress(toTransform.p);
             }
+            // Manually call back to the animator listener and update callback
             if (toAnimation.listener != null) {
                 toAnimation.listener.onAnimationEnd(null);
             }
+            if (updateCallback != null) {
+                updateCallback.onAnimationUpdate(null);
+            }
         } else {
             if (Float.compare(getTaskProgress(), toTransform.p) != 0) {
                 mTmpAnimators.add(ObjectAnimator.ofFloat(this, TASK_PROGRESS, getTaskProgress(),
                         toTransform.p));
             }
-            ValueAnimator updateCallbackAnim = ValueAnimator.ofInt(0, 1);
-            updateCallbackAnim.addUpdateListener(updateCallback);
-            mTmpAnimators.add(updateCallbackAnim);
+            if (updateCallback != null) {
+                ValueAnimator updateCallbackAnim = ValueAnimator.ofInt(0, 1);
+                updateCallbackAnim.addUpdateListener(updateCallback);
+                mTmpAnimators.add(updateCallbackAnim);
+            }
 
             // Create the animator
             mTransformAnimation = toAnimation.createAnimator(mTmpAnimators);
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 15b5502..d3fed6d 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -2537,7 +2537,7 @@
         }
         // By this IPC call, only a process which shares the same uid with the IME can add
         // additional input method subtypes to the IME.
-        if (TextUtils.isEmpty(imiId) || subtypes == null || subtypes.length == 0) return;
+        if (TextUtils.isEmpty(imiId) || subtypes == null) return;
         synchronized (mMethodMap) {
             final InputMethodInfo imi = mMethodMap.get(imiId);
             if (imi == null) return;
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 8266c08..f8b6f57 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -1903,8 +1903,10 @@
                             if ("authority".equals(tagName)) {
                                 authority = parseAuthority(parser, version);
                                 periodicSync = null;
-                                if (authority.ident > highestAuthorityId) {
-                                    highestAuthorityId = authority.ident;
+                                if (authority != null) {
+                                    if (authority.ident > highestAuthorityId) {
+                                        highestAuthorityId = authority.ident;
+                                    }
                                 }
                             } else if (XML_TAG_LISTEN_FOR_TICKLES.equals(tagName)) {
                                 parseListenForTickles(parser);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 46240783..785f166 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -405,6 +405,17 @@
         }
     }
 
+    void setReplacingChildren() {
+        if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "Marking app token " + appWindowToken
+                + " with replacing child windows.");
+        for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+            final WindowState w = allAppWindows.get(i);
+            if (w.isChildWindow()) {
+                w.setReplacing(false /* animate */);
+            }
+        }
+    }
+
     void resetReplacingWindows() {
         if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "Resetting app token " + appWindowToken
                 + " of replacing window marks.");
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index ac38424..8aaf430 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -200,6 +200,11 @@
                 deferTransactionUntilFrame, outFrame);
     }
 
+    @Override
+    public void prepareToReplaceChildren(IBinder appToken) {
+        mService.setReplacingChildren(appToken);
+    }
+
     public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
             int requestedWidth, int requestedHeight, int viewFlags,
             int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d05a61a..4b0015e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2693,7 +2693,14 @@
                     final boolean notExitingOrAnimating =
                             !win.mExiting && !win.isAnimatingWithSavedSurface();
                     result |= notExitingOrAnimating ? RELAYOUT_RES_SURFACE_CHANGED : 0;
-                    if (notExitingOrAnimating) {
+                    // We don't want to animate visibility of windows which are pending
+                    // replacement. In the case of activity relaunch child windows
+                    // could request visibility changes as they are detached from the main
+                    // application window during the tear down process. If we satisfied
+                    // these visibility changes though, we would cause a visual glitch
+                    // hiding the window before it's replacement was available.
+                    // So we just do nothing on our side.
+                    if (notExitingOrAnimating && win.mWillReplaceWindow == false) {
                         focusMayChange = tryStartingAnimation(win, winAnimator, isDefaultDisplay,
                                 focusMayChange);
 
@@ -2893,9 +2900,10 @@
         try {
             synchronized (mWindowMap) {
                 WindowState win = windowForClientLocked(session, client, false);
-                if (win == null) {
+                if (win == null || win.mWillReplaceWindow) {
                     return;
                 }
+
                 win.mWinAnimator.destroyDeferredSurfaceLocked();
             }
         } finally {
@@ -10184,6 +10192,27 @@
     }
 
     /**
+     * Hint to a token that its children will be replaced across activity relaunch.
+     * The children would otherwise be removed  shortly following this as the
+     * activity is torn down.
+     * @param token Application token for which the activity will be relaunched.
+     */
+    public void setReplacingChildren(IBinder token) {
+        AppWindowToken appWindowToken = null;
+        synchronized (mWindowMap) {
+            appWindowToken = findAppWindowToken(token);
+            if (appWindowToken == null || !appWindowToken.isVisible()) {
+                Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token "
+                        + token);
+                return;
+            }
+
+            appWindowToken.setReplacingChildren();
+            scheduleClearReplacingWindowIfNeeded(token, true /* replacing */);
+        }
+    }
+
+    /**
      * If we're replacing the window, schedule a timer to clear the replaced window
      * after a timeout, in case the replacing window is not coming.
      *
diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java
index a915d37..a5e8fe1 100644
--- a/telecomm/java/android/telecom/DefaultDialerManager.java
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -21,6 +21,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
+import android.os.Process;
 import android.provider.Settings;
 import android.text.TextUtils;
 
@@ -150,12 +151,13 @@
      *
      * @hide
      **/
-    public static List<String> getInstalledDialerApplications(Context context) {
+    public static List<String> getInstalledDialerApplications(Context context, int userId) {
         PackageManager packageManager = context.getPackageManager();
 
         // Get the list of apps registered for the DIAL intent with empty scheme
         Intent intent = new Intent(Intent.ACTION_DIAL);
-        List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, 0);
+        List<ResolveInfo> resolveInfoList =
+                packageManager.queryIntentActivitiesAsUser(intent, 0, userId);
 
         List<String> packageNames = new ArrayList<>();
 
@@ -171,6 +173,10 @@
         return filterByIntent(context, packageNames, dialIntentWithTelScheme);
     }
 
+    public static List<String> getInstalledDialerApplications(Context context) {
+        return getInstalledDialerApplications(context, Process.myUserHandle().getIdentifier());
+    }
+
     /**
      * Determines if the package name belongs to the user-selected default dialer or the preloaded
      * system dialer, and thus should be allowed to perform certain privileged operations.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index aae5ac4..769285f 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -231,4 +231,9 @@
     public void pokeDrawLock(IBinder window) {
         // pass for now.
     }
+
+    @Override
+    public void prepareToReplaceChildren(IBinder appToken) {
+        // pass for now.
+    }
 }