main.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. /*
  2. *
  3. * Copyright (c) 2021 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 "AppMain.h"
  19. #include <app/clusters/ota-requestor/BDXDownloader.h>
  20. #include <app/clusters/ota-requestor/DefaultOTARequestor.h>
  21. #include <app/clusters/ota-requestor/DefaultOTARequestorStorage.h>
  22. #include <app/clusters/ota-requestor/DefaultOTARequestorUserConsent.h>
  23. #include <app/clusters/ota-requestor/ExtendedOTARequestorDriver.h>
  24. #include <app/util/af.h>
  25. #include <platform/Linux/OTAImageProcessorImpl.h>
  26. using chip::BDXDownloader;
  27. using chip::ByteSpan;
  28. using chip::CharSpan;
  29. using chip::EndpointId;
  30. using chip::FabricIndex;
  31. using chip::GetRequestorInstance;
  32. using chip::NodeId;
  33. using chip::OnDeviceConnected;
  34. using chip::OnDeviceConnectionFailure;
  35. using chip::OTADownloader;
  36. using chip::OTAImageProcessorImpl;
  37. using chip::PeerId;
  38. using chip::Server;
  39. using chip::VendorId;
  40. using chip::app::Clusters::OtaSoftwareUpdateRequestor::OTAUpdateStateEnum;
  41. using chip::Callback::Callback;
  42. using chip::System::Layer;
  43. using chip::Transport::PeerAddress;
  44. using namespace chip;
  45. using namespace chip::app;
  46. using namespace chip::ArgParser;
  47. using namespace chip::DeviceLayer;
  48. using namespace chip::Messaging;
  49. using namespace chip::app::Clusters::OtaSoftwareUpdateProvider::Commands;
  50. class CustomOTARequestorDriver : public DeviceLayer::ExtendedOTARequestorDriver
  51. {
  52. public:
  53. bool CanConsent() override;
  54. void UpdateDownloaded() override;
  55. };
  56. DefaultOTARequestor gRequestorCore;
  57. DefaultOTARequestorStorage gRequestorStorage;
  58. CustomOTARequestorDriver gRequestorUser;
  59. BDXDownloader gDownloader;
  60. OTAImageProcessorImpl gImageProcessor;
  61. chip::ota::DefaultOTARequestorUserConsent gUserConsentProvider;
  62. static chip::ota::UserConsentState gUserConsentState = chip::ota::UserConsentState::kUnknown;
  63. bool HandleOptions(const char * aProgram, OptionSet * aOptions, int aIdentifier, const char * aName, const char * aValue);
  64. constexpr uint16_t kOptionAutoApplyImage = 'a';
  65. constexpr uint16_t kOptionRequestorCanConsent = 'c';
  66. constexpr uint16_t kOptionDisableNotify = 'd';
  67. constexpr uint16_t kOptionOtaDownloadPath = 'f';
  68. constexpr uint16_t kOptionPeriodicQueryTimeout = 'p';
  69. constexpr uint16_t kOptionUserConsentState = 'u';
  70. constexpr uint16_t kOptionWatchdogTimeout = 'w';
  71. constexpr size_t kMaxFilePathSize = 256;
  72. uint32_t gPeriodicQueryTimeoutSec = 0;
  73. uint32_t gWatchdogTimeoutSec = 0;
  74. chip::Optional<bool> gRequestorCanConsent;
  75. static char gOtaDownloadPath[kMaxFilePathSize] = "/tmp/test.bin";
  76. bool gAutoApplyImage = false;
  77. bool gSendNotifyUpdateApplied = true;
  78. OptionDef cmdLineOptionsDef[] = {
  79. { "autoApplyImage", chip::ArgParser::kNoArgument, kOptionAutoApplyImage },
  80. { "requestorCanConsent", chip::ArgParser::kArgumentRequired, kOptionRequestorCanConsent },
  81. { "disableNotifyUpdateApplied", chip::ArgParser::kNoArgument, kOptionDisableNotify },
  82. { "otaDownloadPath", chip::ArgParser::kArgumentRequired, kOptionOtaDownloadPath },
  83. { "periodicQueryTimeout", chip::ArgParser::kArgumentRequired, kOptionPeriodicQueryTimeout },
  84. { "userConsentState", chip::ArgParser::kArgumentRequired, kOptionUserConsentState },
  85. { "watchdogTimeout", chip::ArgParser::kArgumentRequired, kOptionWatchdogTimeout },
  86. {},
  87. };
  88. // Options for various test scenarios
  89. OptionSet cmdLineOptions = {
  90. HandleOptions, cmdLineOptionsDef, "PROGRAM OPTIONS",
  91. " -a, --autoApplyImage\n"
  92. " If supplied, apply the image immediately after download.\n"
  93. " Otherwise, the OTA update is complete after image download.\n"
  94. " -c, --requestorCanConsent <true | false>\n"
  95. " Value for the RequestorCanConsent field in the QueryImage command.\n"
  96. " If not supplied, the value is determined by the driver.\n"
  97. " -d, --disableNotifyUpdateApplied\n"
  98. " If supplied, disable sending of the NotifyUpdateApplied command.\n"
  99. " Otherwise, after successfully loading into the updated image, send the NotifyUpdateApplied command.\n"
  100. " -f, --otaDownloadPath <file path>\n"
  101. " If supplied, the OTA image is downloaded to the given fully-qualified file-path.\n"
  102. " Otherwise, the default location for the downloaded image is at /tmp/test.bin\n"
  103. " -p, --periodicQueryTimeout <time in seconds>\n"
  104. " The periodic time interval to wait before attempting to query a provider from the default OTA provider list.\n"
  105. " If none or zero is supplied, the timeout is determined by the driver.\n"
  106. " -u, --userConsentState <granted | denied | deferred>\n"
  107. " Represents the current user consent status when the OTA Requestor is acting as a user consent\n"
  108. " delegate. This value is only applicable if value of the UserConsentNeeded field in the\n"
  109. " QueryImageResponse is set to true. This value is used for the first attempt to\n"
  110. " download. For all subsequent queries, the value of granted will be used.\n"
  111. " granted: Authorize OTA requestor to download an OTA image\n"
  112. " denied: Forbid OTA requestor to download an OTA image\n"
  113. " deferred: Defer obtaining user consent\n"
  114. " -w, --watchdogTimeout <time in seconds>\n"
  115. " Maximum amount of time allowed for an OTA download before the process is cancelled and state reset to idle.\n"
  116. " If none or zero is supplied, the timeout is determined by the driver.\n"
  117. };
  118. OptionSet * allOptions[] = { &cmdLineOptions, nullptr };
  119. // Network commissioning
  120. namespace {
  121. constexpr EndpointId kNetworkCommissioningEndpointSecondary = 0xFFFE;
  122. } // namespace
  123. bool CustomOTARequestorDriver::CanConsent()
  124. {
  125. return gRequestorCanConsent.ValueOr(DeviceLayer::ExtendedOTARequestorDriver::CanConsent());
  126. }
  127. void CustomOTARequestorDriver::UpdateDownloaded()
  128. {
  129. if (gAutoApplyImage)
  130. {
  131. // Let the default driver take further action to apply the image.
  132. // All member variables will be implicitly reset upon loading into the new image.
  133. DefaultOTARequestorDriver::UpdateDownloaded();
  134. }
  135. else
  136. {
  137. // Download complete but we're not going to apply image, so reset provider retry counter.
  138. mProviderRetryCount = 0;
  139. // Reset to put the state back to idle to allow the next OTA update to occur
  140. gRequestorCore.Reset();
  141. }
  142. }
  143. static void InitOTARequestor(void)
  144. {
  145. // Set the global instance of the OTA requestor core component
  146. SetRequestorInstance(&gRequestorCore);
  147. // Periodic query timeout must be set prior to the driver being initialized
  148. gRequestorUser.SetPeriodicQueryTimeout(gPeriodicQueryTimeoutSec);
  149. // Watchdog timeout can be set any time before a query image is sent
  150. gRequestorUser.SetWatchdogTimeout(gWatchdogTimeoutSec);
  151. gRequestorUser.SetSendNotifyUpdateApplied(gSendNotifyUpdateApplied);
  152. gRequestorStorage.Init(chip::Server::GetInstance().GetPersistentStorage());
  153. gRequestorCore.Init(chip::Server::GetInstance(), gRequestorStorage, gRequestorUser, gDownloader);
  154. gRequestorUser.Init(&gRequestorCore, &gImageProcessor);
  155. gImageProcessor.SetOTAImageFile(gOtaDownloadPath);
  156. gImageProcessor.SetOTADownloader(&gDownloader);
  157. // Set the image processor instance used for handling image being downloaded
  158. gDownloader.SetImageProcessorDelegate(&gImageProcessor);
  159. if (gUserConsentState != chip::ota::UserConsentState::kUnknown)
  160. {
  161. gUserConsentProvider.SetUserConsentState(gUserConsentState);
  162. gRequestorUser.SetUserConsentDelegate(&gUserConsentProvider);
  163. }
  164. }
  165. bool HandleOptions(const char * aProgram, OptionSet * aOptions, int aIdentifier, const char * aName, const char * aValue)
  166. {
  167. bool retval = true;
  168. switch (aIdentifier)
  169. {
  170. case kOptionPeriodicQueryTimeout:
  171. gPeriodicQueryTimeoutSec = static_cast<uint32_t>(strtoul(aValue, NULL, 0));
  172. break;
  173. case kOptionRequestorCanConsent:
  174. if (strcmp(aValue, "true") == 0)
  175. {
  176. gRequestorCanConsent.SetValue(true);
  177. }
  178. else if (strcmp(aValue, "false") == 0)
  179. {
  180. gRequestorCanConsent.SetValue(false);
  181. }
  182. else
  183. {
  184. ChipLogError(SoftwareUpdate, "%s: ERROR: Invalid requestorCanConsent parameter: %s\n", aProgram, aValue);
  185. retval = false;
  186. }
  187. break;
  188. case kOptionOtaDownloadPath:
  189. chip::Platform::CopyString(gOtaDownloadPath, aValue);
  190. break;
  191. case kOptionUserConsentState:
  192. if (strcmp(aValue, "granted") == 0)
  193. {
  194. gUserConsentState = chip::ota::UserConsentState::kGranted;
  195. }
  196. else if (strcmp(aValue, "denied") == 0)
  197. {
  198. gUserConsentState = chip::ota::UserConsentState::kDenied;
  199. }
  200. else if (strcmp(aValue, "deferred") == 0)
  201. {
  202. gUserConsentState = chip::ota::UserConsentState::kObtaining;
  203. }
  204. else
  205. {
  206. ChipLogError(SoftwareUpdate, "%s: ERROR: Invalid UserConsent parameter: %s\n", aProgram, aValue);
  207. retval = false;
  208. }
  209. break;
  210. case kOptionAutoApplyImage:
  211. gAutoApplyImage = true;
  212. break;
  213. case kOptionWatchdogTimeout:
  214. gWatchdogTimeoutSec = static_cast<uint32_t>(strtoul(aValue, NULL, 0));
  215. break;
  216. case kOptionDisableNotify:
  217. // By default, NotifyUpdateApplied should always be sent. In the presence of this option, disable sending of the command.
  218. gSendNotifyUpdateApplied = false;
  219. break;
  220. default:
  221. ChipLogError(SoftwareUpdate, "%s: INTERNAL ERROR: Unhandled option: %s\n", aProgram, aName);
  222. retval = false;
  223. break;
  224. }
  225. return (retval);
  226. }
  227. void ApplicationInit()
  228. {
  229. // Initialize all OTA download components
  230. InitOTARequestor();
  231. }
  232. void ApplicationShutdown() {}
  233. int main(int argc, char * argv[])
  234. {
  235. VerifyOrDie(ChipLinuxAppInit(argc, argv, &cmdLineOptions, MakeOptional(kNetworkCommissioningEndpointSecondary)) == 0);
  236. ChipLinuxAppMainLoop();
  237. // If the event loop had been stopped due to an update being applied, boot into the new image
  238. if (gRequestorCore.GetCurrentUpdateState() == OTAUpdateStateEnum::kApplying)
  239. {
  240. if (kMaxFilePathSize <= strlen(kImageExecPath))
  241. {
  242. ChipLogError(SoftwareUpdate, "Buffer too small for the new image file path: %s", kImageExecPath);
  243. return -1;
  244. }
  245. argv[0] = kImageExecPath;
  246. execv(argv[0], argv);
  247. // If successfully executing the new image, execv should not return
  248. ChipLogError(SoftwareUpdate, "The OTA image is invalid");
  249. }
  250. return 0;
  251. }