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.
+ }
}