The past and present life of Hongmeng Service Ability--Advanced chapter

2. SA configuration

The operation of SA requires multiple configuration items, which are specifically explained in this section.

​ SA in OpenHarmony generally consists of two configuration files and one so. The previous chapter has introduced the generation of code in so. This section describes the other two configuration files (.cfg or .rc, xml).

​ SA is generally started using .cfg or .rc + .xml + libxxx.z.so. The init process of OpenHarmony executes the corresponding xxx.cfg file to pull up the relevant SA process.

2.1 .xml

Connect to llibtel_core_service.z.so generated by the CoreService service above.

The serviceId of this SA is

// foundation\systemabilitymgr\samgr\interfaces\innerkits\samgr_proxy\include\system_ability_definition.h
TELEPHONY_CORE_SERVICE_SYS_ABILITY_ID            = 4010,

Generally, the sa_profile directory will be created under the subsystem where the SA is located, and the 4010.xml file and BUILD.gn will be added. The contents of 4010.xml and BUILD.gn are as follows

2.1.1 4001.xml

// base\telephony\core_service\sa_profile\4010.xml
<info>
    <process>telephony</process>
    <systemability> 
        <name>4010</name>
        <libpath>libtel_core_service.z.so</libpath>
        <run-on-create>true</run-on-create>
        <distributed>false</distributed>
        <dump-level>1</dump-level>
    </systemability>
</info>
  • The process name is the process space where the SA will run, and this field is required.
  • An xml file can only have one node, and configuring multiple nodes will cause compilation failure.
  • The name is the corresponding serviceId. required fields
  • is the loading path of SA. required fields
  • : true means that the SA will be registered with samgr after the process is started, and false means that it will be started on demand, that is, it will be started when other modules access the SA. required fields
  • :true means the SA is a distributed SA, and false means only local inter-IPC access.
  • : Can not be set. Three types can be set: BootStartPhase, CoreStartPhase, and OtherStartPhase (default type). The priorities of the three decrease in order. When the same process is in the same process, the SA that registers and configures BootStartPhase will be pulled up first, then the SA that configures CoreStartPhase, and finally OtherStartPhase. After all high-priority SAs start and register, the next-level SA registration will be started.
  • : Indicates the level supported by hidumper, the default configuration is 1

2.1.2 BUILD.gn

# base\telephony\core_service\sa_profile\BUILD.gn
import("//build/ohos/sa_profile/sa_profile.gni")

ohos_sa_profile("core_service_sa_profile") {
    
    
  sources = [ "4010.xml" ]
  part_name = "core_service"
}
  • sources: Indicates the list of xml files of SAs that the current subsystem needs to configure. Can support multiple SAs
  • subsystem_name: is the current subsystem
  • part_name: is the submodule to which the current subsystem belongs

Finally, add the BUILD.gn to the bundle.json of the subsystem to participate in compilation.

// base\telephony\core_service\bundle.json
"//base/telephony/core_service:tel_core_service",
"//base/telephony/core_service/sa_profile:core_service_sa_profile",

After compilation is completed, the out path will generate an xml file telephony.xml prefixed with the process name.

2.2 .cfg or .rc

The .cfg configuration file is mainly used on L2 devices, and L3-L5 is implemented using .rc configuration. There is not much difference between the two. This article mainly introduces L2 cfg.

// base\telephony\core_service\services\etc\init\telephony.cfg
{
    
    
    "jobs" : [{
    
    
            "name" : "early-boot",
            "cmds" : [
                "mkdir /data/service/el1/public/telephony 0711 radio radio",
                "start telephony_sa"
                ]
        }
    ],
    "services" : [{
    
    
            "name" : "telephony_sa",
            "path" : ["/system/bin/sa_main", "/system/profile/telephony.xml"],
            "uid" : "radio",
            "gid" : ["radio", "shell"],
            "permission" : [
                "ohos.permission.COMMONEVENT_STICKY",
                "ohos.permission.CONNECTIVITY_INTERNAL",
                "ohos.permission.GET_TELEPHONY_STATE",
                "ohos.permission.PERMISSION_USED_STATS",
                "ohos.permission.RECEIVE_SMS",
                "ohos.permission.SET_TELEPHONY_STATE",
                "ohos.permission.MANAGE_SECURE_SETTINGS"
            ],
            "secon" : "u:r:telephony_sa:s0"
        }
    ]
}

