Merge "Make public getFragments() and onGetLayoutInflater() methods" into oc-dev am: c8d8c27c68
am: abea405064

Change-Id: Ib8bf2e7e6e0403f17c7f58f31a26554ca74f3272
diff --git a/api/current.txt b/api/current.txt
index 13a45e4..aa74aa5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -31898,6 +31898,7 @@
     method public boolean isObbMounted(java.lang.String);
     method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
     method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback) throws java.io.IOException;
+    method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback, android.os.Handler) throws java.io.IOException;
     method public void setCacheBehaviorGroup(java.io.File, boolean) throws java.io.IOException;
     method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException;
     method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
@@ -41078,9 +41079,9 @@
   public static final class FontConfig.Font implements android.os.Parcelable {
     method public int describeContents();
     method public android.text.FontConfig.Axis[] getAxes();
-    method public android.os.ParcelFileDescriptor getFd();
     method public java.lang.String getFontName();
     method public int getTtcIndex();
+    method public android.net.Uri getUri();
     method public int getWeight();
     method public boolean isItalic();
     method public void writeToParcel(android.os.Parcel, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index ff20a46..463ec55 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -34733,6 +34733,7 @@
     method public boolean isObbMounted(java.lang.String);
     method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
     method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback) throws java.io.IOException;
+    method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback, android.os.Handler) throws java.io.IOException;
     method public void setCacheBehaviorGroup(java.io.File, boolean) throws java.io.IOException;
     method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException;
     method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
@@ -44534,9 +44535,9 @@
   public static final class FontConfig.Font implements android.os.Parcelable {
     method public int describeContents();
     method public android.text.FontConfig.Axis[] getAxes();
-    method public android.os.ParcelFileDescriptor getFd();
     method public java.lang.String getFontName();
     method public int getTtcIndex();
+    method public android.net.Uri getUri();
     method public int getWeight();
     method public boolean isItalic();
     method public void writeToParcel(android.os.Parcel, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 6b8cd83..ca9f4748 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -32035,6 +32035,7 @@
     method public boolean isObbMounted(java.lang.String);
     method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
     method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback) throws java.io.IOException;
+    method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback, android.os.Handler) throws java.io.IOException;
     method public void setCacheBehaviorGroup(java.io.File, boolean) throws java.io.IOException;
     method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException;
     method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
@@ -41285,9 +41286,9 @@
   public static final class FontConfig.Font implements android.os.Parcelable {
     method public int describeContents();
     method public android.text.FontConfig.Axis[] getAxes();
-    method public android.os.ParcelFileDescriptor getFd();
     method public java.lang.String getFontName();
     method public int getTtcIndex();
+    method public android.net.Uri getUri();
     method public int getWeight();
     method public boolean isItalic();
     method public void writeToParcel(android.os.Parcel, int);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index bace226..147b5d0 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2740,6 +2740,10 @@
                 return true;
             }
             return false;
+        } else if (keyCode == KeyEvent.KEYCODE_TAB) {
+            // Don't consume TAB here since it's used for navigation. Arrow keys
+            // aren't considered "typing keys" so they already won't get consumed.
+            return false;
         } else {
             // Common code for DEFAULT_KEYS_DIALER & DEFAULT_KEYS_SEARCH_*
             boolean clearSpannable = false;
diff --git a/core/java/android/companion/BluetoothDeviceFilterUtils.java b/core/java/android/companion/BluetoothDeviceFilterUtils.java
index 8a316f1..3665d1b 100644
--- a/core/java/android/companion/BluetoothDeviceFilterUtils.java
+++ b/core/java/android/companion/BluetoothDeviceFilterUtils.java
@@ -37,7 +37,7 @@
     private BluetoothDeviceFilterUtils() {}
 
     private static final boolean DEBUG = false;
-    private static final String LOG_TAG = "BluetoothDeviceFilterUtil";
+    private static final String LOG_TAG = "BluetoothDeviceFilterUtils";
 
     @Nullable
     static String patternToString(@Nullable Pattern p) {
@@ -50,8 +50,10 @@
     }
 
     static boolean matches(ScanFilter filter, BluetoothDevice device) {
-        return matchesAddress(filter.getDeviceAddress(), device)
+        boolean result = matchesAddress(filter.getDeviceAddress(), device)
                 && matchesServiceUuid(filter.getServiceUuid(), filter.getServiceUuidMask(), device);
+        if (DEBUG) debugLogMatchResult(result, device, filter);
+        return result;
     }
 
     static boolean matchesAddress(String deviceAddress, BluetoothDevice device) {
diff --git a/core/java/android/companion/BluetoothLEDeviceFilter.java b/core/java/android/companion/BluetoothLEDeviceFilter.java
index 0444775..5a1b29d 100644
--- a/core/java/android/companion/BluetoothLEDeviceFilter.java
+++ b/core/java/android/companion/BluetoothLEDeviceFilter.java
@@ -31,6 +31,7 @@
 import android.os.Parcel;
 import android.provider.OneTimeUseBuilder;
 import android.text.TextUtils;
+import android.util.Log;
 
 import com.android.internal.util.BitUtils;
 import com.android.internal.util.ObjectUtils;
@@ -47,6 +48,9 @@
  */
 public final class BluetoothLEDeviceFilter implements DeviceFilter<ScanResult> {
 
+    private static final boolean DEBUG = false;
+    private static final String LOG_TAG = "BluetoothLEDeviceFilter";
+
     private static final int RENAME_PREFIX_LENGTH_LIMIT = 10;
 
     private final Pattern mNamePattern;
@@ -145,9 +149,13 @@
     /** @hide */
     @Override
     public boolean matches(ScanResult device) {
-        return matches(device.getDevice())
-                && BitUtils.maskedEquals(device.getScanRecord().getBytes(),
-                        mRawDataFilter, mRawDataFilterMask);
+        boolean result = matches(device.getDevice())
+                && (mRawDataFilter == null
+                    || BitUtils.maskedEquals(device.getScanRecord().getBytes(),
+                            mRawDataFilter, mRawDataFilterMask));
+        if (DEBUG) Log.i(LOG_TAG, "matches(this = " + this + ", device = " + device +
+                ") -> " + result);
+        return result;
     }
 
     private boolean matches(BluetoothDevice device) {
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index dbe1394..00e047d 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -1112,6 +1112,8 @@
                 return ImageFormat.DEPTH_POINT_CLOUD;
             case HAL_PIXEL_FORMAT_Y16:
                 return ImageFormat.DEPTH16;
+            case HAL_PIXEL_FORMAT_RAW16:
+                return ImageFormat.RAW_DEPTH;
             case ImageFormat.JPEG:
                 throw new IllegalArgumentException(
                         "ImageFormat.JPEG is an unknown internal format");
@@ -1179,6 +1181,8 @@
                 return HAL_PIXEL_FORMAT_BLOB;
             case ImageFormat.DEPTH16:
                 return HAL_PIXEL_FORMAT_Y16;
+            case ImageFormat.RAW_DEPTH:
+                return HAL_PIXEL_FORMAT_RAW16;
             default:
                 return format;
         }
@@ -1220,6 +1224,7 @@
                 return HAL_DATASPACE_V0_JFIF;
             case ImageFormat.DEPTH_POINT_CLOUD:
             case ImageFormat.DEPTH16:
+            case ImageFormat.RAW_DEPTH:
                 return HAL_DATASPACE_DEPTH;
             default:
                 return HAL_DATASPACE_UNKNOWN;
@@ -1609,6 +1614,8 @@
                 return "DEPTH16";
             case ImageFormat.DEPTH_POINT_CLOUD:
                 return "DEPTH_POINT_CLOUD";
+            case ImageFormat.RAW_DEPTH:
+                return "RAW_DEPTH";
             case ImageFormat.PRIVATE:
                 return "PRIVATE";
             default:
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index 14bb923..58c3d24 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -196,6 +196,19 @@
             return mIsCaptureSupported;
         }
 
+        /**
+         * {@code true} if the module supports background scanning. At the given time it may not
+         * be available though, see {@link RadioTuner#startBackgroundScan()}.
+         *
+         * @return {@code true} if background scanning is supported (not necessary available
+         * at a given time), {@code false} otherwise.
+         *
+         * @hide FutureFeature
+         */
+        public boolean isBackgroundScanningSupported() {
+            return false;
+        }
+
         /** List of descriptors for all bands supported by this module.
          * @return an array of {@link BandDescriptor}.
          */
diff --git a/core/java/android/hardware/radio/RadioModule.java b/core/java/android/hardware/radio/RadioModule.java
index 8964893..033403a 100644
--- a/core/java/android/hardware/radio/RadioModule.java
+++ b/core/java/android/hardware/radio/RadioModule.java
@@ -79,6 +79,8 @@
 
     public native int getProgramInformation(RadioManager.ProgramInfo[] info);
 
+    public native boolean startBackgroundScan();
+
     public native @NonNull List<RadioManager.ProgramInfo> getProgramList(@Nullable String filter);
 
     public native boolean isAntennaConnected();
diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java
index c8034eb..1159d7d 100644
--- a/core/java/android/hardware/radio/RadioTuner.java
+++ b/core/java/android/hardware/radio/RadioTuner.java
@@ -212,6 +212,23 @@
     public abstract int getProgramInformation(RadioManager.ProgramInfo[] info);
 
     /**
+     * Initiates a background scan to update internally cached program list.
+     *
+     * It may not be necessary to initiate the scan explicitly - the scan MAY be performed on boot.
+     *
+     * The operation is asynchronous and {@link Callback} backgroundScanComplete or onError will
+     * be called if the return value of this call was {@code true}. As result of this call
+     * programListChanged may be triggered (if the scanned list differs).
+     *
+     * @return {@code true} if the scan was properly scheduled, {@code false} if the scan feature
+     * is unavailable; ie. temporarily due to ongoing foreground playback in single-tuner device
+     * or permanently if the feature is not supported
+     * (see ModuleProperties#isBackgroundScanningSupported()).
+     * @hide FutureFeature
+     */
+    public abstract boolean startBackgroundScan();
+
+    /**
      * Get the list of discovered radio stations.
      *
      * To get the full list, set filter to null or empty string. Otherwise, client application
@@ -219,7 +236,8 @@
      *
      * @param filter vendor-specific selector for radio stations.
      * @return a list of radio stations.
-     * @throws IllegalStateException if the scan is in progress or has not been started.
+     * @throws IllegalStateException if the scan is in progress or has not been started,
+     *         startBackgroundScan() call may fix it.
      * @throws IllegalArgumentException if the filter argument is not valid.
      * @hide FutureFeature
      */
@@ -317,6 +335,32 @@
          * with control set to {@code true}.
          */
         public void onControlChanged(boolean control) {}
+
+        /**
+         * onBackgroundScanAvailabilityChange() is called when background scan
+         * feature becomes available or not.
+         *
+         * @param isAvailable true, if the tuner turned temporarily background-
+         *                    capable, false in the other case.
+         * @hide FutureFeature
+         */
+        public void onBackgroundScanAvailabilityChange(boolean isAvailable) {}
+
+        /**
+         * Called when a background scan completes successfully.
+         *
+         * @hide FutureFeature
+         */
+        public void onBackgroundScanComplete() {}
+
+        /**
+         * Called when available program list changed.
+         *
+         * Use getProgramList() to get the actual list.
+         *
+         * @hide FutureFeature
+         */
+        public void onProgramListChanged() {}
     }
 
 }
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 7e1b5ab..e5d73e0 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -59,6 +59,8 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.os.AppFuseMount;
 import com.android.internal.os.FuseAppLoop;
+import com.android.internal.os.FuseAppLoop.UnmountedException;
+import com.android.internal.os.FuseUnavailableMountException;
 import com.android.internal.os.RoSystemProperties;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.Preconditions;
@@ -82,6 +84,7 @@
 import java.util.Objects;
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.atomic.AtomicInteger;
+import libcore.io.IoUtils;
 
 /**
  * StorageManager is the interface to the systems storage service. The storage
@@ -1390,53 +1393,52 @@
     /** {@hide} */
     @VisibleForTesting
     public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
