您的位置:首页 > 移动开发 > Android开发

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

2016-07-28 23:07 453 查看
上次说到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
= 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: