main.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. /*
  2. *
  3. * Copyright (c) 2020-2022 Project CHIP Authors
  4. * All rights reserved.
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. #include <app/clusters/ota-provider/DefaultOTAProviderUserConsent.h>
  19. #include <app/clusters/ota-provider/ota-provider-delegate.h>
  20. #include <app/clusters/ota-provider/ota-provider.h>
  21. #include <app/server/Server.h>
  22. #include <app/util/util.h>
  23. #include <json/json.h>
  24. #include <ota-provider-common/BdxOtaSender.h>
  25. #include <ota-provider-common/OTAProviderExample.h>
  26. #include "AppMain.h"
  27. #include <fstream>
  28. #include <iostream>
  29. #include <unistd.h>
  30. using chip::BitFlags;
  31. using chip::app::Clusters::OTAProviderDelegate;
  32. using chip::ArgParser::OptionDef;
  33. using chip::ArgParser::OptionSet;
  34. using chip::ArgParser::PrintArgError;
  35. using chip::bdx::TransferControlFlags;
  36. using chip::Messaging::ExchangeManager;
  37. using namespace chip::app::Clusters::OtaSoftwareUpdateProvider;
  38. // TODO: this should probably be done dynamically
  39. constexpr chip::EndpointId kOtaProviderEndpoint = 0;
  40. constexpr uint16_t kOptionUpdateAction = 'a';
  41. constexpr uint16_t kOptionUserConsentNeeded = 'c';
  42. constexpr uint16_t kOptionFilepath = 'f';
  43. constexpr uint16_t kOptionImageUri = 'i';
  44. constexpr uint16_t kOptionOtaImageList = 'o';
  45. constexpr uint16_t kOptionDelayedApplyActionTimeSec = 'p';
  46. constexpr uint16_t kOptionQueryImageStatus = 'q';
  47. constexpr uint16_t kOptionDelayedQueryActionTimeSec = 't';
  48. constexpr uint16_t kOptionUserConsentState = 'u';
  49. constexpr uint16_t kOptionIgnoreQueryImage = 'x';
  50. constexpr uint16_t kOptionIgnoreApplyUpdate = 'y';
  51. constexpr uint16_t kOptionPollInterval = 'P';
  52. OTAProviderExample gOtaProvider;
  53. chip::ota::DefaultOTAProviderUserConsent gUserConsentProvider;
  54. // Global variables used for passing the CLI arguments to the OTAProviderExample object
  55. static OTAQueryStatus gQueryImageStatus = OTAQueryStatus::kUpdateAvailable;
  56. static OTAApplyUpdateAction gOptionUpdateAction = OTAApplyUpdateAction::kProceed;
  57. static uint32_t gDelayedQueryActionTimeSec = 0;
  58. static uint32_t gDelayedApplyActionTimeSec = 0;
  59. static const char * gOtaFilepath = nullptr;
  60. static const char * gOtaImageListFilepath = nullptr;
  61. static const char * gImageUri = nullptr;
  62. static chip::ota::UserConsentState gUserConsentState = chip::ota::UserConsentState::kUnknown;
  63. static bool gUserConsentNeeded = false;
  64. static uint32_t gIgnoreQueryImageCount = 0;
  65. static uint32_t gIgnoreApplyUpdateCount = 0;
  66. static uint32_t gPollInterval = 0;
  67. // Parses the JSON filepath and extracts DeviceSoftwareVersionModel parameters
  68. static bool ParseJsonFileAndPopulateCandidates(const char * filepath,
  69. std::vector<OTAProviderExample::DeviceSoftwareVersionModel> & candidates)
  70. {
  71. bool ret = false;
  72. Json::Value root;
  73. Json::CharReaderBuilder builder;
  74. JSONCPP_STRING errs;
  75. std::ifstream ifs;
  76. builder["collectComments"] = true; // allow C/C++ type comments in JSON file
  77. ifs.open(filepath);
  78. if (!ifs.good())
  79. {
  80. ChipLogError(SoftwareUpdate, "Error opening ifstream with file: \"%s\"", filepath);
  81. return ret;
  82. }
  83. if (!parseFromStream(builder, ifs, &root, &errs))
  84. {
  85. ChipLogError(SoftwareUpdate, "Error parsing JSON from file: \"%s\"", filepath);
  86. return ret;
  87. }
  88. const Json::Value devSofVerModValue = root["deviceSoftwareVersionModel"];
  89. if (!devSofVerModValue || !devSofVerModValue.isArray())
  90. {
  91. ChipLogError(SoftwareUpdate, "Error: Key deviceSoftwareVersionModel not found or its value is not of type Array");
  92. }
  93. else
  94. {
  95. for (auto iter : devSofVerModValue)
  96. {
  97. OTAProviderExample::DeviceSoftwareVersionModel candidate;
  98. candidate.vendorId = static_cast<chip::VendorId>(iter.get("vendorId", 1).asUInt());
  99. candidate.productId = static_cast<uint16_t>(iter.get("productId", 1).asUInt());
  100. candidate.softwareVersion = static_cast<uint32_t>(iter.get("softwareVersion", 10).asUInt64());
  101. chip::Platform::CopyString(candidate.softwareVersionString, iter.get("softwareVersionString", "1.0.0").asCString());
  102. candidate.cDVersionNumber = static_cast<uint16_t>(iter.get("cDVersionNumber", 0).asUInt());
  103. candidate.softwareVersionValid = iter.get("softwareVersionValid", true).asBool() ? true : false;
  104. candidate.minApplicableSoftwareVersion = static_cast<uint32_t>(iter.get("minApplicableSoftwareVersion", 0).asUInt64());
  105. candidate.maxApplicableSoftwareVersion =
  106. static_cast<uint32_t>(iter.get("maxApplicableSoftwareVersion", 1000).asUInt64());
  107. chip::Platform::CopyString(candidate.otaURL, iter.get("otaURL", "https://test.com").asCString());
  108. candidates.push_back(candidate);
  109. ret = true;
  110. }
  111. }
  112. return ret;
  113. }
  114. bool HandleOptions(const char * aProgram, OptionSet * aOptions, int aIdentifier, const char * aName, const char * aValue)
  115. {
  116. bool retval = true;
  117. static bool kOptionFilepathSelected;
  118. static bool kOptionOtaImageListSelected;
  119. switch (aIdentifier)
  120. {
  121. case kOptionFilepath:
  122. kOptionFilepathSelected = true;
  123. if (0 != access(aValue, R_OK))
  124. {
  125. PrintArgError("%s: not permitted to read %s\n", aProgram, aValue);
  126. retval = false;
  127. }
  128. else if (kOptionOtaImageListSelected)
  129. {
  130. PrintArgError("%s: Cannot have both OptionOtaImageList and kOptionOtaFilepath \n", aProgram);
  131. retval = false;
  132. }
  133. else
  134. {
  135. gOtaFilepath = aValue;
  136. }
  137. break;
  138. case kOptionImageUri:
  139. gImageUri = aValue;
  140. break;
  141. case kOptionOtaImageList:
  142. kOptionOtaImageListSelected = true;
  143. if (0 != access(aValue, R_OK))
  144. {
  145. PrintArgError("%s: not permitted to read %s\n", aProgram, aValue);
  146. retval = false;
  147. }
  148. else if (kOptionFilepathSelected)
  149. {
  150. PrintArgError("%s: Cannot have both OptionOtaImageList and kOptionOtaFilepath \n", aProgram);
  151. retval = false;
  152. }
  153. else
  154. {
  155. gOtaImageListFilepath = aValue;
  156. }
  157. break;
  158. case kOptionQueryImageStatus:
  159. if (strcmp(aValue, "updateAvailable") == 0)
  160. {
  161. gQueryImageStatus = OTAQueryStatus::kUpdateAvailable;
  162. }
  163. else if (strcmp(aValue, "busy") == 0)
  164. {
  165. gQueryImageStatus = OTAQueryStatus::kBusy;
  166. }
  167. else if (strcmp(aValue, "updateNotAvailable") == 0)
  168. {
  169. gQueryImageStatus = OTAQueryStatus::kNotAvailable;
  170. }
  171. else
  172. {
  173. PrintArgError("%s: ERROR: Invalid queryImageStatus parameter: %s\n", aProgram, aValue);
  174. retval = false;
  175. }
  176. break;
  177. case kOptionIgnoreQueryImage:
  178. gIgnoreQueryImageCount = static_cast<uint32_t>(strtoul(aValue, NULL, 0));
  179. break;
  180. case kOptionIgnoreApplyUpdate:
  181. gIgnoreApplyUpdateCount = static_cast<uint32_t>(strtoul(aValue, NULL, 0));
  182. break;
  183. case kOptionUpdateAction:
  184. if (strcmp(aValue, "proceed") == 0)
  185. {
  186. gOptionUpdateAction = OTAApplyUpdateAction::kProceed;
  187. }
  188. else if (strcmp(aValue, "awaitNextAction") == 0)
  189. {
  190. gOptionUpdateAction = OTAApplyUpdateAction::kAwaitNextAction;
  191. }
  192. else if (strcmp(aValue, "discontinue") == 0)
  193. {
  194. gOptionUpdateAction = OTAApplyUpdateAction::kDiscontinue;
  195. }
  196. else
  197. {
  198. PrintArgError("%s: ERROR: Invalid applyUpdateAction parameter: %s\n", aProgram, aValue);
  199. retval = false;
  200. }
  201. break;
  202. case kOptionDelayedQueryActionTimeSec:
  203. gDelayedQueryActionTimeSec = static_cast<uint32_t>(strtoul(aValue, NULL, 0));
  204. break;
  205. case kOptionDelayedApplyActionTimeSec:
  206. gDelayedApplyActionTimeSec = static_cast<uint32_t>(strtoul(aValue, NULL, 0));
  207. break;
  208. case kOptionUserConsentState:
  209. if (strcmp(aValue, "granted") == 0)
  210. {
  211. gUserConsentState = chip::ota::UserConsentState::kGranted;
  212. }
  213. else if (strcmp(aValue, "denied") == 0)
  214. {
  215. gUserConsentState = chip::ota::UserConsentState::kDenied;
  216. }
  217. else if (strcmp(aValue, "deferred") == 0)
  218. {
  219. gUserConsentState = chip::ota::UserConsentState::kObtaining;
  220. }
  221. else
  222. {
  223. PrintArgError("%s: ERROR: Invalid UserConsent parameter: %s\n", aProgram, aValue);
  224. retval = false;
  225. }
  226. break;
  227. case kOptionUserConsentNeeded:
  228. gUserConsentNeeded = true;
  229. break;
  230. case kOptionPollInterval:
  231. gPollInterval = static_cast<uint32_t>(strtoul(aValue, NULL, 0));
  232. break;
  233. default:
  234. PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", aProgram, aName);
  235. retval = false;
  236. break;
  237. }
  238. return (retval);
  239. }
  240. OptionDef cmdLineOptionsDef[] = {
  241. { "applyUpdateAction", chip::ArgParser::kArgumentRequired, kOptionUpdateAction },
  242. { "userConsentNeeded", chip::ArgParser::kNoArgument, kOptionUserConsentNeeded },
  243. { "filepath", chip::ArgParser::kArgumentRequired, kOptionFilepath },
  244. { "imageUri", chip::ArgParser::kArgumentRequired, kOptionImageUri },
  245. { "otaImageList", chip::ArgParser::kArgumentRequired, kOptionOtaImageList },
  246. { "delayedApplyActionTimeSec", chip::ArgParser::kArgumentRequired, kOptionDelayedApplyActionTimeSec },
  247. { "queryImageStatus", chip::ArgParser::kArgumentRequired, kOptionQueryImageStatus },
  248. { "delayedQueryActionTimeSec", chip::ArgParser::kArgumentRequired, kOptionDelayedQueryActionTimeSec },
  249. { "userConsentState", chip::ArgParser::kArgumentRequired, kOptionUserConsentState },
  250. { "ignoreQueryImage", chip::ArgParser::kArgumentRequired, kOptionIgnoreQueryImage },
  251. { "ignoreApplyUpdate", chip::ArgParser::kArgumentRequired, kOptionIgnoreApplyUpdate },
  252. { "pollInterval", chip::ArgParser::kArgumentRequired, kOptionPollInterval },
  253. {},
  254. };
  255. OptionSet cmdLineOptions = { HandleOptions, cmdLineOptionsDef, "PROGRAM OPTIONS",
  256. " -a, --applyUpdateAction <proceed | awaitNextAction | discontinue>\n"
  257. " Value for the Action field in the first ApplyUpdateResponse.\n"
  258. " For all subsequent responses, the value of proceed will be used.\n"
  259. " -c, --userConsentNeeded\n"
  260. " If supplied, value of the UserConsentNeeded field in the QueryImageResponse\n"
  261. " is set to true. This is only applicable if value of the RequestorCanConsent\n"
  262. " field in QueryImage Command is true.\n"
  263. " Otherwise, value of the UserConsentNeeded field is false.\n"
  264. " -f, --filepath <file path>\n"
  265. " Path to a file containing an OTA image\n"
  266. " -i, --imageUri <uri>\n"
  267. " Value for the ImageURI field in the QueryImageResponse.\n"
  268. " If none is supplied, a valid URI is generated.\n"
  269. " -o, --otaImageList <file path>\n"
  270. " Path to a file containing a list of OTA images\n"
  271. " -p, --delayedApplyActionTimeSec <time in seconds>\n"
  272. " Value for the DelayedActionTime field in the first ApplyUpdateResponse.\n"
  273. " For all subsequent responses, the value of zero will be used.\n"
  274. " -q, --queryImageStatus <updateAvailable | busy | updateNotAvailable>\n"
  275. " Value for the Status field in the first QueryImageResponse.\n"
  276. " For all subsequent responses, the value of updateAvailable will be used.\n"
  277. " -t, --delayedQueryActionTimeSec <time in seconds>\n"
  278. " Value for the DelayedActionTime field in the first QueryImageResponse.\n"
  279. " For all subsequent responses, the value of zero will be used.\n"
  280. " -u, --userConsentState <granted | denied | deferred>\n"
  281. " The user consent state for the first QueryImageResponse. For all subsequent\n"
  282. " responses, the value of granted will be used.\n"
  283. " Note that --queryImageStatus overrides this option.\n"
  284. " granted: Status field in the first QueryImageResponse is set to updateAvailable\n"
  285. " denied: Status field in the first QueryImageResponse is set to updateNotAvailable\n"
  286. " deferred: Status field in the first QueryImageResponse is set to busy\n"
  287. " -x, --ignoreQueryImage <ignore count>\n"
  288. " The number of times to ignore the QueryImage Command and not send a response.\n"
  289. " -y, --ignoreApplyUpdate <ignore count>\n"
  290. " The number of times to ignore the ApplyUpdateRequest Command and not send a response.\n"
  291. " -P, --pollInterval <time in milliseconds>\n"
  292. " Poll interval for the BDX transfer \n" };
  293. OptionSet * allOptions[] = { &cmdLineOptions, nullptr };
  294. void ApplicationInit()
  295. {
  296. CHIP_ERROR err = CHIP_NO_ERROR;
  297. BdxOtaSender * bdxOtaSender = gOtaProvider.GetBdxOtaSender();
  298. VerifyOrReturn(bdxOtaSender != nullptr);
  299. err = chip::Server::GetInstance().GetExchangeManager().RegisterUnsolicitedMessageHandlerForProtocol(chip::Protocols::BDX::Id,
  300. bdxOtaSender);
  301. if (err != CHIP_NO_ERROR)
  302. {
  303. ChipLogDetail(SoftwareUpdate, "RegisterUnsolicitedMessageHandler failed: %s", chip::ErrorStr(err));
  304. return;
  305. }
  306. ChipLogDetail(SoftwareUpdate, "Using OTA file: %s", gOtaFilepath ? gOtaFilepath : "(none)");
  307. if (gOtaFilepath != nullptr)
  308. {
  309. gOtaProvider.SetOTAFilePath(gOtaFilepath);
  310. }
  311. if (gImageUri != nullptr)
  312. {
  313. gOtaProvider.SetImageUri(gImageUri);
  314. }
  315. gOtaProvider.SetIgnoreQueryImageCount(gIgnoreQueryImageCount);
  316. gOtaProvider.SetIgnoreApplyUpdateCount(gIgnoreApplyUpdateCount);
  317. gOtaProvider.SetQueryImageStatus(gQueryImageStatus);
  318. gOtaProvider.SetApplyUpdateAction(gOptionUpdateAction);
  319. gOtaProvider.SetDelayedQueryActionTimeSec(gDelayedQueryActionTimeSec);
  320. gOtaProvider.SetDelayedApplyActionTimeSec(gDelayedApplyActionTimeSec);
  321. if (gUserConsentState != chip::ota::UserConsentState::kUnknown)
  322. {
  323. gUserConsentProvider.SetGlobalUserConsentState(gUserConsentState);
  324. gOtaProvider.SetUserConsentDelegate(&gUserConsentProvider);
  325. }
  326. if (gUserConsentNeeded)
  327. {
  328. gOtaProvider.SetUserConsentNeeded(true);
  329. }
  330. if (gPollInterval != 0)
  331. {
  332. gOtaProvider.SetPollInterval(gPollInterval);
  333. }
  334. ChipLogDetail(SoftwareUpdate, "Using ImageList file: %s", gOtaImageListFilepath ? gOtaImageListFilepath : "(none)");
  335. if (gOtaImageListFilepath != nullptr)
  336. {
  337. // Parse JSON file and load the ota candidates
  338. std::vector<OTAProviderExample::DeviceSoftwareVersionModel> candidates;
  339. ParseJsonFileAndPopulateCandidates(gOtaImageListFilepath, candidates);
  340. gOtaProvider.SetOTACandidates(candidates);
  341. }
  342. if ((gOtaFilepath == nullptr) && (gOtaImageListFilepath == nullptr))
  343. {
  344. ChipLogError(SoftwareUpdate, "Either an OTA file or image list file must be specified");
  345. chipDie();
  346. }
  347. chip::app::Clusters::OTAProvider::SetDelegate(kOtaProviderEndpoint, &gOtaProvider);
  348. }
  349. void ApplicationShutdown() {}
  350. int main(int argc, char * argv[])
  351. {
  352. VerifyOrDie(ChipLinuxAppInit(argc, argv, &cmdLineOptions) == 0);
  353. ChipLinuxAppMainLoop();
  354. return 0;
  355. }