-            int mode, ProxyFileDescriptorCallback callback, ThreadFactory factory)
+            int mode, ProxyFileDescriptorCallback callback, Handler handler, ThreadFactory factory)
                     throws IOException {
         MetricsLogger.count(mContext, "storage_open_proxy_file_descriptor", 1);
         // Retry is needed because the mount point mFuseAppLoop is using may be unmounted before
         // invoking StorageManagerService#openProxyFileDescriptor. In this case, we need to re-mount
         // the bridge by calling mountProxyFileDescriptorBridge.
-        int retry = 3;
-        while (retry-- > 0) {
+        while (true) {
             try {
                 synchronized (mFuseAppLoopLock) {
+                    boolean newlyCreated = false;
                     if (mFuseAppLoop == null) {
                         final AppFuseMount mount = mStorageManager.mountProxyFileDescriptorBridge();
                         if (mount == null) {
-                            Log.e(TAG, "Failed to open proxy file bridge.");
-                            throw new IOException("Failed to open proxy file bridge.");
+                            throw new IOException("Failed to mount proxy bridge");
                         }
-                        mFuseAppLoop = FuseAppLoop.open(mount.mountPointId, mount.fd, factory);
+                        mFuseAppLoop = new FuseAppLoop(mount.mountPointId, mount.fd, factory);
+                        newlyCreated = true;
                     }
-
+                    if (handler == null) {
+                        handler = new Handler(Looper.getMainLooper());
+                    }
                     try {
-                        final int fileId = mFuseAppLoop.registerCallback(callback);
-                        final ParcelFileDescriptor pfd =
-                                mStorageManager.openProxyFileDescriptor(
-                                        mFuseAppLoop.getMountPointId(), fileId, mode);
-                        if (pfd != null) {
-                            return pfd;
+                        final int fileId = mFuseAppLoop.registerCallback(callback, handler);
+                        final ParcelFileDescriptor pfd = mStorageManager.openProxyFileDescriptor(
+                                mFuseAppLoop.getMountPointId(), fileId, mode);
+                        if (pfd == null) {
+                            mFuseAppLoop.unregisterCallback(fileId);
+                            throw new FuseUnavailableMountException(
+                                    mFuseAppLoop.getMountPointId());
                         }
-                        // Probably the bridge is being unmounted but mFuseAppLoop has not been
-                        // noticed it yet.
-                        mFuseAppLoop.unregisterCallback(fileId);
-                    } catch (FuseAppLoop.UnmountedException error) {
-                        Log.d(TAG, "mFuseAppLoop has been already unmounted.");
+                        return pfd;
+                    } catch (FuseUnavailableMountException exception) {
+                        // The bridge is being unmounted. Tried to recreate it unless the bridge was
+                        // just created.
+                        if (newlyCreated) {
+                            throw new IOException(exception);
+                        }
                         mFuseAppLoop = null;
                         continue;
                     }
                 }
-                try {
-                    Thread.sleep(100);
-                } catch (InterruptedException e) {
-                    break;
-                }
             } catch (RemoteException e) {
-                e.rethrowFromSystemServer();
+                // Cannot recover from remote exception.
+                throw new IOException(e);
             }
         }
-
-        throw new IOException("Failed to mount bridge.");
     }
 
     /**
@@ -1448,16 +1450,37 @@
      *     {@link ParcelFileDescriptor#MODE_WRITE_ONLY}, or
      *     {@link ParcelFileDescriptor#MODE_READ_WRITE}
      * @param callback Callback to process file operation requests issued on returned file
-     *     descriptor. The callback is invoked on a thread managed by the framework.
+     *     descriptor.
      * @return Seekable ParcelFileDescriptor.
      * @throws IOException
      */
     public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
             int mode, ProxyFileDescriptorCallback callback)
                     throws IOException {
-        return openProxyFileDescriptor(mode, callback, null);
+        return openProxyFileDescriptor(mode, callback, null, null);
     }
 
+    /**
+     * Opens seekable ParcelFileDescriptor that routes file operation requests to
+     * ProxyFileDescriptorCallback.
+     *
+     * @param mode The desired access mode, must be one of
+     *     {@link ParcelFileDescriptor#MODE_READ_ONLY},
+     *     {@link ParcelFileDescriptor#MODE_WRITE_ONLY}, or
+     *     {@link ParcelFileDescriptor#MODE_READ_WRITE}
+     * @param callback Callback to process file operation requests issued on returned file
+     *     descriptor.
+     * @param handler Handler that invokes callback methods.
+     * @return Seekable ParcelFileDescriptor.
+     * @throws IOException
+     */
+    public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
+            int mode, ProxyFileDescriptorCallback callback, Handler handler)
+                    throws IOException {
+        return openProxyFileDescriptor(mode, callback, handler, null);
+    }
+
+
     /** {@hide} */
     @VisibleForTesting
     public int getProxyFileDescriptorMountPointId() {
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 70f9bdd..14d3ad7 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -22,13 +22,11 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.FontListParser;
+import android.net.Uri;
 import android.os.Parcel;
-import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
 
-import java.io.IOException;
 import java.lang.annotation.Retention;
-import java.util.Arrays;
 
 
 /**
@@ -44,20 +42,6 @@
     }
 
     /**
-     * For duplicating file descriptors.
-     *
-     * Note that this copy constructor can not be usable for deep copy.
-     * @hide
-     */
-    public FontConfig(@NonNull FontConfig config) {
-        mFamilies = new Family[config.mFamilies.length];
-        for (int i = 0; i < config.mFamilies.length; ++i) {
-            mFamilies[i] = new Family(config.mFamilies[i]);
-        }
-        mAliases = Arrays.copyOf(config.mAliases, config.mAliases.length);
-    }
-
-    /**
      * Returns the ordered list of families included in the system fonts.
      */
     public @NonNull Family[] getFamilies() {
@@ -174,7 +158,7 @@
         private final @NonNull Axis[] mAxes;
         private final int mWeight;
         private final boolean mIsItalic;
-        private @Nullable ParcelFileDescriptor mFd;
+        private Uri mUri;
 
         /**
          * @hide
@@ -186,29 +170,6 @@
             mAxes = axes;
             mWeight = weight;
             mIsItalic = isItalic;
-            mFd = null;
-        }
-
-        /**
-         * This is for duplicating FileDescriptors.
-         *
-         * Note that this copy ctor doesn't deep copy the members.
-         *
-         * @hide
-         */
-        public Font(Font origin) {
-            mFontName = origin.mFontName;
-            mTtcIndex = origin.mTtcIndex;
-            mAxes = origin.mAxes;
-            mWeight = origin.mWeight;
-            mIsItalic = origin.mIsItalic;
-            if (origin.mFd != null) {
-                try {
-                    mFd = origin.mFd.dup();
-                } catch (IOException e) {
-                    e.printStackTrace();
-                }
-            }
         }
 
         /**
@@ -247,17 +208,20 @@
         }
 
         /**
-         * Returns a file descriptor to access the specified font. This should be closed after use.
+         * Returns the content uri associated to this font.
+         *
+         * You can reach to the font contents by calling {@link
+         * android.content.ContentResolver#openInputStream}.
          */
-        public @Nullable ParcelFileDescriptor getFd() {
-            return mFd;
+        public @Nullable Uri getUri() {
+            return mUri;
         }
 
         /**
          * @hide
          */
-        public void setFd(@NonNull ParcelFileDescriptor fd) {
-            mFd = fd;
+        public void setUri(@NonNull Uri uri) {
+            mUri = uri;
         }
 
         /**
@@ -269,11 +233,7 @@
             mAxes = in.createTypedArray(Axis.CREATOR);
             mWeight = in.readInt();
             mIsItalic = in.readInt() == 1;
-            if (in.readInt() == 1) { /* has FD */
-                mFd = ParcelFileDescriptor.CREATOR.createFromParcel(in);
-            } else {
-                mFd = null;
-            }
+            mUri = in.readTypedObject(Uri.CREATOR);
         }
 
         @Override
@@ -283,10 +243,7 @@
             out.writeTypedArray(mAxes, flag);
             out.writeInt(mWeight);
             out.writeInt(mIsItalic ? 1 : 0);
-            out.writeInt(mFd == null ? 0 : 1);
-            if (mFd != null) {
-                mFd.writeToParcel(out, flag);
-            }
+            out.writeTypedObject(mUri, flag);
         }
 
         @Override
@@ -425,22 +382,6 @@
         }
 
         /**
-         * For duplicating file descriptor underlying Font object.
-         *
-         * This copy constructor is not for deep copying.
-         * @hide
-         */
-        public Family(Family origin) {
-            mName = origin.mName;
-            mLanguage = origin.mLanguage;
-            mVariant = origin.mVariant;
-            mFonts = new Font[origin.mFonts.length];
-            for (int i = 0; i < origin.mFonts.length; ++i) {
-                mFonts[i] = new Font(origin.mFonts[i]);
-            }
-        }
-
-        /**
          * Returns the name given by the system to this font family.
          */
         public @Nullable String getName() {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3f52a9d..e924f77 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -61,6 +61,7 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.hardware.display.DisplayManagerGlobal;
+import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
@@ -4170,14 +4171,14 @@
     /**
      * When this flag is used with {@link #DRAG_FLAG_GLOBAL}, the drag recipient will be able to
      * request read access to the content URI(s) contained in the {@link ClipData} object.
-     * @see android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION
+     * @see android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION
      */
     public static final int DRAG_FLAG_GLOBAL_URI_READ = Intent.FLAG_GRANT_READ_URI_PERMISSION;
 
     /**
      * When this flag is used with {@link #DRAG_FLAG_GLOBAL}, the drag recipient will be able to
      * request write access to the content URI(s) contained in the {@link ClipData} object.
-     * @see android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+     * @see android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION
      */
     public static final int DRAG_FLAG_GLOBAL_URI_WRITE = Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
 
@@ -4185,8 +4186,8 @@
      * When this flag is used with {@link #DRAG_FLAG_GLOBAL_URI_READ} and/or {@link
      * #DRAG_FLAG_GLOBAL_URI_WRITE}, the URI permission grant can be persisted across device
      * reboots until explicitly revoked with
-     * {@link android.content.Context#revokeUriPermission(Uri,int) Context.revokeUriPermission}.
-     * @see android.content.Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+     * {@link android.content.Context#revokeUriPermission(Uri, int)} Context.revokeUriPermission}.
+     * @see android.content.Intent#FLAG_GRANT_PERSISTABLE_URI_PERMISSION
      */
     public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION =
             Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION;
@@ -4195,7 +4196,7 @@
      * When this flag is used with {@link #DRAG_FLAG_GLOBAL_URI_READ} and/or {@link
      * #DRAG_FLAG_GLOBAL_URI_WRITE}, the URI permission grant applies to any URI that is a prefix
      * match against the original granted URI.
-     * @see android.content.Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
+     * @see android.content.Intent#FLAG_GRANT_PREFIX_URI_PERMISSION
      */
     public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION =
             Intent.FLAG_GRANT_PREFIX_URI_PERMISSION;
@@ -7895,7 +7896,7 @@
      * @param arguments A {@link Bundle} holding any arguments relevant for this request. May be
      *                  {@code null} if the service provided no arguments.
      *
-     * @see AccessibilityNodeInfo#setExtraAvailableData
+     * @see AccessibilityNodeInfo#setAvailableExtraData(List)
      */
     public void addExtraDataToAccessibilityNodeInfo(
             @NonNull AccessibilityNodeInfo info, @NonNull String extraDataKey,
@@ -9839,7 +9840,7 @@
      * Focus gets restored for a view hierarchy when the root of the hierarchy gets added to a
      * window or serves as a target of cluster navigation.
      *
-     * @see #restoreDefaultFocus(int)
+     * @see #restoreDefaultFocus()
      *
      * @return {@code true} if this view is the default-focus view, {@code false} otherwise
      * @attr ref android.R.styleable#View_focusedByDefault
@@ -9859,7 +9860,7 @@
      * @param isFocusedByDefault {@code true} to set this view as the default-focus view,
      *                           {@code false} otherwise.
      *
-     * @see #restoreDefaultFocus(int)
+     * @see #restoreDefaultFocus()
      *
      * @attr ref android.R.styleable#View_focusedByDefault
      */
diff --git a/core/java/com/android/internal/app/ToolbarActionBar.java b/core/java/com/android/internal/app/ToolbarActionBar.java
index 7ce5fc3..b3904f4 100644
--- a/core/java/com/android/internal/app/ToolbarActionBar.java
+++ b/core/java/com/android/internal/app/ToolbarActionBar.java
@@ -477,12 +477,9 @@
             final KeyCharacterMap kmap = KeyCharacterMap.load(
                     event != null ? event.getDeviceId() : KeyCharacterMap.VIRTUAL_KEYBOARD);
             menu.setQwertyMode(kmap.getKeyboardType() != KeyCharacterMap.NUMERIC);
-            menu.performShortcut(keyCode, event, 0);
+            return menu.performShortcut(keyCode, event, 0);
         }
-        // This action bar always returns true for handling keyboard shortcuts.
-        // This will block the window from preparing a temporary panel to handle
-        // keyboard shortcuts.
-        return true;
+        return false;
     }
 
     @Override
@@ -525,6 +522,17 @@
             }
             return result;
         }
+
+        @Override
+        public View onCreatePanelView(int featureId) {
+            if (featureId == Window.FEATURE_OPTIONS_PANEL) {
+                // This gets called by PhoneWindow.preparePanel. Since this already manages
+                // its own panel, we return a dummy view here to prevent PhoneWindow from
+                // preparing a default one.
+                return new View(mDecorToolbar.getContext());
+            }
+            return super.onCreatePanelView(featureId);
+        }
     }
 
     private final class ActionMenuPresenterCallback implements MenuPresenter.Callback {
diff --git a/core/java/com/android/internal/os/FuseAppLoop.java b/core/java/com/android/internal/os/FuseAppLoop.java
index 3603b6d..8edd637 100644
--- a/core/java/com/android/internal/os/FuseAppLoop.java
+++ b/core/java/com/android/internal/os/FuseAppLoop.java
@@ -19,16 +19,16 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.ProxyFileDescriptorCallback;
+import android.os.Handler;
 import android.os.ParcelFileDescriptor;
 import android.system.ErrnoException;
 import android.system.OsConstants;
 import android.util.Log;
 import android.util.SparseArray;
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
-
-import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.concurrent.ThreadFactory;
 
 public class FuseAppLoop {
@@ -42,14 +42,21 @@
             return new Thread(r, TAG);
         }
     };
+    private static final int FUSE_OK = 0;
 
     private final Object mLock = new Object();
     private final int mMountPointId;
     private final Thread mThread;
+    private final Handler mDefaultHandler;
+
+    private static final int CMD_FSYNC = 1;
 
     @GuardedBy("mLock")
     private final SparseArray<CallbackEntry> mCallbackMap = new SparseArray<>();
 
+    @GuardedBy("mLock")
+    private final BytesMap mBytesMap = new BytesMap();
+
     /**
      * Sequential number can be used as file name and inode in AppFuse.
      * 0 is regarded as an error, 1 is mount point. So we start the number from 2.
@@ -57,38 +64,40 @@
     @GuardedBy("mLock")
     private int mNextInode = MIN_INODE;
 
-    private FuseAppLoop(
+    @GuardedBy("mLock")
+    private long mInstance;
+
+    public FuseAppLoop(
             int mountPointId, @NonNull ParcelFileDescriptor fd, @Nullable ThreadFactory factory) {
         mMountPointId = mountPointId;
-        final int rawFd = fd.detachFd();
         if (factory == null) {
             factory = sDefaultThreadFactory;
         }
-        mThread = factory.newThread(new Runnable() {
-            @Override
-            public void run() {
-                // rawFd is closed by native_start_loop. Java code does not need to close it.
-                native_start_loop(rawFd);
+        mInstance = native_new(fd.detachFd());
+        mThread = factory.newThread(() -> {
+            native_start(mInstance);
+            synchronized (mLock) {
+                native_delete(mInstance);
+                mInstance = 0;
+                mBytesMap.clear();
             }
         });
+        mThread.start();
+        mDefaultHandler = null;
     }
 
-    public static @NonNull FuseAppLoop open(int mountPointId, @NonNull ParcelFileDescriptor fd,
-            @Nullable ThreadFactory factory) {
-        Preconditions.checkNotNull(fd);
-        final FuseAppLoop loop = new FuseAppLoop(mountPointId, fd, factory);
-        loop.mThread.start();
-        return loop;
-    }
-
-    public int registerCallback(@NonNull ProxyFileDescriptorCallback callback)
-            throws UnmountedException, IOException {
-        if (mThread.getState() == Thread.State.TERMINATED) {
-            throw new UnmountedException();
-        }
+    public int registerCallback(@NonNull ProxyFileDescriptorCallback callback,
+            @NonNull Handler handler) throws FuseUnavailableMountException {
         synchronized (mLock) {
-            if (mCallbackMap.size() >= Integer.MAX_VALUE - MIN_INODE) {
-                throw new IOException("Too many opened files.");
+            Preconditions.checkNotNull(callback);
+            Preconditions.checkNotNull(handler);
+            Preconditions.checkState(
+                    mCallbackMap.size() < Integer.MAX_VALUE - MIN_INODE, "Too many opened files.");
+            Preconditions.checkArgument(
+                    Thread.currentThread().getId() != handler.getLooper().getThread().getId(),
+                    "Handler must be different from the current thread");
+            if (mInstance == 0) {
+                throw new FuseUnavailableMountException(mMountPointId);
             }
             int id;
             while (true) {
@@ -101,118 +110,171 @@
                     break;
                 }
             }
-            mCallbackMap.put(id, new CallbackEntry(callback));
+            mCallbackMap.put(id, new CallbackEntry(callback, handler));
             return id;
         }
     }
 
     public void unregisterCallback(int id) {
-        mCallbackMap.remove(id);
+        synchronized (mLock) {
+            mCallbackMap.remove(id);
+        }
     }
 
     public int getMountPointId() {
         return mMountPointId;
     }
 
-    private CallbackEntry getCallbackEntryOrThrowLocked(long inode) throws ErrnoException {
-        final CallbackEntry entry = mCallbackMap.get(checkInode(inode));
-        if (entry != null) {
-            return entry;
-        } else {
-            throw new ErrnoException("getCallbackEntry", OsConstants.ENOENT);
-        }
-    }
+    // Defined in fuse.h
+    private static final int FUSE_LOOKUP = 1;
+    private static final int FUSE_GETATTR = 3;
+    private static final int FUSE_OPEN = 14;
+    private static final int FUSE_READ = 15;
+    private static final int FUSE_WRITE = 16;
+    private static final int FUSE_RELEASE = 18;
+    private static final int FUSE_FSYNC = 20;
+
+    // Defined in FuseBuffer.h
+    private static final int FUSE_MAX_WRITE = 256 * 1024;
 
     // Called by JNI.
     @SuppressWarnings("unused")
-    private long onGetSize(long inode) {
+    private void onCommand(int command, long unique, long inode, long offset, int size,
+            byte[] data) {
         synchronized(mLock) {
             try {
-                return getCallbackEntryOrThrowLocked(inode).callback.onGetSize();
-            } catch (ErrnoException exp) {
-                return getError(exp);
+                final CallbackEntry entry = getCallbackEntryOrThrowLocked(inode);
+                entry.postRunnable(() -> {
+                    try {
+                        switch (command) {
+                            case FUSE_LOOKUP: {
+                                final long fileSize = entry.callback.onGetSize();
+                                synchronized (mLock) {
+                                    if (mInstance != 0) {
+                                        native_replyLookup(mInstance, unique, inode, fileSize);
+                                    }
+                                }
+                                break;
+                            }
+                            case FUSE_GETATTR: {
+                                final long fileSize = entry.callback.onGetSize();
+                                synchronized (mLock) {
+                                    if (mInstance != 0) {
+                                        native_replyGetAttr(mInstance, unique, inode, fileSize);
+                                    }
+                                }
+                                break;
+                            }
+                            case FUSE_READ:
+                                final int readSize = entry.callback.onRead(offset, size, data);
+                                synchronized (mLock) {
+                                    if (mInstance != 0) {
+                                        native_replyRead(mInstance, unique, readSize, data);
+                                    }
+                                }
+                                break;
+                            case FUSE_WRITE:
+                                final int writeSize = entry.callback.onWrite(offset, size, data);
+                                synchronized (mLock) {
+                                    if (mInstance != 0) {
+                                        native_replyWrite(mInstance, unique, writeSize);
+                                    }
+                                }
+                                break;
+                            case FUSE_FSYNC:
+                                entry.callback.onFsync();
+                                synchronized (mLock) {
+                                    if (mInstance != 0) {
+                                        native_replySimple(mInstance, unique, FUSE_OK);
+                                    }
+                                }
+                                break;
+                            case FUSE_RELEASE:
+                                entry.callback.onRelease();
+                                synchronized (mLock) {
+                                    if (mInstance != 0) {
+                                        native_replySimple(mInstance, unique, FUSE_OK);
+                                    }
+                                    mBytesMap.stopUsing(entry.getThreadId());
+                                }
+                                break;
+                            default:
+                                throw new IllegalArgumentException(
+                                        "Unknown FUSE command: " + command);
+                        }
+                    } catch (Exception error) {
+                        Log.e(TAG, "", error);
+                        replySimple(unique, getError(error));
+                    }
+                });
+            } catch (ErrnoException error) {
+                Log.e(TAG, "", error);
+                replySimpleLocked(unique, getError(error));
             }
         }
     }
 
     // Called by JNI.
     @SuppressWarnings("unused")
-    private int onOpen(long inode) {
-        synchronized(mLock) {
+    private byte[] onOpen(long unique, long inode) {
+        synchronized (mLock) {
             try {
                 final CallbackEntry entry = getCallbackEntryOrThrowLocked(inode);
                 if (entry.opened) {
                     throw new ErrnoException("onOpen", OsConstants.EMFILE);
                 }
-                entry.opened = true;
-                // Use inode as file handle. It's OK because AppFuse does not allow to open the same
-                // file twice.
-                return (int) inode;
-            } catch (ErrnoException exp) {
-                return getError(exp);
+                if (mInstance != 0) {
+                    native_replyOpen(mInstance, unique, /* fh */ inode);
+                    entry.opened = true;
+                    return mBytesMap.startUsing(entry.getThreadId());
+                }
+            } catch (ErrnoException error) {
+                replySimpleLocked(unique, getError(error));
             }
