Merge "Wire up lifecycle, send unlocked broadcast."
diff --git a/api/current.txt b/api/current.txt
index 73962f8f..fc95ca3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8344,6 +8344,7 @@
     field public static final java.lang.String ACTION_USER_FOREGROUND = "android.intent.action.USER_FOREGROUND";
     field public static final java.lang.String ACTION_USER_INITIALIZE = "android.intent.action.USER_INITIALIZE";
     field public static final java.lang.String ACTION_USER_PRESENT = "android.intent.action.USER_PRESENT";
+    field public static final java.lang.String ACTION_USER_UNLOCKED = "android.intent.action.USER_UNLOCKED";
     field public static final java.lang.String ACTION_VIEW = "android.intent.action.VIEW";
     field public static final java.lang.String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND";
     field public static final deprecated java.lang.String ACTION_WALLPAPER_CHANGED = "android.intent.action.WALLPAPER_CHANGED";
diff --git a/api/system-current.txt b/api/system-current.txt
index c4d99b8..409a7d5 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -8602,6 +8602,7 @@
     field public static final java.lang.String ACTION_USER_FOREGROUND = "android.intent.action.USER_FOREGROUND";
     field public static final java.lang.String ACTION_USER_INITIALIZE = "android.intent.action.USER_INITIALIZE";
     field public static final java.lang.String ACTION_USER_PRESENT = "android.intent.action.USER_PRESENT";
+    field public static final java.lang.String ACTION_USER_UNLOCKED = "android.intent.action.USER_UNLOCKED";
     field public static final java.lang.String ACTION_VIEW = "android.intent.action.VIEW";
     field public static final java.lang.String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND";
     field public static final deprecated java.lang.String ACTION_WALLPAPER_CHANGED = "android.intent.action.WALLPAPER_CHANGED";
diff --git a/api/test-current.txt b/api/test-current.txt
index 73962f8f..fc95ca3 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -8344,6 +8344,7 @@
     field public static final java.lang.String ACTION_USER_FOREGROUND = "android.intent.action.USER_FOREGROUND";
     field public static final java.lang.String ACTION_USER_INITIALIZE = "android.intent.action.USER_INITIALIZE";
     field public static final java.lang.String ACTION_USER_PRESENT = "android.intent.action.USER_PRESENT";
+    field public static final java.lang.String ACTION_USER_UNLOCKED = "android.intent.action.USER_UNLOCKED";
     field public static final java.lang.String ACTION_VIEW = "android.intent.action.VIEW";
     field public static final java.lang.String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND";
     field public static final deprecated java.lang.String ACTION_WALLPAPER_CHANGED = "android.intent.action.WALLPAPER_CHANGED";
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f7aee75..f1a7de8 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3084,7 +3084,7 @@
     /** {@hide} */
     public static final int FLAG_OR_STOPPED = 1 << 0;
     /** {@hide} */
-    public static final int FLAG_WITH_AMNESIA = 1 << 1;
+    public static final int FLAG_AND_LOCKED = 1 << 1;
 
     /**
      * Return whether the given user is actively running.  This means that
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 4a7cbc7..e25f1d7 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2877,6 +2877,14 @@
             "android.intent.action.USER_SWITCHED";
 
     /**
+     * Broadcast Action: Sent when the credential-encrypted private storage has
+     * become unlocked for the target user. This is only sent to registered
+     * receivers, not manifest receivers.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_USER_UNLOCKED = "android.intent.action.USER_UNLOCKED";
+
+    /**
      * Broadcast sent to the system when a user's information changes. Carries an extra
      * {@link #EXTRA_USER_HANDLE} to indicate which user's information changed.
      * This is only sent to registered receivers, not manifest receivers. It is sent to all users.
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 1996e0f..65e5945 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -487,6 +487,13 @@
     public static final int PRIVATE_FLAG_AUTOPLAY = 1 << 7;
 
     /**
+     * When set, at least one component inside this application is encryption aware.
+     *
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_PARTIALLY_ENCRYPTION_AWARE = 1 << 8;
+
+    /**
      * Private/hidden flags. See {@code PRIVATE_FLAG_...} constants.
      * {@hide}
      */
@@ -1054,6 +1061,11 @@
         return (privateFlags & ApplicationInfo.PRIVATE_FLAG_ENCRYPTION_AWARE) != 0;
     }
 
