鸿蒙子系统解读-包管理子系统篇

鸿蒙子系统解读-包管理子系统篇

本文作者:江苏润和软件股份有限公司 戴海


1. 框架简介

包管理子系统,是OpenHarmony为开发者提供的安装包管理框架,应用程序的安装、卸载、升级、权限管理等一系列操作都是通过包管理器完成的。包管理子系统由如下图模块组成:
鸿蒙子系统解读-包管理子系统篇_第1张图片


· BundleKit:是包管理服务对外提供的接口,有安装/卸载接口、包信息查询接口、包状态变化监听接口。

· 包扫描器:用来解析本地预制或者安装的安装包,提取里面的各种信息,供管理子模块进行管理,持久化。

· 包安装子模块:安装,卸载,升级一个包;包安装服务一个单独进程的用于创建删除安装目录,具有较高的权限。

· 包管理子模块:管理安装包相关的信息,存储持久化包信息。

· 包安全管理子模块:签名检查、权限授予、权限管理。

2. 代码目录结构

foundation/appexecfwk/interfaces/kits/bundle_lite BundleKit为开发者提供的接口
foundation/appexecfwk/interfaces/innerkits/bundlemgr_lite BundleKit实现的核心代码,及包管理服务为其它子系统提供的接口
foundation/appexecfwk/frameworks/bundle_lite 管理BundleKit与包管理服务通信的客户端代码
foundation/appexecfwk/utils/bundle_lite 包管理服务实现中用到的工具性的代码
foundation/appexecfwk/services/bundlemgr_lite 包管理服务的实现代码

3. 实例讲解

为了能够熟悉包管理的是如何工作的,接下来将以包安装和包信息查询来进行详解。

Ø 案例一:包安装

Install是包安装的入口函数,首先创建了GetBmsInnerClient对象,这是用于和Server端进行IPC通讯的IClientProxy,通过Invoke发送了ID为INSTALL的消息,在Server端(bundlems)将对INSTALL进行功能实现,安装结果通过BundleSelfCallback回调回来。需要注意这里的Client和Server并不是直接进行IPC通讯的,而是通过/dev/lite_ipc驱动中转调用到Server端,这个中转过程就不在本章进行详细讲解。

//bundle_manager.cpp
bool Install(const char *hapPath, const InstallParam *installParam, InstallerCallback installerCallback)
{
……
const SvcIdentity *svc = OHOS::BundleSelfCallback::GetInstance().RegisterBundleSelfCallback(installerCallback); //注册回调,用于反馈包安装的状态结果
IpcIoPushSvc(&ipcIo, svc);
……
auto bmsInnerClient = GetBmsInnerClient();
……
    int32_t ret = bmsInnerClient->Invoke(bmsInnerClient, INSTALL, &ipcIo, &result, Notify);//通过IPC和包管理服务通讯
……
}

static IClientProxy *GetBmsInnerClient()
{
……
IUnknown *iUnknown = SAMGR_GetInstance()->GetFeatureApi(BMS_SERVICE, BMS_INNER_FEATURE);//获取BMS_SERVICE的FeatureApi,用于调用包安装接口
……
}

定义INSTALL的Invoke ID,在Server端通过ID来映射到对应的包安装接口函数,这里同时还定义了卸载、包信息查询等,GET_BUNDLE_INFO后面的包信息查询中会用到。

// bundle_inner_interface.h
enum BmsCmd {
    ……
    GET_BUNDLE_INFO,
    ……
    INSTALL = BMS_INNER_BEGIN, // bms install application
    ……
};

刚才提到了Client端IClientProxy的创建,那对应Server端则有一个IServerProxy,用于远程接口调用。g_bmsInnerImpl 就是声明了IServerProxy,当收到INSTALL的消息时,则会通过BundleMsInvokeFuc根据Invoke ID来映射到对应的函数指针InstallInnerBundle。 由于包安装是个较为耗时的操作,所以采用异步线程Request的请求方式。包安装完成后向Client端反馈包安装状态。

//bundle_inner_feature.cpp
static BmsInnerImpl g_bmsInnerImpl = {
    SERVER_IPROXY_IMPL_BEGIN,
    .Invoke = BundleInnerFeature::Invoke, //创建IServerProxy
    IPROXY_END
};

BundleInvokeType BundleInnerFeature::BundleMsInvokeFuc[BMS_CMD_END - BMS_INNER_BEGIN] {
    InstallInnerBundle,//绑定包安装的接口函数
    ……
};

