Merge "Broadcast lifecycle events for instant apps"
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f60cb06..e0f3ec7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -607,6 +607,12 @@
      */
     private static final boolean DEFAULT_PACKAGE_PARSER_CACHE_ENABLED = true;
 
+    /**
+     * Permissions required in order to receive instant application lifecycle broadcasts.
+     */
+    private static final String[] INSTANT_APP_BROADCAST_PERMISSION =
+            new String[] { android.Manifest.permission.ACCESS_INSTANT_APPS };
+
     final ServiceThread mHandlerThread;
 
     final PackageHandler mHandler;
@@ -1967,16 +1973,20 @@
 
             // Determine the set of users who are adding this package for
             // the first time vs. those who are seeing an update.
-            int[] firstUsers = EMPTY_INT_ARRAY;
-            int[] updateUsers = EMPTY_INT_ARRAY;
+            int[] firstUserIds = EMPTY_INT_ARRAY;
+            int[] firstInstantUserIds = EMPTY_INT_ARRAY;
+            int[] updateUserIds = EMPTY_INT_ARRAY;
+            int[] instantUserIds = EMPTY_INT_ARRAY;
             final boolean allNewUsers = res.origUsers == null || res.origUsers.length == 0;
             final PackageSetting ps = (PackageSetting) res.pkg.mExtras;
             for (int newUser : res.newUsers) {
-                if (ps.getInstantApp(newUser)) {
-                    continue;
-                }
+                final boolean isInstantApp = ps.getInstantApp(newUser);
                 if (allNewUsers) {
-                    firstUsers = ArrayUtils.appendInt(firstUsers, newUser);
+                    if (isInstantApp) {
+                        firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);
+                    } else {
+                        firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);
+                    }
                     continue;
                 }
                 boolean isNew = true;
@@ -1987,9 +1997,17 @@
                     }
                 }
                 if (isNew) {
-                    firstUsers = ArrayUtils.appendInt(firstUsers, newUser);
+                    if (isInstantApp) {
+                        firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);
+                    } else {
+                        firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);
+                    }
                 } else {
-                    updateUsers = ArrayUtils.appendInt(updateUsers, newUser);
+                    if (isInstantApp) {
+                        instantUserIds = ArrayUtils.appendInt(instantUserIds, newUser);
+                    } else {
+                        updateUserIds = ArrayUtils.appendInt(updateUserIds, newUser);
+                    }
                 }
             }
 
@@ -2002,7 +2020,7 @@
                 int appId = UserHandle.getAppId(res.uid);
                 boolean isSystem = res.pkg.applicationInfo.isSystemApp();
                 sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload,
-                        virtualPreload /*startReceiver*/, appId, firstUsers);
+                        virtualPreload /*startReceiver*/, appId, firstUserIds, firstInstantUserIds);
 
                 // Send added for users that don't see the package for the first time
                 Bundle extras = new Bundle(1);
@@ -2012,11 +2030,13 @@
                 }
                 sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
                         extras, 0 /*flags*/,
-                        null /*targetPackage*/, null /*finishedReceiver*/, updateUsers);
+                        null /*targetPackage*/, null /*finishedReceiver*/,
+                        updateUserIds, instantUserIds);
                 if (installerPackageName != null) {
                     sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
                             extras, 0 /*flags*/,
-                            installerPackageName, null /*finishedReceiver*/, updateUsers);
+                            installerPackageName, null /*finishedReceiver*/,
+                            updateUserIds, instantUserIds);
                 }
 
                 // Send replaced for users that don't see the package for the first time
@@ -2024,24 +2044,26 @@
                     sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
                             packageName, extras, 0 /*flags*/,
                             null /*targetPackage*/, null /*finishedReceiver*/,
-                            updateUsers);
+                            updateUserIds, instantUserIds);
                     if (installerPackageName != null) {
                         sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
                                 extras, 0 /*flags*/,
-                                installerPackageName, null /*finishedReceiver*/, updateUsers);
+                                installerPackageName, null /*finishedReceiver*/,
+                                updateUserIds, instantUserIds);
                     }
                     sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
                             null /*package*/, null /*extras*/, 0 /*flags*/,
                             packageName /*targetPackage*/,