+    /** @hide */
+    public boolean isPartiallyEncryptionAware() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PARTIALLY_ENCRYPTION_AWARE) != 0;
+    }
+
     /**
      * @hide
      */
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 6fe1efd..b9a42eb 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -59,7 +59,7 @@
  *  {@hide}
  */
 interface IPackageManager {
-    boolean isPackageFrozen(String packageName);
+    void checkPackageStartable(String packageName, int userId);
     boolean isPackageAvailable(String packageName, int userId);
     PackageInfo getPackageInfo(String packageName, int flags, int userId);
     int getPackageUid(String packageName, int userId);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 838da37..17af944 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3262,6 +3262,11 @@
                     owner.applicationInfo.isEncryptionAware());
         }
 
+        if (a.info.encryptionAware) {
+            owner.applicationInfo.privateFlags |=
+                    ApplicationInfo.PRIVATE_FLAG_PARTIALLY_ENCRYPTION_AWARE;
+        }
+
         sa.recycle();
 
         if (receiver && (owner.applicationInfo.privateFlags
@@ -3663,6 +3668,10 @@
         p.info.encryptionAware = sa.getBoolean(
                 R.styleable.AndroidManifestProvider_encryptionAware,
                 owner.applicationInfo.isEncryptionAware());
+        if (p.info.encryptionAware) {
+            owner.applicationInfo.privateFlags |=
+                    ApplicationInfo.PRIVATE_FLAG_PARTIALLY_ENCRYPTION_AWARE;
+        }
 
         sa.recycle();
 
@@ -3947,6 +3956,10 @@
         s.info.encryptionAware = sa.getBoolean(
                 R.styleable.AndroidManifestService_encryptionAware,
                 owner.applicationInfo.isEncryptionAware());
+        if (s.info.encryptionAware) {
+            owner.applicationInfo.privateFlags |=
+                    ApplicationInfo.PRIVATE_FLAG_PARTIALLY_ENCRYPTION_AWARE;
+        }
 
         sa.recycle();
 
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index da81528..d6c6f13 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -16,6 +16,8 @@
 
 package com.android.server;
 
+import android.app.ActivityManagerNative;
+import android.app.AppGlobals;
 import android.app.admin.DevicePolicyManager;
 import android.app.backup.BackupManager;
 import android.app.trust.IStrongAuthTracker;
@@ -388,6 +390,13 @@
         }
     }
 
