1. Attribute Service
In Windows, there is something like a registry that can store key-value pairs such as Key/value.
In general, the system or some applications will store some of their own properties in the registry, even if the system restarts or the application restarts, it can be based on the previous properties set in the registry, the corresponding initialization operation. The Android platform also provides a similar mechanism, called the property service.
2. Attribute Service Initialization 2.1 code information
The author's code is located at this location: system/core/init/property_service.c
Code information in INIT.C:
static int property_init_action (int nargs, char **args) { INFO ("Property init\n"); Property_init (); return 0;}
2.1 Creating storage space
See property_service.c first.
void Property_init (void) {//Initialize attribute storage area Init_property_area ();//load Default.prop file load_properties_from_file ( Prop_path_ramdisk_default);}
Detailed code information:
static int Init_property_area (void) { Prop_area *pa; The IF (pa_info_array) return-1;/*init_workspace function calls the Ashmem_create_region function provided by the Android system to create a piece of shared memory. * /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__ This variable has bionic libc library output */ __system_property_area__ = PA; property_area_inited = 1; return 0;}
Two main functions are completed:
1) The attribute area is created on shared memory and shared memory can be cross-process.
2) Android takes advantage of the GCC constructor attribute, which indicates a _libc_prenit function.
2.2 Client gets storage space
Code INFORMATION at: BIONIC/LIBC/BIONIC/LIBC_INIT_DYNAMIC.C
The constructor property instructs the loader to first call the __libc_preinit function after the library is loaded. This is similar to the DllMain function of the dynamic library on Windows, like void __attribute__ ((constructor)) __libc_preinit (void); void __libc_preinit (void) { ...//call this function __libc_init_common (elfdata); ......}
void __libc_init_common (uintptr_t *elfdata) { ...// Initialize the client's property store area/ * Setup System Properties-requires Environment * /__system_properties_init ();}
3. Property Server 3.1 Startup property server
The init process initiates a property server, and the client can only set properties by interacting with the property server. Look at the contents of the property server first, it has Start_property_service
Source code Name: property_service.c
void Start_property_service (void) { int fd;//loads a property file, which is actually parsing the attributes in those files and then setting it to the attribute space load_properties_from_ File (prop_path_system_build); Load_properties_from_file (Prop_path_system_default); Load_properties_from_file (prop_path_local_override); /* Some property books need to be saved on permanent media, these properties files are loaded by this function * /load_persistent_properties ();/* Create a socket for IPC communication */ FD = Create_ Sockets (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;}
The property service creates a socket to receive the request, which is processed in the For loop in Init.
3.2 Processing Set Property requests
Where the request is received in the INIT process, the code looks like this:
for (i = 0; i < Fd_count; i++) { if (ufds[i].revents = = Pollin) { if (ufds[i].fd = = GET_PROPERTY_SET_FD ()) HANDLE_PROPERTY_SET_FD (); else if (ufds[i].fd = = GET_KEYCHORD_FD ()) Handle_keychord (); else if (ufds[i].fd = = GET_SIGNAL_FD ()) handle_signal ();} }
When the property server receives a client request, Init calls HANDLE_PROPERTY_SET_FD for processing.
The function code is as follows:
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);//Receive TCP connection if (s = Accept (PROPERTY_SET_FD, struct sockaddr *) &addr, &addr_si Ze)) < 0) {return; }/* Remove the client process permissions and other properties */if (getsockopt (S, Sol_socket, so_peercred, &CR, &cr_size) < 0) {close (s); ERROR ("Unable to recieve socket options\n"); Return }/* Receive Request data */R = recv (s, &msg, sizeof (msg), 0); Close (s); if (r = sizeof (prop_msg)) {ERROR ("Sys_prop:mis-match msg size recieved:%d expected:%d\n", R, Sizeo F (prop_msg)); Return } switch (msg.cmd) {case Prop_msg_setprop:msg.name[prop_name_max-1] = 0; MSG.VALUE[PROP_VALUE_MAX-1] = 0;/* If the CTL starts with a message, then the control message is used to execute some commands, for example, after logging in with the ADB shell, enter some commands to look at the boot animation. */if (memcmp (Msg.name, "ctl.", 4) = = 0) {if (Check_control_perms (Msg.value, Cr.uid, Cr.gid)) {handle_control_message (char*) Msg.name + 4, (char*) msg.value); } else {ERROR ("sys_prop:unable to%s service ctl [%s] uid:%d pid:%d\n", Msg.name + 4, Msg.value, Cr.uid, cr.pid); }} else {/* Check whether the client process is provided with sufficient permissions */if (check_perms (Msg.name, Cr.uid, Cr.gid)) {/* Then call property_set settings */ Property_set ((char*) Msg.name, (char*) msg.value); } else {ERROR ("Sys_prop:permission denied uid:%d name:%s\n", Cr.uid, Msg.name); }} break; Default:break; }}
When the client's permissions meet the requirements, Init calls Property_set to make the relevant settings:
int Property_set (const char *name, const char *value) {Prop_area *pa; Prop_info *pi; int namelen = strlen (name); int valuelen = strlen (value); ...//Find out if the property already exists in the property store. Pi = (prop_info*) __system_property_find (name); The IF (pi! = 0) {/* Ro.* property represents a set once and cannot be changed, read only, and return directly to/* ro.* properties may never be modified once set */if ( !STRNCMP (Name, "Ro.", 3)) return-1; PA = __system_property_area__;/* Update the value of this property */Update_prop_info (pi, value, Valuelen); pa->serial++; __futex_wake (&pa->serial, Int32_max); } else {/* If the corresponding attribute is not found, the attribute is considered to be added, so a new item is created. Android supports up to 247 properties and returns */PA = __system_property_area__ If there are 247 entries in the current attribute space; if (Pa->count = = Pa_count_max) return-1; Pi = Pa_info_array + pa->count; Pi->serial = (Valuelen << 24); memcpy (pi->name, name, Namelen + 1); memcpy (Pi->value, value, Valuelen + 1); Pa->toc[pa->count] = (Namelen << 24) | (((unsigned) pi)-((unsigned) PA)); pa->count++; pa->serial++; __futex_wake (&pa->serial, Int32_max); }//has some special properties that require special handling, where the main net starts with properties */* 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 was a special property used track if any * ' net.* ' property name is Updated. It's _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 had read all default properties * To prevent them fro m being overwritten by default values. *///If the property name starts with persistent, you need to write the values to the appropriate file WRITE_PERSISTENT_PROperty (name, value); } property_changed (name, value); return 0;}
3.3 Client Send Request
The client sends the request through Property_set, Property_set is provided by the Libcutils library, with the following code:
int Property_set (const char *key, const char *value) { prop_msg msg; Unsigned resp; 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; Set the message code to prop_msg_setprop msg.cmd = prop_msg_setprop; strcpy ((char*) msg.name, key); strcpy ((char*) msg.value, value);//Send Request return send_prop_msg (&msg);} static int send_prop_msg (Prop_msg *msg) { int s; int R; Establish and attribute the socket connection of the server s = socket_local_client (Prop_service_name, android_socket_namespace_reserved, sock_stream); if (S < 0) return-1; Send out through the socket while ((R = Send (S, msg, sizeof (PROP_MSG), 0)) < 0) { if (errno = = eintr) | | (errno = = Eagain)) Continue; break; } if (r = = sizeof (prop_msg)) { r = 0; } else { r =-1; } Close (s); return r;}
Deep understanding of Android (04)--in-depth understanding of property services