-                            null /*finishedReceiver*/, updateUsers);
+                            null /*finishedReceiver*/, updateUserIds, instantUserIds);
                 } else if (launchedForRestore && !isSystemApp(res.pkg)) {
                     // First-install and we did a restore, so we're responsible for the
                     // first-launch broadcast.
                     if (DEBUG_BACKUP) {
                         Slog.i(TAG, "Post-restore of " + packageName
-                                + " sending FIRST_LAUNCH in " + Arrays.toString(firstUsers));
+                                + " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds));
                     }
-                    sendFirstLaunchBroadcast(packageName, installerPackage, firstUsers);
+                    sendFirstLaunchBroadcast(packageName, installerPackage,
+                            firstUserIds, firstInstantUserIds);
                 }
 
                 // Send broadcast package appeared if forward locked/external for all users
@@ -2059,9 +2081,9 @@
             }
 
             // Work that needs to happen on first install within each user
-            if (firstUsers != null && firstUsers.length > 0) {
+            if (firstUserIds != null && firstUserIds.length > 0) {
                 synchronized (mPackages) {
-                    for (int userId : firstUsers) {
+                    for (int userId : firstUserIds) {
                         // If this app is a browser and it's newly-installed for some
                         // users, clear any default-browser state in those users. The
                         // app's nature doesn't depend on the user, so we can just check
@@ -2099,7 +2121,7 @@
             // should not change.
             // Don't notify the manager for ephemeral apps as they are not expected to
             // survive long enough to benefit of background optimizations.
-            for (int userId : firstUsers) {
+            for (int userId : firstUserIds) {
                 PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId);
                 // There's a race currently where some install events may interleave with an uninstall.
                 // This can lead to package info being null (b/36642664).
@@ -12777,9 +12799,10 @@
         }
     };
 
+    @Override
     public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
             final int flags, final String targetPkg, final IIntentReceiver finishedReceiver,
-            final int[] userIds) {
+            final int[] userIds, int[] instantUserIds) {
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -12792,33 +12815,11 @@
                     } else {
                         resolvedUserIds = userIds;
                     }
-                    for (int id : resolvedUserIds) {
-                        final Intent intent = new Intent(action,
-                                pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null);
-                        if (extras != null) {
-                            intent.putExtras(extras);
-                        }
-                        if (targetPkg != null) {
-                            intent.setPackage(targetPkg);
-                        }
-                        // Modify the UID when posting to other users
-                        int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
-                        if (uid > 0 && UserHandle.getUserId(uid) != id) {
-                            uid = UserHandle.getUid(id, UserHandle.getAppId(uid));
-                            intent.putExtra(Intent.EXTRA_UID, uid);
-                        }
-                        intent.putExtra(Intent.EXTRA_USER_HANDLE, id);
-                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags);
-                        if (DEBUG_BROADCASTS) {
-                            RuntimeException here = new RuntimeException("here");
-                            here.fillInStackTrace();
-                            Slog.d(TAG, "Sending to user " + id + ": "
-                                    + intent.toShortString(false, true, false, false)
-                                    + " " + intent.getExtras(), here);
-                        }
-                        am.broadcastIntent(null, intent, null, finishedReceiver,
-                                0, null, null, null, android.app.AppOpsManager.OP_NONE,
-                                null, finishedReceiver != null, false, id);
+                    doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
+                            resolvedUserIds, false);
+                    if (instantUserIds != null && instantUserIds != EMPTY_INT_ARRAY) {
+                        doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
+                                instantUserIds, true);
                     }
                 } catch (RemoteException ex) {
                 }
