Merge changes I8e2ed73d,I42aeb522

* changes:
  Implement the request and recommend calls.
  Implement the discovery of a network recommendation provider.
diff --git a/api/current.txt b/api/current.txt
index 89573ac..395d7aa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12733,7 +12733,6 @@
     field public static final int RGBA_8888 = 1; // 0x1
     field public static final int RGBA_F16 = 22; // 0x16
     field public static final int RGBX_8888 = 2; // 0x2
-    field public static final int RGBX_F16 = 23; // 0x17
     field public static final deprecated int RGB_332 = 11; // 0xb
     field public static final int RGB_565 = 4; // 0x4
     field public static final int RGB_888 = 3; // 0x3
diff --git a/api/system-current.txt b/api/system-current.txt
index 870070b..8bedf98 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -13226,7 +13226,6 @@
     field public static final int RGBA_8888 = 1; // 0x1
     field public static final int RGBA_F16 = 22; // 0x16
     field public static final int RGBX_8888 = 2; // 0x2
-    field public static final int RGBX_F16 = 23; // 0x17
     field public static final deprecated int RGB_332 = 11; // 0xb
     field public static final int RGB_565 = 4; // 0x4
     field public static final int RGB_888 = 3; // 0x3
diff --git a/api/test-current.txt b/api/test-current.txt
index 10554ff..e43d1b5 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -12764,7 +12764,6 @@
     field public static final int RGBA_8888 = 1; // 0x1
     field public static final int RGBA_F16 = 22; // 0x16
     field public static final int RGBX_8888 = 2; // 0x2
-    field public static final int RGBX_F16 = 23; // 0x17
     field public static final deprecated int RGB_332 = 11; // 0xb
     field public static final int RGB_565 = 4; // 0x4
     field public static final int RGB_888 = 3; // 0x3
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 74aded6..32bf66a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3789,6 +3789,8 @@
                     ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
         }
 
+        final boolean hasVisibleToEphemeral =
+                sa.hasValue(R.styleable.AndroidManifestActivity_visibleToEphemeral);
         final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
         final boolean visibleToEphemeral = isEphemeral
                 || sa.getBoolean(R.styleable.AndroidManifestActivity_visibleToEphemeral, false);
@@ -3827,7 +3829,7 @@
                     return null;
                 }
                 intent.setEphemeral(isEphemeral);
-                intent.setVisibleToEphemeral(visibleToEphemeral);
+                intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
                 if (intent.countActions() == 0) {
                     Slog.w(TAG, "No actions in intent filter at "
                             + mArchiveSourcePath + " "
@@ -3835,6 +3837,10 @@
                 } else {
                     a.intents.add(intent);
                 }
+                // adjust activity flags when we implicitly expose it via a browsable filter
+                if (!hasVisibleToEphemeral && intent.isVisibleToEphemeral()) {
+                    a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+                }
             } else if (!receiver && parser.getName().equals("preferred")) {
                 ActivityIntentInfo intent = new ActivityIntentInfo(a);
                 if (!parseIntent(res, parser, false /*allowGlobs*/, false /*allowAutoVerify*/,
@@ -3842,7 +3848,7 @@
                     return null;
                 }
                 intent.setEphemeral(isEphemeral);
-                intent.setVisibleToEphemeral(visibleToEphemeral);
+                intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
                 if (intent.countActions() == 0) {
                     Slog.w(TAG, "No actions in preferred at "
                             + mArchiveSourcePath + " "
@@ -3853,6 +3859,10 @@
                     }
                     owner.preferredActivityFilters.add(intent);
                 }
+                // adjust activity flags when we implicitly expose it via a browsable filter
+                if (!hasVisibleToEphemeral && intent.isVisibleToEphemeral()) {
+                    a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+                }
             } else if (parser.getName().equals("meta-data")) {
                 if ((a.metaData = parseMetaData(res, parser, a.metaData,
                         outError)) == null) {
@@ -4093,6 +4103,7 @@
             }
         }
 
+        // TODO add visibleToInstantApp attribute to activity alias
         final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
         final boolean visibleToEphemeral = isEphemeral
                 || ((a.info.flags & ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL) != 0);
@@ -4124,9 +4135,14 @@
                             + parser.getPositionDescription());
                 } else {
                     intent.setEphemeral(isEphemeral);
-                    intent.setVisibleToEphemeral(visibleToEphemeral);
+                    intent.setVisibleToEphemeral(visibleToEphemeral
+                            || isWebBrowsableIntent(intent));
                     a.intents.add(intent);
                 }
+                // adjust activity flags when we implicitly expose it via a browsable filter
+                if (intent.isVisibleToEphemeral()) {
+                    a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+                }
             } else if (parser.getName().equals("meta-data")) {
                 if ((a.metaData=parseMetaData(res, parser, a.metaData,
                         outError)) == null) {
@@ -4262,6 +4278,8 @@
                     ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
         }
 
+        final boolean hasVisibleToEphemeral =
+                sa.hasValue(R.styleable.AndroidManifestProvider_visibleToEphemeral);
         final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
         final boolean visibleToEphemeral = isEphemeral
                 || sa.getBoolean(R.styleable.AndroidManifestProvider_visibleToEphemeral, false);
@@ -4291,7 +4309,8 @@
         }
         p.info.authority = cpname.intern();
 
-        if (!parseProviderTags(res, parser, isEphemeral, visibleToEphemeral, p, outError)) {
+        if (!parseProviderTags(
+                res, parser, isEphemeral, hasVisibleToEphemeral, visibleToEphemeral, p, outError)) {
             return null;
         }
 
@@ -4299,8 +4318,9 @@
     }
 
     private boolean parseProviderTags(Resources res, XmlResourceParser parser,
-            boolean isEphemeral, boolean visibleToEphemeral, Provider outInfo, String[] outError)
-            throws XmlPullParserException, IOException {
+            boolean isEphemeral, boolean hasVisibleToEphemeral, boolean visibleToEphemeral,
+            Provider outInfo, String[] outError)
+                    throws XmlPullParserException, IOException {
         int outerDepth = parser.getDepth();
         int type;
         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -4317,8 +4337,12 @@
                     return false;
                 }
                 intent.setEphemeral(isEphemeral);
-                intent.setVisibleToEphemeral(visibleToEphemeral);
+                intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
                 outInfo.intents.add(intent);