+            return null;
         }
     }
 
-    // Called by JNI.
-    @SuppressWarnings("unused")
-    private int onFsync(long inode) {
-        synchronized(mLock) {
-            try {
-                getCallbackEntryOrThrowLocked(inode).callback.onFsync();
-                return 0;
-            } catch (ErrnoException exp) {
-                return getError(exp);
+    private static int getError(@NonNull Exception error) {
+        if (error instanceof ErrnoException) {
+            final int errno = ((ErrnoException) error).errno;
+            if (errno != OsConstants.ENOSYS) {
+                return -errno;
             }
         }
+        return -OsConstants.EBADF;
+    }
+
+    private CallbackEntry getCallbackEntryOrThrowLocked(long inode) throws ErrnoException {
+        final CallbackEntry entry = mCallbackMap.get(checkInode(inode));
+        if (entry == null) {
+            throw new ErrnoException("getCallbackEntryOrThrowLocked", OsConstants.ENOENT);
+        }
+        return entry;
+    }
+
+    private void replySimple(long unique, int result) {
+        synchronized (mLock) {
+            replySimpleLocked(unique, result);
+        }
     }
 
-    // Called by JNI.
-    @SuppressWarnings("unused")
-    private int onRelease(long inode) {
-        synchronized(mLock) {
-            try {
-                getCallbackEntryOrThrowLocked(inode).callback.onRelease();
-                return 0;
-            } catch (ErrnoException exp) {
-                return getError(exp);
-            } finally {
-                mCallbackMap.remove(checkInode(inode));
-            }
+    private void replySimpleLocked(long unique, int result) {
+        if (mInstance != 0) {
+            native_replySimple(mInstance, unique, result);
         }
     }
 
-    // Called by JNI.
-    @SuppressWarnings("unused")
-    private int onRead(long inode, long offset, int size, byte[] bytes) {
-        synchronized(mLock) {
-            try {
-                return getCallbackEntryOrThrowLocked(inode).callback.onRead(offset, size, bytes);
-            } catch (ErrnoException exp) {
-                return getError(exp);
-            }
-        }
-    }
+    native long native_new(int fd);
+    native void native_delete(long ptr);
+    native void native_start(long ptr);
 
-    // Called by JNI.
-    @SuppressWarnings("unused")
-    private int onWrite(long inode, long offset, int size, byte[] bytes) {
-        synchronized(mLock) {
-            try {
-                return getCallbackEntryOrThrowLocked(inode).callback.onWrite(offset, size, bytes);
-            } catch (ErrnoException exp) {
-                return getError(exp);
-            }
-        }
-    }
-
-    private static int getError(@NonNull ErrnoException exp) {
-        // Should not return ENOSYS because the kernel stops
-        // dispatching the FUSE action once FUSE implementation returns ENOSYS for the action.
-        return exp.errno != OsConstants.ENOSYS ? -exp.errno : -OsConstants.EIO;
-    }
-
-    native boolean native_start_loop(int fd);
+    native void native_replySimple(long ptr, long unique, int result);
+    native void native_replyOpen(long ptr, long unique, long fh);
+    native void native_replyLookup(long ptr, long unique, long inode, long size);
+    native void native_replyGetAttr(long ptr, long unique, long inode, long size);
+    native void native_replyWrite(long ptr, long unique, int size);
+    native void native_replyRead(long ptr, long unique, int size, byte[] bytes);
 
     private static int checkInode(long inode) {
         Preconditions.checkArgumentInRange(inode, MIN_INODE, Integer.MAX_VALUE, "checkInode");
@@ -223,10 +285,61 @@
 
     private static class CallbackEntry {
         final ProxyFileDescriptorCallback callback;
+        final Handler handler;
         boolean opened;
-        CallbackEntry(ProxyFileDescriptorCallback callback) {
-            Preconditions.checkNotNull(callback);
-            this.callback = callback;
+
+        CallbackEntry(ProxyFileDescriptorCallback callback, Handler handler) {
+            this.callback = Preconditions.checkNotNull(callback);
+            this.handler = Preconditions.checkNotNull(handler);
+        }
+
+        long getThreadId() {
+            return handler.getLooper().getThread().getId();
+        }
+
+        void postRunnable(Runnable runnable) throws ErrnoException {
+            final boolean result = handler.post(runnable);
+            if (!result) {
+                throw new ErrnoException("postRunnable", OsConstants.EBADF);
+            }
+        }
+    }
+
+    /**
+     * Entry for bytes map.
+     */
+    private static class BytesMapEntry {
+        int counter = 0;
+        byte[] bytes = new byte[FUSE_MAX_WRITE];
+    }
+
+    /**
+     * Map between Thread ID and byte buffer.
+     */
+    private static class BytesMap {
+        final Map<Long, BytesMapEntry> mEntries = new HashMap<>();
+
+        byte[] startUsing(long threadId) {
+            BytesMapEntry entry = mEntries.get(threadId);
+            if (entry == null) {
+                entry = new BytesMapEntry();
+                mEntries.put(threadId, entry);
+            }
+            entry.counter++;
+            return entry.bytes;
+        }
+
+        void stopUsing(long threadId) {
+            final BytesMapEntry entry = mEntries.get(threadId);
+            Preconditions.checkNotNull(entry);
+            entry.counter--;
+            if (entry.counter <= 0) {
+                mEntries.remove(threadId);
+            }
+        }
+
+        void clear() {
+            mEntries.clear();
         }
     }
 }
diff --git a/core/java/com/android/internal/os/FuseUnavailableMountException.java b/core/java/com/android/internal/os/FuseUnavailableMountException.java
new file mode 100644
index 0000000..ca3cfb9
--- /dev/null
+++ b/core/java/com/android/internal/os/FuseUnavailableMountException.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 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.internal.os;
+
+/**
+ * Exception occurred when the mount point has already been unavailable.
+ */
+public class FuseUnavailableMountException extends Exception {
+    public FuseUnavailableMountException(int mountId) {
+        super("AppFuse mount point " + mountId + " is unavailable");
+    }
+}
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 713287e..8a7d1cf 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -135,6 +135,7 @@
         case PublicFormat::DEPTH16:
             return HAL_PIXEL_FORMAT_Y16;
         case PublicFormat::RAW_SENSOR:
+        case PublicFormat::RAW_DEPTH:
             return HAL_PIXEL_FORMAT_RAW16;
         default:
             // Most formats map 1:1
@@ -149,6 +150,7 @@
             return HAL_DATASPACE_V0_JFIF;
         case PublicFormat::DEPTH_POINT_CLOUD:
         case PublicFormat::DEPTH16:
+        case PublicFormat::RAW_DEPTH:
             return HAL_DATASPACE_DEPTH;
         case PublicFormat::RAW_SENSOR:
         case PublicFormat::RAW_PRIVATE:
@@ -182,8 +184,12 @@
             // Enums overlap in both name and value
             return static_cast<PublicFormat>(format);
         case HAL_PIXEL_FORMAT_RAW16:
-            // Name differs, though value is the same
-            return PublicFormat::RAW_SENSOR;
+            switch (dataSpace) {
+                case HAL_DATASPACE_DEPTH:
+                  return PublicFormat::RAW_DEPTH;
+                default:
+                  return PublicFormat::RAW_SENSOR;
+            }
         case HAL_PIXEL_FORMAT_RAW_OPAQUE:
             // Name differs, though value is the same
             return PublicFormat::RAW_PRIVATE;
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 6fbf49b..066ce68 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -167,7 +167,7 @@
             buffer->getHeight(),
             buffer->getPixelFormat(),
             buffer->getUsage(),
-            (void*)buffer.get());
+            (jlong)buffer.get());
 }
 
 static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz,
diff --git a/core/jni/com_android_internal_os_FuseAppLoop.cpp b/core/jni/com_android_internal_os_FuseAppLoop.cpp
index dd003eb..e125150 100644
--- a/core/jni/com_android_internal_os_FuseAppLoop.cpp
+++ b/core/jni/com_android_internal_os_FuseAppLoop.cpp
@@ -20,140 +20,214 @@
 #include <stdlib.h>
 #include <sys/stat.h>
 
+#include <map>
+#include <memory>
+
 #include <android_runtime/Log.h>
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <jni.h>
 #include <libappfuse/FuseAppLoop.h>
 #include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
 
 #include "core_jni_helpers.h"
 
 namespace android {
-
 namespace {
-
 constexpr const char* CLASS_NAME = "com/android/internal/os/FuseAppLoop";
 
 jclass gFuseAppLoopClass;
-jmethodID gOnGetSizeMethod;
+jmethodID gOnCommandMethod;
 jmethodID gOnOpenMethod;
-jmethodID gOnFsyncMethod;
-jmethodID gOnReleaseMethod;
-jmethodID gOnReadMethod;
-jmethodID gOnWriteMethod;
 
 class Callback : public fuse::FuseAppLoopCallback {
 private:
-    static constexpr size_t kBufferSize = std::max(fuse::kFuseMaxWrite, fuse::kFuseMaxRead);
-    static_assert(kBufferSize <= INT32_MAX, "kBufferSize should be fit in int32_t.");
-
+    typedef ScopedLocalRef<jbyteArray> LocalBytes;
     JNIEnv* const mEnv;
     jobject const mSelf;
-    ScopedLocalRef<jbyteArray> mJniBuffer;
-
-    template <typename T>
-    T checkException(T result) const {
-        if (mEnv->ExceptionCheck()) {
-            LOGE_EX(mEnv, nullptr);
-            mEnv->ExceptionClear();
-            return -EIO;
-        }
-        return result;
-    }
+    std::map<uint64_t, std::unique_ptr<LocalBytes>> mBuffers;
 
 public:
     Callback(JNIEnv* env, jobject self) :
-        mEnv(env),
-        mSelf(self),
-        mJniBuffer(env, nullptr) {}
+        mEnv(env), mSelf(self) {}
 
-    bool Init() {
-        mJniBuffer.reset(mEnv->NewByteArray(kBufferSize));
-        return mJniBuffer.get();
+    void OnLookup(uint64_t unique, uint64_t inode) override {
+        mEnv->CallVoidMethod(mSelf, gOnCommandMethod, FUSE_LOOKUP, unique, inode, 0, 0, nullptr);
+        CHECK(!mEnv->ExceptionCheck());
     }
 
-    bool IsActive() override {
-        return true;
+    void OnGetAttr(uint64_t unique, uint64_t inode) override {
+        mEnv->CallVoidMethod(mSelf, gOnCommandMethod, FUSE_GETATTR, unique, inode, 0, 0, nullptr);
+        CHECK(!mEnv->ExceptionCheck());
     }
 
-    int64_t OnGetSize(uint64_t inode) override {
-        return checkException(mEnv->CallLongMethod(mSelf, gOnGetSizeMethod, inode));
-    }
-
-    int32_t OnOpen(uint64_t inode) override {
-        return checkException(mEnv->CallIntMethod(mSelf, gOnOpenMethod, inode));
-    }
-
-    int32_t OnFsync(uint64_t inode) override {
-        return checkException(mEnv->CallIntMethod(mSelf, gOnFsyncMethod, inode));
-    }
-
-    int32_t OnRelease(uint64_t inode) override {
-        return checkException(mEnv->CallIntMethod(mSelf, gOnReleaseMethod, inode));
-    }
-
-    int32_t OnRead(uint64_t inode, uint64_t offset, uint32_t size, void* buffer) override {
-        CHECK_LE(size, static_cast<uint32_t>(kBufferSize));
-        const int32_t result = checkException(mEnv->CallIntMethod(
-                mSelf, gOnReadMethod, inode, offset, size, mJniBuffer.get()));
-        if (result <= 0) {
-            return result;
-        }
-        if (result > static_cast<int32_t>(size)) {
-            LOG(ERROR) << "Returned size is too large.";
-            return -EIO;
+    void OnOpen(uint64_t unique, uint64_t inode) override {
+        const jbyteArray buffer = static_cast<jbyteArray>(mEnv->CallObjectMethod(
+                mSelf, gOnOpenMethod, unique, inode));
+        CHECK(!mEnv->ExceptionCheck());
+        if (buffer == nullptr) {
+            return;
         }
 
-        mEnv->GetByteArrayRegion(mJniBuffer.get(), 0, result, static_cast<jbyte*>(buffer));
-        CHECK(!mEnv->ExceptionCheck());
-
-        return checkException(result);
+        mBuffers.insert(std::make_pair(inode, std::unique_ptr<LocalBytes>(
+                new LocalBytes(mEnv, buffer))));
     }
 
-    int32_t OnWrite(uint64_t inode, uint64_t offset, uint32_t size, const void* buffer) override {
-        CHECK_LE(size, static_cast<uint32_t>(kBufferSize));
+    void OnFsync(uint64_t unique, uint64_t inode) override {
+        mEnv->CallVoidMethod(mSelf, gOnCommandMethod, FUSE_FSYNC, unique, inode, 0, 0, nullptr);
+        CHECK(!mEnv->ExceptionCheck());
+    }
 
-        mEnv->SetByteArrayRegion(mJniBuffer.get(), 0, size, static_cast<const jbyte*>(buffer));
+    void OnRelease(uint64_t unique, uint64_t inode) override {
+        mBuffers.erase(inode);
+        mEnv->CallVoidMethod(mSelf, gOnCommandMethod, FUSE_RELEASE, unique, inode, 0, 0, nullptr);
+        CHECK(!mEnv->ExceptionCheck());
+    }
+
+    void OnRead(uint64_t unique, uint64_t inode, uint64_t offset, uint32_t size) override {
+        CHECK_LE(size, static_cast<uint32_t>(fuse::kFuseMaxRead));
+
+        auto it = mBuffers.find(inode);
+        CHECK(it != mBuffers.end());
+
+        mEnv->CallVoidMethod(
+                mSelf, gOnCommandMethod, FUSE_READ, unique, inode, offset, size,
+                it->second->get());
+        CHECK(!mEnv->ExceptionCheck());
+    }
+
+    void OnWrite(uint64_t unique, uint64_t inode, uint64_t offset, uint32_t size,
+            const void* buffer) override {
+        CHECK_LE(size, static_cast<uint32_t>(fuse::kFuseMaxWrite));
+
+        auto it = mBuffers.find(inode);
+        CHECK(it != mBuffers.end());
+
+        jbyteArray const javaBuffer = it->second->get();
+
+        mEnv->SetByteArrayRegion(javaBuffer, 0, size, static_cast<const jbyte*>(buffer));
         CHECK(!mEnv->ExceptionCheck());
 
-        return checkException(mEnv->CallIntMethod(
-                mSelf, gOnWriteMethod, inode, offset, size, mJniBuffer.get()));
+        mEnv->CallVoidMethod(
+                mSelf, gOnCommandMethod, FUSE_WRITE, unique, inode, offset, size, javaBuffer);
+        CHECK(!mEnv->ExceptionCheck());
     }
 };
 
-jboolean com_android_internal_os_FuseAppLoop_start_loop(JNIEnv* env, jobject self, jint jfd) {
-    base::unique_fd fd(jfd);
+jlong com_android_internal_os_FuseAppLoop_new(JNIEnv* env, jobject self, jint jfd) {
+    return reinterpret_cast<jlong>(new fuse::FuseAppLoop(base::unique_fd(jfd)));
+}
+
+void com_android_internal_os_FuseAppLoop_delete(JNIEnv* env, jobject self, jlong ptr) {
+    delete reinterpret_cast<fuse::FuseAppLoop*>(ptr);
+}
+
+void com_android_internal_os_FuseAppLoop_start(JNIEnv* env, jobject self, jlong ptr) {
     Callback callback(env, self);
+    reinterpret_cast<fuse::FuseAppLoop*>(ptr)->Start(&callback);
+}
 
-    if (!callback.Init()) {
-        LOG(ERROR) << "Failed to init callback";
-        return JNI_FALSE;
+void com_android_internal_os_FuseAppLoop_replySimple(
+        JNIEnv* env, jobject self, jlong ptr, jlong unique, jint result) {
+    if (!reinterpret_cast<fuse::FuseAppLoop*>(ptr)->ReplySimple(unique, result)) {
+        reinterpret_cast<fuse::FuseAppLoop*>(ptr)->Break();
     }
+}
 
-    return fuse::StartFuseAppLoop(fd.release(), &callback);
+void com_android_internal_os_FuseAppLoop_replyOpen(
+        JNIEnv* env, jobject self, jlong ptr, jlong unique, jlong fh) {
+    if (!reinterpret_cast<fuse::FuseAppLoop*>(ptr)->ReplyOpen(unique, fh)) {
+        reinterpret_cast<fuse::FuseAppLoop*>(ptr)->Break();
+    }
+}
+
+void com_android_internal_os_FuseAppLoop_replyLookup(
+        JNIEnv* env, jobject self, jlong ptr, jlong unique, jlong inode, jint size) {
+    if (!reinterpret_cast<fuse::FuseAppLoop*>(ptr)->ReplyLookup(unique, inode, size)) {
+        reinterpret_cast<fuse::FuseAppLoop*>(ptr)->Break();
+    }
+}
+
+void com_android_internal_os_FuseAppLoop_replyGetAttr(
+        JNIEnv* env, jobject self, jlong ptr, jlong unique, jlong inode, jint size) {
+    if (!reinterpret_cast<fuse::FuseAppLoop*>(ptr)->ReplyGetAttr(
+            unique, inode, size, S_IFREG | 0777)) {
+        reinterpret_cast<fuse::FuseAppLoop*>(ptr)->Break();
+    }
+}
+
+void com_android_internal_os_FuseAppLoop_replyWrite(
+        JNIEnv* env, jobject self, jlong ptr, jlong unique, jint size) {
+    if (!reinterpret_cast<fuse::FuseAppLoop*>(ptr)->ReplyWrite(unique, size)) {
+        reinterpret_cast<fuse::FuseAppLoop*>(ptr)->Break();
+    }
+}
+
+void com_android_internal_os_FuseAppLoop_replyRead(
+        JNIEnv* env, jobject self, jlong ptr, jlong unique, jint size, jbyteArray data) {
+    ScopedByteArrayRO array(env, data);
+    CHECK(size >= 0);
+    CHECK(static_cast<size_t>(size) < array.size());
+    if (!reinterpret_cast<fuse::FuseAppLoop*>(ptr)->ReplyRead(unique, size, array.get())) {
+        reinterpret_cast<fuse::FuseAppLoop*>(ptr)->Break();
+    }
 }
 
 const JNINativeMethod methods[] = {
     {
-        "native_start_loop",
-        "(I)Z",
-        (void *) com_android_internal_os_FuseAppLoop_start_loop
-    }
+        "native_new",
+        "(I)J",
+        reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_new)
+    },
+    {
+        "native_delete",
+        "(J)V",
+        reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_delete)
+    },
+    {
+        "native_start",
+        "(J)V",
+        reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_start)
+    },
+    {
+        "native_replySimple",
+        "(JJI)V",
+        reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_replySimple)
+    },
+    {
+        "native_replyOpen",
+        "(JJJ)V",
+        reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_replyOpen)
+    },
+    {
+        "native_replyLookup",
+        "(JJJJ)V",
+        reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_replyLookup)
+    },
+    {
+        "native_replyGetAttr",
+        "(JJJJ)V",
+        reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_replyGetAttr)
+    },
+    {
+        "native_replyRead",
+        "(JJI[B)V",
+        reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_replyRead)
+    },
+    {
+        "native_replyWrite",
+        "(JJI)V",
+        reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_replyWrite)
+    },
 };
-
 }  // namespace
 
 int register_com_android_internal_os_FuseAppLoop(JNIEnv* env) {
     gFuseAppLoopClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, CLASS_NAME));