@@ -12827,6 +12828,49 @@
     }
 
     /**
+     * Sends a broadcast for the given action.
+     * <p>If {@code isInstantApp} is {@code true}, then the broadcast is protected with
+     * the {@link android.Manifest.permission#ACCESS_INSTANT_APPS} permission. This allows
+     * the system and applications allowed to see instant applications to receive package
+     * lifecycle events for instant applications.
+     */
+    private void doSendBroadcast(IActivityManager am, String action, String pkg, Bundle extras,
+            int flags, String targetPkg, IIntentReceiver finishedReceiver,
+            int[] userIds, boolean isInstantApp)
+                    throws RemoteException {
+        for (int id : userIds) {
+            final Intent intent = new Intent(action,
+                    pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null);
+            final String[] requiredPermissions =
+                    isInstantApp ? INSTANT_APP_BROADCAST_PERMISSION : null;
+            if (extras != null) {
+                intent.putExtras(extras);
+            }
+            if (targetPkg != null) {
+                intent.setPackage(targetPkg);
+            }
+            // Modify the UID when posting to other users
+            int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+            if (uid > 0 && UserHandle.getUserId(uid) != id) {
+                uid = UserHandle.getUid(id, UserHandle.getAppId(uid));
+                intent.putExtra(Intent.EXTRA_UID, uid);
+            }
+            intent.putExtra(Intent.EXTRA_USER_HANDLE, id);
+            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags);
+            if (DEBUG_BROADCASTS) {
+                RuntimeException here = new RuntimeException("here");
+                here.fillInStackTrace();
+                Slog.d(TAG, "Sending to user " + id + ": "
+                        + intent.toShortString(false, true, false, false)
+                        + " " + intent.getExtras(), here);
+            }
+            am.broadcastIntent(null, intent, null, finishedReceiver,
+                    0, null, null, requiredPermissions, android.app.AppOpsManager.OP_NONE,
+                    null, finishedReceiver != null, false, id);
+        }
+    }
+
+    /**
      * Check if the external storage media is available. This is true if there
      * is a mounted external storage medium or if the external storage is
      * emulated.
@@ -13075,8 +13119,11 @@
     private void sendPackageAddedForUser(String packageName, PackageSetting pkgSetting,
             int userId) {
         final boolean isSystem = isSystemApp(pkgSetting) || isUpdatedSystemApp(pkgSetting);
+        final boolean isInstantApp = pkgSetting.getInstantApp(userId);
+        final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
+        final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
         sendPackageAddedForNewUsers(packageName, isSystem /*sendBootCompleted*/,
-                false /*startReceiver*/, pkgSetting.appId, userId);
+                false /*startReceiver*/, pkgSetting.appId, userIds, instantUserIds);
 
         // Send a session commit broadcast
         final PackageInstaller.SessionInfo info = new PackageInstaller.SessionInfo();
@@ -13085,18 +13132,21 @@
         sendSessionCommitBroadcast(info, userId);
     }
 
