Unavailable DataLoader status.

This is a temporary failure, does not fail the session, but requires
caller to re-commit. E.g. there are connectivity issues which can be
fixed later.

Bug: b/153874006
Test: atest PackageManagerShellCommandTest PackageManagerShellCommandIncrementalTest IncrementalServiceTest
Change-Id: I02791a2963130dbecb510c4a7cafcf04f6245761
diff --git a/core/java/android/content/pm/IDataLoaderStatusListener.aidl b/core/java/android/content/pm/IDataLoaderStatusListener.aidl
index 24a62c5..efb00a0 100644
--- a/core/java/android/content/pm/IDataLoaderStatusListener.aidl
+++ b/core/java/android/content/pm/IDataLoaderStatusListener.aidl
@@ -40,11 +40,15 @@
     /** Installation can't continue as DataLoader failed to stream necessary data. */
     const int DATA_LOADER_IMAGE_NOT_READY = 6;
 
+    /** DataLoader instance can't run at the moment, but might recover later.
+     *  It's up to system to decide if the app is still usable. */
+    const int DATA_LOADER_UNAVAILABLE = 7;
+
     /** DataLoader reports that this instance is invalid and can never be restored.
     *   Warning: this is a terminal status that data loader should use carefully and
     *            the system should almost never use - e.g. only if all recovery attempts
     *            fail and all retry limits are exceeded. */
-    const int DATA_LOADER_UNRECOVERABLE = 7;
+    const int DATA_LOADER_UNRECOVERABLE = 8;
 
     /** Data loader status callback */
     void onStatusChanged(in int dataLoaderId, in int status);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index d9275f5..9c0ac18 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2691,17 +2691,23 @@
                             }
                             break;
                         }
+                        case IDataLoaderStatusListener.DATA_LOADER_UNAVAILABLE: {
+                            // Don't fail or commit the session. Allow caller to commit again.
+                            sendPendingStreaming(mContext, mRemoteStatusReceiver, sessionId,
+                                    "DataLoader unavailable");
+                            break;
+                        }
                         case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE:
                             mDataLoaderFinished = true;
                             dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
                                     "DataLoader reported unrecoverable failure.");
-                            return;
+                            break;
                     }
                 } catch (RemoteException e) {
                     // In case of streaming failure we don't want to fail or commit the session.
                     // Just return from this method and allow caller to commit again.
                     sendPendingStreaming(mContext, mRemoteStatusReceiver, sessionId,
-                            new StreamingException(e));
+                            e.getMessage());
                 }
             }
         };
@@ -3059,13 +3065,13 @@
     }
 
     private static void sendPendingStreaming(Context context, IntentSender target, int sessionId,
-            Throwable cause) {
+            @Nullable String cause) {
         final Intent intent = new Intent();
         intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
         intent.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_STREAMING);
-        if (cause != null && !TextUtils.isEmpty(cause.getMessage())) {
+        if (!TextUtils.isEmpty(cause)) {
             intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
-                    "Staging Image Not Ready [" + cause.getMessage() + "]");
+                    "Staging Image Not Ready [" + cause + "]");
         } else {
             intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, "Staging Image Not Ready");
         }
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 1036858..992a4ef 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -1708,15 +1708,19 @@
     {
         std::unique_lock lock(mStatusMutex);
         oldStatus = mTargetStatus;
-        mTargetStatus = newStatus;
-        mTargetStatusTs = Clock::now();
         curStatus = mCurrentStatus;
+        setTargetStatusLocked(newStatus);
     }
     LOG(DEBUG) << "Target status update for DataLoader " << mId << ": " << oldStatus << " -> "
                << newStatus << " (current " << curStatus << ")";
     return fsmStep();
 }
 
+void IncrementalService::DataLoaderStub::setTargetStatusLocked(int status) {
+    mTargetStatus = status;
+    mTargetStatusTs = Clock::now();
+}
+
 bool IncrementalService::DataLoaderStub::waitForStatus(int status, Clock::duration duration) {
     auto now = Clock::now();
     std::unique_lock lock(mStatusMutex);
@@ -1782,6 +1786,9 @@
     }
 
     switch (targetStatus) {
+        case IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE:
+            // Do nothing, this is a reset state.
+            break;
         case IDataLoaderStatusListener::DATA_LOADER_DESTROYED: {
             return destroy();
         }
@@ -1796,6 +1803,7 @@
         case IDataLoaderStatusListener::DATA_LOADER_CREATED:
             switch (currentStatus) {
                 case IDataLoaderStatusListener::DATA_LOADER_DESTROYED:
+                case IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE:
                     return bind();
                 case IDataLoaderStatusListener::DATA_LOADER_BOUND:
                     return create();
@@ -1825,9 +1833,15 @@
         if (mCurrentStatus == newStatus) {
             return binder::Status::ok();
         }
+
         oldStatus = mCurrentStatus;
         mCurrentStatus = newStatus;
         targetStatus = mTargetStatus;
+
+        if (mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE) {
+            // For unavailable, reset target status.
+            setTargetStatusLocked(IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE);
+        }
     }
 
     LOG(DEBUG) << "Current status update for DataLoader " << mId << ": " << oldStatus << " -> "
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 81fbe74..d5c612d 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -187,6 +187,7 @@
         bool destroy();
 
         bool setTargetStatus(int status);
+        void setTargetStatusLocked(int status);
         bool waitForStatus(int status, Clock::duration duration);
 
         bool fsmStep();