System service framework - subsystem samgr\_lite introduction

Keywords: Java IoT

brief introduction

Due to the limited platform resources and various hardware platforms, it is necessary to shield the different hardware architectures, platform resources and operation forms, and provide a unified system service development framework. According to different hardware platforms of RISC-V, Cortex-M and Cortex-A, there are two hardware platforms, hereinafter referred to as M-core and A-core.

  • M-core: the processor architecture is Cortex-M or a hardware platform with the same processing capacity. The system memory is generally less than 512KB. There is no file system or only a lightweight file system that can be used with limited use, and it complies with the CMSIS interface specification.
  • Core A: the processor architecture is Cortex-A or A hardware platform with the same processing capacity. The memory resources are greater than 512KB, the file system is perfect, and can store A large amount of data. It follows the POSIX interface specification.

Based on the service-oriented architecture, the system service framework provides the development capabilities of service development, service sub function development, external interface development, multi service common process, inter process service invocation and so on. Of which:

  • M core: it includes service development, sub function development of services, development of external interfaces and development framework of multi service common process.
  • A core: Based on the M core capability, it includes interprocess service invocation, interprocess service invocation permission control, interprocess service interface development and other capabilities.

Service Oriented Architecture:

The architecture diagram is as follows:

  • Provider: a service provider that provides capabilities (external interfaces) for the system.
  • Consumer: the consumer of the service, which calls the functions provided by the service (external interface).
  • Samgr: as an intermediary, it manages the capabilities provided by the Provider and helps consumers discover the capabilities of the Provider.

System service development framework subject object

The working module diagram of the object is as follows:

  • SamgrLite: mainly provides service registration and discovery capabilities.
  • Service: the life cycle interface of the service to be implemented when developing a service.
  • Feature: the lifecycle interface of the function to be implemented when developing the function.
  • IUnknown: external interface for developing services or functions based on IUnknown.
  • IClientProxy: the message sending proxy of the consumer when the IPC is called.
  • IServerProxy: when calling IPC, developers need to implement the provider's message processing interface.

catalogue

Table 1 system service framework source code directory structure

name

describe

interfaces/kits/samgr_lite/samgr

External interface definition of M-core and A-core system service framework.

interfaces/kits/samgr_lite/registry

The external interface definition of A core interprocess service call.

interfaces/kits/samgr_lite/communication/broadcast

External interface definition of in-process event broadcasting service of M core and A core.

services/samgr_lite/samgr/adapter

POSIX and CMSIS interface adaptation layers are used to shield the differences between A-core and M-core interfaces.

services/samgr_lite/samgr/registry

The M-core service registers the discovered stake functions.

services/samgr_lite/samgr/source

Basic code of M-core and A-core system service development framework.

services/samgr_lite/samgr_client

A core is the registration and discovery of interprocess service calls.

services/samgr_lite/samgr_server

IPC address management and access control of A-core interprocess service call.

services/samgr_lite/samgr_endpoint

A nuclear IPC communication message receiving and contracting management.

services/samgr_lite/communication/broadcast

In process event broadcasting service for M core and A core.

constraint

  • The system service development framework is developed in C.
  • The IUnknown interface is used for calls between services in the same process, and the message interface is passed to the service through the IUnknown interface.
  • Service and function names must use constant strings and be less than 16 bytes in length.
  • M core: the system relies on bootstrap services and calls OHOS_ in the system boot function. Systeminit() function.
  • A core: the system relies on the samgr library and calls SAMGR_ in the main function. Bootstrap() function.

Main function module

Development services

  • Inherit and redefine services:
//Service Sample
    typedef struct ExampleService {
        INHERIT_SERVICE; //Inheritance services
        INHERIT_IUNKNOWNENTRY(DefaultFeatureApi);  //Unknown entry
        Identity identity; //identity authentication 
    } ExampleService;
  • Implement the lifecycle function of the service:
    // Get service name
    static const char *GetName(Service *service)
    {
        return EXAMPLE_SERVICE;  //Returns the service sample variable
    }
    
    //Service initialization
    static BOOL Initialize(Service *service, Identity identity)
    {
        ExampleService *example = (ExampleService *)service;
        // Save the unique identity of the service, which is used when sending messages to the service through its own IUnknown interface.
        example->identity = identity;
        return TRUE;
    }
    //Return to information processing example
    static BOOL MessageHandle(Service *service, Request *msg)
    {
        ExampleService *example = (ExampleService *)service;
        switch (msg->msgId) {
            case MSG_SYNC:
                // Business processing
                break;
            default:break;
        }
        return FALSE;
    }
    //Get task configuration method
    static TaskConfig GetTaskConfig(Service *service)
    {
        TaskConfig config = {LEVEL_HIGH, PRI_BELOW_NORMAL,
                             0x800, 20, SHARED_TASK};
        return config;
    }
  • Create service object:
//Set the static variable to get the service sample
    static ExampleService g_example = {
        .GetName = GetName, //Service name
        .Initialize = Initialize,//Service initialization
        .MessageHandle = MessageHandle, //information processing
        .GetTaskConfig = GetTaskConfig, //Get task configuration
        SERVER_IPROXY_IMPL_BEGIN,//Server ip port mapping started
            .Invoke = NULL,//Call request
            .SyncCall = SyncCall,//Backup record
        IPROXY_END,ip End of mapping
    };
  • Register services and interfaces with SAMGR:
    static void Init(void)
    {
    	//samgr get registration service instance
        SAMGR_GetInstance()->RegisterService((Service *)&g_example);
        //samgr get registered default feature Api instance
        SAMGR_GetInstance()->RegisterDefaultFeatureApi(EXAMPLE_SERVICE, GET_IUNKNOWN(g_example));
    }
  • Define the initialization entry of the service:
    SYSEX_SERVICE_INIT(Init);

Sub functions of development services

  • Inherit and redefine functions:
	//Define display feature structure
    typedef struct DemoFeature {
        INHERIT_FEATURE;//Inheritance characteristics
        INHERIT_IUNKNOWNENTRY(DemoApi);//Inherit fuzzy entry Api
        Identity identity;//Identity quantity
        Service *parent;//Parent service
    } DemoFeature;
  • Lifecycle functions that implement functions:
	//Get feature name
   static const char *FEATURE_GetName(Feature *feature)
    {
        return EXAMPLE_FEATURE;
    }
    
    //Feature initialization
    static void FEATURE_OnInitialize(Feature *feature, Service *parent, Identity identity)
    {
        DemoFeature *demoFeature = (DemoFeature *)feature;
        demoFeature->identity = identity;
        demoFeature->parent = parent;
    }
    
    //Empty feature quantity
    static void FEATURE_OnStop(Feature *feature, Identity identity)
    {
        g_example.identity.queueId = NULL;
        g_example.identity.featureId = -1;
        g_example.identity.serviceId = -1;
    }
    
    //Characteristic transmission
    static BOOL FEATURE_OnMessage(Feature *feature, Request *request)
    {
    	//When the request id is MSG_ During proc, feedback and response are sent directly
        if (request->msgId == MSG_PROC) {
            Response response = {.data = "Yes, you did!", .len = 0};
            SAMGR_SendResponse(request, &response);
            return TRUE;
        } else {
        	//When the request id is MSG_ TIME_ During proc, wait for the characteristic process first, and then print the service or samgr operation process
            if (request->msgId == MSG_TIME_PROC) {
                LOS_Msleep(WAIT_FEATURE_PROC * 10);
                if (request->msgValue) {
                    SAMGR_PrintServices();
                } else {
                    SAMGR_PrintOperations();
                }
                AsyncTimeCall(GET_IUNKNOWN(g_example));
                return FALSE;
            }
        }
        return FALSE;
    }
  • To create a function object:
  static DemoFeature g_example = {
        .GetName = FEATURE_GetName,
        .OnInitialize = FEATURE_OnInitialize,
        .OnStop = FEATURE_OnStop,
        .OnMessage = FEATURE_OnMessage,
        DEFAULT_IUNKNOWN_ENTRY_BEGIN,
            .AsyncCall = AsyncCall,
            .AsyncTimeCall = AsyncTimeCall,
            .SyncCall = SyncCall,
            .AsyncCallBack = AsyncCallBack,
        DEFAULT_IUNKNOWN_ENTRY_END,
        .identity = {-1, -1, NULL},
    };
    ```

-   towards SAMGR Registration function and interface:

    ```
    static void Init(void){
        SAMGR_GetInstance()->RegisterFeature(EXAMPLE_SERVICE, (Feature *)&g_example);
        SAMGR_GetInstance()->RegisterFeatureApi(EXAMPLE_SERVICE, EXAMPLE_FEATURE, GET_IUNKNOWN(g_example));
    }
  • Define the initialization entry of the function:
    SYSEX_FEATURE_INIT(Init);