The cfg configuration file is the native process pull-up strategy provided by OpenHarmony. During the startup phase of the device, the init process parses the configured cfg file and pulls up the device.

  • The jobs configuration item is the action type corresponding to the key, that is, after the event identified as name is satisfied, the actions configured in the cmds type are triggered. For specific configuration parameters, please view the startup subsystem of OpenHarmony (base/startup/init).
  • services is for the init (fork) process to execute the entity code, and the path is the sa_main executable program and the corresponding parameters. The xml is the configuration xml file that needs to be parsed after the sa_main process starts.
  • uid, gid, permission, secon involve user and selinux permission management, and will be explained in a separate article when there is time in the future.

After the configuration is completed, cfg needs to be added to BUILD.gn to participate in compilation.

// base\telephony\core_service\services\etc\init\BUILD.gn
import("//build/ohos.gni")

## Install telephony.cfg to /system/etc/init/telephony.cfg
ohos_prebuilt_etc("telephony.cfg") {
    
    
  source = "telephony.cfg"

  relative_install_dir = "init"
  part_name = "core_service"
  subsystem_name = "telephony"
}
// base\telephony\core_service\bundle.json
"//base/telephony/core_service/services/etc/init:telephony.cfg",
  • If you need to debug, you can directly push the telephony.cfg file to the device/system/etc/init directory, and push the telephony.xml file to the device/system/profile directory. Then restart the phone, and check whether the process is started through ps -ef | grep telephony.
  • After the process starts normally, use the hdc shell hidumper -ls to filter said to be 1410, and check whether the SA has been successfully registered. (If the hidumper command is not supported, you can compile and generate it yourself)

3. Life cycle in SA

Insert image description here
The picture above shows the life cycle and state transition diagram of Ability. In this example CoreService inherits SystemAbility. SystemAbility is a service running in the background. There is no distinction between front-end and background. Referring to the code in the SystemAbility class, the life cycle of SA is OnStart, OnStop and OnDump.

At this point, when implementing SA, you need to implement the three methods OnStart, OnStop and OnDump in the service class (CoreService) to complete the SA startup end and exception handling.

4. SA’s callback

How to register a callback function in SA and execute the registered callback function after SA's service completes certain functions.

Since SA is a cross-process IPC communication, the SA interface cannot simply set a function pointer and call back this function pointer. At this time, you only need to implement a callback SA class, and set the callback SA class into SA. At this time, SA can call back the callback function registered in SA through IPC.

The implementation elements of callback SA are similar to the implementation of SA.

  • Define external IPC interface class

  • Define client communication proxy proxy class

  • Define server-side communication stub class

  • SA service implementation class

Also take CoreService to add a callback interface as an example. There is an interface in CoreService

// base\telephony\core_service\services\core\include\core_service.h
int32_t GetNetworkSearchInformation(int32_t slotId, const sptr<INetworkSearchCallback> &callback) override;

The INetworkSearchCallback passed in this interface is the first external IPC interface class.

4.1 Callback IPC interface class

// base\telephony\core_service\interfaces\innerkits\include\i_network_search_callback.h
class INetworkSearchCallback : public IRemoteBroker {
public:
    virtual ~INetworkSearchCallback() = default;
    enum class NetworkSearchCallback {
        GET_AVAILABLE_RESULT = 0,
        GET_NETWORK_MODE_RESULT,
        SET_NETWORK_MODE_RESULT,
        GET_RADIO_STATUS_RESULT,
        SET_RADIO_STATUS_RESULT,
        GET_PREFERRED_NETWORK_MODE_RESULT,
        SET_PREFERRED_NETWORK_MODE_RESULT,
    };
    virtual int32_t OnNetworkSearchCallback(NetworkSearchCallback requestId, MessageParcel &data) = 0;

public:
    DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.Telephony.INetworkSearchCallback");
};

4.2 Callback proxy class

// base\telephony\core_service\services\network_search\include\network_search_callback_proxy.h
class NetworkSearchCallBackProxy : public IRemoteProxy<INetworkSearchCallback> {
public:
    explicit NetworkSearchCallBackProxy(const sptr<IRemoteObject> &impl);
    virtual ~NetworkSearchCallBackProxy() = default;
    int32_t OnNetworkSearchCallback(
        NetworkSearchCallback requestId, MessageParcel &callBackParcel) override;

private:
    static inline BrokerDelegator<NetworkSearchCallBackProxy> delegator_;
};

4.3 Callback stub class