-    gOnGetSizeMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onGetSize", "(J)J");
-    gOnOpenMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onOpen", "(J)I");
-    gOnFsyncMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onFsync", "(J)I");
-    gOnReleaseMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onRelease", "(J)I");
-    gOnReadMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onRead", "(JJI[B)I");
-    gOnWriteMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onWrite", "(JJI[B)I");
+    gOnCommandMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onCommand", "(IJJJI[B)V");
+    gOnOpenMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onOpen", "(JJ)[B");
     RegisterMethodsOrDie(env, CLASS_NAME, methods, NELEM(methods));
     return 0;
 }
-
 }  // namespace android
diff --git a/core/jni/include/android_runtime/android_view_Surface.h b/core/jni/include/android_runtime/android_view_Surface.h
index 3f1bdff..d27c5a3 100644
--- a/core/jni/include/android_runtime/android_view_Surface.h
+++ b/core/jni/include/android_runtime/android_view_Surface.h
@@ -53,6 +53,7 @@
     RGBA_1010102      = 0x2b,
     JPEG              = 0x100,
     DEPTH_POINT_CLOUD = 0x101,
+    RAW_DEPTH         = 0x1002, // @hide
     YV12              = 0x32315659,
     Y8                = 0x20203859, // @hide
     Y16               = 0x20363159, // @hide
diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
index ff98eb7..fbba6ff 100644
--- a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
+++ b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
@@ -264,7 +264,7 @@
         final MyThreadFactory factory = new MyThreadFactory();
         int firstMountId;
         try (final ParcelFileDescriptor fd = mSm.openProxyFileDescriptor(
-                ParcelFileDescriptor.MODE_READ_ONLY, callback, factory)) {
+                ParcelFileDescriptor.MODE_READ_ONLY, callback, null, factory)) {
             assertNotSame(Thread.State.TERMINATED, factory.thread.getState());
             firstMountId = mSm.getProxyFileDescriptorMountPointId();
             assertNotSame(-1, firstMountId);
@@ -276,7 +276,7 @@
 
         // StorageManager should mount another bridge on the next open request.
         try (final ParcelFileDescriptor fd = mSm.openProxyFileDescriptor(
-                ParcelFileDescriptor.MODE_WRITE_ONLY, callback, factory)) {
+                ParcelFileDescriptor.MODE_WRITE_ONLY, callback, null, factory)) {
             assertNotSame(Thread.State.TERMINATED, factory.thread.getState());
             assertNotSame(firstMountId, mSm.getProxyFileDescriptorMountPointId());
         }
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index b78df34..ff9f11d 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -189,9 +189,8 @@
                 skip(parser);
             }
         }
-        String fullFilename = "/system/fonts/" +
-                FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll("");
-        return new FontConfig.Font(fullFilename, index,
+        String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll("");
+        return new FontConfig.Font(sanitizedName, index,
                 axes.toArray(new FontConfig.Axis[axes.size()]), weight, isItalic);
     }
 
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index a226e85..e3527e3 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -662,6 +662,15 @@
     public static final int DEPTH_POINT_CLOUD = 0x101;
 
     /**
+     * Unprocessed implementation-dependent raw
+     * depth measurements, opaque with 16 bit
+     * samples.
+     *
+     * @hide
+     */
+    public static final int RAW_DEPTH = 0x1002;
+
+    /**
      * Android private opaque image format.
      * <p>
      * The choices of the actual format and pixel data layout are entirely up to
@@ -723,6 +732,7 @@
                 return 24;
             case FLEX_RGBA_8888:
                 return 32;
+            case RAW_DEPTH:
             case RAW_SENSOR:
                 return 16;
             case RAW10:
@@ -765,6 +775,7 @@
             case DEPTH16:
             case DEPTH_POINT_CLOUD:
             case PRIVATE:
+            case RAW_DEPTH:
                 return true;
         }
 
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 228d950..8c3a2e8 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -1003,21 +1003,22 @@
             Map<String, ByteBuffer> bufferForPath) {
         FontFamily fontFamily = new FontFamily(family.getLanguage(), family.getVariant());
         for (FontConfig.Font font : family.getFonts()) {
-            ByteBuffer fontBuffer = bufferForPath.get(font.getFontName());
+            String fullPathName = "/system/fonts/" + font.getFontName();
+            ByteBuffer fontBuffer = bufferForPath.get(fullPathName);
             if (fontBuffer == null) {
-                try (FileInputStream file = new FileInputStream(font.getFontName())) {
+                try (FileInputStream file = new FileInputStream(fullPathName)) {
                     FileChannel fileChannel = file.getChannel();
                     long fontSize = fileChannel.size();
                     fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
-                    bufferForPath.put(font.getFontName(), fontBuffer);
+                    bufferForPath.put(fullPathName, fontBuffer);
                 } catch (IOException e) {
-                    Log.e(TAG, "Error mapping font file " + font.getFontName());
+                    Log.e(TAG, "Error mapping font file " + fullPathName);
                     continue;
                 }
             }
             if (!fontFamily.addFontFromBuffer(fontBuffer, font.getTtcIndex(), font.getAxes(),
                     font.getWeight(), font.isItalic() ? Builder.ITALIC : Builder.NORMAL)) {
-                Log.e(TAG, "Error creating font " + font.getFontName() + "#" + font.getTtcIndex());
+                Log.e(TAG, "Error creating font " + fullPathName + "#" + font.getTtcIndex());
             }
         }
         fontFamily.freeze();
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 8823a92..f6b2912 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -491,6 +491,36 @@
     return *mCache.bitmap;
 }
 
+void Tree::updateCache(sk_sp<SkSurface> surface) {
+    if (surface.get()) {
+        mCache.surface = surface;
+    }
+    if (surface.get() || mCache.dirty) {
+        SkSurface* vdSurface = mCache.surface.get();
+        SkCanvas* canvas = vdSurface->getCanvas();
+        float scaleX = vdSurface->width() / mProperties.getViewportWidth();
+        float scaleY = vdSurface->height() / mProperties.getViewportHeight();
+        SkAutoCanvasRestore acr(canvas, true);
+        canvas->clear(SK_ColorTRANSPARENT);
+        canvas->scale(scaleX, scaleY);
+        mRootNode->draw(canvas, false);
+        mCache.dirty = false;
+        canvas->flush();
+    }
+}
+
+void Tree::draw(SkCanvas* canvas) {
+   /*
+    * TODO address the following...
+    *
+    * 1) figure out how to set path's as volatile during animation
+    * 2) if mRoot->getPaint() != null either promote to layer (during
+    *    animation) or cache in SkSurface (for static content)
+    */
+    canvas->drawImageRect(mCache.surface->makeImageSnapshot().get(),
+        mutateProperties()->getBounds(), getPaint());
+}
+
 void Tree::updateBitmapCache(Bitmap& bitmap, bool useStagingData) {
     SkBitmap outCache;
     bitmap.getSkBitmap(&outCache);
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index 729a4dd..22cfe29 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -31,6 +31,7 @@
 #include <SkPathMeasure.h>
 #include <SkRect.h>
 #include <SkShader.h>
+#include <SkSurface.h>
 
 #include <cutils/compiler.h>
 #include <stddef.h>
@@ -677,15 +678,37 @@
     // This should only be called from animations on RT
     TreeProperties* mutateProperties() { return &mProperties; }
 
+    // called from RT only
+    const TreeProperties& properties() const { return mProperties; }
+
     // This should always be called from RT.
     void markDirty() { mCache.dirty = true; }
     bool isDirty() const { return mCache.dirty; }
     bool getPropertyChangeWillBeConsumed() const { return mWillBeConsumed; }
     void setPropertyChangeWillBeConsumed(bool willBeConsumed) { mWillBeConsumed = willBeConsumed; }
 
+    // Returns true if VD cache surface is big enough. This should always be called from RT and it
+    // works with Skia pipelines only.
+    bool canReuseSurface() {
+        SkSurface* surface = mCache.surface.get();
+        return surface && surface->width() >= mProperties.getScaledWidth()
+              && surface->height() >= mProperties.getScaledHeight();
+    }
+
+    // Draws VD cache into a canvas. This should always be called from RT and it works with Skia
+    // pipelines only.
+    void draw(SkCanvas* canvas);
+
+    // Draws VD into a GPU backed surface. If canReuseSurface returns false, then "surface" must
+    // contain a new surface. This should always be called from RT and it works with Skia pipelines
+    // only.
+    void updateCache(sk_sp<SkSurface> surface);
+
 private:
     struct Cache {
-        sk_sp<Bitmap> bitmap;
+        sk_sp<Bitmap> bitmap; //used by HWUI pipeline and software
+        //TODO: use surface instead of bitmap when drawing in software canvas
+        sk_sp<SkSurface> surface; //used only by Skia pipelines
         bool dirty = true;
     };
 
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index 496f7ba..3ddc09f 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -19,6 +19,7 @@
 #include "renderthread/CanvasContext.h"
 #include "VectorDrawable.h"
 #include "DumpOpsCanvas.h"
+#include "SkiaPipeline.h"
 
 #include <SkImagePriv.h>
 
@@ -92,6 +93,8 @@
         // If any vector drawable in the display list needs update, damage the node.
         if (vectorDrawable->isDirty()) {
             isDirty = true;
+            static_cast<SkiaPipeline*>(info.canvasContext.getRenderPipeline())
+                ->getVectorDrawables()->push_back(vectorDrawable);
         }
         vectorDrawable->setPropertyChangeWillBeConsumed(true);
     }
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 10c1865..75f1adc 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -25,6 +25,7 @@
 #include <SkPictureRecorder.h>
 #include <SkPixelSerializer.h>
 #include <SkStream.h>
