Merge "Rename DISALLOW_CONFIG_LOCATION_MODE to DISALLOW_CONFIG_LOCATION."
diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl
index 4461b16..38d9025 100644
--- a/core/java/android/app/slice/ISliceManager.aidl
+++ b/core/java/android/app/slice/ISliceManager.aidl
@@ -31,4 +31,7 @@
     SliceSpec[] getPinnedSpecs(in Uri uri, String pkg);
     int checkSlicePermission(in Uri uri, String pkg, int pid, int uid);
     void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices);
+
+    byte[] getBackupPayload(int user);
+    void applyRestore(in byte[] payload, int user);
 }
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index e5f3eae..3f13fff 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -20,9 +20,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemService;
+import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.IContentProvider;
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
@@ -193,10 +193,15 @@
      * <p>
      * Pinned state is not persisted across reboots, so apps are expected to re-pin any slices
      * they still care about after a reboot.
+     * <p>
+     * This may only be called by apps that are the default launcher for the device
+     * or the default voice interaction service. Otherwise will throw {@link SecurityException}.
      *
      * @param uri The uri of the slice being pinned.
      * @param specs The list of supported {@link SliceSpec}s of the callback.
      * @see SliceProvider#onSlicePinned(Uri)
+     * @see Intent#ACTION_ASSIST
+     * @see Intent#CATEGORY_HOME
      */
     public void pinSlice(@NonNull Uri uri, @NonNull List<SliceSpec> specs) {
         try {
@@ -211,10 +216,15 @@
      * Remove a pin for a slice.
      * <p>
      * If the slice has no other pins/callbacks then the slice will be unpinned.
+     * <p>
+     * This may only be called by apps that are the default launcher for the device
+     * or the default voice interaction service. Otherwise will throw {@link SecurityException}.
      *
      * @param uri The uri of the slice being unpinned.
      * @see #pinSlice
      * @see SliceProvider#onSliceUnpinned(Uri)
+     * @see Intent#ACTION_ASSIST
+     * @see Intent#CATEGORY_HOME
      */
     public void unpinSlice(@NonNull Uri uri) {
         try {
@@ -262,17 +272,13 @@
      */
     public @NonNull Collection<Uri> getSliceDescendants(@NonNull Uri uri) {
         ContentResolver resolver = mContext.getContentResolver();
-        IContentProvider provider = resolver.acquireProvider(uri);
-        try {
+        try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
             Bundle extras = new Bundle();
             extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
-            final Bundle res = provider.call(resolver.getPackageName(),
-                    SliceProvider.METHOD_GET_DESCENDANTS, null, extras);
+            final Bundle res = provider.call(SliceProvider.METHOD_GET_DESCENDANTS, null, extras);
             return res.getParcelableArrayList(SliceProvider.EXTRA_SLICE_DESCENDANTS);
         } catch (RemoteException e) {
             Log.e(TAG, "Unable to get slice descendants", e);
-        } finally {
-            resolver.releaseProvider(provider);
         }
         return Collections.emptyList();
     }
@@ -288,17 +294,15 @@
     public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
         Preconditions.checkNotNull(uri, "uri");
         ContentResolver resolver = mContext.getContentResolver();
-        IContentProvider provider = resolver.acquireProvider(uri);
-        if (provider == null) {
-            throw new IllegalArgumentException("Unknown URI " + uri);
-        }
-        try {
+        try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
+            if (provider == null) {
+                throw new IllegalArgumentException("Unknown URI " + uri);
+            }
             Bundle extras = new Bundle();
             extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
             extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
                     new ArrayList<>(supportedSpecs));
-            final Bundle res = provider.call(mContext.getPackageName(), SliceProvider.METHOD_SLICE,
-                    null, extras);
+            final Bundle res = provider.call(SliceProvider.METHOD_SLICE, null, extras);
             Bundle.setDefusable(res, true);
             if (res == null) {
                 return null;
@@ -308,8 +312,6 @@
             // Arbitrary and not worth documenting, as Activity
             // Manager will kill this process shortly anyway.
             return null;
-        } finally {
-            resolver.releaseProvider(provider);
         }
     }
 
@@ -344,15 +346,13 @@
         String authority = providers.get(0).providerInfo.authority;
         Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
                 .authority(authority).build();
-        IContentProvider provider = resolver.acquireProvider(uri);
-        if (provider == null) {
-            throw new IllegalArgumentException("Unknown URI " + uri);
-        }
-        try {
+        try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
+            if (provider == null) {
+                throw new IllegalArgumentException("Unknown URI " + uri);
+            }
             Bundle extras = new Bundle();
             extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
-            final Bundle res = provider.call(mContext.getPackageName(),
-                    SliceProvider.METHOD_MAP_ONLY_INTENT, null, extras);
+            final Bundle res = provider.call(SliceProvider.METHOD_MAP_ONLY_INTENT, null, extras);
             if (res == null) {
                 return null;
             }
@@ -361,8 +361,6 @@
             // Arbitrary and not worth documenting, as Activity
             // Manager will kill this process shortly anyway.
             return null;
-        } finally {
-            resolver.releaseProvider(provider);
         }
     }
 
@@ -399,17 +397,15 @@
         String authority = providers.get(0).providerInfo.authority;
         Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
                 .authority(authority).build();
-        IContentProvider provider = resolver.acquireProvider(uri);
-        if (provider == null) {
-            throw new IllegalArgumentException("Unknown URI " + uri);
-        }
-        try {
+        try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
+            if (provider == null) {
+                throw new IllegalArgumentException("Unknown URI " + uri);
+            }
             Bundle extras = new Bundle();
             extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
             extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
                     new ArrayList<>(supportedSpecs));
-            final Bundle res = provider.call(mContext.getPackageName(),
-                    SliceProvider.METHOD_MAP_INTENT, null, extras);
+            final Bundle res = provider.call(SliceProvider.METHOD_MAP_INTENT, null, extras);
             if (res == null) {
                 return null;
             }
@@ -418,8 +414,6 @@
             // Arbitrary and not worth documenting, as Activity
             // Manager will kill this process shortly anyway.
             return null;
-        } finally {
-            resolver.releaseProvider(provider);
         }
     }
 
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index bda720bb..7922276 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -439,6 +439,10 @@
         }
     }
 
