Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basic support for App Install Flow #33445

Merged
merged 26 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6fff237
Add logic for the basic app installation flow
lazarkov May 13, 2024
5839b4a
Add more logic
lazarkov May 14, 2024
3e9f59b
Merge branch 'project-chip:master' into feature/basic-app-install-flow
lazarkov May 14, 2024
e1f2e44
Update basic logic
lazarkov May 14, 2024
8a19733
Update codebase
lazarkov May 14, 2024
33b5912
Update code
lazarkov May 14, 2024
5407248
Merge branch 'master' into feature/basic-app-install-flow
lazarkov May 14, 2024
0613f7b
Update code
lazarkov May 14, 2024
2d0864b
Add missing content apps
lazarkov May 14, 2024
ca5ffb4
Update TODO comment
lazarkov May 14, 2024
45a5202
Update code per comments
lazarkov May 16, 2024
3b23b5b
Update Logic per comments
lazarkov May 17, 2024
fb174cc
Update code to make android work
lazarkov May 17, 2024
2ca78f9
Merge branch 'master' into feature/basic-app-install-flow
lazarkov May 20, 2024
5b2fc5c
Update per comments
lazarkov May 21, 2024
070261e
Restyled by whitespace
restyled-commits May 21, 2024
75be581
Restyled by clang-format
restyled-commits May 21, 2024
af2d200
Restyled by prettier-markdown
restyled-commits May 21, 2024
18750a0
Merge branch 'master' into feature/basic-app-install-flow
lazarkov May 21, 2024
ab88dc4
Merge branch 'master' into feature/basic-app-install-flow
lazarkov May 21, 2024
3ae484e
Merge branch 'master' into feature/basic-app-install-flow
lazarkov May 21, 2024
159cd5f
Merge branch 'master' into feature/basic-app-install-flow
lazarkov May 21, 2024
376cc35
Merge branch 'master' into feature/basic-app-install-flow
lazarkov May 21, 2024
5c4bff8
Merge branch 'master' into feature/basic-app-install-flow
lazarkov May 22, 2024
6a6dc09
Merge branch 'master' into feature/basic-app-install-flow
lazarkov May 22, 2024
f1e0f75
Merge branch 'master' into feature/basic-app-install-flow
lazarkov May 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions examples/tv-app/android/java/MyUserPrompter-JNI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,15 @@ bool JNIMyUserPrompter::DisplaysPasscodeAndQRCode()
return false;
}

/**
* Called to prompt the user for consent to allow the app commissioneeName/vendorId/productId to be installed.
* For example "[commissioneeName] is requesting permission to install app to this TV, approve?"
*/
void JNIMyUserPrompter::PromptForAppInstallOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName)
{
ChipLogError(Zcl, "JNIMyUserPrompter::PromptForAppInstallOKPermission Needs Implementation");
}