+#include "VectorDrawable.h"
 
 #include <unistd.h>
 
@@ -40,7 +41,9 @@
 
 Vector3 SkiaPipeline::mLightCenter = {FLT_MIN, FLT_MIN, FLT_MIN};
 
-SkiaPipeline::SkiaPipeline(RenderThread& thread) :  mRenderThread(thread) { }
+SkiaPipeline::SkiaPipeline(RenderThread& thread) :  mRenderThread(thread) {
+    mVectorDrawables.reserve(30);
+}
 
 TaskManager* SkiaPipeline::getTaskManager() {
     return &mTaskManager;
@@ -74,6 +77,7 @@
         const BakedOpRenderer::LightInfo& lightInfo) {
     updateLighting(lightGeometry, lightInfo);
     ATRACE_NAME("draw layers");
+    renderVectorDrawableCache();
     renderLayersImpl(*layerUpdateQueue, opaque);
     layerUpdateQueue->clear();
 }
@@ -176,10 +180,35 @@
     }
 };
 
+void SkiaPipeline::renderVectorDrawableCache() {
+    //render VectorDrawables into offscreen buffers
+    for (auto vd : mVectorDrawables) {
+        sk_sp<SkSurface> surface;
+        if (!vd->canReuseSurface()) {
+#ifndef ANDROID_ENABLE_LINEAR_BLENDING
+            sk_sp<SkColorSpace> colorSpace = nullptr;
+#else
+            sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB();
+#endif
+            int scaledWidth = SkScalarCeilToInt(vd->properties().getScaledWidth());
+            int scaledHeight = SkScalarCeilToInt(vd->properties().getScaledHeight());
+            SkImageInfo info = SkImageInfo::MakeN32(scaledWidth, scaledHeight,
+                    kPremul_SkAlphaType, colorSpace);
+            SkASSERT(mRenderThread.getGrContext() != nullptr);
+            surface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
+                    info);
+        }
+        vd->updateCache(surface);
+    }
+    mVectorDrawables.clear();
+}
+
 void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
         const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect &contentDrawBounds,
         sk_sp<SkSurface> surface) {
 
+    renderVectorDrawableCache();
+
     // draw all layers up front
     renderLayersImpl(layers, opaque);
 
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index c58fedf..6f5e719 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -49,6 +49,8 @@
             const std::vector< sp<RenderNode> >& nodes, bool opaque, const Rect &contentDrawBounds,
             sk_sp<SkSurface> surface);
 
+    std::vector<VectorDrawableRoot*>* getVectorDrawables() { return &mVectorDrawables; }
+
     static void destroyLayer(RenderNode* node);
 
     static void prepareToDraw(const renderthread::RenderThread& thread, Bitmap* bitmap);
@@ -119,8 +121,18 @@
             const std::vector< sp<RenderNode> >& nodes, const Rect &contentDrawBounds,
             sk_sp<SkSurface>);
 
+    /**
+     *  Render mVectorDrawables into offscreen buffers.
+     */
+    void renderVectorDrawableCache();
+
     TaskManager mTaskManager;
     std::vector<sk_sp<SkImage>> mPinnedImages;
+
+    /**
+     *  populated by prepareTree with dirty VDs
+     */
+    std::vector<VectorDrawableRoot*> mVectorDrawables;
     static float mLightRadius;
     static uint8_t mAmbientShadowAlpha;
     static uint8_t mSpotShadowAlpha;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 559d268..27edc25 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -126,22 +126,7 @@
          return SkRect::MakeLargest();
      }
      virtual void onDraw(SkCanvas* canvas) override {
-         Bitmap& hwuiBitmap = mRoot->getBitmapUpdateIfDirty();
-         SkBitmap bitmap;
-         hwuiBitmap.getSkBitmap(&bitmap);
-         SkPaint* paint = mRoot->getPaint();
-         canvas->drawBitmapRect(bitmap, mRoot->mutateProperties()->getBounds(), paint);
-         /*
-          * TODO we can draw this directly but need to address the following...
-          *
-          * 1) Add drawDirect(SkCanvas*) to VectorDrawableRoot
-          * 2) fix VectorDrawable.cpp's Path::draw to not make a temporary path
-          *    so that we don't break caching
-          * 3) figure out how to set path's as volatile during animation
-          * 4) if mRoot->getPaint() != null either promote to layer (during
-          *    animation) or cache in SkSurface (for static content)
-          *
-          */
+         mRoot->draw(canvas);
      }
 
  private:
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 738c091..33eda96 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -194,6 +194,8 @@
 
     void waitOnFences();
 
+    IRenderPipeline* getRenderPipeline() { return mRenderPipeline.get(); }
+
 private:
     CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
             IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline);
diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
index 7ae58a6..e15f5d9 100644
--- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
+++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
@@ -17,6 +17,7 @@
 #include "tests/common/TestUtils.h"
 
 #include <gtest/gtest.h>
+#include <SkBlurDrawLooper.h>
 #include <SkColorMatrixFilter.h>
 #include <SkColorSpace.h>
 #include <SkImagePriv.h>
@@ -95,3 +96,16 @@
     sk_sp<SkColorSpace> sRGB2 = SkColorSpace::MakeSRGB();
     ASSERT_EQ(sRGB1.get(), sRGB2.get());
 }
+
+TEST(SkiaBehavior, blurDrawLooper) {
+    sk_sp<SkDrawLooper> looper = SkBlurDrawLooper::Make(SK_ColorRED, 5.0f, 3.0f, 4.0f);
+
+    SkDrawLooper::BlurShadowRec blur;
+    bool success = looper->asABlurShadow(&blur);
+    ASSERT_TRUE(success);
+
+    ASSERT_EQ(SK_ColorRED, blur.fColor);
+    ASSERT_EQ(5.0f, blur.fSigma);
+    ASSERT_EQ(3.0f, blur.fOffset.fX);
+    ASSERT_EQ(4.0f, blur.fOffset.fY);
+}
diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java
index abf6b20..2a0e04e 100644
--- a/media/java/android/media/ImageUtils.java
+++ b/media/java/android/media/ImageUtils.java
@@ -62,6 +62,7 @@
             case ImageFormat.RAW12:
             case ImageFormat.DEPTH16:
             case ImageFormat.DEPTH_POINT_CLOUD:
+            case ImageFormat.RAW_DEPTH:
                 return 1;
             case ImageFormat.PRIVATE:
                 return 0;
@@ -103,6 +104,10 @@
             throw new IllegalArgumentException(
                     "Copy of RAW_OPAQUE format has not been implemented");
         }
+        if (src.getFormat() == ImageFormat.RAW_DEPTH) {
+            throw new IllegalArgumentException(
+                    "Copy of RAW_DEPTH format has not been implemented");
+        }
         if (!(dst.getOwner() instanceof ImageWriter)) {
             throw new IllegalArgumentException("Destination image is not from ImageWriter. Only"
                     + " the images from ImageWriter are writable");
@@ -206,6 +211,7 @@
             case PixelFormat.RGB_565:
             case ImageFormat.YUY2:
             case ImageFormat.Y16:
+            case ImageFormat.RAW_DEPTH:
             case ImageFormat.RAW_SENSOR:
             case ImageFormat.RAW_PRIVATE: // round estimate, real size is unknown
             case ImageFormat.DEPTH16:
@@ -253,6 +259,7 @@
             case ImageFormat.RAW_SENSOR:
             case ImageFormat.RAW10:
             case ImageFormat.RAW12:
+            case ImageFormat.RAW_DEPTH:
                 return new Size(image.getWidth(), image.getHeight());
             case ImageFormat.PRIVATE:
                 return new Size(0, 0);
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
index 1b6aca1..227d804 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
@@ -110,6 +110,11 @@
     private final ScanCallback mBLEScanCallback = new ScanCallback() {
         @Override
         public void onScanResult(int callbackType, ScanResult result) {
+            if (DEBUG) {
+                Log.i(LOG_TAG,
+                        "BLE.onScanResult(callbackType = " + callbackType + ", result = " + result
+                                + ")");
+            }
             final DeviceFilterPair<ScanResult> deviceFilterPair
                     = DeviceFilterPair.findMatch(result, mBLEFilters);
             if (deviceFilterPair == null) return;
@@ -126,6 +131,10 @@
     private BroadcastReceiver mBluetoothDeviceFoundBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
+            if (DEBUG) {
+                Log.i(LOG_TAG,
+                        "BL.onReceive(context = " + context + ", intent = " + intent + ")");
+            }
             final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
             final DeviceFilterPair<BluetoothDevice> deviceFilterPair
                     = DeviceFilterPair.findMatch(device, mBluetoothFilters);
@@ -191,7 +200,10 @@
             mBLEScanFilters = CollectionUtils.map(mBLEFilters, BluetoothLEDeviceFilter::getScanFilter);
 
             reset();
-        }
+        } else if (DEBUG) Log.i(LOG_TAG, "startDiscovery: duplicate request: " + request);
+
+
+
         if (!ArrayUtils.isEmpty(mDevicesFound)) {
             onReadyToShowUI();
         }
@@ -221,6 +233,7 @@
     }
 
     private void reset() {
+        if (DEBUG) Log.i(LOG_TAG, "reset()");
         mDevicesFound.clear();
         mSelectedDevice = null;
         mDevicesAdapter.notifyDataSetChanged();
@@ -369,8 +382,15 @@
         public static <T extends Parcelable> DeviceFilterPair<T> findMatch(
                 T dev, @Nullable List<? extends DeviceFilter<T>> filters) {
             if (isEmpty(filters)) return new DeviceFilterPair<>(dev, null);
-            final DeviceFilter<T> matchingFilter = CollectionUtils.find(filters, (f) -> f.matches(dev));
-            return matchingFilter != null ? new DeviceFilterPair<>(dev, matchingFilter) : null;
+            final DeviceFilter<T> matchingFilter
+                    = CollectionUtils.find(filters, f -> f.matches(dev));
+
+            DeviceFilterPair<T> result = matchingFilter != null
+                    ? new DeviceFilterPair<>(dev, matchingFilter)
+                    : null;
+            if (DEBUG) Log.i(LOG_TAG, "findMatch(dev = " + dev + ", filters = " + filters +
+                    ") -> " + result);
+            return result;
         }
 
         public String getDisplayName() {
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 9a4179f..6571294 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -405,17 +405,17 @@
                     }
                 } else {
                     drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
+                    if (FIXED_SIZED_SURFACE) {
+                        // If the surface is fixed-size, we should only need to
+                        // draw it once and then we'll let the window manager
+                        // position it appropriately.  As such, we no longer needed
+                        // the loaded bitmap.  Yay!
+                        // hw-accelerated renderer retains bitmap for faster rotation
+                        unloadWallpaper(false /* forgetSize */);
+                    }
                 }
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
-                if (FIXED_SIZED_SURFACE && !mIsHwAccelerated) {
-                    // If the surface is fixed-size, we should only need to
-                    // draw it once and then we'll let the window manager
-                    // position it appropriately.  As such, we no longer needed
-                    // the loaded bitmap.  Yay!
-                    // hw-accelerated renderer retains bitmap for faster rotation
-                    unloadWallpaper(false /* forgetSize */);
-                }
             }
         }
 
diff --git a/services/core/java/com/android/server/FontManagerService.java b/services/core/java/com/android/server/FontManagerService.java
index 55a945a..f172647 100644
--- a/services/core/java/com/android/server/FontManagerService.java
+++ b/services/core/java/com/android/server/FontManagerService.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.graphics.FontListParser;
+import android.net.Uri;
 import android.os.ParcelFileDescriptor;
 import android.text.FontConfig;
 import android.util.Slog;
