Merge "Add getStagedSessionInfo binder call."
diff --git a/apexd/Android.bp b/apexd/Android.bp
index 2a9419d..a090e4b 100644
--- a/apexd/Android.bp
+++ b/apexd/Android.bp
@@ -31,6 +31,7 @@
   srcs: [
     "aidl/android/apex/ApexInfo.aidl",
     "aidl/android/apex/ApexInfoList.aidl",
+    "aidl/android/apex/ApexSessionInfo.aidl",
     "aidl/android/apex/IApexService.aidl",
   ],
   local_include_dir: "aidl",
diff --git a/apexd/aidl/android/apex/ApexSessionInfo.aidl b/apexd/aidl/android/apex/ApexSessionInfo.aidl
new file mode 100644
index 0000000..cca1ff9
--- /dev/null
+++ b/apexd/aidl/android/apex/ApexSessionInfo.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.apex;
+
+parcelable ApexSessionInfo {
+    // Maps to apex::proto::SessionState::State enum.
+    boolean isUnknown;
+    boolean isVerified;
+    boolean isStaged;
+    boolean isActivated;
+    boolean isActivationPendingRetry;
+    boolean isActivationFailed;
+}
diff --git a/apexd/aidl/android/apex/IApexService.aidl b/apexd/aidl/android/apex/IApexService.aidl
index b88c27b..a32816e 100644
--- a/apexd/aidl/android/apex/IApexService.aidl
+++ b/apexd/aidl/android/apex/IApexService.aidl
@@ -18,11 +18,13 @@
 
 import android.apex.ApexInfo;
 import android.apex.ApexInfoList;
