【HarmonyOS源码学习系列】包服务守护进程

2,097 阅读9分钟

Bundle Manager Service 负责安装、卸载应用程序,以及管理bundle信息。

鸿蒙系统的安装包模块包括:bundle_daemon、bundlems、bm、API。

bundle_daemon是安装包模块的守护进程,由init进程在设备启动的时候拉起。

bundlems是运行在foundation进程里面的系统服务,也就是安装包管理服务。

bm是系统提供的命令行工具,用于安装、卸载应用程序,以及获取bundle信息。

API封装IPC调用逻辑,供应用程序调用,操作安装程序、卸载程序和管理bundle信息。

code-1.0/foundation/appexecfwk/services/bundlemgr_lite/src/bundle_installer.cpp

bundle_daemon

code-1.0\foundation\appexecfwk\services\bundlemgr_lite\bundle_daemon\

bundle_daemon源代码位于上面这个路径。

const char BDS_SERVICE[] = "bundle_daemon";

static BundleDaemonFeature g_defaultApi = {
    SERVER_IPROXY_IMPL_BEGIN,
    .Invoke = BundleDaemon::Invoke,
    IPROXY_END,
};

static void Init()
{
    SamgrLite *samgrLite = SAMGR_GetInstance();
    if (samgrLite == nullptr) {
        PRINTE("BundleDaemon", "get samgr is nullptr");
        return;
    }
    // 注册服务bundle daemon
    BOOL result = samgrLite->RegisterService(&BundleDaemon::GetInstance());
    if (!result) {
        PRINTE("BundleDaemon", "register bundle_daemon service fail");
        return;
    }
    // 注册bundle_daemon服务的默认feature
    result = samgrLite->RegisterDefaultFeatureApi(BDS_SERVICE, GET_IUNKNOWN(g_defaultApi));
    PRINTE("BundleDaemon", "register default feature api %{public}s", result ? "success" : "fail");
}
SYSEX_SERVICE_INIT(Init);

使用鸿蒙系统的IPC(服务)框架,定义并注册一个服务和服务的feature。这里服务的名字是bundle_daemon,服务的默认feature只有一个函数Invoke()

Invoke

static constexpr InvokeFunc invokeFuncs[BDS_CMD_END] {
    BundleDaemon::ExtractHapInvoke,             // 安装应用程序
    BundleDaemon::RenameFileInvoke,             // 文件重命名
    BundleDaemon::CreatePermissionInvoke,       // 创建文件夹/storage/app/etc/permissions/
    BundleDaemon::CreateDataDirectoryInvoke,    // 创建应用程序数据文件夹
    BundleDaemon::StoreContentToFileInvoke,     // 把数据保存到文件
    BundleDaemon::RemoveFileInvoke,             // 删除文件
    BundleDaemon::RemoveInstallDirectoryInvoke, // 卸载应用程序
};

int32 BundleDaemon::Invoke(IServerProxy *iProxy, int funcId, void *origin, IpcIo *req, IpcIo *reply)
{
    PRINTI("BundleDaemon", "bundle_daemon invoke start %{public}d", funcId);
    if (origin == nullptr || req == nullptr) {
        PRINTE("BundleDaemon", "invalid param");
        return EC_INVALID;
    }
    // check permission
    pid_t uid = GetCallingUid(origin);
    if (uid != BMS_UID) {
        PRINTE("BundleDaemon", "permission denied");
        return EC_PERMISSION;
    }
    if (funcId == REGISTER_CALLBACK) {
        return RegisterCallbackInvoke(req);
    }
    int32 ret = EC_COMMU;
    // funcId有效性判断,调用对应的函数
    if (funcId >= EXTRACT_HAP && funcId < BDS_CMD_END) {
        ret = (BundleDaemon::invokeFuncs[funcId])(req);
    }
    // 返回执行结果给调用进程
    return BundleDaemon::GetInstance().bundleMsClient_->SendReply(ret);
}

根据client传入的funcId,调用对应的函数。这里一共定义了7个函数。

安装应用程序

安装应用程序,会调用函数ExtractHapInvoke()。

code-1.0/foundation/appexecfwk/services/bundlemgr_lite/bundle_daemon/src/bundle_daemon.cpp