+    private void unlockUser(int userId, byte[] token) {
+        try {
+            ActivityManagerNative.getDefault().unlockUser(userId, token);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
 
     private byte[] getCurrentHandle(int userId) {
         CredentialHash credential;
@@ -612,6 +621,7 @@
             byte[] hash = credentialUtil.toHash(credential, userId);
             if (Arrays.equals(hash, storedHash.hash)) {
                 unlockKeystore(credentialUtil.adjustForKeystore(credential), userId);
+                unlockUser(userId, null);
                 // migrate credential to GateKeeper
                 credentialUtil.setCredential(credential, null, userId);
                 if (!hasChallenge) {
@@ -664,6 +674,7 @@
         if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
             // credential has matched
             unlockKeystore(credential, userId);
+            unlockUser(userId, null);
             if (shouldReEnroll) {
                 credentialUtil.setCredential(credential, credential, userId);
             }
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index a32bb2f..bd43a71 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -781,6 +781,7 @@
     }
 
     private void handleSystemReady() {
+        initIfReadyAndConnected();
         resetIfReadyAndConnected();
 
         // Start scheduling nominally-daily fstrim operations
@@ -828,6 +829,22 @@
         mVolumes.put(internal.id, internal);
     }
 
+    private void initIfReadyAndConnected() {
+        Slog.d(TAG, "Thinking about init, mSystemReady=" + mSystemReady
+                + ", mDaemonConnected=" + mDaemonConnected);
+        if (mSystemReady && mDaemonConnected && StorageManager.isFileBasedEncryptionEnabled()) {
+            final List<UserInfo> users = mContext.getSystemService(UserManager.class)
+                    .getUsers();
+            for (UserInfo user : users) {
+                try {
+                    mCryptConnector.execute("cryptfs", "lock_user_key", user.id);
+                } catch (NativeDaemonConnectorException e) {
+                    Slog.w(TAG, "Failed to init vold", e);
+                }
+            }
+        }
+    }
+
     private void resetIfReadyAndConnected() {
         Slog.d(TAG, "Thinking about reset, mSystemReady=" + mSystemReady
                 + ", mDaemonConnected=" + mDaemonConnected);
@@ -928,6 +945,7 @@
     }
 
     private void handleDaemonConnected() {
+        initIfReadyAndConnected();
         resetIfReadyAndConnected();
 
         /*
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 557b386..99470c8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3356,10 +3356,8 @@
 
         try {
             try {
-                if (AppGlobals.getPackageManager().isPackageFrozen(app.info.packageName)) {
-                    // This is caught below as if we had failed to fork zygote
-                    throw new RuntimeException("Package " + app.info.packageName + " is frozen!");
-                }
+                final int userId = UserHandle.getUserId(app.uid);
+                AppGlobals.getPackageManager().checkPackageStartable(app.info.packageName, userId);
             } catch (RemoteException e) {
                 throw e.rethrowAsRuntimeException();
             }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index b30905e..9c29149 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -624,6 +624,23 @@
             throw new SecurityException(msg);
         }
 
+        final long binderToken = Binder.clearCallingIdentity();
+        try {
+            return unlockUserCleared(userId, token);
+        } finally {
+            Binder.restoreCallingIdentity(binderToken);
+        }
+    }
+
+    boolean unlockUserCleared(final int userId, byte[] token) {
+        synchronized (mService) {
+            final UserState uss = mStartedUsers.get(userId);
+            if (uss.unlocked) {
+                // Bail early when already unlocked
+                return true;
+            }
+        }
+
         final UserInfo userInfo = getUserInfo(userId);
         final IMountService mountService = IMountService.Stub
                 .asInterface(ServiceManager.getService("mount"));
@@ -631,7 +648,7 @@
             mountService.unlockUserKey(userId, userInfo.serialNumber, token);
         } catch (RemoteException e) {
             Slog.w(TAG, "Failed to unlock: " + e.getMessage());
-            throw e.rethrowAsRuntimeException();
+            return false;
         }
 
         synchronized (mService) {
@@ -639,6 +656,11 @@
             updateUserUnlockedState(uss);
         }
 
+        final Intent intent = new Intent(Intent.ACTION_USER_UNLOCKED);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
+        mService.broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+                AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, userId);
+
         return true;
     }
 
@@ -1011,7 +1033,7 @@
         if ((flags & ActivityManager.FLAG_OR_STOPPED) != 0) {
             return true;
         }
-        if ((flags & ActivityManager.FLAG_WITH_AMNESIA) != 0) {
+        if ((flags & ActivityManager.FLAG_AND_LOCKED) != 0) {
             // If user is currently locked, we fall through to default "running"
             // behavior below
             if (state.unlocked) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4bc79cb..c7d1171 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2800,15 +2800,24 @@
     }
 
     @Override
-    public boolean isPackageFrozen(String packageName) {
+    public void checkPackageStartable(String packageName, int userId) {
+        final boolean userKeyUnlocked = isUserKeyUnlocked(userId);
+
         synchronized (mPackages) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
-            if (ps != null) {
-                return ps.frozen;
+            if (ps == null) {
+                throw new SecurityException("Package " + packageName + " was not found!");
+            }
+
+            if (ps.frozen) {
+                throw new SecurityException("Package " + packageName + " is currently frozen!");
+            }
+
+            if (!userKeyUnlocked && !(ps.pkg.applicationInfo.isEncryptionAware()
+                    || ps.pkg.applicationInfo.isPartiallyEncryptionAware())) {
+                throw new SecurityException("Package " + packageName + " is not encryption aware!");
             }
         }
-        Slog.w(TAG, "Package " + packageName + " is missing; assuming frozen");
-        return true;
     }
 
     @Override
@@ -3143,29 +3152,36 @@
     }
 
     /**
-     * Augment the given flags depending on current user running state. This is
-     * purposefully done before acquiring {@link #mPackages} lock.
+     * Return if the user key is currently unlocked.
      */
-    private int augmentFlagsForUser(int flags, int userId) {
+    private boolean isUserKeyUnlocked(int userId) {
         if (StorageManager.isFileBasedEncryptionEnabled()) {
             final IMountService mount = IMountService.Stub
                     .asInterface(ServiceManager.getService("mount"));
             if (mount == null) {
-                // We must be early in boot, so the best we can do is assume the
-                // user is fully running.
-                Slog.w(TAG, "Early during boot, assuming not encrypted");
-                return flags;
+                Slog.w(TAG, "Early during boot, assuming locked");
+                return false;
             }
             final long token = Binder.clearCallingIdentity();
             try {
-                if (!mount.isUserKeyUnlocked(userId)) {
-                    flags |= PackageManager.MATCH_ENCRYPTION_AWARE_ONLY;
-                }
+                return mount.isUserKeyUnlocked(userId);
             } catch (RemoteException e) {
                 throw e.rethrowAsRuntimeException();
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     * Augment the given flags depending on current user running state. This is
+     * purposefully done before acquiring {@link #mPackages} lock.
+     */
+    private int augmentFlagsForUser(int flags, int userId) {
+        if (!isUserKeyUnlocked(userId)) {
+            flags |= PackageManager.MATCH_ENCRYPTION_AWARE_ONLY;
         }
         return flags;
     }