Android应用安装过程分析(二)

来源:互联网 发布:易店淘宝店铺买卖骗局 编辑:程序博客网 时间:2024/05/17 00:11

上次说到initView的两句核心代码,installPackageWithVerificationAndEncryption函数和installExistingPackage函数。

而成员pm是一个PackageManager类型的变量,这是一个接口类,具体的实现代码在PackageManagerService中

    public void installPackageWithVerificationAndEncryption(Uri packageURI,            IPackageInstallObserver observer, int flags, String installerPackageName,            VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,                null);        final int uid = Binder.getCallingUid();        if (isUserRestricted(UserHandle.getUserId(uid), UserManager.DISALLOW_INSTALL_APPS)) {            try {                observer.packageInstalled("", PackageManager.INSTALL_FAILED_USER_RESTRICTED);            } catch (RemoteException re) {            }            return;        }        UserHandle user;        if ((flags&PackageManager.INSTALL_ALL_USERS) != 0) {            user = UserHandle.ALL;        } else {            user = new UserHandle(UserHandle.getUserId(uid));        }        final int filteredFlags;        if (uid == Process.SHELL_UID || uid == 0) {            if (DEBUG_INSTALL) {                Slog.v(TAG, "Install from ADB");            }            filteredFlags = flags | PackageManager.INSTALL_FROM_ADB;        } else {            filteredFlags = flags & ~PackageManager.INSTALL_FROM_ADB;        }        verificationParams.setInstallerUid(uid);        final Message msg = mHandler.obtainMessage(INIT_COPY);        msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName,                verificationParams, encryptionParams, user);        mHandler.sendMessage(msg);    }

在Android中,PackageManager负责管理安装包的信息,但是应用的安装和卸载是PackageManager访问installd服务来获得

int res = mInstaller.install(packageName, uid, uid, seinfo);
这是PackageManager中的代码

mInstaller来自Installer

    public int install(String name, int uid, int gid, String seinfo) {        StringBuilder builder = new StringBuilder("install");        builder.append(' ');        builder.append(name);        builder.append(' ');        builder.append(uid);        builder.append(' ');        builder.append(gid);        builder.append(' ');        builder.append(seinfo != null ? seinfo : "!");        return execute(builder.toString());    }
install中调用execute

    private int execute(String cmd) {        String res = transaction(cmd);        try {            return Integer.parseInt(res);        } catch (NumberFormatException ex) {            return -1;        }    }
再调用transaction

    private synchronized String transaction(String cmd) {        if (!connect()) {            Slog.e(TAG, "connection failed");            return "-1";        }        if (!writeCommand(cmd)) {            /*             * If installd died and restarted in the background (unlikely but             * possible) we'll fail on the next write (this one). Try to             * reconnect and write the command one more time before giving up.             */            Slog.e(TAG, "write command failed? reconnect!");            if (!connect() || !writeCommand(cmd)) {                return "-1";            }        }        if (LOCAL_DEBUG) {            Slog.i(TAG, "send: '" + cmd + "'");        }        if (readReply()) {            String s = new String(buf, 0, buflen);            if (LOCAL_DEBUG) {                Slog.i(TAG, "recv: '" + s + "'");            }            return s;        } else {            if (LOCAL_DEBUG) {                Slog.i(TAG, "fail");            }            return "-1";        }    }
可以看到首先通过connect函数进行连接

    private boolean connect() {        if (mSocket != null) {            return true;        }        Slog.i(TAG, "connecting...");        try {            mSocket = new LocalSocket();            LocalSocketAddress address = new LocalSocketAddress("installd",                    LocalSocketAddress.Namespace.RESERVED);            mSocket.connect(address);            mIn = mSocket.getInputStream();            mOut = mSocket.getOutputStream();        } catch (IOException ex) {            disconnect();            return false;        }        return true;    }

address指向的就是install服务,通过LocalSocket本地套接字。此外还有输入输出流

public final class Installer {    private static final String TAG = "Installer";    private static final boolean LOCAL_DEBUG = false;    InputStream mIn;    OutputStream mOut;    LocalSocket mSocket;    byte buf[] = new byte[1024];    int buflen = 0;
connect连接成功后使用writeCommand函数将内容写入通信流当中

