docs/upgrading.md
AttributePersistenceProviderAttributePersistenceProvider was moved to src/app/persistence and its
interface was updated:
This update was done so that the interface is decoupled from ember and metadata types. The reasons for this approach:
Callers will validate data validity on read instead of relying on data validation by the persistence provider.
See https://github.com/project-chip/connectedhomeip/pull/39693 for changes.
The only change is that the EmberAfAttributeMetadata argument is not passed in
anymore into Read and implementations are expected to just return the opaque
data stored. API updates changed:
FROM OLD
ReturnErrorOnFailure(ReadValue(aPath, aMetadata, aByteSpan));
TO NEW
ReturnErrorOnFailure(ReadValue(aPath, aByteSpan));
ReturnErrorOnFailure(ValidateData(aByteSpan, aMetadata));
Where callers would implement ValidateData. Implementations do not have
the use of aMetadata anymore and cluster data is opaque now.
CommandHandlerCommandHandler ability to directly invoke Prepare/TLV-Write/Finish cycles
has been changed to only expose AddResponse/AddStatus/AddClusterSpecific*.
Original versions of CommandHandler exposed the following low-level
implementation-specific methods: PrepareCommand,
PrepareInvokeResponseCommand, GetCommandDataIBTLVWriter and FinishCommand.
These are not exposed anymore and instead one should use AddResponse or
AddResponseData. When using an EncodableToTLV argument, the same
functionality should be achievable.
Example
Before:
const CommandHandler::InvokeResponseParameters prepareParams(requestPath);
ReturnOnFailure(commandHandler->PrepareInvokeResponseCommand(path, prepareParams));
TLV::TLVWriter *writer = commandHandler->GetCommandDataIBTLVWriter();
ReturnOnFailure(writer->Put(chip::TLV::ContextTag(1), 123));
ReturnOnFailure(writer->Put(chip::TLV::ContextTag(2), 234));
return commandHandler->FinishCommand();
After:
class ReplyEncoder : public DataModel::EncodableToTLV
{
public:
CHIP_ERROR EncodeTo(TLV::TLVWriter & writer, TLV::Tag tag) const override
{
TLV::TLVType outerType;
ReturnErrorOnFailure(writer.StartContainer(tag, TLV::kTLVType_Structure, outerType));
ReturnOnFailure(writer.Put(chip::TLV::ContextTag(1), 123));
ReturnOnFailure(writer.Put(chip::TLV::ContextTag(2), 234));
return writer.EndContainer(outerType);
}
};
// ...
ReplyEncoder replyEncoder;
commandHandler->AddResponse(path, kReplyCommandId, replyEncoder);
// or if error handling is implemented:
//
// ReturnErrorOnFailure(commandHandler->AddResponseData(path, kReplyCommandId, replyEncoder));
//
// In many cases error recovery from not being able to send a reply is not easy or expected,
// so code does AddResponse rather than AddResponseData.
CommandHandlerInterface from EmberTestCodegenModelViaMocks.cpp:1513:77 MetadataCommandHandler Interface was coupled with Ember data in ways that caused bugs if not set up correctly, updates were made for decoupling, now this data is provided through the new interface.
With this the interfaces
CommandHandlerInterface::RetrieveGeneratedCommands,
CommandHandlerInterface::RetrieveAcceptedCommands
go through some changes, a shim is provided to make the transition simpler
Changed the old callback based iteration into a ListBuilder based approach for the enumeration of generated commands
CommandHandlerInterface::EnumerateGeneratedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context)
becomes
CommandHandlerInterface::RetrieveGeneratedCommands(const ConcreteClusterPath & cluster, ListBuilder<CommandId> & builder)
Changes for implementation
for (auto && cmd : { ScanNetworksResponse::Id, NetworkConfigResponse::Id, ConnectNetworkResponse::Id })
{
VerifyOrExit(callback(cmd, context) == Loop::Continue, /**/);
}
ReturnErrorOnFailure(
builder.AppendElements({ ScanNetworksResponse::Id, NetworkConfigResponse::Id, ConnectNetworkResponse::Id })
)
Changed the old callback based iteration into a ListBuilder based approach for the enumeration of accepted commands. The new Interface allows for the full metadata lookup.
CommandHandlerInterface::EnumerateAcceptedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context)
becomes
CommandHandlerInterface::RetrieveAcceptedCommands(const ConcreteClusterPath & cluster, ListBuilder<AcceptedCommandEntry> & builder)
Changes for implementation:
for (auto && cmd : {
Disable::Id,
EnableCharging::Id,
})
{
VerifyOrExit(callback(cmd, context) == Loop::Continue, /**/);
}
if (HasFeature(Feature::kV2x))
{
VerifyOrExit(callback(EnableDischarging::Id, context) == Loop::Continue, /**/);
}
ReturnErrorOnFailure(builder.AppendElements({
Disable::kMetadataEntry,
EnableCharging::kMetadataEntry,
}));
if (HasFeature(Feature::kV2x))
{
ReturnErrorOnFailure(builder.EnsureAppendCapacity(1));
ReturnErrorOnFailure(
builder.Append(EnableDischarging::kMetadataEntry)
);
}
Important Notes:
Use EnsureAppendCapacity before ListBuilder::Append to prevent buffer
overflow when appending a single element, this function never allocates.
If the changes above are too high friction for upgrading, we provide a shim that allows implementing these changes with a very minimal change. It adds a little extra code size compared to the code above but should be decent for most cases.
To use this shim just replace inheriting from CommandHandlerInterface with inheriting from CommandHandlerInterfaceShim<> with a list of the cluster IDs required for your implementation
- #include <app/CommandHandlerInterface.h>
+ #include <app/CommandHandlerInterfaceShim.h>
...
- class Instance : public CommandHandlerInterface,
+ class Instance : public CommandHandlerInterfaceShim<NetworkCommissioning::Id>
CommandHandlerInterface in chip::app::InteractionModelEngineCommand handler lists were placed in a separate registry class that is independent of the InteractionModelEngine class.
The following replacements exist:
chip::app::InteractionModelEngine::RegisterCommandHandler replaced by
chip::app::CommandHandlerInterfaceRegistry::Instance().RegisterCommandHandlerchip::app::InteractionModelEngine::UnregisterCommandHandler replaced by
chip::app::CommandHandlerInterfaceRegistry::Instance().UnregisterCommandHandlerchip::app::InteractionModelEngine::FindCommandHandler replaced by
chip::app::CommandHandlerInterfaceRegistry::Instance().GetCommandHandlerchip::app::InteractionModelEngine::UnregisterCommandHandlers replaced by
chip::app::CommandHandlerInterfaceRegistry::Instance().UnregisterAllCommandHandlersForEndpointA new object exists for the attribute access interface registry, accessible as
chip::app::AttributeHandlerInterfaceRegistry::Instance()
Replacements for methods are:
registerAttributeAccessOverride replaced by
chip::app::AttributeAccessInterfaceRegistry::Instance().RegisterunregisterAttributeAccessOverride replaced by
chip::app::AttributeAccessInterfaceRegistry::Instance().UnregisterunregisterAllAttributeAccessOverridesForEndpoint replaced by
chip::app::AttributeAccessInterfaceRegistry::Instance().UnregisterAllForEndpointchip::app::GetAttributeAccessOverride replaced by
chip::app::AttributeAccessInterfaceRegistry::Instance().GetServerInitParams::dataModelProvider in Server::Init and FactoryInitParamsServer and controller initialization require a set data model provider to work rather than auto-initializing ember-compatible code-generated data models.
To preserve codegen/zap generated logic, use
CodegenDataModelProviderInstance (see changes in
36558 and
36613 ).
To use default attribute persistence, you need to pass in a
PersistentStorageDelegate to CodegenDataModelProviderInstance. See example
changes in 36658
).