/**
* Called to display the given setup passcode to the user,
* for commissioning the given commissioneeName with the given vendorId and productId,
Expand Down
1 change: 1 addition & 0 deletions examples/tv-app/android/java/MyUserPrompter-JNI.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class JNIMyUserPrompter : public UserPrompter
void PromptForCommissionOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override;
void PromptForCommissionPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName, uint16_t pairingHint,
const char * pairingInstruction) override;
void PromptForAppInstallOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override;
void HidePromptsOnCancel(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override;
bool DisplaysPasscodeAndQRCode() override;
void PromptWithCommissionerPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName, uint32_t passcode,
Expand Down
5 changes: 5 additions & 0 deletions examples/tv-app/linux/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ id):
$ app add 9050 (vendor id 9050)
$ app remove 1

You can also install or uninstall the app by using commands:

$ app install 65521 32768
$ app uninstall 65521 32768

As an app platform, local apps can be used to facilitate commissioning using
their AccountLogin clusters. The dummy apps have hardcoded setup codes - on a
real device, these apps would communicate with a cloud service to obtain the
Expand Down
17 changes: 10 additions & 7 deletions examples/tv-app/tv-common/include/AppTv.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ class DLL_EXPORT ContentAppImpl : public ContentApp
KeypadInputDelegate * GetKeypadInputDelegate() override { return &mKeypadInputDelegate; };
MediaPlaybackDelegate * GetMediaPlaybackDelegate() override { return &mMediaPlaybackDelegate; };
TargetNavigatorDelegate * GetTargetNavigatorDelegate() override { return &mTargetNavigatorDelegate; };
bool MatchesPidVid(uint16_t productId, uint16_t vendorId)
{
return vendorId == mApplicationBasicDelegate.HandleGetVendorId() &&
productId == mApplicationBasicDelegate.HandleGetProductId();
}

protected:
ApplicationBasicManager mApplicationBasicDelegate;
Expand Down Expand Up @@ -138,15 +143,13 @@ class DLL_EXPORT ContentAppFactoryImpl : public ContentAppFactory
uint16_t productId) override;

void AddAdminVendorId(uint16_t vendorId);
// Add the app to the list of mContentApps
void InstallContentApp(uint16_t vendorId, uint16_t productId);
// Remove the app from the list of mContentApps
bool UninstallContentApp(uint16_t vendorId, uint16_t productId);

protected:
ContentAppImpl mContentApps[APP_LIBRARY_SIZE] = {
ContentAppImpl("Vendor1", 1, "exampleid", 11, "Version1", "34567890"),
ContentAppImpl("Vendor2", 65521, "exampleString", 32768, "Version2", "20202021"),
ContentAppImpl("Vendor3", 9050, "App3", 22, "Version3", "20202021"),
ContentAppImpl("TestSuiteVendor", 1111, "applicationId", 22, "v2", "20202021")
};

std::vector<std::unique_ptr<ContentAppImpl>> mContentApps;
std::vector<uint16_t> mAdminVendorIds{};
};

Expand Down
49 changes: 49 additions & 0 deletions examples/tv-app/tv-common/shell/AppTvShellCommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,55 @@ static CHIP_ERROR AppPlatformHandler(int argc, char ** argv)

return CHIP_NO_ERROR;
}
else if (strcmp(argv[0], "install") == 0)
{
if (argc < 2)
{
return PrintAllCommands();
}
char * eptr;

uint16_t vid = (uint16_t) strtol(argv[1], &eptr, 10);
uint16_t pid = 0;
if (argc >= 3)
{
pid = (uint16_t) strtol(argv[2], &eptr, 10);
}
ContentAppFactoryImpl * factory = GetContentAppFactoryImpl();
factory->InstallContentApp(vid, pid);

ChipLogProgress(DeviceLayer, "installed an app");

return CHIP_NO_ERROR;
}
else if (strcmp(argv[0], "uninstall") == 0)
{
if (argc < 2)
{
return PrintAllCommands();
}
char * eptr;

uint16_t vid = (uint16_t) strtol(argv[1], &eptr, 10);
uint16_t pid = 0;
if (argc >= 3)
{
pid = (uint16_t) strtol(argv[2], &eptr, 10);
}
ContentAppFactoryImpl * factory = GetContentAppFactoryImpl();
bool isAppUninstalled = factory->UninstallContentApp(vid, pid);

if (isAppUninstalled)
{
ChipLogProgress(DeviceLayer, "uninstalled an app");
}
else
{
ChipLogProgress(DeviceLayer, "app not found.");
}

return CHIP_NO_ERROR;
}
else if (strcmp(argv[0], "add") == 0)
{
if (argc < 2)
Expand Down
93 changes: 87 additions & 6 deletions examples/tv-app/tv-common/src/AppTv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ class MyUserPrompter : public UserPrompter

// tv should override this with a dialog prompt
inline void PromptCommissioningFailed(const char * commissioneeName, CHIP_ERROR error) override { return; }

// tv should override this with a dialog prompt
inline void PromptForAppInstallOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override
{
return;
}
};

MyUserPrompter gMyUserPrompter;
Expand Down Expand Up @@ -146,6 +152,16 @@ class MyPasscodeService : public PasscodeService
};
MyPasscodeService gMyPasscodeService;

class MyAppInstallationService : public AppInstallationService
{
bool LookupTargetContentApp(uint16_t vendorId, uint16_t productId) override
{
return ContentAppPlatform::GetInstance().LoadContentAppByClient(vendorId, productId) != nullptr;
}
};

MyAppInstallationService gMyAppInstallationService;

class MyPostCommissioningListener : public PostCommissioningListener
{
void CommissioningCompleted(uint16_t vendorId, uint16_t productId, NodeId nodeId, Messaging::ExchangeManager & exchangeMgr,
Expand Down Expand Up @@ -527,19 +543,23 @@ ContentApp * ContentAppFactoryImpl::LoadContentApp(const CatalogVendorApp & vend
{
ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl: LoadContentAppByAppId catalogVendorId=%d applicationId=%s ",
vendorApp.catalogVendorId, vendorApp.applicationId);
int index = 0;

for (size_t i = 0; i < ArraySize(mContentApps); ++i)
for (auto & contentApp : mContentApps)
{
auto & app = mContentApps[i];

ChipLogProgress(DeviceLayer, " Looking next=%s ", app.GetApplicationBasicDelegate()->GetCatalogVendorApp()->applicationId);
if (app.GetApplicationBasicDelegate()->GetCatalogVendorApp()->Matches(vendorApp))
auto app = contentApp.get();

ChipLogProgress(DeviceLayer, " Looking next=%s ", app->GetApplicationBasicDelegate()->GetCatalogVendorApp()->applicationId);
if (app->GetApplicationBasicDelegate()->GetCatalogVendorApp()->Matches(vendorApp))
{
ContentAppPlatform::GetInstance().AddContentApp(&app, &contentAppEndpoint, Span<DataVersion>(gDataVersions[i]),
ContentAppPlatform::GetInstance().AddContentApp(app, &contentAppEndpoint, Span<DataVersion>(gDataVersions[index]),
Span<const EmberAfDeviceType>(gContentAppDeviceType));
return &app;
return app;
}
index++;
}

ChipLogProgress(DeviceLayer, "LoadContentAppByAppId NOT FOUND catalogVendorId=%d applicationId=%s ", vendorApp.catalogVendorId,
vendorApp.applicationId);

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

void ContentAppFactoryImpl::InstallContentApp(uint16_t vendorId, uint16_t productId)
{
ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl: InstallContentApp vendorId=%d productId=%d ", vendorId, productId);
if (vendorId == 1 && productId == 11)
{
mContentApps.emplace_back(
std::make_unique<ContentAppImpl>("Vendor1", vendorId, "exampleid", productId, "Version1", "34567890"));
}
else if (vendorId == 65521 && productId == 32768)
{
mContentApps.emplace_back(
std::make_unique<ContentAppImpl>("Vendor2", vendorId, "exampleString", productId, "Version2", "20202021"));
}
else if (vendorId == 9050 && productId == 22)
{
mContentApps.emplace_back(std::make_unique<ContentAppImpl>("Vendor3", vendorId, "App3", productId, "Version3", "20202021"));
}
else if (vendorId == 1111 && productId == 22)
{
mContentApps.emplace_back(
std::make_unique<ContentAppImpl>("TestSuiteVendor", vendorId, "applicationId", productId, "v2", "20202021"));
}
else
{
mContentApps.emplace_back(
std::make_unique<ContentAppImpl>("NewAppVendor", vendorId, "newAppApplicationId", productId, "v2", "20202021"));
}
}

bool ContentAppFactoryImpl::UninstallContentApp(uint16_t vendorId, uint16_t productId)
{
ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl: UninstallContentApp vendorId=%d productId=%d ", vendorId, productId);

int index = 0;
for (auto & contentApp : mContentApps)
{

auto app = contentApp.get();

ChipLogProgress(DeviceLayer, "Looking next vid=%d pid=%d", app->GetApplicationBasicDelegate()->HandleGetVendorId(),
app->GetApplicationBasicDelegate()->HandleGetProductId());

if (app->MatchesPidVid(productId, vendorId))
{
ChipLogProgress(DeviceLayer, "Found an app vid=%d pid=%d. Uninstalling it.",
app->GetApplicationBasicDelegate()->HandleGetVendorId(),
app->GetApplicationBasicDelegate()->HandleGetProductId());
mContentApps.erase(mContentApps.begin() + index);
return true;
}

index++;
}
return false;
}

Access::Privilege ContentAppFactoryImpl::GetVendorPrivilege(uint16_t vendorId)
{
for (size_t i = 0; i < mAdminVendorIds.size(); ++i)
Expand Down Expand Up @@ -607,6 +683,10 @@ CHIP_ERROR AppTvInit()
#if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED
ContentAppPlatform::GetInstance().SetupAppPlatform();
ContentAppPlatform::GetInstance().SetContentAppFactory(&gFactory);
gFactory.InstallContentApp((uint16_t) 1, (uint16_t) 11);
gFactory.InstallContentApp((uint16_t) 65521, (uint16_t) 32768);
gFactory.InstallContentApp((uint16_t) 9050, (uint16_t) 22);
gFactory.InstallContentApp((uint16_t) 1111, (uint16_t) 22);
uint16_t value;
if (DeviceLayer::GetDeviceInstanceInfoProvider()->GetVendorId(value) != CHIP_NO_ERROR)
{
Expand All @@ -623,6 +703,7 @@ CHIP_ERROR AppTvInit()
if (cdc != nullptr)
{
cdc->SetPasscodeService(&gMyPasscodeService);
cdc->SetAppInstallationService(&gMyAppInstallationService);
cdc->SetUserPrompter(&gMyUserPrompter);
cdc->SetPostCommissioningListener(&gMyPostCommissioningListener);
}
Expand Down
30 changes: 30 additions & 0 deletions src/controller/CommissionerDiscoveryController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,34 @@ void CommissionerDiscoveryController::InternalOk()
ChipLogError(AppServer, "UX InternalOk: could not find instance=%s", mCurrentInstance);
return;
}

if (mAppInstallationService == nullptr)
{
ChipLogError(AppServer, "UX InternalOk: no app installation service");
return;
}

if (!mAppInstallationService->LookupTargetContentApp(client->GetVendorId(), client->GetProductId()))
{
ChipLogDetail(AppServer, "UX InternalOk: app not installed.");

// notify client that app will be installed
CommissionerDeclaration cd;
cd.SetErrorCode(CommissionerDeclaration::CdError::kAppInstallConsentPending);
mUdcServer->SendCDCMessage(cd, Transport::PeerAddress::UDP(client->GetPeerAddress().GetIPAddress(), client->GetCdPort()));

// dialog
ChipLogDetail(Controller, "------PROMPT USER: %s is requesting to install app on this TV. vendorId=%d, productId=%d",
client->GetDeviceName(), client->GetVendorId(), client->GetProductId());

if (mUserPrompter != nullptr)
{
mUserPrompter->PromptForAppInstallOKPermission(client->GetVendorId(), client->GetProductId(), client->GetDeviceName());
}
ChipLogDetail(Controller, "------Via Shell Enter: app install <pid> <vid>");
return;
}

if (client->GetUDCClientProcessingState() != UDCClientProcessingState::kPromptingUser)
{
ChipLogError(AppServer, "UX InternalOk: invalid state for ok");
Expand All @@ -244,6 +272,7 @@ void CommissionerDiscoveryController::InternalOk()
CharSpan rotatingIdSpan(rotatingIdBuffer, 2 * rotatingIdLength);

uint8_t targetAppCount = client->GetNumTargetAppInfos();

if (targetAppCount > 0)
{
ChipLogDetail(AppServer, "UX InternalOk: checking for each target app specified");
Expand Down Expand Up @@ -583,6 +612,7 @@ void CommissionerDiscoveryController::Cancel()
}
client->SetUDCClientProcessingState(UDCClientProcessingState::kUserDeclined);
mPendingConsent = false;
ResetState();
}

void CommissionerDiscoveryController::CommissioningSucceeded(uint16_t vendorId, uint16_t productId, NodeId nodeId,
Expand Down
45 changes: 44 additions & 1 deletion src/controller/CommissionerDiscoveryController.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,21 @@ class DLL_EXPORT UserPrompter
*/
virtual void PromptCommissioningFailed(const char * commissioneeName, CHIP_ERROR error) = 0;