int32 BundleDaemon::ExtractHapInvoke(IpcIo *req)
{
    size_t len = 0;
    // 从传入的参数获得安装包路径
    const char *hapPath = reinterpret_cast<char *>(IpcIoPopString(req, &len));
    if (hapPath == nullptr || len == 0) {
        return EC_INVALID;
    }
    // 从传入的参数获得安装路径
    const char *codePath = reinterpret_cast<char *>(IpcIoPopString(req, &len));
    if (codePath == nullptr || len == 0) {
        return EC_INVALID;
    }
    // 执行安装过程
    return BundleDaemon::GetInstance().handler_.ExtractHap(hapPath, codePath);
}

code-1.0/foundation/appexecfwk/services/bundlemgr_lite/bundle_daemon/src/bundle_daemon_handler.cpp

int32 BundleDaemonHandler::ExtractHap(const char *hapPath, const char *codePath)
{
    // 获得安装包的真实路径
    char realHapPath[PATH_MAX] = { '\0' };
    if (hapPath == nullptr || realpath(hapPath, realHapPath) == nullptr) {
        PRINTE("BundleDaemonHandler", "realPath fail!");
        return EC_INVALID;
    }
    // 使用ExtractorUtil打开安装包
    ExtractorUtil extractorUtil(realHapPath);
    if (!extractorUtil.Init()) {
        PRINTE("BundleDaemonHandler", "init fail!");
        return EC_NOINIT;
    }

    // check and mkdir code path
    if (!IsValideCodePath(codePath)) {
        return EC_INVALID;
    }
    if (!BundleFileUtils::RemoveFile(codePath)) {
        PRINTE("BundleDaemonHandler", "remove codePath fail!");
        return EC_NODIR;
    }
    std::string codeDir = std::string(codePath);
    if (codeDir.back() != PATH_SEPARATOR) {
        codeDir += PATH_SEPARATOR;
    }

    if (!BundleFileUtils::MkRecursiveDir(codeDir.c_str(), true)) {
        PRINTE("BundleDaemonHandler", "create codePath fail!");
        return EC_NODIR;
    }

    // unzip one by one
    const std::vector<std::string> &fileNames = extractorUtil.GetZipFileNames();
    for (const auto &fileName : fileNames) {
        if (fileName.find("..") != std::string::npos) {
            PRINTE("BundleDaemonHandler", "zip file is invalid!");
            return EC_NODIR;
        }
        if (fileName.back() == PATH_SEPARATOR) {
            continue;
        }
        const std::string dir = BundleFileUtils::GetPathDir(fileName);
        if (!dir.empty()) {
            std::string fileDir = codeDir + dir;
            if (!BundleFileUtils::MkRecursiveDir(fileDir.c_str(), false)) {
                PRINTE("BundleDaemonHandler", "create other dir fail!");
                return EC_NODIR;
            }
        }
        std::string filePath = codeDir + fileName;
        if (!extractorUtil.ExtractFileToPath(filePath, fileName)) {
            PRINTE("BundleDaemonHandler", "ExtractFileToPath fail!");
            return EC_NODIR;
        }
    }
    return EC_SUCCESS;
}

其实,就是把应用程序安装包(安装包是一个zip文件)从原始路径解压到目标路径。

卸载应用程序

code-1.0/foundation/appexecfwk/services/bundlemgr_lite/bundle_daemon/src/bundle_daemon.cpp

int32 BundleDaemon::RemoveInstallDirectoryInvoke(IpcIo *req)
{
    size_t len = 0;
    // 应用程序包安装路径
    const char *codePath = reinterpret_cast<char *>(IpcIoPopString(req, &len));
    if (codePath == nullptr || len == 0) {
        return EC_INVALID;
    }
    // 应用程序数据路径
    const char *dataPath = reinterpret_cast<char *>(IpcIoPopString(req, &len));
    if (dataPath == nullptr || len == 0) {
        return EC_INVALID;
    }
    // 删除程序包路径和数据路径
    return BundleDaemon::GetInstance().handler_.RemoveInstallDirectory(codePath, dataPath);
}

int32 BundleDaemonHandler::RemoveInstallDirectory(const char *codePath, const char *dataPath)
{
    bool result = IsValideCodePath(codePath) && BundleFileUtils::RemoveFile(codePath);
    result = IsValideDataPath(dataPath) && BundleFileUtils::RemoveFile(dataPath) && result;
    return result ? EC_SUCCESS : EC_NODIR;
}