+    @Override
     public void sendPackageAddedForNewUsers(String packageName, boolean sendBootCompleted,
-            boolean includeStopped, int appId, int... userIds) {
-        if (ArrayUtils.isEmpty(userIds)) {
+            boolean includeStopped, int appId, int[] userIds, int[] instantUserIds) {
+        if (ArrayUtils.isEmpty(userIds) && ArrayUtils.isEmpty(instantUserIds)) {
             return;
         }
         Bundle extras = new Bundle(1);
         // Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast
-        extras.putInt(Intent.EXTRA_UID, UserHandle.getUid(userIds[0], appId));
+        final int uid = UserHandle.getUid(
+                (ArrayUtils.isEmpty(userIds) ? instantUserIds[0] : userIds[0]), appId);
+        extras.putInt(Intent.EXTRA_UID, uid);
 
         sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
-                packageName, extras, 0, null, null, userIds);
-        if (sendBootCompleted) {
+                packageName, extras, 0, null, null, userIds, instantUserIds);
+        if (sendBootCompleted && !ArrayUtils.isEmpty(userIds)) {
             mHandler.post(() -> {
                         for (int userId : userIds) {
                             sendBootCompletedBroadcastToSystemApp(
@@ -13240,7 +13290,7 @@
                     suspended ? Intent.ACTION_PACKAGES_SUSPENDED
                             : Intent.ACTION_PACKAGES_UNSUSPENDED,
                     null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null,
-                    new int[] {userId});
+                    new int[] {userId}, null);
         }
     }
 
@@ -14128,7 +14178,8 @@
      * the first-launch broadcast will be sent implicitly on that basis in POST_INSTALL
      * handling.
      */
-    void notifyFirstLaunch(final String pkgName, final String installerPackage, final int userId) {
+    void notifyFirstLaunch(final String packageName, final String installerPackage,
+            final int userId) {
         // Serialize this with the rest of the install-process message chain.  In the
         // restore-at-install case, this Runnable will necessarily run before the
         // POST_INSTALL message is processed, so the contents of mRunningInstalls
@@ -14143,12 +14194,12 @@
                     if (data.res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
                         continue;
                     }
-                    if (pkgName.equals(data.res.pkg.applicationInfo.packageName)) {
+                    if (packageName.equals(data.res.pkg.applicationInfo.packageName)) {
                         // right package; but is it for the right user?
                         for (int uIndex = 0; uIndex < data.res.newUsers.length; uIndex++) {
                             if (userId == data.res.newUsers[uIndex]) {
                                 if (DEBUG_BACKUP) {
-                                    Slog.i(TAG, "Package " + pkgName
+                                    Slog.i(TAG, "Package " + packageName
                                             + " being restored so deferring FIRST_LAUNCH");
                                 }
                                 return;
@@ -14158,16 +14209,20 @@
                 }
                 // didn't find it, so not being restored
                 if (DEBUG_BACKUP) {
-                    Slog.i(TAG, "Package " + pkgName + " sending normal FIRST_LAUNCH");
+                    Slog.i(TAG, "Package " + packageName + " sending normal FIRST_LAUNCH");
                 }
-                sendFirstLaunchBroadcast(pkgName, installerPackage, new int[] {userId});
+                final boolean isInstantApp = isInstantApp(packageName, userId);
+                final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
+                final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
+                sendFirstLaunchBroadcast(packageName, installerPackage, userIds, instantUserIds);
             }
         });
     }
 
-    private void sendFirstLaunchBroadcast(String pkgName, String installerPkg, int[] userIds) {
+    private void sendFirstLaunchBroadcast(String pkgName, String installerPkg,
+            int[] userIds, int[] instantUserIds) {
         sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0,
-                installerPkg, null, userIds);
+                installerPkg, null, userIds, instantUserIds);
     }
 
     private abstract class HandlerParams {
@@ -17314,6 +17369,7 @@
         int[] origUsers;
         int[] removedUsers = null;
         int[] broadcastUsers = null;
+        int[] instantUserIds = null;
         SparseArray<Integer> installReasons;
         boolean isRemovedPackageSystemUpdate = false;
         boolean isUpdate;
@@ -17359,7 +17415,7 @@
                 PackageInstalledInfo installedInfo = appearedChildPackages.valueAt(i);
                 packageSender.sendPackageAddedForNewUsers(installedInfo.name,
                     true /*sendBootCompleted*/, false /*startReceiver*/,
-                    UserHandle.getAppId(installedInfo.uid), installedInfo.newUsers);
+                    UserHandle.getAppId(installedInfo.uid), installedInfo.newUsers, null);
             }
         }
 
@@ -17368,18 +17424,18 @@
             extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid);
             extras.putBoolean(Intent.EXTRA_REPLACING, true);
             packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
-                removedPackage, extras, 0, null /*targetPackage*/, null, null);
+                removedPackage, extras, 0, null /*targetPackage*/, null, null, null);
             packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
-                removedPackage, extras, 0, null /*targetPackage*/, null, null);
+                removedPackage, extras, 0, null /*targetPackage*/, null, null, null);
             packageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
-                null, null, 0, removedPackage, null, null);
+                null, null, 0, removedPackage, null, null, null);
             if (installerPackageName != null) {
                 packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
                         removedPackage, extras, 0 /*flags*/,
-                        installerPackageName, null, null);
+                        installerPackageName, null, null, null);
                 packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
                         removedPackage, extras, 0 /*flags*/,
-                        installerPackageName, null, null);
+                        installerPackageName, null, null, null);
             }
         }
 