// base\telephony\core_service\interfaces\innerkits\include\i_network_search_callback_stub.h
class INetworkSearchCallbackStub : public IRemoteStub<INetworkSearchCallback> {
public:
    static const int32_t DEFAULT_ERROR = -1;
    static const int32_t DEFAULT_RESULT = 0;
    INetworkSearchCallbackStub() = default;
    virtual ~INetworkSearchCallbackStub() = default;
    int32_t OnNetworkSearchCallback(NetworkSearchCallback requestId, MessageParcel &data) override;
    int OnRemoteRequest(
        uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override final;
    virtual void OnSetNetworkModeCallback(const bool setResult, const int32_t errorCode);
    virtual void OnGetNetworkModeCallback(const int32_t searchModel, const int32_t errorCode);
    virtual void OnSetRadioStateCallback(const bool setResult, const int32_t errorCode);
    virtual void OnGetRadioStateCallback(const bool setResult, const int32_t errorCode);
    virtual void OnGetNetworkSearchInformation(
        const sptr<NetworkSearchResult> &networkSearchResult, const int32_t errorCode);
    virtual void OnSetPreferredNetworkCallback(const bool result, const int32_t errorCode);
    virtual void OnGetPreferredNetworkCallback(const int32_t networkMode, const int32_t errorCode);

private:
    void OnSetNetworkModeCallback(MessageParcel &data);
    void OnGetNetworkModeCallback(MessageParcel &data);
    void OnSetRadioStateCallback(MessageParcel &data);
    void OnGetRadioStateCallback(MessageParcel &data);
    void OnGetNetworkSearchInformation(MessageParcel &data);
    void OnSetPreferredNetworkCallback(MessageParcel &data);
    void OnGetPreferredNetworkCallback(MessageParcel &data);
};

4.4 Implementation class of callback service

// base\telephony\core_service\frameworks\js\network_search\include\get_network_search_info_callback.h
class GetNetworkSearchInfoCallback : public INetworkSearchCallbackStub {
public:
    explicit GetNetworkSearchInfoCallback(GetSearchInfoContext *context);
    void OnGetNetworkSearchInformation(
        const sptr<NetworkSearchResult> &networkSearchResult, const int32_t errorCode) override;

private:
    GetSearchInfoContext *asyncContext_;
};
// base\telephony\core_service\frameworks\js\network_search\include\get_preferred_network_callback.h
class GetPreferredNetworkCallback : public INetworkSearchCallbackStub {
public:
    explicit GetPreferredNetworkCallback(PreferredNetworkModeContext *asyncContext);
    void OnGetPreferredNetworkCallback(const int32_t networkMode, const int32_t errorCode) override;

private:
    PreferredNetworkModeContext *asyncContext_;
};

4.5 SA callback registration

// base\telephony\core_service\frameworks\js\network_search\src\napi_radio.cpp
static void NativeGetNetworkSearchInformation(napi_env env, void *data)
{
    auto asyncContext = static_cast<GetSearchInfoContext *>(data);
    if (!IsValidSlotId(asyncContext->slotId)) {
        TELEPHONY_LOGE("NativeGetNetworkSearchInformation slotId is invalid");
        asyncContext->errorCode = ERROR_SLOT_ID_INVALID;
        return;
    }
    std::unique_ptr<GetNetworkSearchInfoCallback> callback =
        std::make_unique<GetNetworkSearchInfoCallback>(asyncContext);
    std::unique_lock<std::mutex> callbackLock(asyncContext->callbackMutex);
    asyncContext->errorCode = DelayedRefSingleton<CoreServiceClient>::GetInstance().GetNetworkSearchInformation(
        asyncContext->slotId, callback.release());
    if (asyncContext->errorCode == TELEPHONY_SUCCESS) {
        asyncContext->cv.wait_for(
            callbackLock, std::chrono::seconds(WAIT_TIME_SECOND), [asyncContext] { return asyncContext->callbackEnd; });
        TELEPHONY_LOGI("NativeGetNetworkSearchInformation after callback end");
    }
    TELEPHONY_LOGI("NativeGetNetworkSearchInformation end");
}

使用方式就比较简单直接 std::make_unique<GetNetworkSearchInfoCallback>(asyncContext);

然后通过CoreService的SA的接口GetNetworkSearchInformation注册回调的SA类GetNetworkSearchInfoCallback进CoreService的SA。到此SA实现了回调方式的IPC调用。

Guess you like

Origin blog.csdn.net/procedurecode/article/details/130228410