Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a44686c4 authored by Gavin Corkery's avatar Gavin Corkery
Browse files

Add retrieveBugreport dumpstate implementation

Adds the native logic necessary for handling the
DEFER_CONSENT flag when calling startBugreport, and
handling the retrieveBugreport API.

When calling startBugreport with the DEFER_CONSENT flag
set, the consent flow will not be invoked. Instead, the
bugreport files will be stored in the bugreport internal
directories and will only be passed to the caller's file
descriptors when the same caller invokes retrieveBugreport
at a later time.

Test: atest CtsRootBugreportTestCases
Test: atest dumpstate_test
Bug: 245328405
Change-Id: I3a46482e173c6084ebda96f08ec60a31953d5f94
parent 43736725
Loading
Loading
Loading
Loading
+42 −0
Original line number Diff line number Diff line
@@ -58,6 +58,13 @@ static binder::Status exception(uint32_t code, const std::string& msg,
    exit(0);
}

[[noreturn]] static void* dumpstate_thread_retrieve(void* data) {
    std::unique_ptr<DumpstateInfo> ds_info(static_cast<DumpstateInfo*>(data));
    ds_info->ds->Retrieve(ds_info->calling_uid, ds_info->calling_package);
    MYLOGD("Finished retrieving a bugreport. Exiting.\n");
    exit(0);
}

[[noreturn]] static void signalErrorAndExit(sp<IDumpstateListener> listener, int error_code) {
    listener->onError(error_code);
    exit(0);
@@ -192,6 +199,41 @@ binder::Status DumpstateService::cancelBugreport(int32_t calling_uid,
    return binder::Status::ok();
}

binder::Status DumpstateService::retrieveBugreport(
    int32_t calling_uid, const std::string& calling_package,
    android::base::unique_fd bugreport_fd,
    const std::string& bugreport_file,
    const sp<IDumpstateListener>& listener) {

    ds_ = &(Dumpstate::GetInstance());
    DumpstateInfo* ds_info = new DumpstateInfo();
    ds_info->ds = ds_;
    ds_info->calling_uid = calling_uid;
    ds_info->calling_package = calling_package;
    ds_->listener_ = listener;
    std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>();
    // Use a /dev/null FD when initializing options since none is provided.
    android::base::unique_fd devnull_fd(
        TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY | O_CLOEXEC)));

    options->Initialize(Dumpstate::BugreportMode::BUGREPORT_DEFAULT,
                        0, bugreport_fd, devnull_fd, false);

    if (bugreport_fd.get() == -1) {
        MYLOGE("Invalid filedescriptor");
        signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
    }
    ds_->SetOptions(std::move(options));
    ds_->path_ = bugreport_file;
    pthread_t thread;
    status_t err = pthread_create(&thread, nullptr, dumpstate_thread_retrieve, ds_info);
    if (err != 0) {
        MYLOGE("Could not create a thread");
        signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR);
    }
    return binder::Status::ok();
}