+    private static long addIfSupported(long stat) {
+        return (stat == UNSUPPORTED) ? 0 : stat;
+    }
+
     /**
      * Return number of packets transmitted across mobile networks since device
      * boot. Counts packets across all mobile network interfaces, and always
@@ -451,7 +455,7 @@
     public static long getMobileTxPackets() {
         long total = 0;
         for (String iface : getMobileIfaces()) {
-            total += getTxPackets(iface);
+            total += addIfSupported(getTxPackets(iface));
         }
         return total;
     }
@@ -468,7 +472,7 @@
     public static long getMobileRxPackets() {
         long total = 0;
         for (String iface : getMobileIfaces()) {
-            total += getRxPackets(iface);
+            total += addIfSupported(getRxPackets(iface));
         }
         return total;
     }
@@ -485,7 +489,7 @@
     public static long getMobileTxBytes() {
         long total = 0;
         for (String iface : getMobileIfaces()) {
-            total += getTxBytes(iface);
+            total += addIfSupported(getTxBytes(iface));
         }
         return total;
     }
@@ -502,7 +506,7 @@
     public static long getMobileRxBytes() {
         long total = 0;
         for (String iface : getMobileIfaces()) {
-            total += getRxBytes(iface);
+            total += addIfSupported(getRxBytes(iface));
         }
         return total;
     }
@@ -517,9 +521,7 @@
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
-            if (stat != UNSUPPORTED) {
-                total += stat;
-            }
+            total += addIfSupported(stat);
         }
         return total;
     }
@@ -534,9 +536,7 @@
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
-            if (stat != UNSUPPORTED) {
-                total += stat;
-            }
+            total += addIfSupported(stat);
         }
         return total;
     }
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 39279b5..74802c8 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -583,7 +583,7 @@
                     installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName,
                             instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
                             uuid, classLoaderContext, seInfo, false /* downgrade */,
