aboutsummaryrefslogtreecommitdiff
path: root/pw_software_update/public/pw_software_update/bundled_update_service.h
blob: e24a032da3ebccc4a117bcf4988c923273a1b3fe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// Copyright 2021 The Pigweed Authors
//
// 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
//
//     https://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.

#pragma once

#include "pw_software_update/bundled_update.rpc.pb.h"
#include "pw_software_update/bundled_update_backend.h"
#include "pw_software_update/update_bundle_accessor.h"
#include "pw_status/status.h"
#include "pw_sync/borrow.h"
#include "pw_sync/lock_annotations.h"
#include "pw_sync/mutex.h"
#include "pw_work_queue/work_queue.h"

namespace pw::software_update {

// Implementation class for pw.software_update.BundledUpdate.
// See bundled_update.proto for RPC method documentation.
class BundledUpdateService
    : public pw_rpc::nanopb::BundledUpdate::Service<BundledUpdateService> {
 public:
  PW_MODIFY_DIAGNOSTICS_PUSH();
  PW_MODIFY_DIAGNOSTIC(ignored, "-Wmissing-field-initializers");
  BundledUpdateService(UpdateBundleAccessor& bundle,
                       BundledUpdateBackend& backend,
                       work_queue::WorkQueue& work_queue)
      : unsafe_status_{.state =
                           pw_software_update_BundledUpdateState_Enum_INACTIVE},
        status_(unsafe_status_, status_mutex_),
        backend_(backend),
        bundle_(bundle),
        bundle_open_(false),
        work_queue_(work_queue),
        work_enqueued_(false) {}
  PW_MODIFY_DIAGNOSTICS_POP();

  Status GetStatus(const pw_protobuf_Empty& request,
                   pw_software_update_BundledUpdateStatus& response);

  // Sync
  Status Start(const pw_software_update_StartRequest& request,
               pw_software_update_BundledUpdateStatus& response);

  // Sync
  Status SetTransferred(const pw_protobuf_Empty& request,
                        pw_software_update_BundledUpdateStatus& response);

  // Async
  Status Verify(const pw_protobuf_Empty& request,
                pw_software_update_BundledUpdateStatus& response);

  // Async
  Status Apply(const pw_protobuf_Empty& request,
               pw_software_update_BundledUpdateStatus& response);

  // Currently sync, should be async.
  // TODO(keir): Make this async to support aborting verify/apply.
  Status Abort(const pw_protobuf_Empty& request,
               pw_software_update_BundledUpdateStatus& response);

  // Sync
  Status Reset(const pw_protobuf_Empty& request,
               pw_software_update_BundledUpdateStatus& response);

  // Notify the service that the bundle transfer has completed. The service has
  // no way to know when the bundle transfer completes, so users must invoke
  // this method in their transfer completion handler.
  //
  // After this call, the service will be in TRANSFERRED state if and only if
  // it was in the TRANSFERRING state.
  void NotifyTransferSucceeded();

  // TODO(davidrogers) Add a MaybeFinishApply() method that is called after
  // reboot to finish any need apply and verify work.

  // TODO(keir): VerifyProgress - to update % complete.
  // TODO(keir): ApplyProgress - to update % complete.

 private:
  // Top-level lock for OTA state coherency. May be held for extended periods.
  sync::Mutex mutex_;
  // Nested lock for safe status updates and queries.
  sync::Mutex status_mutex_ PW_ACQUIRED_AFTER(mutex_);
  pw_software_update_BundledUpdateStatus unsafe_status_
      PW_GUARDED_BY(status_mutex_);
  sync::Borrowable<pw_software_update_BundledUpdateStatus, sync::Mutex> status_;
  BundledUpdateBackend& backend_ PW_GUARDED_BY(mutex_);
  UpdateBundleAccessor& bundle_ PW_GUARDED_BY(mutex_);
  bool bundle_open_ PW_GUARDED_BY(mutex_);
  work_queue::WorkQueue& work_queue_ PW_GUARDED_BY(mutex_);
  bool work_enqueued_ PW_GUARDED_BY(mutex_);

  void DoVerify() PW_LOCKS_EXCLUDED(status_mutex_);
  void DoApply() PW_LOCKS_EXCLUDED(status_mutex_);
  void Finish(_pw_software_update_BundledUpdateResult_Enum result)
      PW_EXCLUSIVE_LOCKS_REQUIRED(mutex_) PW_LOCKS_EXCLUDED(status_mutex_);
  bool IsFinished() PW_EXCLUSIVE_LOCKS_REQUIRED(mutex_)
      PW_LOCKS_EXCLUDED(status_mutex_) {
    return status_.acquire()->state ==
           pw_software_update_BundledUpdateState_Enum_FINISHED;
  }
};

}  // namespace pw::software_update