@@ -34,6 +35,7 @@
 public class FontManagerService extends IFontManager.Stub {
     private static final String TAG = "FontManagerService";
     private static final String FONTS_CONFIG = "/system/etc/fonts.xml";
+    private static final String SYSTEM_FONT_DIR = "/system/fonts/";
 
     @GuardedBy("mLock")
     private FontConfig mConfig;
@@ -63,28 +65,22 @@
     public FontConfig getSystemFonts() {
         synchronized (mLock) {
             if (mConfig != null) {
-                return new FontConfig(mConfig);
+                return mConfig;
             }
 
-            FontConfig config = loadFromSystem();
-            if (config == null) {
+            mConfig = loadFromSystem();
+            if (mConfig == null) {
                 return null;
             }
 
-            for (FontConfig.Family family : config.getFamilies()) {
+            for (FontConfig.Family family : mConfig.getFamilies()) {
                 for (FontConfig.Font font : family.getFonts()) {
-                    File fontFile = new File(font.getFontName());
-                    try {
-                        font.setFd(ParcelFileDescriptor.open(
-                                fontFile, ParcelFileDescriptor.MODE_READ_ONLY));
-                    } catch (IOException e) {
-                        Slog.e(TAG, "Error opening font file " + font.getFontName(), e);
-                    }
+                    File fontFile = new File(SYSTEM_FONT_DIR, font.getFontName());
+                    font.setUri(Uri.fromFile(fontFile));
                 }
             }
 
-            mConfig = config;
-            return new FontConfig(mConfig);
+            return mConfig;
         }
     }
 
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 457c5f8..c68000a 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -98,6 +98,7 @@
 import com.android.internal.app.IMediaContainerService;
 import com.android.internal.os.AppFuseMount;
 import com.android.internal.os.FuseAppLoop;
+import com.android.internal.os.FuseUnavailableMountException;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.os.Zygote;
 import com.android.internal.util.ArrayUtils;
@@ -3007,32 +3008,36 @@
         }
     }
 
-    private ParcelFileDescriptor mountAppFuse(int uid, int mountId)
-            throws NativeDaemonConnectorException {
-        final NativeDaemonEvent event = StorageManagerService.this.mConnector.execute(
-                "appfuse", "mount", uid, Process.myPid(), mountId);
-        if (event.getFileDescriptors() == null ||
-            event.getFileDescriptors().length == 0) {
-            throw new NativeDaemonConnectorException("Cannot obtain device FD");
-        }
-        return new ParcelFileDescriptor(event.getFileDescriptors()[0]);
-    }
-
     class AppFuseMountScope extends AppFuseBridge.MountScope {
-        public AppFuseMountScope(int uid, int pid, int mountId)
-                throws NativeDaemonConnectorException {
-            super(uid, pid, mountId, mountAppFuse(uid, mountId));
+        boolean opened = false;
+
+        public AppFuseMountScope(int uid, int pid, int mountId) {
+            super(uid, pid, mountId);
+        }
+
+        @Override
+        public ParcelFileDescriptor open() throws NativeDaemonConnectorException {
+            final NativeDaemonEvent event = StorageManagerService.this.mConnector.execute(
+                    "appfuse", "mount", uid, Process.myPid(), mountId);
+            opened = true;
+            if (event.getFileDescriptors() == null ||
+                event.getFileDescriptors().length == 0) {
+                throw new NativeDaemonConnectorException("Cannot obtain device FD");
+            }
+            return new ParcelFileDescriptor(event.getFileDescriptors()[0]);
         }
 
         @Override
         public void close() throws Exception {
-            super.close();
-            mConnector.execute("appfuse", "unmount", uid, Process.myPid(), mountId);
+            if (opened) {
+                mConnector.execute("appfuse", "unmount", uid, Process.myPid(), mountId);
+                opened = false;
+            }
         }
     }
 
     @Override
-    public AppFuseMount mountProxyFileDescriptorBridge() throws RemoteException {
+    public @Nullable AppFuseMount mountProxyFileDescriptorBridge() {
         Slog.v(TAG, "mountProxyFileDescriptorBridge");
         final int uid = Binder.getCallingUid();
         final int pid = Binder.getCallingPid();
@@ -3049,12 +3054,12 @@
                     final int name = mNextAppFuseName++;
                     try {
                         return new AppFuseMount(
-                            name,
-                            mAppFuseBridge.addBridge(new AppFuseMountScope(uid, pid, name)));
-                    } catch (AppFuseBridge.BridgeException e) {
+                            name, mAppFuseBridge.addBridge(new AppFuseMountScope(uid, pid, name)));
+                    } catch (FuseUnavailableMountException e) {
                         if (newlyCreated) {
                             // If newly created bridge fails, it's a real error.
-                            throw new RemoteException(e.getMessage());
+                            Slog.e(TAG, "", e);
+                            return null;
                         }
                         // It seems the thread of mAppFuseBridge has already been terminated.
                         mAppFuseBridge = null;
@@ -3067,19 +3072,21 @@
     }
 
     @Override
-    public ParcelFileDescriptor openProxyFileDescriptor(int mountId, int fileId, int mode)
-            throws RemoteException {
-        Slog.v(TAG, "mountProxyFileDescriptorBridge");
+    public @Nullable ParcelFileDescriptor openProxyFileDescriptor(
+            int mountId, int fileId, int mode) {
+        Slog.v(TAG, "mountProxyFileDescriptor");
         final int pid = Binder.getCallingPid();
         try {
             synchronized (mAppFuseLock) {
                 if (mAppFuseBridge == null) {
-                    throw new RemoteException("Cannot find mount point");
+                    Slog.e(TAG, "FuseBridge has not been created");
+                    return null;
                 }
                 return mAppFuseBridge.openFile(pid, mountId, fileId, mode);
             }
-        } catch (FileNotFoundException | SecurityException | InterruptedException error) {
-            throw new RemoteException(error.getMessage());
+        } catch (FuseUnavailableMountException | InterruptedException error) {
+            Slog.v(TAG, "The mount point has already been invalid", error);
+            return null;
         }
     }
 
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 93e30ff..b165d34 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -75,6 +75,9 @@
 import static android.net.wifi.WifiManager.EXTRA_NETWORK_INFO;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_CONFIGURATION;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_INFO;
+import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
+import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT;
+import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 
 import static com.android.internal.util.ArrayUtils.appendInt;
@@ -141,6 +144,7 @@
 import android.os.INetworkManagementService;
 import android.os.Message;
 import android.os.MessageQueue.IdleHandler;
+import android.os.PersistableBundle;
 import android.os.PowerManager;
 import android.os.PowerManagerInternal;
 import android.os.Process;
@@ -153,6 +157,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -174,6 +179,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
@@ -205,6 +211,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Calendar;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -321,6 +328,7 @@
     private UsageStatsManagerInternal mUsageStats;
     private final TrustedTime mTime;
     private final UserManager mUserManager;
+    private final CarrierConfigManager mCarrierConfigManager;
 
     private IConnectivityManager mConnManager;
     private INotificationManager mNotifManager;
@@ -472,6 +480,7 @@
                 Context.DEVICE_IDLE_CONTROLLER));
         mTime = checkNotNull(time, "missing TrustedTime");
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
         mIPm = pm;
 
         HandlerThread thread = new HandlerThread(TAG);
@@ -756,6 +765,11 @@
                     WifiManager.NETWORK_STATE_CHANGED_ACTION);
             mContext.registerReceiver(mWifiStateReceiver, wifiStateFilter, null, mHandler);
 
+            // listen for carrier config changes to update data cycle information
+            final IntentFilter carrierConfigFilter = new IntentFilter(
+                    ACTION_CARRIER_CONFIG_CHANGED);
+            mContext.registerReceiver(mCarrierConfigReceiver, carrierConfigFilter, null, mHandler);
+
             mUsageStats.addAppIdleStateChangeListener(new AppIdleStateChangeListener());
             // tell systemReady() that the service has been initialized
             initCompleteSignal.countDown();
@@ -1292,6 +1306,213 @@
     };
 
     /**
+     * Update mobile policies with data cycle information from {@link CarrierConfigManager}
+     * if necessary.
+     *
+     * @param subId that has its associated NetworkPolicy updated if necessary
+     * @return if any policies were updated
+     */
+    private boolean maybeUpdateMobilePolicyCycleNL(int subId) {
+        if (LOGV) Slog.v(TAG, "maybeUpdateMobilePolicyCycleNL()");
+        final PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId);
+
+        if (config == null) {
+            return false;
+        }
+
+        boolean policyUpdated = false;
+        final String subscriberId = TelephonyManager.from(mContext).getSubscriberId(subId);
+
+        // find and update the mobile NetworkPolicy for this subscriber id
+        final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
+                TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true);
+        for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
+            final NetworkTemplate template = mNetworkPolicy.keyAt(i);
+            if (template.matches(probeIdent)) {
+                NetworkPolicy policy = mNetworkPolicy.valueAt(i);
+
+                // only update the policy if the user didn't change any of the defaults.
+                if (!policy.inferred) {
+                    // TODO: inferred could be split, so that if a user changes their data limit or
+                    // warning, it doesn't prevent their cycle date from being updated.
+                    if (LOGD) Slog.v(TAG, "Didn't update NetworkPolicy because policy.inferred");
+                    continue;
+                }
+
+                final int cycleDay = getCycleDayFromCarrierConfig(config, policy.cycleDay);
+                final long warningBytes = getWarningBytesFromCarrierConfig(config,
+                        policy.warningBytes);
+                final long limitBytes = getLimitBytesFromCarrierConfig(config,
+                        policy.limitBytes);
+
+                if (policy.cycleDay == cycleDay &&
+                        policy.warningBytes == warningBytes &&
+                        policy.limitBytes == limitBytes) {
+                    continue;
+                }
+
+                policyUpdated = true;
+                policy.cycleDay = cycleDay;
+                policy.warningBytes = warningBytes;
+                policy.limitBytes = limitBytes;
+
+                if (LOGD) {
+                    Slog.d(TAG, "Updated NetworkPolicy " + policy + " which matches subscriber "
+                            + NetworkIdentity.scrubSubscriberId(subscriberId)
+                            + " from CarrierConfigManager");
+                }
+            }
+        }
+
+        return policyUpdated;
+    }
+
+    /**
+     * Returns the cycle day that should be used for a mobile NetworkPolicy.
+     *
+     * It attempts to get an appropriate cycle day from the passed in CarrierConfig. If it's unable
+     * to do so, it returns the fallback value.
+     *
+     * @param config The CarrierConfig to read the value from.
+     * @param fallbackCycleDay to return if the CarrierConfig can't be read.
+     * @return cycleDay to use in the mobile NetworkPolicy.
+     */
+    @VisibleForTesting
+    public int getCycleDayFromCarrierConfig(@Nullable PersistableBundle config,
+            int fallbackCycleDay) {
+        if (config == null) {
+            return fallbackCycleDay;
+        }
+        int cycleDay =
+                config.getInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT);
+        if (cycleDay == DATA_CYCLE_USE_PLATFORM_DEFAULT) {
+            return fallbackCycleDay;
+        }
+        // validate cycleDay value
+        final Calendar cal = Calendar.getInstance();
+        if (cycleDay < cal.getMinimum(Calendar.DAY_OF_MONTH) ||
+                cycleDay > cal.getMaximum(Calendar.DAY_OF_MONTH)) {
+            Slog.e(TAG, "Invalid date in "
+                    + "CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT: " + cycleDay);
+            return fallbackCycleDay;
+        }
+        return cycleDay;
+    }
+
+    /**
+     * Returns the warning bytes that should be used for a mobile NetworkPolicy.
+     *
+     * It attempts to get an appropriate value from the passed in CarrierConfig. If it's unable
+     * to do so, it returns the fallback value.
+     *
+     * @param config The CarrierConfig to read the value from.
+     * @param fallbackWarningBytes to return if the CarrierConfig can't be read.
+     * @return warningBytes to use in the mobile NetworkPolicy.
+     */
+    @VisibleForTesting
+    public long getWarningBytesFromCarrierConfig(@Nullable PersistableBundle config,
+            long fallbackWarningBytes) {
+        if (config == null) {
+            return fallbackWarningBytes;
+        }
+        long warningBytes =
+                config.getLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG);
+
+        if (warningBytes == DATA_CYCLE_THRESHOLD_DISABLED) {
+            return WARNING_DISABLED;
+        } else if (warningBytes == DATA_CYCLE_USE_PLATFORM_DEFAULT) {
+            return getPlatformDefaultWarningBytes();
+        } else if (warningBytes < 0) {
+            Slog.e(TAG, "Invalid value in "
+                    + "CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG; expected a "
+                    + "non-negative value but got: " + warningBytes);
+            return fallbackWarningBytes;
+        }
+
+        return warningBytes;
+    }
+
+    /**
+     * Returns the limit bytes that should be used for a mobile NetworkPolicy.
+     *
+     * It attempts to get an appropriate value from the passed in CarrierConfig. If it's unable
+     * to do so, it returns the fallback value.
+     *
+     * @param config The CarrierConfig to read the value from.
+     * @param fallbackLimitBytes to return if the CarrierConfig can't be read.
+     * @return limitBytes to use in the mobile NetworkPolicy.
+     */
+    @VisibleForTesting
+    public long getLimitBytesFromCarrierConfig(@Nullable PersistableBundle config,
+            long fallbackLimitBytes) {
+        if (config == null) {
+            return fallbackLimitBytes;
+        }
+        long limitBytes =
+                config.getLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG);
+
+        if (limitBytes == DATA_CYCLE_THRESHOLD_DISABLED) {
+            return LIMIT_DISABLED;
+        } else if (limitBytes == DATA_CYCLE_USE_PLATFORM_DEFAULT) {
+            return getPlatformDefaultLimitBytes();
+        } else if (limitBytes < 0) {
+            Slog.e(TAG, "Invalid value in "
+                    + "CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG; expected a "
+                    + "non-negative value but got: " + limitBytes);
+            return fallbackLimitBytes;
+        }
+        return limitBytes;
+    }
+
+    /**
+     * Receiver that watches for {@link CarrierConfigManager} to be changed.
+     */
+    private BroadcastReceiver mCarrierConfigReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            // No need to do a permission check, because the ACTION_CARRIER_CONFIG_CHANGED
+            // broadcast is protected and can't be spoofed. Runs on a background handler thread.
+
+            if (!intent.hasExtra(PhoneConstants.SUBSCRIPTION_KEY)) {
+                return;
+            }
+            final int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, -1);
+            final TelephonyManager tele = TelephonyManager.from(mContext);
+            final String subscriberId = tele.getSubscriberId(subId);
+
+            maybeRefreshTrustedTime();
+            synchronized (mUidRulesFirstLock) {
+                synchronized (mNetworkPoliciesSecondLock) {
+                    final boolean added = ensureActiveMobilePolicyNL(subId, subscriberId);
+                    if (added) return;
+                    final boolean updated = maybeUpdateMobilePolicyCycleNL(subId);
+                    if (!updated) return;
+                    // update network and notification rules, as the data cycle changed and it's
+                    // possible that we should be triggering warnings/limits now
+                    handleNetworkPoliciesUpdateAL(true);
+                }
+            }
+        }
+    };
+
+    /**
+     * Handles all tasks that need to be run after a new network policy has been set, or an existing
+     * one has been updated.
+     *
+     * @param shouldNormalizePolicies true iff network policies need to be normalized after the
+     *                                update.
+     */
+    void handleNetworkPoliciesUpdateAL(boolean shouldNormalizePolicies) {
+        if (shouldNormalizePolicies) {
+            normalizePoliciesNL();
+        }
+        updateNetworkEnabledNL();
+        updateNetworkRulesNL();
+        updateNotificationsNL();
+        writePolicyAL();
+    }
+
+    /**
      * Proactively control network data connections when they exceed
      * {@link NetworkPolicy#limitBytes}.
      */
@@ -1516,11 +1737,19 @@
         final int[] subIds = sub.getActiveSubscriptionIdList();
         for (int subId : subIds) {
             final String subscriberId = tele.getSubscriberId(subId);
-            ensureActiveMobilePolicyNL(subscriberId);
+            ensureActiveMobilePolicyNL(subId, subscriberId);
         }
     }
 
-    private void ensureActiveMobilePolicyNL(String subscriberId) {
+    /**
+     * Once any {@link #mNetworkPolicy} are loaded from disk, ensure that we
+     * have at least a default mobile policy defined.
+     *
+     * @param subId to build a default policy for
+     * @param subscriberId that we check for an existing policy
+     * @return true if a mobile network policy was added, or false one already existed.
+     */
+    private boolean ensureActiveMobilePolicyNL(int subId, String subscriberId) {
         // Poke around to see if we already have a policy
         final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
                 TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true);
@@ -1531,33 +1760,51 @@
                     Slog.d(TAG, "Found template " + template + " which matches subscriber "
                             + NetworkIdentity.scrubSubscriberId(subscriberId));
                 }
-                return;
+                return false;
             }
         }
 
         Slog.i(TAG, "No policy for subscriber " + NetworkIdentity.scrubSubscriberId(subscriberId)
                 + "; generating default policy");
+        final NetworkPolicy policy = buildDefaultMobilePolicy(subId, subscriberId);
+        addNetworkPolicyNL(policy);
+        return true;
+    }
 
-        // Build default mobile policy, and assume usage cycle starts today
+    private long getPlatformDefaultWarningBytes() {
         final int dataWarningConfig = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_networkPolicyDefaultWarning);