小节:应用程序的安装和卸载一共涉及到三个路径,分别是应用程序安装包路径、应用程序安装路径、应用程序数据路径。其中应用程序安装包路径是client指定的。应用程序安装路径是/storage/app/run//sdcard/app/run/。应用程序数据路径是/storage/app/data//sdcard/app/data/

另外,还有几个路径如下:

  • 系统应用路径 /system/internal/
  • 预制的第三方应用路径 /system/external/
  • 应用权限路径 /storage/app/etc/permissions/

bundle daemon启动过程

前情提要

code-1.0\foundation\appexecfwk\services\bundlemgr_lite\bundle_daemon\src\bundle_daemon.cpp

static void Init()
{
    SamgrLite *samgrLite = SAMGR_GetInstance();
    if (samgrLite == nullptr) {
        PRINTE("BundleDaemon", "get samgr is nullptr");
        return;
    }
    BOOL result = samgrLite->RegisterService(&BundleDaemon::GetInstance());
    if (!result) {
        PRINTE("BundleDaemon", "register bundle_daemon service fail");
        return;
    }
    result = samgrLite->RegisterDefaultFeatureApi(BDS_SERVICE, GET_IUNKNOWN(g_defaultApi));
    PRINTE("BundleDaemon", "register default feature api %{public}s", result ? "success" : "fail");
}
SYSEX_SERVICE_INIT(Init);

这里作为一个背景知识点进行介绍,不能理解的话,知道就可以了。

bundle_daemon.cpp也被一起打包进可执行文件bundle_daemon,运行bundle_daemon这个bin档的时候,这里的Init()函数会在main()函数之前被执行。

code-1.0\foundation\appexecfwk\services\bundlemgr_lite\bundle_daemon\src\main.cpp

extern "C" void __attribute__((weak)) HOS_SystemInit(void)
{
    SAMGR_Bootstrap();
}

int main(int argc, char* argv[])
{
    HOS_SystemInit();
    while (true) {
        pause();
    }
    return 0;
}

在执行bundle_daemon可执行文件时,会进入程序入口main()函数,函数体很简单,先调用SAMGR_Bootstrap()函数,然后进入无限循环。

SAMGR_Bootstrap()函数是动态链接库libsamgr.so里面的,这里暂不展开,后面会专门分享这个库的实现。这个函数完成的工作是:创建线程,线程首先向sa manager service注册endpoint,然后注册identity,最后进入无限循环处理clients发来的消息。

上面这些其实都是显式框架代码,如果想要自己构建一个SA,就可以参考bundle_daemon的实现。SAMGR_Bootstrap()是隐式的框架代码,庞大的框架代码都隐藏在这个函数身后。

API

code-1.0/foundation/appexecfwk/frameworks/bundle_lite/

代码位于上面的目录,模块名字是bundle,编译生成动态链接库libbundle.so,供应用程序调用。提供包操作的相关接口:

  • Install() // 安装应用程序
  • Uninstall() // 卸载应用程序
  • GetBundleInfo() // 获取应用程序信息

Install

安装或更新一个应用程序。