status_t DumpstateService::dump(int fd, const Vector<String16>&) {
    std::lock_guard<std::mutex> lock(lock_);
    if (ds_ == nullptr) {
+7 −0
Original line number Diff line number Diff line
@@ -46,6 +46,13 @@ class DumpstateService : public BinderService<DumpstateService>, public BnDumpst
                                  int bugreport_flags, const sp<IDumpstateListener>& listener,
                                  bool is_screenshot_requested) override;

    binder::Status retrieveBugreport(int32_t calling_uid,
                                     const std::string& calling_package,
                                     android::base::unique_fd bugreport_fd,
                                     const std::string& bugreport_file,
                                     const sp<IDumpstateListener>& listener)
                                     override;

    binder::Status cancelBugreport(int32_t calling_uid,
                                   const std::string& calling_package) override;

+22 −1
Original line number Diff line number Diff line
@@ -50,7 +50,10 @@ interface IDumpstate {
    const int BUGREPORT_MODE_DEFAULT = 6;

    // Use pre-dumped data.
    const int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 1;
    const int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 0x1;

    // Defer user consent.
    const int BUGREPORT_FLAG_DEFER_CONSENT = 0x2;

    /**
     * Speculatively pre-dumps UI data for a bugreport request that might come later.
@@ -100,4 +103,22 @@ interface IDumpstate {
     * @param callingPackage package of the original application that requested the cancellation.
     */
    void cancelBugreport(int callingUid, @utf8InCpp String callingPackage);

    /**
     * Retrieves a previously generated bugreport.
     *
     * <p>The caller must have previously generated a bugreport using
     * {@link #startBugreport} with the {@link BUGREPORT_FLAG_DEFER_CONSENT}
     * flag set.
     *
     * @param callingUid UID of the original application that requested the report.
     * @param callingPackage package of the original application that requested the report.
     * @param bugreportFd the file to which the zipped bugreport should be written
     * @param bugreportFile the path of the bugreport file
     * @param listener callback for updates; optional
     */
    void retrieveBugreport(int callingUid, @utf8InCpp String callingPackage,
                           FileDescriptor bugreportFd,
                           @utf8InCpp String bugreportFile,
                           IDumpstateListener listener);
}
+6 −1
Original line number Diff line number Diff line
@@ -50,6 +50,9 @@ interface IDumpstateListener {
    /* There is currently a bugreport running. The caller should try again later. */
    const int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS = 5;

    /* There is no bugreport to retrieve for the given caller. */
    const int BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE = 6;

    /**
     * Called on an error condition with one of the error codes listed above.
     */
@@ -57,8 +60,10 @@ interface IDumpstateListener {

    /**
     * Called when taking bugreport finishes successfully.
     *
     * @param bugreportFile The location of the bugreport file
     */
    oneway void onFinished();
    oneway void onFinished(@utf8InCpp String bugreportFile);

    /**
     * Called when screenshot is taken.
+63 −8
Original line number Diff line number Diff line
@@ -2825,6 +2825,7 @@ void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode,
                                        const android::base::unique_fd& screenshot_fd_in,
                                        bool is_screenshot_requested) {
    this->use_predumped_ui_data = bugreport_flags & BugreportFlag::BUGREPORT_USE_PREDUMPED_UI_DATA;
    this->is_consent_deferred = bugreport_flags & BugreportFlag::BUGREPORT_FLAG_DEFER_CONSENT;
    // Duplicate the fds because the passed in fds don't outlive the binder transaction.
    bugreport_fd.reset(fcntl(bugreport_fd_in.get(), F_DUPFD_CLOEXEC, 0));
    screenshot_fd.reset(fcntl(screenshot_fd_in.get(), F_DUPFD_CLOEXEC, 0));
@@ -2907,10 +2908,64 @@ void Dumpstate::Initialize() {

Dumpstate::RunStatus Dumpstate::Run(int32_t calling_uid, const std::string& calling_package) {
    Dumpstate::RunStatus status = RunInternal(calling_uid, calling_package);
    HandleRunStatus(status);
    return status;
}

Dumpstate::RunStatus Dumpstate::Retrieve(int32_t calling_uid, const std::string& calling_package) {
    Dumpstate::RunStatus status = RetrieveInternal(calling_uid, calling_package);
    HandleRunStatus(status);
    return status;
}

Dumpstate::RunStatus  Dumpstate::RetrieveInternal(int32_t calling_uid,
                                                  const std::string& calling_package) {
  consent_callback_ = new ConsentCallback();
  const String16 incidentcompanion("incidentcompanion");
  sp<android::IBinder> ics(
      defaultServiceManager()->checkService(incidentcompanion));
  android::String16 package(calling_package.c_str());
  if (ics != nullptr) {
    MYLOGD("Checking user consent via incidentcompanion service\n");
    android::interface_cast<android::os::IIncidentCompanion>(ics)->authorizeReport(
        calling_uid, package, String16(), String16(),
        0x1 /* FLAG_CONFIRMATION_DIALOG */, consent_callback_.get());
  } else {
    MYLOGD(
        "Unable to check user consent; incidentcompanion service unavailable\n");
    return RunStatus::USER_CONSENT_TIMED_OUT;
  }
  UserConsentResult consent_result = consent_callback_->getResult();
  int timeout_ms = 30 * 1000;
  while (consent_result == UserConsentResult::UNAVAILABLE &&
      consent_callback_->getElapsedTimeMs() < timeout_ms) {
    sleep(1);
    consent_result = consent_callback_->getResult();
  }
  if (consent_result == UserConsentResult::DENIED) {
    return RunStatus::USER_CONSENT_DENIED;
  }
  if (consent_result == UserConsentResult::UNAVAILABLE) {
    MYLOGD("Canceling user consent request via incidentcompanion service\n");
    android::interface_cast<android::os::IIncidentCompanion>(ics)->cancelAuthorization(
        consent_callback_.get());
    return RunStatus::USER_CONSENT_TIMED_OUT;
  }

  bool copy_succeeded =
      android::os::CopyFileToFd(path_, options_->bugreport_fd.get());
  if (copy_succeeded) {
    android::os::UnlinkAndLogOnError(path_);
  }
  return copy_succeeded ? Dumpstate::RunStatus::OK
                        : Dumpstate::RunStatus::ERROR;
}

void Dumpstate::HandleRunStatus(Dumpstate::RunStatus status) {
      if (listener_ != nullptr) {
        switch (status) {
            case Dumpstate::RunStatus::OK:
                listener_->onFinished();
                listener_->onFinished(path_.c_str());
                break;
            case Dumpstate::RunStatus::HELP:
                break;
@@ -2928,9 +2983,7 @@ Dumpstate::RunStatus Dumpstate::Run(int32_t calling_uid, const std::string& call
                break;
        }
    }
    return status;
}

void Dumpstate::Cancel() {
    CleanupTmpFiles();
    android::os::UnlinkAndLogOnError(log_path_);
@@ -3181,7 +3234,7 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,

    // Share the final file with the caller if the user has consented or Shell is the caller.
    Dumpstate::RunStatus status = Dumpstate::RunStatus::OK;
    if (CalledByApi()) {
    if (CalledByApi() && !options_->is_consent_deferred) {
        status = CopyBugreportIfUserConsented(calling_uid);
        if (status != Dumpstate::RunStatus::OK &&
            status != Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT) {
@@ -3326,9 +3379,11 @@ void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid) {
}

void Dumpstate::MaybeCheckUserConsent(int32_t calling_uid, const std::string& calling_package) {
    if (multiuser_get_app_id(calling_uid) == AID_SHELL || !CalledByApi()) {
        // No need to get consent for shell triggered dumpstates, or not through
        // bugreporting API (i.e. no fd to copy back).
    if (multiuser_get_app_id(calling_uid) == AID_SHELL ||
        !CalledByApi() || options_->is_consent_deferred) {
        // No need to get consent for shell triggered dumpstates, or not
        // through bugreporting API (i.e. no fd to copy back), or when consent
        // is deferred.
        return;
    }
    consent_callback_ = new ConsentCallback();
Loading