-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
main.cpp
388 lines (354 loc) · 16.8 KB
/
main.cpp
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
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
/*
*
* Copyright (c) 2020-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.
*/
#include <app/clusters/ota-provider/DefaultOTAProviderUserConsent.h>
#include <app/clusters/ota-provider/ota-provider-delegate.h>
#include <app/clusters/ota-provider/ota-provider.h>
#include <app/server/Server.h>
#include <app/util/util.h>
#include <json/json.h>
#include <ota-provider-common/BdxOtaSender.h>
#include <ota-provider-common/OTAProviderExample.h>
#include "AppMain.h"
#include <fstream>
#include <iostream>
#include <unistd.h>
using chip::BitFlags;
using chip::app::Clusters::OTAProviderDelegate;
using chip::ArgParser::OptionDef;
using chip::ArgParser::OptionSet;
using chip::ArgParser::PrintArgError;
using chip::bdx::TransferControlFlags;
using chip::Messaging::ExchangeManager;
using namespace chip::app::Clusters::OtaSoftwareUpdateProvider;
// TODO: this should probably be done dynamically
constexpr chip::EndpointId kOtaProviderEndpoint = 0;
constexpr uint16_t kOptionUpdateAction = 'a';
constexpr uint16_t kOptionUserConsentNeeded = 'c';
constexpr uint16_t kOptionFilepath = 'f';
constexpr uint16_t kOptionImageUri = 'i';
constexpr uint16_t kOptionOtaImageList = 'o';
constexpr uint16_t kOptionDelayedApplyActionTimeSec = 'p';
constexpr uint16_t kOptionQueryImageStatus = 'q';
constexpr uint16_t kOptionDelayedQueryActionTimeSec = 't';
constexpr uint16_t kOptionUserConsentState = 'u';
constexpr uint16_t kOptionIgnoreQueryImage = 'x';
constexpr uint16_t kOptionIgnoreApplyUpdate = 'y';
constexpr uint16_t kOptionPollInterval = 'P';
OTAProviderExample gOtaProvider;
chip::ota::DefaultOTAProviderUserConsent gUserConsentProvider;
// Global variables used for passing the CLI arguments to the OTAProviderExample object
static OTAQueryStatus gQueryImageStatus = OTAQueryStatus::kUpdateAvailable;
static OTAApplyUpdateAction gOptionUpdateAction = OTAApplyUpdateAction::kProceed;
static uint32_t gDelayedQueryActionTimeSec = 0;
static uint32_t gDelayedApplyActionTimeSec = 0;
static const char * gOtaFilepath = nullptr;
static const char * gOtaImageListFilepath = nullptr;
static const char * gImageUri = nullptr;
static chip::ota::UserConsentState gUserConsentState = chip::ota::UserConsentState::kUnknown;
static bool gUserConsentNeeded = false;
static uint32_t gIgnoreQueryImageCount = 0;
static uint32_t gIgnoreApplyUpdateCount = 0;
static uint32_t gPollInterval = 0;
// Parses the JSON filepath and extracts DeviceSoftwareVersionModel parameters
static bool ParseJsonFileAndPopulateCandidates(const char * filepath,
std::vector<OTAProviderExample::DeviceSoftwareVersionModel> & candidates)
{
bool ret = false;
Json::Value root;
Json::CharReaderBuilder builder;
JSONCPP_STRING errs;
std::ifstream ifs;
builder["collectComments"] = true; // allow C/C++ type comments in JSON file
ifs.open(filepath);
if (!ifs.good())
{
ChipLogError(SoftwareUpdate, "Error opening ifstream with file: \"%s\"", filepath);
return ret;
}
if (!parseFromStream(builder, ifs, &root, &errs))
{
ChipLogError(SoftwareUpdate, "Error parsing JSON from file: \"%s\"", filepath);
return ret;
}
const Json::Value devSofVerModValue = root["deviceSoftwareVersionModel"];
if (!devSofVerModValue || !devSofVerModValue.isArray())
{
ChipLogError(SoftwareUpdate, "Error: Key deviceSoftwareVersionModel not found or its value is not of type Array");
}
else
{
for (auto iter : devSofVerModValue)
{
OTAProviderExample::DeviceSoftwareVersionModel candidate;
candidate.vendorId = static_cast<chip::VendorId>(iter.get("vendorId", 1).asUInt());
candidate.productId = static_cast<uint16_t>(iter.get("productId", 1).asUInt());
candidate.softwareVersion = static_cast<uint32_t>(iter.get("softwareVersion", 10).asUInt64());
chip::Platform::CopyString(candidate.softwareVersionString, iter.get("softwareVersionString", "1.0.0").asCString());
candidate.cDVersionNumber = static_cast<uint16_t>(iter.get("cDVersionNumber", 0).asUInt());
candidate.softwareVersionValid = iter.get("softwareVersionValid", true).asBool() ? true : false;
candidate.minApplicableSoftwareVersion = static_cast<uint32_t>(iter.get("minApplicableSoftwareVersion", 0).asUInt64());
candidate.maxApplicableSoftwareVersion =
static_cast<uint32_t>(iter.get("maxApplicableSoftwareVersion", 1000).asUInt64());
chip::Platform::CopyString(candidate.otaURL, iter.get("otaURL", "https://test.com").asCString());
candidates.push_back(candidate);
ret = true;
}
}
return ret;
}
bool HandleOptions(const char * aProgram, OptionSet * aOptions, int aIdentifier, const char * aName, const char * aValue)
{
bool retval = true;
static bool kOptionFilepathSelected;
static bool kOptionOtaImageListSelected;
switch (aIdentifier)
{
case kOptionFilepath:
kOptionFilepathSelected = true;
if (0 != access(aValue, R_OK))
{
PrintArgError("%s: not permitted to read %s\n", aProgram, aValue);
retval = false;
}
else if (kOptionOtaImageListSelected)
{
PrintArgError("%s: Cannot have both OptionOtaImageList and kOptionOtaFilepath \n", aProgram);
retval = false;
}
else
{
gOtaFilepath = aValue;
}
break;
case kOptionImageUri:
gImageUri = aValue;
break;
case kOptionOtaImageList:
kOptionOtaImageListSelected = true;
if (0 != access(aValue, R_OK))
{
PrintArgError("%s: not permitted to read %s\n", aProgram, aValue);
retval = false;
}
else if (kOptionFilepathSelected)
{
PrintArgError("%s: Cannot have both OptionOtaImageList and kOptionOtaFilepath \n", aProgram);
retval = false;
}
else
{
gOtaImageListFilepath = aValue;
}
break;
case kOptionQueryImageStatus:
if (strcmp(aValue, "updateAvailable") == 0)
{
gQueryImageStatus = OTAQueryStatus::kUpdateAvailable;
}
else if (strcmp(aValue, "busy") == 0)
{
gQueryImageStatus = OTAQueryStatus::kBusy;
}
else if (strcmp(aValue, "updateNotAvailable") == 0)
{
gQueryImageStatus = OTAQueryStatus::kNotAvailable;
}
else
{
PrintArgError("%s: ERROR: Invalid queryImageStatus parameter: %s\n", aProgram, aValue);
retval = false;
}
break;
case kOptionIgnoreQueryImage:
gIgnoreQueryImageCount = static_cast<uint32_t>(strtoul(aValue, NULL, 0));
break;
case kOptionIgnoreApplyUpdate:
gIgnoreApplyUpdateCount = static_cast<uint32_t>(strtoul(aValue, NULL, 0));
break;
case kOptionUpdateAction:
if (strcmp(aValue, "proceed") == 0)
{
gOptionUpdateAction = OTAApplyUpdateAction::kProceed;
}
else if (strcmp(aValue, "awaitNextAction") == 0)
{
gOptionUpdateAction = OTAApplyUpdateAction::kAwaitNextAction;
}
else if (strcmp(aValue, "discontinue") == 0)
{
gOptionUpdateAction = OTAApplyUpdateAction::kDiscontinue;
}
else
{
PrintArgError("%s: ERROR: Invalid applyUpdateAction parameter: %s\n", aProgram, aValue);
retval = false;
}
break;
case kOptionDelayedQueryActionTimeSec:
gDelayedQueryActionTimeSec = static_cast<uint32_t>(strtoul(aValue, NULL, 0));
break;
case kOptionDelayedApplyActionTimeSec:
gDelayedApplyActionTimeSec = static_cast<uint32_t>(strtoul(aValue, NULL, 0));
break;
case kOptionUserConsentState:
if (strcmp(aValue, "granted") == 0)
{
gUserConsentState = chip::ota::UserConsentState::kGranted;
}
else if (strcmp(aValue, "denied") == 0)
{
gUserConsentState = chip::ota::UserConsentState::kDenied;
}
else if (strcmp(aValue, "deferred") == 0)
{
gUserConsentState = chip::ota::UserConsentState::kObtaining;
}
else
{
PrintArgError("%s: ERROR: Invalid UserConsent parameter: %s\n", aProgram, aValue);
retval = false;
}
break;
case kOptionUserConsentNeeded:
gUserConsentNeeded = true;
break;
case kOptionPollInterval:
gPollInterval = static_cast<uint32_t>(strtoul(aValue, NULL, 0));
break;
default:
PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", aProgram, aName);
retval = false;
break;
}
return (retval);
}
OptionDef cmdLineOptionsDef[] = {
{ "applyUpdateAction", chip::ArgParser::kArgumentRequired, kOptionUpdateAction },
{ "userConsentNeeded", chip::ArgParser::kNoArgument, kOptionUserConsentNeeded },
{ "filepath", chip::ArgParser::kArgumentRequired, kOptionFilepath },
{ "imageUri", chip::ArgParser::kArgumentRequired, kOptionImageUri },
{ "otaImageList", chip::ArgParser::kArgumentRequired, kOptionOtaImageList },
{ "delayedApplyActionTimeSec", chip::ArgParser::kArgumentRequired, kOptionDelayedApplyActionTimeSec },
{ "queryImageStatus", chip::ArgParser::kArgumentRequired, kOptionQueryImageStatus },
{ "delayedQueryActionTimeSec", chip::ArgParser::kArgumentRequired, kOptionDelayedQueryActionTimeSec },
{ "userConsentState", chip::ArgParser::kArgumentRequired, kOptionUserConsentState },
{ "ignoreQueryImage", chip::ArgParser::kArgumentRequired, kOptionIgnoreQueryImage },
{ "ignoreApplyUpdate", chip::ArgParser::kArgumentRequired, kOptionIgnoreApplyUpdate },
{ "pollInterval", chip::ArgParser::kArgumentRequired, kOptionPollInterval },
{},
};
OptionSet cmdLineOptions = { HandleOptions, cmdLineOptionsDef, "PROGRAM OPTIONS",
" -a, --applyUpdateAction <proceed | awaitNextAction | discontinue>\n"
" Value for the Action field in the first ApplyUpdateResponse.\n"
" For all subsequent responses, the value of proceed will be used.\n"
" -c, --userConsentNeeded\n"
" If supplied, value of the UserConsentNeeded field in the QueryImageResponse\n"
" is set to true. This is only applicable if value of the RequestorCanConsent\n"
" field in QueryImage Command is true.\n"
" Otherwise, value of the UserConsentNeeded field is false.\n"
" -f, --filepath <file path>\n"
" Path to a file containing an OTA image\n"
" -i, --imageUri <uri>\n"
" Value for the ImageURI field in the QueryImageResponse.\n"
" If none is supplied, a valid URI is generated.\n"
" -o, --otaImageList <file path>\n"
" Path to a file containing a list of OTA images\n"
" -p, --delayedApplyActionTimeSec <time in seconds>\n"
" Value for the DelayedActionTime field in the first ApplyUpdateResponse.\n"
" For all subsequent responses, the value of zero will be used.\n"
" -q, --queryImageStatus <updateAvailable | busy | updateNotAvailable>\n"
" Value for the Status field in the first QueryImageResponse.\n"
" For all subsequent responses, the value of updateAvailable will be used.\n"
" -t, --delayedQueryActionTimeSec <time in seconds>\n"
" Value for the DelayedActionTime field in the first QueryImageResponse.\n"
" For all subsequent responses, the value of zero will be used.\n"
" -u, --userConsentState <granted | denied | deferred>\n"
" The user consent state for the first QueryImageResponse. For all subsequent\n"
" responses, the value of granted will be used.\n"
" Note that --queryImageStatus overrides this option.\n"
" granted: Status field in the first QueryImageResponse is set to updateAvailable\n"
" denied: Status field in the first QueryImageResponse is set to updateNotAvailable\n"
" deferred: Status field in the first QueryImageResponse is set to busy\n"
" -x, --ignoreQueryImage <ignore count>\n"
" The number of times to ignore the QueryImage Command and not send a response.\n"
" -y, --ignoreApplyUpdate <ignore count>\n"
" The number of times to ignore the ApplyUpdateRequest Command and not send a response.\n"
" -P, --pollInterval <time in milliseconds>\n"
" Poll interval for the BDX transfer \n" };
OptionSet * allOptions[] = { &cmdLineOptions, nullptr };
void ApplicationInit()
{
CHIP_ERROR err = CHIP_NO_ERROR;
BdxOtaSender * bdxOtaSender = gOtaProvider.GetBdxOtaSender();
VerifyOrReturn(bdxOtaSender != nullptr);
err = chip::Server::GetInstance().GetExchangeManager().RegisterUnsolicitedMessageHandlerForProtocol(chip::Protocols::BDX::Id,
bdxOtaSender);
if (err != CHIP_NO_ERROR)
{
ChipLogDetail(SoftwareUpdate, "RegisterUnsolicitedMessageHandler failed: %s", chip::ErrorStr(err));
return;
}
ChipLogDetail(SoftwareUpdate, "Using OTA file: %s", gOtaFilepath ? gOtaFilepath : "(none)");
if (gOtaFilepath != nullptr)
{
gOtaProvider.SetOTAFilePath(gOtaFilepath);
}
if (gImageUri != nullptr)
{
gOtaProvider.SetImageUri(gImageUri);
}
gOtaProvider.SetIgnoreQueryImageCount(gIgnoreQueryImageCount);
gOtaProvider.SetIgnoreApplyUpdateCount(gIgnoreApplyUpdateCount);
gOtaProvider.SetQueryImageStatus(gQueryImageStatus);
gOtaProvider.SetApplyUpdateAction(gOptionUpdateAction);
gOtaProvider.SetDelayedQueryActionTimeSec(gDelayedQueryActionTimeSec);
gOtaProvider.SetDelayedApplyActionTimeSec(gDelayedApplyActionTimeSec);
if (gUserConsentState != chip::ota::UserConsentState::kUnknown)
{
gUserConsentProvider.SetGlobalUserConsentState(gUserConsentState);
gOtaProvider.SetUserConsentDelegate(&gUserConsentProvider);
}
if (gUserConsentNeeded)
{
gOtaProvider.SetUserConsentNeeded(true);
}
if (gPollInterval != 0)
{
gOtaProvider.SetPollInterval(gPollInterval);
}
ChipLogDetail(SoftwareUpdate, "Using ImageList file: %s", gOtaImageListFilepath ? gOtaImageListFilepath : "(none)");
if (gOtaImageListFilepath != nullptr)
{
// Parse JSON file and load the ota candidates
std::vector<OTAProviderExample::DeviceSoftwareVersionModel> candidates;
ParseJsonFileAndPopulateCandidates(gOtaImageListFilepath, candidates);
gOtaProvider.SetOTACandidates(candidates);
}
if ((gOtaFilepath == nullptr) && (gOtaImageListFilepath == nullptr))
{
ChipLogError(SoftwareUpdate, "Either an OTA file or image list file must be specified");
chipDie();
}
chip::app::Clusters::OTAProvider::SetDelegate(kOtaProviderEndpoint, &gOtaProvider);
}
int main(int argc, char * argv[])
{
VerifyOrDie(ChipLinuxAppInit(argc, argv, &cmdLineOptions) == 0);
ChipLinuxAppMainLoop();
return 0;
}