installd分析


android apk安装最后使用服务installd来实现,源码路径:frameworks/base/cmds/installd
$ ls -l frameworks/base/cmds/installd
total 68
-rw-r--r-- 1 lizhiguo lizhiguo 2321 2011-11-15 17:06 Android.mk
-rw-r--r-- 1 lizhiguo lizhiguo 34863 2011-11-15 17:06 commands.c
-rw-r--r-- 1 lizhiguo lizhiguo 9742 2011-11-15 17:06 installd.c
-rw-r--r-- 1 lizhiguo lizhiguo 5586 2011-11-15 17:06 installd.h
-rw-r--r-- 1 lizhiguo lizhiguo 6902 2011-11-15 17:06 utils.c

frameworks/base/services/java/com/android/server/Installer.java该文件中实现了java的Installer类,负责和Installd通信,主要实现的功能包括:
connect,disconnect,execute,install,dexopt,movedex等,对应于installd里面的功能:
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", 3, do_rename },
{ "freecache", 1, do_free_cache },
{ "rmcache", 2, do_rm_cache },
{ "protect", 2, do_protect },
{ "getsize", 4, do_get_size },
{ "rmuserdata", 2, do_rm_user_data },
{ "movefiles", 0, do_movefiles },
{ "linklib", 2, do_linklib },
{ "unlinklib", 1, do_unlinklib },
};

@ frameworks/base/cmds/installd/installd.c
int main(const int argc, const char *argv[]) {
char buf[BUFFER_MAX];
struct sockaddr addr;
socklen_t alen;
int lsocket, s, count;

lsocket = android_get_control_socket(SOCKET_PATH); // #define SOCKET_PATH "installd"
// frameworks/base/cmds/installd/installd.h
// 获得socket的描述符,见后注释
...
if (listen(lsocket, 5)) {
LOGE("Listen on socket failed: %s\n", strerror(errno));
exit(1);
}
fcntl(lsocket, F_SETFD, FD_CLOEXEC);
LOGI("new connection\n");
for (;;) {
unsigned short count;
if (readx(s, &count, sizeof(count))) {
LOGE("failed to read size\n");
break;
} // 读数据大小
if ((count < 1) || (count >= BUFFER_MAX)) {
LOGE("invalid size %d\n", count);
break;
}
if (readx(s, buf, count)) {
LOGE("failed to read command\n");
break;
} // 读实际数据
buf[count] = 0;
if (execute(s, buf)) break; // 执行命令
}
LOGI("closing connection\n");
close(s);
}

return 0;

}

//////////////////////////////////////////////////////////////////////////
@ system/core/include/cutils/Sockets.h
#define ANDROID_SOCKET_ENV_PREFIX"ANDROID_SOCKET_"
#define ANDROID_SOCKET_DIR"/dev/socket"

static inline int android_get_control_socket(const char *name)
{
char key[64] = ANDROID_SOCKET_ENV_PREFIX; // ANDROID_SOCKET_
const char *val;
int fd;
...
strncpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1, name, sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
key[sizeof(key)-1] = '\0';
...

val = getenv(key); // 从环境变量中得到这个key值,那么它什么时候设置的呢?
if (!val)
return -1;

errno = 0;
fd = strtol(val, NULL, 10);
if (errno)
return -1;

return fd;
}

/*
* See also android.os.LocalSocketAddress.Namespace
*/
// Linux "abstract" (non-filesystem) namespace
#define ANDROID_SOCKET_NAMESPACE_ABSTRACT 0
// Android "reserved" (/dev/socket) namespace
#define ANDROID_SOCKET_NAMESPACE_RESERVED 1
// Normal filesystem namespace
#define ANDROID_SOCKET_NAMESPACE_FILESYSTEM 2


系统属性区是在init进程启动过程中初始化的,而起在init.rc中会发现如下的语句:
service installd /system/bin/installd
socket installd stream 600 system system

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
socket zygote stream 666
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
在描述启动服务的语句时,会跟上关于这个服务的所有options,而这里socket就是其中一项option。

1. 系统属性区和属性服务的建立
android的每一个版本的init代码都有一些差别,所以以下是android2.3的init代码:
@ system/core/init/init.c
int main(int argc, char **argv)
{
...
queue_builtin_action(property_init_action, "property_init");
// 在这里来来初始化属性区域。
...

queue_builtin_action(property_service_init_action, "property_service_init");
// 开启属性服务,会导入其余三个文件:/system/build.prop,/system/default.prop, /data/local.prop中的属性和/data/property中的persist.开头的属性。
// 然后加上lcd density的属性,ro.sf.lcd_density默认是160(MDPI), 从底层读取像素值判断是否是120(LDPI),240(HDPI).
// @ system/core/init/proerty_service.c
// @ system/core/init/property_patch.c
// 接着调用fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);创建一个流套接字
// listen(fd, 8); property_set_fd = fd;
// 最后在init进去无限循环中,通过socket接收其他进程的属性请求来处理。
...

}
static int property_init_action(int nargs, char **args)
{
INFO("property init\n");
property_init();
return 0;
}

