Skip to content

Commit 78b6ae5

Browse files
Add basic support for App Install Flow (project-chip#33445)
* Add logic for the basic app installation flow * Add more logic * Update basic logic * Update codebase * Update code * Update code * Add missing content apps * Update TODO comment * Update code per comments * Update Logic per comments * Update code to make android work * Update per comments * Restyled by whitespace * Restyled by clang-format * Restyled by prettier-markdown --------- Co-authored-by: Restyled.io <commits@restyled.io>
1 parent f9c8d63 commit 78b6ae5

File tree

8 files changed

+235
-14
lines changed

8 files changed

+235
-14
lines changed

examples/tv-app/android/java/MyUserPrompter-JNI.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,15 @@ bool JNIMyUserPrompter::DisplaysPasscodeAndQRCode()
227227
return false;
228228
}
229229

230+
/**
231+
* Called to prompt the user for consent to allow the app commissioneeName/vendorId/productId to be installed.
232+
* For example "[commissioneeName] is requesting permission to install app to this TV, approve?"
233+
*/
234+
void JNIMyUserPrompter::PromptForAppInstallOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName)
235+
{
236+
ChipLogError(Zcl, "JNIMyUserPrompter::PromptForAppInstallOKPermission Needs Implementation");
237+
}
238+
230239
/**
231240
* Called to display the given setup passcode to the user,
232241
* for commissioning the given commissioneeName with the given vendorId and productId,

examples/tv-app/android/java/MyUserPrompter-JNI.h

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class JNIMyUserPrompter : public UserPrompter
2929
void PromptForCommissionOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override;
3030
void PromptForCommissionPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName, uint16_t pairingHint,
3131
const char * pairingInstruction) override;
32+
void PromptForAppInstallOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override;
3233
void HidePromptsOnCancel(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override;
3334
bool DisplaysPasscodeAndQRCode() override;
3435
void PromptWithCommissionerPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName, uint32_t passcode,

examples/tv-app/linux/README.md

+5
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ id):
107107
$ app add 9050 (vendor id 9050)
108108
$ app remove 1
109109

110+
You can also install or uninstall the app by using commands:
111+
112+
$ app install 65521 32768
113+
$ app uninstall 65521 32768
114+
110115
As an app platform, local apps can be used to facilitate commissioning using
111116
their AccountLogin clusters. The dummy apps have hardcoded setup codes - on a
112117
real device, these apps would communicate with a cloud service to obtain the

examples/tv-app/tv-common/include/AppTv.h

+10-7
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ class DLL_EXPORT ContentAppImpl : public ContentApp
9191
KeypadInputDelegate * GetKeypadInputDelegate() override { return &mKeypadInputDelegate; };
9292
MediaPlaybackDelegate * GetMediaPlaybackDelegate() override { return &mMediaPlaybackDelegate; };
9393
TargetNavigatorDelegate * GetTargetNavigatorDelegate() override { return &mTargetNavigatorDelegate; };
94+
bool MatchesPidVid(uint16_t productId, uint16_t vendorId)
95+
{
96+
return vendorId == mApplicationBasicDelegate.HandleGetVendorId() &&
97+
productId == mApplicationBasicDelegate.HandleGetProductId();
98+
}
9499

95100
protected:
96101
ApplicationBasicManager mApplicationBasicDelegate;
@@ -138,15 +143,13 @@ class DLL_EXPORT ContentAppFactoryImpl : public ContentAppFactory
138143
uint16_t productId) override;
139144

140145
void AddAdminVendorId(uint16_t vendorId);
146+
// Add the app to the list of mContentApps
147+
void InstallContentApp(uint16_t vendorId, uint16_t productId);
148+
// Remove the app from the list of mContentApps
149+
bool UninstallContentApp(uint16_t vendorId, uint16_t productId);
141150

142151
protected:
143-
ContentAppImpl mContentApps[APP_LIBRARY_SIZE] = {
144-
ContentAppImpl("Vendor1", 1, "exampleid", 11, "Version1", "34567890"),
145-
ContentAppImpl("Vendor2", 65521, "exampleString", 32768, "Version2", "20202021"),
146-
ContentAppImpl("Vendor3", 9050, "App3", 22, "Version3", "20202021"),
147-
ContentAppImpl("TestSuiteVendor", 1111, "applicationId", 22, "v2", "20202021")
148-
};
149-
152+
std::vector<std::unique_ptr<ContentAppImpl>> mContentApps;
150153
std::vector<uint16_t> mAdminVendorIds{};
151154
};
152155

examples/tv-app/tv-common/shell/AppTvShellCommands.cpp

+49
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,55 @@ static CHIP_ERROR AppPlatformHandler(int argc, char ** argv)
243243

244244
return CHIP_NO_ERROR;
245245
}
246+
else if (strcmp(argv[0], "install") == 0)
247+
{
248+
if (argc < 2)
249+
{
250+
return PrintAllCommands();
251+
}
252+
char * eptr;
253+
254+
uint16_t vid = (uint16_t) strtol(argv[1], &eptr, 10);
255+
uint16_t pid = 0;
256+
if (argc >= 3)
257+
{
258+
pid = (uint16_t) strtol(argv[2], &eptr, 10);
259+
}
260+
ContentAppFactoryImpl * factory = GetContentAppFactoryImpl();
261+
factory->InstallContentApp(vid, pid);
262+
263+
ChipLogProgress(DeviceLayer, "installed an app");
264+
265+
return CHIP_NO_ERROR;
266+
}
267+
else if (strcmp(argv[0], "uninstall") == 0)
268+
{
269+
if (argc < 2)
270+
{
271+
return PrintAllCommands();
272+
}
273+
char * eptr;
274+
275+
uint16_t vid = (uint16_t) strtol(argv[1], &eptr, 10);
276+
uint16_t pid = 0;
277+
if (argc >= 3)
278+
{
279+
pid = (uint16_t) strtol(argv[2], &eptr, 10);
280+
}
281+
ContentAppFactoryImpl * factory = GetContentAppFactoryImpl();
282+
bool isAppUninstalled = factory->UninstallContentApp(vid, pid);
283+
284+
if (isAppUninstalled)
285+
{
286+
ChipLogProgress(DeviceLayer, "uninstalled an app");
287+
}
288+
else
289+
{
290+
ChipLogProgress(DeviceLayer, "app not found.");
291+
}
292+
293+
return CHIP_NO_ERROR;
294+
}
246295
else if (strcmp(argv[0], "add") == 0)
247296
{
248297
if (argc < 2)

examples/tv-app/tv-common/src/AppTv.cpp

+87-6
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,12 @@ class MyUserPrompter : public UserPrompter
9898

9999
// tv should override this with a dialog prompt
100100
inline void PromptCommissioningFailed(const char * commissioneeName, CHIP_ERROR error) override { return; }
101+
102+
// tv should override this with a dialog prompt
103+
inline void PromptForAppInstallOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override
104+
{
105+
return;
106+
}
101107
};
102108

103109
MyUserPrompter gMyUserPrompter;
@@ -146,6 +152,16 @@ class MyPasscodeService : public PasscodeService
146152
};
147153
MyPasscodeService gMyPasscodeService;
148154

155+
class MyAppInstallationService : public AppInstallationService
156+
{
157+
bool LookupTargetContentApp(uint16_t vendorId, uint16_t productId) override
158+
{
159+
return ContentAppPlatform::GetInstance().LoadContentAppByClient(vendorId, productId) != nullptr;
160+
}
161+
};
162+
163+
MyAppInstallationService gMyAppInstallationService;
164+
149165
class MyPostCommissioningListener : public PostCommissioningListener
150166
{
151167
void CommissioningCompleted(uint16_t vendorId, uint16_t productId, NodeId nodeId, Messaging::ExchangeManager & exchangeMgr,
@@ -527,19 +543,23 @@ ContentApp * ContentAppFactoryImpl::LoadContentApp(const CatalogVendorApp & vend
527543
{
528544
ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl: LoadContentAppByAppId catalogVendorId=%d applicationId=%s ",
529545
vendorApp.catalogVendorId, vendorApp.applicationId);
546+
int index = 0;
530547

531-
for (size_t i = 0; i < ArraySize(mContentApps); ++i)
548+
for (auto & contentApp : mContentApps)
532549
{
533-
auto & app = mContentApps[i];
534550

535-
ChipLogProgress(DeviceLayer, " Looking next=%s ", app.GetApplicationBasicDelegate()->GetCatalogVendorApp()->applicationId);
536-
if (app.GetApplicationBasicDelegate()->GetCatalogVendorApp()->Matches(vendorApp))
551+
auto app = contentApp.get();
552+
553+
ChipLogProgress(DeviceLayer, " Looking next=%s ", app->GetApplicationBasicDelegate()->GetCatalogVendorApp()->applicationId);
554+
if (app->GetApplicationBasicDelegate()->GetCatalogVendorApp()->Matches(vendorApp))
537555
{
538-
ContentAppPlatform::GetInstance().AddContentApp(&app, &contentAppEndpoint, Span<DataVersion>(gDataVersions[i]),
556+
ContentAppPlatform::GetInstance().AddContentApp(app, &contentAppEndpoint, Span<DataVersion>(gDataVersions[index]),
539557
Span<const EmberAfDeviceType>(gContentAppDeviceType));
540-
return &app;
558+
return app;
541559
}
560+
index++;
542561
}
562+
543563
ChipLogProgress(DeviceLayer, "LoadContentAppByAppId NOT FOUND catalogVendorId=%d applicationId=%s ", vendorApp.catalogVendorId,
544564
vendorApp.applicationId);
545565

@@ -551,6 +571,62 @@ void ContentAppFactoryImpl::AddAdminVendorId(uint16_t vendorId)
551571
mAdminVendorIds.push_back(vendorId);
552572
}
553573

574+
void ContentAppFactoryImpl::InstallContentApp(uint16_t vendorId, uint16_t productId)
575+
{
576+
ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl: InstallContentApp vendorId=%d productId=%d ", vendorId, productId);
577+
if (vendorId == 1 && productId == 11)
578+
{
579+
mContentApps.emplace_back(
580+
std::make_unique<ContentAppImpl>("Vendor1", vendorId, "exampleid", productId, "Version1", "34567890"));
581+
}
582+
else if (vendorId == 65521 && productId == 32768)
583+
{
584+
mContentApps.emplace_back(
585+
std::make_unique<ContentAppImpl>("Vendor2", vendorId, "exampleString", productId, "Version2", "20202021"));
586+
}
587+
else if (vendorId == 9050 && productId == 22)
588+
{
589+
mContentApps.emplace_back(std::make_unique<ContentAppImpl>("Vendor3", vendorId, "App3", productId, "Version3", "20202021"));
590+
}
591+
else if (vendorId == 1111 && productId == 22)
592+
{
593+
mContentApps.emplace_back(
594+
std::make_unique<ContentAppImpl>("TestSuiteVendor", vendorId, "applicationId", productId, "v2", "20202021"));
595+
}
596+
else
597+
{
598+
mContentApps.emplace_back(
599+
std::make_unique<ContentAppImpl>("NewAppVendor", vendorId, "newAppApplicationId", productId, "v2", "20202021"));
600+
}
601+
}
602+
603+
bool ContentAppFactoryImpl::UninstallContentApp(uint16_t vendorId, uint16_t productId)
604+
{
605+
ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl: UninstallContentApp vendorId=%d productId=%d ", vendorId, productId);
606+
607+
int index = 0;
608+
for (auto & contentApp : mContentApps)
609+
{
610+
611+
auto app = contentApp.get();
612+
613+
ChipLogProgress(DeviceLayer, "Looking next vid=%d pid=%d", app->GetApplicationBasicDelegate()->HandleGetVendorId(),
614+
app->GetApplicationBasicDelegate()->HandleGetProductId());
615+
616+
if (app->MatchesPidVid(productId, vendorId))
617+
{
618+
ChipLogProgress(DeviceLayer, "Found an app vid=%d pid=%d. Uninstalling it.",
619+
app->GetApplicationBasicDelegate()->HandleGetVendorId(),
620+
app->GetApplicationBasicDelegate()->HandleGetProductId());
621+
mContentApps.erase(mContentApps.begin() + index);
622+
return true;
623+
}
624+
625+
index++;
626+
}
627+
return false;
628+
}
629+
554630
Access::Privilege ContentAppFactoryImpl::GetVendorPrivilege(uint16_t vendorId)
555631
{
556632
for (size_t i = 0; i < mAdminVendorIds.size(); ++i)
@@ -607,6 +683,10 @@ CHIP_ERROR AppTvInit()
607683
#if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED
608684
ContentAppPlatform::GetInstance().SetupAppPlatform();
609685
ContentAppPlatform::GetInstance().SetContentAppFactory(&gFactory);
686+
gFactory.InstallContentApp((uint16_t) 1, (uint16_t) 11);
687+
gFactory.InstallContentApp((uint16_t) 65521, (uint16_t) 32768);
688+
gFactory.InstallContentApp((uint16_t) 9050, (uint16_t) 22);
689+
gFactory.InstallContentApp((uint16_t) 1111, (uint16_t) 22);
610690
uint16_t value;
611691
if (DeviceLayer::GetDeviceInstanceInfoProvider()->GetVendorId(value) != CHIP_NO_ERROR)
612692
{
@@ -623,6 +703,7 @@ CHIP_ERROR AppTvInit()
623703
if (cdc != nullptr)
624704
{
625705
cdc->SetPasscodeService(&gMyPasscodeService);
706+
cdc->SetAppInstallationService(&gMyAppInstallationService);
626707
cdc->SetUserPrompter(&gMyUserPrompter);
627708
cdc->SetPostCommissioningListener(&gMyPostCommissioningListener);
628709
}

src/controller/CommissionerDiscoveryController.cpp

+30
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,34 @@ void CommissionerDiscoveryController::InternalOk()
219219
ChipLogError(AppServer, "UX InternalOk: could not find instance=%s", mCurrentInstance);
220220
return;
221221
}
222+
223+
if (mAppInstallationService == nullptr)
224+
{
225+
ChipLogError(AppServer, "UX InternalOk: no app installation service");
226+
return;
227+
}
228+
229+
if (!mAppInstallationService->LookupTargetContentApp(client->GetVendorId(), client->GetProductId()))
230+
{
231+
ChipLogDetail(AppServer, "UX InternalOk: app not installed.");
232+
233+
// notify client that app will be installed
234+
CommissionerDeclaration cd;
235+
cd.SetErrorCode(CommissionerDeclaration::CdError::kAppInstallConsentPending);
236+
mUdcServer->SendCDCMessage(cd, Transport::PeerAddress::UDP(client->GetPeerAddress().GetIPAddress(), client->GetCdPort()));
237+
238+
// dialog
239+
ChipLogDetail(Controller, "------PROMPT USER: %s is requesting to install app on this TV. vendorId=%d, productId=%d",
240+
client->GetDeviceName(), client->GetVendorId(), client->GetProductId());
241+
242+
if (mUserPrompter != nullptr)
243+
{
244+
mUserPrompter->PromptForAppInstallOKPermission(client->GetVendorId(), client->GetProductId(), client->GetDeviceName());
245+
}
246+
ChipLogDetail(Controller, "------Via Shell Enter: app install <pid> <vid>");
247+
return;
248+
}
249+
222250
if (client->GetUDCClientProcessingState() != UDCClientProcessingState::kPromptingUser)
223251
{
224252
ChipLogError(AppServer, "UX InternalOk: invalid state for ok");
@@ -244,6 +272,7 @@ void CommissionerDiscoveryController::InternalOk()
244272
CharSpan rotatingIdSpan(rotatingIdBuffer, 2 * rotatingIdLength);
245273

246274
uint8_t targetAppCount = client->GetNumTargetAppInfos();
275+
247276
if (targetAppCount > 0)
248277
{
249278
ChipLogDetail(AppServer, "UX InternalOk: checking for each target app specified");
@@ -583,6 +612,7 @@ void CommissionerDiscoveryController::Cancel()
583612
}
584613
client->SetUDCClientProcessingState(UDCClientProcessingState::kUserDeclined);
585614
mPendingConsent = false;
615+
ResetState();
586616
}
587617

588618
void CommissionerDiscoveryController::CommissioningSucceeded(uint16_t vendorId, uint16_t productId, NodeId nodeId,

src/controller/CommissionerDiscoveryController.h

+44-1
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,21 @@ class DLL_EXPORT UserPrompter
150150
*/
151151
virtual void PromptCommissioningFailed(const char * commissioneeName, CHIP_ERROR error) = 0;
152152

