Configuration API Guide - C (Thin Core)
Obtain the Configuration service framework
The source code for this service framework can be found on the AllSeen Alliance gerrit page as a git project. In addition, the ajtcl project is needed to compile this service framework.
If the target platform already supports the AllJoyn™ Thin Library framework, follow the target platform documentation for detailed setup and download instructions.
If the target platform does not support the AllJoyn Thin Library framework, porting work is required to support this target. See the Introduction to AllJoyn Thin Library for more information about the AllJoyn Thin Library framework.
Reference code
The reference code consists of a module implementing the Config Server layer to provide remote access of ConfigData and device simple control. It also has a ConfigService-specific sample code for integrating with the ServerSample application code.
Config Server modules
Server module | Description |
---|---|
ConfigSample | This module is responsible for the general flow of the Config sample application including initialization, shutdown of the ConfigService module, and performing any ConfigService-related business logic when the application's message loop is idling. |
ConfigService | Config service core code. Implements the BusObjects and exposes the developer's API. |
PropertyStore | PropertyStore sample implementation code. This supports all core services. This module is part of the AppsCommon. |
ApplicationProvisioning | Application provisioning code for all services. This module is part of the sample application ServerSample or ACServerSample. |
Build a Config Server
The following steps provide the high-level process to build a Config Server.
- Create the base for the AllJoyn application. See the Build an Application using the Thin Library section for instructions.
- Implement the PropertyStore and provision its values.
- Implement the remote callbacks.
- (Optional) Integrate with the AuthListener's PasswordCallback to provide a remotely modifiable passcode based ALLJOYN_ECDHE_PSK secure connection.
Implementing the Config Server
Create the base for the AllJoyn application
See the Build an Application using the Thin Library section for instructions.
Provision the PropertyStore for the Configuration service framework
A PropertyStore structure is required by the ConfigService to store the provisioned values for the Config interface data fields listed below. See the Configuration Interface Definition for more information.
Config interface data fields
Field name | Required | Announced | Type |
---|---|---|---|
DefaultLanguage |
yes | yes | s |
DeviceName |
yes | yes | s |
PropertyStore implementation
The PropertyStore code is an example PropertyStore implementation that supports the requirements of ConfigService and is included in the AppsCommon code.
The PropertyStore uses the provisioning structures that are defined in the PropertyStoreOEMProvisioning.h file in the AppsCommon. A provisioning example is provided in the ServerSample code and is explained in the Build an Application using the Thin Library section.
Adding custom field definition and values
Complete the following steps to add your own custom fields.
- Decide to which subset the field belongs and add it to
the enumeration
AJSVC_PropertyStoreFieldIndices
accordingly. - Add a new field entry at the respective index to the propertyStoreProperties.
- Decide whether the field is publicly accessible from remote
clients. If the field is public then set the
mode7Public
bit to 1. - Decide whether the field is to be allowed to be configured
remotely through the Configuration service framework. If the
field is updateable, then set the
mode0Write
bit to 1. NOTE: If you set this bit, the field's index must be included in the Persisted or Config keys subsets. - Decide whether the field is to be included in the Announcement.
If the field is announced then set the
mode1Announce
bit to 1. NOTE: It is recommended to limit the inclusion of a field in the Announcement according to its immediate relevance to the relevant service framework's discovery. Only the value associated with the currentDefaultLanguage will be sent in the Announcement. - Decide whether the field is multi-language and add the
relevant values for the provisioned languages. If the field
is multi-language then set the
mode2MultiLng bit
to 1. - Decide whether the field is to be provisioned dynamically
in code and persisted during first-time (or post-factory reset)
device startup. If the field is to be initialized once, then
set the
mode3Init
bit to 1 and add the relevant code to initialize it.
Refer to PropertyStore.c PropertyStore_Init()
and
InitMandatoryPropertiesInRAM()
for an example setup for the
DeviceId and AppId fields.
NOTE: If you set this bit, the field's index must be included in the Persisted keys subsets.
- Add relevant validation of updated value for your custom key
by modifying the default implementation of
IsValueValid()
in ConfigSample.c file.
c
uint8_t IsValueValid(const char* key, const char* value) {return TRUE;}
- Add entry in corresponding index of propertyStoreDefaultValues to provision default value(s).
- Add entry in corresponding index of propertyStoreRuntimeValues to provision for runtime value(s) buffer(s).
NOTE: The example implementation of PropertyStore supports properties with value of type String ('s') only. If your property must be of a different type, you must provision the default value as a String and perform the relevant de/serialization on the client side.
Implement remote callbacks
The Configuration service framework has the following callbacks that allow for the application writer to react to remote initiated events and updating of configurable values. The prototype of these are defined in ConfigService.h .
- Restart-Restart the device or at least its Wi-Fi driver.
- FactoryReset-Clear persistent values and restore any factory defaults. Optionally, the application developer can mandate a power cycle of the device as well.
- SetPasscode-Persist new device passcode and revoke encryption keys generated from the previous passcode. Optionally, restart the device as well to cause current sessions to be re-established.
- IsValueValid-Validate a given value for a key and a specific language.
The ServerSample code provides the following in the ConfigSample module:
- Required callbacks defined in ConfigService.h and provided
to the service framework in the call to
AJCFG_Start()
. - Example implementation of these callbacks in the ConfigSample.c file.
Start the Config server
The Config server is required to be passed the provisioning
by the application via a call to AJCFG_Start()
.
An example is in Config_Init()
of ConfigSample.c:
/**
* Actions to perform when factory reset is requested.
*/
static AJ_Status FactoryReset() {...}
/**
* Actions to perform when a device restart is requested.
*/
static AJ_Status Restart() {...}
/**
* Actions to perform when a new device passcode is set.
*/
static AJ_Status SetPasscode(const char* daemonRealm, const uint8_t*
newPasscode, uint8_t newPasscodeLen) {...}
/**
* Check whether the given value is valid for the given key.
*/
static uint8_t IsValueValid(const char* key, const char* value) {...}
AJ_Status Config_Init()
{
AJ_Status status = AJCFG_Start(&FactoryReset, &Restart, &SetPasscode,
&IsValueValid);
return status;
}
Integrate the Configuration service framework with an application's AuthListener
The Configuration service framework and other AllJoyn service framework interface methods and signals require a secure AllJoyn connection. The application writer may choose to use an ALLJOYN_ECDHE_PSK authentication mechanism in its AuthListener implementation. The Configuration service framework enables remote setting of a password that can be used as the secret for a key exchange authentication mechanism. An example implementation that uses this facility is included in the sample server application in the Services_Handlers.c file as shown below.
uint32_t PasswordCallback(uint8_t* buffer, uint32_t bufLen)
{
AJ_Status status = AJ_OK;
const char* hexPassword;
size_t hexPasswordLen;
uint32_t len = 0;
hexPassword = AJSVC_PropertyStore_GetValue(AJSVC_PROPERTY_STORE_PASSCODE);
if (hexPassword == NULL) { AJ_ErrPrintf(("Password is NULL!\n")); return len;
}
AJ_InfoPrintf(("Retrieved password=%s\n", hexPassword));
hexPasswordLen = strlen(hexPassword);
len = hexPasswordLen / 2;
status = AJ_HexToRaw(hexPassword, hexPasswordLen, buffer, bufLen);
if (status == AJ_ERR_RESOURCES) {
len = 0;
}
The above implementation calls AJSVC_PropertyStore_GetValue
(AJSVC_PROPERTY_STORE_PASSCODE
) to retrieve the current password.
This implementation relies on an extension in the sample
implementation of the PropertyStore which was extended with
the Passcode field defined in the AJSCV_PropertyStoreFieldIndices
enumeration. The field is remotely updateable via a Configuration
service framework session using the dedicated SetPasscode()
method.
The stored Passcode is limited to the size of
65 allowing for 64 characters long secret:
#define PASSWORD_VALUE_LENGTH 65
This is achieved using the field definition that masks the field as writable yet private, as shown in the propertyStoreProperties initialization in PropertyStore.c:
{ "Passcode", 1, 0, 0, 0, 0, 0, 0, 0 }
The default value is provisioned in ServerSample.c as follows:
static const char* DEFAULT_PASSCODES[] = { "303030303030" };
// HEX encoded { '0', '0', '0', '0', '0', '0' }
and added to propertyStoreDefaultValues.
DEFAULT_PASSCODES, /*Passcode*/
Since the Passcode enumeration value is less than AJSVC_PROPERTY_STORE_NUMBER_OF_RUNTIME_KEYS
,
it is also considered part of the propertyStoreRuntimeValues
;
its modified value is persisted.
Also, when SetPasscode()
is called remotely, the SetPasscode()
callback is invoked with the example implementation in ConfigSample.c file shown below:
static AJ_Status SetPasscode(const char* daemonRealm,
const uint8_t* newPasscode, uint8_t newPasscodeLen)
{
AJ_Status status = AJ_OK;
char newStringPasscode[PASSWORD_VALUE_LENGTH + 1];
status = AJ_RawToHex(newPasscode, newPasscodeLen, newStringPasscode,
sizeof(newStringPasscode), FALSE);
if (status != AJ_OK) {
return status;
}
if (AJSVC_PropertyStore_SetValue(AJSVC_PROPERTY_STORE_REALM_NAME,
daemonRealm) && AJSVC_PropertyStore_SetValue(AJSVC_PROPERTY_STORE_PASSCODE,
newStringPasscode)) {
status = AJSVC_PropertyStore_SaveAll();
if (status != AJ_OK) {
return status;
}
AJ_ClearCredentials();
status = AJ_ERR_READ; //Force disconnect of AJ and services
to refresh current sessions
} else {
status = AJSVC_PropertyStore_LoadAll();
if (status != AJ_OK) {
return status;
}
}
return status;
}
The above implementation stores the passcode as part of the PropertyStore persistence:
- It calls
AJSVC_ PropertyStore_SetValue(Passcode, newStringPasscode)
to set the value in RAM. - If successful, it also calls
AJSVC_ PropertyStore_SaveAll()
to persist to NVRAM. - Finally,
AJ_ClearCredentials()
is called to revoke all current keys based on the old passcode.
NOTES
- The stored passcode is HEX and is limited to the size of 32, allowing for 16 bytes long secret:
#define PASSWORD_VALUE_LENGTH (AJ_ADHOC_LEN * 2)
- The default value for the Passcode field is provisioned as '303030303030' (the HEX encoded string value for the byte array { '0', '0', '0', '0', '0', '0'}) as per the requirement outlined in the [Configuration Interface Definition][config-interface-definition.
- The realm name is similarly persisted alongside the passcode
by adding the RealmNamefield index to the
AJSVC_PropertyStoreFieldIndices
enumeration and provisioning for it inpropertyStoreDefaultValues
andpropertyStoreRuntimeValues
initialization.
Compile the code
The process to compile varies depending on the host and target platform. Each host and platform needs may require a specific directory and file layout, build toolchains, procedures, and supported AllJoyn service frameworks. Refer to the target platform documentation that contains instructions on how to organize and set up the build process to incorporate the necessary files to compile your Thin Library application.
For more details on how to combine this AllJoyn service framework with other AllJoyn service framework software, see the Build an Application using the Thin Library section.