Develop internal and external interfaces

  • Define IUnknown interface:
    typedef struct DemoApi {
        INHERIT_IUNKNOWN;
        BOOL (*AsyncCall)(IUnknown *iUnknown, const char *buff);
        BOOL (*AsyncTimeCall)(IUnknown *iUnknown);
        BOOL (*SyncCall)(IUnknown *iUnknown, struct Payload *payload);
        BOOL (*AsyncCallBack)(IUnknown *iUnknown, const char *buff, Handler handler);
    } DemoApi;
  • Define the reference object of IUnknown:
    typedef struct DemoRefApi {
        INHERIT_IUNKNOWNENTRY(DemoApi);
    } DemoRefApi;
  • Initialize interface object:
    static DemoRefApi api = {
        DEFAULT_IUNKNOWN_ENTRY_BEGIN,
            .AsyncCall = AsyncCall,
            .AsyncTimeCall = AsyncTimeCall,
            .SyncCall = SyncCall,
            .AsyncCallBack = AsyncCallBack,
        DEFAULT_IUNKNOWN_ENTRY_END,
    };
  • Registration service interface:
    SAMGR_GetInstance()->RegisterFeatureApi(EXAMPLE_SERVICE, EXAMPLE_FEATURE, GET_IUNKNOWN(api));

Call in-process service

  • Get the external interface of the service:
    DemoApi *demoApi = NULL;
    IUnknown *iUnknown = SAMGR_GetInstance()->GetFeatureApi(EXAMPLE_SERVICE, EXAMPLE_FEATURE);
    if (iUnknown == NULL) {
        return NULL;
    }
    int result = iUnknown->QueryInterface(iUnknown, DEFAULT_VERSION, (void **)&demoApi);
    if (result != 0 || demoApi == NULL) {
        return NULL;
    }
  • Interface call:
   if (demoApi->AsyncCallBack == NULL) {
        return NULL;
    }
    demoApi->AsyncCallBack((IUnknown *)demoApi, "I wanna async call callback good result!", AsyncHandler);
  • Release interface:
    int32 ref = demoApi->Release((IUnknown *)demoApi);

Develop cross process external interfaces

  • Inheriting IServerProxy instead of inheriting IUnknown: INHERIT_SERVER_IPROXY
    typedef struct DemoFeatureApi {
        INHERIT_SERVER_IPROXY;
        BOOL (*AsyncCall)(IUnknown *iUnknown, const char *buff);
        BOOL (*AsyncTimeCall)(IUnknown *iUnknown);
        BOOL (*SyncCall)(IUnknown *iUnknown, struct Payload *payload);
        BOOL (*AsyncCallBack)(IUnknown *iUnknown, const char *buff, IOwner notify, INotifyFunc handler);
    } DemoFeatureApi;
  • Initialize IServerProxy object:
    static DemoFeature g_example = {
        SERVER_IPROXY_IMPL_BEGIN,
        .Invoke = Invoke,
        .AsyncCall = AsyncCall,
        .AsyncTimeCall = AsyncTimeCall,
        .SyncCall = SyncCall,
        .AsyncCallBack = AsyncCallBack,
        IPROXY_END,
    };
  • Implement the Invoke function to process Ipc messages:
    static int32 Invoke(IServerProxy *iProxy, int funcId, void *origin, IpcIo *req, IpcIo *reply)
    {
        DemoFeatureApi *api = (DemoFeatureApi *)iProxy;
        BOOL ret;
        size_t len = 0;
        switch (funcId) {
            case ID_ASYNCALL:
                ret = api->AsyncCall((IUnknown *)iProxy, (char *)IpcIoPopString(req, &len));
                IpcIoPushBool(reply, ret);
                break;
            case ID_ASYNTIMECALL:
                ret = api->AsyncTimeCall((IUnknown *)iProxy);
                IpcIoPushBool(reply, ret);
                break;
            case ID_SYNCCALL: {
                struct Payload payload;
                payload.id = IpcIoPopInt32(req);
                payload.value = IpcIoPopInt32(req);
                payload.name = (char *)IpcIoPopString(req, &len);
                ret = api->SyncCall((IUnknown *)iProxy, &payload);
                IpcIoPushString(reply, ret ? "TRUE" : "FALSE");
            }
                break;
            case ID_ASYNCCALLBACK: { // convert to sync proxy
                IpcIoPushString(reply, "Yes, you did!");
                IpcIoPushBool(reply, TRUE);
            }
                break;
            default:
                IpcIoPushBool(reply, FALSE);
                break;
        }
        return EC_SUCCESS;
    }
  • Registered interface: consistent with the in-process interface registration
    SAMGR_GetInstance()->RegisterFeatureApi(EXAMPLE_SERVICE, EXAMPLE_FEATURE, GET_IUNKNOWN(g_example));