-        final long warningBytes;
         if (dataWarningConfig == WARNING_DISABLED) {
-            warningBytes = WARNING_DISABLED;
+            return WARNING_DISABLED;
         } else {
-            warningBytes = dataWarningConfig * MB_IN_BYTES;
+            return dataWarningConfig * MB_IN_BYTES;
         }
+    }
 
+    private long getPlatformDefaultLimitBytes() {
+        return LIMIT_DISABLED;
+    }
+
+    @VisibleForTesting
+    public NetworkPolicy buildDefaultMobilePolicy(int subId, String subscriberId) {
+        PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId);
+
+        // assume usage cycle starts today
         final Time time = new Time();
         time.setToNow();
 
-        final int cycleDay = time.monthDay;
         final String cycleTimezone = time.timezone;
 
+        final int cycleDay = getCycleDayFromCarrierConfig(config, time.monthDay);
+        final long warningBytes = getWarningBytesFromCarrierConfig(config,
+                getPlatformDefaultWarningBytes());
+        final long limitBytes = getLimitBytesFromCarrierConfig(config,
+                getPlatformDefaultLimitBytes());
+
         final NetworkTemplate template = buildTemplateMobileAll(subscriberId);
         final NetworkPolicy policy = new NetworkPolicy(template, cycleDay, cycleTimezone,
-                warningBytes, LIMIT_DISABLED, SNOOZE_NEVER, SNOOZE_NEVER, true, true);
-        addNetworkPolicyNL(policy);
+                warningBytes, limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, true, true);
+        return policy;
     }
 
     private void readPolicyAL() {
@@ -2025,10 +2272,7 @@
             synchronized (mUidRulesFirstLock) {
                 synchronized (mNetworkPoliciesSecondLock) {
                     normalizePoliciesNL(policies);
-                    updateNetworkEnabledNL();
-                    updateNetworkRulesNL();
-                    updateNotificationsNL();
-                    writePolicyAL();
+                    handleNetworkPoliciesUpdateAL(false);
                 }
             }
         } finally {
@@ -2125,11 +2369,7 @@
                         throw new IllegalArgumentException("unexpected type");
                 }
 
-                normalizePoliciesNL();
-                updateNetworkEnabledNL();
-                updateNetworkRulesNL();
-                updateNotificationsNL();
-                writePolicyAL();
+                handleNetworkPoliciesUpdateAL(true);
             }
         }
     }
@@ -2360,11 +2600,7 @@
                         mNetworkPolicy.valueAt(i).clearSnooze();
                     }
 
-                    normalizePoliciesNL();
-                    updateNetworkEnabledNL();
-                    updateNetworkRulesNL();
-                    updateNotificationsNL();
-                    writePolicyAL();
+                    handleNetworkPoliciesUpdateAL(true);
 
                     fout.println("Cleared snooze timestamps");
                     return;
diff --git a/services/core/java/com/android/server/storage/AppFuseBridge.java b/services/core/java/com/android/server/storage/AppFuseBridge.java
index 904d915..6a0b648 100644
--- a/services/core/java/com/android/server/storage/AppFuseBridge.java
+++ b/services/core/java/com/android/server/storage/AppFuseBridge.java
@@ -21,7 +21,9 @@
 import android.system.Os;
 import android.util.SparseArray;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.FuseUnavailableMountException;
 import com.android.internal.util.Preconditions;
+import com.android.server.NativeDaemonConnectorException;
 import libcore.io.IoUtils;
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -54,17 +56,17 @@
     }
 
     public ParcelFileDescriptor addBridge(MountScope mountScope)