-                            targetSdkVersion, /*profileName*/ null);
+                            targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null);
                 } catch (RemoteException | ServiceSpecificException e) {
                     // Ignore (but log), we need this on the classpath for fallback mode.
                     Log.w(TAG, "Failed compiling classpath element for system server: "
diff --git a/core/java/com/android/server/backup/SliceBackupHelper.java b/core/java/com/android/server/backup/SliceBackupHelper.java
new file mode 100644
index 0000000..8e5a5ee
--- /dev/null
+++ b/core/java/com/android/server/backup/SliceBackupHelper.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 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.server.backup;
+
+import android.app.backup.BlobBackupHelper;
+import android.app.slice.ISliceManager;
+import android.content.Context;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+
+public class SliceBackupHelper extends BlobBackupHelper {
+    static final String TAG = "SliceBackupHelper";
+    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    // Current version of the blob schema
+    static final int BLOB_VERSION = 1;
+
+    // Key under which the payload blob is stored
+    static final String KEY_SLICES = "slices";
+
+    public SliceBackupHelper(Context context) {
+        super(BLOB_VERSION, KEY_SLICES);
+        // context is currently unused
+    }
+
+    @Override
+    protected byte[] getBackupPayload(String key) {
+        byte[] newPayload = null;
+        if (KEY_SLICES.equals(key)) {
+            try {
+                ISliceManager sm = ISliceManager.Stub.asInterface(
+                        ServiceManager.getService(Context.SLICE_SERVICE));
+                // TODO: http://b/22388012
+                newPayload = sm.getBackupPayload(UserHandle.USER_SYSTEM);
+            } catch (Exception e) {
+                // Treat as no data
+                Slog.e(TAG, "Couldn't communicate with slice manager");
+                newPayload = null;
+            }
+        }
+        return newPayload;
+    }
+
+    @Override
+    protected void applyRestoredPayload(String key, byte[] payload) {
+        if (DEBUG) Slog.v(TAG, "Got restore of " + key);
+
+        if (KEY_SLICES.equals(key)) {
+            try {
+                ISliceManager sm = ISliceManager.Stub.asInterface(
+                        ServiceManager.getService(Context.SLICE_SERVICE));
+                // TODO: http://b/22388012
+                sm.applyRestore(payload, UserHandle.USER_SYSTEM);
+            } catch (Exception e) {
+                Slog.e(TAG, "Couldn't communicate with slice manager");
+            }
+        }
+    }
+
+}
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index a96b5dd..47e7a0e7 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -51,6 +51,7 @@
     private static final String USAGE_STATS_HELPER = "usage_stats";
     private static final String SHORTCUT_MANAGER_HELPER = "shortcut_manager";
     private static final String ACCOUNT_MANAGER_HELPER = "account_manager";
+    private static final String SLICES_HELPER = "slices";
 
     // These paths must match what the WallpaperManagerService uses.  The leaf *_FILENAME
     // are also used in the full-backup file format, so must not change unless steps are
@@ -88,6 +89,7 @@
         addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this));
         addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper());
         addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper());
+        addHelper(SLICES_HELPER, new SliceBackupHelper(this));
         super.onBackup(oldState, data, newState);
     }
 
@@ -116,6 +118,7 @@
         addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this));
         addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper());
         addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper());
+        addHelper(SLICES_HELPER, new SliceBackupHelper(this));
 
         super.onRestore(data, appVersionCode, newState);
     }
diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
index 0fe7246..f2b4fe0 100644
--- a/media/java/android/media/audiofx/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -546,22 +546,39 @@
         /**
          * Method called when a new waveform capture is available.
          * <p>Data in the waveform buffer is valid only within the scope of the callback.
-         * Applications which needs access to the waveform data after returning from the callback
+         * Applications which need access to the waveform data after returning from the callback
          * should make a copy of the data instead of holding a reference.
          * @param visualizer Visualizer object on which the listener is registered.
          * @param waveform array of bytes containing the waveform representation.
-         * @param samplingRate sampling rate of the audio visualized.
+         * @param samplingRate sampling rate of the visualized audio.
          */
         void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate);
 
         /**
          * Method called when a new frequency capture is available.
          * <p>Data in the fft buffer is valid only within the scope of the callback.
-         * Applications which needs access to the fft data after returning from the callback
+         * Applications which need access to the fft data after returning from the callback
          * should make a copy of the data instead of holding a reference.
+         *
+         * <p>In order to obtain magnitude and phase values the following formulas can
+         * be used:
+         *    <pre class="prettyprint">
+         *       for (int i = 0; i &lt; fft.size(); i += 2) {
+         *           float magnitude = (float)Math.hypot(fft[i], fft[i + 1]);
+         *           float phase = (float)Math.atan2(fft[i + 1], fft[i]);
+         *       }</pre>
          * @param visualizer Visualizer object on which the listener is registered.
          * @param fft array of bytes containing the frequency representation.
-         * @param samplingRate sampling rate of the audio visualized.
+         *    The fft array only contains the first half of the actual
+         *    FFT spectrum (frequencies up to Nyquist frequency), exploiting
+         *    the symmetry of the spectrum. For each frequencies bin <code>i</code>:
+         *    <ul>
+         *      <li>the element at index <code>2*i</code> in the array contains
+         *          the real part of a complex number,</li>
+         *      <li>the element at index <code>2*i+1</code> contains the imaginary
+         *          part of the complex number.</li>
+         *    </ul>
+         * @param samplingRate sampling rate of the visualized audio.
          */
         void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate);
     }
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index c3f20af..b79caca 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -290,13 +290,14 @@
             int dexoptNeeded, @Nullable String outputPath, int dexFlags,
             String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries,
             @Nullable String seInfo, boolean downgrade, int targetSdkVersion,
