Merge "Sending explicit broadcast to the launcher when a package is installed"
diff --git a/api/current.txt b/api/current.txt
index 3710b29..221c55b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10166,9 +10166,11 @@
     method public void unregisterSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
     method public void updateSessionAppIcon(int, android.graphics.Bitmap);
     method public void updateSessionAppLabel(int, java.lang.CharSequence);
+    field public static final java.lang.String ACTION_SESSION_COMMITTED = "android.content.pm.action.SESSION_COMMITTED";
     field public static final java.lang.String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
     field public static final java.lang.String EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME";
     field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
+    field public static final java.lang.String EXTRA_SESSION = "android.content.pm.extra.SESSION";
     field public static final java.lang.String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID";
     field public static final java.lang.String EXTRA_STATUS = "android.content.pm.extra.STATUS";
     field public static final java.lang.String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
@@ -10211,6 +10213,7 @@
     method public android.graphics.Bitmap getAppIcon();
     method public java.lang.CharSequence getAppLabel();
     method public java.lang.String getAppPackageName();
+    method public int getInstallReason();
     method public java.lang.String getInstallerPackageName();
     method public float getProgress();
     method public int getSessionId();
diff --git a/api/system-current.txt b/api/system-current.txt
index 4f89f0c..51e35dd 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -10703,9 +10703,11 @@
     method public void unregisterSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
     method public void updateSessionAppIcon(int, android.graphics.Bitmap);
     method public void updateSessionAppLabel(int, java.lang.CharSequence);
+    field public static final java.lang.String ACTION_SESSION_COMMITTED = "android.content.pm.action.SESSION_COMMITTED";
     field public static final java.lang.String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
     field public static final java.lang.String EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME";
     field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
+    field public static final java.lang.String EXTRA_SESSION = "android.content.pm.extra.SESSION";
     field public static final java.lang.String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID";
     field public static final java.lang.String EXTRA_STATUS = "android.content.pm.extra.STATUS";
     field public static final java.lang.String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
@@ -10748,6 +10750,7 @@
     method public android.graphics.Bitmap getAppIcon();
     method public java.lang.CharSequence getAppLabel();
     method public java.lang.String getAppPackageName();
+    method public int getInstallReason();
     method public java.lang.String getInstallerPackageName();
     method public float getProgress();
     method public int getSessionId();
diff --git a/api/test-current.txt b/api/test-current.txt
index a33bb8f..befc97f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -10198,9 +10198,11 @@
     method public void unregisterSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
     method public void updateSessionAppIcon(int, android.graphics.Bitmap);
     method public void updateSessionAppLabel(int, java.lang.CharSequence);
+    field public static final java.lang.String ACTION_SESSION_COMMITTED = "android.content.pm.action.SESSION_COMMITTED";
     field public static final java.lang.String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
     field public static final java.lang.String EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME";
     field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
+    field public static final java.lang.String EXTRA_SESSION = "android.content.pm.extra.SESSION";
     field public static final java.lang.String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID";
     field public static final java.lang.String EXTRA_STATUS = "android.content.pm.extra.STATUS";
     field public static final java.lang.String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
@@ -10243,6 +10245,7 @@
     method public android.graphics.Bitmap getAppIcon();
     method public java.lang.CharSequence getAppLabel();
     method public java.lang.String getAppPackageName();
+    method public int getInstallReason();
     method public java.lang.String getInstallerPackageName();
     method public float getProgress();
     method public int getSessionId();
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 1aef363..91520f1 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -547,6 +547,12 @@
                         throw new IllegalArgumentException("Missing inherit package name");
                     }
                     break;
+                case "--pkg":
+                    sessionParams.appPackageName = nextOptionData();
+                    if (sessionParams.appPackageName == null) {
+                        throw new IllegalArgumentException("Missing package name");
+                    }
+                    break;
                 case "-S":
                     final long sizeBytes = Long.parseLong(nextOptionData());
                     if (sizeBytes <= 0) {
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 278a6d0..9d04cc9 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -95,6 +95,18 @@
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
 
+    /**
+     * Broadcast Action: Explicit broadcast sent to the last known default launcher when a session
+     * for a new install is committed. For managed profile, this is sent to the default launcher
+     * of the primary profile.
+     * <p>
+     * The associated session is defined in {@link #EXTRA_SESSION} and the user for which this
+     * session was created in {@link Intent#EXTRA_USER}.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SESSION_COMMITTED =
+            "android.content.pm.action.SESSION_COMMITTED";
+
     /** {@hide} */
     public static final String
             ACTION_CONFIRM_PERMISSIONS = "android.content.pm.action.CONFIRM_PERMISSIONS";
@@ -107,6 +119,13 @@
     public static final String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID";
 
     /**
+     * {@link SessionInfo} that an operation is working with.
+     *
+     * @see Intent#getParcelableExtra(String)
+     */
+    public static final String EXTRA_SESSION = "android.content.pm.extra.SESSION";
+
+    /**
      * Package name that an operation is working with.
      *
      * @see Intent#getStringExtra(String)
@@ -1184,6 +1203,8 @@
         /** {@hide} */
         public int mode;
         /** {@hide} */
+        public int installReason;
+        /** {@hide} */
         public long sizeBytes;
         /** {@hide} */
         public String appPackageName;
@@ -1206,6 +1227,7 @@
             active = source.readInt() != 0;
 
             mode = source.readInt();
+            installReason = source.readInt();
             sizeBytes = source.readLong();
             appPackageName = source.readString();
             appIcon = source.readParcelable(null);
@@ -1256,6 +1278,15 @@
             return active;
         }
 