uint8_t BundleInnerFeature::InstallInnerBundle(const uint8_t funcId, IpcIo *req, IpcIo *reply)
{
"......"
    Request request = {
        .msgId = BUNDLE_INSTALLED,
        .len = static_cast<int16>(sizeof(SvcIdentityInfo)),
        .data = reinterpret_cast<void *>(info),
        .msgValue = 0
    };
    int32 propRet = SAMGR_SendRequest(&(GetInstance()->identity_), &request, nullptr);//发送BUNDLE_INSTALLED消息
"......"
}

//bundle_manager_service.cpp
void ManagerService::ServiceMsgProcess(Request* request)
{
    switch (request->msgId) {
        case BUNDLE_INSTALLED: {
            "......"
            InstallThirdBundle(info->path, *(info->svc));//处理消息
            break;
        }
    }
}

void ManagerService::InstallThirdBundle(const char *path, const SvcIdentity &svc)
{
	"......"
      uint8_t bResult = installer_->Install(path);//包安装
	InnerSelfTransact(INSTALL_CALLBACK, bResult, svc);//回调安装结果
InnerTransact(INSTALL_CALLBACK, bResult, bundleName);
	"......"
}

通过流程图总结一下调用流程:
鸿蒙子系统解读-包管理子系统篇_第2张图片

前面介绍的是包安装是如何从Client调用到Server端的,而包安装真正功能代码是在BundleInstaller::Install实现的,以下是具体步骤描述:

(1)首先需要检查包的路径是否有效。

(2)HapSignVerify::VerifySignature签名认证,在debug模式下也分签名模式和非签名模式,通过ManagerService::IsSignMode来查询,非debug模式是必须要进行签名认证,appid就是在这里生成的,也是APP的唯一标识。

(3)BundleParser::ParseHapProfile解析包中配置文件config.json,用来获取app、deviceConfig、module、reqPermissions信息。

(4)BundleInstaller::CheckProvisionInfoIsValid检查config.json配置文件是否有效,这里主要检查包的Permissions和bundleName是否和包签名信息匹配,这一步在debug且非签名模式下是不会检查配置文件的。

(5)BundleInstaller::CheckVersionAndSignature旧检查版本号和签名,首先需要根据bundleName通过包查询接口来获取bundleInfo信息,如果有值则表示此APP已经安装,这也是判断是否是升级的依据;接着需要检查旧包和新包的版本号,如果是降级安装是会报错的;最后将旧的安装包的codePath和codeData数据记录下来,保存在budleInfo对象中。

(6)BundleDaemonClient::ExtractHap->BundleDaemonHandler::ExtractHap包解压到目标路径的app/ace/run/ {moduleName}/temp目录下,moduleName是通过包查询从config.json中读取到的,如果有旧版本的路径则先会删除再解压,到此代码就解压完成了。

(7)BundleInstaller::HandleFileAndBackUpRecord 备份bundleInfo,通过包查询的bundleInfo的信息以及安装过程中保存的信息都会被记录,最终保存在/app/etc/{bundleName}_tmp.json中。

非升级情况:首先为APP生成一个UID,并记录下来,并创建app/ace/data /{bundleName}目录用来存放APP的持久化数据

升级情况:记录老的APP的UID

(8)BundleInstaller::StorePermissions将包权限以Json格式的字符串存储在/storage/app/etc/permissions/{bundleName}路径下

(9)BundleInstaller::UpdateBundleInfo-> ManagerService::AddBundleInfo保存包的BundleInfo数据到bundlemap中,后面包查询会根据bundleName从bundlemap查询对应的包信息

补充说明,在上述步骤中Bundle_ms再通过 BundleDaemonClient 进行IPC操作,远程调用到 bundle_daemon,包的解压就是在bundle_daemon中完成的,下图简要描述了调用流程:
鸿蒙子系统解读-包管理子系统篇_第3张图片

Ø 案例二:包信息查询

包信息查询用于获取HAP包的参数信息,参数信息如下表:

Variable Name Description
isKeepAlive 查询包是否是活跃状态
isNativeApp 是否是本地应用,本地应用程序是指在系统中使用C++开发的应用程序
uid 应用安装时分配的uid
gid 应用程序安装时分配的应用程序组ID
isSystemApp 查询是否是系统应用,系统应用是无法被卸载的
compatibleApi 应用开发所需的最低API版本
targetApi 应用开发API版本
versionCode 应用版本号,这是开发的内部版本号,对用户不可见
versionName 应用版本号,对用户可见
bundleName 应用程序包名,是应用的ID,对用户不可见
label 应用程序包名,对用户可见
bigIconPath 应用图标路径
codePath 应用的安装的路径
dataPath 应用本地数据保存路径
vendor 应用程序的供应商名称
moduleInfos 应用程序的moduleInfos信息
numOfModule 应用程序中包含的ModuleInfo对象个数
appId 应用程序ID,唯一标识应用程序,它是捆绑包名称和应用程序签名的组合
abilityInfos 应用程序的abilityInfos信息
numOfAbility 应用程序中包含的abilityInfos对象个数