-            @Nullable String profileName) throws InstallerException {
+            @Nullable String profileName, @Nullable String dexMetadataPath)
+            throws InstallerException {
         assertValidInstructionSet(instructionSet);
         if (!checkBeforeRemote()) return;
         try {
             mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
                     dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade,
-                    targetSdkVersion, profileName);
+                    targetSdkVersion, profileName, dexMetadataPath);
         } catch (Exception e) {
             throw InstallerException.from(e);
         }
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 10e05cf..fc73142 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -261,12 +261,12 @@
                     String instructionSet, int dexoptNeeded, @Nullable String outputPath,
                     int dexFlags, String compilerFilter, @Nullable String volumeUuid,
                     @Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade,
-                    int targetSdkVersion, @Nullable String profileName)
-                    throws InstallerException {
+                    int targetSdkVersion, @Nullable String profileName,
+                    @Nullable String dexMetadataPath) throws InstallerException {
                 final StringBuilder builder = new StringBuilder();
 
-                // The version. Right now it's 5.
-                builder.append("5 ");
+                // The version. Right now it's 6.
+                builder.append("6 ");
 
                 builder.append("dexopt");
 
@@ -284,6 +284,7 @@
                 encodeParameter(builder, downgrade);
                 encodeParameter(builder, targetSdkVersion);
                 encodeParameter(builder, profileName);
+                encodeParameter(builder, dexMetadataPath);
 
                 commands.add(builder.toString());
             }
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index cde8cb7..2c68e67 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -21,6 +21,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageParser;
 import android.content.pm.dex.ArtManager;
+import android.content.pm.dex.DexMetadataHelper;
 import android.os.FileUtils;
 import android.os.PowerManager;
 import android.os.SystemClock;
@@ -209,6 +210,13 @@
 
             String profileName = ArtManager.getProfileName(i == 0 ? null : pkg.splitNames[i - 1]);
 
+            String dexMetadataPath = null;
+            if (options.isDexoptInstallWithDexMetadata()) {
+                File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(new File(path));
+                dexMetadataPath = dexMetadataFile == null
+                        ? null : dexMetadataFile.getAbsolutePath();
+            }
+
             final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary()
                     || packageUseInfo.isUsedByOtherApps(path);
             final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
@@ -223,7 +231,7 @@
             for (String dexCodeIsa : dexCodeInstructionSets) {
                 int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter,
                         profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid,
-                        packageStats, options.isDowngrade(), profileName);
+                        packageStats, options.isDowngrade(), profileName, dexMetadataPath);
                 // The end result is:
                 //  - FAILED if any path failed,
                 //  - PERFORMED if at least one path needed compilation,
@@ -248,7 +256,7 @@
     private int dexOptPath(PackageParser.Package pkg, String path, String isa,
             String compilerFilter, boolean profileUpdated, String classLoaderContext,
             int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade,
-            String profileName) {
+            String profileName, String dexMetadataPath) {
         int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, classLoaderContext,
                 profileUpdated, downgrade);
         if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
@@ -275,7 +283,7 @@
             mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
                     compilerFilter, pkg.volumeUuid, classLoaderContext, pkg.applicationInfo.seInfo,
                     false /* downgrade*/, pkg.applicationInfo.targetSdkVersion,
-                    profileName);
+                    profileName, dexMetadataPath);
 
             if (packageStats != null) {
                 long endTime = System.currentTimeMillis();
@@ -396,7 +404,8 @@
                 mInstaller.dexopt(path, info.uid, info.packageName, isa, /*dexoptNeeded*/ 0,
                         /*oatDir*/ null, dexoptFlags,
                         compilerFilter, info.volumeUuid, classLoaderContext, info.seInfoUser,
-                        options.isDowngrade(), info.targetSdkVersion, /*profileName*/ null);
+                        options.isDowngrade(), info.targetSdkVersion, /*profileName*/ null,
+                        /*dexMetadataPath*/ null);
             }
 
             return DEX_OPT_PERFORMED;