+        /**
+         * Return the reason for installing this package.
+         *
+         * @see PackageManager#INSTALL_REASON_UNKNOWN
+         */
+        public int getInstallReason() {
+            return installReason;
+        }
+
         /** {@hide} */
         @Deprecated
         public boolean isOpen() {
@@ -1324,6 +1355,7 @@
             dest.writeInt(active ? 1 : 0);
 
             dest.writeInt(mode);
+            dest.writeInt(installReason);
             dest.writeLong(sizeBytes);
             dest.writeString(appPackageName);
             dest.writeParcelable(appIcon, flags);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 054fad2..54d7249 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -522,6 +522,8 @@
     <protected-broadcast android:name="android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED" />
     <protected-broadcast android:name="com.android.server.wm.ACTION_REVOKE_SYSTEM_ALERT_WINDOW_PERMISSION" />
 
+    <protected-broadcast android:name="android.content.pm.action.SESSION_COMMITTED" />
+
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
     <!-- ====================================================================== -->
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 1c5675a..fd731c3 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -29,6 +29,7 @@
 import static com.android.server.pm.PackageInstallerService.prepareStageDir;
 
 import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
@@ -45,6 +46,7 @@
 import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.Signature;
+import android.content.pm.UserInfo;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.FileBridge;
@@ -295,6 +297,7 @@
             info.active = mActiveCount.get() > 0;
 
             info.mode = params.mode;
+            info.installReason = params.installReason;
             info.sizeBytes = params.sizeBytes;
             info.appPackageName = params.appPackageName;
             info.appIcon = params.appIcon;
@@ -1139,6 +1142,25 @@
         }
 
         final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
+
+        // Send broadcast to default launcher only if it's a new install
+        final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);
+        if (success && isNewInstall) {
+            UserManagerService ums = UserManagerService.getInstance();
+            if (ums != null) {
+                final UserInfo parent = ums.getProfileParent(userId);
+                final int launcherUid = (parent != null) ? parent.id : userId;
+                final ComponentName launcherComponent = mPm.getDefaultHomeActivity(launcherUid);
+                if (launcherComponent != null) {
+                    Intent launcherIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED)
+                            .putExtra(PackageInstaller.EXTRA_SESSION, generateInfo())
+                            .putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
+                            .setPackage(launcherComponent.getPackageName());
+                    mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUid));
+                }
+            }
+        }
+
         mCallback.onSessionFinished(this, success);
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 5ebced8..06f8077 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -19645,6 +19645,35 @@
         return getHomeActivitiesAsUser(allHomeCandidates, UserHandle.getCallingUserId());
     }
 
+    /**
+     * Report the 'Home' activity which is currently set as "always use this one". If non is set
+     * then reports the most likely home activity or null if there are more than one.
+     */
+    public ComponentName getDefaultHomeActivity(int userId) {
+        List<ResolveInfo> allHomeCandidates = new ArrayList<>();
+        ComponentName cn = getHomeActivitiesAsUser(allHomeCandidates, userId);
+        if (cn != null) {
+            return cn;
+        }
+
+        // Find the launcher with the highest priority and return that component if there are no
+        // other home activity with the same priority.
+        int lastPriority = Integer.MIN_VALUE;
+        ComponentName lastComponent = null;
+        final int size = allHomeCandidates.size();
+        for (int i = 0; i < size; i++) {
+            final ResolveInfo ri = allHomeCandidates.get(i);
+            if (ri.priority > lastPriority) {
+                lastComponent = ri.activityInfo.getComponentName();
+                lastPriority = ri.priority;
+            } else if (ri.priority == lastPriority) {
+                // Two components found with same priority.
+                lastComponent = null;
+            }
+        }
+        return lastComponent;
+    }
+
     private Intent getHomeIntent() {
         Intent intent = new Intent(Intent.ACTION_MAIN);
         intent.addCategory(Intent.CATEGORY_HOME);