2. Configuração SA
A operação do SA requer vários itens de configuração, que são explicados especificamente nesta seção.
SA no OpenHarmony geralmente consiste em dois arquivos de configuração e um. O capítulo anterior introduziu a geração de código assim. Esta seção descreve os outros dois arquivos de configuração (.cfg ou .rc, xml).
O SA geralmente é iniciado usando o método .cfg ou .rc + .xml + libxxx.z.so. O processo init do OpenHarmony executa o arquivo xxx.cfg correspondente para abrir o processo SA relevante.
2.1.xml
Conecte-se ao llibtel_core_service.z.so gerado pelo serviço CoreService acima.
O serviceId deste SA é
// foundation\systemabilitymgr\samgr\interfaces\innerkits\samgr_proxy\include\system_ability_definition.h
TELEPHONY_CORE_SERVICE_SYS_ABILITY_ID = 4010,
Geralmente, o diretório sa_profile é criado no subsistema onde o SA está localizado e o arquivo 4010.xml e BUILD.gn são adicionados. O conteúdo de 4010.xml e BUILD.gn é o seguinte
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>
- O nome do processo é o espaço do processo no qual o SA será executado. Este campo é obrigatório.
- Um arquivo xml pode ter apenas um nó. Configurar vários nós causará falha na compilação.
- O nome é o serviceId correspondente. Os campos obrigatórios
- É o caminho de carregamento do SA. Os campos obrigatórios
- : true significa que o SA será cadastrado no samgr após o início do processo, false significa que ele será iniciado sob demanda, ou seja, será iniciado quando outros módulos acessarem o SA. Os campos obrigatórios
- :true indica que a SA é uma SA distribuída, false indica que ela possui apenas acesso local cross-IPC.
- : Não pode ser definido. Três tipos podem ser definidos: BootStartPhase, CoreStartPhase e OtherStartPhase (tipo padrão). A prioridade dos três diminui em ordem. Quando o mesmo processo está no mesmo processo, o SA que registra e configura BootStartPhase irá ser puxado primeiro, depois o SA que configura CoreStartPhase e, finalmente, Other Start Phase. Quando todas as SAs de alta prioridade forem iniciadas e registradas, o registro da SA de nível seguinte será iniciado.
- : Indica o nível suportado pelo hidumper. A configuração padrão é 1.
2.1.2 CONSTRUIR.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"
}
- fontes: indica a lista de arquivos xml de SAs que precisam ser configurados no subsistema atual. Pode suportar vários SAs
- subsystem_name: é o subsistema atual
- part_name: é o submódulo ao qual pertence o subsistema atual
Por fim, adicione BUILD.gn ao bundle.json do subsistema para participar da compilação.
// base\telephony\core_service\bundle.json
"//base/telephony/core_service:tel_core_service",
"//base/telephony/core_service/sa_profile:core_service_sa_profile",
Após a conclusão da compilação, o caminho de saída gerará um arquivo xml telephony.xml prefixado com o nome do processo.
2.2 .cfg ou .rc
O arquivo de configuração .cfg é usado principalmente em dispositivos L2, e L3-L5 é implementado usando a configuração .rc. Não há muita diferença entre os dois. Este artigo apresenta principalmente 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"
}
]
}
O arquivo de configuração cfg é a estratégia de pull-up do processo nativo fornecida pelo OpenHarmony.Durante a fase de inicialização do dispositivo, o processo init analisa o arquivo cfg configurado e puxa o dispositivo.
- O item de configuração jobs é o tipo de ação correspondente à chave, ou seja, após o evento identificado como nome ser satisfeito, as ações configuradas pelo tipo cmds são acionadas. Para parâmetros de configuração específicos, consulte o subsistema de inicialização do OpenHarmony (base/startup /iniciar).
- Os serviços puxam (fork) o processo para executar o código da entidade para init, e o caminho é o programa executável sa_main e os parâmetros correspondentes. Este xml é o arquivo xml de configuração que precisa ser analisado após o início do processo sa_main.
- uid, gid, permissão, secon envolvem gerenciamento de permissões de usuário e selinux e serão explicados em um artigo separado quando houver tempo no futuro.
Após a conclusão da configuração, cfg precisa ser adicionado ao BUILD.gn para participar da compilação.
// 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",
- Se precisar depurar, você pode enviar diretamente o arquivo telephony.cfg para o diretório device/system/etc/init e enviar o arquivo telephony.xml para o diretório device/system/profile. Em seguida, reinicie o telefone e verifique se esse processo foi iniciado através da telefonia ps -ef | grep.
- Após o processo iniciar normalmente, use hdc shell hidumper -ls para filtrar o referido ID para 1410 e verifique se o SA foi registrado com sucesso. (Se o comando hidumper não for compatível, você mesmo pode compilá-lo e gerá-lo)
3. Ciclo de vida em SA
A imagem acima mostra o ciclo de vida e o diagrama de transição de estado da Habilidade. Neste exemplo, CoreService herda SystemAbility. SystemAbility é um serviço executado em segundo plano. Não há distinção entre front-end e segundo plano. Referindo-se ao código da classe SystemAbility, o ciclo de vida do SA é OnStart, OnStop e OnDump.
Neste ponto, ao implementar o SA, você precisa implementar os três métodos OnStart, OnStop e OnDump na classe de serviço (CoreService) para concluir o término da inicialização do SA e o tratamento de exceções.
4. Retorno de chamada do SA
Como registrar uma função de retorno de chamada no SA e executar a função de retorno de chamada registrada após o serviço do SA concluir determinadas funções.
Como SA é uma comunicação IPC entre processos, a interface SA não pode simplesmente definir um ponteiro de função e chamar de volta esse ponteiro de função. Neste momento, você só precisa implementar uma classe SA de retorno de chamada e definir a classe SA de retorno de chamada como SA. Neste momento, o SA pode retornar a função de retorno de chamada registrada no SA por meio do IPC.
Os elementos de implementação do callback SA são semelhantes à implementação do SA.
-
Definir classe de interface IPC externa
-
Definir classe de proxy de proxy de comunicação do cliente
-
Definir classe de stub de comunicação do lado do servidor
-
Classe de implementação de serviço SA
Tome também como exemplo o CoreService para adicionar uma interface de retorno de chamada. Existe uma interface no CoreService
// base\telephony\core_service\services\core\include\core_service.h
int32_t GetNetworkSearchInformation(int32_t slotId, const sptr<INetworkSearchCallback> &callback) override;
O INetworkSearchCallback passado nesta interface é a primeira classe de interface IPC externa.
4.1 Classe de interface IPC de retorno de chamada
// 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 Classe de proxy de retorno de chamada
// 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 Classe stub de retorno de chamada
// 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 Classe de implementação do serviço de retorno de chamada
// 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 Registro de retorno de chamada SA
// 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调用。