Merge "Add an app stopped state parameter to ApplicationStartInfo" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 2d03be7..301dbfa 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5329,6 +5329,7 @@
     method public int getStartType();
     method public int getStartupState();
     method @NonNull public java.util.Map<java.lang.Integer,java.lang.Long> getStartupTimestamps();
+    method @FlaggedApi("android.content.pm.stay_stopped") public boolean wasForceStopped();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.ApplicationStartInfo> CREATOR;
     field public static final int LAUNCH_MODE_SINGLE_INSTANCE = 2; // 0x2
diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java
index c6712c0..3715c6e 100644
--- a/core/java/android/app/ApplicationStartInfo.java
+++ b/core/java/android/app/ApplicationStartInfo.java
@@ -22,6 +22,7 @@
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.icu.text.SimpleDateFormat;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -249,7 +250,7 @@
     private @StartType int mStartType;
 
     /**
-     * @see #getStartIntent
+     * @see #getIntent
      */
     private Intent mStartIntent;
 
@@ -259,6 +260,11 @@
     private @LaunchMode int mLaunchMode;
 
     /**
+     * @see #wasForceStopped()
+     */
+    private boolean mWasForceStopped;
+
+    /**
      * @hide *
      */
     @IntDef(
@@ -427,6 +433,15 @@
     }
 
     /**
+     * @see #wasForceStopped()
+     * @param wasForceStopped whether the app had been force-stopped in the past
+     * @hide
+     */
+    public void setForceStopped(boolean wasForceStopped) {
+        mWasForceStopped = wasForceStopped;
+    }
+
+    /**
      * Current state of startup.
      *
      * Can be used to determine whether the object will have additional fields added as it may be
@@ -578,6 +593,20 @@
         return mLaunchMode;
     }
 
+    /**
+     * Informs whether this is the first process launch for an app since it was
+     * {@link ApplicationInfo#FLAG_STOPPED force-stopped} for some reason.
+     * This allows the app to know if it should re-register for any alarms, jobs and other callbacks
+     * that were cleared when the app was force-stopped.
+     *
+     * @return {@code true} if this is the first process launch of the app after having been
+     *      stopped, {@code false} otherwise.
+     */
+    @FlaggedApi(android.content.pm.Flags.FLAG_STAY_STOPPED)
+    public boolean wasForceStopped() {
+        return mWasForceStopped;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -603,6 +632,7 @@
         dest.writeInt(mStartType);
         dest.writeParcelable(mStartIntent, flags);
         dest.writeInt(mLaunchMode);
+        dest.writeBoolean(mWasForceStopped);
     }
 
     /** @hide */
@@ -622,6 +652,7 @@
         mStartType = other.mStartType;
         mStartIntent = other.mStartIntent;
         mLaunchMode = other.mLaunchMode;
+        mWasForceStopped = other.mWasForceStopped;
     }
 
     private ApplicationStartInfo(@NonNull Parcel in) {
@@ -643,6 +674,7 @@
         mStartIntent =
                 in.readParcelable(Intent.class.getClassLoader(), android.content.Intent.class);
         mLaunchMode = in.readInt();
+        mWasForceStopped = in.readBoolean();
     }
 
     private static String intern(@Nullable String source) {
@@ -720,6 +752,7 @@
             intentOut.close();
         }
         proto.write(ApplicationStartInfoProto.LAUNCH_MODE, mLaunchMode);
+        proto.write(ApplicationStartInfoProto.WAS_FORCE_STOPPED, mWasForceStopped);
         proto.end(token);
     }
 
@@ -799,6 +832,10 @@
                 case (int) ApplicationStartInfoProto.LAUNCH_MODE:
                     mLaunchMode = proto.readInt(ApplicationStartInfoProto.LAUNCH_MODE);
                     break;
+                case (int) ApplicationStartInfoProto.WAS_FORCE_STOPPED:
+                    mWasForceStopped = proto.readBoolean(
+                            ApplicationStartInfoProto.WAS_FORCE_STOPPED);
+                    break;
             }
         }
         proto.end(token);
@@ -823,6 +860,7 @@
                 .append(" reason=").append(reasonToString(mReason))
                 .append(" startType=").append(startTypeToString(mStartType))
                 .append(" launchMode=").append(mLaunchMode)
+                .append(" wasForceStopped=").append(mWasForceStopped)
                 .append('\n');
         if (mStartIntent != null) {
             sb.append(" intent=").append(mStartIntent.toString())
@@ -878,7 +916,7 @@
             && mDefiningUid == o.mDefiningUid && mReason == o.mReason
             && mStartupState == o.mStartupState && mStartType == o.mStartType
             && mLaunchMode == o.mLaunchMode && TextUtils.equals(mProcessName, o.mProcessName)
-            && timestampsEquals(o);
+            && timestampsEquals(o) && mWasForceStopped == o.mWasForceStopped;
     }
 
     @Override
diff --git a/core/proto/android/app/appstartinfo.proto b/core/proto/android/app/appstartinfo.proto
index 8c33041..d9ed911 100644
--- a/core/proto/android/app/appstartinfo.proto
+++ b/core/proto/android/app/appstartinfo.proto
@@ -39,4 +39,5 @@
     optional AppStartStartType start_type = 9;
     optional bytes start_intent = 10;
     optional AppStartLaunchMode launch_mode = 11;
+    optional bool was_force_stopped = 12;
 }
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index c857235..1dc384d 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -383,6 +383,11 @@
         start.setDefiningUid(definingUid > 0 ? definingUid : app.info.uid);
         start.setProcessName(app.processName);
         start.setPackageName(app.info.packageName);
+        if (android.content.pm.Flags.stayStopped()) {
+            // TODO: Verify this is created at the right time to have the correct force-stopped
+            // state in the ProcessRecord. Also use the WindowProcessRecord if activity.
+            start.setForceStopped(app.wasForceStopped());
+        }
     }
 
     void reportApplicationOnCreateTimeNanos(ProcessRecord app, long timeNs) {