Add instant cookie APIs

This change adds APIs for instant apps to store cookie data
that is presisted across instant installs and across the
upgrade from an instant to a standard app. Standard apps
can use the cookie APIs but when they are uninstalled the
cookie is also deleted. The cookies are kept longer than
the instant apps as they are much smaller - 16KB by default.
We can change the cookie size via a system setting i.e.
after we ship we can increase size if needed.

We also add internal APIs to surface information about
installed and uninstalled instant apps which should be
used for showing them in the UI. For this puporse we store
the icon, permissions, and label of uninstalled apps. If
the app is re-installed we drop this meta-data but keep
the cookie around. If we have cookie data stored and the
signing cert of the app changes when it gets re-intalled
we wipe the cookie.

Test: CTS tests pass; hiddent APIs tested manually

Change-Id: If145c0440cc61a5303e2cbb70228d235d36037a5
diff --git a/api/current.txt b/api/current.txt
index 95e8a5d..71187b0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10154,6 +10154,8 @@
     method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
+    method public abstract byte[] getInstantAppCookie();
+    method public abstract int getInstantAppCookieMaxSize();
     method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public abstract android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
@@ -10187,6 +10189,7 @@
     method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract boolean hasSystemFeature(java.lang.String);
     method public abstract boolean hasSystemFeature(java.lang.String, int);
+    method public abstract boolean isInstantApp();
     method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public abstract boolean isSafeMode();
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -10206,6 +10209,7 @@
     method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
     method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
     method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
+    method public abstract boolean setInstantAppCookie(byte[]);
     method public abstract void verifyPendingInstall(int, int);
     field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
     field public static final int COMPONENT_ENABLED_STATE_DISABLED = 2; // 0x2
@@ -15247,11 +15251,11 @@
     method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
     field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
     field public static final int VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR = 16; // 0x10
+    field public static final int VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
     field public static final int VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = 8; // 0x8
     field public static final int VIRTUAL_DISPLAY_FLAG_PRESENTATION = 2; // 0x2
     field public static final int VIRTUAL_DISPLAY_FLAG_PUBLIC = 1; // 0x1
     field public static final int VIRTUAL_DISPLAY_FLAG_SECURE = 4; // 0x4
-    field public static final int VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
   }
 
   public static abstract interface DisplayManager.DisplayListener {
@@ -39669,6 +39673,8 @@
     method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public java.lang.String getInstallerPackageName(java.lang.String);
+    method public byte[] getInstantAppCookie();
+    method public int getInstantAppCookieMaxSize();
     method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
@@ -39701,6 +39707,7 @@
     method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public boolean hasSystemFeature(java.lang.String);
     method public boolean hasSystemFeature(java.lang.String, int);
+    method public boolean isInstantApp();
     method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public boolean isSafeMode();
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -39720,6 +39727,7 @@
     method public void setApplicationEnabledSetting(java.lang.String, int, int);
     method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
     method public void setInstallerPackageName(java.lang.String, java.lang.String);
+    method public boolean setInstantAppCookie(byte[]);
     method public void verifyPendingInstall(int, int);
   }
 
@@ -42619,11 +42627,11 @@
     method public boolean isValid();
     method public boolean isWideColorGamut();
     field public static final int DEFAULT_DISPLAY = 0; // 0x0
+    field public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
     field public static final int FLAG_PRESENTATION = 8; // 0x8
     field public static final int FLAG_PRIVATE = 4; // 0x4
     field public static final int FLAG_ROUND = 16; // 0x10
     field public static final int FLAG_SECURE = 2; // 0x2
-    field public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
     field public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1; // 0x1
     field public static final int INVALID_DISPLAY = -1; // 0xffffffff
     field public static final int STATE_DOZE = 3; // 0x3
diff --git a/api/system-current.txt b/api/system-current.txt
index 4bd3838..bb9d64f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -10627,6 +10627,8 @@
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
+    method public abstract byte[] getInstantAppCookie();
+    method public abstract int getInstantAppCookieMaxSize();
     method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
     method public abstract int getIntentVerificationStatusAsUser(java.lang.String, int);
@@ -10664,6 +10666,7 @@
     method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract boolean hasSystemFeature(java.lang.String);
     method public abstract boolean hasSystemFeature(java.lang.String, int);
+    method public abstract boolean isInstantApp();
     method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public abstract boolean isSafeMode();
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -10687,6 +10690,7 @@
     method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
     method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
     method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
+    method public abstract boolean setInstantAppCookie(byte[]);
     method public abstract boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int);
     method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
     method public abstract void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
@@ -15825,11 +15829,11 @@
     method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
     field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
     field public static final int VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR = 16; // 0x10
+    field public static final int VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
     field public static final int VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = 8; // 0x8
     field public static final int VIRTUAL_DISPLAY_FLAG_PRESENTATION = 2; // 0x2
     field public static final int VIRTUAL_DISPLAY_FLAG_PUBLIC = 1; // 0x1
     field public static final int VIRTUAL_DISPLAY_FLAG_SECURE = 4; // 0x4
-    field public static final int VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
   }
 
   public static abstract interface DisplayManager.DisplayListener {
@@ -43061,6 +43065,8 @@
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
     method public java.lang.String getInstallerPackageName(java.lang.String);
+    method public byte[] getInstantAppCookie();
+    method public int getInstantAppCookieMaxSize();
     method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
     method public int getIntentVerificationStatusAsUser(java.lang.String, int);
@@ -43097,6 +43103,7 @@
     method public void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public boolean hasSystemFeature(java.lang.String);
     method public boolean hasSystemFeature(java.lang.String, int);
+    method public boolean isInstantApp();
     method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public boolean isSafeMode();
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -43119,6 +43126,7 @@
     method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
     method public boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
     method public void setInstallerPackageName(java.lang.String, java.lang.String);
+    method public boolean setInstantAppCookie(byte[]);
     method public boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int);
     method public void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
     method public void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
@@ -46022,11 +46030,11 @@
     method public boolean isValid();
     method public boolean isWideColorGamut();
     field public static final int DEFAULT_DISPLAY = 0; // 0x0
+    field public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
     field public static final int FLAG_PRESENTATION = 8; // 0x8
     field public static final int FLAG_PRIVATE = 4; // 0x4
     field public static final int FLAG_ROUND = 16; // 0x10
     field public static final int FLAG_SECURE = 2; // 0x2
-    field public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
     field public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1; // 0x1
     field public static final int INVALID_DISPLAY = -1; // 0xffffffff
     field public static final int STATE_DOZE = 3; // 0x3
diff --git a/api/test-current.txt b/api/test-current.txt
index 89b098a..31aa02d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -10185,6 +10185,8 @@
     method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
+    method public abstract byte[] getInstantAppCookie();
+    method public abstract int getInstantAppCookieMaxSize();
     method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public abstract android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
@@ -10218,6 +10220,7 @@
     method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract boolean hasSystemFeature(java.lang.String);
     method public abstract boolean hasSystemFeature(java.lang.String, int);
+    method public abstract boolean isInstantApp();
     method public abstract boolean isPermissionReviewModeEnabled();
     method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public abstract boolean isSafeMode();
@@ -10238,6 +10241,7 @@
     method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
     method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
     method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
+    method public abstract boolean setInstantAppCookie(byte[]);
     method public abstract void verifyPendingInstall(int, int);
     field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
     field public static final int COMPONENT_ENABLED_STATE_DISABLED = 2; // 0x2
@@ -15281,11 +15285,11 @@
     method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
     field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
     field public static final int VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR = 16; // 0x10
+    field public static final int VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
     field public static final int VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = 8; // 0x8
     field public static final int VIRTUAL_DISPLAY_FLAG_PRESENTATION = 2; // 0x2
     field public static final int VIRTUAL_DISPLAY_FLAG_PUBLIC = 1; // 0x1
     field public static final int VIRTUAL_DISPLAY_FLAG_SECURE = 4; // 0x4
-    field public static final int VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
   }
 
   public static abstract interface DisplayManager.DisplayListener {
@@ -39807,6 +39811,8 @@
     method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public java.lang.String getInstallerPackageName(java.lang.String);
+    method public byte[] getInstantAppCookie();
+    method public int getInstantAppCookieMaxSize();
     method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
@@ -39839,6 +39845,7 @@
     method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public boolean hasSystemFeature(java.lang.String);
     method public boolean hasSystemFeature(java.lang.String, int);
+    method public boolean isInstantApp();
     method public boolean isPermissionReviewModeEnabled();
     method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public boolean isSafeMode();
@@ -39859,6 +39866,7 @@
     method public void setApplicationEnabledSetting(java.lang.String, int, int);
     method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
     method public void setInstallerPackageName(java.lang.String, java.lang.String);
+    method public boolean setInstantAppCookie(byte[]);
     method public void verifyPendingInstall(int, int);
   }
 
@@ -42923,11 +42931,11 @@
     method public boolean isValid();
     method public boolean isWideColorGamut();
     field public static final int DEFAULT_DISPLAY = 0; // 0x0
+    field public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
     field public static final int FLAG_PRESENTATION = 8; // 0x8
     field public static final int FLAG_PRIVATE = 4; // 0x4
     field public static final int FLAG_ROUND = 16; // 0x10
     field public static final int FLAG_SECURE = 2; // 0x2
-    field public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
     field public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1; // 0x1
     field public static final int INVALID_DISPLAY = -1; // 0xffffffff
     field public static final int STATE_DOZE = 3; // 0x3
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 2c2b279..0c6c4ba 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -17,7 +17,6 @@
 package android.app;
 
 import android.annotation.DrawableRes;
-import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringRes;
@@ -31,7 +30,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ComponentInfo;
-import android.content.pm.EphemeralApplicationInfo;
+import android.content.pm.InstantAppInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IOnPermissionsChangeListener;
 import android.content.pm.IPackageDataObserver;
@@ -766,10 +765,10 @@
     /** @hide */
     @SuppressWarnings("unchecked")
     @Override