bool Install(const char *hapPath, const InstallParam *installParam, InstallerCallback installerCallback)
{
    // installParam是保留参数,目前没有使用,传入为nullptr
    if ((hapPath == nullptr) || (installerCallback == nullptr)) {
        HILOG_ERROR(HILOG_MODULE_APP, "BundleManager install failed due to nullptr parameters");
        return false;
    }
    // 检查权限
    if (CheckPermission(0, static_cast<const char *>(PERMISSION_INSTALL_BUNDLE)) != GRANTED) {
        HILOG_ERROR(HILOG_MODULE_APP, "BundleManager install failed due to permission denied");
        return false;
    }
    // 进行IPC,从sa manager service获取bms inner proxy
    auto bmsInnerClient = GetBmsInnerClient();
    if (bmsInnerClient == nullptr) {
        HILOG_ERROR(HILOG_MODULE_APP, "BundleManager install failed due to nullptr bms client");
        return false;
    }

    // 构建IPC request变量
    IpcIo ipcIo;
    char data[IPC_IO_DATA_MAX];
    IpcIoInit(&ipcIo, data, IPC_IO_DATA_MAX, 1);
    // 向request中放入hapPath
    IpcIoPushString(&ipcIo, hapPath);
    const SvcIdentity *svc = OHOS::BundleSelfCallback::GetInstance().RegisterBundleSelfCallback(installerCallback);
    if (svc == nullptr) {
        HILOG_ERROR(HILOG_MODULE_APP, "BundleManager Install svc is nullptr");
        return false;
    }
    // 向request中放入SvcIndentity
    IpcIoPushSvc(&ipcIo, svc);
    if (!IpcIoAvailable(&ipcIo)) {
        HILOG_ERROR(HILOG_MODULE_APP, "BundleManager Install ipc failed");
        return false;
    }
    HILOG_DEBUG(HILOG_MODULE_APP, "BMS client invoke install");
    uint8_t result = 0;
    // 进行IPC,调用bms的Invoke()函数
    int32_t ret = bmsInnerClient->Invoke(bmsInnerClient, INSTALL, &ipcIo, &result, Notify);
    if (ret != OHOS_SUCCESS) {
        HILOG_ERROR(HILOG_MODULE_APP, "BundleManager Install invoke failed: %{public}d", ret);
        return false;
    }
    return result == OHOS_SUCCESS;
}

安装应用程序示例

#include "bundle_manager.h"

static void ReceiveCallback(const uint8_t resultCode, const void *resultMessage)
{
    std::string strMessage = reinterpret_cast<const char *>(resultMessage);
    if (!strMessage.empty()) {
        printf("resultMessage is %s\n", strMessage.c_str());
    }
    sem_post(&g_sem);
}

Install(hapPath, null, ReceiveCallback);

Uninstall

卸载系统已经安装的第三方应用程序。

bool Uninstall(const char *bundleName, const InstallParam *installParam, InstallerCallback installerCallback)
{
    // 同样,installParam参数目前保留未使用
    // 包名长度不能超过128个字符
    if ((bundleName == nullptr) || (installerCallback == nullptr) || (strlen(bundleName) >= MAX_BUNDLE_NAME)) {
        HILOG_ERROR(HILOG_MODULE_APP, "BundleManager uninstall failed due to nullptr or invalid parameters");
        return false;
    }
    // 权限检查
    if (CheckPermission(0, static_cast<const char *>(PERMISSION_INSTALL_BUNDLE)) != GRANTED) {
        HILOG_ERROR(HILOG_MODULE_APP, "BundleManager uninstall failed due to permission denied");
        return false;
    }
    // 通过IPC,从sa manager service获取bms inner proxy
    auto bmsInnerClient = GetBmsInnerClient();
    if (bmsInnerClient == nullptr) {
        HILOG_ERROR(HILOG_MODULE_APP, "BundleManager uninstall failed due to nullptr bms client");
        return false;
    }
    // 注册InstallerCallback
    // 注册成功后,会返回SvcIndentity
    const SvcIdentity *svc = OHOS::BundleSelfCallback::GetInstance().RegisterBundleSelfCallback(installerCallback);
    if (svc == nullptr) {
        HILOG_ERROR(HILOG_MODULE_APP, "BundleManager Uninstall svc is nullptr");
        return false;
    }

    // 构建IPC request
    IpcIo ipcIo;
    char data[IPC_IO_DATA_MAX];
    IpcIoInit(&ipcIo, data, IPC_IO_DATA_MAX, 1);
    // 向IPC request中写入bundle name
    IpcIoPushString(&ipcIo, bundleName);
    // 向IPC request中写入SvcIndentity
    IpcIoPushSvc(&ipcIo, svc);
    if (!IpcIoAvailable(&ipcIo)) {
        HILOG_ERROR(HILOG_MODULE_APP, "BundleManager Uninstall ipc failed");
        return false;
    }
    HILOG_DEBUG(HILOG_MODULE_APP, "BMS client invoke uninstall");
    uint8_t result = 0;
    // 通过IPC,调用bms inner的Invoke()
    int32_t ret = bmsInnerClient->Invoke(bmsInnerClient, UNINSTALL, &ipcIo, &result, Notify);
    if (ret != OHOS_SUCCESS) {
        HILOG_ERROR(HILOG_MODULE_APP, "BundleManager Uninstall invoke failed: %{public}d", ret);
        return false;
    }
    return result == OHOS_SUCCESS;
}