/**
* @brief
* Called to prompt the user for consent to allow the app commissioneeName/vendorId/productId to be installed.
* For example "[commissioneeName] is requesting permission to install app to this TV, approve?"
*
* If user responds with OK then implementor should call CommissionerRespondOk();
* If user responds with Cancel then implementor should call CommissionerRespondCancel();
*
* @param[in] vendorId The vendorId in the DNS-SD advertisement of the requesting commissionee.
* @param[in] productId The productId in the DNS-SD advertisement of the requesting commissionee.
* @param[in] commissioneeName The commissioneeName in the DNS-SD advertisement of the requesting commissionee.
*
*/
virtual void PromptForAppInstallOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName) = 0;

virtual ~UserPrompter() = default;
};

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

class DLL_EXPORT AppInstallationService
{
public:
/**
* @brief
* Called to check if the given target app is available to the commissione with th given
* vendorId/productId
*
* This will be called by the main chip thread so any blocking work should be moved to a separate thread.
*
* @param[in] vendorId The vendorId in the DNS-SD advertisement of the requesting commissionee.
* @param[in] productId The productId in the DNS-SD advertisement of the requesting commissionee.
*
*/
virtual bool LookupTargetContentApp(uint16_t vendorId, uint16_t productId) = 0;

virtual ~AppInstallationService() = default;
};

class DLL_EXPORT PostCommissioningListener
{
public:
Expand Down Expand Up @@ -392,6 +426,14 @@ class CommissionerDiscoveryController : public chip::Protocols::UserDirectedComm
inline void SetPasscodeService(PasscodeService * passcodeService) { mPasscodeService = passcodeService; }
inline PasscodeService * GetPasscodeService() { return mPasscodeService; }

/**
* Assign an AppInstallationService
*/
inline void SetAppInstallationService(AppInstallationService * appInstallationService)
{
mAppInstallationService = appInstallationService;
}

/**
* Assign a Commissioner Callback to perform commissioning once user consent has been given
*/
Expand Down Expand Up @@ -430,6 +472,7 @@ class CommissionerDiscoveryController : public chip::Protocols::UserDirectedComm
UserDirectedCommissioningServer * mUdcServer = nullptr;
UserPrompter * mUserPrompter = nullptr;
PasscodeService * mPasscodeService = nullptr;
AppInstallationService * mAppInstallationService = nullptr;
CommissionerCallback * mCommissionerCallback = nullptr;
PostCommissioningListener * mPostCommissioningListener = nullptr;
};
Expand Down
Loading