天天看點

鴻蒙子系統解讀-包管理子系統篇

鴻蒙子系統解讀-包管理子系統篇

本文作者:江蘇潤和軟體股份有限公司 戴海

1. 架構簡介

包管理子系統,是OpenHarmony為開發者提供的安裝包管理架構,應用程式的安裝、解除安裝、更新、權限管理等一系列操作都是通過包管理器完成的。包管理子系統由如下圖子產品組成:

鴻蒙子系統解讀-包管理子系統篇

· 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);
	"......"
}
           

通過流程圖總結一下調用流程:

鴻蒙子系統解讀-包管理子系統篇

前面介紹的是包安裝是如何從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中完成的,下圖簡要描述了調用流程:

鴻蒙子系統解讀-包管理子系統篇

Ø 案例二:包資訊查詢

包資訊查詢用于擷取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;
}
           

通過流程圖總結一下調用流程:

鴻蒙子系統解讀-包管理子系統篇

繼續閱讀