-    public List<EphemeralApplicationInfo> getEphemeralApplications() {
+    public List<InstantAppInfo> getInstantApps() {
         try {
-            ParceledListSlice<EphemeralApplicationInfo> slice =
-                    mPM.getEphemeralApplications(mContext.getUserId());
+            ParceledListSlice<InstantAppInfo> slice =
+                    mPM.getInstantApps(mContext.getUserId());
             if (slice != null) {
                 return slice.getList();
             }
@@ -781,9 +780,9 @@
 
     /** @hide */
     @Override
-    public Drawable getEphemeralApplicationIcon(String packageName) {
+    public Drawable getInstantAppIcon(String packageName) {
         try {
-            Bitmap bitmap = mPM.getEphemeralApplicationIcon(
+            Bitmap bitmap = mPM.getInstantAppIcon(
                     packageName, mContext.getUserId());
             if (bitmap != null) {
                 return new BitmapDrawable(null, bitmap);
@@ -795,26 +794,26 @@
     }
 
     @Override
-    public boolean isEphemeralApplication() {
+    public boolean isInstantApp() {
         try {
-            return mPM.isEphemeralApplication(
-                    mContext.getPackageName(), mContext.getUserId());
+            return mPM.isInstantApp(mContext.getPackageName(),
+                    mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     @Override
-    public int getEphemeralCookieMaxSizeBytes() {
+    public int getInstantAppCookieMaxSize() {
         return Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES,
                 DEFAULT_EPHEMERAL_COOKIE_MAX_SIZE_BYTES);
     }
 
     @Override
-    public @NonNull byte[] getEphemeralCookie() {
+    public @NonNull byte[] getInstantAppCookie() {
         try {
-            final byte[] cookie = mPM.getEphemeralApplicationCookie(
+            final byte[] cookie = mPM.getInstantAppCookie(
                     mContext.getPackageName(), mContext.getUserId());
             if (cookie != null) {
                 return cookie;
@@ -827,10 +826,10 @@
     }
 
     @Override
-    public boolean setEphemeralCookie(@NonNull  byte[] cookie) {
+    public boolean setInstantAppCookie(@NonNull byte[] cookie) {
         try {
-            return mPM.setEphemeralApplicationCookie(
-                    mContext.getPackageName(), cookie, mContext.getUserId());
+            return mPM.setInstantAppCookie(mContext.getPackageName(),
+                    cookie, mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index b53792f..8465f0f 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1409,7 +1409,7 @@
     /**
      * @hide
      */
-    public boolean isEphemeralApp() {
+    public boolean isInstantApp() {
         return (privateFlags & ApplicationInfo.PRIVATE_FLAG_EPHEMERAL) != 0;
     }
 
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 4cf65ab..2754452 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -23,7 +23,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ContainerEncryptionParams;
-import android.content.pm.EphemeralApplicationInfo;
+import android.content.pm.InstantAppInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IPackageInstallObserver2;
 import android.content.pm.IPackageInstaller;
@@ -599,11 +599,11 @@
 
     String getPermissionControllerPackageName();
 
-    ParceledListSlice getEphemeralApplications(int userId);
-    byte[] getEphemeralApplicationCookie(String packageName, int userId);
-    boolean setEphemeralApplicationCookie(String packageName, in byte[] cookie, int userId);
-    Bitmap getEphemeralApplicationIcon(String packageName, int userId);
-    boolean isEphemeralApplication(String packageName, int userId);
+    ParceledListSlice getInstantApps(int userId);
+    byte[] getInstantAppCookie(String packageName, int userId);
+    boolean setInstantAppCookie(String packageName, in byte[] cookie, int userId);
+    Bitmap getInstantAppIcon(String packageName, int userId);
+    boolean isInstantApp(String packageName, int userId);
 
     boolean setRequiredForSystemUser(String packageName, boolean systemUserApp);
 
diff --git a/core/java/android/content/pm/EphemeralApplicationInfo.aidl b/core/java/android/content/pm/InstantAppInfo.aidl
similarity index 94%
rename from core/java/android/content/pm/EphemeralApplicationInfo.aidl
rename to core/java/android/content/pm/InstantAppInfo.aidl
index 5aaae78..a35892f 100644
--- a/core/java/android/content/pm/EphemeralApplicationInfo.aidl
+++ b/core/java/android/content/pm/InstantAppInfo.aidl
@@ -16,4 +16,4 @@
 
 package android.content.pm;
 
-parcelable EphemeralApplicationInfo;
+parcelable InstantAppInfo;
\ No newline at end of file
diff --git a/core/java/android/content/pm/EphemeralApplicationInfo.java b/core/java/android/content/pm/InstantAppInfo.java
similarity index 65%
rename from core/java/android/content/pm/EphemeralApplicationInfo.java
rename to core/java/android/content/pm/InstantAppInfo.java
index 87663f1..898ee110 100644
--- a/core/java/android/content/pm/EphemeralApplicationInfo.java
+++ b/core/java/android/content/pm/InstantAppInfo.java
@@ -23,11 +23,15 @@
 import android.os.Parcelable;
 
 /**
- * This class represents the state of an ephemeral app.
+ * This class represents the state of an instant app. Instant apps can
+ * be installed or uninstalled. If the app is installed you can call
+ * {@link #getApplicationInfo()} to get the app info, otherwise this
+ * class provides APIs to get basic app info for showing it in the UI,
+ * such as permissions, label, package name.
  *
  * @hide
  */
-public final class EphemeralApplicationInfo implements Parcelable {
+public final class InstantAppInfo implements Parcelable {
     private final ApplicationInfo mApplicationInfo;
 
     private final String mPackageName;
@@ -36,7 +40,7 @@
     private final String[] mRequestedPermissions;
     private final String[] mGrantedPermissions;
 
-    public EphemeralApplicationInfo(ApplicationInfo appInfo,
+    public InstantAppInfo(ApplicationInfo appInfo,
             String[] requestedPermissions, String[] grantedPermissions) {
         mApplicationInfo = appInfo;
         mPackageName = null;
@@ -45,7 +49,7 @@
         mGrantedPermissions = grantedPermissions;
     }
 
-    public EphemeralApplicationInfo(String packageName, CharSequence label,
+    public InstantAppInfo(String packageName, CharSequence label,
             String[] requestedPermissions, String[] grantedPermissions) {
         mApplicationInfo = null;
         mPackageName = packageName;
@@ -54,7 +58,7 @@
         mGrantedPermissions = grantedPermissions;
     }
 
-    private EphemeralApplicationInfo(Parcel parcel) {
+    private InstantAppInfo(Parcel parcel) {
         mPackageName = parcel.readString();
         mLabelText = parcel.readCharSequence();
         mRequestedPermissions = parcel.readStringArray();
@@ -62,6 +66,17 @@
         mApplicationInfo = parcel.readParcelable(null);
     }
 
+    /**
+     * @return The application info if the app is installed,
+     *     <code>null</code> otherwise,
+     */
+    public @Nullable ApplicationInfo getApplicationInfo() {
+        return mApplicationInfo;
+    }
+
+    /**
+     * @return The pakcage name.
+     */
     public @NonNull String getPackageName() {
         if (mApplicationInfo != null) {
             return mApplicationInfo.packageName;
@@ -69,6 +84,10 @@
         return mPackageName;
     }
 
+    /**
+     * @param packageManager Package manager for loading resources.
+     * @return Loads the label if the app is installed or returns the cached one otherwise.
+     */
     public @NonNull CharSequence loadLabel(@NonNull PackageManager packageManager) {
         if (mApplicationInfo != null) {
             return mApplicationInfo.loadLabel(packageManager);
@@ -76,17 +95,27 @@
         return mLabelText;
     }
 
+    /**
+     * @param packageManager Package manager for loading resources.
+     * @return Loads the icon if the app is installed or returns the cached one otherwise.
+     */
     public @NonNull Drawable loadIcon(@NonNull PackageManager packageManager) {
         if (mApplicationInfo != null) {
             return mApplicationInfo.loadIcon(packageManager);
         }
-        return packageManager.getEphemeralApplicationIcon(mPackageName);
+        return packageManager.getInstantAppIcon(mPackageName);
     }
 
+    /**
+     * @return The requested permissions.
+     */
     public @Nullable String[] getRequestedPermissions() {
         return mRequestedPermissions;
     }
 
+    /**
+     * @return The granted permissions.
+     */
     public @Nullable String[] getGrantedPermissions() {
         return mGrantedPermissions;
     }
@@ -105,16 +134,16 @@
         parcel.writeParcelable(mApplicationInfo, flags);
     }
 
-    public static final Creator<EphemeralApplicationInfo> CREATOR =
-            new Creator<EphemeralApplicationInfo>() {
+    public static final Creator<InstantAppInfo> CREATOR =
+            new Creator<InstantAppInfo>() {
         @Override
-        public EphemeralApplicationInfo createFromParcel(Parcel parcel) {
-            return new EphemeralApplicationInfo(parcel);
+        public InstantAppInfo createFromParcel(Parcel parcel) {
+            return new InstantAppInfo(parcel);
         }
 
         @Override
-        public EphemeralApplicationInfo[] newArray(int size) {
-            return new EphemeralApplicationInfo[0];
+        public InstantAppInfo[] newArray(int size) {
+            return new InstantAppInfo[0];
         }
     };
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 76c69ca..ab641c9 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -20,7 +20,6 @@
 import android.annotation.CheckResult;
 import android.annotation.DrawableRes;
 import android.annotation.IntDef;
-import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -3422,7 +3421,6 @@
      */
     public abstract void removePermission(String name);
 
-
     /**
      * Permission flags set when granting or revoking a permission.
      *
@@ -3698,89 +3696,86 @@
             @ApplicationInfoFlags int flags, @UserIdInt int userId);
 
     /**
-     * Gets the ephemeral applications the user recently used. Requires
-     * holding "android.permission.ACCESS_EPHEMERAL_APPS".
+     * Gets the instant applications the user recently used. Requires
+     * holding "android.permission.ACCESS_INSTANT_APPS".
      *
-     * @return The ephemeral app list.
+     * @return The instant app list.
      *
      * @hide
      */
-    @RequiresPermission(Manifest.permission.ACCESS_EPHEMERAL_APPS)
-    public abstract List<EphemeralApplicationInfo> getEphemeralApplications();
+    @RequiresPermission(Manifest.permission.ACCESS_INSTANT_APPS)
+    public abstract @NonNull List<InstantAppInfo> getInstantApps();
 
     /**
-     * Gets the icon for an ephemeral application.
+     * Gets the icon for an instant application.
      *
      * @param packageName The app package name.
      *
      * @hide
      */
-    public abstract Drawable getEphemeralApplicationIcon(String packageName);
+    @RequiresPermission(Manifest.permission.ACCESS_INSTANT_APPS)
+    public abstract @Nullable Drawable getInstantAppIcon(String packageName);
 
     /**
-     * Gets whether the caller is an ephemeral app.
+     * Gets whether the caller is an instant app.
      *
-     * @return Whether caller is an ephemeral app.
+     * @return Whether caller is an instant app.
      *
-     * @see #setEphemeralCookie(byte[])
-     * @see #getEphemeralCookie()
-     * @see #getEphemeralCookieMaxSizeBytes()
-     *
-     * @hide
+     * @see #setInstantAppCookie(byte[])
+     * @see #getInstantAppCookie()
+     * @see #getInstantAppCookieMaxSize()
      */
-    public abstract boolean isEphemeralApplication();
+    public abstract boolean isInstantApp();
 
     /**
-     * Gets the maximum size in bytes of the cookie data an ephemeral app
+     * Gets the maximum size in bytes of the cookie data an instant app
      * can store on the device.
      *
      * @return The max cookie size in bytes.
      *
-     * @see #isEphemeralApplication()
-     * @see #setEphemeralCookie(byte[])
-     * @see #getEphemeralCookie()
-     *
-     * @hide
+     * @see #isInstantApp()
+     * @see #setInstantAppCookie(byte[])
+     * @see #getInstantAppCookie()
      */
-    public abstract int getEphemeralCookieMaxSizeBytes();
+    public abstract int getInstantAppCookieMaxSize();
 
     /**
-     * Gets the ephemeral application cookie for this app. Non
-     * ephemeral apps and apps that were ephemeral but were upgraded
-     * to non-ephemeral can still access this API. For ephemeral apps
+     * Gets the instant application cookie for this app. Non
+     * instant apps and apps that were instant but were upgraded
+     * to normal apps can still access this API. For instant apps
      * this cooke is cached for some time after uninstall while for
      * normal apps the cookie is deleted after the app is uninstalled.
      * The cookie is always present while the app is installed.
      *
      * @return The cookie.
      *
-     * @see #isEphemeralApplication()
-     * @see #setEphemeralCookie(byte[])
-     * @see #getEphemeralCookieMaxSizeBytes()
-     *
-     * @hide
+     * @see #isInstantApp()
+     * @see #setInstantAppCookie(byte[])
+     * @see #getInstantAppCookieMaxSize()
      */
-    public abstract @NonNull byte[] getEphemeralCookie();
+    public abstract @NonNull byte[] getInstantAppCookie();
 
     /**
-     * Sets the ephemeral application cookie for the calling app. Non
-     * ephemeral apps and apps that were ephemeral but were upgraded
-     * to non-ephemeral can still access this API. For ephemeral apps
+     * Sets the instant application cookie for the calling app. Non
+     * instant apps and apps that were instant but were upgraded
+     * to normal apps can still access this API. For instant apps
      * this cooke is cached for some time after uninstall while for
      * normal apps the cookie is deleted after the app is uninstalled.
      * The cookie is always present while the app is installed. The
-     * cookie size is limited by {@link #getEphemeralCookieMaxSizeBytes()}.
+     * cookie size is limited by {@link #getInstantAppCookieMaxSize()}.
+     * If the provided cookie size is over the limit this method
+     * returns <code>false</code>. Passing <code>null</code> or an empty
+     * array clears the cookie.
+     * </p>
      *
      * @param cookie The cookie data.
-     * @return True if the cookie was set.
+     * @return Whether the cookie was set.
      *
-     * @see #isEphemeralApplication()
-     * @see #getEphemeralCookieMaxSizeBytes()
-     * @see #getEphemeralCookie()
-     *
-     * @hide
+     * @see #isInstantApp()
+     * @see #getInstantAppCookieMaxSize()
+     * @see #getInstantAppCookie()
      */
-    public abstract boolean setEphemeralCookie(@NonNull  byte[] cookie);
+    public abstract boolean setInstantAppCookie(@Nullable byte[] cookie);
 
     /**
      * Get a list of shared libraries that are available on the
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index e3e02b1f..e4e0a19 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -243,6 +243,12 @@
             int targetAppId, int ephemeralAppId);
 
     /**
+     * Prunes instant apps and state associated with uninstalled
+     * instant apps according to the current platform policy.
+     */
+    public abstract void pruneInstantApps();
+
+    /**
      * @return The SetupWizard package name.
      */
     public abstract String getSetupWizardPackageName();
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 6901ba1..0703138 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -120,7 +120,6 @@
      */
     public static final int PROTECTION_FLAG_SETUP = 0x800;
 
-
     /**
      * Additional flag for {@link #protectionLevel}, corresponding
      * to the <code>ephemeral</code> value of
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f7ebf99a..9bc7034 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9504,13 +9504,13 @@
         public static final String ENABLE_EPHEMERAL_FEATURE = "enable_ephemeral_feature";
 
         /**
-         * The duration for caching uninstalled ephemeral apps.
+         * The duration for caching uninstalled instant apps.
          * <p>
          * Type: long
          * @hide
          */
-        public static final String UNINSTALLED_EPHEMERAL_APP_CACHE_DURATION_MILLIS =
-                "uninstalled_ephemeral_app_cache_duration_millis";
+        public static final String UNINSTALLED_INSTANT_APP_CACHE_DURATION_MILLIS =
+                "uninstalled_instant_app_cache_duration_millis";
 
         /**
          * Allows switching users when system user is locked.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 540f924..11eb47b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3176,8 +3176,8 @@
 
     <!-- Allows the holder to access the instant applications on the device.
     @hide -->
-    <permission android:name="android.permission.ACCESS_EPHEMERAL_APPS"
-            android:protectionLevel="signature" />
+    <permission android:name="android.permission.ACCESS_INSTANT_APPS"
+            android:protectionLevel="signature|installer" />
 
     <!-- Allows receiving the usage of media resource e.g. video/audio codec and
          graphic memory.
@@ -3519,11 +3519,15 @@
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
 
-        <service android:name="com.android.server.pm.BackgroundDexOptService"
+        <service android:name="com.android.server.BackgroundDexOptJobService"
                  android:exported="true"
                  android:permission="android.permission.BIND_JOB_SERVICE">
         </service>
 
+        <service android:name="com.android.server.PruneInstantAppsJobService"
+                 android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
+
         <service android:name="com.android.server.storage.DiskStatsLoggingService"
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
diff --git a/core/tests/coretests/src/android/provider/SettingsTest.java b/core/tests/coretests/src/android/provider/SettingsTest.java
index 0c9c3c17..05b9730 100644
--- a/core/tests/coretests/src/android/provider/SettingsTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsTest.java
@@ -309,7 +309,7 @@
                     Settings.Global.TRUSTED_SOUND,
                     Settings.Global.TZINFO_UPDATE_CONTENT_URL,
                     Settings.Global.TZINFO_UPDATE_METADATA_URL,
-                    Settings.Global.UNINSTALLED_EPHEMERAL_APP_CACHE_DURATION_MILLIS,
+                    Settings.Global.UNINSTALLED_INSTANT_APP_CACHE_DURATION_MILLIS,
                     Settings.Global.UNLOCK_SOUND,
                     Settings.Global.USE_GOOGLE_MAIL,
                     Settings.Global.VT_IMS_ENABLED,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 19ce3d0..8bbc8c9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -879,7 +879,7 @@
                 Settings.Global.ENABLE_EPHEMERAL_FEATURE,
                 GlobalSettingsProto.ENABLE_EPHEMERAL_FEATURE);
         dumpSetting(s, p,
-                Settings.Global.UNINSTALLED_EPHEMERAL_APP_CACHE_DURATION_MILLIS,
+                Settings.Global.UNINSTALLED_INSTANT_APP_CACHE_DURATION_MILLIS,
                 GlobalSettingsProto.UNINSTALLED_EPHEMERAL_APP_CACHE_DURATION_MILLIS);
         dumpSetting(s, p,
                 Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 7a9ba20..8be9243 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1546,7 +1546,7 @@
 
     private List<String> getSettingsNamesLocked(int settingsType, int userId) {
         ApplicationInfo ai = getCallingApplicationInfoOrThrow(userId);
-        if (ai.isEphemeralApp()) {
+        if (ai.isInstantApp()) {
             return new ArrayList<String>(getEphemeralAccessibleSettings(settingsType));
         } else {
             return mSettingsRegistry.getSettingsNamesLocked(settingsType, userId);
@@ -1558,7 +1558,7 @@
             return;
         }
         ApplicationInfo ai = getCallingApplicationInfoOrThrow(userId);
-        if (!ai.isEphemeralApp()) {
+        if (!ai.isInstantApp()) {
             return;
         }
         if (!getEphemeralAccessibleSettings(settingsType).contains(settingName)) {
diff --git a/services/core/java/com/android/server/BackgroundDexOptJobService.java b/services/core/java/com/android/server/BackgroundDexOptJobService.java
new file mode 100644
index 0000000..69e6ac5
--- /dev/null
+++ b/services/core/java/com/android/server/BackgroundDexOptJobService.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2014 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;
+
+import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.Environment;
+import android.os.ServiceManager;
+import android.os.storage.StorageManager;
+import android.util.ArraySet;
+import android.util.Log;
+import com.android.server.pm.PackageManagerService;
+
+import java.io.File;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.TimeUnit;
+
+public class BackgroundDexOptJobService extends JobService {
+    private static final String TAG = "BackgroundDexOptJobService";
+
+    private static final boolean DEBUG = false;
+
+    private static final int JOB_IDLE_OPTIMIZE = 800;
+    private static final int JOB_POST_BOOT_UPDATE = 801;
+
+    private static final long IDLE_OPTIMIZATION_PERIOD = DEBUG
+            ? TimeUnit.MINUTES.toMillis(1)
+            : TimeUnit.DAYS.toMillis(1);
+
+    private static ComponentName sDexoptServiceName = new ComponentName(
+            "android",
+            BackgroundDexOptJobService.class.getName());
+
+    /**
+     * Set of failed packages remembered across job runs.
+     */
+    static final ArraySet<String> sFailedPackageNames = new ArraySet<String>();
+
+    /**
+     * Atomics set to true if the JobScheduler requests an abort.
+     */
+    final AtomicBoolean mAbortPostBootUpdate = new AtomicBoolean(false);
+    final AtomicBoolean mAbortIdleOptimization = new AtomicBoolean(false);
+
+    /**
+     * Atomic set to true if one job should exit early because another job was started.
+     */
+    final AtomicBoolean mExitPostBootUpdate = new AtomicBoolean(false);
+
+    private final File mDataDir = Environment.getDataDirectory();
+
+    public static void schedule(Context context) {
+        JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+
+        // Schedule a one-off job which scans installed packages and updates
+        // out-of-date oat files.
+        js.schedule(new JobInfo.Builder(JOB_POST_BOOT_UPDATE, sDexoptServiceName)
+                    .setMinimumLatency(TimeUnit.MINUTES.toMillis(1))
+                    .setOverrideDeadline(TimeUnit.MINUTES.toMillis(1))
+                    .build());
+
+        // Schedule a daily job which scans installed packages and compiles
+        // those with fresh profiling data.
+        js.schedule(new JobInfo.Builder(JOB_IDLE_OPTIMIZE, sDexoptServiceName)
+                    .setRequiresDeviceIdle(true)
+                    .setRequiresCharging(true)
+                    .setPeriodic(IDLE_OPTIMIZATION_PERIOD)
+                    .build());
+
+        if (DEBUG_DEXOPT) {
+            Log.i(TAG, "Jobs scheduled");
+        }
+    }
+
+    public static void notifyPackageChanged(String packageName) {
+        // The idle maintanance job skips packages which previously failed to
+        // compile. The given package has changed and may successfully compile
+        // now. Remove it from the list of known failing packages.
+        synchronized (sFailedPackageNames) {
+            sFailedPackageNames.remove(packageName);
+        }
+    }
+
+    // Returns the current battery level as a 0-100 integer.
+    private int getBatteryLevel() {
+        IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+        Intent intent = registerReceiver(null, filter);
+        int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+        int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
+
+        if (level < 0 || scale <= 0) {
+            // Battery data unavailable. This should never happen, so assume the worst.
+            return 0;
+        }
+
+        return (100 * level / scale);
+    }
+
+    private long getLowStorageThreshold() {
+        @SuppressWarnings("deprecation")
+        final long lowThreshold = StorageManager.from(this).getStorageLowBytes(mDataDir);
+        if (lowThreshold == 0) {
+            Log.e(TAG, "Invalid low storage threshold");
+        }
+
+        return lowThreshold;
+    }
+
+    private boolean runPostBootUpdate(final JobParameters jobParams,
+            final PackageManagerService pm, final ArraySet<String> pkgs) {
+        if (mExitPostBootUpdate.get()) {
+            // This job has already been superseded. Do not start it.
+            return false;
+        }
+        new Thread("BackgroundDexOptService_PostBootUpdate") {
+            @Override
+            public void run() {
+                postBootUpdate(jobParams, pm, pkgs);
+            }
+
+        }.start();
+        return true;
+    }
+
+    private void postBootUpdate(JobParameters jobParams, PackageManagerService pm,
+            ArraySet<String> pkgs) {
+        // Load low battery threshold from the system config. This is a 0-100 integer.
+        final int lowBatteryThreshold = getResources().getInteger(
+                com.android.internal.R.integer.config_lowBatteryWarningLevel);
+        final long lowThreshold = getLowStorageThreshold();
+
+        mAbortPostBootUpdate.set(false);
+
+        for (String pkg : pkgs) {
+            if (mAbortPostBootUpdate.get()) {
+                // JobScheduler requested an early abort.
+                return;
+            }
+            if (mExitPostBootUpdate.get()) {
+                // Different job, which supersedes this one, is running.
+                break;
+            }
+            if (getBatteryLevel() < lowBatteryThreshold) {
+                // Rather bail than completely drain the battery.
+                break;
+            }
+            long usableSpace = mDataDir.getUsableSpace();
+            if (usableSpace < lowThreshold) {
+                // Rather bail than completely fill up the disk.
+                Log.w(TAG, "Aborting background dex opt job due to low storage: " +
+                        usableSpace);
+                break;
+            }
+
+            if (DEBUG_DEXOPT) {
+                Log.i(TAG, "Updating package " + pkg);
+            }
+
+            // Update package if needed. Note that there can be no race between concurrent
+            // jobs because PackageDexOptimizer.performDexOpt is synchronized.
+
+            // checkProfiles is false to avoid merging profiles during boot which
+            // might interfere with background compilation (b/28612421).
+            // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will
+            // behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a
+            // trade-off worth doing to save boot time work.
+            pm.performDexOpt(pkg,
+                    /* checkProfiles */ false,
+                    PackageManagerService.REASON_BOOT,
+                    /* force */ false);
+        }
+        // Ran to completion, so we abandon our timeslice and do not reschedule.
+        jobFinished(jobParams, /* reschedule */ false);
+    }
+
+    private boolean runIdleOptimization(final JobParameters jobParams,
+            final PackageManagerService pm, final ArraySet<String> pkgs) {
+        new Thread("BackgroundDexOptService_IdleOptimization") {
+            @Override
+            public void run() {
+                idleOptimization(jobParams, pm, pkgs);
+            }
+        }.start();
+        return true;
+    }
+
+    private void idleOptimization(JobParameters jobParams, PackageManagerService pm,
+            ArraySet<String> pkgs) {
+        Log.i(TAG, "Performing idle optimizations");
+        // If post-boot update is still running, request that it exits early.
+        mExitPostBootUpdate.set(true);
+
+        mAbortIdleOptimization.set(false);
+
+        final long lowThreshold = getLowStorageThreshold();
+        for (String pkg : pkgs) {
+            if (mAbortIdleOptimization.get()) {
+                // JobScheduler requested an early abort.
+                return;
+            }
+
+            synchronized (sFailedPackageNames) {
+                if (sFailedPackageNames.contains(pkg)) {
+                    // Skip previously failing package
+                    continue;
+                }
+            }
+
+            long usableSpace = mDataDir.getUsableSpace();
+            if (usableSpace < lowThreshold) {
+                // Rather bail than completely fill up the disk.
+                Log.w(TAG, "Aborting background dex opt job due to low storage: " +
+                        usableSpace);
+                break;
+            }
+
+            // Conservatively add package to the list of failing ones in case performDexOpt
+            // never returns.
+            synchronized (sFailedPackageNames) {
+                sFailedPackageNames.add(pkg);
+            }
+            // Optimize package if needed. Note that there can be no race between
+            // concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized.
+            if (pm.performDexOpt(pkg,
+                    /* checkProfiles */ true,
+                    PackageManagerService.REASON_BACKGROUND_DEXOPT,
+                    /* force */ false)) {
+                // Dexopt succeeded, remove package from the list of failing ones.
+                synchronized (sFailedPackageNames) {
+                    sFailedPackageNames.remove(pkg);
+                }
+            }
+        }
+        // Ran to completion, so we abandon our timeslice and do not reschedule.
+        jobFinished(jobParams, /* reschedule */ false);
+    }
+
+    @Override
+    public boolean onStartJob(JobParameters params) {
+        if (DEBUG_DEXOPT) {
+            Log.i(TAG, "onStartJob");
+        }
+
+        // NOTE: PackageManagerService.isStorageLow uses a different set of criteria from
+        // the checks above. This check is not "live" - the value is determined by a background
+        // restart with a period of ~1 minute.
+        PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");
+        if (pm.isStorageLow()) {
+            if (DEBUG_DEXOPT) {
+                Log.i(TAG, "Low storage, skipping this run");
+            }
+            return false;
+        }
+
+        final ArraySet<String> pkgs = pm.getOptimizablePackages();
+        if (pkgs == null || pkgs.isEmpty()) {
+            if (DEBUG_DEXOPT) {
+                Log.i(TAG, "No packages to optimize");
+            }
+            return false;
+        }
+
+        if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
+            return runPostBootUpdate(params, pm, pkgs);
+        } else {
+            return runIdleOptimization(params, pm, pkgs);
+        }
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters params) {
+        if (DEBUG_DEXOPT) {
+            Log.i(TAG, "onStopJob");
+        }
+
+        if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
+            mAbortPostBootUpdate.set(true);
+        } else {
+            mAbortIdleOptimization.set(true);
+        }
+        return false;
+    }
+}
diff --git a/services/core/java/com/android/server/PruneInstantAppsJobService.java b/services/core/java/com/android/server/PruneInstantAppsJobService.java
new file mode 100644
index 0000000..a6c3685
--- /dev/null
+++ b/services/core/java/com/android/server/PruneInstantAppsJobService.java
@@ -0,0 +1,61 @@
+/*
+ * 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.server;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManagerInternal;
+
+import java.util.concurrent.TimeUnit;
+
+public class PruneInstantAppsJobService extends JobService {
+    private static final boolean DEBUG = false;
+
+    private static final int JOB_ID = 765123;
+
+    private static final long PRUNE_INSTANT_APPS_PERIOD_MILLIS = DEBUG
+            ? TimeUnit.MINUTES.toMillis(1) : TimeUnit.DAYS.toMillis(1);
+
+    public static void schedule(Context context) {
+        JobInfo pruneJob = new JobInfo.Builder(JOB_ID, new ComponentName(
+                context.getPackageName(), PruneInstantAppsJobService.class.getName()))
+                .setRequiresDeviceIdle(true)
+                .setPeriodic(PRUNE_INSTANT_APPS_PERIOD_MILLIS)
+                .build();
+
+        JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+        jobScheduler.schedule(pruneJob);
+    }
+
+    @Override
+    public boolean onStartJob(JobParameters params) {
+        PackageManagerInternal packageManagerInternal = LocalServices.getService(
+                PackageManagerInternal.class);
+        packageManagerInternal.pruneInstantApps();
+        jobFinished(params, false);
+        return true;
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters params) {
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c08bcef..0809792 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18651,7 +18651,7 @@
                     List<BroadcastFilter> registeredReceiversForUser =
                             mReceiverResolver.queryIntent(intent,
                                     resolvedType, false, false /*visibleToEphemeral*/,
-                                    false /*isEphemeral*/, users[i]);
+                                    false /*isInstant*/, users[i]);
                     if (registeredReceivers == null) {
                         registeredReceivers = registeredReceiversForUser;
                     } else if (registeredReceiversForUser != null) {
@@ -18661,7 +18661,7 @@
             } else {
                 registeredReceivers = mReceiverResolver.queryIntent(intent,
                         resolvedType, false, false /*visibleToEphemeral*/,
-                        false /*isEphemeral*/, userId);
+                        false /*isInstant*/, userId);
             }
         }
 
@@ -21553,7 +21553,7 @@
                     // Keeping this process, update its uid.
                     final UidRecord uidRec = app.uidRecord;
                     if (uidRec != null) {
-                        uidRec.ephemeral = app.info.isEphemeralApp();
+                        uidRec.ephemeral = app.info.isInstantApp();
                         if (uidRec.curProcState > app.curProcState) {
                             uidRec.curProcState = app.curProcState;
                         }
diff --git a/services/core/java/com/android/server/firewall/IntentFirewall.java b/services/core/java/com/android/server/firewall/IntentFirewall.java
index 19eed35..93c14b9 100644
--- a/services/core/java/com/android/server/firewall/IntentFirewall.java
+++ b/services/core/java/com/android/server/firewall/IntentFirewall.java
@@ -152,7 +152,7 @@
         // component-filter that matches this intent
         List<Rule> candidateRules;
         candidateRules = resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/,
-                false /*visibleToEphemeral*/, false /*isEphemeral*/, 0);
+                false /*visibleToEphemeral*/, false /*isInstant*/, 0);
         if (candidateRules == null) {
             candidateRules = new ArrayList<Rule>();
         }
diff --git a/services/core/java/com/android/server/pm/BasePermission.java b/services/core/java/com/android/server/pm/BasePermission.java
index 8948fa3..07c9dec 100644
--- a/services/core/java/com/android/server/pm/BasePermission.java
+++ b/services/core/java/com/android/server/pm/BasePermission.java
@@ -95,7 +95,7 @@
                 && (protectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0;
     }
 
-    public boolean isEphemeral() {
+    public boolean isInstant() {
         return (protectionLevel & PermissionInfo.PROTECTION_FLAG_EPHEMERAL) != 0;
     }
 }
diff --git a/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java b/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java
deleted file mode 100644
index 3d946e0..0000000
--- a/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java
+++ /dev/null
@@ -1,851 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      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.pm;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.EphemeralApplicationInfo;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageUserState;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Binder;
-import android.os.Environment;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.AtomicFile;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
-import android.util.Xml;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.XmlUtils;
-import libcore.io.IoUtils;
-import libcore.util.EmptyArray;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * This class is a part of the package manager service that is responsible
- * for managing data associated with ephemeral apps such as cached uninstalled
- * ephemeral apps and ephemeral apps' cookies.
- */
-class EphemeralApplicationRegistry {
-    private static final boolean DEBUG = false;
-
-    private static final boolean ENABLED = true;
-
-    private static final String LOG_TAG = "EphemeralAppRegistry";
-
-    private static final long DEFAULT_UNINSTALLED_EPHEMERAL_APP_CACHE_DURATION_MILLIS =
-            DEBUG ? 60 * 1000L /* one min */ : 30 * 24 * 60 * 60 * 1000L; /* one month */
-
-    private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
-
-    private static final String EPHEMERAL_APPS_FOLDER = "ephemeral";
-    private static final String EPHEMERAL_APP_ICON_FILE = "icon.png";
-    private static final String EPHEMERAL_APP_COOKIE_FILE_PREFIX = "cookie_";
-    private static final String EPHEMERAL_APP_COOKIE_FILE_SIFFIX = ".dat";
-    private static final String EPHEMERAL_APP_METADATA_FILE = "metadata.xml";
-
-    private static final String TAG_PACKAGE = "package";
-    private static final String TAG_PERMS = "perms";
-    private static final String TAG_PERM = "perm";
-
-    private static final String ATTR_LABEL = "label";
-    private static final String ATTR_NAME = "name";
-    private static final String ATTR_GRANTED = "granted";
-
-    private final PackageManagerService mService;
-
-    @GuardedBy("mService.mPackages")
-    private SparseArray<List<UninstalledEphemeralAppState>> mUninstalledEphemeralApps;
-
-    /**
-     * Automatic grants for access to instant app metadata.
-     * The key is the target application UID.
-     * The value is a set of instant app UIDs.
-     * UserID -> TargetAppId -> InstantAppId
-     */
-    private SparseArray<SparseArray<SparseBooleanArray>> mEphemeralGrants;
-    /** The set of all installed instant apps. UserID -> AppID */
-    private SparseArray<SparseBooleanArray> mInstalledEphemeralAppUids;
-
-    public EphemeralApplicationRegistry(PackageManagerService service) {
-        mService = service;
-    }
-
-    public byte[] getEphemeralApplicationCookieLPw(String packageName, int userId) {
-        if (!ENABLED) {
-            return EmptyArray.BYTE;
-        }
-        pruneUninstalledEphemeralAppsLPw(userId);
-
-        File cookieFile = peekEphemeralCookieFile(packageName, userId);
-        if (cookieFile != null && cookieFile.exists()) {
-            try {
-                return IoUtils.readFileAsByteArray(cookieFile.toString());
-            } catch (IOException e) {
-                Slog.w(LOG_TAG, "Error reading cookie file: " + cookieFile);
-            }
-        }
-        return null;
-    }
-
-    public boolean setEphemeralApplicationCookieLPw(String packageName,
-            byte[] cookie, int userId) {
-        if (!ENABLED) {
-            return false;
-        }
-        pruneUninstalledEphemeralAppsLPw(userId);
-
-        PackageParser.Package pkg = mService.mPackages.get(packageName);
-        if (pkg == null) {
-            return false;
-        }
-
-        if (!isValidCookie(mService.mContext, cookie)) {
-            return false;
-        }
-
-        File appDir = getEphemeralApplicationDir(pkg.packageName, userId);
-        if (!appDir.exists() && !appDir.mkdirs()) {
-            return false;
-        }
-
-        File cookieFile = computeEphemeralCookieFile(pkg, userId);
-        if (cookieFile.exists() && !cookieFile.delete()) {
-            return false;
-        }
-
-        try (FileOutputStream fos = new FileOutputStream(cookieFile)) {
-            fos.write(cookie, 0, cookie.length);
-        } catch (IOException e) {
-            Slog.w(LOG_TAG, "Error writing cookie file: " + cookieFile);
-            return false;
-        }
-        return true;
-    }
-
-    public Bitmap getEphemeralApplicationIconLPw(String packageName, int userId) {
-        if (!ENABLED) {
-            return null;
-        }
-        pruneUninstalledEphemeralAppsLPw(userId);
-
-        File iconFile = new File(getEphemeralApplicationDir(packageName, userId),
-                EPHEMERAL_APP_ICON_FILE);
-        if (iconFile.exists()) {
-            return BitmapFactory.decodeFile(iconFile.toString());
-        }
-        return null;
-    }
-
-    public List<EphemeralApplicationInfo> getEphemeralApplicationsLPw(int userId) {
-        if (!ENABLED) {
-            return Collections.emptyList();
-        }
-        pruneUninstalledEphemeralAppsLPw(userId);
-
-        List<EphemeralApplicationInfo> result = getInstalledEphemeralApplicationsLPr(userId);
-        result.addAll(getUninstalledEphemeralApplicationsLPr(userId));
-        return result;
-    }
-
-    public void onPackageInstalledLPw(PackageParser.Package pkg) {
-        if (!ENABLED) {
-            return;
-        }
-        PackageSetting ps = (PackageSetting) pkg.mExtras;
-        if (ps == null) {
-            return;
-        }
-        for (int userId : UserManagerService.getInstance().getUserIds()) {
-            pruneUninstalledEphemeralAppsLPw(userId);
-
-            // Ignore not installed apps
-            if (mService.mPackages.get(pkg.packageName) == null || !ps.getInstalled(userId)) {
-                continue;
-            }
-
-            // Propagate permissions before removing any state
-            // TODO: Fix this later
-            // propagateEphemeralAppPermissionsIfNeeded(pkg, userId);
-            if (pkg.applicationInfo.isEphemeralApp()) {
-                addEphemeralAppLPw(userId, ps.appId);
-            }
-
-            // Remove the in-memory state
-            if (mUninstalledEphemeralApps != null) {
-                List<UninstalledEphemeralAppState> uninstalledAppStates =
-                        mUninstalledEphemeralApps.get(userId);
-                if (uninstalledAppStates != null) {
-                    final int appCount = uninstalledAppStates.size();
-                    for (int i = 0; i < appCount; i++) {
-                        UninstalledEphemeralAppState uninstalledAppState =
-                                uninstalledAppStates.get(i);
-                        if (uninstalledAppState.mEphemeralApplicationInfo
-                                .getPackageName().equals(pkg.packageName)) {
-                            uninstalledAppStates.remove(i);
-                            break;
-                        }
-                    }
-                }
-            }
-
-            // Remove the on-disk state except the cookie
-            File ephemeralAppDir = getEphemeralApplicationDir(pkg.packageName, userId);
-            new File(ephemeralAppDir, EPHEMERAL_APP_METADATA_FILE).delete();
-            new File(ephemeralAppDir, EPHEMERAL_APP_ICON_FILE).delete();
-
-            // If app signature changed - wipe the cookie
-            File currentCookieFile = peekEphemeralCookieFile(pkg.packageName, userId);
-            if (currentCookieFile == null) {
-                continue;
-            }
-            File expectedCookeFile = computeEphemeralCookieFile(pkg, userId);
-            if (!currentCookieFile.equals(expectedCookeFile)) {
-                Slog.i(LOG_TAG, "Signature for package " + pkg.packageName
-                        + " changed - dropping cookie");
-                currentCookieFile.delete();
-            }
-        }
-    }
-
-    public void onPackageUninstalledLPw(PackageParser.Package pkg) {
-        if (!ENABLED) {
-            return;
-        }
-        if (pkg == null) {
-            return;
-        }
-        PackageSetting ps = (PackageSetting) pkg.mExtras;
-        if (ps == null) {
-            return;
-        }
-        for (int userId : UserManagerService.getInstance().getUserIds()) {
-            pruneUninstalledEphemeralAppsLPw(userId);
-
-            if (mService.mPackages.get(pkg.packageName) != null && ps.getInstalled(userId)) {
-                continue;
-            }
-
-            if (pkg.applicationInfo.isEphemeralApp()) {
-                // Add a record for an uninstalled ephemeral app
-                addUninstalledEphemeralAppLPw(pkg, userId);
-                removeEphemeralAppLPw(userId, ps.appId);
-            } else {
-                // Deleting an app prunes all ephemeral state such as cookie
-                deleteDir(getEphemeralApplicationDir(pkg.packageName, userId));
-                removeAppLPw(userId, ps.appId);
-            }
-        }
-    }
-
-    public void onUserRemovedLPw(int userId) {
-        if (!ENABLED) {
-            return;
-        }
-        if (mUninstalledEphemeralApps != null) {
-            mUninstalledEphemeralApps.remove(userId);
-        }
-        if (mInstalledEphemeralAppUids != null) {
-            mInstalledEphemeralAppUids.remove(userId);
-        }
-        if (mEphemeralGrants != null) {
-            mEphemeralGrants.remove(userId);
-        }
-        deleteDir(getEphemeralApplicationsDir(userId));
-    }
-
-    public boolean isEphemeralAccessGranted(int userId, int targetAppId, int ephemeralAppId) {
-        if (mEphemeralGrants == null) {
-            return false;
-        }
-        final SparseArray<SparseBooleanArray> targetAppList = mEphemeralGrants.get(userId);
-        if (targetAppList == null) {
-            return false;
-        }
-        final SparseBooleanArray ephemeralGrantList = targetAppList.get(targetAppId);
-        if (ephemeralGrantList == null) {
-            return false;
-        }
-        return ephemeralGrantList.get(ephemeralAppId);
-    }
-
-    public void grantEphemeralAccessLPw(int userId, Intent intent,
-            int targetAppId, int ephemeralAppId) {
-        if (mInstalledEphemeralAppUids == null) {
-            return;     // no ephemeral apps installed; no need to grant
-        }
-        SparseBooleanArray ephemeralAppList = mInstalledEphemeralAppUids.get(userId);
-        if (ephemeralAppList == null || !ephemeralAppList.get(ephemeralAppId)) {
-            return;     // ephemeral app id isn't installed; no need to grant
-        }
-        if (ephemeralAppList.get(targetAppId)) {
-            return;     // target app id is an ephemeral app; no need to grant
-        }
-        if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) {
-            final Set<String> categories = intent.getCategories();
-            if (categories != null && categories.contains(Intent.CATEGORY_BROWSABLE)) {
-                return;  // launched via VIEW/BROWSABLE intent; no need to grant
-            }
-        }
-        if (mEphemeralGrants == null) {
-            mEphemeralGrants = new SparseArray<>();
-        }
-        SparseArray<SparseBooleanArray> targetAppList = mEphemeralGrants.get(userId);
-        if (targetAppList == null) {
-            targetAppList = new SparseArray<>();
-            mEphemeralGrants.put(userId, targetAppList);
-        }
-        SparseBooleanArray ephemeralGrantList = targetAppList.get(targetAppId);
-        if (ephemeralGrantList == null) {
-            ephemeralGrantList = new SparseBooleanArray();
-            targetAppList.put(targetAppId, ephemeralGrantList);
-        }
-        ephemeralGrantList.put(ephemeralAppId, true /*granted*/);
-    }
-
-    public void addEphemeralAppLPw(int userId, int ephemeralAppId) {
-        if (mInstalledEphemeralAppUids == null) {
-            mInstalledEphemeralAppUids = new SparseArray<>();
-        }
-        SparseBooleanArray ephemeralAppList = mInstalledEphemeralAppUids.get(userId);
-        if (ephemeralAppList == null) {
-            ephemeralAppList = new SparseBooleanArray();
-            mInstalledEphemeralAppUids.put(userId, ephemeralAppList);
-        }
-        ephemeralAppList.put(ephemeralAppId, true /*installed*/);
-    }
-
-    private void removeEphemeralAppLPw(int userId, int ephemeralAppId) {
-        // remove from the installed list
-        if (mInstalledEphemeralAppUids == null) {
-            return; // no ephemeral apps on the system
-        }
-        final SparseBooleanArray ephemeralAppList = mInstalledEphemeralAppUids.get(userId);
-        if (ephemeralAppList == null) {
-            Slog.w(LOG_TAG, "Remove ephemeral not in install list");
-            return;
-        } else {
-            ephemeralAppList.delete(ephemeralAppId);
-        }
-        // remove any grants
-        if (mEphemeralGrants == null) {
-            return; // no grants on the system
-        }
-        final SparseArray<SparseBooleanArray> targetAppList = mEphemeralGrants.get(userId);
-        if (targetAppList == null) {
-            return; // no grants for this user
-        }
-        final int numApps = targetAppList.size();
-        for (int i = targetAppList.size() - 1; i >= 0; --i) {
-            targetAppList.valueAt(i).delete(ephemeralAppId);
-        }
-    }
-
-    private void removeAppLPw(int userId, int targetAppId) {
-        // remove from the installed list
-        if (mEphemeralGrants == null) {
-            return; // no grants on the system
-        }
-        final SparseArray<SparseBooleanArray> targetAppList = mEphemeralGrants.get(userId);
-        if (targetAppList == null) {
-            return; // no grants for this user
-        }
-        targetAppList.delete(targetAppId);
-    }
-
-    private void addUninstalledEphemeralAppLPw(PackageParser.Package pkg, int userId) {
-        EphemeralApplicationInfo uninstalledApp = createEphemeralAppInfoForPackage(pkg, userId);
-        if (uninstalledApp == null) {
-            return;
-        }
-        if (mUninstalledEphemeralApps == null) {
-            mUninstalledEphemeralApps = new SparseArray<>();
-        }
-        List<UninstalledEphemeralAppState> uninstalledAppStates =
-                mUninstalledEphemeralApps.get(userId);
-        if (uninstalledAppStates == null) {
-            uninstalledAppStates = new ArrayList<>();
-            mUninstalledEphemeralApps.put(userId, uninstalledAppStates);
-        }
-        UninstalledEphemeralAppState uninstalledAppState = new UninstalledEphemeralAppState(
-                uninstalledApp, System.currentTimeMillis());
-        uninstalledAppStates.add(uninstalledAppState);
-
-        writeUninstalledEphemeralAppMetadata(uninstalledApp, userId);
-        writeEphemeralApplicationIconLPw(pkg, userId);
-    }
-
-    private void writeEphemeralApplicationIconLPw(PackageParser.Package pkg, int userId) {
-        File appDir = getEphemeralApplicationDir(pkg.packageName, userId);
-        if (!appDir.exists()) {
-            return;
-        }
-
-        Drawable icon = pkg.applicationInfo.loadIcon(mService.mContext.getPackageManager());
-
-        final Bitmap bitmap;
-        if (icon instanceof BitmapDrawable) {
-            bitmap = ((BitmapDrawable) icon).getBitmap();
-        } else  {
-            bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(),
-                    icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
-            Canvas canvas = new Canvas(bitmap);
-            icon.draw(canvas);
-        }
-
-        File iconFile = new File(getEphemeralApplicationDir(pkg.packageName, userId),
-                EPHEMERAL_APP_ICON_FILE);
-
-        try (FileOutputStream out = new FileOutputStream(iconFile)) {
-            bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
-        } catch (Exception e) {
-            Slog.e(LOG_TAG, "Error writing ephemeral app icon", e);
-        }
-    }
-
-    private void pruneUninstalledEphemeralAppsLPw(int userId) {
-        final long maxCacheDurationMillis = Settings.Global.getLong(
-                mService.mContext.getContentResolver(),
-                Settings.Global.UNINSTALLED_EPHEMERAL_APP_CACHE_DURATION_MILLIS,
-                DEFAULT_UNINSTALLED_EPHEMERAL_APP_CACHE_DURATION_MILLIS);
-
-        // Prune in-memory state
-        if (mUninstalledEphemeralApps != null) {
-            List<UninstalledEphemeralAppState> uninstalledAppStates =
-                    mUninstalledEphemeralApps.get(userId);
-            if (uninstalledAppStates != null) {
-                final int appCount = uninstalledAppStates.size();
-                for (int j = appCount - 1; j >= 0; j--) {
-                    UninstalledEphemeralAppState uninstalledAppState = uninstalledAppStates.get(j);
-                    final long elapsedCachingMillis = System.currentTimeMillis()
-                            - uninstalledAppState.mTimestamp;
-                    if (elapsedCachingMillis > maxCacheDurationMillis) {
-                        uninstalledAppStates.remove(j);
-                    }
-                }
-                if (uninstalledAppStates.isEmpty()) {
-                    mUninstalledEphemeralApps.remove(userId);
-                }
-            }
-        }
-
-        // Prune on-disk state
-        File ephemeralAppsDir = getEphemeralApplicationsDir(userId);
-        if (!ephemeralAppsDir.exists()) {
-            return;
-        }
-        File[] files = ephemeralAppsDir.listFiles();
-        if (files == null) {
-            return;
-        }
-        for (File ephemeralDir : files) {
-            if (!ephemeralDir.isDirectory()) {
-                continue;
-            }
-
-            File metadataFile = new File(ephemeralDir, EPHEMERAL_APP_METADATA_FILE);
-            if (!metadataFile.exists()) {
-                continue;
-            }
-
-            final long elapsedCachingMillis = System.currentTimeMillis()
-                    - metadataFile.lastModified();
-            if (elapsedCachingMillis > maxCacheDurationMillis) {
-                deleteDir(ephemeralDir);
-            }
-        }
-    }
-
-    private List<EphemeralApplicationInfo> getInstalledEphemeralApplicationsLPr(int userId) {
-        List<EphemeralApplicationInfo> result = null;
-
-        final int packageCount = mService.mPackages.size();
-        for (int i = 0; i < packageCount; i++) {
-            PackageParser.Package pkg = mService.mPackages.valueAt(i);
-            if (!pkg.applicationInfo.isEphemeralApp()) {
-                continue;
-            }
-            EphemeralApplicationInfo info = createEphemeralAppInfoForPackage(pkg, userId);
-            if (info == null) {
-                continue;
-            }
-            if (result == null) {
-                result = new ArrayList<>();
-            }
-            result.add(info);
-        }
-
-        return result;
-    }
-
-    private EphemeralApplicationInfo createEphemeralAppInfoForPackage(
-            PackageParser.Package pkg, int userId) {
-        PackageSetting ps = (PackageSetting) pkg.mExtras;
-        if (ps == null) {
-            return null;
-        }
-        PackageUserState userState = ps.readUserState(userId);
-        if (userState == null || !userState.installed || userState.hidden) {
-            return null;
-        }
-
-        String[] requestedPermissions = new String[pkg.requestedPermissions.size()];
-        pkg.requestedPermissions.toArray(requestedPermissions);
-
-        Set<String> permissions = ps.getPermissionsState().getPermissions(userId);
-        String[] grantedPermissions = new String[permissions.size()];
-        permissions.toArray(grantedPermissions);
-
-        return new EphemeralApplicationInfo(pkg.applicationInfo,
-                requestedPermissions, grantedPermissions);
-    }
-
-    private List<EphemeralApplicationInfo> getUninstalledEphemeralApplicationsLPr(int userId) {
-        List<UninstalledEphemeralAppState> uninstalledAppStates =
-                getUninstalledEphemeralAppStatesLPr(userId);
-        if (uninstalledAppStates == null || uninstalledAppStates.isEmpty()) {
-            return Collections.emptyList();
-        }
-
-        List<EphemeralApplicationInfo> uninstalledApps = new ArrayList<>();
-        final int stateCount = uninstalledAppStates.size();
-        for (int i = 0; i < stateCount; i++) {
-            UninstalledEphemeralAppState uninstalledAppState = uninstalledAppStates.get(i);
-            uninstalledApps.add(uninstalledAppState.mEphemeralApplicationInfo);
-        }
-        return uninstalledApps;
-    }
-
-    private void propagateEphemeralAppPermissionsIfNeeded(PackageParser.Package pkg, int userId) {
-        EphemeralApplicationInfo appInfo = getOrParseUninstalledEphemeralAppInfo(pkg.packageName, userId);
-        if (appInfo == null) {
-            return;
-        }
-        if (ArrayUtils.isEmpty(appInfo.getGrantedPermissions())) {
-            return;
-        }
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            for (String grantedPermission : appInfo.getGrantedPermissions()) {
-                mService.grantRuntimePermission(pkg.packageName, grantedPermission, userId);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    private EphemeralApplicationInfo getOrParseUninstalledEphemeralAppInfo(String packageName,
-            int userId) {
-        if (mUninstalledEphemeralApps != null) {
-            List<UninstalledEphemeralAppState> uninstalledAppStates =
-                    mUninstalledEphemeralApps.get(userId);
-            if (uninstalledAppStates != null) {
-                final int appCount = uninstalledAppStates.size();
-                for (int i = 0; i < appCount; i++) {
-                    UninstalledEphemeralAppState uninstalledAppState = uninstalledAppStates.get(i);
-                    if (uninstalledAppState.mEphemeralApplicationInfo
-                            .getPackageName().equals(packageName)) {
-                        return uninstalledAppState.mEphemeralApplicationInfo;
-                    }
-                }
-            }
-        }
-
-        File metadataFile = new File(getEphemeralApplicationDir(packageName, userId),
-                EPHEMERAL_APP_METADATA_FILE);
-        UninstalledEphemeralAppState uninstalledAppState = parseMetadataFile(metadataFile);
-        if (uninstalledAppState == null) {
-            return null;
-        }
-
-        return uninstalledAppState.mEphemeralApplicationInfo;
-    }
-
-    private List<UninstalledEphemeralAppState> getUninstalledEphemeralAppStatesLPr(int userId) {
-        List<UninstalledEphemeralAppState> uninstalledAppStates = null;
-        if (mUninstalledEphemeralApps != null) {
-            uninstalledAppStates = mUninstalledEphemeralApps.get(userId);
-            if (uninstalledAppStates != null) {
-                return uninstalledAppStates;
-            }
-        }
-
-        File ephemeralAppsDir = getEphemeralApplicationsDir(userId);
-        if (ephemeralAppsDir.exists()) {
-            File[] files = ephemeralAppsDir.listFiles();
-            if (files != null) {
-                for (File ephemeralDir : files) {
-                    if (!ephemeralDir.isDirectory()) {
-                        continue;
-                    }
-                    File metadataFile = new File(ephemeralDir,
-                            EPHEMERAL_APP_METADATA_FILE);
-                    UninstalledEphemeralAppState uninstalledAppState =
-                            parseMetadataFile(metadataFile);
-                    if (uninstalledAppState == null) {
-                        continue;
-                    }
-                    if (uninstalledAppStates == null) {
-                        uninstalledAppStates = new ArrayList<>();
-                    }
-                    uninstalledAppStates.add(uninstalledAppState);
-                }
-            }
-        }
-
-        if (uninstalledAppStates != null) {
-            if (mUninstalledEphemeralApps == null) {
-                mUninstalledEphemeralApps = new SparseArray<>();
-            }
-            mUninstalledEphemeralApps.put(userId, uninstalledAppStates);
-        }
-
-        return uninstalledAppStates;
-    }
-
-    private static boolean isValidCookie(Context context, byte[] cookie) {
-        if (ArrayUtils.isEmpty(cookie)) {
-            return true;
-        }
-        return cookie.length <= context.getPackageManager().getEphemeralCookieMaxSizeBytes();
-    }
-
-    private static UninstalledEphemeralAppState parseMetadataFile(File metadataFile) {
-        if (!metadataFile.exists()) {
-            return null;
-        }
-        FileInputStream in;
-        try {
-            in = new AtomicFile(metadataFile).openRead();
-        } catch (FileNotFoundException fnfe) {
-            Slog.i(LOG_TAG, "No ephemeral metadata file");
-            return null;
-        }
-
-        final File ephemeralDir = metadataFile.getParentFile();
-        final long timestamp = metadataFile.lastModified();
-        final String packageName = ephemeralDir.getName();
-
-        try {
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(in, StandardCharsets.UTF_8.name());
-            return new UninstalledEphemeralAppState(
-                    parseMetadata(parser, packageName), timestamp);
-        } catch (XmlPullParserException | IOException e) {
-            throw new IllegalStateException("Failed parsing ephemeral"
-                    + " metadata file: " + metadataFile, e);
-        } finally {
-            IoUtils.closeQuietly(in);
-        }
-    }
-
-    private static File computeEphemeralCookieFile(PackageParser.Package pkg, int userId) {
-        File appDir = getEphemeralApplicationDir(pkg.packageName, userId);
-        String cookieFile = EPHEMERAL_APP_COOKIE_FILE_PREFIX + computePackageCertDigest(pkg)
-                + EPHEMERAL_APP_COOKIE_FILE_SIFFIX;
-        return new File(appDir, cookieFile);
-    }
-
-    private static File peekEphemeralCookieFile(String packageName, int userId) {
-        File appDir = getEphemeralApplicationDir(packageName, userId);
-        if (!appDir.exists()) {
-            return null;
-        }
-        for (File file : appDir.listFiles()) {
-            if (!file.isDirectory()
-                    && file.getName().startsWith(EPHEMERAL_APP_COOKIE_FILE_PREFIX)
-                    && file.getName().endsWith(EPHEMERAL_APP_COOKIE_FILE_SIFFIX)) {
-                return file;
-            }
-        }
-        return null;
-    }
-
-    private static EphemeralApplicationInfo parseMetadata(XmlPullParser parser, String packageName)
-            throws IOException, XmlPullParserException {
-        final int outerDepth = parser.getDepth();
-        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
-            if (TAG_PACKAGE.equals(parser.getName())) {
-                return parsePackage(parser, packageName);
-            }
-        }
-        return null;
-    }
-
-    private static EphemeralApplicationInfo parsePackage(XmlPullParser parser, String packageName)
-            throws IOException, XmlPullParserException {
-        String label = parser.getAttributeValue(null, ATTR_LABEL);
-
-        List<String> outRequestedPermissions = new ArrayList<>();
-        List<String> outGrantedPermissions = new ArrayList<>();
-
-        final int outerDepth = parser.getDepth();
-        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
-            if (TAG_PERMS.equals(parser.getName())) {
-                parsePermissions(parser, outRequestedPermissions, outGrantedPermissions);
-            }
-        }
-
-        String[] requestedPermissions = new String[outRequestedPermissions.size()];
-        outRequestedPermissions.toArray(requestedPermissions);
-
-        String[] grantedPermissions = new String[outGrantedPermissions.size()];
-        outGrantedPermissions.toArray(grantedPermissions);
-
-        return new EphemeralApplicationInfo(packageName, label,
-                requestedPermissions, grantedPermissions);
-    }
-
-    private static void parsePermissions(XmlPullParser parser, List<String> outRequestedPermissions,
-            List<String> outGrantedPermissions) throws IOException, XmlPullParserException {
-        final int outerDepth = parser.getDepth();
-        while (XmlUtils.nextElementWithin(parser,outerDepth)) {
-            if (TAG_PERM.equals(parser.getName())) {
-                String permission = XmlUtils.readStringAttribute(parser, ATTR_NAME);
-                outRequestedPermissions.add(permission);
-                if (XmlUtils.readBooleanAttribute(parser, ATTR_GRANTED)) {
-                    outGrantedPermissions.add(permission);
-                }
-            }
-        }
-    }
-
-    private void writeUninstalledEphemeralAppMetadata(
-            EphemeralApplicationInfo ephemeralApp, int userId) {
-        File appDir = getEphemeralApplicationDir(ephemeralApp.getPackageName(), userId);
-        if (!appDir.exists() && !appDir.mkdirs()) {
-            return;
-        }
-
-        File metadataFile = new File(appDir, EPHEMERAL_APP_METADATA_FILE);
-
-        AtomicFile destination = new AtomicFile(metadataFile);
-        FileOutputStream out = null;
-        try {
-            out = destination.startWrite();
-
-            XmlSerializer serializer = Xml.newSerializer();
-            serializer.setOutput(out, StandardCharsets.UTF_8.name());
-            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
-
-            serializer.startDocument(null, true);
-
-            serializer.startTag(null, TAG_PACKAGE);
-            serializer.attribute(null, ATTR_LABEL, ephemeralApp.loadLabel(
-                    mService.mContext.getPackageManager()).toString());
-
-            serializer.startTag(null, TAG_PERMS);
-            for (String permission : ephemeralApp.getRequestedPermissions()) {
-                serializer.startTag(null, TAG_PERM);
-                serializer.attribute(null, ATTR_NAME, permission);
-                if (ArrayUtils.contains(ephemeralApp.getGrantedPermissions(), permission)) {
-                    serializer.attribute(null, ATTR_GRANTED, String.valueOf(true));
-                }
-                serializer.endTag(null, TAG_PERM);
-            }
-            serializer.endTag(null, TAG_PERMS);
-
-            serializer.endTag(null, TAG_PACKAGE);
-
-            serializer.endDocument();
-            destination.finishWrite(out);
-        } catch (Throwable t) {
-            Slog.wtf(LOG_TAG, "Failed to write ephemeral state, restoring backup", t);
-            destination.failWrite(out);
-        } finally {
-            IoUtils.closeQuietly(out);
-        }
-    }
-
-    private static String computePackageCertDigest(PackageParser.Package pkg) {
-        MessageDigest messageDigest;
-        try {
-            messageDigest = MessageDigest.getInstance("SHA256");
-        } catch (NoSuchAlgorithmException e) {
-            /* can't happen */
-            return null;
-        }
-
-        messageDigest.update(pkg.mSignatures[0].toByteArray());
-
-        final byte[] digest = messageDigest.digest();
-        final int digestLength = digest.length;
-        final int charCount = 2 * digestLength;
-
-        final char[] chars = new char[charCount];
-        for (int i = 0; i < digestLength; i++) {
-            final int byteHex = digest[i] & 0xFF;
-            chars[i * 2] = HEX_ARRAY[byteHex >>> 4];
-            chars[i * 2 + 1] = HEX_ARRAY[byteHex & 0x0F];
-        }
-        return new String(chars);
-    }
-
-    private static File getEphemeralApplicationsDir(int userId) {
-        return new File(Environment.getUserSystemDirectory(userId),
-                EPHEMERAL_APPS_FOLDER);
-    }
-
-    private static File getEphemeralApplicationDir(String packageName, int userId) {
-        return new File (getEphemeralApplicationsDir(userId), packageName);
-    }
-
-    private static void deleteDir(File dir) {
-        File[] files = dir.listFiles();
-        if (files != null) {
-            for (File file : dir.listFiles()) {
-                deleteDir(file);
-            }
-        }
-        dir.delete();
-    }
-
-    private static final class UninstalledEphemeralAppState {
-        final EphemeralApplicationInfo mEphemeralApplicationInfo;
-        final long mTimestamp;
-
-        public UninstalledEphemeralAppState(EphemeralApplicationInfo ephemeralApp,
-                long timestamp) {
-            mEphemeralApplicationInfo = ephemeralApp;
-            mTimestamp = timestamp;
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/pm/EphemeralResolver.java b/services/core/java/com/android/server/pm/EphemeralResolver.java
index 96a0d18..3c55422 100644
--- a/services/core/java/com/android/server/pm/EphemeralResolver.java
+++ b/services/core/java/com/android/server/pm/EphemeralResolver.java
@@ -235,7 +235,7 @@
                 }
                 List<EphemeralResponse> matchedResolveInfoList = ephemeralResolver.queryIntent(
                         intent, resolvedType, false /*defaultOnly*/, false /*visibleToEphemeral*/,
-                        false /*isEphemeral*/, userId);
+                        false /*isInstant*/, userId);
                 if (!matchedResolveInfoList.isEmpty()) {
                     return matchedResolveInfoList.get(0);
                 }
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
new file mode 100644
index 0000000..829c473
--- /dev/null
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -0,0 +1,959 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      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.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.Intent;
+import android.content.pm.InstantAppInfo;
+import android.content.pm.PackageParser;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings;
+import android.util.ArrayMap;
+import android.util.AtomicFile;
+import android.util.PackageUtils;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.Xml;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.XmlUtils;
+import libcore.io.IoUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
+
+/**
+ * This class is a part of the package manager service that is responsible
+ * for managing data associated with instant apps such as cached uninstalled
+ * instant apps and instant apps' cookies. In addition it is responsible for
+ * pruning installed instant apps and meta-data for uninstalled instant apps
+ * when free space is needed.
+ */
+class InstantAppRegistry {
+    private static final boolean DEBUG = false;
+
+    private static final String LOG_TAG = "InstantAppRegistry";
+
+    private static final long DEFAULT_UNINSTALLED_INSTANT_APP_CACHE_DURATION_MILLIS =
+            DEBUG ? 60 * 1000L /* one min */ : 6 * 30 * 24 * 60 * 60 * 1000L; /* six months */
+
+    private static final String INSTANT_APPS_FOLDER = "instant";
+    private static final String INSTANT_APP_ICON_FILE = "icon.png";
+    private static final String INSTANT_APP_COOKIE_FILE_PREFIX = "cookie_";
+    private static final String INSTANT_APP_COOKIE_FILE_SIFFIX = ".dat";
+    private static final String INSTANT_APP_METADATA_FILE = "metadata.xml";
+
+    private static final String TAG_PACKAGE = "package";
+    private static final String TAG_PERMISSIONS = "permissions";
+    private static final String TAG_PERMISSION = "permission";
+
+    private static final String ATTR_LABEL = "label";
+    private static final String ATTR_NAME = "name";
+    private static final String ATTR_GRANTED = "granted";
+
+    private final PackageManagerService mService;
+    private final CookiePersistence mCookiePersistence;
+
+    /** State for uninstalled instant apps */
+    @GuardedBy("mService.mPackages")
+    private SparseArray<List<UninstalledInstantAppState>> mUninstalledInstantApps;
+
+    /**
+     * Automatic grants for access to instant app metadata.
+     * The key is the target application UID.
+     * The value is a set of instant app UIDs.
+     * UserID -> TargetAppId -> InstantAppId
+     */
+    @GuardedBy("mService.mPackages")
+    private SparseArray<SparseArray<SparseBooleanArray>> mInstantGrants;
+
+    /** The set of all installed instant apps. UserID -> AppID */
+    @GuardedBy("mService.mPackages")
+    private SparseArray<SparseBooleanArray> mInstalledInstantAppUids;
+
+    public InstantAppRegistry(PackageManagerService service) {
+        mService = service;
+        mCookiePersistence = new CookiePersistence(BackgroundThread.getHandler().getLooper());
+    }
+
+    public byte[] getInstantAppCookieLPw(@NonNull String packageName,
+                                         @UserIdInt int userId) {
+        byte[] pendingCookie = mCookiePersistence.getPendingPersistCookie(userId, packageName);
+        if (pendingCookie != null) {
+            return pendingCookie;
+        }
+        File cookieFile = peekInstantCookieFile(packageName, userId);
+        if (cookieFile != null && cookieFile.exists()) {
+            try {
+                return IoUtils.readFileAsByteArray(cookieFile.toString());
+            } catch (IOException e) {
+                Slog.w(LOG_TAG, "Error reading cookie file: " + cookieFile);
+            }
+        }
+        return null;
+    }
+
+    public boolean setInstantAppCookieLPw(@NonNull String packageName,
+                                          @Nullable byte[] cookie, @UserIdInt int userId) {
+        if (cookie != null && cookie.length > 0) {
+            final int maxCookieSize = mService.mContext.getPackageManager()
+                    .getInstantAppCookieMaxSize();
+            if (cookie.length > maxCookieSize) {
+                Slog.e(LOG_TAG, "Instant app cookie for package " + packageName + " size "
+                        + cookie.length + " bytes while max size is " + maxCookieSize);
+                return false;
+            }
+        }
+
+        mCookiePersistence.schedulePersist(userId, packageName, cookie);
+        return true;
+    }
+
+    private void persistInstantApplicationCookie(@Nullable byte[] cookie,
+            @NonNull String packageName, @UserIdInt int userId) {
+        synchronized (mService.mPackages) {
+            PackageParser.Package pkg = mService.mPackages.get(packageName);
+            if (pkg == null) {
+                return;
+            }
+
+            File appDir = getInstantApplicationDir(packageName, userId);
+            if (!appDir.exists() && !appDir.mkdirs()) {
+                Slog.e(LOG_TAG, "Cannot create instant app cookie directory");
+                return;
+            }
+
+            File cookieFile = computeInstantCookieFile(pkg, userId);
+            if (cookieFile.exists() && !cookieFile.delete()) {
+                Slog.e(LOG_TAG, "Cannot delete instant app cookie file");
+            }
+
+            // No cookie or an empty one means delete - done
+            if (cookie == null || cookie.length <= 0) {
+                return;
+            }
+
+            try (FileOutputStream fos = new FileOutputStream(cookieFile)) {
+                fos.write(cookie, 0, cookie.length);
+            } catch (IOException e) {
+                Slog.e(LOG_TAG, "Error writing instant app cookie file: " + cookieFile, e);
+            }
+        }
+    }
+
+    public Bitmap getInstantAppIconLPw(@NonNull String packageName,
+                                       @UserIdInt int userId) {
+        File iconFile = new File(getInstantApplicationDir(packageName, userId),
+                INSTANT_APP_ICON_FILE);
+        if (iconFile.exists()) {
+            return BitmapFactory.decodeFile(iconFile.toString());
+        }
+        return null;
+    }
+
+    public @Nullable List<InstantAppInfo> getInstantAppsLPr(@UserIdInt int userId) {
+        List<InstantAppInfo> installedApps = getInstalledInstantApplicationsLPr(userId);
+        List<InstantAppInfo> uninstalledApps = getUninstalledInstantApplicationsLPr(userId);
+        if (installedApps != null) {
+            if (uninstalledApps != null) {
+                installedApps.addAll(uninstalledApps);
+            }
+            return installedApps;
+        }
+        return uninstalledApps;
+    }
+
+    public void onPackageInstalledLPw(@NonNull PackageParser.Package pkg, @NonNull int[] userIds) {
+        PackageSetting ps = (PackageSetting) pkg.mExtras;
+        if (ps == null) {
+            return;
+        }
+
+        for (int userId : userIds) {
+            // Ignore not installed apps
+            if (mService.mPackages.get(pkg.packageName) == null || !ps.getInstalled(userId)) {
+                continue;
+            }
+
+            // Propagate permissions before removing any state
+            propagateInstantAppPermissionsIfNeeded(pkg.packageName, userId);
+
+            // Track instant apps
+            if (pkg.applicationInfo.isInstantApp()) {
+                addInstantAppLPw(userId, ps.appId);
+            }
+
+            // Remove the in-memory state
+            removeUninstalledInstantAppStateLPw((UninstalledInstantAppState state) ->
+                            state.mInstantAppInfo.getPackageName().equals(pkg.packageName),
+                    userId);
+
+            // Remove the on-disk state except the cookie
+            File instantAppDir = getInstantApplicationDir(pkg.packageName, userId);
+            new File(instantAppDir, INSTANT_APP_METADATA_FILE).delete();
+            new File(instantAppDir, INSTANT_APP_ICON_FILE).delete();
+
+            // If app signature changed - wipe the cookie
+            File currentCookieFile = peekInstantCookieFile(pkg.packageName, userId);
+            if (currentCookieFile == null) {
+                continue;
+            }
+            File expectedCookeFile = computeInstantCookieFile(pkg, userId);
+            if (!currentCookieFile.equals(expectedCookeFile)) {
+                Slog.i(LOG_TAG, "Signature for package " + pkg.packageName
+                        + " changed - dropping cookie");
+                currentCookieFile.delete();
+            }
+        }
+    }
+
+    public void onPackageUninstalledLPw(@NonNull PackageParser.Package pkg,
+            @NonNull int[] userIds) {
+        PackageSetting ps = (PackageSetting) pkg.mExtras;
+        if (ps == null) {
+            return;
+        }
+
+        for (int userId : userIds) {
+            if (mService.mPackages.get(pkg.packageName) != null && ps.getInstalled(userId)) {
+                continue;
+            }
+
+            if (pkg.applicationInfo.isInstantApp()) {
+                // Add a record for an uninstalled instant app
+                addUninstalledInstantAppLPw(pkg, userId);
+                removeInstantAppLPw(userId, ps.appId);
+            } else {
+                // Deleting an app prunes all instant state such as cookie
+                deleteDir(getInstantApplicationDir(pkg.packageName, userId));
+                removeAppLPw(userId, ps.appId);
+            }
+        }
+    }
+
+    public void onUserRemovedLPw(int userId) {
+        if (mUninstalledInstantApps != null) {
+            mUninstalledInstantApps.remove(userId);
+            if (mUninstalledInstantApps.size() <= 0) {
+                mUninstalledInstantApps = null;
+            }
+        }
+        if (mInstalledInstantAppUids != null) {
+            mInstalledInstantAppUids.remove(userId);
+            if (mInstalledInstantAppUids.size() <= 0) {
+                mInstalledInstantAppUids = null;
+            }
+        }
+        if (mInstantGrants != null) {
+            mInstantGrants.remove(userId);
+            if (mInstantGrants.size() <= 0) {
+                mInstantGrants = null;
+            }
+        }
+        deleteDir(getInstantApplicationsDir(userId));
+    }
+
+    public boolean isInstantAccessGranted(@UserIdInt int userId, int targetAppId,
+            int instantAppId) {
+        if (mInstantGrants == null) {
+            return false;
+        }
+        final SparseArray<SparseBooleanArray> targetAppList = mInstantGrants.get(userId);
+        if (targetAppList == null) {
+            return false;
+        }
+        final SparseBooleanArray instantGrantList = targetAppList.get(targetAppId);
+        if (instantGrantList == null) {
+            return false;
+        }
+        return instantGrantList.get(instantAppId);
+    }
+
+    public void grantInstantAccessLPw(@UserIdInt int userId, @Nullable Intent intent,
+            int targetAppId, int instantAppId) {
+        if (mInstalledInstantAppUids == null) {
+            return;     // no instant apps installed; no need to grant
+        }
+        SparseBooleanArray instantAppList = mInstalledInstantAppUids.get(userId);
+        if (instantAppList == null || !instantAppList.get(instantAppId)) {
+            return;     // instant app id isn't installed; no need to grant
+        }
+        if (instantAppList.get(targetAppId)) {
+            return;     // target app id is an instant app; no need to grant
+        }
+        if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) {
+            final Set<String> categories = intent.getCategories();
+            if (categories != null && categories.contains(Intent.CATEGORY_BROWSABLE)) {
+                return;  // launched via VIEW/BROWSABLE intent; no need to grant
+            }
+        }
+        if (mInstantGrants == null) {
+            mInstantGrants = new SparseArray<>();
+        }
+        SparseArray<SparseBooleanArray> targetAppList = mInstantGrants.get(userId);
+        if (targetAppList == null) {
+            targetAppList = new SparseArray<>();
+            mInstantGrants.put(userId, targetAppList);
+        }
+        SparseBooleanArray instantGrantList = targetAppList.get(targetAppId);
+        if (instantGrantList == null) {
+            instantGrantList = new SparseBooleanArray();
+            targetAppList.put(targetAppId, instantGrantList);
+        }
+        instantGrantList.put(instantAppId, true /*granted*/);
+    }
+
+    public void addInstantAppLPw(@UserIdInt int userId, int instantAppId) {
+        if (mInstalledInstantAppUids == null) {
+            mInstalledInstantAppUids = new SparseArray<>();
+        }
+        SparseBooleanArray instantAppList = mInstalledInstantAppUids.get(userId);
+        if (instantAppList == null) {
+            instantAppList = new SparseBooleanArray();
+            mInstalledInstantAppUids.put(userId, instantAppList);
+        }
+        instantAppList.put(instantAppId, true /*installed*/);
+    }
+
+    private void removeInstantAppLPw(@UserIdInt int userId, int instantAppId) {
+        // remove from the installed list
+        if (mInstalledInstantAppUids == null) {
+            return; // no instant apps on the system
+        }
+        final SparseBooleanArray instantAppList = mInstalledInstantAppUids.get(userId);
+        if (instantAppList == null) {
+            return;
+        }
+
+        instantAppList.delete(instantAppId);
+
+        // remove any grants
+        if (mInstantGrants == null) {
+            return; // no grants on the system
+        }
+        final SparseArray<SparseBooleanArray> targetAppList = mInstantGrants.get(userId);
+        if (targetAppList == null) {
+            return; // no grants for this user
+        }
+        for (int i = targetAppList.size() - 1; i >= 0; --i) {
+            targetAppList.valueAt(i).delete(instantAppId);
+        }
+    }
+
+    private void removeAppLPw(@UserIdInt int userId, int targetAppId) {
+        // remove from the installed list
+        if (mInstantGrants == null) {
+            return; // no grants on the system
+        }
+        final SparseArray<SparseBooleanArray> targetAppList = mInstantGrants.get(userId);
+        if (targetAppList == null) {
+            return; // no grants for this user
+        }
+        targetAppList.delete(targetAppId);
+    }
+
+    private void addUninstalledInstantAppLPw(@NonNull PackageParser.Package pkg,
+            @UserIdInt int userId) {
+        InstantAppInfo uninstalledApp = createInstantAppInfoForPackage(
+                pkg, userId, false);
+        if (uninstalledApp == null) {
+            return;
+        }
+        if (mUninstalledInstantApps == null) {
+            mUninstalledInstantApps = new SparseArray<>();
+        }
+        List<UninstalledInstantAppState> uninstalledAppStates =
+                mUninstalledInstantApps.get(userId);
+        if (uninstalledAppStates == null) {
+            uninstalledAppStates = new ArrayList<>();
+            mUninstalledInstantApps.put(userId, uninstalledAppStates);
+        }
+        UninstalledInstantAppState uninstalledAppState = new UninstalledInstantAppState(
+                uninstalledApp, System.currentTimeMillis());
+        uninstalledAppStates.add(uninstalledAppState);
+
+        writeUninstalledInstantAppMetadata(uninstalledApp, userId);
+        writeInstantApplicationIconLPw(pkg, userId);
+    }
+
+    private void writeInstantApplicationIconLPw(@NonNull PackageParser.Package pkg,
+            @UserIdInt int userId) {
+        File appDir = getInstantApplicationDir(pkg.packageName, userId);
+        if (!appDir.exists()) {
+            return;
+        }
+
+        Drawable icon = pkg.applicationInfo.loadIcon(mService.mContext.getPackageManager());
+
+        final Bitmap bitmap;
+        if (icon instanceof BitmapDrawable) {
+            bitmap = ((BitmapDrawable) icon).getBitmap();
+        } else  {
+            bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(),
+                    icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+            Canvas canvas = new Canvas(bitmap);
+            icon.draw(canvas);
+        }
+
+        File iconFile = new File(getInstantApplicationDir(pkg.packageName, userId),
+                INSTANT_APP_ICON_FILE);
+
+        try (FileOutputStream out = new FileOutputStream(iconFile)) {
+            bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
+        } catch (Exception e) {
+            Slog.e(LOG_TAG, "Error writing instant app icon", e);
+        }
+    }
+
+    public void deleteInstantApplicationMetadataLPw(@NonNull String packageName,
+            @UserIdInt int userId) {
+        removeUninstalledInstantAppStateLPw((UninstalledInstantAppState state) ->
+                state.mInstantAppInfo.getPackageName().equals(packageName),
+                userId);
+
+        File instantAppDir = getInstantApplicationDir(packageName, userId);
+        new File(instantAppDir, INSTANT_APP_METADATA_FILE).delete();
+        new File(instantAppDir, INSTANT_APP_ICON_FILE).delete();
+        File cookie = peekInstantCookieFile(packageName, userId);
+        if (cookie != null) {
+            cookie.delete();
+        }
+    }
+
+    private void removeUninstalledInstantAppStateLPw(
+            @NonNull Predicate<UninstalledInstantAppState> criteria, @UserIdInt int userId) {
+        if (mUninstalledInstantApps == null) {
+            return;
+        }
+        List<UninstalledInstantAppState> uninstalledAppStates =
+                mUninstalledInstantApps.get(userId);
+        if (uninstalledAppStates == null) {
+            return;
+        }
+        final int appCount = uninstalledAppStates.size();
+        for (int i = 0; i < appCount; i++) {
+            UninstalledInstantAppState uninstalledAppState = uninstalledAppStates.get(i);
+            if (!criteria.test(uninstalledAppState)) {
+                continue;
+            }
+            uninstalledAppStates.remove(i);
+            if (uninstalledAppStates.isEmpty()) {
+                mUninstalledInstantApps.remove(userId);
+                if (mUninstalledInstantApps.size() <= 0) {
+                    mUninstalledInstantApps = null;
+                }
+                return;
+            }
+        }
+    }
+
+    public void pruneInstantAppsLPw() {
+        // For now we prune only state for uninstalled instant apps
+        final long maxCacheDurationMillis = Settings.Global.getLong(
+                mService.mContext.getContentResolver(),
+                Settings.Global.UNINSTALLED_INSTANT_APP_CACHE_DURATION_MILLIS,
+                DEFAULT_UNINSTALLED_INSTANT_APP_CACHE_DURATION_MILLIS);
+
+        for (int userId : UserManagerService.getInstance().getUserIds()) {
+            // Prune in-memory state
+            removeUninstalledInstantAppStateLPw((UninstalledInstantAppState state) -> {
+                final long elapsedCachingMillis = System.currentTimeMillis() - state.mTimestamp;
+                return (elapsedCachingMillis > maxCacheDurationMillis);
+            }, userId);
+
+            // Prune on-disk state
+            File instantAppsDir = getInstantApplicationsDir(userId);
+            if (!instantAppsDir.exists()) {
+                continue;
+            }
+            File[] files = instantAppsDir.listFiles();
+            if (files == null) {
+                continue;
+            }
+            for (File instantDir : files) {
+                if (!instantDir.isDirectory()) {
+                    continue;
+                }
+
+                File metadataFile = new File(instantDir, INSTANT_APP_METADATA_FILE);
+                if (!metadataFile.exists()) {
+                    continue;
+                }
+
+                final long elapsedCachingMillis = System.currentTimeMillis()
+                        - metadataFile.lastModified();
+                if (elapsedCachingMillis > maxCacheDurationMillis) {
+                    deleteDir(instantDir);
+                }
+            }
+        }
+    }
+
+    private @Nullable List<InstantAppInfo> getInstalledInstantApplicationsLPr(
+            @UserIdInt int userId) {
+        List<InstantAppInfo> result = null;
+
+        final int packageCount = mService.mPackages.size();
+        for (int i = 0; i < packageCount; i++) {
+            PackageParser.Package pkg = mService.mPackages.valueAt(i);
+            if (!pkg.applicationInfo.isInstantApp()) {
+                continue;
+            }
+            InstantAppInfo info = createInstantAppInfoForPackage(
+                    pkg, userId, true);
+            if (info == null) {
+                continue;
+            }
+            if (result == null) {
+                result = new ArrayList<>();
+            }
+            result.add(info);
+        }
+
+        return result;
+    }
+
+    private @NonNull
+    InstantAppInfo createInstantAppInfoForPackage(
+            @NonNull PackageParser.Package pkg, @UserIdInt int userId,
+            boolean addApplicationInfo) {
+        PackageSetting ps = (PackageSetting) pkg.mExtras;
+        if (ps == null) {
+            return null;
+        }
+        if (!ps.getInstalled(userId)) {
+            return null;
+        }
+
+        String[] requestedPermissions = new String[pkg.requestedPermissions.size()];
+        pkg.requestedPermissions.toArray(requestedPermissions);
+
+        Set<String> permissions = ps.getPermissionsState().getPermissions(userId);
+        String[] grantedPermissions = new String[permissions.size()];
+        permissions.toArray(grantedPermissions);
+
+        if (addApplicationInfo) {
+            return new InstantAppInfo(pkg.applicationInfo,
+                    requestedPermissions, grantedPermissions);
+        } else {
+            return new InstantAppInfo(pkg.applicationInfo.packageName,
+                    pkg.applicationInfo.loadLabel(mService.mContext.getPackageManager()),
+                    requestedPermissions, grantedPermissions);
+        }
+    }
+
+    private @Nullable List<InstantAppInfo> getUninstalledInstantApplicationsLPr(
+            @UserIdInt int userId) {
+        List<UninstalledInstantAppState> uninstalledAppStates =
+                getUninstalledInstantAppStatesLPr(userId);
+        if (uninstalledAppStates == null || uninstalledAppStates.isEmpty()) {
+            return null;
+        }
+
+        List<InstantAppInfo> uninstalledApps = null;
+        final int stateCount = uninstalledAppStates.size();
+        for (int i = 0; i < stateCount; i++) {
+            UninstalledInstantAppState uninstalledAppState = uninstalledAppStates.get(i);
+            if (uninstalledApps == null) {
+                uninstalledApps = new ArrayList<>();
+            }
+            uninstalledApps.add(uninstalledAppState.mInstantAppInfo);
+        }
+        return uninstalledApps;
+    }
+
+    private void propagateInstantAppPermissionsIfNeeded(@NonNull String packageName,
+            @UserIdInt int userId) {
+        InstantAppInfo appInfo = peekOrParseUninstalledInstantAppInfo(
+                packageName, userId);
+        if (appInfo == null) {
+            return;
+        }
+        if (ArrayUtils.isEmpty(appInfo.getGrantedPermissions())) {
+            return;
+        }
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            for (String grantedPermission : appInfo.getGrantedPermissions()) {
+                BasePermission bp = mService.mSettings.mPermissions.get(grantedPermission);
+                if (bp != null && (bp.isRuntime() || bp.isDevelopment()) && bp.isInstant()) {
+                    mService.grantRuntimePermission(packageName, grantedPermission, userId);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    private @NonNull
+    InstantAppInfo peekOrParseUninstalledInstantAppInfo(
+            @NonNull String packageName, @UserIdInt int userId) {
+        if (mUninstalledInstantApps != null) {
+            List<UninstalledInstantAppState> uninstalledAppStates =
+                    mUninstalledInstantApps.get(userId);
+            if (uninstalledAppStates != null) {
+                final int appCount = uninstalledAppStates.size();
+                for (int i = 0; i < appCount; i++) {
+                    UninstalledInstantAppState uninstalledAppState = uninstalledAppStates.get(i);
+                    if (uninstalledAppState.mInstantAppInfo
+                            .getPackageName().equals(packageName)) {
+                        return uninstalledAppState.mInstantAppInfo;
+                    }
+                }
+            }
+        }
+
+        File metadataFile = new File(getInstantApplicationDir(packageName, userId),
+                INSTANT_APP_METADATA_FILE);
+        UninstalledInstantAppState uninstalledAppState = parseMetadataFile(metadataFile);
+        if (uninstalledAppState == null) {
+            return null;
+        }
+
+        return uninstalledAppState.mInstantAppInfo;
+    }
+
+    private @Nullable List<UninstalledInstantAppState> getUninstalledInstantAppStatesLPr(
+            @UserIdInt int userId) {
+        List<UninstalledInstantAppState> uninstalledAppStates = null;
+        if (mUninstalledInstantApps != null) {
+            uninstalledAppStates = mUninstalledInstantApps.get(userId);
+            if (uninstalledAppStates != null) {
+                return uninstalledAppStates;
+            }
+        }
+
+        File instantAppsDir = getInstantApplicationsDir(userId);
+        if (instantAppsDir.exists()) {
+            File[] files = instantAppsDir.listFiles();
+            if (files != null) {
+                for (File instantDir : files) {
+                    if (!instantDir.isDirectory()) {
+                        continue;
+                    }
+                    File metadataFile = new File(instantDir,
+                            INSTANT_APP_METADATA_FILE);
+                    UninstalledInstantAppState uninstalledAppState =
+                            parseMetadataFile(metadataFile);
+                    if (uninstalledAppState == null) {
+                        continue;
+                    }
+                    if (uninstalledAppStates == null) {
+                        uninstalledAppStates = new ArrayList<>();
+                    }
+                    uninstalledAppStates.add(uninstalledAppState);
+                }
+            }
+        }
+
+        if (uninstalledAppStates != null) {
+            if (mUninstalledInstantApps == null) {
+                mUninstalledInstantApps = new SparseArray<>();
+            }
+            mUninstalledInstantApps.put(userId, uninstalledAppStates);
+        }
+
+        return uninstalledAppStates;
+    }
+
+    private static @Nullable UninstalledInstantAppState parseMetadataFile(
+            @NonNull File metadataFile) {
+        if (!metadataFile.exists()) {
+            return null;
+        }
+        FileInputStream in;
+        try {
+            in = new AtomicFile(metadataFile).openRead();
+        } catch (FileNotFoundException fnfe) {
+            Slog.i(LOG_TAG, "No instant metadata file");
+            return null;
+        }
+
+        final File instantDir = metadataFile.getParentFile();
+        final long timestamp = metadataFile.lastModified();
+        final String packageName = instantDir.getName();
+
+        try {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(in, StandardCharsets.UTF_8.name());
+            return new UninstalledInstantAppState(
+                    parseMetadata(parser, packageName), timestamp);
+        } catch (XmlPullParserException | IOException e) {
+            throw new IllegalStateException("Failed parsing instant"
+                    + " metadata file: " + metadataFile, e);
+        } finally {
+            IoUtils.closeQuietly(in);
+        }
+    }
+
+    private static @NonNull File computeInstantCookieFile(@NonNull PackageParser.Package pkg,
+            @UserIdInt int userId) {
+        File appDir = getInstantApplicationDir(pkg.packageName, userId);
+        String cookieFile = INSTANT_APP_COOKIE_FILE_PREFIX + PackageUtils.computeSha256Digest(
+                pkg.mSignatures[0].toByteArray()) + INSTANT_APP_COOKIE_FILE_SIFFIX;
+        return new File(appDir, cookieFile);
+    }
+
+    private static @Nullable File peekInstantCookieFile(@NonNull String packageName,
+            @UserIdInt int userId) {
+        File appDir = getInstantApplicationDir(packageName, userId);
+        if (!appDir.exists()) {
+            return null;
+        }
+        File[] files = appDir.listFiles();
+        if (files == null) {
+            return null;
+        }
+        for (File file : files) {
+            if (!file.isDirectory()
+                    && file.getName().startsWith(INSTANT_APP_COOKIE_FILE_PREFIX)
+                    && file.getName().endsWith(INSTANT_APP_COOKIE_FILE_SIFFIX)) {
+                return file;
+            }
+        }
+        return null;
+    }
+
+    private static @Nullable
+    InstantAppInfo parseMetadata(@NonNull XmlPullParser parser,
+                                 @NonNull String packageName)
+            throws IOException, XmlPullParserException {
+        final int outerDepth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+            if (TAG_PACKAGE.equals(parser.getName())) {
+                return parsePackage(parser, packageName);
+            }
+        }
+        return null;
+    }
+
+    private static InstantAppInfo parsePackage(@NonNull XmlPullParser parser,
+                                               @NonNull String packageName)
+            throws IOException, XmlPullParserException {
+        String label = parser.getAttributeValue(null, ATTR_LABEL);
+
+        List<String> outRequestedPermissions = new ArrayList<>();
+        List<String> outGrantedPermissions = new ArrayList<>();
+
+        final int outerDepth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+            if (TAG_PERMISSIONS.equals(parser.getName())) {
+                parsePermissions(parser, outRequestedPermissions, outGrantedPermissions);
+            }
+        }
+
+        String[] requestedPermissions = new String[outRequestedPermissions.size()];
+        outRequestedPermissions.toArray(requestedPermissions);
+
+        String[] grantedPermissions = new String[outGrantedPermissions.size()];
+        outGrantedPermissions.toArray(grantedPermissions);
+
+        return new InstantAppInfo(packageName, label,
+                requestedPermissions, grantedPermissions);
+    }
+
+    private static void parsePermissions(@NonNull XmlPullParser parser,
+            @NonNull List<String> outRequestedPermissions,
+            @NonNull List<String> outGrantedPermissions)
+            throws IOException, XmlPullParserException {
+        final int outerDepth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser,outerDepth)) {
+            if (TAG_PERMISSION.equals(parser.getName())) {
+                String permission = XmlUtils.readStringAttribute(parser, ATTR_NAME);
+                outRequestedPermissions.add(permission);
+                if (XmlUtils.readBooleanAttribute(parser, ATTR_GRANTED)) {
+                    outGrantedPermissions.add(permission);
+                }
+            }
+        }
+    }
+
+    private void writeUninstalledInstantAppMetadata(
+            @NonNull InstantAppInfo instantApp, @UserIdInt int userId) {
+        File appDir = getInstantApplicationDir(instantApp.getPackageName(), userId);
+        if (!appDir.exists() && !appDir.mkdirs()) {
+            return;
+        }
+
+        File metadataFile = new File(appDir, INSTANT_APP_METADATA_FILE);
+
+        AtomicFile destination = new AtomicFile(metadataFile);
+        FileOutputStream out = null;
+        try {
+            out = destination.startWrite();
+
+            XmlSerializer serializer = Xml.newSerializer();
+            serializer.setOutput(out, StandardCharsets.UTF_8.name());
+            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+            serializer.startDocument(null, true);
+
+            serializer.startTag(null, TAG_PACKAGE);
+            serializer.attribute(null, ATTR_LABEL, instantApp.loadLabel(
+                    mService.mContext.getPackageManager()).toString());
+
+            serializer.startTag(null, TAG_PERMISSIONS);
+            for (String permission : instantApp.getRequestedPermissions()) {
+                serializer.startTag(null, TAG_PERMISSION);
+                serializer.attribute(null, ATTR_NAME, permission);
+                if (ArrayUtils.contains(instantApp.getGrantedPermissions(), permission)) {
+                    serializer.attribute(null, ATTR_GRANTED, String.valueOf(true));
+                }
+                serializer.endTag(null, TAG_PERMISSION);
+            }
+            serializer.endTag(null, TAG_PERMISSIONS);
+
+            serializer.endTag(null, TAG_PACKAGE);
+
+            serializer.endDocument();
+            destination.finishWrite(out);
+        } catch (Throwable t) {
+            Slog.wtf(LOG_TAG, "Failed to write instant state, restoring backup", t);
+            destination.failWrite(out);
+        } finally {
+            IoUtils.closeQuietly(out);
+        }
+    }
+
+    private static @NonNull File getInstantApplicationsDir(int userId) {
+        return new File(Environment.getUserSystemDirectory(userId),
+                INSTANT_APPS_FOLDER);
+    }
+
+    private static @NonNull File getInstantApplicationDir(String packageName, int userId) {
+        return new File (getInstantApplicationsDir(userId), packageName);
+    }
+
+    private static void deleteDir(@NonNull File dir) {
+        File[] files = dir.listFiles();
+        if (files != null) {
+            for (File file : files) {
+                deleteDir(file);
+            }
+        }
+        dir.delete();
+    }
+
+    private static final class UninstalledInstantAppState {
+        final InstantAppInfo mInstantAppInfo;
+        final long mTimestamp;
+
+        public UninstalledInstantAppState(InstantAppInfo instantApp,
+                long timestamp) {
+            mInstantAppInfo = instantApp;
+            mTimestamp = timestamp;
+        }
+    }
+
+    private final class CookiePersistence extends Handler {
+        private static final long PERSIST_COOKIE_DELAY_MILLIS = 1000L; /* one second */
+
+        // In case you wonder why we stash the cookies aside, we use
+        // the user id for the message id and the package for the payload.
+        // Handler allows removing messages by id and tag where the
+        // tag is is compared using ==. So to allow cancelling the
+        // pending persistence for an app under a given user we use
+        // the fact that package names are interned in the system
+        // process so the == comparison would match and we end up
+        // with a way to cancel persisting the cookie for a user
+        // and package.
+        private final SparseArray<ArrayMap<String, byte[]>> mPendingPersistCookies =
+                new SparseArray<>();
+
+        public CookiePersistence(Looper looper) {
+            super(looper);
+        }
+
+        public void schedulePersist(@UserIdInt int userId,
+                @NonNull String packageName, @NonNull byte[] cookie) {
+            cancelPendingPersist(userId, packageName);
+            addPendingPersistCookie(userId, packageName, cookie);
+            sendMessageDelayed(obtainMessage(userId, packageName),
+                    PERSIST_COOKIE_DELAY_MILLIS);
+        }
+
+        public @Nullable byte[] getPendingPersistCookie(@UserIdInt int userId,
+                @NonNull String packageName) {
+            ArrayMap<String, byte[]> pendingWorkForUser = mPendingPersistCookies.get(userId);
+            if (pendingWorkForUser != null) {
+                return pendingWorkForUser.remove(packageName);
+            }
+            return null;
+        }
+
+        private void cancelPendingPersist(@UserIdInt int userId,
+                @NonNull String packageName) {
+            removePendingPersistCookie(userId, packageName);
+            removeMessages(userId, packageName);
+        }
+
+        private void addPendingPersistCookie(@UserIdInt int userId,
+                @NonNull String packageName, @NonNull byte[] cookie) {
+            ArrayMap<String, byte[]> pendingWorkForUser = mPendingPersistCookies.get(userId);
+            if (pendingWorkForUser == null) {
+                pendingWorkForUser = new ArrayMap<>();
+                mPendingPersistCookies.put(userId, pendingWorkForUser);
+            }
+            pendingWorkForUser.put(packageName, cookie);
+        }
+
+        private byte[] removePendingPersistCookie(@UserIdInt int userId,
+                @NonNull String packageName) {
+            ArrayMap<String, byte[]> pendingWorkForUser = mPendingPersistCookies.get(userId);
+            byte[] cookie = null;
+            if (pendingWorkForUser != null) {
+                cookie = pendingWorkForUser.remove(packageName);
+                if (pendingWorkForUser.isEmpty()) {
+                    mPendingPersistCookies.remove(userId);
+                }
+            }
+            return cookie;
+        }
+
+        @Override
+        public void handleMessage(Message message) {
+            int userId = message.what;
+            String packageName = (String) message.obj;
+            byte[] cookie = removePendingPersistCookie(userId, packageName);
+            persistInstantApplicationCookie(cookie, packageName, userId);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index da6a67e..efd3132 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -225,8 +225,8 @@
         synchronized (mSessions) {
             readSessionsLocked();
 
-            reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL, false /*isEphemeral*/);
-            reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL, true /*isEphemeral*/);
+            reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL, false /*isInstant*/);
+            reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL, true /*isInstant*/);
 
             final ArraySet<File> unclaimedIcons = newArraySet(
                     mSessionsDir.listFiles());
@@ -271,7 +271,7 @@
 
     public void onPrivateVolumeMounted(String volumeUuid) {
         synchronized (mSessions) {
-            reconcileStagesLocked(volumeUuid, false /*isEphemeral*/);
+            reconcileStagesLocked(volumeUuid, false /*isInstant*/);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4a426bd..add2c66 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -127,7 +127,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.AppsQueryHelper;
 import android.content.pm.ComponentInfo;
-import android.content.pm.EphemeralApplicationInfo;
+import android.content.pm.InstantAppInfo;
 import android.content.pm.EphemeralRequest;
 import android.content.pm.EphemeralResolveInfo;
 import android.content.pm.EphemeralResponse;
@@ -255,6 +255,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.server.AttributeCache;
+import com.android.server.BackgroundDexOptJobService;
 import com.android.server.EventLogTags;
 import com.android.server.FgThread;
 import com.android.server.IntentResolver;
@@ -375,7 +376,7 @@
     // Debug output for dexopting. This is shared between PackageManagerService, OtaDexoptService
     // and PackageDexOptimizer. All these classes have their own flag to allow switching a single
     // user, but by default initialize to this.
-    static final boolean DEBUG_DEXOPT = false;
+    public static final boolean DEBUG_DEXOPT = false;
 
     private static final boolean DEBUG_ABI_SELECTION = false;
     private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE;
@@ -386,7 +387,7 @@
     static final boolean CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE = false;
 
     private static final boolean DISABLE_EPHEMERAL_APPS = false;
-    private static final boolean HIDE_EPHEMERAL_APIS = true;
+    private static final boolean HIDE_EPHEMERAL_APIS = false;
 
     private static final boolean ENABLE_QUOTA =
             SystemProperties.getBoolean("persist.fw.quota", false);
@@ -716,7 +717,7 @@
     // If mac_permissions.xml was found for seinfo labeling.
     boolean mFoundPolicyFile;
 
-    private final EphemeralApplicationRegistry mEphemeralApplicationRegistry;
+    private final InstantAppRegistry mInstantAppRegistry;
 
     public static final class SharedLibraryEntry {
         public final String path;
@@ -1751,7 +1752,7 @@
             }
 
             synchronized (mPackages) {
-                mEphemeralApplicationRegistry.onPackageInstalledLPw(res.pkg);
+                mInstantAppRegistry.onPackageInstalledLPw(res.pkg, res.newUsers);
             }
 
             final String packageName = res.pkg.applicationInfo.packageName;
@@ -2255,7 +2256,7 @@
             Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
 
             mDefaultPermissionPolicy = new DefaultPermissionGrantPolicy(this);
-            mEphemeralApplicationRegistry = new EphemeralApplicationRegistry(this);
+            mInstantAppRegistry = new InstantAppRegistry(this);
 
             File dataDir = Environment.getDataDirectory();
             mAppInstallDir = new File(dataDir, "app");
@@ -3303,10 +3304,13 @@
         //   * An installed app can see metadata for 1) other installed apps
         //     and 2) ephemeral apps that have explicitly interacted with it
         //   * Ephemeral apps can only see their own metadata
+        //   * Holding a signature permission allows seeing instant apps
         final int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
         if (callingAppId != Process.SYSTEM_UID
                 && callingAppId != Process.SHELL_UID
-                && callingAppId != Process.ROOT_UID) {
+                && callingAppId != Process.ROOT_UID
+                && checkUidPermission(Manifest.permission.ACCESS_INSTANT_APPS,
+                        Binder.getCallingUid()) != PackageManager.PERMISSION_GRANTED) {
             final String ephemeralPackageName = getEphemeralPackageName(Binder.getCallingUid());
             if (ephemeralPackageName != null) {
                 // ephemeral apps can only get information on themselves
@@ -3314,9 +3318,9 @@
                     return null;
                 }
             } else {
-                if (p.applicationInfo.isEphemeralApp()) {
+                if (p.applicationInfo.isInstantApp()) {
                     // only get access to the ephemeral app if we've been granted access
-                    if (!mEphemeralApplicationRegistry.isEphemeralAccessGranted(
+                    if (!mInstantAppRegistry.isInstantAccessGranted(
                             userId, callingAppId, ps.appId)) {
                         return null;
                     }
@@ -4617,7 +4621,7 @@
                 return;
             }
 
-            if (pkg.applicationInfo.isEphemeralApp() && !bp.isEphemeral()) {
+            if (pkg.applicationInfo.isInstantApp() && !bp.isInstant()) {
                 throw new SecurityException("Cannot grant non-ephemeral permission"
                         + name + " for package " + packageName);
             }
@@ -5912,7 +5916,7 @@
         CrossProfileIntentResolver resolver = mSettings.mCrossProfileIntentResolvers.get(userId);
         if (resolver != null) {
             return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/,
-                    false /*visibleToEphemeral*/, false /*isEphemeral*/, userId);
+                    false /*visibleToEphemeral*/, false /*isInstant*/, userId);
         }
         return null;
     }
@@ -5940,7 +5944,7 @@
             final Object obj = mSettings.getUserIdLPr(appId);
             if (obj instanceof PackageSetting) {
                 final PackageSetting ps = (PackageSetting) obj;
-                return ps.pkg.applicationInfo.isEphemeralApp() ? ps.pkg.packageName : null;
+                return ps.pkg.applicationInfo.isInstantApp() ? ps.pkg.packageName : null;
             }
         }
         return null;
@@ -6218,7 +6222,7 @@
         }
         for (int i = resolveInfos.size() - 1; i >= 0; i--) {
             ResolveInfo info = resolveInfos.get(i);
-            final boolean isEphemeralApp = info.activityInfo.applicationInfo.isEphemeralApp();
+            final boolean isEphemeralApp = info.activityInfo.applicationInfo.isInstantApp();
             // allow activities that are defined in the provided package
             if (isEphemeralApp && ephemeralPkgName.equals(info.activityInfo.packageName)) {
                 continue;
@@ -7034,31 +7038,31 @@
     }
 
     @Override
-    public ParceledListSlice<EphemeralApplicationInfo> getEphemeralApplications(int userId) {
+    public ParceledListSlice<InstantAppInfo> getInstantApps(int userId) {
         if (HIDE_EPHEMERAL_APIS || isEphemeralDisabled()) {
             return null;
         }
 
-        mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_EPHEMERAL_APPS,
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
                 "getEphemeralApplications");
         enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 true /* requireFullPermission */, false /* checkShell */,
                 "getEphemeralApplications");
         synchronized (mPackages) {
-            List<EphemeralApplicationInfo> ephemeralApps = mEphemeralApplicationRegistry
-                    .getEphemeralApplicationsLPw(userId);
-            if (ephemeralApps != null) {
-                return new ParceledListSlice<>(ephemeralApps);
+            List<InstantAppInfo> instantApps = mInstantAppRegistry
+                    .getInstantAppsLPr(userId);
+            if (instantApps != null) {
+                return new ParceledListSlice<>(instantApps);
             }
         }
         return null;
     }
 
     @Override
-    public boolean isEphemeralApplication(String packageName, int userId) {
+    public boolean isInstantApp(String packageName, int userId) {
         enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 true /* requireFullPermission */, false /* checkShell */,
-                "isEphemeral");
+                "isInstantApp");
         if (HIDE_EPHEMERAL_APIS || isEphemeralDisabled()) {
             return false;
         }
@@ -7069,62 +7073,63 @@
         synchronized (mPackages) {
             PackageParser.Package pkg = mPackages.get(packageName);
             if (pkg != null) {
-                return pkg.applicationInfo.isEphemeralApp();
+                return pkg.applicationInfo.isInstantApp();
             }
         }
         return false;
     }
 
     @Override
-    public byte[] getEphemeralApplicationCookie(String packageName, int userId) {
+    public byte[] getInstantAppCookie(String packageName, int userId) {
         if (HIDE_EPHEMERAL_APIS || isEphemeralDisabled()) {
             return null;
         }
 
         enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 true /* requireFullPermission */, false /* checkShell */,
-                "getCookie");
+                "getInstantAppCookie");
         if (!isCallerSameApp(packageName)) {
             return null;
         }
         synchronized (mPackages) {
-            return mEphemeralApplicationRegistry.getEphemeralApplicationCookieLPw(
+            return mInstantAppRegistry.getInstantAppCookieLPw(
                     packageName, userId);
         }
     }
 
     @Override
-    public boolean setEphemeralApplicationCookie(String packageName, byte[] cookie, int userId) {
+    public boolean setInstantAppCookie(String packageName, byte[] cookie, int userId) {
         if (HIDE_EPHEMERAL_APIS || isEphemeralDisabled()) {
             return true;
         }
 
         enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 true /* requireFullPermission */, true /* checkShell */,
-                "setCookie");
+                "setInstantAppCookie");
         if (!isCallerSameApp(packageName)) {
             return false;
         }
         synchronized (mPackages) {
-            return mEphemeralApplicationRegistry.setEphemeralApplicationCookieLPw(
+            return mInstantAppRegistry.setInstantAppCookieLPw(
                     packageName, cookie, userId);
         }
     }
 
     @Override
-    public Bitmap getEphemeralApplicationIcon(String packageName, int userId) {
+    public Bitmap getInstantAppIcon(String packageName, int userId) {
         if (HIDE_EPHEMERAL_APIS || isEphemeralDisabled()) {
             return null;
         }
 
-        mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_EPHEMERAL_APPS,
-                "getEphemeralApplicationIcon");
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
+                "getInstantAppIcon");
 
         enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 true /* requireFullPermission */, false /* checkShell */,
-                "getEphemeralApplicationIcon");
+                "getInstantAppIcon");
+
         synchronized (mPackages) {
-            return mEphemeralApplicationRegistry.getEphemeralApplicationIconLPw(
+            return mInstantAppRegistry.getInstantAppIconLPw(
                     packageName, userId);
         }
     }
@@ -9345,7 +9350,7 @@
                     (policyFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/);
             if (isEphemeral(pkg)) {
                 final int userId = user == null ? 0 : user.getIdentifier();
-                mEphemeralApplicationRegistry.addEphemeralAppLPw(userId, pkgSetting.appId);
+                mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId);
             }
         }
         return pkg;
@@ -9460,7 +9465,7 @@
                 }
 
                 // Package declaring static a shared lib cannot be ephemeral
-                if (pkg.applicationInfo.isEphemeralApp()) {
+                if (pkg.applicationInfo.isInstantApp()) {
                     throw new PackageManagerException(
                             "Packages declaring static-shared libs cannot be ephemeral");
                 }
@@ -9994,7 +9999,7 @@
                 PackageParser.PermissionGroup cur = mPermissionGroups.get(pg.info.name);
                 final String curPackageName = cur == null ? null : cur.info.packageName;
                 // Dont allow ephemeral apps to define new permission groups.
-                if (pkg.applicationInfo.isEphemeralApp()) {
+                if (pkg.applicationInfo.isInstantApp()) {
                     Slog.w(TAG, "Permission group " + pg.info.name + " from package "
                             + pg.info.packageName
                             + " ignored: ephemeral apps cannot define new permission groups.");
@@ -10039,7 +10044,7 @@
                 PackageParser.Permission p = pkg.permissions.get(i);
 
                 // Dont allow ephemeral apps to define new permissions.
-                if (pkg.applicationInfo.isEphemeralApp()) {
+                if (pkg.applicationInfo.isInstantApp()) {
                     Slog.w(TAG, "Permission " + p.info.name + " from package "
                             + p.info.packageName
                             + " ignored: ephemeral apps cannot define new permissions.");
@@ -11242,7 +11247,7 @@
 
 
             // Limit ephemeral apps to ephemeral allowed permissions.
-            if (pkg.applicationInfo.isEphemeralApp() && !bp.isEphemeral()) {
+            if (pkg.applicationInfo.isInstantApp() && !bp.isInstant()) {
                 Log.i(TAG, "Denying non-ephemeral permission " + bp.name + " for package "
                         + pkg.packageName);
                 continue;
@@ -15752,7 +15757,7 @@
             }
 
             // don't allow an upgrade from full to ephemeral
-            final boolean oldIsEphemeral = oldPackage.applicationInfo.isEphemeralApp();
+            final boolean oldIsEphemeral = oldPackage.applicationInfo.isInstantApp();
             if (isEphemeral && !oldIsEphemeral) {
                 // can't downgrade from full to ephemeral
                 Slog.w(TAG, "Can't replace app with ephemeral: " + pkgName);
@@ -16776,10 +16781,11 @@
                     getOrCreateCompilerPackageStats(pkg));
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
 
-            // Notify BackgroundDexOptService that the package has been changed.
+            // Notify BackgroundDexOptJobService that the package has been changed.
             // If this is an update of a package which used to fail to compile,
             // BDOS will remove it from its blacklist.
-            BackgroundDexOptService.notifyPackageChanged(pkg.packageName);
+            // TODO: Layering violation
+            BackgroundDexOptJobService.notifyPackageChanged(pkg.packageName);
         }
 
         if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
@@ -16970,7 +16976,7 @@
     }
 
     private static boolean isEphemeral(PackageParser.Package pkg) {
-        return pkg.applicationInfo.isEphemeralApp();
+        return pkg.applicationInfo.isInstantApp();
     }
 
     private static boolean isEphemeral(PackageSetting ps) {
@@ -17407,7 +17413,8 @@
             }
             synchronized (mPackages) {
                 if (res) {
-                    mEphemeralApplicationRegistry.onPackageUninstalledLPw(uninstalledPs.pkg);
+                    mInstantAppRegistry.onPackageUninstalledLPw(uninstalledPs.pkg,
+                            info.removedUsers);
                 }
             }
         }
@@ -18260,6 +18267,10 @@
                         succeeded = clearApplicationUserDataLIF(packageName, userId);
                     }
                     clearExternalStorageDataSync(packageName, userId, true);
+                    synchronized (mPackages) {
+                        mInstantAppRegistry.deleteInstantApplicationMetadataLPw(
+                                packageName, userId);
+                    }
                 }
                 if (succeeded) {
                     // invoke DeviceStorageMonitor's update method to clear any notifications
@@ -22165,7 +22176,7 @@
             mUserNeedsBadging.delete(userHandle);
             mSettings.removeUserLPw(userHandle);
             mPendingBroadcasts.remove(userHandle);
-            mEphemeralApplicationRegistry.onUserRemovedLPw(userHandle);
+            mInstantAppRegistry.onUserRemovedLPw(userHandle);
             removeUnusedPackagesLPw(userManager, userHandle);
         }
     }
@@ -22746,7 +22757,7 @@
         public boolean isPackageEphemeral(int userId, String packageName) {
             synchronized (mPackages) {
                 PackageParser.Package p = mPackages.get(packageName);
-                return p != null ? p.applicationInfo.isEphemeralApp() : false;
+                return p != null ? p.applicationInfo.isInstantApp() : false;
             }
         }
 
@@ -22788,11 +22799,19 @@
         public void grantEphemeralAccess(int userId, Intent intent,
                 int targetAppId, int ephemeralAppId) {
             synchronized (mPackages) {
-                mEphemeralApplicationRegistry.grantEphemeralAccessLPw(userId, intent,
+                mInstantAppRegistry.grantInstantAccessLPw(userId, intent,
                         targetAppId, ephemeralAppId);
             }
         }
 
+        @Override
+        public void pruneInstantApps() {
+            synchronized (mPackages) {
+                mInstantAppRegistry.pruneInstantAppsLPw();
+            }
+        }
+
+        @Override
         public String getSetupWizardPackageName() {
             return mSetupWizardPackage;
         }
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index b9bf1db..2781150 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -287,7 +287,7 @@
             }
         }
 
-        if (pkg.applicationInfo.isEphemeralApp())
+        if (pkg.applicationInfo.isInstantApp())
             pkg.applicationInfo.seinfo += EPHEMERAL_APP_STR;
 
         if (pkg.applicationInfo.targetSandboxVersion == 2)
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index c0fc24c..d8857b7 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -3081,7 +3081,7 @@
     }
 
     private static boolean isEphemeralApp(@Nullable ApplicationInfo ai) {
-        return (ai != null) && ai.isEphemeralApp();
+        return (ai != null) && ai.isInstantApp();
     }
 
     private static boolean isInstalled(@Nullable PackageInfo pi) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e4eaf4a..70b0bf2 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -83,7 +83,6 @@
 import com.android.server.notification.NotificationManagerService;
 import com.android.server.os.DeviceIdentifiersPolicyService;
 import com.android.server.os.SchedulingPolicyService;
-import com.android.server.pm.BackgroundDexOptService;
 import com.android.server.pm.Installer;
 import com.android.server.pm.LauncherAppsService;
 import com.android.server.pm.OtaDexoptService;
@@ -1380,11 +1379,19 @@
                     traceEnd();
                 }
 
-                traceBeginAndSlog("StartBackgroundDexOptService");
+                traceBeginAndSlog("StartBackgroundDexOptJobService");
                 try {
-                    BackgroundDexOptService.schedule(context);
+                    BackgroundDexOptJobService.schedule(context);
                 } catch (Throwable e) {
-                    reportWtf("starting BackgroundDexOptService", e);
+                    reportWtf("starting StartBackgroundDexOptJobService", e);
+                }
+                traceEnd();
+
+                traceBeginAndSlog("StartPruneInstantAppsJobService");
+                try {
+                    PruneInstantAppsJobService.schedule(context);
+                } catch (Throwable e) {
+                    reportWtf("StartPruneInstantAppsJobService", e);
                 }
                 traceEnd();
             }
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 71c9c73..0dcd0f1 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -16,9 +16,7 @@
 
 package android.test.mock;
 
-import android.annotation.IntRange;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.app.PackageInstallObserver;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -26,7 +24,7 @@
 import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.EphemeralApplicationInfo;
+import android.content.pm.InstantAppInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageDeleteObserver;
@@ -342,38 +340,38 @@
 
     /** @hide */
     @Override
-    public List<EphemeralApplicationInfo> getEphemeralApplications() {
+    public List<InstantAppInfo> getInstantApps() {
         throw new UnsupportedOperationException();
     }
 
     /** @hide */
     @Override
-    public Drawable getEphemeralApplicationIcon(String packageName) {
+    public Drawable getInstantAppIcon(String packageName) {
         throw new UnsupportedOperationException();
     }
 
     /** @hide */
     @Override
-    public byte[] getEphemeralCookie() {
-        return new byte[0];
+    public byte[] getInstantAppCookie() {
+        throw new UnsupportedOperationException();
     }
 
     /** @hide */
     @Override
-    public boolean isEphemeralApplication() {
-        return false;
+    public boolean isInstantApp() {
+        throw new UnsupportedOperationException();
     }
 
     /** @hide */
     @Override
-    public int getEphemeralCookieMaxSizeBytes() {
-        return 0;
+    public int getInstantAppCookieMaxSize() {
+        throw new UnsupportedOperationException();
     }
 
     /** @hide */
     @Override
-    public boolean setEphemeralCookie(@NonNull byte[] cookie) {
-        return false;
+    public boolean setInstantAppCookie(@NonNull byte[] cookie) {
+        throw new UnsupportedOperationException();
     }
 
     @Override
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index de04c43..00799a1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -24,7 +24,7 @@
 import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.EphemeralApplicationInfo;
+import android.content.pm.InstantAppInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageDeleteObserver;
@@ -300,32 +300,32 @@
     }
 
     @Override
-    public List<EphemeralApplicationInfo> getEphemeralApplications() {
+    public List<InstantAppInfo> getInstantApps() {
         return null;
     }
 
     @Override
-    public Drawable getEphemeralApplicationIcon(String packageName) {
+    public Drawable getInstantAppIcon(String packageName) {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public byte[] getEphemeralCookie() {
+    public byte[] getInstantAppCookie() {
         return new byte[0];
     }
 
     @Override
-    public boolean isEphemeralApplication() {
+    public boolean isInstantApp() {
         return false;
     }
 
     @Override
-    public int getEphemeralCookieMaxSizeBytes() {
+    public int getInstantAppCookieMaxSize() {
         return 0;
     }
 
     @Override
-    public boolean setEphemeralCookie(@NonNull byte[] cookie) {
+    public boolean setInstantAppCookie(@NonNull byte[] cookie) {
         return false;
     }