Call cross process services

  • Get the external interface of cross process service:
    IClientProxy *demoApi = NULL;
    IUnknown *iUnknown = SAMGR_GetInstance()->GetFeatureApi(EXAMPLE_SERVICE, EXAMPLE_FEATURE);
    if (iUnknown == NULL) {
        return NULL;
    }
    int result = iUnknown->QueryInterface(iUnknown, CLIENT_PROXY_VER, (void **)&demoApi);
    if (result != 0 || demoApi == NULL) {
        return NULL;
    }
  • Call the Ipc message interface:
    IpcIo request;char data[250];
    IpcIoInit(&request, data, sizeof(data), 0);
    demoApi->Invoke(demoApi, 0, &request, NULL, NULL);
  • Release interface:
    int32 ref = demoApi->Release((IUnknown *)demoApi);

Develop client-side proxy for cross process service invocation

  • Define IPC interface client proxy:
    typedef struct DemoClientProxy {
        INHERIT_CLIENT_IPROXY;
        BOOL (*AsyncCall)(IUnknown *iUnknown, const char *buff);
        BOOL (*AsyncTimeCall)(IUnknown *iUnknown);
        BOOL (*SyncCall)(IUnknown *iUnknown, struct Payload *payload);
        BOOL (*AsyncCallBack)(IUnknown *iUnknown, const char *buff, IOwner notify, INotifyFunc handler);
    } DemoClientProxy;
    typedef struct DemoClientEntry {
        INHERIT_IUNKNOWNENTRY(DemoClientProxy);
    } DemoClientEntry;
  • Implement the client agent encapsulation Ipc message interface:
 static BOOL AsyncCall(IUnknown *iUnknown, const char *buff)
    {
        DemoClientProxy *proxy = (DemoClientProxy *)iUnknown;
        IpcIo request;
        char data[MAX_DATA_LEN];
        IpcIoInit(&request, data, MAX_DATA_LEN, 0);
        IpcIoPushString(&request, buff);
        int ret = proxy->Invoke((IClientProxy *)proxy, ID_ASYNCALL, &request, NULL, NULL);
        return ret == EC_SUCCESS;
    }
    
    static BOOL AsyncTimeCall(IUnknown *iUnknown)
    {
        DemoClientProxy *proxy = (DemoClientProxy *)iUnknown;
        IpcIo request;
        char data[MAX_DATA_LEN];
        IpcIoInit(&request, data, MAX_DATA_LEN, 0);
        int ret = proxy->Invoke((IClientProxy *)proxy, ID_ASYNTIMECALL, &request, NULL, NULL);
        return ret == EC_SUCCESS;
    }
    
    static int Callback(IOwner owner, int code, IpcIo *reply)
    {
        size_t len = 0;
        return strcpy_s(owner, MAX_DATA_LEN, (char *)IpcIoPopString(reply, &len));
    }
    
    static BOOL SyncCall(IUnknown *iUnknown, struct Payload *payload)
    {
        DemoClientProxy *proxy = (DemoClientProxy *)iUnknown;
        IpcIo request;
        char data[MAX_DATA_LEN];
        IpcIoInit(&request, data, MAX_DATA_LEN, 0);
        IpcIoPushInt32(&request, payload->id);
        IpcIoPushInt32(&request, payload->value);
        IpcIoPushString(&request, payload->name);
        int ret = proxy->Invoke((IClientProxy *)proxy, ID_SYNCCALL, &request, data, Callback);
        data[MAX_DATA_LEN - 1] = 0;
        HILOG_INFO(HILOG_MODULE_APP, "[TID:0x%lx]Remote response is %s!", pthread_self(), data);
        return ret == EC_SUCCESS;
    }
    
    struct CurrentNotify {
        IOwner notify;
        INotifyFunc handler;
    };
    
    static int CurrentCallback(IOwner owner, int code, IpcIo *reply)
    {
        struct CurrentNotify *notify = (struct CurrentNotify *)owner;
        size_t len = 0;
        char *response = (char *)IpcIoPopString(reply, &len);
        HILOG_INFO(HILOG_MODULE_APP, "[TID:0x%lx]Notify Remote response is %s!", pthread_self(), response);
        notify->handler(notify->notify, response);
        return EC_SUCCESS;
    }
    
    static BOOL AsyncCallBack(IUnknown *iUnknown, const char *buff, IOwner notify, INotifyFunc handler)
    {
        struct CurrentNotify owner = {notify, handler};
        DemoClientProxy *proxy = (DemoClientProxy *)iUnknown;
        IpcIo request;
        char data[MAX_DATA_LEN];
        IpcIoInit(&request, data, MAX_DATA_LEN, 0);
        IpcIoPushString(&request, buff);
        int ret = proxy->Invoke((IClientProxy *)proxy, ID_ASYNCCALLBACK, &request, &owner, CurrentCallback);
        return ret == EC_SUCCESS;
    }
  • Factory method for implementing client agent:
    void *DEMO_CreatClient(const char *service, const char *feature, uint32 size)
    {
        (void)service;
        (void)feature;
        uint32 len = size + sizeof(DemoClientEntry);
        uint8 *client = malloc(len);
        (void)memset_s(client, len, 0, len);
        DemoClientEntry *entry = (DemoClientEntry *)&client[size];
        entry->ver = ((uint16)CLIENT_PROXY_VER | (uint16)DEFAULT_VERSION);
        entry->ref = 1;
        entry->iUnknown.QueryInterface = IUNKNOWN_QueryInterface;
        entry->iUnknown.AddRef = IUNKNOWN_AddRef;
        entry->iUnknown.Release = IUNKNOWN_Release;
        entry->iUnknown.Invoke = NULL;
        entry->iUnknown.AsyncCall = AsyncCall;
        entry->iUnknown.AsyncTimeCall = AsyncTimeCall;
        entry->iUnknown.SyncCall = SyncCall;
        entry->iUnknown.AsyncCallBack = AsyncCallBack;
        return client;
    }
    void DEMO_DestroyClient(const char *service, const char *feature, void *iproxy)
    {
        free(iproxy);
    }
  • Register the factory method of the client agent with SAMGR:
    SAMGR_RegisterFactory(EXAMPLE_SERVICE, EXAMPLE_FEATURE, DEMO_CreatClient, DEMO_DestroyClient);
  • Get the external interface of cross process service:
   DemoClientProxy *demoApi = NULL;
    IUnknown *iUnknown = SAMGR_GetInstance()->GetFeatureApi(EXAMPLE_SERVICE, EXAMPLE_FEATURE);
    if (iUnknown == NULL) {
        return NULL;
    }
    int result = iUnknown->QueryInterface(iUnknown, DEFAULT_VERSION, (void **)&demoApi);
    if (result != 0 || demoApi == NULL) {
        return NULL;
    }
  • Call the client proxy interface of the cross process service:
    if (demoApi->AsyncCallBack == NULL) {
        return NULL;
    }
    demoApi->AsyncCallBack((IUnknown *)demoApi,
                           "I wanna async call callback good result!", NULL, AsyncHandler);
  • Release interface:
    int32 ref = demoApi->Release((IUnknown *)demoApi);

Involved warehouse

Distributed task scheduling subsystem

samgr_lite

Posted by catalinus on Fri, 15 Oct 2021 12:12:33 -0700