Ephemeral cookie API

Add APIs for an ephemeral app to set a cookie which is a small
peice of data cached longer than the app itself. This is useful
for avoiding the user to login every time they use the ephemeral
app. The cookie is stored after an ephemeral app is uninstalled.
Normal apps or ephemeral apps upgraded to full apps can also use
these APIs with the difference that once they are uninstalled
the cookie is deleted.

The cookie size defaults to 16KB and is configurable by a global
settings which can be adjusted via gservices. Also eviction policy
is time based with a default of one month and is configurable by
a global setting which can be adjusted via gservices. If the cert
of the app cahnges (when ephemeral is installed, uninstalled and
installed again) the cooke is wiped to prevent data leaks.

This cahange also adds an API for apps to know whether they run in
an ephemeral mode since it this mode some APIs will not be available.
Another API exposed by this change is private for the system and
exposes all ephemeral apps - installed and uninstalled. Only the
system can call this API. When an ephemeral app is uninstalled the
system stores its name, icon, and permissions. When the app is
reinstalled or a full version is installed the permissions are
propagated.

Change-Id: Id4a73a7750bfbabda0bfcb9bf9018d2062e94367
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index e500e15..460e68c 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -30,6 +30,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ComponentInfo;
 import android.content.pm.ContainerEncryptionParams;
+import android.content.pm.EphemeralApplicationInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IOnPermissionsChangeListener;
 import android.content.pm.IPackageDataObserver;
@@ -85,9 +86,11 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.UserIcons;
+import libcore.util.EmptyArray;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -98,6 +101,8 @@
     private static final String TAG = "ApplicationPackageManager";
     private final static boolean DEBUG_ICONS = false;
 
+    private static final int DEFAULT_EPHEMERAL_COOKIE_MAX_SIZE_BYTES = 16384; // 16KB
+
     // Default flags to use with PackageManager when no flags are given.
     private final static int sDefaultFlags = PackageManager.GET_SHARED_LIBRARY_FILES;
 
@@ -626,6 +631,80 @@
         }
     }
 
+    /** @hide */
+    @SuppressWarnings("unchecked")
+    @Override
+    public List<EphemeralApplicationInfo> getEphemeralApplications() {
+        try {
+            ParceledListSlice<EphemeralApplicationInfo> slice =
+                    mPM.getEphemeralApplications(mContext.getUserId());
+            if (slice != null) {
+                return slice.getList();
+            }
+            return Collections.emptyList();
+        } catch (RemoteException e) {
+            throw new RuntimeException("Package manager has died", e);
+        }
+    }
+
+    /** @hide */
+    @Override
+    public Drawable getEphemeralApplicationIcon(String packageName) {
+        try {
+            Bitmap bitmap = mPM.getEphemeralApplicationIcon(
+                    packageName, mContext.getUserId());
+            if (bitmap != null) {
+                return new BitmapDrawable(null, bitmap);
+            }
+            return null;
+        } catch (RemoteException e) {
+            throw new RuntimeException("Package manager has died", e);
+        }
+    }
+
+    @Override
+    public boolean isEphemeralApplication() {
+        try {
+            return mPM.isEphemeralApplication(
+                    mContext.getPackageName(), mContext.getUserId());
+        } catch (RemoteException e) {
+            Log.e(TAG, "System server is dead", e);
+        }
+        return false;
+    }
+
+    @Override
+    public int getEphemeralCookieMaxSizeBytes() {
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES,
+                DEFAULT_EPHEMERAL_COOKIE_MAX_SIZE_BYTES);
+    }
+
+    @Override
+    public @NonNull byte[] getEphemeralCookie() {
+        try {
+            final byte[] cookie = mPM.getEphemeralApplicationCookie(
+                    mContext.getPackageName(), mContext.getUserId());
+            if (cookie != null) {
+                return cookie;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "System server is dead", e);
+        }
+        return EmptyArray.BYTE;
+    }
+
+    @Override
+    public boolean setEphemeralCookie(@NonNull  byte[] cookie) {
+        try {
+            return mPM.setEphemeralApplicationCookie(
+                    mContext.getPackageName(), cookie, mContext.getUserId());
+        } catch (RemoteException e) {
+            Log.e(TAG, "System server is dead", e);
+        }
+        return false;
+    }
+
     @Override
     public ResolveInfo resolveActivity(Intent intent, int flags) {
         return resolveActivityAsUser(intent, flags, mContext.getUserId());