@ system/core/init/proerty_service.c
void property_init(void)
{
init_property_area();
load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);
// 初始了属性区域之后,首先导入编译生成的属性文件:
}
@ bionic/libc/include/sys/_system_properties.h
#define PROP_PATH_RAMDISK_DEFAULT "/default.prop"
#define PROP_PATH_SYSTEM_BUILD "/system/build.prop"
#define PROP_PATH_SYSTEM_DEFAULT "/system/default.prop"
#define PROP_PATH_LOCAL_OVERRIDE "/data/local.prop"

@ system/core/init/proerty_service.c
#define PA_COUNT_MAX 247
#define PA_INFO_START 1024
#define PA_SIZE 32768

static workspace pa_workspace;
static prop_info *pa_info_array;

extern prop_area *__system_property_area__;
static int init_property_area(void)
{
prop_area *pa;

if(pa_info_array)
return -1;

if(init_workspace(&pa_workspace, PA_SIZE))// 初始化工作区。
return -1;

fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);

pa_info_array = (void*) (((char*) pa_workspace.data) + PA_INFO_START);

pa = pa_workspace.data;
memset(pa, 0, PA_SIZE);
pa->magic = PROP_AREA_MAGIC;
pa->version = PROP_AREA_VERSION;

/* plug into the lib property services */
__system_property_area__ = pa;
property_area_inited = 1; // 表明属性区已经经过了初始化。
return 0;
}
// 初始化工作区
static int init_workspace(workspace *w, size_t size)
{
void *data;
int fd;

fd = open("/dev/__properties__", O_RDWR | O_CREAT, 0600);// 以读写方式打开设备文件/dev/__properties__
...
data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// 将该文件描述符映射到init进程用户空间的虚拟地址,可读可写。
...
close(fd);

fd = open("/dev/__properties__", O_RDONLY);// 关闭之后重新已只读打开这个设备文件。
// 这样的打开方式保证了只有init进程有对属性区域写的权限,而其余进程只有读的权限。
unlink("/dev/__properties__");

w->data = data;
w->size = size;
w->fd = fd;// 将大小,文件描述符,虚拟地址保存在全局变量pa_workspace中。
return 0;

out:
close(fd);
return -1;
}

2. 带socket选项的服务启动
解析init.rc中的服务:
init_parse_config_file("/init.rc")
--> parse_config(fn, data)
--> parse_new_section(&state, kw, nargs, args)
--> parse_line_service()
...
case K_socket: {/* name type perm [ uid gid ] */
struct socketinfo *si;
if (nargs < 4) {
parse_error(state, "socket option requires name, type, perm arguments\n");
break;
}
if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")
&& strcmp(args[2],"seqpacket")) {
parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n");
// android只支持这三种形式的socket:数据报文套接字(UDP),流套接字(TCP),seqpacket(使用SCTP协议)
break;
}
si = calloc(1, sizeof(*si)); // 分配socketinfo的内存
if (!si) {
parse_error(state, "out of memory\n");
break;
}
// installd stream 600 system system
si->name = args[1]; // installd
si->type = args[2]; // stream
si->perm = strtoul(args[3], 0, 8); // 600
if (nargs > 4)
si->uid = decode_uid(args[4]); // system
if (nargs > 5)
si->gid = decode_uid(args[5]); // system
si->next = svc->sockets;
svc->sockets = si; // 保存到当前服务中的sockets链表中,该服务启动的时候会创建这些socket。
break;
}
...
开启服务函数:service_start()
void service_start(struct service *svc, const char *dynamic_args)
{
...
NOTICE("starting '%s'\n", svc->name);
pid = fork();

if (pid == 0) {
...
if (properties_inited()) {
get_property_workspace(&fd, &sz);
sprintf(tmp, "%d,%d", dup(fd), sz);
add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
}// 添加系统属性区的描述符和大小打环境变量中,ANDROID_PROPERTY_WORKSPACE
...
for (si = svc->sockets; si; si = si->next) {
int socket_type = (
!strcmp(si->type, "stream") ? SOCK_STREAM :
(!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
int s = create_socket(si->name, socket_type, si->perm, si->uid, si->gid);// 创建socket
if (s >= 0) {
publish_socket(si->name, s);// 将socket添加到环境变量中
}
}
...
}

}
static void publish_socket(const char *name, int fd)
{
char key[64] = ANDROID_SOCKET_ENV_PREFIX;
char val[64];

strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
name,
sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
snprintf(val, sizeof(val), "%d", fd);
add_environment(key, val);// 添加至环境变量

/* make sure we don't close-on-exec */
fcntl(fd, F_SETFD, 0);
}


更多相关文章

  1. Android 编译提示R文件找不到
  2. Android 关于WebView的相关属性
  3. Android JNI学习笔记——so文件动态加载
  4. Android APK 文件自动安装
  5. Android读写文件二
  6. RecylcerView中的子view matchParent属性失效的问题
  7. 【Android】Android中 Paint 字体、粗细等属性的一些设置
  8. 安卓笔记:安卓控件属性大全

随机推荐

  1. android 主题元素映射方式
  2. Porting Android
  3. Android任务栏的图标显示
  4. 提示:Not targeting the latest versions
  5. Android(安卓)InputStream与String,Byte
  6. Android(安卓)异常:Immutable bitmap pass
  7. Android(安卓)开启闪光灯做手电筒 源码
  8. Android: 判断网络连接状态及连接类型
  9. Forward [To: Android(安卓)Beginners ]
  10. Android(安卓)HTTP GET/POST