@@ -511,9 +520,13 @@
     private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) {
         int flags = info.flags;
         boolean debuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
-        // Profile guide compiled oat files should not be public.
+        // Profile guide compiled oat files should not be public unles they are based
+        // on profiles from dex metadata archives.
+        // The flag isDexoptInstallWithDexMetadata applies only on installs when we know that
+        // the user does not have an existing profile.
         boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter);
-        boolean isPublic = !info.isForwardLocked() && !isProfileGuidedFilter;
+        boolean isPublic = !info.isForwardLocked() &&
+                (!isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata());
         int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
         // System apps are invoked with a runtime flag which exempts them from
         // restrictions on hidden API usage. We dexopt with the same runtime flag
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 89fbd17..82cd2d9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -17309,7 +17309,8 @@
             // Also, don't fail application installs if the dexopt step fails.
             DexoptOptions dexoptOptions = new DexoptOptions(pkg.packageName,
                     REASON_INSTALL,
-                    DexoptOptions.DEXOPT_BOOT_COMPLETE);
+                    DexoptOptions.DEXOPT_BOOT_COMPLETE |
+                    DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
             mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
                     null /* instructionSets */,
                     getOrCreateCompilerPackageStats(pkg),
diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
index 0966770..d4f95cb 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
@@ -59,6 +59,10 @@
     // When set, indicates that dexopt is invoked from the background service.
     public static final int DEXOPT_IDLE_BACKGROUND_JOB = 1 << 9;
 
+    // When set, indicates that dexopt is invoked from the install time flow and
+    // should get the dex metdata file if present.
+    public static final int DEXOPT_INSTALL_WITH_DEX_METADATA_FILE = 1 << 10;
+
     // The name of package to optimize.
     private final String mPackageName;
 
@@ -90,7 +94,8 @@
                 DEXOPT_ONLY_SHARED_DEX |
                 DEXOPT_DOWNGRADE |
                 DEXOPT_AS_SHARED_LIBRARY |
-                DEXOPT_IDLE_BACKGROUND_JOB;
+                DEXOPT_IDLE_BACKGROUND_JOB |
+                DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
         if ((flags & (~validityMask)) != 0) {
             throw new IllegalArgumentException("Invalid flags : " + Integer.toHexString(flags));
         }
@@ -141,6 +146,10 @@
         return (mFlags & DEXOPT_IDLE_BACKGROUND_JOB) != 0;
     }
 
+    public boolean isDexoptInstallWithDexMetadata() {
+        return (mFlags & DEXOPT_INSTALL_WITH_DEX_METADATA_FILE) != 0;
+    }
+
     public String getSplitName() {
         return mSplitName;
     }
diff --git a/services/core/java/com/android/server/slice/SliceFullAccessList.java b/services/core/java/com/android/server/slice/SliceFullAccessList.java
index 5e0cd03..6f5afa2 100644
--- a/services/core/java/com/android/server/slice/SliceFullAccessList.java
+++ b/services/core/java/com/android/server/slice/SliceFullAccessList.java
@@ -16,9 +16,9 @@
 
 import android.content.Context;
 import android.content.pm.UserInfo;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.ArraySet;
-import android.util.Log;
 import android.util.SparseArray;
 
 import com.android.internal.util.XmlUtils;
@@ -72,7 +72,7 @@
         pkgs.remove(pkg);
     }
 
-    public void writeXml(XmlSerializer out) throws IOException {
+    public void writeXml(XmlSerializer out, int user) throws IOException {
         out.startTag(null, TAG_LIST);
         out.attribute(null, ATT_VERSION, String.valueOf(DB_VERSION));
 
@@ -80,6 +80,9 @@
         for (int i = 0 ; i < N; i++) {
             final int userId = mFullAccessPkgs.keyAt(i);
             final ArraySet<String> pkgs = mFullAccessPkgs.valueAt(i);
+            if (user != UserHandle.USER_ALL && user != userId) {
+                continue;
+            }
             out.startTag(null, TAG_USER);
             out.attribute(null, ATT_USER_ID, Integer.toString(userId));
             if (pkgs != null) {
@@ -88,7 +91,6 @@
                         out.startTag(null, TAG_PKG);
                         out.text(pkgs.valueAt(j));
                         out.endTag(null, TAG_PKG);
-
                 }
             }
             out.endTag(null, TAG_USER);
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index c4871df..a1def44 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -21,6 +21,7 @@
 import static android.content.ContentProvider.maybeAddUserId;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Process.SYSTEM_UID;
 
 import android.Manifest.permission;
 import android.app.ActivityManager;
@@ -68,8 +69,9 @@
 import org.xmlpull.v1.XmlPullParserFactory;
 import org.xmlpull.v1.XmlSerializer;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -93,6 +95,7 @@
     private final ArraySet<SliceGrant> mUserGrants = new ArraySet<>();
     private final Handler mHandler;
     private final ContentObserver mObserver;
+    @GuardedBy("mSliceAccessFile")
     private final AtomicFile mSliceAccessFile;
     @GuardedBy("mAccessList")
     private final SliceFullAccessList mAccessList;
@@ -257,6 +260,63 @@
         }
     }
 