153+
/**
154+
* @brief
155+
* Called to prompt the user for consent to allow the app commissioneeName/vendorId/productId to be installed.
156+
* For example "[commissioneeName] is requesting permission to install app to this TV, approve?"
157+
*
158+
* If user responds with OK then implementor should call CommissionerRespondOk();
159+
* If user responds with Cancel then implementor should call CommissionerRespondCancel();
160+
*
161+
* @param[in] vendorId The vendorId in the DNS-SD advertisement of the requesting commissionee.
162+
* @param[in] productId The productId in the DNS-SD advertisement of the requesting commissionee.
163+
* @param[in] commissioneeName The commissioneeName in the DNS-SD advertisement of the requesting commissionee.
164+
*
165+
*/
166+
virtual void PromptForAppInstallOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName) = 0;
167+
153168
virtual ~UserPrompter() = default;
154169
};
155170

@@ -158,7 +173,7 @@ class DLL_EXPORT PasscodeService
158173
public:
159174
/**
160175
* @brief
161-
* Called to determine if the given target app is available to the commissionee with the given given
176+
* Called to determine if the given target app is available to the commissionee with the given
162177
* vendorId/productId, and if so, return the passcode.
163178
*
164179
* This will be called by the main chip thread so any blocking work should be moved to a separate thread.
@@ -204,6 +219,25 @@ class DLL_EXPORT PasscodeService
204219
virtual ~PasscodeService() = default;
205220
};
206221

222+
class DLL_EXPORT AppInstallationService
223+
{
224+
public:
225+
/**
226+
* @brief
227+
* Called to check if the given target app is available to the commissione with th given
228+
* vendorId/productId
229+
*
230+
* This will be called by the main chip thread so any blocking work should be moved to a separate thread.
231+
*
232+
* @param[in] vendorId The vendorId in the DNS-SD advertisement of the requesting commissionee.
233+
* @param[in] productId The productId in the DNS-SD advertisement of the requesting commissionee.
234+
*
235+
*/
236+
virtual bool LookupTargetContentApp(uint16_t vendorId, uint16_t productId) = 0;
237+
238+
virtual ~AppInstallationService() = default;
239+
};
240+
207241
class DLL_EXPORT PostCommissioningListener
208242
{
209243
public:
@@ -392,6 +426,14 @@ class CommissionerDiscoveryController : public chip::Protocols::UserDirectedComm
392426
inline void SetPasscodeService(PasscodeService * passcodeService) { mPasscodeService = passcodeService; }
393427
inline PasscodeService * GetPasscodeService() { return mPasscodeService; }
394428

429+
/**
430+
* Assign an AppInstallationService
431+
*/
432+
inline void SetAppInstallationService(AppInstallationService * appInstallationService)
433+
{
434+
mAppInstallationService = appInstallationService;
435+
}
436+
395437
/**
396438
* Assign a Commissioner Callback to perform commissioning once user consent has been given
397439
*/
@@ -430,6 +472,7 @@ class CommissionerDiscoveryController : public chip::Protocols::UserDirectedComm
430472
UserDirectedCommissioningServer * mUdcServer = nullptr;
431473
UserPrompter * mUserPrompter = nullptr;
432474
PasscodeService * mPasscodeService = nullptr;
475+
AppInstallationService * mAppInstallationService = nullptr;
433476
CommissionerCallback * mCommissionerCallback = nullptr;
434477
PostCommissioningListener * mPostCommissioningListener = nullptr;
435478
};

0 commit comments

Comments
 (0)