@@ -17400,23 +17456,24 @@
             extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, removedForAllUsers);
             if (removedPackage != null) {
                 packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
-                    removedPackage, extras, 0, null /*targetPackage*/, null, broadcastUsers);
+                    removedPackage, extras, 0, null /*targetPackage*/, null,
+                    broadcastUsers, instantUserIds);
                 if (installerPackageName != null) {
                     packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
                             removedPackage, extras, 0 /*flags*/,
-                            installerPackageName, null, broadcastUsers);
+                            installerPackageName, null, broadcastUsers, instantUserIds);
                 }
                 if (dataRemoved && !isRemovedPackageSystemUpdate) {
                     packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED,
                         removedPackage, extras,
                         Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
-                        null, null, broadcastUsers);
+                        null, null, broadcastUsers, instantUserIds);
                 }
             }
             if (removedAppId >= 0) {
                 packageSender.sendPackageBroadcast(Intent.ACTION_UID_REMOVED,
                     null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
-                    null, null, broadcastUsers);
+                    null, null, broadcastUsers, instantUserIds);
             }
         }
 
@@ -17428,12 +17485,14 @@
             }
 
             broadcastUsers = EMPTY_INT_ARRAY;
+            instantUserIds = EMPTY_INT_ARRAY;
             for (int i = userIds.length - 1; i >= 0; --i) {
                 final int userId = userIds[i];
                 if (deletedPackageSetting.getInstantApp(userId)) {
-                    continue;
+                    instantUserIds = ArrayUtils.appendInt(instantUserIds, userId);
+                } else {
+                    broadcastUsers = ArrayUtils.appendInt(broadcastUsers, userId);
                 }
-                broadcastUsers = ArrayUtils.appendInt(broadcastUsers, userId);
             }
         }
     }
@@ -19979,8 +20038,12 @@
         // little component state change.
         final int flags = !componentNames.contains(packageName)
                 ? Intent.FLAG_RECEIVER_REGISTERED_ONLY : 0;
+        final int userId = UserHandle.getUserId(packageUid);
+        final boolean isInstantApp = isInstantApp(packageName, userId);
+        final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
+        final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
         sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED,  packageName, extras, flags, null, null,
-                new int[] {UserHandle.getUserId(packageUid)});
+                userIds, instantUserIds);
     }
 
     @Override
@@ -21099,7 +21162,7 @@
             }
             String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
                     : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
-            sendPackageBroadcast(action, null, extras, 0, null, finishedReceiver, null);
+            sendPackageBroadcast(action, null, extras, 0, null, finishedReceiver, null, null);
         }
     }
 
@@ -23328,9 +23391,13 @@
 }
 
 interface PackageSender {
+    /**
+     * @param userIds User IDs where the action occurred on a full application
+     * @param instantUserIds User IDs where the action occurred on an instant application
+     */
     void sendPackageBroadcast(final String action, final String pkg,
         final Bundle extras, final int flags, final String targetPkg,
-        final IIntentReceiver finishedReceiver, final int[] userIds);
+        final IIntentReceiver finishedReceiver, final int[] userIds, int[] instantUserIds);
     void sendPackageAddedForNewUsers(String packageName, boolean sendBootCompleted,
-        boolean includeStopped, int appId, int... userIds);
+        boolean includeStopped, int appId, int[] userIds, int[] instantUserIds);
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index beffcee..e12a8da 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -43,11 +43,13 @@
       class PackageSenderImpl implements PackageSender {
         public void sendPackageBroadcast(final String action, final String pkg,
             final Bundle extras, final int flags, final String targetPkg,
-            final IIntentReceiver finishedReceiver, final int[] userIds) {
+            final IIntentReceiver finishedReceiver, final int[] userIds,
+            int[] instantUserIds) {
         }
 
         public void sendPackageAddedForNewUsers(String packageName,
-            boolean sendBootComplete, boolean includeStopped, int appId, int... userIds) {
+            boolean sendBootComplete, boolean includeStopped, int appId,
+            int[] userIds, int[] instantUserIds) {
         }
       }