+    // Backup/restore interface
+    @Override
+    public byte[] getBackupPayload(int user) {
+        if (Binder.getCallingUid() != SYSTEM_UID) {
+            throw new SecurityException("Caller must be system");
+        }
+        //TODO: http://b/22388012
+        if (user != UserHandle.USER_SYSTEM) {
+            Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
+            return null;
+        }
+        synchronized(mSliceAccessFile) {
+            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            try {
+                XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
+                out.setOutput(baos, Encoding.UTF_8.name());
+                synchronized (mAccessList) {
+                    mAccessList.writeXml(out, user);
+                }
+                out.flush();
+                return baos.toByteArray();
+            } catch (IOException | XmlPullParserException e) {
+                Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void applyRestore(byte[] payload, int user) {
+        if (Binder.getCallingUid() != SYSTEM_UID) {
+            throw new SecurityException("Caller must be system");
+        }
+        if (payload == null) {
+            Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
+            return;
+        }
+        //TODO: http://b/22388012
+        if (user != UserHandle.USER_SYSTEM) {
+            Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
+            return;
+        }
+        synchronized(mSliceAccessFile) {
+            final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
+            try {
+                XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
+                parser.setInput(bais, Encoding.UTF_8.name());
+                synchronized (mAccessList) {
+                    mAccessList.readXml(parser);
+                }
+                mHandler.post(mSaveAccessList);
+            } catch (NumberFormatException | XmlPullParserException | IOException e) {
+                Slog.w(TAG, "applyRestore: error reading payload", e);
+            }
+        }
+    }
+
     ///  ----- internal code -----
     private void removeFullAccess(String pkg, int userId) {
         synchronized (mAccessList) {
@@ -492,7 +552,7 @@
                     XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
                     out.setOutput(stream, Encoding.UTF_8.name());
                     synchronized (mAccessList) {
-                        mAccessList.writeXml(out);
+                        mAccessList.writeXml(out, UserHandle.USER_ALL);
                     }
                     out.flush();
                     mSliceAccessFile.finishWrite(stream);
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
index c2072df..f559986a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
@@ -53,6 +53,7 @@
         assertFalse(opt.isDowngrade());
         assertFalse(opt.isForce());
         assertFalse(opt.isDexoptIdleBackgroundJob());
+        assertFalse(opt.isDexoptInstallWithDexMetadata());
     }
 
     @Test
@@ -65,7 +66,8 @@
                 DexoptOptions.DEXOPT_ONLY_SHARED_DEX |
                 DexoptOptions.DEXOPT_DOWNGRADE  |
                 DexoptOptions.DEXOPT_AS_SHARED_LIBRARY |
-                DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB;
+                DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB |
+                DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
 
         DexoptOptions opt = new DexoptOptions(mPackageName, mCompilerFilter, flags);
         assertEquals(mPackageName, opt.getPackageName());
@@ -79,6 +81,7 @@
         assertTrue(opt.isForce());
         assertTrue(opt.isDexoptAsSharedLibrary());
         assertTrue(opt.isDexoptIdleBackgroundJob());
+        assertTrue(opt.isDexoptInstallWithDexMetadata());
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java
index b784c60..bc28150 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java
@@ -18,6 +18,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import android.os.UserHandle;
 import android.support.test.filters.SmallTest;
 import android.util.Xml.Encoding;
 
@@ -85,7 +86,7 @@
         ByteArrayOutputStream output = new ByteArrayOutputStream();
         XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
         out.setOutput(output, Encoding.UTF_8.name());
-        mAccessList.writeXml(out);
+        mAccessList.writeXml(out, UserHandle.USER_ALL);
         out.flush();
 
         assertEquals(TEST_XML, output.toString(Encoding.UTF_8.name()));