+                // adjust provider flags when we implicitly expose it via a browsable filter
+                if (!hasVisibleToEphemeral && intent.isVisibleToEphemeral()) {
+                    outInfo.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+                }
 
             } else if (parser.getName().equals("meta-data")) {
                 if ((outInfo.metaData=parseMetaData(res, parser,
@@ -4565,6 +4589,8 @@
                     ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
         }
 
+        final boolean hasVisibleToEphemeral =
+                sa.hasValue(R.styleable.AndroidManifestService_visibleToEphemeral);
         final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
         final boolean visibleToEphemeral = isEphemeral
                 || sa.getBoolean(R.styleable.AndroidManifestService_visibleToEphemeral, false);
@@ -4600,8 +4626,11 @@
                     return null;
                 }
                 intent.setEphemeral(isEphemeral);
-                intent.setVisibleToEphemeral(visibleToEphemeral);
-
+                intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
+                // adjust activity flags when we implicitly expose it via a browsable filter
+                if (!hasVisibleToEphemeral && intent.isVisibleToEphemeral()) {
+                    s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+                }
                 s.intents.add(intent);
             } else if (parser.getName().equals("meta-data")) {
                 if ((s.metaData=parseMetaData(res, parser, s.metaData,
@@ -4629,6 +4658,12 @@
         return s;
     }
 
+    private boolean isWebBrowsableIntent(IntentInfo intent) {
+        return intent.hasAction(Intent.ACTION_VIEW)
+                && intent.hasCategory(Intent.CATEGORY_BROWSABLE)
+                && (intent.hasDataScheme("http") || intent.hasDataScheme("https"));
+    }
+
     private boolean parseAllMetaData(Resources res, XmlResourceParser parser, String tag,
             Component<?> outInfo, String[] outError) throws XmlPullParserException, IOException {
         int outerDepth = parser.getDepth();
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index c83298b..7340cf7 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -869,10 +869,13 @@
      */
     @Override
     public View focusSearch(View focused, int direction) {
-        if (isRootNamespace()) {
+        if (isRootNamespace()
+                || isKeyboardNavigationCluster()
+                && (direction == FOCUS_FORWARD || direction == FOCUS_BACKWARD)) {
             // root namespace means we should consider ourselves the top of the
             // tree for focus searching; otherwise we could be focus searching
-            // into other tabs.  see LocalActivityManager and TabHost for more info
+            // into other tabs.  see LocalActivityManager and TabHost for more info.
+            // Cluster's root works same way for the forward and backward navigation.
             return FocusFinder.getInstance().findNextFocus(this, focused, direction);
         } else if (mParent != null) {
             return mParent.focusSearch(focused, direction);
@@ -1104,6 +1107,12 @@
 
     @Override
     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
+        if (isKeyboardNavigationCluster()
+                && (direction == FOCUS_FORWARD || direction == FOCUS_BACKWARD) && !hasFocus()) {
+            // A cluster cannot be focus-entered from outside using forward/backward navigation.
+            return;
+        }
+
         final int focusableCount = views.size();
 
         final int descendantFocusability = getDescendantFocusability();
@@ -3026,7 +3035,8 @@
         final View[] children = mChildren;
         for (int i = index; i != end; i += increment) {
             View child = children[i];
-            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
+            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
+                    && !child.isKeyboardNavigationCluster()) {
                 if (child.requestFocus(direction, previouslyFocusedRect)) {
                     return true;
                 }
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index d968e3c..a8a5549 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -62,6 +62,9 @@
             ClassLoader loader = ApplicationLoaders.getDefault().createAndCacheWebViewClassLoader(
                     packagePath, libsPath);
 
+            // Add the APK to the Zygote's list of allowed files for children.
+            Zygote.nativeAllowFileAcrossFork(packagePath);
+
             // Once we have the classloader, look up the WebViewFactoryProvider implementation and
             // call preloadInZygote() on it to give it the opportunity to preload the native library
             // and perform any other initialisation work that should be shared among the children.
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index fc0ccb7..293de3d 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -153,6 +153,11 @@
             int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
 
     /**
+     * Lets children of the zygote inherit open file descriptors to this path.
+     */
+    native protected static void nativeAllowFileAcrossFork(String path);
+
+    /**
      * Zygote unmount storage space on initializing.
      * This method is called once.
      */
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 35c9ee3..3555057 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -188,6 +188,7 @@
     com_android_internal_util_VirtualRefBasePtr.cpp \
     com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp \
     hwbinder/EphemeralStorage.cpp \
+    fd_utils.cpp \
 
 LOCAL_C_INCLUDES += \
     $(LOCAL_PATH)/include \
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index b656bb0..80711aa 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1190,8 +1190,7 @@
     }
 }
 
-static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle,
-                              jlong bm1Handle) {
+static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle, jlong bm1Handle) {
     SkBitmap bm0;
     SkBitmap bm1;
 
@@ -1207,11 +1206,11 @@
 
     bitmap0->bitmap().getSkBitmap(&bm0);
     bitmap1->bitmap().getSkBitmap(&bm1);
-    if (bm0.width() != bm1.width() ||
-        bm0.height() != bm1.height() ||
-        bm0.colorType() != bm1.colorType() ||
-        bm0.alphaType() != bm1.alphaType() ||
-        bm0.colorSpace() != bm1.colorSpace()) {
+    if (bm0.width() != bm1.width()
+            || bm0.height() != bm1.height()
+            || bm0.colorType() != bm1.colorType()
+            || bm0.alphaType() != bm1.alphaType()
+            || !SkColorSpace::Equals(bm0.colorSpace(), bm1.colorSpace())) {
         return JNI_FALSE;
     }
 
diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
index bb69e27..f18837f 100644
--- a/core/jni/android_view_GraphicBuffer.cpp
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -144,8 +144,6 @@
             return kN32_SkColorType;
         case PIXEL_FORMAT_RGBA_FP16:
             return kRGBA_F16_SkColorType;
-        case PIXEL_FORMAT_RGBX_FP16:
-            return kRGBA_F16_SkColorType;
         case PIXEL_FORMAT_RGB_565:
             return kRGB_565_SkColorType;
         default:
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index a5b7671..96e6f81 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -169,7 +169,6 @@
         case HAL_PIXEL_FORMAT_RGBA_8888:
         case HAL_PIXEL_FORMAT_RGBX_8888:
         case HAL_PIXEL_FORMAT_RGBA_FP16:
-        case HAL_PIXEL_FORMAT_RGBX_FP16:
         case HAL_PIXEL_FORMAT_RGB_888:
         case HAL_PIXEL_FORMAT_RGB_565:
         case HAL_PIXEL_FORMAT_Y8:
@@ -285,7 +284,6 @@
     switch (format) {
     case PIXEL_FORMAT_RGBX_8888:    return kN32_SkColorType;
     case PIXEL_FORMAT_RGBA_8888:    return kN32_SkColorType;
-    case PIXEL_FORMAT_RGBX_FP16:    return kRGBA_F16_SkColorType;
     case PIXEL_FORMAT_RGBA_FP16:    return kRGBA_F16_SkColorType;
     case PIXEL_FORMAT_RGB_565:      return kRGB_565_SkColorType;
     default:                        return kUnknown_SkColorType;
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 62c3d04..ed071cd 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -164,11 +164,6 @@
             alphaType = kPremul_SkAlphaType;
             break;
         }
-        case PIXEL_FORMAT_RGBX_FP16: {
-            colorType = kRGBA_F16_SkColorType;
-            alphaType = kOpaque_SkAlphaType;
-            break;
-        }
         case PIXEL_FORMAT_RGBA_FP16: {
             colorType = kRGBA_F16_SkColorType;
             alphaType = kPremul_SkAlphaType;
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index d3c0202..613e040 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -87,10 +87,6 @@
             colorType = kRGBA_F16_SkColorType;
             alphaType = kPremul_SkAlphaType;
             break;
-        case WINDOW_FORMAT_RGBX_FP16:
-            colorType = kRGBA_F16_SkColorType;
-            alphaType = kOpaque_SkAlphaType;
-            break;
         case WINDOW_FORMAT_RGB_565:
             colorType = kRGB_565_SkColorType;
             alphaType = kOpaque_SkAlphaType;
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index da059e3..cc7b958 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -56,7 +56,7 @@
 #include "ScopedLocalRef.h"
 #include "ScopedPrimitiveArray.h"
 #include "ScopedUtfChars.h"
-#include "fd_utils-inl.h"
+#include "fd_utils.h"
 
 #include "nativebridge/native_bridge.h"
 
@@ -709,6 +709,16 @@
   return pid;
 }
 
+static void com_android_internal_os_Zygote_nativeAllowFileAcrossFork(
+        JNIEnv* env, jclass, jstring path) {
+    ScopedUtfChars path_native(env, path);
+    const char* path_cstr = path_native.c_str();
+    if (!path_cstr) {
+        RuntimeAbort(env, __LINE__, "path_cstr == NULL");
+    }
+    FileDescriptorWhitelist::Get()->Allow(path_cstr);
+}
+
 static void com_android_internal_os_Zygote_nativeUnmountStorageOnInit(JNIEnv* env, jclass) {
     // Zygote process unmount root storage space initially before every child processes are forked.
     // Every forked child processes (include SystemServer) only mount their own root storage space
@@ -753,6 +763,8 @@
       (void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
     { "nativeForkSystemServer", "(II[II[[IJJ)I",
       (void *) com_android_internal_os_Zygote_nativeForkSystemServer },
+    { "nativeAllowFileAcrossFork", "(Ljava/lang/String;)V",
+      (void *) com_android_internal_os_Zygote_nativeAllowFileAcrossFork },
     { "nativeUnmountStorageOnInit", "()V",
       (void *) com_android_internal_os_Zygote_nativeUnmountStorageOnInit }
 };
diff --git a/core/jni/fd_utils-inl.h b/core/jni/fd_utils-inl.h
deleted file mode 100644
index e270911..0000000
--- a/core/jni/fd_utils-inl.h
+++ /dev/null
@@ -1,587 +0,0 @@
-/*
- * 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 <string>
-#include <unordered_map>
-#include <set>
-#include <vector>
-#include <algorithm>
-
-#include <android-base/strings.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <grp.h>
-#include <inttypes.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include <cutils/log.h>
-#include "JNIHelp.h"
-#include "ScopedPrimitiveArray.h"
-
-// Whitelist of open paths that the zygote is allowed to keep open.
-//
-// In addition to the paths listed here, all files ending with
-// ".jar" under /system/framework" are whitelisted. See
-// FileDescriptorInfo::IsWhitelisted for the canonical definition.
-//
-// If the whitelisted path is associated with a regular file or a
-// character device, the file is reopened after a fork with the same
-// offset and mode. If the whilelisted  path is associated with a
-// AF_UNIX socket, the socket will refer to /dev/null after each
-// fork, and all operations on it will fail.
-static const char* kPathWhitelist[] = {
-  "/dev/null",
-  "/dev/socket/zygote",
-  "/dev/socket/zygote_secondary",
-  "/dev/socket/webview_zygote",
-  "/sys/kernel/debug/tracing/trace_marker",
-  "/system/framework/framework-res.apk",
-  "/dev/urandom",
-  "/dev/ion",
-  "/dev/dri/renderD129", // Fixes b/31172436
-};
-
-static const char* kFdPath = "/proc/self/fd";
-
-// Keeps track of all relevant information (flags, offset etc.) of an
-// open zygote file descriptor.
-class FileDescriptorInfo {
- public:
-  // Create a FileDescriptorInfo for a given file descriptor. Returns
-  // |NULL| if an error occurred.
-  static FileDescriptorInfo* createFromFd(int fd) {
-    struct stat f_stat;
-    // This should never happen; the zygote should always have the right set
-    // of permissions required to stat all its open files.
-    if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
-      ALOGE("Unable to stat fd %d : %s", fd, strerror(errno));
-      return NULL;
-    }
-
-    if (S_ISSOCK(f_stat.st_mode)) {
-      std::string socket_name;
-      if (!GetSocketName(fd, &socket_name)) {
-        return NULL;
-      }
-
-      if (!IsWhitelisted(socket_name)) {
-        ALOGE("Socket name not whitelisted : %s (fd=%d)", socket_name.c_str(), fd);
-        return NULL;
-      }
-
-      return new FileDescriptorInfo(fd);
-    }
-
-    // We only handle whitelisted regular files and character devices. Whitelisted
-    // character devices must provide a guarantee of sensible behaviour when
-    // reopened.
-    //
-    // S_ISDIR : Not supported. (We could if we wanted to, but it's unused).
-    // S_ISLINK : Not supported.
-    // S_ISBLK : Not supported.
-    // S_ISFIFO : Not supported. Note that the zygote uses pipes to communicate
-    // with the child process across forks but those should have been closed
-    // before we got to this point.
-    if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) {
-      ALOGE("Unsupported st_mode %d", f_stat.st_mode);
-      return NULL;
-    }
-
-    std::string file_path;
-    if (!Readlink(fd, &file_path)) {
-      return NULL;
-    }
-
-    if (!IsWhitelisted(file_path)) {
-      ALOGE("Not whitelisted : %s", file_path.c_str());
-      return NULL;
-    }
-
-    // File descriptor flags : currently on FD_CLOEXEC. We can set these
-    // using F_SETFD - we're single threaded at this point of execution so
-    // there won't be any races.
-    const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD));
-    if (fd_flags == -1) {
-      ALOGE("Failed fcntl(%d, F_GETFD) : %s", fd, strerror(errno));
-      return NULL;
-    }
-
-    // File status flags :
-    // - File access mode : (O_RDONLY, O_WRONLY...) we'll pass these through
-    //   to the open() call.
-    //
-    // - File creation flags : (O_CREAT, O_EXCL...) - there's not much we can
-    //   do about these, since the file has already been created. We shall ignore
-    //   them here.
-    //
-    // - Other flags : We'll have to set these via F_SETFL. On linux, F_SETFL
-    //   can only set O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK.
-    //   In particular, it can't set O_SYNC and O_DSYNC. We'll have to test for
-    //   their presence and pass them in to open().
-    int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
-    if (fs_flags == -1) {
-      ALOGE("Failed fcntl(%d, F_GETFL) : %s", fd, strerror(errno));
-      return NULL;
-    }
-
-    // File offset : Ignore the offset for non seekable files.
-    const off_t offset = TEMP_FAILURE_RETRY(lseek64(fd, 0, SEEK_CUR));
-
-    // We pass the flags that open accepts to open, and use F_SETFL for
-    // the rest of them.
-    static const int kOpenFlags = (O_RDONLY | O_WRONLY | O_RDWR | O_DSYNC | O_SYNC);
-    int open_flags = fs_flags & (kOpenFlags);
-    fs_flags = fs_flags & (~(kOpenFlags));
-
-    return new FileDescriptorInfo(f_stat, file_path, fd, open_flags, fd_flags, fs_flags, offset);
-  }
-
-  // Checks whether the file descriptor associated with this object
-  // refers to the same description.
-  bool Restat() const {
-    struct stat f_stat;
-    if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
-      return false;
-    }
-
-    return f_stat.st_ino == stat.st_ino && f_stat.st_dev == stat.st_dev;
-  }
-
-  bool ReopenOrDetach() const {
-    if (is_sock) {
-      return DetachSocket();
-    }
-
-    // NOTE: This might happen if the file was unlinked after being opened.
-    // It's a common pattern in the case of temporary files and the like but
-    // we should not allow such usage from the zygote.
-    const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags));
-
-    if (new_fd == -1) {
-      ALOGE("Failed open(%s, %d) : %s", file_path.c_str(), open_flags, strerror(errno));
-      return false;
-    }
-
-    if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) {
-      close(new_fd);
-      ALOGE("Failed fcntl(%d, F_SETFD, %x) : %s", new_fd, fd_flags, strerror(errno));
-      return false;
-    }
-
-    if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) {
-      close(new_fd);
-      ALOGE("Failed fcntl(%d, F_SETFL, %x) : %s", new_fd, fs_flags, strerror(errno));
-      return false;
-    }
-
-    if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) {
-      close(new_fd);
-      ALOGE("Failed lseek64(%d, SEEK_SET) : %s", new_fd, strerror(errno));
-      return false;
-    }
-
-    if (TEMP_FAILURE_RETRY(dup2(new_fd, fd)) == -1) {
-      close(new_fd);
-      ALOGE("Failed dup2(%d, %d) : %s", fd, new_fd, strerror(errno));
-      return false;
-    }
-
-    close(new_fd);
-
-    return true;
-  }
-
-  const int fd;
-  const struct stat stat;
-  const std::string file_path;
-  const int open_flags;
-  const int fd_flags;
-  const int fs_flags;
-  const off_t offset;
-  const bool is_sock;
-
- private:
-  FileDescriptorInfo(int fd) :
-    fd(fd),
-    stat(),
-    open_flags(0),
-    fd_flags(0),
-    fs_flags(0),
-    offset(0),
-    is_sock(true) {
-  }
-
-  FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags,
-                     int fd_flags, int fs_flags, off_t offset) :
-    fd(fd),
-    stat(stat),
-    file_path(file_path),
-    open_flags(open_flags),
-    fd_flags(fd_flags),
-    fs_flags(fs_flags),
-    offset(offset),
-    is_sock(false) {
-  }
-
-  static bool StartsWith(const std::string& str, const std::string& prefix) {
-    return str.compare(0, prefix.size(), prefix) == 0;
-  }
-
-  static bool EndsWith(const std::string& str, const std::string& suffix) {
-    if (suffix.size() > str.size()) {
-      return false;
-    }
-
-    return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
-  }
-
-  // Returns true iff. a given path is whitelisted. A path is whitelisted
-  // if it belongs to the whitelist (see kPathWhitelist) or if it's a path
-  // under /system/framework that ends with ".jar" or if it is a system
-  // framework overlay.
-  static bool IsWhitelisted(const std::string& path) {
-    for (size_t i = 0; i < (sizeof(kPathWhitelist) / sizeof(kPathWhitelist[0])); ++i) {
-      if (kPathWhitelist[i] == path) {
-        return true;
-      }
-    }
-
-    static const std::string kFrameworksPrefix = "/system/framework/";
-    static const std::string kJarSuffix = ".jar";
-    if (StartsWith(path, kFrameworksPrefix) && EndsWith(path, kJarSuffix)) {
-      return true;
-    }
-
-    // Whitelist files needed for Runtime Resource Overlay, like these:
-    // /system/vendor/overlay/framework-res.apk
-    // /system/vendor/overlay-subdir/pg/framework-res.apk
-    // /vendor/overlay/framework-res.apk
-    // /vendor/overlay/PG/android-framework-runtime-resource-overlay.apk
-    // /data/resource-cache/system@vendor@overlay@framework-res.apk@idmap
-    // /data/resource-cache/system@vendor@overlay-subdir@pg@framework-res.apk@idmap
-    // See AssetManager.cpp for more details on overlay-subdir.
-    static const std::string kOverlayDir = "/system/vendor/overlay/";
-    static const std::string kVendorOverlayDir = "/vendor/overlay";
-    static const std::string kOverlaySubdir = "/system/vendor/overlay-subdir/";
-    static const std::string kApkSuffix = ".apk";
-
-    if ((StartsWith(path, kOverlayDir) || StartsWith(path, kOverlaySubdir)
-         || StartsWith(path, kVendorOverlayDir))
-        && EndsWith(path, kApkSuffix)
-        && path.find("/../") == std::string::npos) {
-      return true;
-    }
-
-    static const std::string kOverlayIdmapPrefix = "/data/resource-cache/";
-    static const std::string kOverlayIdmapSuffix = ".apk@idmap";
-    if (StartsWith(path, kOverlayIdmapPrefix) && EndsWith(path, kOverlayIdmapSuffix)
-        && path.find("/../") == std::string::npos) {
-      return true;
-    }
-
-    // All regular files that are placed under this path are whitelisted automatically.
-    static const std::string kZygoteWhitelistPath = "/vendor/zygote_whitelist/";
-    if (StartsWith(path, kZygoteWhitelistPath) && path.find("/../") == std::string::npos) {
-      return true;
-    }
-
-    return false;
-  }
-
-  // TODO: Call android::base::Readlink instead of copying the code here.
-  static bool Readlink(const int fd, std::string* result) {
-    char path[64];
-    snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
-
-    // Code copied from android::base::Readlink starts here :
-
-    // Annoyingly, the readlink system call returns EINVAL for a zero-sized buffer,
-    // and truncates to whatever size you do supply, so it can't be used to query.
-    // We could call lstat first, but that would introduce a race condition that
-    // we couldn't detect.
-    // ext2 and ext4 both have PAGE_SIZE limitations, so we assume that here.
-    char buf[4096];
-    ssize_t len = readlink(path, buf, sizeof(buf));
-    if (len == -1) return false;
-
-    result->assign(buf, len);
-    return true;
-  }
-
-  // Returns the locally-bound name of the socket |fd|. Returns true
-  // iff. all of the following hold :
-  //
-  // - the socket's sa_family is AF_UNIX.
-  // - the length of the path is greater than zero (i.e, not an unnamed socket).
-  // - the first byte of the path isn't zero (i.e, not a socket with an abstract
-  //   address).
-  static bool GetSocketName(const int fd, std::string* result) {
-    sockaddr_storage ss;
-    sockaddr* addr = reinterpret_cast<sockaddr*>(&ss);
-    socklen_t addr_len = sizeof(ss);
-
-    if (TEMP_FAILURE_RETRY(getsockname(fd, addr, &addr_len)) == -1) {
-      ALOGE("Failed getsockname(%d) : %s", fd, strerror(errno));
-      return false;
-    }
-
-    if (addr->sa_family != AF_UNIX) {
-      ALOGE("Unsupported socket (fd=%d) with family %d", fd, addr->sa_family);
-      return false;
-    }
-
-    const sockaddr_un* unix_addr = reinterpret_cast<const sockaddr_un*>(&ss);
-
-    size_t path_len = addr_len - offsetof(struct sockaddr_un, sun_path);
-    // This is an unnamed local socket, we do not accept it.
-    if (path_len == 0) {
-      ALOGE("Unsupported AF_UNIX socket (fd=%d) with empty path.", fd);
-      return false;
-    }
-
-    // This is a local socket with an abstract address, we do not accept it.
-    if (unix_addr->sun_path[0] == '\0') {
-      ALOGE("Unsupported AF_UNIX socket (fd=%d) with abstract address.", fd);
-      return false;
-    }
-
-    // If we're here, sun_path must refer to a null terminated filesystem
-    // pathname (man 7 unix). Remove the terminator before assigning it to an
-    // std::string.
-    if (unix_addr->sun_path[path_len - 1] ==  '\0') {
-      --path_len;
-    }
-
-    result->assign(unix_addr->sun_path, path_len);
-    return true;
-  }
-
-  bool DetachSocket() const {
-    const int dev_null_fd = open("/dev/null", O_RDWR);
-    if (dev_null_fd < 0) {
-      ALOGE("Failed to open /dev/null : %s", strerror(errno));
-      return false;
-    }
-
-    if (dup2(dev_null_fd, fd) == -1) {
-      ALOGE("Failed dup2 on socket descriptor %d : %s", fd, strerror(errno));
-      return false;
-    }
-
-    if (close(dev_null_fd) == -1) {
-      ALOGE("Failed close(%d) : %s", dev_null_fd, strerror(errno));
-      return false;
-    }
-
-    return true;
-  }
-
-  DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo);
-};
-
-// A FileDescriptorTable is a collection of FileDescriptorInfo objects
-// keyed by their FDs.
-class FileDescriptorTable {
- public:
-  // Creates a new FileDescriptorTable. This function scans
-  // /proc/self/fd for the list of open file descriptors and collects
-  // information about them. Returns NULL if an error occurs.
-  static FileDescriptorTable* Create() {
-    DIR* d = opendir(kFdPath);
-    if (d == NULL) {
-      ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
-      return NULL;
-    }
-    int dir_fd = dirfd(d);
-    dirent* e;
-
-    std::unordered_map<int, FileDescriptorInfo*> open_fd_map;
-    while ((e = readdir(d)) != NULL) {
-      const int fd = ParseFd(e, dir_fd);
-      if (fd == -1) {
-        continue;
-      }
-
-      FileDescriptorInfo* info = FileDescriptorInfo::createFromFd(fd);
-      if (info == NULL) {
-        if (closedir(d) == -1) {
-          ALOGE("Unable to close directory : %s", strerror(errno));
-        }
-        return NULL;
-      }
-      open_fd_map[fd] = info;
-    }
-
-    if (closedir(d) == -1) {
-      ALOGE("Unable to close directory : %s", strerror(errno));
-      return NULL;
-    }
-    return new FileDescriptorTable(open_fd_map);
-  }
-
-  bool Restat() {
-    std::set<int> open_fds;
-
-    // First get the list of open descriptors.
-    DIR* d = opendir(kFdPath);
-    if (d == NULL) {
-      ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
-      return false;
-    }
-
-    int dir_fd = dirfd(d);
-    dirent* e;
-    while ((e = readdir(d)) != NULL) {
-      const int fd = ParseFd(e, dir_fd);
-      if (fd == -1) {
-        continue;
-      }
-
-      open_fds.insert(fd);
-    }
-
-    if (closedir(d) == -1) {
-      ALOGE("Unable to close directory : %s", strerror(errno));
-      return false;
-    }
-
-    return RestatInternal(open_fds);
-  }
-
-  // Reopens all file descriptors that are contained in the table. Returns true
-  // if all descriptors were successfully re-opened or detached, and false if an
-  // error occurred.
-  bool ReopenOrDetach() {
-    std::unordered_map<int, FileDescriptorInfo*>::const_iterator it;
-    for (it = open_fd_map_.begin(); it != open_fd_map_.end(); ++it) {
-      const FileDescriptorInfo* info = it->second;
-      if (info == NULL || !info->ReopenOrDetach()) {
-        return false;
-      }
-    }
-
-    return true;
-  }
-
- private:
-  FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map)
-      : open_fd_map_(map) {
-  }
-
-  bool RestatInternal(std::set<int>& open_fds) {
-    bool error = false;
-
-    // Iterate through the list of file descriptors we've already recorded
-    // and check whether :
-    //
-    // (a) they continue to be open.
-    // (b) they refer to the same file.
-    std::unordered_map<int, FileDescriptorInfo*>::iterator it = open_fd_map_.begin();
-    while (it != open_fd_map_.end()) {
-      std::set<int>::const_iterator element = open_fds.find(it->first);
-      if (element == open_fds.end()) {
-        // The entry from the file descriptor table is no longer in the list
-        // of open files. We warn about this condition and remove it from
-        // the list of FDs under consideration.
-        //
-        // TODO(narayan): This will be an error in a future android release.
-        // error = true;
-        // ALOGW("Zygote closed file descriptor %d.", it->first);
-        it = open_fd_map_.erase(it);
-      } else {
-        // The entry from the file descriptor table is still open. Restat
-        // it and check whether it refers to the same file.
-        const bool same_file = it->second->Restat();
-        if (!same_file) {
-          // The file descriptor refers to a different description. We must
-          // update our entry in the table.
-          delete it->second;
-          it->second = FileDescriptorInfo::createFromFd(*element);
-          if (it->second == NULL) {
-            // The descriptor no longer no longer refers to a whitelisted file.
-            // We flag an error and remove it from the list of files we're
-            // tracking.
-            error = true;
-            it = open_fd_map_.erase(it);
-          } else {
-            // Successfully restatted the file, move on to the next open FD.
-            ++it;
-          }
-        } else {
-          // It's the same file. Nothing to do here. Move on to the next open
-          // FD.
-          ++it;
-        }
-
-        // Finally, remove the FD from the set of open_fds. We do this last because
-        // |element| will not remain valid after a call to erase.
-        open_fds.erase(element);
-      }
-    }
-
-    if (open_fds.size() > 0) {
-      // The zygote has opened new file descriptors since our last inspection.
-      // We warn about this condition and add them to our table.
-      //
-      // TODO(narayan): This will be an error in a future android release.
-      // error = true;
-      // ALOGW("Zygote opened %zd new file descriptor(s).", open_fds.size());
-
-      // TODO(narayan): This code will be removed in a future android release.
-      std::set<int>::const_iterator it;
-      for (it = open_fds.begin(); it != open_fds.end(); ++it) {
-        const int fd = (*it);
-        FileDescriptorInfo* info = FileDescriptorInfo::createFromFd(fd);
-        if (info == NULL) {
-          // A newly opened file is not on the whitelist. Flag an error and
-          // continue.
-          error = true;
-        } else {
-          // Track the newly opened file.
-          open_fd_map_[fd] = info;
-        }
-      }
-    }
-
-    return !error;
-  }
-
-  static int ParseFd(dirent* e, int dir_fd) {
-    char* end;
-    const int fd = strtol(e->d_name, &end, 10);
-    if ((*end) != '\0') {
-      return -1;
-    }
-
-    // Don't bother with the standard input/output/error, they're handled
-    // specially post-fork anyway.
-    if (fd <= STDERR_FILENO || fd == dir_fd) {
-      return -1;
-    }
-
-    return fd;
-  }
-
-  // Invariant: All values in this unordered_map are non-NULL.
-  std::unordered_map<int, FileDescriptorInfo*> open_fd_map_;
-
-  DISALLOW_COPY_AND_ASSIGN(FileDescriptorTable);
-};
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
new file mode 100644
index 0000000..969d336f3
--- /dev/null
+++ b/core/jni/fd_utils.cpp
@@ -0,0 +1,559 @@
+/*
+ * 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 "fd_utils.h"
+
+#include <algorithm>
+
+#include <fcntl.h>
+#include <grp.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <android-base/strings.h>
+#include <cutils/log.h>
+
+// Static whitelist of open paths that the zygote is allowed to keep open.
+static const char* kPathWhitelist[] = {
+  "/dev/null",
+  "/dev/socket/zygote",
+  "/dev/socket/zygote_secondary",
+  "/dev/socket/webview_zygote",
+  "/sys/kernel/debug/tracing/trace_marker",
+  "/system/framework/framework-res.apk",
+  "/dev/urandom",
+  "/dev/ion",
+  "/dev/dri/renderD129", // Fixes b/31172436
+};
+
+static const char kFdPath[] = "/proc/self/fd";
+
+// static
+FileDescriptorWhitelist* FileDescriptorWhitelist::Get() {
+  if (instance_ == nullptr) {
+    instance_ = new FileDescriptorWhitelist();
+  }
+  return instance_;
+}
+
+bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const {
+  // Check the static whitelist path.
+  for (const auto& whitelist_path : kPathWhitelist) {
+    if (path == whitelist_path)
+      return true;
+  }
+
+  // Check any paths added to the dynamic whitelist.
+  for (const auto& whitelist_path : whitelist_) {
+    if (path == whitelist_path)
+      return true;
+  }
+
+  static const std::string kFrameworksPrefix = "/system/framework/";
+  static const std::string kJarSuffix = ".jar";
+  if (StartsWith(path, kFrameworksPrefix) && EndsWith(path, kJarSuffix)) {
+    return true;
+  }
+
+  // Whitelist files needed for Runtime Resource Overlay, like these:
+  // /system/vendor/overlay/framework-res.apk
+  // /system/vendor/overlay-subdir/pg/framework-res.apk
+  // /vendor/overlay/framework-res.apk
+  // /vendor/overlay/PG/android-framework-runtime-resource-overlay.apk
+  // /data/resource-cache/system@vendor@overlay@framework-res.apk@idmap
+  // /data/resource-cache/system@vendor@overlay-subdir@pg@framework-res.apk@idmap
+  // See AssetManager.cpp for more details on overlay-subdir.
+  static const std::string kOverlayDir = "/system/vendor/overlay/";
+  static const std::string kVendorOverlayDir = "/vendor/overlay";
+  static const std::string kOverlaySubdir = "/system/vendor/overlay-subdir/";
+  static const std::string kApkSuffix = ".apk";
+
+  if ((StartsWith(path, kOverlayDir) || StartsWith(path, kOverlaySubdir)
+       || StartsWith(path, kVendorOverlayDir))
+      && EndsWith(path, kApkSuffix)
+      && path.find("/../") == std::string::npos) {
+    return true;
+  }
+
+  static const std::string kOverlayIdmapPrefix = "/data/resource-cache/";
+  static const std::string kOverlayIdmapSuffix = ".apk@idmap";
+  if (StartsWith(path, kOverlayIdmapPrefix) && EndsWith(path, kOverlayIdmapSuffix)
+      && path.find("/../") == std::string::npos) {
+    return true;
+  }
+
+  // All regular files that are placed under this path are whitelisted automatically.
+  static const std::string kZygoteWhitelistPath = "/vendor/zygote_whitelist/";
+  if (StartsWith(path, kZygoteWhitelistPath) && path.find("/../") == std::string::npos) {
+    return true;
+  }
+
+  return false;
+}
+
+FileDescriptorWhitelist::FileDescriptorWhitelist()
+    : whitelist_() {
+}
+
+// TODO: Call android::base::StartsWith instead of copying the code here.
+// static
+bool FileDescriptorWhitelist::StartsWith(const std::string& str,
+                                         const std::string& prefix) {
+  return str.compare(0, prefix.size(), prefix) == 0;
+}
+
+// TODO: Call android::base::EndsWith instead of copying the code here.
+// static
+bool FileDescriptorWhitelist::EndsWith(const std::string& str,
+                                       const std::string& suffix) {
+  if (suffix.size() > str.size()) {
+    return false;
+  }
+
+  return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
+}
+
+FileDescriptorWhitelist* FileDescriptorWhitelist::instance_ = nullptr;
+
+// static
+FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) {
+  struct stat f_stat;
+  // This should never happen; the zygote should always have the right set
+  // of permissions required to stat all its open files.
+  if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
+    ALOGE("Unable to stat fd %d : %s", fd, strerror(errno));
+    return NULL;
+  }
+
+  const FileDescriptorWhitelist* whitelist = FileDescriptorWhitelist::Get();
+
+  if (S_ISSOCK(f_stat.st_mode)) {
+    std::string socket_name;
+    if (!GetSocketName(fd, &socket_name)) {
+      return NULL;
+    }
+
+    if (!whitelist->IsAllowed(socket_name)) {
+      ALOGE("Socket name not whitelisted : %s (fd=%d)", socket_name.c_str(), fd);
+      return NULL;
+    }
+
+    return new FileDescriptorInfo(fd);
+  }
+
+  // We only handle whitelisted regular files and character devices. Whitelisted
+  // character devices must provide a guarantee of sensible behaviour when
+  // reopened.
+  //
+  // S_ISDIR : Not supported. (We could if we wanted to, but it's unused).
+  // S_ISLINK : Not supported.
+  // S_ISBLK : Not supported.
+  // S_ISFIFO : Not supported. Note that the zygote uses pipes to communicate
+  // with the child process across forks but those should have been closed
+  // before we got to this point.
+  if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) {
+    ALOGE("Unsupported st_mode %d", f_stat.st_mode);
+    return NULL;
+  }
+
+  std::string file_path;
+  if (!Readlink(fd, &file_path)) {
+    return NULL;
+  }
+
+  if (!whitelist->IsAllowed(file_path)) {
+    ALOGE("Not whitelisted : %s", file_path.c_str());
+    return NULL;
+  }
+
+  // File descriptor flags : currently on FD_CLOEXEC. We can set these
+  // using F_SETFD - we're single threaded at this point of execution so
+  // there won't be any races.
+  const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD));
+  if (fd_flags == -1) {
+    ALOGE("Failed fcntl(%d, F_GETFD) : %s", fd, strerror(errno));
+    return NULL;
+  }
+
+  // File status flags :
+  // - File access mode : (O_RDONLY, O_WRONLY...) we'll pass these through
+  //   to the open() call.
+  //
+  // - File creation flags : (O_CREAT, O_EXCL...) - there's not much we can
+  //   do about these, since the file has already been created. We shall ignore
+  //   them here.
+  //
+  // - Other flags : We'll have to set these via F_SETFL. On linux, F_SETFL
+  //   can only set O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK.
+  //   In particular, it can't set O_SYNC and O_DSYNC. We'll have to test for
+  //   their presence and pass them in to open().
+  int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
+  if (fs_flags == -1) {
+    ALOGE("Failed fcntl(%d, F_GETFL) : %s", fd, strerror(errno));
+    return NULL;
+  }
+
+  // File offset : Ignore the offset for non seekable files.
+  const off_t offset = TEMP_FAILURE_RETRY(lseek64(fd, 0, SEEK_CUR));
+
+  // We pass the flags that open accepts to open, and use F_SETFL for
+  // the rest of them.
+  static const int kOpenFlags = (O_RDONLY | O_WRONLY | O_RDWR | O_DSYNC | O_SYNC);
+  int open_flags = fs_flags & (kOpenFlags);
+  fs_flags = fs_flags & (~(kOpenFlags));
+
+  return new FileDescriptorInfo(f_stat, file_path, fd, open_flags, fd_flags, fs_flags, offset);
+}
+
+bool FileDescriptorInfo::Restat() const {
+  struct stat f_stat;
+  if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
+    return false;
+  }
+
+  return f_stat.st_ino == stat.st_ino && f_stat.st_dev == stat.st_dev;
+}
+
+bool FileDescriptorInfo::ReopenOrDetach() const {
+  if (is_sock) {
+    return DetachSocket();
+  }
+
+  // NOTE: This might happen if the file was unlinked after being opened.
+  // It's a common pattern in the case of temporary files and the like but
+  // we should not allow such usage from the zygote.
+  const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags));
+
+  if (new_fd == -1) {
+    ALOGE("Failed open(%s, %d) : %s", file_path.c_str(), open_flags, strerror(errno));
+    return false;
+  }
+
+  if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) {
+    close(new_fd);
+    ALOGE("Failed fcntl(%d, F_SETFD, %x) : %s", new_fd, fd_flags, strerror(errno));
+    return false;
+  }
+
+  if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) {
+    close(new_fd);
+    ALOGE("Failed fcntl(%d, F_SETFL, %x) : %s", new_fd, fs_flags, strerror(errno));
+    return false;
+  }
+
+  if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) {
+    close(new_fd);
+    ALOGE("Failed lseek64(%d, SEEK_SET) : %s", new_fd, strerror(errno));
+    return false;
+  }
+
+  if (TEMP_FAILURE_RETRY(dup2(new_fd, fd)) == -1) {
+    close(new_fd);
+    ALOGE("Failed dup2(%d, %d) : %s", fd, new_fd, strerror(errno));
+    return false;
+  }
+
+  close(new_fd);
+
+  return true;
+}
+
+FileDescriptorInfo::FileDescriptorInfo(int fd) :
+  fd(fd),
+  stat(),
+  open_flags(0),
+  fd_flags(0),
+  fs_flags(0),
+  offset(0),
+  is_sock(true) {
+}
+
+FileDescriptorInfo::FileDescriptorInfo(struct stat stat, const std::string& file_path,
+                                       int fd, int open_flags, int fd_flags, int fs_flags,
+                                       off_t offset) :
+  fd(fd),
+  stat(stat),
+  file_path(file_path),
+  open_flags(open_flags),
+  fd_flags(fd_flags),
+  fs_flags(fs_flags),
+  offset(offset),
+  is_sock(false) {
+}
+
+// TODO: Call android::base::Readlink instead of copying the code here.
+// static
+bool FileDescriptorInfo::Readlink(const int fd, std::string* result) {
+  char path[64];
+  snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
+
+  // Code copied from android::base::Readlink starts here :
+
+  // Annoyingly, the readlink system call returns EINVAL for a zero-sized buffer,
+  // and truncates to whatever size you do supply, so it can't be used to query.
+  // We could call lstat first, but that would introduce a race condition that
+  // we couldn't detect.
+  // ext2 and ext4 both have PAGE_SIZE limitations, so we assume that here.
+  char buf[4096];
+  ssize_t len = readlink(path, buf, sizeof(buf));
+  if (len == -1) return false;
+
+  result->assign(buf, len);
+  return true;
+}
+
+// static
+bool FileDescriptorInfo::GetSocketName(const int fd, std::string* result) {
+  sockaddr_storage ss;
+  sockaddr* addr = reinterpret_cast<sockaddr*>(&ss);
+  socklen_t addr_len = sizeof(ss);
+
+  if (TEMP_FAILURE_RETRY(getsockname(fd, addr, &addr_len)) == -1) {
+    ALOGE("Failed getsockname(%d) : %s", fd, strerror(errno));
+    return false;
+  }
+
+  if (addr->sa_family != AF_UNIX) {
+    ALOGE("Unsupported socket (fd=%d) with family %d", fd, addr->sa_family);
+    return false;
+  }
+
+  const sockaddr_un* unix_addr = reinterpret_cast<const sockaddr_un*>(&ss);
+
+  size_t path_len = addr_len - offsetof(struct sockaddr_un, sun_path);
+  // This is an unnamed local socket, we do not accept it.
+  if (path_len == 0) {
+    ALOGE("Unsupported AF_UNIX socket (fd=%d) with empty path.", fd);
+    return false;
+  }
+
+  // This is a local socket with an abstract address, we do not accept it.
+  if (unix_addr->sun_path[0] == '\0') {
+    ALOGE("Unsupported AF_UNIX socket (fd=%d) with abstract address.", fd);
+    return false;
+  }
+
+  // If we're here, sun_path must refer to a null terminated filesystem
+  // pathname (man 7 unix). Remove the terminator before assigning it to an
+  // std::string.
+  if (unix_addr->sun_path[path_len - 1] ==  '\0') {
+    --path_len;
+  }
+
+  result->assign(unix_addr->sun_path, path_len);
+  return true;
+}
+
+bool FileDescriptorInfo::DetachSocket() const {
+  const int dev_null_fd = open("/dev/null", O_RDWR);
+  if (dev_null_fd < 0) {
+    ALOGE("Failed to open /dev/null : %s", strerror(errno));
+    return false;
+  }
+
+  if (dup2(dev_null_fd, fd) == -1) {
+    ALOGE("Failed dup2 on socket descriptor %d : %s", fd, strerror(errno));
+    return false;
+  }
+
+  if (close(dev_null_fd) == -1) {
+    ALOGE("Failed close(%d) : %s", dev_null_fd, strerror(errno));
+    return false;
+  }
+
+  return true;
+}
+
+// static
+FileDescriptorTable* FileDescriptorTable::Create() {
+  DIR* d = opendir(kFdPath);
+  if (d == NULL) {
+    ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
+    return NULL;
+  }
+  int dir_fd = dirfd(d);
+  dirent* e;
+
+  std::unordered_map<int, FileDescriptorInfo*> open_fd_map;
+  while ((e = readdir(d)) != NULL) {
+    const int fd = ParseFd(e, dir_fd);
+    if (fd == -1) {
+      continue;
+    }
+
+    FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd);
+    if (info == NULL) {
+      if (closedir(d) == -1) {
+        ALOGE("Unable to close directory : %s", strerror(errno));
+      }
+      return NULL;
+    }
+    open_fd_map[fd] = info;
+  }
+
+  if (closedir(d) == -1) {
+    ALOGE("Unable to close directory : %s", strerror(errno));
+    return NULL;
+  }
+  return new FileDescriptorTable(open_fd_map);
+}
+
+bool FileDescriptorTable::Restat() {
+  std::set<int> open_fds;
+
+  // First get the list of open descriptors.
+  DIR* d = opendir(kFdPath);
+  if (d == NULL) {
+    ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
+    return false;
+  }
+
+  int dir_fd = dirfd(d);
+  dirent* e;
+  while ((e = readdir(d)) != NULL) {
+    const int fd = ParseFd(e, dir_fd);
+    if (fd == -1) {
+      continue;
+    }
+
+    open_fds.insert(fd);
+  }
+
+  if (closedir(d) == -1) {
+    ALOGE("Unable to close directory : %s", strerror(errno));
+    return false;
+  }
+
+  return RestatInternal(open_fds);
+}
+
+// Reopens all file descriptors that are contained in the table. Returns true
+// if all descriptors were successfully re-opened or detached, and false if an
+// error occurred.
+bool FileDescriptorTable::ReopenOrDetach() {
+  std::unordered_map<int, FileDescriptorInfo*>::const_iterator it;
+  for (it = open_fd_map_.begin(); it != open_fd_map_.end(); ++it) {
+    const FileDescriptorInfo* info = it->second;
+    if (info == NULL || !info->ReopenOrDetach()) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+FileDescriptorTable::FileDescriptorTable(
+    const std::unordered_map<int, FileDescriptorInfo*>& map)
+    : open_fd_map_(map) {
+}
+
+bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds) {
+  bool error = false;
+
+  // Iterate through the list of file descriptors we've already recorded
+  // and check whether :
+  //
+  // (a) they continue to be open.
+  // (b) they refer to the same file.
+  std::unordered_map<int, FileDescriptorInfo*>::iterator it = open_fd_map_.begin();
+  while (it != open_fd_map_.end()) {
+    std::set<int>::const_iterator element = open_fds.find(it->first);
+    if (element == open_fds.end()) {
+      // The entry from the file descriptor table is no longer in the list
+      // of open files. We warn about this condition and remove it from
+      // the list of FDs under consideration.
+      //
+      // TODO(narayan): This will be an error in a future android release.
+      // error = true;
+      // ALOGW("Zygote closed file descriptor %d.", it->first);
+      it = open_fd_map_.erase(it);
+    } else {
+      // The entry from the file descriptor table is still open. Restat
+      // it and check whether it refers to the same file.
+      const bool same_file = it->second->Restat();
+      if (!same_file) {
+        // The file descriptor refers to a different description. We must
+        // update our entry in the table.
+        delete it->second;
+        it->second = FileDescriptorInfo::CreateFromFd(*element);
+        if (it->second == NULL) {
+          // The descriptor no longer no longer refers to a whitelisted file.
+          // We flag an error and remove it from the list of files we're
+          // tracking.
+          error = true;
+          it = open_fd_map_.erase(it);
+        } else {
+          // Successfully restatted the file, move on to the next open FD.
+          ++it;
+        }
+      } else {
+        // It's the same file. Nothing to do here. Move on to the next open
+        // FD.
+        ++it;
+      }
+
+      // Finally, remove the FD from the set of open_fds. We do this last because
+      // |element| will not remain valid after a call to erase.
+      open_fds.erase(element);
+    }
+  }
+
+  if (open_fds.size() > 0) {
+    // The zygote has opened new file descriptors since our last inspection.
+    // We warn about this condition and add them to our table.
+    //
+    // TODO(narayan): This will be an error in a future android release.
+    // error = true;
+    // ALOGW("Zygote opened %zd new file descriptor(s).", open_fds.size());
+
+    // TODO(narayan): This code will be removed in a future android release.
+    std::set<int>::const_iterator it;
+    for (it = open_fds.begin(); it != open_fds.end(); ++it) {
+      const int fd = (*it);
+      FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd);
+      if (info == NULL) {
+        // A newly opened file is not on the whitelist. Flag an error and
+        // continue.
+        error = true;
+      } else {
+        // Track the newly opened file.
+        open_fd_map_[fd] = info;
+      }
+    }
+  }
+
+  return !error;
+}
+
+// static
+int FileDescriptorTable::ParseFd(dirent* e, int dir_fd) {
+  char* end;
+  const int fd = strtol(e->d_name, &end, 10);
+  if ((*end) != '\0') {
+    return -1;
+  }
+
+  // Don't bother with the standard input/output/error, they're handled
+  // specially post-fork anyway.
+  if (fd <= STDERR_FILENO || fd == dir_fd) {
+    return -1;
+  }
+
+  return fd;
+}
diff --git a/core/jni/fd_utils.h b/core/jni/fd_utils.h
new file mode 100644
index 0000000..9e3afd9
--- /dev/null
+++ b/core/jni/fd_utils.h
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+#ifndef FRAMEWORKS_BASE_CORE_JNI_FD_UTILS_H_
+#define FRAMEWORKS_BASE_CORE_JNI_FD_UTILS_H_
+
+#include <set>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <dirent.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+
+#include <android-base/macros.h>
+
+// Whitelist of open paths that the zygote is allowed to keep open.
+//
+// In addition to the paths listed in kPathWhitelist in file_utils.cpp, and
+// paths dynamically added with Allow(), all files ending with ".jar"
+// under /system/framework" are whitelisted. See IsAllowed() for the canonical
+// definition.
+//
+// If the whitelisted path is associated with a regular file or a
+// character device, the file is reopened after a fork with the same
+// offset and mode. If the whilelisted  path is associated with a
+// AF_UNIX socket, the socket will refer to /dev/null after each
+// fork, and all operations on it will fail.
+class FileDescriptorWhitelist {
+ public:
+  // Lazily creates the global whitelist.
+  static FileDescriptorWhitelist* Get();
+
+  // Adds a path to the whitelist.
+  void Allow(const std::string& path) {
+    whitelist_.push_back(path);
+  }
+
+  // Returns true iff. a given path is whitelisted. A path is whitelisted
+  // if it belongs to the whitelist (see kPathWhitelist) or if it's a path
+  // under /system/framework that ends with ".jar" or if it is a system
+  // framework overlay.
+  bool IsAllowed(const std::string& path) const;
+
+ private:
+  FileDescriptorWhitelist();
+
+  static bool StartsWith(const std::string& str, const std::string& prefix);
+
+  static bool EndsWith(const std::string& str, const std::string& suffix);
+
+  static FileDescriptorWhitelist* instance_;
+
+  std::vector<std::string> whitelist_;
+
+  DISALLOW_COPY_AND_ASSIGN(FileDescriptorWhitelist);
+};
+
+// Keeps track of all relevant information (flags, offset etc.) of an
+// open zygote file descriptor.
+class FileDescriptorInfo {
+ public:
+  // Create a FileDescriptorInfo for a given file descriptor. Returns
+  // |NULL| if an error occurred.
+  static FileDescriptorInfo* CreateFromFd(int fd);
+
+  // Checks whether the file descriptor associated with this object
+  // refers to the same description.
+  bool Restat() const;
+
+  bool ReopenOrDetach() const;
+
+  const int fd;
+  const struct stat stat;
+  const std::string file_path;
+  const int open_flags;
+  const int fd_flags;
+  const int fs_flags;
+  const off_t offset;
+  const bool is_sock;
+
+ private:
+  FileDescriptorInfo(int fd);
+
+  FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags,
+                     int fd_flags, int fs_flags, off_t offset);
+
+  static bool Readlink(const int fd, std::string* result);
+
+  // Returns the locally-bound name of the socket |fd|. Returns true
+  // iff. all of the following hold :
+  //
+  // - the socket's sa_family is AF_UNIX.
+  // - the length of the path is greater than zero (i.e, not an unnamed socket).
+  // - the first byte of the path isn't zero (i.e, not a socket with an abstract
+  //   address).
+  static bool GetSocketName(const int fd, std::string* result);
+
+  bool DetachSocket() const;
+
+  DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo);
+};
+
+// A FileDescriptorTable is a collection of FileDescriptorInfo objects
+// keyed by their FDs.
+class FileDescriptorTable {
+ public:
+  // Creates a new FileDescriptorTable. This function scans
+  // /proc/self/fd for the list of open file descriptors and collects
+  // information about them. Returns NULL if an error occurs.
+  static FileDescriptorTable* Create();
+
+  bool Restat();
+
+  // Reopens all file descriptors that are contained in the table. Returns true
+  // if all descriptors were successfully re-opened or detached, and false if an
+  // error occurred.
+  bool ReopenOrDetach();
+
+ private:
+  FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map);
+
+  bool RestatInternal(std::set<int>& open_fds);
+
+  static int ParseFd(dirent* e, int dir_fd);
+
+  // Invariant: All values in this unordered_map are non-NULL.
+  std::unordered_map<int, FileDescriptorInfo*> open_fd_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(FileDescriptorTable);
+};
+
+#endif  // FRAMEWORKS_BASE_CORE_JNI_FD_UTILS_H_
diff --git a/core/jni/include/android_runtime/android_view_Surface.h b/core/jni/include/android_runtime/android_view_Surface.h
index b1e552a..1bc8521 100644
--- a/core/jni/include/android_runtime/android_view_Surface.h
+++ b/core/jni/include/android_runtime/android_view_Surface.h
@@ -42,6 +42,7 @@
     NV16              = 0x10,
     NV21              = 0x11,
     YUY2              = 0x14,
+    RGBA_FP16         = 0x16,
     RAW_SENSOR        = 0x20,
     PRIVATE           = 0x22,
     YUV_420_888       = 0x23,
diff --git a/core/tests/coretests/src/android/view/ViewCaptureTest.java b/core/tests/coretests/src/android/view/ViewCaptureTest.java
index 15cfe23..1b4d4a2 100644
--- a/core/tests/coretests/src/android/view/ViewCaptureTest.java
+++ b/core/tests/coretests/src/android/view/ViewCaptureTest.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import static org.junit.Assert.assertTrue;
+
 import android.app.Activity;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -32,8 +34,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertTrue;
-
 @RunWith(AndroidJUnit4.class)
 public class ViewCaptureTest {
 
@@ -70,6 +70,7 @@
 
     private void testCreateSnapshot(boolean skipChildren, int goldenResId) {
         Bitmap result = mViewToCapture.createSnapshot(Bitmap.Config.ARGB_8888, 0, skipChildren);
+        result.setHasAlpha(false); // resource will have no alpha, since content is opaque
         Bitmap golden = BitmapFactory.decodeResource(mActivity.getResources(), goldenResId);
         assertTrue(golden.sameAs(result));
     }
diff --git a/graphics/java/android/graphics/PixelFormat.java b/graphics/java/android/graphics/PixelFormat.java
index 0fa52f8..c3a9443 100644
--- a/graphics/java/android/graphics/PixelFormat.java
+++ b/graphics/java/android/graphics/PixelFormat.java
@@ -29,7 +29,7 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({RGBA_8888, RGBX_8888, RGBA_F16, RGBX_F16, RGB_888, RGB_565})
+    @IntDef({RGBA_8888, RGBX_8888, RGBA_F16, RGB_888, RGB_565})
     public @interface Format { };
 
     // NOTE: these constants must match the values from graphics/common/x.x/types.hal
@@ -88,7 +88,6 @@
     public static final int YCbCr_422_I = 0x14;
 
     public static final int RGBA_F16    = 0x16;
-    public static final int RGBX_F16    = 0x17;
 
     /**
      * @deprecated use {@link android.graphics.ImageFormat#JPEG
@@ -134,7 +133,6 @@
                 info.bytesPerPixel = 1;
                 break;
             case RGBA_F16:
-            case RGBX_F16:
                 info.bitsPerPixel = 64;
                 info.bytesPerPixel = 8;
                 break;
@@ -178,7 +176,6 @@
             case RGB_888:
             case RGB_565:
             case RGBA_F16:
-            case RGBX_F16:
                 return true;
         }
 
diff --git a/packages/SystemUI/res/drawable/header_dot.xml b/packages/SystemUI/res/drawable/header_dot.xml
index 568a9c2..dbc6b37 100644
--- a/packages/SystemUI/res/drawable/header_dot.xml
+++ b/packages/SystemUI/res/drawable/header_dot.xml
@@ -2,26 +2,27 @@
 <!--
 ** Copyright 2015, 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 
+** 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 
+**     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 
+** 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.
 -->
 <shape
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval">
+    android:shape="oval"
+    android:tint="?android:attr/colorControlNormal">
 
-    <solid 
+    <solid
         android:color="#FFFFFF"/>
 
-    <size 
+    <size
         android:width="3dp"
         android:height="3dp"/>
 </shape>
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 13e047c..6a868d5 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -218,7 +218,8 @@
                     Preconditions.checkState(mState == State.DOZE_REQUEST_PULSE);
                     break;
                 case DOZE_PULSE_DONE:
-                    Preconditions.checkState(mState == State.DOZE_PULSING);
+                    Preconditions.checkState(
+                            mState == State.DOZE_REQUEST_PULSE || mState == State.DOZE_PULSING);
                     break;
                 default:
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 790f3f6..d9fc2fc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -207,8 +207,6 @@
                 getSystemService(Context.UI_MODE_SERVICE);
         if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
             mImpl = new RecentsTvImpl(mContext);
-        } else if (SystemProperties.getBoolean("ro.recents.grid", false) == true) {
-            mImpl = new RecentsGridImpl(mContext);
         } else {
             mImpl = new RecentsImpl(mContext);
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index 8b99d72..c3948258 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -214,6 +214,16 @@
 
     @Test
     @UiThreadTest
+    public void testSuppressingPulse_doesntCrash() {
+        mMachine.requestState(INITIALIZED);
+
+        mMachine.requestState(DOZE);
+        mMachine.requestState(DOZE_REQUEST_PULSE);
+        mMachine.requestState(DOZE_PULSE_DONE);
+    }
+
+    @Test
+    @UiThreadTest
     public void testScreen_offInDoze() {
         mMachine.requestState(INITIALIZED);
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9b67bd2..eae4905 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -369,6 +369,7 @@
 import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
 import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
 import static com.android.server.am.ActivityStackSupervisor.RESTORE_FROM_RECENTS;
 import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
@@ -539,8 +540,6 @@
     private static final String INTENT_REMOTE_BUGREPORT_FINISHED =
             "android.intent.action.REMOTE_BUGREPORT_FINISHED";
 
-    // Used to indicate that a task is removed it should also be removed from recents.
-    private static final boolean REMOVE_FROM_RECENTS = true;
     // Used to indicate that an app transition should be animated.
     static final boolean ANIMATE = true;
 
@@ -4826,7 +4825,7 @@
                     // because we don't support returning them across task boundaries. Also, to
                     // keep backwards compatibility we remove the task from recents when finishing
                     // task with root activity.
-                    res = removeTaskByIdLocked(tr.taskId, false, finishWithRootActivity);
+                    res = mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, finishWithRootActivity);
                     if (!res) {
                         Slog.i(TAG, "Removing task failed to finish activity");
                     }
@@ -5471,7 +5470,7 @@
                             tr.getBaseIntent().getComponent().getPackageName();
                     if (tr.userId != userId) continue;
                     if (!taskPackageName.equals(packageName)) continue;
-                    removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
+                    mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
                 }
             }
 
@@ -9585,79 +9584,6 @@
         mWindowManager.executeAppTransition();
     }
 
-    private void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess,
-            boolean removeFromRecents) {
-        if (removeFromRecents) {
-            mRecentTasks.remove(tr);
-            tr.removedFromRecents();
-        }
-        ComponentName component = tr.getBaseIntent().getComponent();
-        if (component == null) {
-            Slog.w(TAG, "No component for base intent of task: " + tr);
-            return;
-        }
-
-        // Find any running services associated with this app and stop if needed.
-        mServices.cleanUpRemovedTaskLocked(tr, component, new Intent(tr.getBaseIntent()));
-
-        if (!killProcess) {
-            return;
-        }
-
-        // Determine if the process(es) for this task should be killed.
-        final String pkg = component.getPackageName();
-        ArrayList<ProcessRecord> procsToKill = new ArrayList<>();
-        ArrayMap<String, SparseArray<ProcessRecord>> pmap = mProcessNames.getMap();
-        for (int i = 0; i < pmap.size(); i++) {
-
-            SparseArray<ProcessRecord> uids = pmap.valueAt(i);
-            for (int j = 0; j < uids.size(); j++) {
-                ProcessRecord proc = uids.valueAt(j);
-                if (proc.userId != tr.userId) {
-                    // Don't kill process for a different user.
-                    continue;
-                }
-                if (proc == mHomeProcess) {
-                    // Don't kill the home process along with tasks from the same package.
-                    continue;
-                }
-                if (!proc.pkgList.containsKey(pkg)) {
-                    // Don't kill process that is not associated with this task.
-                    continue;
-                }
-
-                for (int k = 0; k < proc.activities.size(); k++) {
-                    TaskRecord otherTask = proc.activities.get(k).task;
-                    if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
-                        // Don't kill process(es) that has an activity in a different task that is
-                        // also in recents.
-                        return;
-                    }
-                }
-
-                if (proc.foregroundServices) {
-                    // Don't kill process(es) with foreground service.
-                    return;
-                }
-
-                // Add process to kill list.
-                procsToKill.add(proc);
-            }
-        }
-
-        // Kill the running processes.
-        for (int i = 0; i < procsToKill.size(); i++) {
-            ProcessRecord pr = procsToKill.get(i);
-            if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
-                    && pr.curReceivers.isEmpty()) {
-                pr.kill("remove task", true);
-            } else {
-                // We delay killing processes that are not in the background or running a receiver.
-                pr.waitingToKill = "remove task";
-            }
-        }
-    }
-
     private void removeTasksByPackageNameLocked(String packageName, int userId) {
         // Remove all tasks with activities in the specified package from the list of recent tasks
         for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
@@ -9667,7 +9593,7 @@
             ComponentName cn = tr.intent.getComponent();
             if (cn != null && cn.getPackageName().equals(packageName)) {
                 // If the package name matches, remove the task.
-                removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS);
+                mStackSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS);
             }
         }
     }
@@ -9685,35 +9611,11 @@
             final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName)
                     && (filterByClasses == null || filterByClasses.contains(cn.getClassName()));
             if (sameComponent) {
-                removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
+                mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
             }
         }
     }
 
-    /**
-     * Removes the task with the specified task id.
-     *
-     * @param taskId Identifier of the task to be removed.
-     * @param killProcess Kill any process associated with the task if possible.
-     * @param removeFromRecents Whether to also remove the task from recents.
-     * @return Returns true if the given task was found and removed.
-     */
-    private boolean removeTaskByIdLocked(int taskId, boolean killProcess,
-            boolean removeFromRecents) {
-        final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(
-                taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
-        if (tr != null) {
-            tr.removeTaskActivitiesLocked();
-            cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
-            if (tr.isPersistable) {
-                notifyTaskPersisterLocked(null, true);
-            }
-            return true;
-        }
-        Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);
-        return false;
-    }
-
     @Override
     public void removeStack(int stackId) {
         enforceCallingPermission(Manifest.permission.MANAGE_ACTIVITY_STACKS, "removeStack()");
@@ -9724,15 +9626,7 @@
         synchronized (this) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                final ActivityStack stack = mStackSupervisor.getStack(stackId);
-                if (stack == null) {
-                    return;
-                }
-                final ArrayList<TaskRecord> tasks = stack.getAllTasks();
-                for (int i = tasks.size() - 1; i >= 0; i--) {
-                    removeTaskByIdLocked(
-                            tasks.get(i).taskId, true /* killProcess */, REMOVE_FROM_RECENTS);
-                }
+                mStackSupervisor.removeStackLocked(stackId);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -9761,7 +9655,7 @@
         synchronized (this) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                return removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS);
+                return mStackSupervisor.removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -22520,7 +22414,8 @@
                 long origId = Binder.clearCallingIdentity();
                 try {
                     // We remove the task from recents to preserve backwards
-                    if (!removeTaskByIdLocked(mTaskId, false, REMOVE_FROM_RECENTS)) {
+                    if (!mStackSupervisor.removeTaskByIdLocked(mTaskId, false,
+                            REMOVE_FROM_RECENTS)) {
                         throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
                     }
                 } finally {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index fe261f1..b4b3465 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -249,6 +249,9 @@
     // Don't execute any calls to resume.
     static final boolean DEFER_RESUME = true;
 
+    // Used to indicate that a task is removed it should also be removed from recents.
+    static final boolean REMOVE_FROM_RECENTS = true;
+
     // Activity actions an app cannot start if it uses a permission which is not granted.
     private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
             new ArrayMap<>();
@@ -2183,7 +2186,8 @@
                         // Update the return-to to reflect where the pinned stack task was moved
                         // from so that we retain the stack that was previously visible if the
                         // pinned stack is recreated. See moveActivityToPinnedStackLocked().
-                        task.setTaskToReturnTo(getFocusedStack().getStackId() == HOME_STACK_ID
+                        final int focusedStackId = getFocusedStack().getStackId();
+                        task.setTaskToReturnTo(focusedStackId == HOME_STACK_ID || !onTop
                                 ? HOME_ACTIVITY_TYPE : APPLICATION_ACTIVITY_TYPE);
                     }
                     moveTaskToStackLocked(tasks.get(i).taskId,
@@ -2374,6 +2378,141 @@
         return activityContainer.mStack;
     }
 
+    /**
+     * Removes the stack associed with the given {@param stackId}.  If the {@param stackId} is the
+     * pinned stack, then its tasks are not explicitly removed when the stack is destroyed, but
+     * instead moved back onto the fullscreen stack.
+     */
+    void removeStackLocked(int stackId) {
+        final ActivityStack stack = getStack(stackId);
+        if (stack == null) {
+            return;
+        }
+
+        final ArrayList<TaskRecord> tasks = stack.getAllTasks();
+        if (stack.getStackId() == PINNED_STACK_ID) {
+            final ActivityStack fullscreenStack = getStack(FULLSCREEN_WORKSPACE_STACK_ID);
+            if (fullscreenStack != null) {
+                final boolean isFullscreenStackVisible =
+                        fullscreenStack.getStackVisibilityLocked(null) == STACK_VISIBLE;
+                for (int i = 0; i < tasks.size(); i++) {
+                    // Insert the task either at the top of the fullscreen stack if it is hidden,
+                    // or just under the top task if it is currently visible
+                    final int insertPosition = isFullscreenStackVisible
+                            ? Math.max(0, fullscreenStack.getChildCount() - 1)
+                            : fullscreenStack.getChildCount();
+                    positionTaskInStackLocked(tasks.get(i).taskId, FULLSCREEN_WORKSPACE_STACK_ID,
+                            insertPosition);
+                }
+                ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                resumeFocusedStackTopActivityLocked();
+            } else {
+                // If there is no fullscreen stack, then create the stack and move all the tasks
+                // onto the stack
+                moveTasksToFullscreenStackLocked(PINNED_STACK_ID, false /* onTop */);
+            }
+        } else {
+            for (int i = tasks.size() - 1; i >= 0; i--) {
+                removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */,
+                        REMOVE_FROM_RECENTS);
+            }
+        }
+    }
+
+    /**
+     * Removes the task with the specified task id.
+     *
+     * @param taskId Identifier of the task to be removed.
+     * @param killProcess Kill any process associated with the task if possible.
+     * @param removeFromRecents Whether to also remove the task from recents.
+     * @return Returns true if the given task was found and removed.
+     */
+    boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents) {
+        final TaskRecord tr = anyTaskForIdLocked(taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
+        if (tr != null) {
+            tr.removeTaskActivitiesLocked();
+            cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
+            if (tr.isPersistable) {
+                mService.notifyTaskPersisterLocked(null, true);
+            }
+            return true;
+        }
+        Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);
+        return false;
+    }
+
+    void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess, boolean removeFromRecents) {
+        if (removeFromRecents) {
+            mRecentTasks.remove(tr);
+            tr.removedFromRecents();
+        }
+        ComponentName component = tr.getBaseIntent().getComponent();
+        if (component == null) {
+            Slog.w(TAG, "No component for base intent of task: " + tr);
+            return;
+        }
+
+        // Find any running services associated with this app and stop if needed.
+        mService.mServices.cleanUpRemovedTaskLocked(tr, component, new Intent(tr.getBaseIntent()));
+
+        if (!killProcess) {
+            return;
+        }
+
+        // Determine if the process(es) for this task should be killed.
+        final String pkg = component.getPackageName();
+        ArrayList<ProcessRecord> procsToKill = new ArrayList<>();
+        ArrayMap<String, SparseArray<ProcessRecord>> pmap = mService.mProcessNames.getMap();
+        for (int i = 0; i < pmap.size(); i++) {
+
+            SparseArray<ProcessRecord> uids = pmap.valueAt(i);
+            for (int j = 0; j < uids.size(); j++) {
+                ProcessRecord proc = uids.valueAt(j);
+                if (proc.userId != tr.userId) {
+                    // Don't kill process for a different user.
+                    continue;
+                }
+                if (proc == mService.mHomeProcess) {
+                    // Don't kill the home process along with tasks from the same package.
+                    continue;
+                }
+                if (!proc.pkgList.containsKey(pkg)) {
+                    // Don't kill process that is not associated with this task.
+                    continue;
+                }
+
+                for (int k = 0; k < proc.activities.size(); k++) {
+                    TaskRecord otherTask = proc.activities.get(k).task;
+                    if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
+                        // Don't kill process(es) that has an activity in a different task that is
+                        // also in recents.
+                        return;
+                    }
+                }
+
+                if (proc.foregroundServices) {
+                    // Don't kill process(es) with foreground service.
+                    return;
+                }
+
+                // Add process to kill list.
+                procsToKill.add(proc);
+            }
+        }
+
+        // Kill the running processes.
+        for (int i = 0; i < procsToKill.size(); i++) {
+            ProcessRecord pr = procsToKill.get(i);
+            if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
+                    && pr.curReceivers.isEmpty()) {
+                pr.kill("remove task", true);
+            } else {
+                // We delay killing processes that are not in the background or running a receiver.
+                pr.waitingToKill = "remove task";
+            }
+        }
+    }
+
     int getNextStackId() {
         while (true) {
             if (mNextFreeStackId >= FIRST_DYNAMIC_STACK_ID