編輯:關於Android編程
void property_init(void) { init_property_area(); }
static int init_property_area(void) { if (property_area_inited) return -1; if(__system_property_area_init()) return -1; if(init_workspace(&pa_workspace, 0)) return -1; fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC); property_area_inited = 1; return 0; }
int __system_property_area_init() { return map_prop_area_rw(); }
具體property file的路徑如下:
cheny.le@cheny-desktop:~/kitkat2_git/bionic$ grep property_filename * -nr libc/bionic/system_properties.c:111:static char property_filename[PATH_MAX] = PROP_FILENAME; cheny.le@cheny-desktop:~/kitkat2_git/bionic$ grep PROP_FILENAME * -nr libc/include/sys/_system_properties.h:44:#define PROP_FILENAME "/dev/__properties__"
static int map_prop_area_rw() { prop_area *pa; int fd; int ret; /* dev is a tmpfs that we can use to carve a shared workspace * out of, so let's do that... */ fd = open(property_filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444); if (fd < 0) { if (errno == EACCES) { /* for consistency with the case where the process has already * mapped the page in and segfaults when trying to write to it */ abort(); } return -1; } ret = fcntl(fd, F_SETFD, FD_CLOEXEC); if (ret < 0) goto out; if (ftruncate(fd, PA_SIZE) < 0) goto out; pa_size = PA_SIZE; pa_data_size = pa_size - sizeof(prop_area); compat_mode = false; pa = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if(pa == MAP_FAILED) goto out; memset(pa, 0, pa_size); pa->magic = PROP_AREA_MAGIC; pa->version = PROP_AREA_VERSION; /* reserve root node */ pa->bytes_used = sizeof(prop_bt); /* plug into the lib property services */ __system_property_area__ = pa; close(fd); return 0; out: close(fd); return -1; }
cheny.le@cheny-desktop:~/kitkat2_git/bionic$ grep PROP_PATH_SYSTEM_BUILD * -nr libc/include/sys/_system_properties.h:82:#define PROP_PATH_SYSTEM_BUILD "/system/build.prop" cheny.le@cheny-desktop:~/kitkat2_git/bionic$ grep PROP_PATH_SYSTEM_DEFAULT * -nr libc/include/sys/_system_properties.h:83:#define PROP_PATH_SYSTEM_DEFAULT "/system/default.prop" cheny.le@cheny-desktop:~/kitkat2_git/bionic$ grep PROP_PATH_RAMDISK_DEFAULT * -nr libc/include/sys/_system_properties.h:81:#define PROP_PATH_RAMDISK_DEFAULT "/default.prop"
void property_load_boot_defaults(void) { load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT); }
static void load_properties_from_file(const char *fn) { char *data; unsigned sz; data = read_file(fn, &sz); if(data != 0) { load_properties(data); free(data); } }
void *read_file(const char *fn, unsigned *_sz) { char *data; int sz; int fd; struct stat sb; data = 0; fd = open(fn, O_RDONLY); if(fd < 0) return 0; // for security reasons, disallow world-writable // or group-writable files if (fstat(fd, &sb) < 0) { ERROR("fstat failed for '%s'\n", fn); goto oops; } if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) { ERROR("skipping insecure file '%s'\n", fn); goto oops; } sz = lseek(fd, 0, SEEK_END); if(sz < 0) goto oops; if(lseek(fd, 0, SEEK_SET) != 0) goto oops; data = (char*) malloc(sz + 2); if(data == 0) goto oops; if(read(fd, data, sz) != sz) goto oops; close(fd); data[sz] = '\n'; data[sz+1] = 0; if(_sz) *_sz = sz; return data; oops: close(fd); if(data != 0) free(data); return 0; }
static void load_properties(char *data) { char *key, *value, *eol, *sol, *tmp; sol = data; while((eol = strchr(sol, '\n'))) { key = sol; *eol++ = 0; sol = eol; value = strchr(key, '='); if(value == 0) continue; *value++ = 0; while(isspace(*key)) key++; if(*key == '#') continue; tmp = value - 2; while((tmp > key) && isspace(*tmp)) *tmp-- = 0; while(isspace(*value)) value++; tmp = eol - 2; while((tmp > value) && isspace(*tmp)) *tmp-- = 0; property_set(key, value); } }
cheny.le@cheny-desktop:~/kitkat2_git/bionic$ grep PROP_SERVICE_NAME * -nr libc/include/sys/_system_properties.h:43:#define PROP_SERVICE_NAME "property_service"
void start_property_service(void) { int fd; load_properties_from_file(PROP_PATH_SYSTEM_BUILD); load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT); load_override_properties(); /* Read persistent properties after all default values have been loaded. */ load_persistent_properties(); fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0); if(fd < 0) return; fcntl(fd, F_SETFD, FD_CLOEXEC); fcntl(fd, F_SETFL, O_NONBLOCK); listen(fd, 8); property_set_fd = fd; }
cheny.le@cheny-desktop:~/kitkat2_git/system/core$ grep ANDROID_SOCKET_DIR * -nr include/cutils/sockets.h:33:#define ANDROID_SOCKET_DIR "/dev/socket"這也就是說會建立一個/dev/socket/property_service的socket,然後listen這個socket
int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid) { struct sockaddr_un addr; int fd, ret; char *secon; fd = socket(PF_UNIX, type, 0); if (fd < 0) { ERROR("Failed to open socket '%s': %s\n", name, strerror(errno)); return -1; } memset(&addr, 0 , sizeof(addr)); addr.sun_family = AF_UNIX; snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s", name); ret = unlink(addr.sun_path); if (ret != 0 && errno != ENOENT) { ERROR("Failed to unlink old socket '%s': %s\n", name, strerror(errno)); goto out_close; } secon = NULL; if (sehandle) { ret = selabel_lookup(sehandle, &secon, addr.sun_path, S_IFSOCK); if (ret == 0) setfscreatecon(secon); } ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr)); if (ret) { ERROR("Failed to bind socket '%s': %s\n", name, strerror(errno)); goto out_unlink; } setfscreatecon(NULL); freecon(secon); chown(addr.sun_path, uid, gid); chmod(addr.sun_path, perm); INFO("Created socket '%s' with mode '%o', user '%d', group '%d'\n", addr.sun_path, perm, uid, gid); return fd; out_unlink: unlink(addr.sun_path); out_close: close(fd); return -1; }
if (!property_set_fd_init && get_property_set_fd() > 0) { ufds[fd_count].fd = get_property_set_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; fd_count++; property_set_fd_init = 1; } if (ufds[i].revents == POLLIN) { if (ufds[i].fd == get_property_set_fd()) handle_property_set_fd();
void handle_property_set_fd() { prop_msg msg; int s; int r; int res; struct ucred cr; struct sockaddr_un addr; socklen_t addr_size = sizeof(addr); socklen_t cr_size = sizeof(cr); char * source_ctx = NULL; if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) { return; } /* Check socket options here */ if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) { close(s); ERROR("Unable to receive socket options\n"); return; } r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), 0)); if(r != sizeof(prop_msg)) { ERROR("sys_prop: mis-match msg size received: %d expected: %d errno: %d\n", r, sizeof(prop_msg), errno); close(s); return; } switch(msg.cmd) { case PROP_MSG_SETPROP: msg.name[PROP_NAME_MAX-1] = 0; msg.value[PROP_VALUE_MAX-1] = 0; if (!is_legal_property_name(msg.name, strlen(msg.name))) { ERROR("sys_prop: illegal property name. Got: \"%s\"\n", msg.name); close(s); return; } getpeercon(s, &source_ctx); if(memcmp(msg.name,"ctl.",4) == 0) { // Keep the old close-socket-early behavior when handling // ctl.* properties. close(s); if (check_control_perms(msg.value, cr.uid, cr.gid, source_ctx)) { handle_control_message((char*) msg.name + 4, (char*) msg.value); } else { ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n", msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid); } } else { if (check_perms(msg.name, cr.uid, cr.gid, source_ctx)) { property_set((char*) msg.name, (char*) msg.value); } else { ERROR("sys_prop: permission denied uid:%d name:%s\n", cr.uid, msg.name); } // Note: bionic's property client code assumes that the // property server will not close the socket until *AFTER* // the property is written to memory. close(s); } freecon(source_ctx); break; default: close(s); break; } }
static bool is_legal_property_name(const char* name, size_t namelen) { size_t i; bool previous_was_dot = false; if (namelen >= PROP_NAME_MAX) return false; if (namelen < 1) return false; if (name[0] == '.') return false; if (name[namelen - 1] == '.') return false; /* Only allow alphanumeric, plus '.', '-', or '_' */ /* Don't allow ".." to appear in a property name */ for (i = 0; i < namelen; i++) { if (name[i] == '.') { if (previous_was_dot == true) return false; previous_was_dot = true; continue; } previous_was_dot = false; if (name[i] == '_' || name[i] == '-') continue; if (name[i] >= 'a' && name[i] <= 'z') continue; if (name[i] >= 'A' && name[i] <= 'Z') continue; if (name[i] >= '0' && name[i] <= '9') continue; return false; } return true; }
static int check_control_perms(const char *name, unsigned int uid, unsigned int gid, char *sctx) {
int i; if (uid == AID_SYSTEM || uid == AID_ROOT) return check_control_mac_perms(name, sctx); /* Search the ACL */ for (i = 0; control_perms[i].service; i++) { if (strcmp(control_perms[i].service, name) == 0) { if ((uid && control_perms[i].uid == uid) || (gid && control_perms[i].gid == gid)) { return check_control_mac_perms(name, sctx); } } } return 0; }
static int check_control_mac_perms(const char *name, char *sctx) { /* * Create a name prefix out of ctl.* The new prefix allows the use of the existing * property service backend labeling while avoiding * mislabels based on true property prefixes. */ char ctl_name[PROP_VALUE_MAX+4]; int ret = snprintf(ctl_name, sizeof(ctl_name), "ctl.%s", name); if (ret < 0 || (size_t) ret >= sizeof(ctl_name)) return 0; return check_mac_perms(ctl_name, sctx); }
static int check_mac_perms(const char *name, char *sctx) { if (is_selinux_enabled() <= 0) return 1; char *tctx = NULL; const char *class = "property_service"; const char *perm = "set"; int result = 0; if (!sctx) goto err; if (!sehandle_prop) goto err; if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0) goto err; if (selinux_check_access(sctx, tctx, class, perm, name) == 0) result = 1; freecon(tctx); err: return result; }
static int check_perms(const char *name, unsigned int uid, unsigned int gid, char *sctx) { int i; unsigned int app_id; if(!strncmp(name, "ro.", 3)) name +=3; if (uid == 0) return check_mac_perms(name, sctx); app_id = multiuser_get_app_id(uid); if (app_id == AID_BLUETOOTH) { uid = app_id; } for (i = 0; property_perms[i].prefix; i++) { if (strncmp(property_perms[i].prefix, name, strlen(property_perms[i].prefix)) == 0) { if ((uid && property_perms[i].uid == uid) || (gid && property_perms[i].gid == gid)) { return check_mac_perms(name, sctx); } } } return 0; }
void handle_control_message(const char *msg, const char *arg) { if (!strcmp(msg,"start")) { msg_start(arg); } else if (!strcmp(msg,"stop")) { msg_stop(arg); } else if (!strcmp(msg,"restart")) { msg_restart(arg); } else { ERROR("unknown control msg '%s'\n", msg); } }
static void msg_start(const char *name) { struct service *svc = NULL; char *tmp = NULL; char *args = NULL; if (!strchr(name, ':')) svc = service_find_by_name(name); else { tmp = strdup(name); if (tmp) { args = strchr(tmp, ':'); *args = '\0'; args++; svc = service_find_by_name(tmp); } } if (svc) { service_start(svc, args); } else { ERROR("no such service '%s'\n", name); } if (tmp) free(tmp); }
void service_start(struct service *svc, const char *dynamic_args) { struct stat s; pid_t pid; int needs_console; int n; char *scon = NULL; int rc; /* starting a service removes it from the disabled or reset * state and immediately takes it out of the restarting * state if it was in there */ svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART)); svc->time_started = 0; /* running processes require no additional work -- if * they're in the process of exiting, we've ensured * that they will immediately restart on exit, unless * they are ONESHOT */ if (svc->flags & SVC_RUNNING) { return; } needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0; if (needs_console && (!have_console)) { ERROR("service '%s' requires console\n", svc->name); svc->flags |= SVC_DISABLED; return; } if (stat(svc->args[0], &s) != 0) { ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name); svc->flags |= SVC_DISABLED; return; } if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) { ERROR("service '%s' must be one-shot to use dynamic args, disabling\n", svc->args[0]); svc->flags |= SVC_DISABLED; return; } if (is_selinux_enabled() > 0) { if (svc->seclabel) { scon = strdup(svc->seclabel); if (!scon) { ERROR("Out of memory while starting '%s'\n", svc->name); return; } } else { char *mycon = NULL, *fcon = NULL; INFO("computing context for service '%s'\n", svc->args[0]); rc = getcon(&mycon); if (rc < 0) { ERROR("could not get context while starting '%s'\n", svc->name); return; } rc = getfilecon(svc->args[0], &fcon); if (rc < 0) { ERROR("could not get context while starting '%s'\n", svc->name); freecon(mycon); return; } rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon); freecon(mycon); freecon(fcon); if (rc < 0) { ERROR("could not get context while starting '%s'\n", svc->name); return; } } } NOTICE("starting '%s'\n", svc->name); pid = fork(); if (pid == 0) { struct socketinfo *si; struct svcenvinfo *ei; char tmp[32]; int fd, sz; umask(077); if (properties_inited()) { get_property_workspace(&fd, &sz); sprintf(tmp, "%d,%d", dup(fd), sz); add_environment("ANDROID_PROPERTY_WORKSPACE", tmp); } for (ei = svc->envvars; ei; ei = ei->next) add_environment(ei->name, ei->value); setsockcreatecon(scon); 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); if (s >= 0) { publish_socket(si->name, s); } } freecon(scon); scon = NULL; setsockcreatecon(NULL); if (svc->ioprio_class != IoSchedClass_NONE) { if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) { ERROR("Failed to set pid %d ioprio = %d,%d: %s\n", getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno)); } } if (needs_console) { setsid(); open_console(); } else { zap_stdio(); } #if 0 for (n = 0; svc->args[n]; n++) { INFO("args[%d] = '%s'\n", n, svc->args[n]); } for (n = 0; ENV[n]; n++) { INFO("env[%d] = '%s'\n", n, ENV[n]); } #endif setpgid(0, getpid()); /* as requested, set our gid, supplemental gids, and uid */ if (svc->gid) { if (setgid(svc->gid) != 0) { ERROR("setgid failed: %s\n", strerror(errno)); _exit(127); } } if (svc->nr_supp_gids) { if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) { ERROR("setgroups failed: %s\n", strerror(errno)); _exit(127); } } if (svc->uid) { if (setuid(svc->uid) != 0) { ERROR("setuid failed: %s\n", strerror(errno)); _exit(127); } } if (svc->seclabel) { if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) { ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno)); _exit(127); } } if (!dynamic_args) { if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) { ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno)); } } else { char *arg_ptrs[INIT_PARSER_MAXARGS+1]; int arg_idx = svc->nargs; char *tmp = strdup(dynamic_args); char *next = tmp; char *bword; /* Copy the static arguments */ memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *))); while((bword = strsep(&next, " "))) { arg_ptrs[arg_idx++] = bword; if (arg_idx == INIT_PARSER_MAXARGS) break; } arg_ptrs[arg_idx] = '\0'; execve(svc->args[0], (char**) arg_ptrs, (char**) ENV); } _exit(127); } freecon(scon); if (pid < 0) { ERROR("failed to start '%s'\n", svc->name); svc->pid = 0; return; } svc->time_started = gettime(); svc->pid = pid; svc->flags |= SVC_RUNNING; if (properties_inited()) notify_service_state(svc->name, "running"); }
void notify_service_state(const char *name, const char *state) { char pname[PROP_NAME_MAX]; int len = strlen(name); if ((len + 10) > PROP_NAME_MAX) return; snprintf(pname, sizeof(pname), "init.svc.%s", name); property_set(pname, state); }
方法二:使用__system_property_find+__system_property_update(__system_property_add)的組合方法把具體的property設置到具體的mmap所對應的共享內存中,這個才是最終的方法
int property_set(const char *key, const char *value) { return __system_property_set(key, value); }
int __system_property_set(const char *key, const char *value) { int err; prop_msg msg; if(key == 0) return -1; if(value == 0) value = ""; if(strlen(key) >= PROP_NAME_MAX) return -1; if(strlen(value) >= PROP_VALUE_MAX) return -1; memset(&msg, 0, sizeof msg); msg.cmd = PROP_MSG_SETPROP; strlcpy(msg.name, key, sizeof msg.name); strlcpy(msg.value, value, sizeof msg.value); err = send_prop_msg(&msg); if(err < 0) { return err; } return 0; }
cheny.le@cheny-desktop:~/kitkat2_git/bionic$ grep property_service_socket * -nr libc/bionic/system_properties.c:110:static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME; cheny.le@cheny-desktop:~/kitkat2_git/bionic$ grep PROP_SERVICE_NAME * -nr libc/include/sys/_system_properties.h:43:#define PROP_SERVICE_NAME "property_service"
static int send_prop_msg(prop_msg *msg) { struct pollfd pollfds[1]; struct sockaddr_un addr; socklen_t alen; size_t namelen; int s; int r; int result = -1; s = socket(AF_LOCAL, SOCK_STREAM, 0); if(s < 0) { return result; } memset(&addr, 0, sizeof(addr)); namelen = strlen(property_service_socket); strlcpy(addr.sun_path, property_service_socket, sizeof addr.sun_path); addr.sun_family = AF_LOCAL; alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1; if(TEMP_FAILURE_RETRY(connect(s, (struct sockaddr *) &addr, alen)) < 0) { close(s); return result; } if(r == sizeof(prop_msg)) { // We successfully wrote to the property server but now we // wait for the property server to finish its work. It // acknowledges its completion by closing the socket so we // poll here (on nothing), waiting for the socket to close. // If you 'adb shell setprop foo bar' you'll see the POLLHUP // once the socket closes. Out of paranoia we cap our poll // at 250 ms. pollfds[0].fd = s; pollfds[0].events = 0; r = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 /* ms */)); if (r == 1 && (pollfds[0].revents & POLLHUP) != 0) { result = 0; } else { // Ignore the timeout and treat it like a success anyway. // The init process is single-threaded and its property // service is sometimes slow to respond (perhaps it's off // starting a child process or something) and thus this // times out and the caller thinks it failed, even though // it's still getting around to it. So we fake it here, // mostly for ctl.* properties, but we do try and wait 250 // ms so callers who do read-after-write can reliably see // what they've written. Most of the time. // TODO: fix the system properties design. result = 0; } } close(s); return result; }
int property_set(const char *name, const char *value) { prop_info *pi; int ret; size_t namelen = strlen(name); size_t valuelen = strlen(value); if (!is_legal_property_name(name, namelen)) return -1; if (valuelen >= PROP_VALUE_MAX) return -1; pi = (prop_info*) __system_property_find(name); if(pi != 0) { /* ro.* properties may NEVER be modified once set */ if(!strncmp(name, "ro.", 3)) return -1; __system_property_update(pi, value, valuelen); } else { ret = __system_property_add(name, namelen, value, valuelen); if (ret < 0) { ERROR("Failed to set '%s'='%s'\n", name, value); return ret; } } /* If name starts with "net." treat as a DNS property. */ if (strncmp("net.", name, strlen("net.")) == 0) { if (strcmp("net.change", name) == 0) { return 0; } /* * The 'net.change' property is a special property used track when any * 'net.*' property name is updated. It is _ONLY_ updated here. Its value * contains the last updated 'net.*' property. */ property_set("net.change", name); } else if (persistent_properties_loaded && strncmp("persist.", name, strlen("persist.")) == 0) { /* * Don't write properties to disk until after we have read all default properties * to prevent them from being overwritten by default values. */ write_persistent_property(name, value); } else if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) { selinux_reload_policy(); } property_changed(name, value); return 0; }
const prop_info *__system_property_find(const char *name) { if (__predict_false(compat_mode)) { return __system_property_find_compat(name); } return find_property(root_node(), name, strlen(name), NULL, 0, false); }
static const prop_info *find_property(prop_bt *trie, const char *name, uint8_t namelen, const char *value, uint8_t valuelen, bool alloc_if_needed) { const char *remaining_name = name; while (true) { char *sep = strchr(remaining_name, '.'); bool want_subtree = (sep != NULL); uint8_t substr_size; prop_bt *root; if (want_subtree) { substr_size = sep - remaining_name; } else { substr_size = strlen(remaining_name); } if (!substr_size) return NULL; if (trie->children) { root = to_prop_obj(trie->children); } else if (alloc_if_needed) { root = new_prop_bt(remaining_name, substr_size, &trie->children); } else { root = NULL; } if (!root) return NULL; trie = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed); if (!trie) return NULL; if (!want_subtree) break; remaining_name = sep + 1; } if (trie->prop) { return to_prop_obj(trie->prop); } else if (alloc_if_needed) { return new_prop_info(name, namelen, value, valuelen, &trie->prop); } else { return NULL; } }
static prop_bt *find_prop_bt(prop_bt *bt, const char *name, uint8_t namelen, bool alloc_if_needed) { while (true) { int ret; if (!bt) return bt; ret = cmp_prop_name(name, namelen, bt->name, bt->namelen); if (ret == 0) { return bt; } else if (ret < 0) { if (bt->left) { bt = to_prop_obj(bt->left); } else { if (!alloc_if_needed) return NULL; bt = new_prop_bt(name, namelen, &bt->left); } } else { if (bt->right) { bt = to_prop_obj(bt->right); } else { if (!alloc_if_needed) return NULL; bt = new_prop_bt(name, namelen, &bt->right); } } } }
static int cmp_prop_name(const char *one, uint8_t one_len, const char *two, uint8_t two_len) { if (one_len < two_len) return -1; else if (one_len > two_len) return 1; else return strncmp(one, two, one_len); }
static void *to_prop_obj(prop_off_t off) { if (off > pa_data_size) return NULL; return __system_property_area__->data + off; }
static void *new_prop_obj(size_t size, prop_off_t *off) { prop_area *pa = __system_property_area__; size = ALIGN(size, sizeof(uint32_t)); if (pa->bytes_used + size > pa_data_size) return NULL; *off = pa->bytes_used; __system_property_area__->bytes_used += size; return __system_property_area__->data + *off; }
static prop_bt *new_prop_bt(const char *name, uint8_t namelen, prop_off_t *off) { prop_off_t off_tmp; prop_bt *bt = new_prop_obj(sizeof(prop_bt) + namelen + 1, &off_tmp); if (bt) { memcpy(bt->name, name, namelen); bt->name[namelen] = '\0'; bt->namelen = namelen; ANDROID_MEMBAR_FULL(); *off = off_tmp; } return bt; }
int __system_property_update(prop_info *pi, const char *value, unsigned int len) { prop_area *pa = __system_property_area__; if (len >= PROP_VALUE_MAX) return -1; pi->serial = pi->serial | 1; ANDROID_MEMBAR_FULL(); memcpy(pi->value, value, len + 1); ANDROID_MEMBAR_FULL(); pi->serial = (len << 24) | ((pi->serial + 1) & 0xffffff); __futex_wake(&pi->serial, INT32_MAX); pa->serial++; __futex_wake(&pa->serial, INT32_MAX); return 0; }這裡我們可以看到memcpy(pi->value, value, len + 1);,這就是把用戶的value設置到了prop info這個節點裡面了
int __system_property_add(const char *name, unsigned int namelen, const char *value, unsigned int valuelen) { prop_area *pa = __system_property_area__; const prop_info *pi; if (namelen >= PROP_NAME_MAX) return -1; if (valuelen >= PROP_VALUE_MAX) return -1; if (namelen < 1) return -1; pi = find_property(root_node(), name, namelen, value, valuelen, true); if (!pi) return -1; pa->serial++; __futex_wake(&pa->serial, INT32_MAX); return 0; }
int __system_property_get(const char *name, char *value) { const prop_info *pi = __system_property_find(name); if(pi != 0) { return __system_property_read(pi, 0, value); } else { value[0] = 0; return 0; } }
int __system_property_read(const prop_info *pi, char *name, char *value) { unsigned serial, len; if (__predict_false(compat_mode)) { return __system_property_read_compat(pi, name, value); } for(;;) { serial = pi->serial; while(SERIAL_DIRTY(serial)) { __futex_wait((volatile void *)&pi->serial, serial, 0); serial = pi->serial; } len = SERIAL_VALUE_LEN(serial); memcpy(value, pi->value, len + 1); ANDROID_MEMBAR_FULL(); if(serial == pi->serial) { if(name != 0) { strcpy(name, pi->name); } return len; } } }
前言1、什麼是NDK?NDK全稱是Native Development Kit,NDK提供了一系列的工具,幫助開發者快速開發C(或C++)的動態庫,並能自動將so和jav
一、前言介紹 直奔主題啦,很多Android app都有菜單欄,菜單欄除了背景圖片、圖標的不同外,布局基本一致。大致可以分為三部分:菜單欄的左側區域、菜單
0x00閱讀本文前,建議讀者首先閱讀Android加殼原理,參考文章Android中的Apk的加固(加殼)原理解析和實現。如果沒有看過這篇文章,本文理解起來比較困難。0x
在網上搜怎麼加載Reveal的,好多都是利用Reveal.frameWork來拖進工程裡面,這樣很不方便,每次更換新工程都要這樣做,這裡給大家講解一個利用配置一個全局斷點