    private boolean writeCommand(String _cmd) {        byte[] cmd = _cmd.getBytes();        int len = cmd.length;        if ((len < 1) || (len > 1024))            return false;        buf[0] = (byte) (len & 0xff);        buf[1] = (byte) ((len >> 8) & 0xff);        try {            mOut.write(buf, 0, 2);            mOut.write(cmd, 0, len);        } catch (IOException ex) {            Slog.e(TAG, "write error");            disconnect();            return false;        }        return true;    }
脉络很清楚pm ——> PackageManagerServer ——> Installer.install ——>通过LocalSocket ——> Installd
相关文件位置

frameworks\native\cmds\installd\installd.c

frameworks\base\services\java\com\android\server\pm\PackageManagerService.java

frameworks\base\services\java\com\android\server\pm\Installer.java

下面着重关注一下installd

主函数

int main(const int argc, const char *argv[]) {    char buf[BUFFER_MAX];    struct sockaddr addr;    socklen_t alen;    int lsocket, s, count;    ALOGI("installd firing up\n");    if (initialize_globals() < 0) {//初始化全局变量        ALOGE("Could not initialize globals; exiting.\n");        exit(1);    }    if (initialize_directories() < 0) {//初始化安装目录        ALOGE("Could not create directories; exiting.\n");        exit(1);    }    drop_privileges();//权限限制    lsocket = android_get_control_socket(SOCKET_PATH);//取得套接字句柄    if (lsocket < 0) {        ALOGE("Failed to get socket from environment: %s\n", strerror(errno));        exit(1);    }    if (listen(lsocket, 5)) {//监听socket        ALOGE("Listen on socket failed: %s\n", strerror(errno));        exit(1);    }    fcntl(lsocket, F_SETFD, FD_CLOEXEC);//修改socket的属性    for (;;) {//循环等待客户端的消息        alen = sizeof(addr);        s = accept(lsocket, &addr, &alen);        if (s < 0) {            ALOGE("Accept failed: %s\n", strerror(errno));            continue;        }        fcntl(s, F_SETFD, FD_CLOEXEC);        ALOGI("new connection\n");        for (;;) {//循环读取内容            unsigned short count;    //客户端数据的格式为  | 数据长度 | 数据内容 |    //读取头四个字节获得长度            if (readx(s, &count, sizeof(count))) {                ALOGE("failed to read size\n");                break;            }    //判断长度的合法性            if ((count < 1) || (count >= BUFFER_MAX)) {                ALOGE("invalid size %d\n", count);                break;            }    //读取全部内容            if (readx(s, buf, count)) {                ALOGE("failed to read command\n");                break;            }    //执行            buf[count] = 0;            if (execute(s, buf)) break;        }        ALOGI("closing connection\n");        close(s);    }    return 0;}
全局变量的初始化函数initialize_globals
int initialize_globals() {    // Get the android data directory.    if (get_path_from_env(&android_data_dir, "ANDROID_DATA") < 0) {        return -1;    }    // Get the android app directory.    if (copy_and_append(&android_app_dir, &android_data_dir, APP_SUBDIR) < 0) {        return -1;    }    // Get the android protected app directory.    if (copy_and_append(&android_app_private_dir, &android_data_dir, PRIVATE_APP_SUBDIR) < 0) {        return -1;    }    // Get the android app native library directory.    if (copy_and_append(&android_app_lib_dir, &android_data_dir, APP_LIB_SUBDIR) < 0) {        return -1;    }    // Get the sd-card ASEC mount point.    if (get_path_from_env(&android_asec_dir, "ASEC_MOUNTPOINT") < 0) {        return -1;    }    // Get the android media directory.    if (copy_and_append(&android_media_dir, &android_data_dir, MEDIA_SUBDIR) < 0) {        return -1;    }    // Take note of the system and vendor directories.    android_system_dirs.count = 2;    android_system_dirs.dirs = calloc(android_system_dirs.count, sizeof(dir_rec_t));    if (android_system_dirs.dirs == NULL) {        ALOGE("Couldn't allocate array for dirs; aborting\n");        return -1;    }    // system    if (get_path_from_env(&android_system_dirs.dirs[0], "ANDROID_ROOT") < 0) {        free_globals();        return -1;    }    // append "app/" to dirs[0]    char *system_app_path = build_string2(android_system_dirs.dirs[0].path, APP_SUBDIR);    android_system_dirs.dirs[0].path = system_app_path;    android_system_dirs.dirs[0].len = strlen(system_app_path);    // vendor    // TODO replace this with an environment variable (doesn't exist yet)    android_system_dirs.dirs[1].path = "/vendor/app/";    android_system_dirs.dirs[1].len = strlen(android_system_dirs.dirs[1].path);    return 0;}
全局变量位于同目录下的installd.h文件,部分变量如下所示

typedef struct {    char* path;    size_t len;} dir_rec_t;typedef struct {    size_t count;    dir_rec_t* dirs;} dir_rec_array_t;extern dir_rec_t android_app_dir;extern dir_rec_t android_app_private_dir;extern dir_rec_t android_app_lib_dir;extern dir_rec_t android_data_dir;extern dir_rec_t android_asec_dir;extern dir_rec_t android_media_dir;extern dir_rec_array_t android_system_dirs;
此外这当中的一些函数比如copy_and_append,也位于同目录下的utils.c

int copy_and_append(dir_rec_t* dst, const dir_rec_t* src, const char* suffix) {    dst->len = src->len + strlen(suffix);    const size_t dstSize = dst->len + 1;    dst->path = (char*) malloc(dstSize);    if (dst->path == NULL            || snprintf(dst->path, dstSize, "%s%s", src->path, suffix)                    != (ssize_t) dst->len) {        ALOGE("Could not allocate memory to hold appended path; aborting\n");        return -1;    }    return 0;}
之后是初始化安装目录initialize_directories

int initialize_directories() {    int res = -1;    // Read current filesystem layout version to handle upgrade paths    char version_path[PATH_MAX];    snprintf(version_path, PATH_MAX, "%s.layout_version", android_data_dir.path);    int oldVersion;    if (fs_read_atomic_int(version_path, &oldVersion) == -1) {        oldVersion = 0;    }    int version = oldVersion;    // /data/user    char *user_data_dir = build_string2(android_data_dir.path, SECONDARY_USER_PREFIX);    // /data/data    char *legacy_data_dir = build_string2(android_data_dir.path, PRIMARY_USER_PREFIX);    // /data/user/0    char *primary_data_dir = build_string3(android_data_dir.path, SECONDARY_USER_PREFIX, "0");    if (!user_data_dir || !legacy_data_dir || !primary_data_dir) {        goto fail;    }    // Make the /data/user directory if necessary    if (access(user_data_dir, R_OK) < 0) {        if (mkdir(user_data_dir, 0711) < 0) {            goto fail;        }        if (chown(user_data_dir, AID_SYSTEM, AID_SYSTEM) < 0) {            goto fail;        }        if (chmod(user_data_dir, 0711) < 0) {            goto fail;        }    }    // Make the /data/user/0 symlink to /data/data if necessary    if (access(primary_data_dir, R_OK) < 0) {        if (symlink(legacy_data_dir, primary_data_dir)) {            goto fail;        }    }    if (version == 0) {        // Introducing multi-user, so migrate /data/media contents into /data/media/0        ALOGD("Upgrading /data/media for multi-user");        // Ensure /data/media        if (fs_prepare_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {            goto fail;        }        // /data/media.tmp        char media_tmp_dir[PATH_MAX];        snprintf(media_tmp_dir, PATH_MAX, "%smedia.tmp", android_data_dir.path);        // Only copy when upgrade not already in progress        if (access(media_tmp_dir, F_OK) == -1) {            if (rename(android_media_dir.path, media_tmp_dir) == -1) {                ALOGE("Failed to move legacy media path: %s", strerror(errno));                goto fail;            }        }        // Create /data/media again        if (fs_prepare_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {            goto fail;        }        // /data/media/0        char owner_media_dir[PATH_MAX];        snprintf(owner_media_dir, PATH_MAX, "%s0", android_media_dir.path);        // Move any owner data into place        if (access(media_tmp_dir, F_OK) == 0) {            if (rename(media_tmp_dir, owner_media_dir) == -1) {                ALOGE("Failed to move owner media path: %s", strerror(errno));                goto fail;            }        }        // Ensure media directories for any existing users        DIR *dir;        struct dirent *dirent;        char user_media_dir[PATH_MAX];        dir = opendir(user_data_dir);        if (dir != NULL) {            while ((dirent = readdir(dir))) {                if (dirent->d_type == DT_DIR) {                    const char *name = dirent->d_name;                    // skip "." and ".."                    if (name[0] == '.') {                        if (name[1] == 0) continue;                        if ((name[1] == '.') && (name[2] == 0)) continue;                    }                    // /data/media/<user_id>                    snprintf(user_media_dir, PATH_MAX, "%s%s", android_media_dir.path, name);                    if (fs_prepare_dir(user_media_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {                        goto fail;                    }                }            }            closedir(dir);        }        version = 1;    }    // /data/media/obb    char media_obb_dir[PATH_MAX];    snprintf(media_obb_dir, PATH_MAX, "%sobb", android_media_dir.path);    if (version == 1) {        // Introducing /data/media/obb for sharing OBB across users; migrate        // any existing OBB files from owner.        ALOGD("Upgrading to shared /data/media/obb");        // /data/media/0/Android/obb        char owner_obb_path[PATH_MAX];        snprintf(owner_obb_path, PATH_MAX, "%s0/Android/obb", android_media_dir.path);        // Only move if target doesn't already exist        if (access(media_obb_dir, F_OK) != 0 && access(owner_obb_path, F_OK) == 0) {            if (rename(owner_obb_path, media_obb_dir) == -1) {                ALOGE("Failed to move OBB from owner: %s", strerror(errno));                goto fail;            }        }        version = 2;    }    if (ensure_media_user_dirs(0) == -1) {        ALOGE("Failed to setup media for user 0");        goto fail;    }    if (fs_prepare_dir(media_obb_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {        goto fail;    }    // Persist layout version if changed    if (version != oldVersion) {        if (fs_write_atomic_int(version_path, version) == -1) {            ALOGE("Failed to save version to %s: %s", version_path, strerror(errno));            goto fail;        }    }    // Success!    res = 0;fail:    free(user_data_dir);    free(legacy_data_dir);    free(primary_data_dir);    return res;}
然后drop_privileges,用于设置权限

static void drop_privileges() {    if (prctl(PR_SET_KEEPCAPS, 1) < 0) {        ALOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));        exit(1);    }    if (setgid(AID_INSTALL) < 0) {        ALOGE("setgid() can't drop privileges; exiting.\n");        exit(1);    }    if (setuid(AID_INSTALL) < 0) {        ALOGE("setuid() can't drop privileges; exiting.\n");        exit(1);    }    struct __user_cap_header_struct capheader;    struct __user_cap_data_struct capdata[2];    memset(&capheader, 0, sizeof(capheader));    memset(&capdata, 0, sizeof(capdata));    capheader.version = _LINUX_CAPABILITY_VERSION_3;    capheader.pid = 0;    capdata[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE);    capdata[CAP_TO_INDEX(CAP_CHOWN)].permitted        |= CAP_TO_MASK(CAP_CHOWN);    capdata[CAP_TO_INDEX(CAP_SETUID)].permitted       |= CAP_TO_MASK(CAP_SETUID);    capdata[CAP_TO_INDEX(CAP_SETGID)].permitted       |= CAP_TO_MASK(CAP_SETGID);    capdata[0].effective = capdata[0].permitted;    capdata[1].effective = capdata[1].permitted;    capdata[0].inheritable = 0;    capdata[1].inheritable = 0;    if (capset(&capheader, &capdata[0]) < 0) {        ALOGE("capset failed: %s\n", strerror(errno));        exit(1);    }}
这当中包含了gid和uid的设定

执行函数execute

static int execute(int s, char cmd[BUFFER_MAX]){    char reply[REPLY_MAX];    char *arg[TOKEN_MAX+1];    unsigned i;    unsigned n = 0;    unsigned short count;    int ret = -1;//    ALOGI("execute('%s')\n", cmd);        /* default reply is "" */    reply[0] = 0;        /* n is number of args (not counting arg[0]) */    arg[0] = cmd;    while (*cmd) {        if (isspace(*cmd)) {            *cmd++ = 0;            n++;            arg[n] = cmd;            if (n == TOKEN_MAX) {                ALOGE("too many arguments\n");                goto done;            }        }        cmd++;    }    //与cmds中保存的命令进行匹配,并且要检查参数个数是否符合要求    for (i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) {        if (!strcmp(cmds[i].name,arg[0])) {            if (n != cmds[i].numargs) {                ALOGE("%s requires %d arguments (%d given)\n",                     cmds[i].name, cmds[i].numargs, n);            } else {                ret = cmds[i].func(arg + 1, reply);            }            goto done;        }    }    ALOGE("unsupported command '%s'\n", arg[0]);done:    if (reply[0]) {        n = snprintf(cmd, BUFFER_MAX, "%d %s", ret, reply);    } else {        n = snprintf(cmd, BUFFER_MAX, "%d", ret);    }    if (n > BUFFER_MAX) n = BUFFER_MAX;    count = n;//    ALOGI("reply: '%s'\n", cmd);    if (writex(s, &count, sizeof(count))) return -1;    if (writex(s, cmd, count)) return -1;    return 0;}
函数很简单。。。

cmds的内容

struct cmdinfo cmds[] = {    { "ping",                 0, do_ping },    { "install",              4, do_install },    { "dexopt",               3, do_dexopt },    { "movedex",              2, do_move_dex },    { "rmdex",                1, do_rm_dex },    { "remove",               2, do_remove },    { "rename",               2, do_rename },    { "fixuid",               3, do_fixuid },    { "freecache",            1, do_free_cache },    { "rmcache",              2, do_rm_cache },    { "getsize",              6, do_get_size },    { "rmuserdata",           2, do_rm_user_data },    { "movefiles",            0, do_movefiles },    { "linklib",              3, do_linklib },    { "mkuserdata",           3, do_mk_user_data },    { "rmuser",               1, do_rm_user },};
第一列是命令字符串,第二列是该命令的参数个数,第三列是函数指针。

cmdinfo结构

struct cmdinfo {    const char *name;    unsigned numargs;    int (*func)(char **arg, char reply[REPLY_MAX]);};

这些do开头的函数只是对真正执行函数的一个简单封装,比如do_install

static int do_install(char **arg, char reply[REPLY_MAX]){    return install(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3]); /* pkgname, uid, gid, seinfo */}
而真正的执行函数都保存在同目录下的commands.c文件中

int install(const char *pkgname, uid_t uid, gid_t gid, const char *seinfo){    char pkgdir[PKG_PATH_MAX];    char libsymlink[PKG_PATH_MAX];    char applibdir[PKG_PATH_MAX];    struct stat libStat;    if ((uid < AID_SYSTEM) || (gid < AID_SYSTEM)) {        ALOGE("invalid uid/gid: %d %d\n", uid, gid);        return -1;    }    if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, 0)) {        ALOGE("cannot create package path\n");        return -1;    }    if (create_pkg_path(libsymlink, pkgname, PKG_LIB_POSTFIX, 0)) {        ALOGE("cannot create package lib symlink origin path\n");        return -1;    }    if (create_pkg_path_in_dir(applibdir, &android_app_lib_dir, pkgname, PKG_DIR_POSTFIX)) {        ALOGE("cannot create package lib symlink dest path\n");        return -1;    }    if (mkdir(pkgdir, 0751) < 0) {        ALOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno));        return -1;    }    if (chmod(pkgdir, 0751) < 0) {        ALOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno));        unlink(pkgdir);        return -1;    }    if (lstat(libsymlink, &libStat) < 0) {        if (errno != ENOENT) {            ALOGE("couldn't stat lib dir: %s\n", strerror(errno));            return -1;        }    } else {        if (S_ISDIR(libStat.st_mode)) {            if (delete_dir_contents(libsymlink, 1, 0) < 0) {                ALOGE("couldn't delete lib directory during install for: %s", libsymlink);                return -1;            }        } else if (S_ISLNK(libStat.st_mode)) {            if (unlink(libsymlink) < 0) {                ALOGE("couldn't unlink lib directory during install for: %s", libsymlink);                return -1;            }        }    }    if (symlink(applibdir, libsymlink) < 0) {        ALOGE("couldn't symlink directory '%s' -> '%s': %s\n", libsymlink, applibdir,                strerror(errno));        unlink(pkgdir);        return -1;    }    if (selinux_android_setfilecon2(pkgdir, pkgname, seinfo, uid) < 0) {        ALOGE("cannot setfilecon dir '%s': %s\n", pkgdir, strerror(errno));        unlink(libsymlink);        unlink(pkgdir);        return -errno;    }    if (chown(pkgdir, uid, gid) < 0) {        ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno));        unlink(libsymlink);        unlink(pkgdir);        return -1;    }    return 0;}














0 0
原创粉丝点击