從前面Android包管理機制(三)PMS處理APK安裝中能夠了解到PMS在安裝APK的時候調用了Installer的函數,而Installer實際是調用了installd中的方法。今天咱們就來了解下守護進程installd,看看它作了什麼,以及爲何須要有一個守護進程?java
frameworks/native/cmds/installd/installd.rc
node
service installd /system/bin/installd
class main socket installd stream 600 system system 複製代碼
installd將做爲service被init進程啓動,同時會建立一個名爲installd的socketlinux
frameworks/native/cmds/installd/installd.cpp
android
int main(const int argc, char *argv[]) {
return android::installd::installd_main(argc, argv);
}
static int installd_main(const int argc ATTRIBUTE_UNUSED, char *argv[]) {
char buf[BUFFER_MAX];
struct sockaddr addr;
socklen_t alen;
int lsocket, s;
int selinux_enabled = (is_selinux_enabled() > 0);
setenv("ANDROID_LOG_TAGS", "*:v", 1);
android::base::InitLogging(argv);
ALOGI("installd firing up\n");
union selinux_callback cb;
cb.func_log = log_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
if (!initialize_globals()) {
ALOGE("Could not initialize globals; exiting.\n");
exit(1);
}
if (initialize_directories() < 0) {
ALOGE("Could not create directories; exiting.\n");
exit(1);
}
if (selinux_enabled && selinux_status_open(true) < 0) {
ALOGE("Could not open selinux status; exiting.\n");
exit(1);
}
//獲得"installd" socket
lsocket = android_get_control_socket(SOCKET_PATH);
if (lsocket < 0) {
ALOGE("Failed to get socket from environment: %s\n", strerror(errno));
exit(1);
}
//socket監聽
if (listen(lsocket, 5)) {
ALOGE("Listen on socket failed: %s\n", strerror(errno));
exit(1);
}
fcntl(lsocket, F_SETFD, FD_CLOEXEC);
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 (selinux_enabled && selinux_status_updated() > 0) {
selinux_android_seapp_context_reload();
}
//執行命令
if (execute(s, buf)) break;
}
ALOGI("closing connection\n");
close(s);
}
return 0;
}
複製代碼
上面的代碼也比較簡單,啓動後獲取installd socket,監聽鏈接,獲取客戶端發送的命令並執行。git
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;
/* 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;
//參數個數+1
n++;
//保存參數
arg[n] = cmd;
if (n == TOKEN_MAX) {
ALOGE("too many arguments\n");
goto done;
}
}
if (*cmd) {
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]);
return 0;
}
複製代碼
execute就是在接收到命令後在cmds中查找對應的指令,而後執行。github
struct cmdinfo cmds[] = {
{ "ping", 0, do_ping },
{ "create_app_data", 7, do_create_app_data },
{ "restorecon_app_data", 6, do_restorecon_app_data },
{ "migrate_app_data", 4, do_migrate_app_data },
{ "clear_app_data", 5, do_clear_app_data },
{ "destroy_app_data", 5, do_destroy_app_data },
{ "move_complete_app", 7, do_move_complete_app },
{ "get_app_size", 6, do_get_app_size },
{ "get_app_data_inode", 4, do_get_app_data_inode },
{ "create_user_data", 4, do_create_user_data },
{ "destroy_user_data", 3, do_destroy_user_data },
{ "dexopt", 10, do_dexopt },
{ "markbootcomplete", 1, do_mark_boot_complete },
{ "rmdex", 2, do_rm_dex },
{ "freecache", 2, do_free_cache },
{ "linklib", 4, do_linklib },
{ "idmap", 3, do_idmap },
{ "createoatdir", 2, do_create_oat_dir },
{ "rmpackagedir", 1, do_rm_package_dir },
{ "clear_app_profiles", 1, do_clear_app_profiles },
{ "destroy_app_profiles", 1, do_destroy_app_profiles },
{ "linkfile", 3, do_link_file },
{ "move_ab", 3, do_move_ab },
{ "merge_profiles", 2, do_merge_profiles },
{ "dump_profiles", 3, do_dump_profiles },
{ "delete_odex", 3, do_delete_odex },
};
複製代碼
這個就是cmds數組,第一列是指令名稱,第二列是要求的參數個數,第三列是指令對應的執行函數。數組
以create_app_data爲例markdown
frameworks/native/cmds/installd/installd.cpp
app
static int do_create_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
/* const char *uuid, const char *pkgname, userid_t userid, int flags, appid_t appid, const char* seinfo, int target_sdk_version */
return create_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]),
atoi(arg[4]), arg[5], atoi(arg[6]));
}
複製代碼
frameworks/native/cmds/installd/commands.cpp
socket
int create_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags, appid_t appid, const char* seinfo, int target_sdk_version) {
uid_t uid = multiuser_get_uid(userid, appid);
mode_t target_mode = target_sdk_version >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751;
if (flags & FLAG_STORAGE_CE) {
auto path = create_data_user_ce_package_path(uuid, userid, pkgname);
if (prepare_app_dir(path, target_mode, uid) ||
prepare_app_dir(path, "cache", 0771, uid) ||
prepare_app_dir(path, "code_cache", 0771, uid)) {
return -1;
}
// Consider restorecon over contents if label changed
if (restorecon_app_data_lazy(path, seinfo, uid) ||
restorecon_app_data_lazy(path, "cache", seinfo, uid) ||
restorecon_app_data_lazy(path, "code_cache", seinfo, uid)) {
return -1;
}
// Remember inode numbers of cache directories so that we can clear
// contents while CE storage is locked
if (write_path_inode(path, "cache", kXattrInodeCache) ||
write_path_inode(path, "code_cache", kXattrInodeCodeCache)) {
return -1;
}
}
if (flags & FLAG_STORAGE_DE) {
auto path = create_data_user_de_package_path(uuid, userid, pkgname);
if (prepare_app_dir(path, target_mode, uid)) {
// TODO: include result once 25796509 is fixed
return 0;
}
// Consider restorecon over contents if label changed
if (restorecon_app_data_lazy(path, seinfo, uid)) {
return -1;
}
if (property_get_bool("dalvik.vm.usejitprofiles")) {
const std::string profile_path = create_data_user_profile_package_path(userid, pkgname);
// read-write-execute only for the app user.
if (fs_prepare_dir_strict(profile_path.c_str(), 0700, uid, uid) != 0) {
PLOG(ERROR) << "Failed to prepare " << profile_path;
return -1;
}
std::string profile_file = create_primary_profile(profile_path);
// read-write only for the app user.
if (fs_prepare_file_strict(profile_file.c_str(), 0600, uid, uid) != 0) {
PLOG(ERROR) << "Failed to prepare " << profile_path;
return -1;
}
const std::string ref_profile_path = create_data_ref_profile_package_path(pkgname);
// dex2oat/profman runs under the shared app gid and it needs to read/write reference
// profiles.
appid_t shared_app_gid = multiuser_get_shared_app_gid(uid);
if (fs_prepare_dir_strict(
ref_profile_path.c_str(), 0700, shared_app_gid, shared_app_gid) != 0) {
PLOG(ERROR) << "Failed to prepare " << ref_profile_path;
return -1;
}
}
}
return 0;
}
複製代碼
建立cache和code_cache文件夾,並授予相應的權限。
installd在Framework中對應的是Installer服務。
frameworks/base/services/core/java/com/android/server/pm/Installer.java
public final class Installer extends SystemService 複製代碼
它是一個SystemService,在系統啓動的時候做爲核心服務在SystemServer中啓動。
private void startBootstrapServices() {
//啓動installer服務
Installer installer = mSystemServiceManager.startService(Installer.class);
......
}
複製代碼
Install中部分代碼
public Installer(Context context) {
super(context);
mInstaller = new InstallerConnection();
}
// Package-private installer that accepts a custom InstallerConnection. Used for
// OtaDexoptService.
Installer(Context context, InstallerConnection connection) {
super(context);
mInstaller = connection;
}
/** * Yell loudly if someone tries making future calls while holding a lock on * the given object. */
public void setWarnIfHeld(Object warnIfHeld) {
mInstaller.setWarnIfHeld(warnIfHeld);
}
@Override
public void onStart() {
Slog.i(TAG, "Waiting for installd to be ready.");
mInstaller.waitForConnection();
}
public void createAppData(String uuid, String pkgname, int userid, int flags, int appid, String seinfo, int targetSdkVersion) throws InstallerException {
mInstaller.execute("create_app_data", uuid, pkgname, userid, flags, appid, seinfo,
targetSdkVersion);
}
複製代碼
能夠看到,Installer在被建立的時候InstallerConnection也被建立了一個實例,createAppData實際調用也是InstallerConnection中的execute方法。因此Installer服務實際是使用InstallerConnection在作具體操做。
frameworks/base/core/java/com/android/internal/os/InstallerConnection.java
public class InstallerConnection {
public synchronized String transact(String cmd) {
if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) {
Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x"
+ Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable());
}
if (!connect()) {
Slog.e(TAG, "connection failed");
return "-1";
}
if (!writeCommand(cmd)) {
Slog.e(TAG, "write command failed? reconnect!");
if (!connect() || !writeCommand(cmd)) {
return "-1";
}
}
}
public String[] execute(String cmd, Object... args) throws InstallerException {
final String[] resRaw = transact(builder.toString()).split(" ");
int res = -1;
try {
res = Integer.parseInt(resRaw[0]);
} catch (ArrayIndexOutOfBoundsException | NumberFormatException ignored) {
}
return resRaw;
}
public void dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded, int dexFlags, String compilerFilter, String volumeUuid, String sharedLibraries) throws InstallerException {
dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded, null /*outputPath*/, dexFlags,
compilerFilter, volumeUuid, sharedLibraries);
}
public void dexopt(String apkPath, int uid, String pkgName, String instructionSet, int dexoptNeeded, String outputPath, int dexFlags, String compilerFilter, String volumeUuid, String sharedLibraries) throws InstallerException {
execute("dexopt",apkPath,uid,pkgName,instructionSet,dexoptNeeded,outputPath,dexFlags,compilerFilter,volumeUuid,sharedLibraries);
}
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;
}
public void disconnect() {
Slog.i(TAG, "disconnecting...");
IoUtils.closeQuietly(mSocket);
IoUtils.closeQuietly(mIn);
IoUtils.closeQuietly(mOut);
mSocket = null;
mIn = null;
mOut = null;
}
private boolean writeCommand(String cmdString) {
final byte[] cmd = cmdString.getBytes();
final int len = cmd.length;
if ((len < 1) || (len > buf.length)) {
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;
}
public void waitForConnection() {
for (;;) {
try {
execute("ping");
return;
} catch (InstallerException ignored) {
}
Slog.w(TAG, "installd not ready");
SystemClock.sleep(1000);
}
}
public static class InstallerException extends Exception {
public InstallerException(String detailMessage) {
super(detailMessage);
}
}
}
複製代碼
能夠看到在InstallerConnection中經過socket鏈接installd服務端,而後將Installer傳入的cmd經過socket再傳給installd處理。
Android已經有了PMS這麼複雜的一個服務了,爲何還須要再有一個installd守護進程?
答案其實很簡單,system_server以system用戶的身份運行,PMS運行在system_server中,因此PMS也是system用戶。installd是root用戶。
USER PID PPID VSIZE RSS WCHAN PC NAME
system 1698 699 2408960 149752 SyS_epoll_ 0000000000 S system_server
root 706 1 9972 2612 unix_strea 0000000000 S /system/bin/installd
複製代碼
system用戶並無訪問應用程序目錄的權限。可是做爲root用戶的installd卻能夠訪問data/data/下的目錄,這就是installd存在的緣由。