+import android.apex.ApexSessionInfo;
 
 interface IApexService {
    boolean stagePackage(in @utf8InCpp String package_tmp_path);
    boolean stagePackages(in @utf8InCpp List<String> package_tmp_paths);
    boolean submitStagedSession(int session_id, out ApexInfoList packages);
+   ApexSessionInfo getStagedSessionInfo(int session_id);
    ApexInfo[] getActivePackages();
 
    /**
diff --git a/apexd/apexservice.cpp b/apexd/apexservice.cpp
index a43befd..066bef3 100644
--- a/apexd/apexservice.cpp
+++ b/apexd/apexservice.cpp
@@ -32,6 +32,7 @@
 #include <utils/String16.h>
 
 #include "apexd.h"
+#include "apexd_session.h"
 #include "status.h"
 #include "string_log.h"
 
@@ -56,6 +57,8 @@
                              bool* aidl_return) override;
   BinderStatus submitStagedSession(int session_id, ApexInfoList* apex_info_list,
                                    bool* aidl_return) override;
+  BinderStatus getStagedSessionInfo(
+      int session_id, ApexSessionInfo* apex_session_info) override;
   BinderStatus activatePackage(const std::string& packagePath) override;
   BinderStatus deactivatePackage(const std::string& packagePath) override;
   BinderStatus getActivePackages(std::vector<ApexInfo>* aidl_return) override;
@@ -131,6 +134,49 @@
   return BinderStatus::ok();
 }
 
+BinderStatus ApexService::getStagedSessionInfo(
+    int session_id, ApexSessionInfo* apex_session_info) {
+  LOG(DEBUG) << "getStagedSessionInfo() received by ApexService, session id "
+             << session_id;
+  apex_session_info->isUnknown = true;
+  apex_session_info->isVerified = false;
+  apex_session_info->isStaged = false;
+  apex_session_info->isActivated = false;
+  apex_session_info->isActivationPendingRetry = false;
+  apex_session_info->isActivationFailed = false;
+  auto state = readSessionState(session_id);
+  if (!state.Ok()) {
+    // Unknown session.
+    return BinderStatus::ok();
+  }
+
+  apex_session_info->isUnknown = false;
+  auto session_state = *state;
+  switch (session_state.state()) {
+    case session_state.VERIFIED:
+      apex_session_info->isVerified = true;
+      break;
+    case session_state.STAGED:
+      apex_session_info->isStaged = true;
+      break;
+    case session_state.ACTIVATED:
+      apex_session_info->isActivated = true;
+      break;
+    case session_state.ACTIVATION_PENDING_RETRY:
+      apex_session_info->isActivationPendingRetry = true;
+      break;
+    case session_state.ACTIVATION_FAILED:
+      apex_session_info->isActivationFailed = true;
+      break;
+    case session_state.UNKNOWN:
+    default:
+      apex_session_info->isUnknown = true;
+      break;
+  }
+
+  return BinderStatus::ok();
+}
+
 BinderStatus ApexService::activatePackage(const std::string& packagePath) {
   BinderStatus debugCheck = CheckDebuggable("activatePackage");
   if (!debugCheck.isOk()) {
@@ -259,6 +305,8 @@
         << "  deactivatePackage [packagePath] - deactivate package from the "
            "given path"
         << std::endl
+        << "  getStagedSessionInfo [sessionId] - displays information about a "
+           "given session previously submitted"
         << "  submitStagedSession [sessionId] - attempts to submit the "
            "installer session with given id"
         << std::endl;
@@ -377,6 +425,41 @@
     return BAD_VALUE;
   }
 
+  if (cmd == String16("getStagedSessionInfo")) {
+    if (args.size() != 2) {
+      print_help(err, "getStagedSessionInfo requires one session id");
+      return BAD_VALUE;
+    }
+    int session_id = strtol(String8(args[1]).c_str(), nullptr, 10);
+    if (session_id < 0) {
+      std::string msg = StringLog()
+                        << "Failed to parse session id. Must be an integer.";
+      dprintf(err, "%s", msg.c_str());
+      return BAD_VALUE;
+    }
+
+    ApexSessionInfo session_info;
+    BinderStatus status = getStagedSessionInfo(session_id, &session_info);
+    if (status.isOk()) {
+      std::string msg = StringLog()
+                        << "session_info: "
+                        << " isUnknown: " << session_info.isUnknown
+                        << " isVerified: " << session_info.isVerified
+                        << " isStaged: " << session_info.isStaged
+                        << " isActivated: " << session_info.isActivated
+                        << " isActivationPendingRetry: "
+                        << session_info.isActivationPendingRetry
+                        << " isActivationFailed: "
+                        << session_info.isActivationFailed << std::endl;
+      dprintf(out, "%s", msg.c_str());
+      return OK;
+    }
+    std::string msg = StringLog() << "Failed to query session: "
+                                  << status.toString8().string() << std::endl;
+    dprintf(err, "%s", msg.c_str());
+    return BAD_VALUE;
+  }
+
   if (cmd == String16("submitStagedSession")) {
     if (args.size() != 2) {
       print_help(err, "submitStagedSession requires one session id");
diff --git a/apexd/apexservice_test.cpp b/apexd/apexservice_test.cpp
index 24852e1..d10996f 100644
--- a/apexd/apexservice_test.cpp
+++ b/apexd/apexservice_test.cpp
@@ -701,12 +701,23 @@
   ASSERT_EQ(installer.package, match.packageName);
   ASSERT_EQ(installer.version, static_cast<uint64_t>(match.versionCode));
   ASSERT_EQ(installer.test_file, match.packagePath);
+
+  ApexSessionInfo session;
+  status = service_->getStagedSessionInfo(123, &session);
+  ASSERT_TRUE(status.isOk())
+      << status.toString8().c_str() << " " << GetDebugStr(&installer);
+  EXPECT_FALSE(session.isUnknown);
+  EXPECT_FALSE(session.isVerified);
+  EXPECT_TRUE(session.isStaged);
+  EXPECT_FALSE(session.isActivated);
+  EXPECT_FALSE(session.isActivationPendingRetry);
+  EXPECT_FALSE(session.isActivationFailed);
 }
 
 TEST_F(ApexServiceTest, SubmitSessionTestFail) {
   PrepareTestApexForInstall installer(
       GetTestFile("apex.apexd_test_no_inst_key.apex"),
-      "/data/staging/session_123", "staging_data_file");
+      "/data/staging/session_456", "staging_data_file");
   if (!installer.Prepare()) {
     FAIL() << GetDebugStr(&installer);
   }
@@ -714,11 +725,22 @@
   ApexInfoList list;
   bool ret_value;
   android::binder::Status status =
-      service_->submitStagedSession(123, &list, &ret_value);
+      service_->submitStagedSession(456, &list, &ret_value);
 
   ASSERT_TRUE(status.isOk())
       << status.toString8().c_str() << " " << GetDebugStr(&installer);
   EXPECT_FALSE(ret_value);
+
+  ApexSessionInfo session;
+  status = service_->getStagedSessionInfo(456, &session);
+  ASSERT_TRUE(status.isOk())
+      << status.toString8().c_str() << " " << GetDebugStr(&installer);
+  EXPECT_TRUE(session.isUnknown);
+  EXPECT_FALSE(session.isVerified);
+  EXPECT_FALSE(session.isStaged);
+  EXPECT_FALSE(session.isActivated);
+  EXPECT_FALSE(session.isActivationPendingRetry);
+  EXPECT_FALSE(session.isActivationFailed);
 }
 
 class LogTestToLogcat : public testing::EmptyTestEventListener {