-            throws BridgeException {
+            throws FuseUnavailableMountException, NativeDaemonConnectorException {
         try {
             synchronized (this) {
                 Preconditions.checkArgument(mScopes.indexOfKey(mountScope.mountId) < 0);
                 if (mNativeLoop == 0) {
-                    throw new BridgeException("The thread has already been terminated");
+                    throw new FuseUnavailableMountException(mountScope.mountId);
                 }
                 final int fd = native_add_bridge(
-                        mNativeLoop, mountScope.mountId, mountScope.deviceFd.detachFd());
+                        mNativeLoop, mountScope.mountId, mountScope.open().detachFd());
                 if (fd == -1) {
-                    throw new BridgeException("Failed to invoke native_add_bridge");
+                    throw new FuseUnavailableMountException(mountScope.mountId);
                 }
                 final ParcelFileDescriptor result = ParcelFileDescriptor.adoptFd(fd);
                 mScopes.put(mountScope.mountId, mountScope);
@@ -86,12 +88,12 @@
     }
 
     public ParcelFileDescriptor openFile(int pid, int mountId, int fileId, int mode)
-            throws FileNotFoundException, SecurityException, InterruptedException {
+            throws FuseUnavailableMountException, InterruptedException {
         final MountScope scope;
         synchronized (this) {
             scope = mScopes.get(mountId);
             if (scope == null) {
-                throw new FileNotFoundException("Cannot find mount point");
+                throw new FuseUnavailableMountException(mountId);
             }
         }
         if (scope.pid != pid) {
@@ -99,17 +101,14 @@
         }
         final boolean result = scope.waitForMount();
         if (result == false) {
-            throw new FileNotFoundException("Mount failed");
+            throw new FuseUnavailableMountException(mountId);
         }
         try {
-            if (Os.stat(scope.mountPoint.getPath()).st_ino != 1) {
-                throw new FileNotFoundException("Could not find bridge mount point.");
-            }
-        } catch (ErrnoException e) {
-            throw new FileNotFoundException(
-                    "Failed to stat mount point: " + scope.mountPoint.getParent());
+            return ParcelFileDescriptor.open(
+                    new File(scope.mountPoint, String.valueOf(fileId)), mode);
+        } catch (FileNotFoundException error) {
+            throw new FuseUnavailableMountException(mountId);
         }
-        return ParcelFileDescriptor.open(new File(scope.mountPoint, String.valueOf(fileId)), mode);
     }
 
     // Used by com_android_server_storage_AppFuse.cpp.
@@ -130,20 +129,18 @@
         }
     }
 
-    public static class MountScope implements AutoCloseable {
+    public static abstract class MountScope implements AutoCloseable {
         public final int uid;
         public final int pid;
         public final int mountId;
-        public final ParcelFileDescriptor deviceFd;
         public final File mountPoint;
         private final CountDownLatch mMounted = new CountDownLatch(1);
         private boolean mMountResult = false;
 
-        public MountScope(int uid, int pid, int mountId, ParcelFileDescriptor deviceFd) {
+        public MountScope(int uid, int pid, int mountId) {
             this.uid = uid;
             this.pid = pid;
             this.mountId = mountId;
-            this.deviceFd = deviceFd;
             this.mountPoint = new File(String.format(APPFUSE_MOUNT_NAME_TEMPLATE,  uid, mountId));
         }
 
@@ -161,16 +158,7 @@
             return mMountResult;
         }
 
-        @Override
-        public void close() throws Exception {
-            deviceFd.close();
-        }
-    }
-
-    public static class BridgeException extends Exception {
-        public BridgeException(String message) {
-            super(message);
-        }
+        public abstract ParcelFileDescriptor open() throws NativeDaemonConnectorException;
     }
 
     private native long native_new();
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 29c6f89..dbba727 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -19,6 +19,7 @@
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
+import static android.net.NetworkPolicy.SNOOZE_NEVER;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
 import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
@@ -26,8 +27,15 @@
 import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
 import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
 import static android.net.NetworkPolicyManager.uidPoliciesToString;
+import static android.net.NetworkTemplate.buildTemplateMobileAll;
 import static android.net.TrafficStats.KB_IN_BYTES;
 import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
+import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT;
+import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED;
+import static android.telephony.CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG;
+import static android.telephony.CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG;
+import static android.telephony.CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 import static android.text.format.Time.TIMEZONE_UTC;
@@ -94,18 +102,24 @@
 import android.net.NetworkTemplate;
 import android.os.Binder;
 import android.os.INetworkManagementService;
+import android.os.PersistableBundle;
 import android.os.PowerManagerInternal;
 import android.os.PowerSaveState;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.text.format.Time;
 import android.util.Log;
 import android.util.TrustedTime;
 
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent;
 import com.android.server.net.NetworkPolicyManagerInternal;
@@ -143,6 +157,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 import java.util.Arrays;
+import java.util.Calendar;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
@@ -200,6 +215,9 @@
     private @Mock INotificationManager mNotifManager;
     private @Mock PackageManager mPackageManager;
     private @Mock IPackageManager mIpm;
+    private @Mock SubscriptionManager mSubscriptionManager;
+    private @Mock CarrierConfigManager mCarrierConfigManager;
+    private @Mock TelephonyManager mTelephonyManager;
 
     private static ActivityManagerInternal mActivityManagerInternal;
 
@@ -214,6 +232,12 @@
     private long mElapsedRealtime;
 
     private static final int USER_ID = 0;
+    private static final int FAKE_SUB_ID = 3737373;
+    private static final String FAKE_SUBSCRIBER_ID = "FAKE_SUB_ID";
+    private static final int DEFAULT_CYCLE_DAY = 1;
+    private static final int INVALID_CARRIER_CONFIG_VALUE = -9999;
+    private long mDefaultWarningBytes; // filled in with the actual default before tests are run
+    private long mDefaultLimitBytes; // filled in with the actual default before tests are run
 
     private static final int APP_ID_A = android.os.Process.FIRST_APPLICATION_UID + 4;
     private static final int APP_ID_B = android.os.Process.FIRST_APPLICATION_UID + 8;
@@ -235,6 +259,8 @@
 
     @BeforeClass
     public static void registerLocalServices() {
+        final PowerManagerInternal powerManager = addLocalServiceMock(PowerManagerInternal.class);
+        when(powerManager.getLowPowerState(anyInt())).thenReturn(mock(PowerSaveState.class));
         addLocalServiceMock(DeviceIdleController.LocalService.class);
         final UsageStatsManagerInternal usageStats =
                 addLocalServiceMock(UsageStatsManagerInternal.class);
@@ -255,7 +281,8 @@
 
         setCurrentTimeMillis(TEST_START);
 
-        // intercept various broadcasts, and pretend that uids have packages
+        // Intercept various broadcasts, and pretend that uids have packages.
+        // Also return mock service instances for a few critical services.
         mServiceContext = new BroadcastInterceptingContext(context) {
             @Override
             public PackageManager getPackageManager() {
@@ -266,6 +293,20 @@
             public void startActivity(Intent intent) {
                 // ignored
             }
+
+            @Override
+            public Object getSystemService(String name) {
+                switch (name) {
+                    case Context.TELEPHONY_SUBSCRIPTION_SERVICE:
+                        return mSubscriptionManager;
+                    case Context.CARRIER_CONFIG_SERVICE:
+                        return mCarrierConfigManager;
+                    case Context.TELEPHONY_SERVICE:
+                        return mTelephonyManager;
+                    default:
+                        return super.getSystemService(name);
+                }
+            }
         };
 
         setNetpolicyXml(context);
@@ -321,6 +362,10 @@
               ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
         verify(mNetworkManager).registerObserver(networkObserver.capture());
         mNetworkObserver = networkObserver.getValue();
+
+        NetworkPolicy defaultPolicy = mService.buildDefaultMobilePolicy(0, "");
+        mDefaultWarningBytes = defaultPolicy.warningBytes;
+        mDefaultLimitBytes = defaultPolicy.limitBytes;
     }
 
     @After
@@ -1132,6 +1177,269 @@
         }
     }
 
+    private void assertCycleDayAsExpected(PersistableBundle config, int carrierCycleDay,
+            boolean expectValid) {
+        config.putInt(KEY_MONTHLY_DATA_CYCLE_DAY_INT, carrierCycleDay);
+        int actualCycleDay = mService.getCycleDayFromCarrierConfig(config,
+                INVALID_CARRIER_CONFIG_VALUE);
+        if (expectValid) {
+            assertEquals(carrierCycleDay, actualCycleDay);
+        } else {
+            // INVALID_CARRIER_CONFIG_VALUE is returned for invalid values
+            assertEquals(INVALID_CARRIER_CONFIG_VALUE, actualCycleDay);
+        }
+    }
+
+    @Test
+    public void testGetCycleDayFromCarrierConfig() {
+        PersistableBundle config = CarrierConfigManager.getDefaultConfig();
+        final Calendar cal = Calendar.getInstance();
+        int actualCycleDay;
+
+        config.putInt(KEY_MONTHLY_DATA_CYCLE_DAY_INT, DATA_CYCLE_USE_PLATFORM_DEFAULT);
+        actualCycleDay = mService.getCycleDayFromCarrierConfig(config, DEFAULT_CYCLE_DAY);
+        assertEquals(DEFAULT_CYCLE_DAY, actualCycleDay);
+
+        // null config returns a default value
+        actualCycleDay = mService.getCycleDayFromCarrierConfig(null, DEFAULT_CYCLE_DAY);
+        assertEquals(DEFAULT_CYCLE_DAY, actualCycleDay);
+
+        // Sane, non-default values
+        assertCycleDayAsExpected(config, 1, true);
+        assertCycleDayAsExpected(config, cal.getMaximum(Calendar.DAY_OF_MONTH), true);
+        assertCycleDayAsExpected(config, cal.getMinimum(Calendar.DAY_OF_MONTH), true);
+
+        // Invalid values
+        assertCycleDayAsExpected(config, 0, false);
+        assertCycleDayAsExpected(config, DATA_CYCLE_THRESHOLD_DISABLED, false);
+        assertCycleDayAsExpected(config, cal.getMaximum(Calendar.DAY_OF_MONTH) + 1, false);
+        assertCycleDayAsExpected(config, cal.getMinimum(Calendar.DAY_OF_MONTH) - 5, false);
+    }
+
+    private void assertWarningBytesAsExpected(PersistableBundle config, long carrierWarningBytes,
+            long expected) {
+        config.putLong(KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, carrierWarningBytes);
+        long actualWarning = mService.getWarningBytesFromCarrierConfig(config,
+                INVALID_CARRIER_CONFIG_VALUE);
+        assertEquals(expected, actualWarning);
+    }
+
+    @Test
+    public void testGetWarningBytesFromCarrierConfig() {
+        PersistableBundle config = CarrierConfigManager.getDefaultConfig();
+        long actualWarningBytes;
+
+        assertWarningBytesAsExpected(config, DATA_CYCLE_USE_PLATFORM_DEFAULT,
+                mDefaultWarningBytes);
+        assertWarningBytesAsExpected(config, DATA_CYCLE_THRESHOLD_DISABLED, WARNING_DISABLED);
+        assertWarningBytesAsExpected(config, 0, 0);
+        // not a valid value
+        assertWarningBytesAsExpected(config, -1000, INVALID_CARRIER_CONFIG_VALUE);
+
+        // null config returns a default value
+        actualWarningBytes = mService.getWarningBytesFromCarrierConfig(null, mDefaultWarningBytes);
+        assertEquals(mDefaultWarningBytes, actualWarningBytes);
+    }
+
+    private void assertLimitBytesAsExpected(PersistableBundle config,  long carrierWarningBytes,
+            long expected) {
+        config.putLong(KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, carrierWarningBytes);
+        long actualWarning = mService.getLimitBytesFromCarrierConfig(config,
+                INVALID_CARRIER_CONFIG_VALUE);
+        assertEquals(expected, actualWarning);
+    }
+
+    @Test
+    public void testGetLimitBytesFromCarrierConfig() {
+        PersistableBundle config = CarrierConfigManager.getDefaultConfig();
+        long actualLimitBytes;
+
+        assertLimitBytesAsExpected(config, DATA_CYCLE_USE_PLATFORM_DEFAULT,
+                mDefaultLimitBytes);
+        assertLimitBytesAsExpected(config, DATA_CYCLE_THRESHOLD_DISABLED, LIMIT_DISABLED);
+        assertLimitBytesAsExpected(config, 0, 0);
+        // not a valid value
+        assertLimitBytesAsExpected(config, -1000, INVALID_CARRIER_CONFIG_VALUE);
+
+        // null config returns a default value
+        actualLimitBytes = mService.getWarningBytesFromCarrierConfig(null, mDefaultLimitBytes);
+        assertEquals(mDefaultLimitBytes, actualLimitBytes);
+    }
+
+    private PersistableBundle setupUpdateMobilePolicyCycleTests() throws RemoteException {
+        when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[0]);
+        when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{FAKE_SUB_ID});
+        when(mTelephonyManager.getSubscriberId(FAKE_SUB_ID)).thenReturn(FAKE_SUBSCRIBER_ID);
+        PersistableBundle bundle = CarrierConfigManager.getDefaultConfig();
+        when(mCarrierConfigManager.getConfigForSubId(FAKE_SUB_ID)).thenReturn(bundle);
+        setNetworkPolicies(buildDefaultFakeMobilePolicy());
+        return bundle;
+    }
+
+    @Test
+    public void testUpdateMobilePolicyCycleWithNullConfig() throws RemoteException {
+        when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[0]);
+        when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{FAKE_SUB_ID});
+        when(mTelephonyManager.getSubscriberId(FAKE_SUB_ID)).thenReturn(FAKE_SUBSCRIBER_ID);
+        when(mCarrierConfigManager.getConfigForSubId(FAKE_SUB_ID)).thenReturn(null);
+        setNetworkPolicies(buildDefaultFakeMobilePolicy());
+        // smoke test to make sure no errors are raised
+        mServiceContext.sendBroadcast(
+                new Intent(ACTION_CARRIER_CONFIG_CHANGED)
+                        .putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID)
+        );
+        assertNetworkPolicyEquals(DEFAULT_CYCLE_DAY, mDefaultWarningBytes, mDefaultLimitBytes,
+                true);
+    }
+
+    @Test
+    public void testUpdateMobilePolicyCycleWithInvalidConfig() throws RemoteException {
+        PersistableBundle bundle = setupUpdateMobilePolicyCycleTests();
+        // Test with an invalid CarrierConfig, there should be no changes or crashes.
+        bundle.putInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT, -100);
+        bundle.putLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, -100);
+        bundle.putLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, -100);
+        mServiceContext.sendBroadcast(
+                new Intent(ACTION_CARRIER_CONFIG_CHANGED)
+                        .putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID)
+        );
+
+        assertNetworkPolicyEquals(DEFAULT_CYCLE_DAY, mDefaultWarningBytes, mDefaultLimitBytes,
+                true);
+    }
+
+    @Test
+    public void testUpdateMobilePolicyCycleWithDefaultConfig() throws RemoteException {
+        PersistableBundle bundle = setupUpdateMobilePolicyCycleTests();
+        // Test that we respect the platform values when told to
+        bundle.putInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT,
+                DATA_CYCLE_USE_PLATFORM_DEFAULT);
+        bundle.putLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG,
+                DATA_CYCLE_USE_PLATFORM_DEFAULT);
+        bundle.putLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG,
+                DATA_CYCLE_USE_PLATFORM_DEFAULT);
+        mServiceContext.sendBroadcast(
+                new Intent(ACTION_CARRIER_CONFIG_CHANGED)
+                        .putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID)
+        );
+
+        assertNetworkPolicyEquals(DEFAULT_CYCLE_DAY, mDefaultWarningBytes, mDefaultLimitBytes,
+                true);
+    }
+
+    @Test
+    public void testUpdateMobilePolicyCycleWithUserOverrides() throws RemoteException {
+        PersistableBundle bundle = setupUpdateMobilePolicyCycleTests();
+
+        // inferred = false implies that a user manually modified this policy.
+        NetworkPolicy policy = buildDefaultFakeMobilePolicy();
+        policy.inferred = false;
+        setNetworkPolicies(policy);
+
+        bundle.putInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT, 31);
+        bundle.putLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, 9999);
+        bundle.putLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG,
+                DATA_CYCLE_THRESHOLD_DISABLED);
+        mServiceContext.sendBroadcast(
+                new Intent(ACTION_CARRIER_CONFIG_CHANGED)
+                        .putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID)
+        );
+
+        // The policy still shouldn't change, because we don't want to overwrite user settings.
+        assertNetworkPolicyEquals(DEFAULT_CYCLE_DAY, mDefaultWarningBytes, mDefaultLimitBytes,
+                false);
+    }
+
+    @Test
+    public void testUpdateMobilePolicyCycleUpdatesDataCycle() throws RemoteException {
+        PersistableBundle bundle = setupUpdateMobilePolicyCycleTests();
+
+        bundle.putInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT, 31);
+        bundle.putLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, 9999);
+        bundle.putLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, 9999);
+        mServiceContext.sendBroadcast(
+                new Intent(ACTION_CARRIER_CONFIG_CHANGED)
+                        .putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID)
+        );
+
+        assertNetworkPolicyEquals(31, 9999, 9999, true);
+    }
+
+    @Test
+    public void testUpdateMobilePolicyCycleDisableThresholds() throws RemoteException {
+        PersistableBundle bundle = setupUpdateMobilePolicyCycleTests();
+
+        bundle.putInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT, 31);
+        bundle.putLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG,
+                DATA_CYCLE_THRESHOLD_DISABLED);
+        bundle.putLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG,
+                DATA_CYCLE_THRESHOLD_DISABLED);
+        mServiceContext.sendBroadcast(
+                new Intent(ACTION_CARRIER_CONFIG_CHANGED)
+                        .putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID)
+        );
+
+        assertNetworkPolicyEquals(31, WARNING_DISABLED, LIMIT_DISABLED, true);
+    }
+
+    @Test
+    public void testUpdateMobilePolicyCycleRevertsToDefault() throws RemoteException {
+        PersistableBundle bundle = setupUpdateMobilePolicyCycleTests();
+
+        bundle.putInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT, 31);
+        bundle.putLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG,
+                DATA_CYCLE_THRESHOLD_DISABLED);
+        bundle.putLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG,
+                DATA_CYCLE_THRESHOLD_DISABLED);
+        mServiceContext.sendBroadcast(
+                new Intent(ACTION_CARRIER_CONFIG_CHANGED)
+                        .putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID)
+        );
+        assertNetworkPolicyEquals(31, WARNING_DISABLED, LIMIT_DISABLED, true);
+
+        // If the user switches carriers to one that doesn't use a CarrierConfig, we should revert
+        // to the default data limit and warning. The cycle date doesn't need to revert as it's
+        // arbitrary anyways.
+        bundle.putInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT,
+                DATA_CYCLE_USE_PLATFORM_DEFAULT);
+        bundle.putLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG,
+                DATA_CYCLE_USE_PLATFORM_DEFAULT);
+        bundle.putLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG,
+                DATA_CYCLE_USE_PLATFORM_DEFAULT);
+        mServiceContext.sendBroadcast(
+                new Intent(ACTION_CARRIER_CONFIG_CHANGED)
+                        .putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID)
+        );
+
+        assertNetworkPolicyEquals(31, mDefaultWarningBytes, mDefaultLimitBytes,
+                true);
+    }
+
+    private NetworkPolicy buildDefaultFakeMobilePolicy() {
+        NetworkPolicy p = mService.buildDefaultMobilePolicy(FAKE_SUB_ID, FAKE_SUBSCRIBER_ID);
+        // set a deterministic cycle date
+        p.cycleDay = DEFAULT_CYCLE_DAY;
+        return p;
+    }
+
+    private static NetworkPolicy buildFakeMobilePolicy(int cycleDay, long warningBytes,
+            long limitBytes, boolean inferred){
+        final NetworkTemplate template = buildTemplateMobileAll(FAKE_SUBSCRIBER_ID);
+        return new NetworkPolicy(template, cycleDay, "America/Los_Angeles", warningBytes,
+                limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, true, inferred);
+    }
+
+    private void assertNetworkPolicyEquals(int expectedCycleDay, long expectedWarningBytes,
+            long expectedLimitBytes, boolean expectedInferred) {
+        NetworkPolicy[] policies = mService.getNetworkPolicies(
+                mServiceContext.getOpPackageName());
+        assertEquals("Unexpected number of network policies", 1, policies.length);
+        NetworkPolicy actualPolicy = policies[0];
+        NetworkPolicy expectedPolicy = buildFakeMobilePolicy(expectedCycleDay, expectedWarningBytes,
+                expectedLimitBytes, expectedInferred);
+        assertEquals(expectedPolicy, actualPolicy);
+    }
+
     private static long parseTime(String time) {
         final Time result = new Time();
         result.parse3339(time);
diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
index e3bc34b..c20ee12 100644
--- a/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
@@ -33,7 +33,6 @@
 import com.android.layoutlib.bridge.util.NinePatchInputStream;
 import com.android.ninepatch.NinePatch;
 import com.android.resources.ResourceType;
-import com.android.resources.ResourceUrl;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 import com.android.util.Pair;
 
@@ -60,8 +59,6 @@
 import java.io.InputStream;
 import java.util.Iterator;
 
-import static com.android.SdkConstants.ANDROID_NS_NAME;
-
 @SuppressWarnings("deprecation")
 public class Resources_Delegate {
 
@@ -140,8 +137,8 @@
 
             if (value == null) {
                 // Unable to resolve the attribute, just leave the unresolved value
-                value = new ResourceValue(ResourceUrl.create(resourceInfo.getFirst(), attributeName,
-                        platformResFlag_out[0]), attributeName);
+                value = new ResourceValue(resourceInfo.getFirst(), attributeName, attributeName,
+                        platformResFlag_out[0]);
             }
             return Pair.of(attributeName, value);
         }
@@ -681,7 +678,7 @@
         String packageName;
         if (resourceInfo != null) {
             if (platformOut[0]) {
-                packageName = ANDROID_NS_NAME;
+                packageName = SdkConstants.ANDROID_NS_NAME;
             } else {
                 packageName = resources.mContext.getPackageName();
                 packageName = packageName == null ? SdkConstants.APP_PREFIX : packageName;
@@ -699,7 +696,7 @@
         Pair<ResourceType, String> resourceInfo = getResourceInfo(resources, resid, platformOut);
         if (resourceInfo != null) {
             if (platformOut[0]) {
-                return ANDROID_NS_NAME;
+                return SdkConstants.ANDROID_NS_NAME;
             }
             String packageName = resources.mContext.getPackageName();
             return packageName == null ? SdkConstants.APP_PREFIX : packageName;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 432fdda..a385847 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -31,7 +31,6 @@
 import com.android.layoutlib.bridge.impl.ParserFactory;
 import com.android.layoutlib.bridge.impl.Stack;
 import com.android.resources.ResourceType;
-import com.android.resources.ResourceUrl;
 import com.android.util.Pair;
 import com.android.util.PropertiesMap;
 import com.android.util.PropertiesMap.Property;
@@ -87,6 +86,7 @@
 import android.view.BridgeInflater;
 import android.view.Display;
 import android.view.DisplayAdjustments;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
@@ -107,7 +107,6 @@
 import java.util.Map;
 
 import static android.os._Original_Build.VERSION_CODES.JELLY_BEAN_MR1;
-import static com.android.SdkConstants.ANDROID_NS_NAME;
 import static com.android.layoutlib.bridge.android.RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE;
 
 /**
@@ -122,23 +121,20 @@
 
     static {
         FRAMEWORK_PATCHED_VALUES.put("animateFirstView", new ResourceValue(
-                ResourceUrl.create(ANDROID_NS_NAME, ResourceType.BOOL, "animateFirstView"),
-                "false"));
-        FRAMEWORK_PATCHED_VALUES.put("animateLayoutChanges", new ResourceValue(
-                ResourceUrl.create(ANDROID_NS_NAME, ResourceType.BOOL, "animateLayoutChanges"),
-                "false"));
+                ResourceType.BOOL, "animateFirstView", "false", false));
+        FRAMEWORK_PATCHED_VALUES.put("animateLayoutChanges",
+                new ResourceValue(ResourceType.BOOL, "animateLayoutChanges", "false", false));
 
 
-        FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionItemLayout", new ResourceValue(
-                ResourceUrl.create(ANDROID_NS_NAME, ResourceType.LAYOUT,
-                        "textEditSuggestionItemLayout"), "text_edit_suggestion_item"));
-        FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionContainerLayout", new ResourceValue(
-                ResourceUrl.create(ANDROID_NS_NAME, ResourceType.LAYOUT,
-                        "textEditSuggestionContainerLayout"), "text_edit_suggestion_container"));
-        FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionHighlightStyle", new ResourceValue(
-                ResourceUrl.create(ANDROID_NS_NAME, ResourceType.STYLE,
-                        "textEditSuggestionHighlightStyle"),
-                "TextAppearance.Holo.SuggestionHighlight"));
+        FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionItemLayout",
+                new ResourceValue(ResourceType.LAYOUT, "textEditSuggestionItemLayout",
+                        "text_edit_suggestion_item", true));
+        FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionContainerLayout",
+                new ResourceValue(ResourceType.LAYOUT, "textEditSuggestionContainerLayout",
+                        "text_edit_suggestion_container", true));
+        FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionHighlightStyle",
+                new ResourceValue(ResourceType.STYLE, "textEditSuggestionHighlightStyle",
+                        "TextAppearance.Holo.SuggestionHighlight", true));
 
     }
 
@@ -967,9 +963,7 @@
                     // there is a value in the XML, but we need to resolve it in case it's
                     // referencing another resource or a theme value.
                     ta.bridgeSetValue(index, attrName, frameworkAttr,
-                            mRenderResources.resolveResValue(new ResourceValue(
-                                    ResourceUrl.create(ResourceType.STRING, attrName,
-                                            isPlatformFile), value)));
+                            mRenderResources.resolveValue(null, attrName, value, isPlatformFile));
                 }
             }
         }
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java
index 00dddee..8739b7f 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java
@@ -311,7 +311,6 @@
         sFrameworkRepo = null;
         sProjectResources = null;
         sLogger = null;
-        sBridge.dispose();
         sBridge = null;
 
         TestUtils.gc();
@@ -329,7 +328,6 @@
         RenderSession session = sBridge.createSession(params);
 
         try {
-
             if (frameTimeNanos != -1) {
                 session.setElapsedFrameTimeNanos(frameTimeNanos);
             }
@@ -338,11 +336,13 @@
                 getLogger().error(session.getResult().getException(),
                         session.getResult().getErrorMessage());
             }
-            // Render the session with a timeout of 50s.
-            Result renderResult = session.render(50000);
-            if (!renderResult.isSuccess()) {
-                getLogger().error(session.getResult().getException(),
-                        session.getResult().getErrorMessage());
+            else {
+                // Render the session with a timeout of 50s.
+                Result renderResult = session.render(50000);
+                if (!renderResult.isSuccess()) {
+                    getLogger().error(session.getResult().getException(),
+                            session.getResult().getErrorMessage());
+                }
             }
 
             return RenderResult.getFromSession(session);