获取应用程序信息

获取系统安装的应用程序的信息。

uint8_t GetBundleInfo(const char *bundleName, int32_t flags, BundleInfo *bundleInfo)
{
    if ((bundleName == nullptr) || (bundleInfo == nullptr)) {
        return ERR_APPEXECFWK_OBJECT_NULL;
    }
    if (flags < 0 || flags > 1 || (strlen(bundleName) >= MAX_BUNDLE_NAME)) {
        return ERR_APPEXECFWK_QUERY_PARAMETER_ERROR;
    }
    if (CheckPermission(0, static_cast<const char *>(PERMISSION_GET_BUNDLE_INFO)) != GRANTED) {
        HILOG_ERROR(HILOG_MODULE_APP, "BundleManager get BundleInfo failed due to permission denied");
        return ERR_APPEXECFWK_PERMISSION_DENIED;
    }
    // 通过IPC,向sa manager service获取bms proxy
    auto bmsClient = GetBmsClient();
    if (bmsClient == nullptr) {
        HILOG_ERROR(HILOG_MODULE_APP, "BundleManager get BundleInfo failed due to nullptr bms client");
        return ERR_APPEXECFWK_OBJECT_NULL;
    }
    // 构建IPC request data
    IpcIo ipcIo;
    char data[IPC_IO_DATA_MAX];
    IpcIoInit(&ipcIo, data, IPC_IO_DATA_MAX, 0);
    // 向IPC request中写入bundle name
    IpcIoPushString(&ipcIo, bundleName);
    IpcIoPushInt32(&ipcIo, flags);
    if (!IpcIoAvailable(&ipcIo)) {
        HILOG_ERROR(HILOG_MODULE_APP, "BundleManager GetBundleInfo ipc failed");
        return ERR_APPEXECFWK_SERIALIZATION_FAILED;
    }
    ResultOfGetBundleInfo resultOfGetBundleInfo;
    resultOfGetBundleInfo.bundleInfo = nullptr;
    // 通过IPC,调用bms的Invoke()
    int32_t ret = bmsClient->Invoke(bmsClient, GET_BUNDLE_INFO, &ipcIo, &resultOfGetBundleInfo, Notify);
    if (ret != OHOS_SUCCESS) {
        HILOG_ERROR(HILOG_MODULE_APP, "BundleManager GetBundleInfo invoke failed: %{public}d", ret);
        return ERR_APPEXECFWK_INVOKE_ERROR;
    }
    // 将返回数据写入到bundleInfo
    if (resultOfGetBundleInfo.resultCode == ERR_OK) {
        OHOS::BundleInfoUtils::CopyBundleInfo(flags, bundleInfo, *(resultOfGetBundleInfo.bundleInfo));
        ClearBundleInfo(resultOfGetBundleInfo.bundleInfo);
        AdapterFree(resultOfGetBundleInfo.bundleInfo);
    }
    return resultOfGetBundleInfo.resultCode;
}

应用程序信息结构体

包括基本的:

  • 是否常驻内存
  • 是否是c/c++开发的应用
  • uid、gid
  • 是否是系统应用
  • targetApi、compatibleApi
  • 版本号、版本名称
  • bundle name、label、iconPath
  • 代码路径、数据路径
  • ModuleInfo和数量
  • AbilityInfo和数量
  • appId、sharedLibPath

这些信息里面,有两个比较有特点:ModuleInfo和AbilityInfo。一个应用程序可以包含多个子模块和多个Ability。