包信息查询和包安装一样,需要创建IClientProxy和IServerProxy进行IPC通讯。首先创建GetBmsClient得到IClientProxy指针,并发送GET_BUNDLE_INFO消息。GetBundleInfo是包信息查询的接口函数。

//bundle_manager.cpp
static IClientProxy *GetBmsClient()
{
......
	IUnknown *iUnknown = SAMGR_GetInstance()->GetFeatureApi(BMS_SERVICE, BMS_FEATURE);//获取BMS_SERVICE的FeatureApi,用于查询包信息的接口调用
......
} 

uint8_t GetBundleInfo(const char *bundleName, int32_t flags, BundleInfo *bundleInfo)
{
......
auto bmsClient = GetBmsClient();
......
    int32_t ret = bmsClient->Invoke(bmsClient, GET_BUNDLE_INFO, &ipcIo, &resultOfGetBundleInfo, Notify);//Invoke跨进程通信
......
}

g_bmsImpl是IServerProxy的声明,用来处理IClientProxy的远程接口调用,之前有提到过Invoke ID (GET_BUNDLE_INFO),这是用来映射到Server端的GetInnerBundleInfo函数。Server端获取数据后将查询结果反馈给Client端。

//bundle_ms_feature.cpp
static BmsImpl g_bmsImpl = {
    SERVER_IPROXY_IMPL_BEGIN,
.Invoke = BundleMsFeature::Invoke,
……
.GetBundleInfo = BundleMsFeature::GetBundleInfo,
……
    IPROXY_END
};

BundleInvokeType BundleMsFeature::BundleMsInvokeFuc[BMS_INNER_BEGIN] {
    QueryInnerAbilityInfo,
    GetInnerBundleInfo,
    ChangeInnerCallbackServiceId,
    GetInnerBundleNameForUid,
    HandleGetBundleInfos,
};

uint8_t BundleMsFeature::GetInnerBundleInfo(const uint8_t funcId, IpcIo *req, IpcIo *reply)
{
......
	uint8_t errorCode = GetBundleInfo(bundleName, IpcIoPopInt32(req), &bundleInfo);
......
	IpcIoPushUint8(reply, OHOS_SUCCESS);//用户反馈包查询结果
......
}

uint8_t BundleMsFeature::GetBundleInfo(const char *bundleName, int32_t flags, BundleInfo *bundleInfo)
{
    return OHOS::ManagerService::GetInstance().GetBundleInfo(bundleName, flags, *bundleInfo);//调用bundle_manager_service
}

在此包信息查询和包安装有所差异,由于当前操作并非有等待耗时,所以直接进行了函数调用,而不像包安装需要起一个异步线程等待安装结果。在GetCopyBundleInfo 方法中最终调用了BundleMap::GetBundleInfo接口来获取包信息,这个包信息就是在安装包时保存在BundleMap中的bundleInfo数据。

// bundle_manager_service.cpp
uint8_t ManagerService::GetBundleInfo(const char *bundleName, int32_t flags, BundleInfo &bundleInfo)
{
    if (bundleName == nullptr || bundleMap_ == nullptr) {
        return ERR_APPEXECFWK_QUERY_PARAMETER_ERROR;
    }
    return bundleMap_->GetBundleInfo(bundleName, flags, bundleInfo);
}

//bundle_map.cpp
uint8_t BundleMap::GetBundleInfo(const char *bundleName, int32_t flags, BundleInfo &bundleInfo) const
{
    if (bundleName == nullptr) {
        return ERR_APPEXECFWK_QUERY_PARAMETER_ERROR;
    }

    BundleInfo *specialBundleInfo = Get(bundleName);
    if (specialBundleInfo == nullptr) {
        return ERR_APPEXECFWK_QUERY_NO_INFOS;
    }

    GetCopyBundleInfo (flags, specialBundleInfo, bundleInfo);//获取包数据
    return ERR_OK;
}

通过流程图总结一下调用流程:

鸿蒙子系统解读-包管理子系统篇_第4张图片

你可能感兴趣的:(鸿蒙,Harmony,OS,编程语言,c++)