-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
Copy pathDefaultOTARequestor.h
348 lines (281 loc) · 14.2 KB
/
DefaultOTARequestor.h
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
/*
*
* Copyright (c) 2021-2022 Project CHIP Authors
* All rights reserved.
*
* 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.
*/
/* This file contains the declarations for the Matter OTA Requestor implementation and API.
* Applications implementing the OTA Requestor functionality must include this file.
*/
#pragma once
#include <app/CASESessionManager.h>
#include <app/server/Server.h>
#include <protocols/bdx/BdxMessages.h>
#include "BDXDownloader.h"
#include "OTARequestorDriver.h"
#include "OTARequestorInterface.h"
#include "OTARequestorStorage.h"
namespace chip {
// This class implements all of the core logic of the OTA Requestor
class DefaultOTARequestor : public OTARequestorInterface, public BDXDownloader::StateDelegate
{
public:
DefaultOTARequestor() : mOnConnectedCallback(OnConnected, this), mOnConnectionFailureCallback(OnConnectionFailure, this) {}
//////////// OTARequestorInterface Implementation ///////////////
void Reset(void) override;
void HandleAnnounceOTAProvider(
app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath,
const app::Clusters::OtaSoftwareUpdateRequestor::Commands::AnnounceOTAProvider::DecodableType & commandData) override;
// Application API to send the QueryImage command and start the image update process with the next available Provider
CHIP_ERROR TriggerImmediateQuery(FabricIndex fabricIndex) override;
// Internal API meant for use by OTARequestorDriver to send the QueryImage command and start the image update process
// with the Provider currently set
void TriggerImmediateQueryInternal() override;
// Initiate download of the new image
void DownloadUpdate() override;
// Set the requestor state to kDelayedOnUserConsent
void DownloadUpdateDelayedOnUserConsent() override;
// Initiate the session to send ApplyUpdateRequest command
void ApplyUpdate() override;
// Initiate the session to send NotifyUpdateApplied command
void NotifyUpdateApplied() override;
// Get the value of the UpdateStateProgress attribute (in percentage) of the OTA Software Update Requestor Cluster on the given
// endpoint
CHIP_ERROR GetUpdateStateProgressAttribute(EndpointId endpointId, app::DataModel::Nullable<uint8_t> & progress) override;
// Get the value of the UpdateState attribute of the OTA Software Update Requestor Cluster on the given endpoint
CHIP_ERROR GetUpdateStateAttribute(EndpointId endpointId, OTAUpdateStateEnum & state) override;
// Get the current state of the OTA update
OTAUpdateStateEnum GetCurrentUpdateState() override { return mCurrentUpdateState; }
// Get the target version of the OTA update
uint32_t GetTargetVersion() override { return mTargetVersion; }
// Application directs the Requestor to cancel image update in progress. All the Requestor state is
// cleared, UpdateState is reset to Idle
void CancelImageUpdate() override;
// Clear all entries with the specified fabric index in the default OTA provider list
CHIP_ERROR ClearDefaultOtaProviderList(FabricIndex fabricIndex) override;
void SetCurrentProviderLocation(ProviderLocationType providerLocation) override
{
mProviderLocation.SetValue(providerLocation);
}
void GetProviderLocation(Optional<ProviderLocationType> & providerLocation) override { providerLocation = mProviderLocation; }
// Set the metadata value for the provider to be used in the next query and OTA update process
// NOTE: Does not persist across reboot.
void SetMetadataForProvider(ByteSpan metadataForProvider) override { mMetadataForProvider.SetValue(metadataForProvider); }
// Add a default OTA provider to the cached list
CHIP_ERROR AddDefaultOtaProvider(const ProviderLocationType & providerLocation) override;
// Retrieve an iterator to the cached default OTA provider list
ProviderLocationList::Iterator GetDefaultOTAProviderListIterator(void) override { return mDefaultOtaProviderList.Begin(); }
//////////// BDXDownloader::StateDelegate Implementation ///////////////
void OnDownloadStateChanged(OTADownloader::State state,
app::Clusters::OtaSoftwareUpdateRequestor::OTAChangeReasonEnum reason) override;
void OnUpdateProgressChanged(app::DataModel::Nullable<uint8_t> percent) override;
//////////// DefaultOTARequestor public APIs ///////////////
/**
* Called to perform some initialization. Note that some states that must be initalized in the CHIP context will be deferred to
* InitState.
*/
CHIP_ERROR Init(Server & server, OTARequestorStorage & storage, OTARequestorDriver & driver, BDXDownloader & downloader);
private:
using QueryImageResponseDecodableType = app::Clusters::OtaSoftwareUpdateProvider::Commands::QueryImageResponse::DecodableType;
using ApplyUpdateResponseDecodableType = app::Clusters::OtaSoftwareUpdateProvider::Commands::ApplyUpdateResponse::DecodableType;
using OTAChangeReasonEnum = app::Clusters::OtaSoftwareUpdateRequestor::OTAChangeReasonEnum;
static constexpr size_t kMaxUpdateTokenLen = 32;
// TODO: the application should define this, along with initializing the BDXDownloader
// This class is purely for delivering messages and sending outgoing messages to/from the BDXDownloader.
class BDXMessenger : public chip::BDXDownloader::MessagingDelegate, public chip::Messaging::ExchangeDelegate
{
public:
CHIP_ERROR SendMessage(const chip::bdx::TransferSession::OutputEvent & event) override
{
ChipLogDetail(SoftwareUpdate, "BDX::SendMessage");
VerifyOrReturnError(mExchangeCtx != nullptr, CHIP_ERROR_INCORRECT_STATE);
chip::Messaging::SendFlags sendFlags;
if (!event.msgTypeData.HasMessageType(chip::bdx::MessageType::BlockAckEOF) &&
!event.msgTypeData.HasMessageType(chip::Protocols::SecureChannel::MsgType::StatusReport))
{
sendFlags.Set(chip::Messaging::SendMessageFlags::kExpectResponse);
}
CHIP_ERROR err = mExchangeCtx->SendMessage(event.msgTypeData.ProtocolId, event.msgTypeData.MessageType,
event.MsgData.Retain(), sendFlags);
if (err != CHIP_NO_ERROR)
{
Reset();
}
return err;
}
CHIP_ERROR OnMessageReceived(chip::Messaging::ExchangeContext * ec, const chip::PayloadHeader & payloadHeader,
chip::System::PacketBufferHandle && payload) override
{
if (mDownloader == nullptr)
{
ChipLogError(BDX, "BDXDownloader instance is null, can't pass message");
return CHIP_NO_ERROR;
}
mDownloader->OnMessageReceived(payloadHeader, std::move(payload));
// For a receiver using BDX Protocol, all received messages will require a response except for a StatusReport
if (!payloadHeader.HasMessageType(chip::Protocols::SecureChannel::MsgType::StatusReport))
{
ec->WillSendMessage();
}
return CHIP_NO_ERROR;
}
void OnResponseTimeout(chip::Messaging::ExchangeContext * ec) override
{
ChipLogError(BDX, "exchange timed out");
// Null out mExchangeCtx before calling OnDownloadTimeout, in case
// the downloader decides to call Reset() on us. If we don't, we
// will end up closing the exchange from Reset and then the caller
// will close it _again_ (see API documentation for
// OnResponseTimeout), which will lead to refcount underflow.
mExchangeCtx = nullptr;
if (mDownloader != nullptr)
{
mDownloader->OnDownloadTimeout();
}
}
void OnExchangeClosing(Messaging::ExchangeContext * ec) override { mExchangeCtx = nullptr; }
void Init(chip::BDXDownloader * downloader, chip::Messaging::ExchangeContext * ec)
{
mExchangeCtx = ec;
mDownloader = downloader;
}
void Reset()
{
VerifyOrReturn(mExchangeCtx != nullptr);
mExchangeCtx->Close();
mExchangeCtx = nullptr;
}
private:
chip::Messaging::ExchangeContext * mExchangeCtx;
chip::BDXDownloader * mDownloader;
};
/**
* Callback to initialize states and server attributes in the CHIP context
*/
static void InitState(intptr_t context);
/**
* Map a CHIP_ERROR to an IdleStateReason enum type
*/
IdleStateReason MapErrorToIdleStateReason(CHIP_ERROR error);
ScopedNodeId GetProviderScopedId() const
{
return ScopedNodeId(mProviderLocation.Value().providerNodeID, mProviderLocation.Value().fabricIndex);
}
/**
* Record the new update state by updating the corresponding server attribute and logging a StateTransition event
*/
void RecordNewUpdateState(OTAUpdateStateEnum newState, OTAChangeReasonEnum reason, CHIP_ERROR error = CHIP_NO_ERROR);
/**
* Record the error update state and transition to the idle state
*/
void RecordErrorUpdateState(CHIP_ERROR error, OTAChangeReasonEnum reason = OTAChangeReasonEnum::kFailure);
/**
* Generate an update token using the operational node ID in case of token lost, received in QueryImageResponse
*/
CHIP_ERROR GenerateUpdateToken();
/**
* Send QueryImage request using values matching Basic cluster
*/
CHIP_ERROR SendQueryImageRequest(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle);
/**
* Validate and extract mandatory information from QueryImageResponse
*/
CHIP_ERROR ExtractUpdateDescription(const QueryImageResponseDecodableType & response, UpdateDescription & update) const;
// Various actions to take when OnConnected callback is called
enum OnConnectedAction
{
kQueryImage = 0,
kDownload,
kApplyUpdate,
kNotifyUpdateApplied,
};
/**
* Called to establish a session to provider indicated by mProviderLocation
*
* @param onConnectedAction The action to take once session to provider has been established
*/
void ConnectToProvider(OnConnectedAction onConnectedAction);
/**
* Called to tear down a session to provider indicated by mProviderLocation
*/
void DisconnectFromProvider();
/**
* Start download of the software image returned in QueryImageResponse
*/
CHIP_ERROR StartDownload(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle);
/**
* Send ApplyUpdate request using values obtained from QueryImageResponse
*/
CHIP_ERROR SendApplyUpdateRequest(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle);
/**
* Send NotifyUpdateApplied request
*/
CHIP_ERROR SendNotifyUpdateAppliedRequest(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle);
/**
* Store current update information to KVS
*/
void StoreCurrentUpdateInfo();
/**
* Load current update information to KVS
*/
void LoadCurrentUpdateInfo();
/**
* Session connection callbacks
*/
static void OnConnected(void * context, Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle);
static void OnConnectionFailure(void * context, const ScopedNodeId & peerId, CHIP_ERROR error);
Callback::Callback<OnDeviceConnected> mOnConnectedCallback;
Callback::Callback<OnDeviceConnectionFailure> mOnConnectionFailureCallback;
/**
* QueryImage callbacks
*/
static void OnQueryImageResponse(void * context, const QueryImageResponseDecodableType & response);
static void OnQueryImageFailure(void * context, CHIP_ERROR error);
/**
* ApplyUpdate callbacks
*/
static void OnApplyUpdateResponse(void * context, const ApplyUpdateResponseDecodableType & response);
static void OnApplyUpdateFailure(void * context, CHIP_ERROR error);
/**
* NotifyUpdateApplied callbacks
*/
static void OnNotifyUpdateAppliedResponse(void * context, const app::DataModel::NullObjectType & response);
static void OnNotifyUpdateAppliedFailure(void * context, CHIP_ERROR error);
/**
* Commissioning callback
*/
static void OnCommissioningCompleteRequestor(const DeviceLayer::ChipDeviceEvent * event, intptr_t arg);
OTARequestorStorage * mStorage = nullptr;
OTARequestorDriver * mOtaRequestorDriver = nullptr;
CASESessionManager * mCASESessionManager = nullptr;
OnConnectedAction mOnConnectedAction = kQueryImage;
BDXDownloader * mBdxDownloader = nullptr; // TODO: this should be OTADownloader
BDXMessenger mBdxMessenger; // TODO: ideally this is held by the application
uint8_t mUpdateTokenBuffer[kMaxUpdateTokenLen];
Optional<ByteSpan> mMetadataForProvider;
ByteSpan mUpdateToken;
uint32_t mCurrentVersion = 0;
uint32_t mTargetVersion = 0;
char mFileDesignatorBuffer[bdx::kMaxFileDesignatorLen];
CharSpan mFileDesignator;
OTAUpdateStateEnum mCurrentUpdateState = OTAUpdateStateEnum::kUnknown;
Server * mServer = nullptr;
ProviderLocationList mDefaultOtaProviderList;
// Provider location used for the current/last update in progress. Note that on reboot, this value will be read from the
// persistent storage (if available), used for sending the NotifyApplied message, and then cleared. This will ensure determinism
// in the OTARequestorDriver on reboot.
Optional<ProviderLocationType> mProviderLocation;
SessionHolder mSessionHolder;
};
} // namespace chip