typedef struct {
#ifdef OHOS_APPEXECFWK_BMS_BUNDLEMANAGER
    /** Whether the application is kept alive */
    bool isKeepAlive;

    /**
     * Whether the application is a local application. A local application refers to an application developed using
     * C++ in the system. The value <b>true</b> indicates a local application, and <b>false</b> indicates a non-local
     * application.
     */
    bool isNativeApp;

    /** UID allocated during application installation */
    int32_t uid;

    /** Application group ID allocated during application installation */
    int32_t gid;
#endif
    /**
     * Whether the application is a system application. System applications cannot be uninstalled. The value
     * <b>true</b> indicates a system application, and <b>false</b> indicates a non-system application.
     */
    bool isSystemApp;

    /** Pointer to the minimum API version required for running the application */
    int32_t compatibleApi;

    /** Pointer to the target API version for running the application */
    int32_t targetApi;

    /** Version code of the application, which is the internal version number */
    int32_t versionCode;

    /** Pointer to the version name visible to users */
    char *versionName;

    /** Pointer to the bundle name of the application */
    char *bundleName;

    /** Pointer to the application name visible to users */
    char *label;

    /** Pointer to the big icon of the application */
    char *bigIconPath;

    /**
     * Pointer to the installation path of the application, which is in the <b>/app/run/<i>bundle name</i></b>
     * format
     */
    char *codePath;

    /** Pointer to the path for storing data files of the application. The data path is <b>/app/data</b>. */
    char *dataPath;

    /** Pointer to the vendor name of the application */
    char *vendor;

    /**
     * Pointer to the HAP package information about the application. The HAP information is encapsulated in
     * {@link ModuleInfo} objects.
     */
    ModuleInfo *moduleInfos;

    /** Number of {@link ModuleInfo} objects included in the application */
    int32_t numOfModule;
#ifdef OHOS_APPEXECFWK_BMS_BUNDLEMANAGER
    /** Pointer to the shared library path */
    char *sharedLibPath;

    /**
     * Application ID, which uniquely identifies an application. It is a combination of the bundle name and
     * signature of the application.
     */
    char *appId;

    /**
     * Pointer to the ability information about the application. The ability information is encapsulated in
     * {@link AbilityInfo} objects.
     */
    AbilityInfo *abilityInfos;

    /** Number of {@link AbilityInfo} objects in the application */
    int32_t numOfAbility;
#else
    /**
     * Pointer to the path for storing the small icon of the application. This field is available only to basic
     * watches.
     */
    char *smallIconPath;

    /** Pointer to the ability information about the application. This field is available only to basic watches. */
    AbilityInfo *abilityInfo;
#endif
} BundleInfo;

bm

code-1.0\foundation\appexecfwk\services\bundlemgr_lite\tools\

bm模块的代码位于上面的路径。bm是系统提供的一个工具,用于从命令行操作bundle,它通过调用bundle API实现,即依赖动态链接库libbundle.so(模块bundle)。

安装应用程序

通过bm安装应用程序,命令大概如下:

bm install -p xxx.hap

code-1.0/foundation/appexecfwk/services/bundlemgr_lite/tools/src/command_parser.cpp

#include "bundle_manager.h"

void CommandParser::RunAsInstallCommand(int32_t argc, char *argv[]) const
{
    int32_t option;
    // 路径最长是4096个字符
    char realPath[PATH_MAX + 1] = { 0 };
    uint32_t cbId = INITIAL_CBID;
    SvcIdentity sid = SAMGR_GetRemoteIdentity(BMS_SERVICE, BMS_INNER_FEATURE);
    while ((option = getopt_long_only(argc, argv, SHORT_OPTIONS.c_str(), LONG_OPTIONS, nullptr)) != -1) {
        switch (option) {
            case 'p':
                // 调用Install()安装应用程序
                Install(realPath, nullptr, ReceiveCallback);
                sem_wait(&g_sem);
                break;
        }
    }
}

安装应用程序,直接调用bundle_manager的Install()函数。

卸载应用程序

通过bm卸载应用程序,命令大概如下:

bm uninstall -n "bundle_name"

code-1.0/foundation/appexecfwk/services/bundlemgr_lite/tools/src/command_parser.cpp

#include "bundle_manager.h"

void CommandParser::RunAsUninstallCommand(int32_t argc, char *argv[]) const
{
    int32_t option;
    uint32_t cbId = INITIAL_CBID;
    SvcIdentity sid = SAMGR_GetRemoteIdentity(BMS_SERVICE, BMS_INNER_FEATURE);
    while ((option = getopt_long_only(argc, argv, SHORT_OPTIONS.c_str(), LONG_OPTIONS, nullptr)) != -1) {
        switch (option) {
            case 'n':
                // 调用Uninstall()卸载应用程序
                Uninstall(optarg, nullptr, ReceiveCallback);
                sem_wait(&g_sem);
                break;
        }
    }
}

卸载应用程序,直接调用bundle_manager的Uninstall()函数。