diff --git a/Android.mk b/Android.mk index 44ea560be..8b79cebad 100644 --- a/Android.mk +++ b/Android.mk @@ -19,8 +19,9 @@ ifneq ($(TARGET_SIMULATOR),true) include $(call first-makefiles-under,$(LOCAL_PATH)) else include $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \ + adb \ libcutils \ - liblog \ + liblog \ libnetutils \ libpixelflinger \ libzipfile \ diff --git a/adb/Android.mk b/adb/Android.mk index 8ac5eb437..c06485fbb 100644 --- a/adb/Android.mk +++ b/adb/Android.mk @@ -7,6 +7,7 @@ LOCAL_PATH:= $(call my-dir) # adb host tool # ========================================================= +ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean (also unused with the sim) include $(CLEAR_VARS) # Default to a virtual (sockets) usb interface @@ -29,7 +30,7 @@ ifeq ($(HOST_OS),windows) USB_SRCS := usb_windows.c EXTRA_SRCS := get_my_path_windows.c EXTRA_STATIC_LIBS := AdbWinApi - LOCAL_C_INCLUDES += /usr/include/w32api/ddk $(LOCAL_PATH)/../windows/usb/api + LOCAL_C_INCLUDES += /usr/include/w32api/ddk development/host/windows/usb/api/ ifneq ($(strip $(USE_CYGWIN)),) LOCAL_LDLIBS += -lpthread else @@ -51,7 +52,8 @@ LOCAL_SRC_FILES := \ file_sync_client.c \ $(EXTRA_SRCS) \ $(USB_SRCS) \ - shlist.c + shlist.c \ + utils.c \ ifneq ($(USE_SYSDEPS_WIN32),) @@ -69,6 +71,8 @@ endif include $(BUILD_HOST_EXECUTABLE) +$(call dist-for-goals,droid,$(LOCAL_BUILT_MODULE)) + ifeq ($(HOST_OS),windows) $(LOCAL_INSTALLED_MODULE): $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll endif @@ -81,10 +85,25 @@ LOCAL_MODULE := kdbg include $(BUILD_HOST_EXECUTABLE) endif +endif # adbd device daemon # ========================================================= -ifeq ($(TARGET_ARCH),arm) + +# build adbd in all non-simulator builds +BUILD_ADBD := false +ifneq ($(TARGET_SIMULATOR),true) + BUILD_ADBD := true +endif + +# build adbd for the Linux simulator build +# so we can use it to test the adb USB gadget driver on x86 +ifeq ($(HOST_OS),linux) + BUILD_ADBD := true +endif + + +ifeq ($(BUILD_ADBD),true) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ @@ -99,18 +118,31 @@ LOCAL_SRC_FILES := \ framebuffer_service.c \ remount_service.c \ usb_linux_client.c \ - log_service.c + log_service.c \ + utils.c \ - -LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -DANDROID_GADGET=1 -Wall -Wno-unused-parameter +LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE + +# TODO: This should probably be board specific, whether or not the kernel has +# the gadget driver; rather than relying on the architecture type. +ifeq ($(TARGET_ARCH),arm) +LOCAL_CFLAGS += -DANDROID_GADGET=1 +endif + LOCAL_MODULE := adbd LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN) LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED) -LOCAL_STATIC_LIBRARIES := libcutils libc - -include $(BUILD_EXECUTABLE) +ifeq ($(TARGET_SIMULATOR),true) + LOCAL_STATIC_LIBRARIES := libcutils + LOCAL_LDLIBS += -lpthread + include $(BUILD_HOST_EXECUTABLE) +else + LOCAL_STATIC_LIBRARIES := libcutils libc + include $(BUILD_EXECUTABLE) +endif + endif diff --git a/adb/OVERVIEW.TXT b/adb/OVERVIEW.TXT new file mode 100644 index 000000000..6a5191aee --- /dev/null +++ b/adb/OVERVIEW.TXT @@ -0,0 +1,139 @@ +Implementation notes regarding ADB. + +I. General Overview: + +The Android Debug Bridge (ADB) is used to: + +- keep track of all Android devices and emulators instances + connected to or running on a given host developer machine + +- implement various control commands (e.g. "adb shell", "adb pull", etc..) + for the benefit of clients (command-line users, or helper programs like + DDMS). These commands are what is called a 'service' in ADB. + +As a whole, everything works through the following components: + + 1. The ADB server + + This is a background process that runs on the host machine. Its purpose + if to sense the USB ports to know when devices are attached/removed, + as well as when emulator instances start/stop. + + It thus maintains a list of "connected devices" and assigns a 'state' + to each one of them: OFFLINE, BOOTLOADER, RECOVERY or ONLINE (more on + this below). + + The ADB server is really one giant multiplexing loop whose purpose is + to orchestrate the exchange of data (packets, really) between clients, + services and devices. + + + 2. The ADB daemon (adbd) + + The 'adbd' program runs as a background process within an Android device + or emulated system. Its purpose is to connect to the ADB server + (through USB for devices, through TCP for emulators) and provide a + few services for clients that run on the host. + + The ADB server considers that a device is ONLINE when it has succesfully + connected to the adbd program within it. Otherwise, the device is OFFLINE, + meaning that the ADB server detected a new device/emulator, but could not + connect to the adbd daemon. + + the BOOTLOADER and RECOVERY states correspond to alternate states of + devices when they are in the bootloader or recovery mode. + + 3. The ADB command-line client + + The 'adb' command-line program is used to run adb commands from a shell + or a script. It first tries to locate the ADB server on the host machine, + and will start one automatically if none is found. + + then, the client sends its service requests to the ADB server. It doesn't + need to know. + + Currently, a single 'adb' binary is used for both the server and client. + this makes distribution and starting the server easier. + + + 4. Services + + There are essentially two kinds of services that a client can talk to. + + Host Services: + these services run within the ADB Server and thus do not need to + communicate with a device at all. A typical example is "adb devices" + which is used to return the list of currently known devices and their + state. They are a few couple other services though. + + Local Services: + these services either run within the adbd daemon, or are started by + it on the device. The ADB server is used to multiplex streams + between the client and the service running in adbd. In this case + its role is to initiate the connection, then of being a pass-through + for the data. + + +II. Protocol details: + + 1. Client <-> Server protocol: + + This details the protocol used between ADB clients and the ADB + server itself. The ADB server listens on TCP:localhost:5037. + + A client sends a request using the following format: + + 1. A 4-byte hexadecimal string giving the length of the payload + 2. Followed by the payload itself. + + For example, to query the ADB server for its internal version number, + the client will do the following: + + 1. Connect to tcp:localhost:5037 + 2. Send the string "000Chost:version" to the corresponding socket + + The 'host:' prefix is used to indicate that the request is addressed + to the server itself (we will talk about other kinds of requests later). + The content length is encoded in ASCII for easier debugging. + + The server should answer a request with one of the following: + + 1. For success, the 4-byte "OKAY" string + + 2. For failure, the 4-byte "FAIL" string, followed by a + 4-byte hex length, followed by a string giving the reason + for failure. + + 3. As a special exception, for 'host:version', a 4-byte + hex string corresponding to the server's internal version number + + Note that the connection is still alive after an OKAY, which allows the + client to make other requests. But in certain cases, an OKAY will even + change the state of the connection. + + For example, the case of the 'host:transport:' request, + where '' is used to identify a given device/emulator; after + the "OKAY" answer, all further requests made by the client will go + directly to the corresponding adbd daemon. + + The file SERVICES.TXT lists all services currently implemented by ADB. + + + 2. Transports: + + An ADB transport models a connection between the ADB server and one device + or emulator. There are currently two kinds of transports: + + - USB transports, for physical devices through USB + + - Local transports, for emulators running on the host, connected to + the server through TCP + + In theory, it should be possible to write a local transport that proxies + a connection between an ADB server and a device/emulator connected to/ + running on another machine. This hasn't been done yet though. + + Each transport can carry one or more multiplexed streams between clients + and the device/emulator they point to. The ADB server must handle + unexpected transport disconnections (e.g. when a device is physically + unplugged) properly. diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT new file mode 100644 index 000000000..d059e181f --- /dev/null +++ b/adb/SERVICES.TXT @@ -0,0 +1,245 @@ +This file tries to document all requests a client can make +to the ADB server of an adbd daemon. See the OVERVIEW.TXT document +to understand what's going on here. + +HOST SERVICES: + +host:version + Ask the ADB server for its internal version number. + + As a special exception, the server will respond with a 4-byte + hex string corresponding to its internal version number, without + any OKAY or FAIL. + +host:kill + Ask the ADB server to quit immediately. This is used when the + ADB client detects that an obsolete server is running after an + upgrade. + +host:devices + Ask to return the list of available Android devices and their + state. After the OKAY, this is followed by a 4-byte hex len, + and a string that will be dumped as-is by the client, then + the connection is closed + +host:track-devices + This is a variant of host:devices which doesn't close the + connection. Instead, a new device list description is sent + each time a device is added/removed or the state of a given + device changes (hex4 + content). This allows tools like DDMS + to track the state of connected devices in real-time without + polling the server repeatedly. + +host:emulator: + This is a special query that is sent to the ADB server when a + new emulator starts up. is a decimal number corresponding + to the emulator's ADB control port, i.e. the TCP port that the + emulator will forward automatically to the adbd daemon running + in the emulator system. + + This mechanism allows the ADB server to know when new emulator + instances start. + +host:transport: + Ask to switch the connection to the device/emulator identified by + . After the OKAY response, every client request will + be sent directly to the adbd daemon running on the device. + (Used to implement the -s option) + +host:transport-usb + Ask to switch the connection to one device connected through USB + to the host machine. This will fail if there are more than one such + devices. (Used to implement the -d convenience option) + +host:transport-local + Ask to switch the connection to one emulator connected through TCP. + This will fail if there is more than one such emulator instance + running. (Used to implement the -e convenience option) + +host:transport-any + Another host:transport variant. Ask to switch the connection to + either the device or emulator connect to/running on the host. + Will fail if there is more than one such device/emulator available. + (Used when neither -s, -d or -e are provided) + +host-serial:: + This is a special form of query, where the 'host-serial::' + prefix can be used to indicate that the client is asking the ADB server + for information related to a specific device. can be in one + of the format described below. + +host-usb: + A variant of host-serial used to target the single USB device connected + to the host. This will fail if there is none or more than one. + +host-local: + A variant of host-serial used to target the single emulator instance + running on the host. This will fail if therre is none or more than one. + +host: + When asking for information related to a device, 'host:' can also be + interpreted as 'any single device or emulator connected to/running on + the host'. + +:get-product + XXX + +:get-serialno + Returns the serial number of the corresponding device/emulator. + Note that emulator serial numbers are of the form "emulator-5554" + +:get-state + Returns the state of a given device as a string. + +:forward:: + Asks the ADB server to forward local connections from + to the address on a given device. + + There, can be one of the + host-serial/host-usb/host-local/host prefixes as described previously + and indicates which device/emulator to target. + + the format of is one of: + + tcp: -> TCP connection on localhost: + local: -> Unix local domain socket on + + the format of is one of: + + tcp: -> TCP localhost: on device + local: -> Unix local domain socket on device + jdwp: -> JDWP thread on VM process + + or even any one of the local services described below. + + + +LOCAL SERVICES: + +All the queries below assumed that you already switched the transport +to a real device, or that you have used a query prefix as described +above. + +shell:command arg1 arg2 ... + Run 'command arg1 arg2 ...' in a shell on the device, and return + its output and error streams. Note that arguments must be separated + by spaces. If an argument contains a space, it must be quoted with + double-quotes. Arguments cannot contain double quotes or things + will go very wrong. + + Note that this is the non-interactive version of "adb shell" + +shell: + Start an interactive shell session on the device. Redirect + stdin/stdout/stderr as appropriate. Note that the ADB server uses + this to implement "adb shell", but will also cook the input before + sending it to the device (see interactive_shell() in commandline.c) + +bootdebug: + Ask debugging information to the bootloader. The adbd daemon will + respond with FAIL to this request. + +bootloader: + Send a request to the bootloader. This can also work if the device + is currently in the bootloader state. The adbd daemon will respond + with FAIL to such requests. + +remount: + Ask adbd to remount the device's filesystem in read-write mode, + instead of read-only. This is usually necessary before performing + an "adb sync" or "adb push" request. + + This request may not succeed on certain builds which do not allow + that. + +dev: + Opens a device file and connects the client directly to it for + read/write purposes. Useful for debugging, but may require special + priviledges and thus may not run on all devices. is a full + path from the root of the filesystem. + +tcp: + Tries to connect to tcp port on localhost. + +tcp:: + Tries to connect to tcp port on machine from + the device. This can be useful to debug some networking/proxy + issues that can only be revealed on the device itself. + +local: + Tries to connect to a Unix domain socket on the device + +localreserved: +localabstract: +localfilesystem: + Variants of local: that are used to access other Android + socket namespaces. + +log: + Opens one of the system logs (/dev/log/) and allows the client + to read them directly. Used to implement 'adb logcat'. The stream + will be read-only for the client. + +framebuffer: + This service is used to send snapshots of the framebuffer to a client. + It requires sufficient priviledges but works as follow: + + After the OKAY, the service sends 16-byte binary structure + containing the following fields (little-endian format): + + depth: uint32_t: framebuffer depth + size: uint32_t: framebuffer size in bytes + width: uint32_t: framebuffer width in pixels + height: uint32_t: framebuffer height in pixels + + With the current implementation, depth is always 16, and + size is always width*height*2 + + Then, each time the client wants a snapshot, it should send + one byte through the channel, which will trigger the service + to send it 'size' bytes of framebuffer data. + + If the adbd daemon doesn't have sufficient priviledges to open + the framebuffer device, the connection is simply closed immediately. + +dns: + This service is an exception because it only runs within the ADB server. + It is used to implement USB networking, i.e. to provide a network connection + to the device through the host machine (note: this is the exact opposite of + network thetering). + + It is used to perform a gethostbyname(
) on the host and return + the corresponding IP address as a 4-byte string. + +recover: + This service is used to upload a recovery image to the device. + must be a number corresponding to the size of the file. The service works + by: + + - creating a file named /tmp/update + - reading 'size' bytes from the client and writing them to /tmp/update + - when everything is read succesfully, create a file named /tmp/update.start + + This service can only work when the device is in recovery mode. Otherwise, + the /tmp directory doesn't exist and the connection will be closed immediately. + +jdwp: + Connects to the JDWP thread running in the VM of process . + +track-jdwp + This is used to send the list of JDWP pids periodically to the client. + The format of the returned data is the following: + + : the length of all content as a 4-char hexadecimal string + : a series of ASCII lines of the following format: + "\n" + + This service is used by DDMS to know which debuggable processes are running + on the device/emulator. + + Note that there is no single-shot service to retrieve the list only once. + +sync: + This starts the file synchronisation service, used to implement "adb push" + and "adb pull". Since this service is pretty complex, it will be detailed + in a companion document named SYNC.TXT diff --git a/adb/adb.c b/adb/adb.c index a50ef334b..27d0c8156 100644 --- a/adb/adb.c +++ b/adb/adb.c @@ -67,59 +67,59 @@ int adb_trace_mask; */ void adb_trace_init(void) { - const char* p = getenv("ADB_TRACE"); - const char* q; + const char* p = getenv("ADB_TRACE"); + const char* q; - static const struct { - const char* tag; - int flag; - } tags[] = { - { "1", 0 }, - { "all", 0 }, - { "adb", TRACE_ADB }, - { "sockets", TRACE_SOCKETS }, - { "packets", TRACE_PACKETS }, - { "rwx", TRACE_RWX }, - { "usb", TRACE_USB }, - { "sync", TRACE_SYNC }, - { "sysdeps", TRACE_SYSDEPS }, + static const struct { + const char* tag; + int flag; + } tags[] = { + { "1", 0 }, + { "all", 0 }, + { "adb", TRACE_ADB }, + { "sockets", TRACE_SOCKETS }, + { "packets", TRACE_PACKETS }, + { "rwx", TRACE_RWX }, + { "usb", TRACE_USB }, + { "sync", TRACE_SYNC }, + { "sysdeps", TRACE_SYSDEPS }, { "transport", TRACE_TRANSPORT }, { "jdwp", TRACE_JDWP }, - { NULL, 0 } - }; + { NULL, 0 } + }; - if (p == NULL) - return; + if (p == NULL) + return; - /* use a comma/column/semi-colum/space separated list */ - while (*p) { - int len, tagn; + /* use a comma/column/semi-colum/space separated list */ + while (*p) { + int len, tagn; - q = strpbrk(p, " ,:;"); - if (q == NULL) { - q = p + strlen(p); - } - len = q - p; + q = strpbrk(p, " ,:;"); + if (q == NULL) { + q = p + strlen(p); + } + len = q - p; - for (tagn = 0; tags[tagn].tag != NULL; tagn++) - { - int taglen = strlen(tags[tagn].tag); + for (tagn = 0; tags[tagn].tag != NULL; tagn++) + { + int taglen = strlen(tags[tagn].tag); - if (len == taglen && !memcmp(tags[tagn].tag, p, len) ) - { - int flag = tags[tagn].flag; - if (flag == 0) { - adb_trace_mask = ~0; - return; - } - adb_trace_mask |= (1 << flag); - break; - } - } - p = q; - if (*p) - p++; - } + if (len == taglen && !memcmp(tags[tagn].tag, p, len) ) + { + int flag = tags[tagn].flag; + if (flag == 0) { + adb_trace_mask = ~0; + return; + } + adb_trace_mask |= (1 << flag); + break; + } + } + p = q; + if (*p) + p++; + } } @@ -783,8 +783,8 @@ int launch_server() // child process int result = execl(path, "adb", "fork-server", "server", NULL); - // this should not return - fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno); + // this should not return + fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno); } else { // parent side of the fork @@ -894,7 +894,7 @@ int adb_main(int is_daemon) if(access("/dev/android_adb", F_OK) == 0 || access("/dev/android", F_OK) == 0) { usb_init(); - } else { + } else { local_init(); } init_jdwp(); diff --git a/adb/adb.h b/adb/adb.h index 5a74b7b9a..a17c8ddc5 100644 --- a/adb/adb.h +++ b/adb/adb.h @@ -79,6 +79,11 @@ struct asocket { */ unsigned id; + /* flag: set when the socket's peer has closed + ** but packets are still queued for delivery + */ + int closing; + /* the asocket we are connected to */ @@ -309,15 +314,15 @@ int writex(int fd, const void *ptr, size_t len); * the adb_trace_init() function implemented in adb.c */ typedef enum { - TRACE_ADB = 0, - TRACE_SOCKETS, - TRACE_PACKETS, - TRACE_TRANSPORT, - TRACE_RWX, - TRACE_USB, - TRACE_SYNC, - TRACE_SYSDEPS, - TRACE_JDWP, + TRACE_ADB = 0, + TRACE_SOCKETS, + TRACE_PACKETS, + TRACE_TRANSPORT, + TRACE_RWX, + TRACE_USB, + TRACE_SYNC, + TRACE_SYSDEPS, + TRACE_JDWP, } AdbTrace; #if ADB_TRACE diff --git a/adb/adb_client.c b/adb/adb_client.c index c1b87ee51..586874480 100644 --- a/adb/adb_client.c +++ b/adb/adb_client.c @@ -211,7 +211,7 @@ int adb_connect(const char *service) return -1; } else { fprintf(stdout,"* daemon started successfully *\n"); - } + } /* give the server some time to start properly and detect devices */ adb_sleep_ms(2000); // fall through to _adb_connect @@ -223,13 +223,13 @@ int adb_connect(const char *service) // if we have a file descriptor, then parse version result if(fd >= 0) { - if(readx(fd, buf, 4)) goto error; + if(readx(fd, buf, 4)) goto error; - buf[4] = 0; - n = strtoul(buf, 0, 16); - if(n > (int)sizeof(buf)) goto error; - if(readx(fd, buf, n)) goto error; - adb_close(fd); + buf[4] = 0; + n = strtoul(buf, 0, 16); + if(n > (int)sizeof(buf)) goto error; + if(readx(fd, buf, n)) goto error; + adb_close(fd); if (sscanf(buf, "%04x", &version) != 1) goto error; } else { @@ -240,14 +240,14 @@ int adb_connect(const char *service) } if(version != ADB_SERVER_VERSION) { - printf("adb server is out of date. killing...\n"); - fd = _adb_connect("host:kill"); - adb_close(fd); + printf("adb server is out of date. killing...\n"); + fd = _adb_connect("host:kill"); + adb_close(fd); - /* XXX can we better detect its death? */ - adb_sleep_ms(2000); + /* XXX can we better detect its death? */ + adb_sleep_ms(2000); goto start_server; - } + } } // if the command is start-server, we are done. diff --git a/adb/commandline.c b/adb/commandline.c index c1ef8b011..cbf079c40 100644 --- a/adb/commandline.c +++ b/adb/commandline.c @@ -296,8 +296,8 @@ static void *stdin_read_thread(void *x) buf_ptr[cmdlen] = '\0'; if( (item = shListFindItem( &history, (void *)buf_ptr, shItemCmp )) == NULL ) { shListInsFirstItem( &history, (void *)buf_ptr ); - item = &history; - } + item = &history; + } } } cmdlen = 0; @@ -322,8 +322,8 @@ static void *stdin_read_thread(void *x) default: #ifdef SH_HISTORY if( buf[n] == SH_DEL_CHAR ) { - if( cmdlen > 0 ) - cmdlen--; + if( cmdlen > 0 ) + cmdlen--; } else { realbuf[cmdlen] = buf[n]; @@ -478,7 +478,7 @@ static void status_window(transport_type ttype, const char* serial) #ifdef _WIN32 /* XXX: TODO */ #else - int fd; + int fd; fd = unix_open("/dev/null", O_WRONLY); dup2(fd, 2); adb_close(fd); @@ -512,7 +512,7 @@ static void status_window(transport_type ttype, const char* serial) } } -/** duplicate string and quote all \ " ( ) chars */ +/** duplicate string and quote all \ " ( ) chars + space character. */ static char * dupAndQuote(const char *s) { @@ -527,7 +527,7 @@ dupAndQuote(const char *s) for( ;*ts != '\0'; ts++) { alloc_len++; - if (*ts == '"' || *ts == '\\') { + if (*ts == ' ' || *ts == '"' || *ts == '\\' || *ts == '(' || *ts == ')') { alloc_len++; } } @@ -538,7 +538,7 @@ dupAndQuote(const char *s) dest = ret; for ( ;*ts != '\0'; ts++) { - if (*ts == '"' || *ts == '\\' || *ts == '(' || *ts == ')') { + if (*ts == ' ' || *ts == '"' || *ts == '\\' || *ts == '(' || *ts == ')') { *dest++ = '\\'; } @@ -561,7 +561,7 @@ int ppp(int argc, char **argv) { #ifdef HAVE_WIN32_PROC fprintf(stderr, "error: adb %s not implemented on Win32\n", argv[0]); - return -1; + return -1; #else char *adb_service_name; pid_t pid; @@ -657,8 +657,8 @@ static int logcat(transport_type transport, char* serial, int argc, char **argv) quoted_log_tags = dupAndQuote(log_tags == NULL ? "" : log_tags); snprintf(buf, sizeof(buf), - "shell:export ANDROID_LOG_TAGS=\"\%s\" ; exec logcat", - quoted_log_tags); + "shell:export ANDROID_LOG_TAGS=\"\%s\" ; exec logcat", + quoted_log_tags); free(quoted_log_tags); @@ -847,7 +847,7 @@ int adb_commandline(int argc, char **argv) if (gProductOutPath == NULL || gProductOutPath[0] == '\0') { gProductOutPath = NULL; } - // TODO: also try TARGET_PRODUCT as a hint + // TODO: also try TARGET_PRODUCT/TARGET_DEVICE as a hint /* modifiers and flags */ while(argc > 0) { @@ -1077,15 +1077,15 @@ top: return 1; } - /* Allow a command to be run after wait-for-device, - * e.g. 'adb wait-for-device shell'. - */ - if(argc > 1) { - argc--; - argv++; - goto top; - } - return 0; + /* Allow a command to be run after wait-for-device, + * e.g. 'adb wait-for-device shell'. + */ + if(argc > 1) { + argc--; + argv++; + goto top; + } + return 0; } if(!strcmp(argv[0], "forward")) { @@ -1299,7 +1299,7 @@ static int pm_command(transport_type transport, char* serial, while(argc-- > 0) { char *quoted; - quoted = dupAndQuote (*argv++); + quoted = dupAndQuote(*argv++); strncat(buf, " ", sizeof(buf)-1); strncat(buf, quoted, sizeof(buf)-1); @@ -1312,6 +1312,18 @@ static int pm_command(transport_type transport, char* serial, int uninstall_app(transport_type transport, char* serial, int argc, char** argv) { + /* if the user choose the -k option, we refuse to do it until devices are + out with the option to uninstall the remaining data somehow (adb/ui) */ + if (argc == 3 && strcmp(argv[1], "-k") == 0) + { + printf( + "The -k option uninstalls the application while retaining the data/cache.\n" + "At the moment, there is no way to remove the remaining data.\n" + "You will have to reinstall the application with the same signature, and fully uninstall it.\n" + "If you truly wish to continue, execute 'adb shell pm uninstall -k %s'\n", argv[2]); + return -1; + } + /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */ return pm_command(transport, serial, argc, argv); } diff --git a/adb/file_sync_client.c b/adb/file_sync_client.c index f9e80edbf..4e6d38591 100644 --- a/adb/file_sync_client.c +++ b/adb/file_sync_client.c @@ -55,7 +55,7 @@ static void END() if(total_bytes == 0) return; if (t == 0) /* prevent division by 0 :-) */ - t = 1000000; + t = 1000000; fprintf(stderr,"%lld KB/s (%d bytes in %lld.%03llds)\n", ((((long long) total_bytes) * 1000000LL) / t) / 1024LL, @@ -227,14 +227,14 @@ static int write_data_file(int fd, const char *path, syncsendbuf *sbuf) if(ret < 0) { if(errno == EINTR) - continue; + continue; fprintf(stderr,"cannot read '%s': %s\n", path, strerror(errno)); break; } sbuf->size = htoll(ret); if(writex(fd, sbuf, sizeof(unsigned) * 2 + ret)){ - err = -1; + err = -1; break; } total_bytes += ret; @@ -259,7 +259,7 @@ static int write_data_buffer(int fd, char* file_buffer, int size, syncsendbuf *s memcpy(sbuf->data, &file_buffer[total], count); sbuf->size = htoll(count); if(writex(fd, sbuf, sizeof(unsigned) * 2 + count)){ - err = -1; + err = -1; break; } total += count; @@ -277,7 +277,7 @@ static int write_data_link(int fd, const char *path, syncsendbuf *sbuf) len = readlink(path, sbuf->data, SYNC_DATA_MAX-1); if(len < 0) { fprintf(stderr, "error reading link '%s': %s\n", path, strerror(errno)); - return -1; + return -1; } sbuf->data[len] = '\0'; @@ -377,10 +377,10 @@ static int sync_send(int fd, const char *lpath, const char *rpath, } if (file_buffer) { - write_data_buffer(fd, file_buffer, size, sbuf); - free(file_buffer); + write_data_buffer(fd, file_buffer, size, sbuf); + free(file_buffer); } else if (S_ISREG(mode)) - write_data_file(fd, lpath, sbuf); + write_data_file(fd, lpath, sbuf); #ifdef HAVE_SYMLINKS else if (S_ISLNK(mode)) write_data_link(fd, lpath, sbuf); @@ -641,7 +641,7 @@ static int local_build_list(copyinfo **filelist, } else { ci = mkcopyinfo(lpath, rpath, name, 0); if(lstat(ci->src, &st)) { - closedir(d); + closedir(d); fprintf(stderr,"cannot stat '%s': %s\n", ci->src, strerror(errno)); return -1; } @@ -651,7 +651,7 @@ static int local_build_list(copyinfo **filelist, } else { ci->time = st.st_mtime; ci->mode = st.st_mode; - ci->size = st.st_size; + ci->size = st.st_size; ci->next = *filelist; *filelist = ci; } @@ -707,12 +707,12 @@ static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, i unsigned int timestamp, mode, size; if(sync_finish_readtime(fd, ×tamp, &mode, &size)) return 1; - if(size == ci->size) { + if(size == ci->size) { /* for links, we cannot update the atime/mtime */ if((S_ISREG(ci->mode & mode) && timestamp == ci->time) || - (S_ISLNK(ci->mode & mode) && timestamp >= ci->time)) + (S_ISLNK(ci->mode & mode) && timestamp >= ci->time)) ci->flag = 1; - } + } } } for(ci = filelist; ci != 0; ci = next) { diff --git a/adb/file_sync_service.c b/adb/file_sync_service.c index 21f8af775..a231e93d6 100644 --- a/adb/file_sync_service.c +++ b/adb/file_sync_service.c @@ -164,12 +164,12 @@ static int handle_send_file(int s, char *path, mode_t mode, char *buffer) } if(fd < 0) { if(fail_errno(s)) - return -1; + return -1; fd = -1; } for(;;) { - unsigned int len; + unsigned int len; if(readx(s, &msg.data, sizeof(msg.data))) goto fail; @@ -264,7 +264,7 @@ static int handle_send_link(int s, char *path, char *buffer) return -1; } else { fail_message(s, "invalid data message: expected ID_DONE"); - return -1; + return -1; } return 0; diff --git a/adb/framebuffer_service.c b/adb/framebuffer_service.c index 1d2c4b56b..0de0dd5e6 100644 --- a/adb/framebuffer_service.c +++ b/adb/framebuffer_service.c @@ -24,6 +24,7 @@ #include "adb.h" #include +#include #include /* TODO: @@ -37,9 +38,9 @@ void framebuffer_service(int fd, void *cookie) int fb; void *ptr = MAP_FAILED; char x; - + unsigned fbinfo[4]; - + fb = open("/dev/graphics/fb0", O_RDONLY); if(fb < 0) goto done; @@ -53,14 +54,14 @@ void framebuffer_service(int fd, void *cookie) ptr = mmap(0, fbinfo[1], PROT_READ, MAP_SHARED, fb, 0); if(ptr == MAP_FAILED) goto done; - + if(writex(fd, fbinfo, sizeof(unsigned) * 4)) goto done; for(;;) { if(readx(fd, &x, 1)) goto done; if(writex(fd, ptr, fbinfo[1])) goto done; } - + done: if(ptr != MAP_FAILED) munmap(ptr, fbinfo[1]); if(fb >= 0) close(fb); diff --git a/adb/get_my_path_windows.c b/adb/get_my_path_windows.c index 64a597d3c..fc7143c10 100644 --- a/adb/get_my_path_windows.c +++ b/adb/get_my_path_windows.c @@ -20,12 +20,12 @@ void get_my_path(char exe[PATH_MAX]) { - char* r; + char* r; - GetModuleFileName( NULL, exe, PATH_MAX-1 ); - exe[PATH_MAX-1] = 0; - r = strrchr( exe, '\\' ); - if (r) - *r = 0; + GetModuleFileName( NULL, exe, PATH_MAX-1 ); + exe[PATH_MAX-1] = 0; + r = strrchr( exe, '\\' ); + if (r) + *r = 0; } diff --git a/adb/jdwp_service.c b/adb/jdwp_service.c index 43dc69eb4..ae7f12dfd 100644 --- a/adb/jdwp_service.c +++ b/adb/jdwp_service.c @@ -287,7 +287,7 @@ jdwp_process_event( int socket, unsigned events, void* _proc ) if (len <= 0) { if (len < 0 && errno == EINTR) continue; - if (len < 0 && errno == EAGAIN) + if (len < 0 && errno == EAGAIN) return; else { D("terminating JDWP %d connection: %s\n", proc->pid, @@ -295,7 +295,7 @@ jdwp_process_event( int socket, unsigned events, void* _proc ) break; } } - else { + else { D( "ignoring unexpected JDWP %d control socket activity (%d bytes)\n", proc->pid, len ); } diff --git a/adb/sockets.c b/adb/sockets.c index b9c9ae1cf..9f1b59870 100644 --- a/adb/sockets.c +++ b/adb/sockets.c @@ -50,6 +50,15 @@ static asocket local_socket_list = { .prev = &local_socket_list, }; +/* the the list of currently closing local sockets. +** these have no peer anymore, but still packets to +** write to their fd. +*/ +static asocket local_socket_closing_list = { + .next = &local_socket_closing_list, + .prev = &local_socket_closing_list, +}; + asocket *find_local_socket(unsigned id) { asocket *s; @@ -64,16 +73,22 @@ asocket *find_local_socket(unsigned id) return result; } +static void +insert_local_socket(asocket* s, asocket* list) +{ + s->next = list; + s->prev = s->next->prev; + s->prev->next = s; + s->next->prev = s; +} + + void install_local_socket(asocket *s) { adb_mutex_lock(&socket_list_lock); s->id = local_socket_next_id++; - - s->next = &local_socket_list; - s->prev = local_socket_list.prev; - s->prev->next = s; - s->next->prev = s; + insert_local_socket(s, &local_socket_list); adb_mutex_unlock(&socket_list_lock); } @@ -177,19 +192,11 @@ static void local_socket_close(asocket *s) adb_mutex_unlock(&socket_list_lock); } -static void local_socket_close_locked(asocket *s) +// be sure to hold the socket list lock when calling this +static void local_socket_destroy(asocket *s) { apacket *p, *n; - if(s->peer) { - s->peer->peer = 0; - // tweak to avoid deadlock - if (s->peer->close == local_socket_close) - local_socket_close_locked(s->peer); - else - s->peer->close(s->peer); - } - /* IMPORTANT: the remove closes the fd ** that belongs to this socket */ @@ -201,16 +208,94 @@ static void local_socket_close_locked(asocket *s) n = p->next; put_apacket(p); } - - D("LS(%d): closed\n", s->id); remove_socket(s); free(s); } + +static void local_socket_close_locked(asocket *s) +{ + if(s->peer) { + s->peer->peer = 0; + // tweak to avoid deadlock + if (s->peer->close == local_socket_close) + local_socket_close_locked(s->peer); + else + s->peer->close(s->peer); + } + + /* If we are already closing, or if there are no + ** pending packets, destroy immediately + */ + if (s->closing || s->pkt_first == NULL) { + int id = s->id; + local_socket_destroy(s); + D("LS(%d): closed\n", id); + return; + } + + /* otherwise, put on the closing list + */ + D("LS(%d): closing\n", s->id); + s->closing = 1; + fdevent_del(&s->fde, FDE_READ); + remove_socket(s); + insert_local_socket(s, &local_socket_closing_list); +} + static void local_socket_event_func(int fd, unsigned ev, void *_s) { asocket *s = _s; + /* put the FDE_WRITE processing before the FDE_READ + ** in order to simplify the code. + */ + if(ev & FDE_WRITE){ + apacket *p; + + while((p = s->pkt_first) != 0) { + while(p->len > 0) { + int r = adb_write(fd, p->ptr, p->len); + if(r > 0) { + p->ptr += r; + p->len -= r; + continue; + } + if(r < 0) { + /* returning here is ok because FDE_READ will + ** be processed in the next iteration loop + */ + if(errno == EAGAIN) return; + if(errno == EINTR) continue; + } + s->close(s); + return; + } + + if(p->len == 0) { + s->pkt_first = p->next; + if(s->pkt_first == 0) s->pkt_last = 0; + put_apacket(p); + } + } + + /* if we sent the last packet of a closing socket, + ** we can now destroy it. + */ + if (s->closing) { + s->close(s); + return; + } + + /* no more packets queued, so we can ignore + ** writable events again and tell our peer + ** to resume writing + */ + fdevent_del(&s->fde, FDE_WRITE); + s->peer->ready(s->peer); + } + + if(ev & FDE_READ){ apacket *p = get_apacket(); unsigned char *x = p->data; @@ -244,7 +329,12 @@ static void local_socket_event_func(int fd, unsigned ev, void *_s) if(r < 0) { /* error return means they closed us as a side-effect - ** and we must retutn immediately + ** and we must return immediately. + ** + ** note that if we still have buffered packets, the + ** socket will be placed on the closing socket list. + ** this handler function will be called again + ** to process FDE_WRITE events. */ return; } @@ -261,42 +351,6 @@ static void local_socket_event_func(int fd, unsigned ev, void *_s) if(is_eof) { s->close(s); } - return; - } - - if(ev & FDE_WRITE){ - apacket *p; - - while((p = s->pkt_first) != 0) { - while(p->len > 0) { - int r = adb_write(fd, p->ptr, p->len); - if(r > 0) { - p->ptr += r; - p->len -= r; - continue; - } - if(r < 0) { - if(errno == EAGAIN) return; - if(errno == EINTR) continue; - } - s->close(s); - return; - } - - if(p->len == 0) { - s->pkt_first = p->next; - if(s->pkt_first == 0) s->pkt_last = 0; - put_apacket(p); - } - } - - /* no more packets queued, so we can ignore - ** writable events again and tell our peer - ** to resume writing - */ - fdevent_del(&s->fde, FDE_WRITE); - s->peer->ready(s->peer); - return; } if(ev & FDE_ERROR){ diff --git a/adb/sysdeps_win32.c b/adb/sysdeps_win32.c index 9d6a596cf..c2a9a985f 100644 --- a/adb/sysdeps_win32.c +++ b/adb/sysdeps_win32.c @@ -940,12 +940,12 @@ bip_buffer_write( BipBuffer bip, const void* src, int len ) /* we can append to region A */ if (avail > len) avail = len; - + memcpy( bip->buff + bip->a_end, src, avail ); src += avail; count += avail; len -= avail; - + bip->a_end += avail; if (bip->a_end == BIP_BUFFER_SIZE && bip->a_start == 0) { bip->can_write = 0; @@ -953,25 +953,25 @@ bip_buffer_write( BipBuffer bip, const void* src, int len ) goto Exit; } } - + if (len == 0) goto Exit; avail = bip->a_start - bip->b_end; assert( avail > 0 ); /* since can_write is TRUE */ - + if (avail > len) avail = len; - + memcpy( bip->buff + bip->b_end, src, avail ); count += avail; bip->b_end += avail; - + if (bip->b_end == bip->a_start) { bip->can_write = 0; ResetEvent( bip->evt_write ); } - + Exit: assert( count > 0 ); @@ -979,7 +979,7 @@ Exit: bip->can_read = 1; SetEvent( bip->evt_read ); } - + BIPD(( "bip_buffer_write: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n", bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read )); LeaveCriticalSection( &bip->lock ); @@ -991,12 +991,12 @@ static int bip_buffer_read( BipBuffer bip, void* dst, int len ) { int avail, count = 0; - + if (len <= 0) return 0; - + BIPD(( "bip_buffer_read: enter %d->%d len %d\n", bip->fdin, bip->fdout, len )); - + EnterCriticalSection( &bip->lock ); while ( !bip->can_read ) { @@ -1007,7 +1007,7 @@ bip_buffer_read( BipBuffer bip, void* dst, int len ) #else int ret; LeaveCriticalSection( &bip->lock ); - + if (bip->closed) { errno = EPIPE; return -1; @@ -1023,30 +1023,30 @@ bip_buffer_read( BipBuffer bip, void* dst, int len ) return -1; } EnterCriticalSection( &bip->lock ); -#endif +#endif } BIPD(( "bip_buffer_read: exec %d->%d len %d\n", bip->fdin, bip->fdout, len )); - + avail = bip->a_end - bip->a_start; assert( avail > 0 ); /* since can_read is TRUE */ - + if (avail > len) avail = len; - + memcpy( dst, bip->buff + bip->a_start, avail ); dst += avail; count += avail; len -= avail; - + bip->a_start += avail; if (bip->a_start < bip->a_end) goto Exit; - + bip->a_start = 0; bip->a_end = bip->b_end; bip->b_end = 0; - + avail = bip->a_end; if (avail > 0) { if (avail > len) @@ -1054,13 +1054,13 @@ bip_buffer_read( BipBuffer bip, void* dst, int len ) memcpy( dst, bip->buff, avail ); count += avail; bip->a_start += avail; - + if ( bip->a_start < bip->a_end ) goto Exit; - + bip->a_start = bip->a_end = 0; } - + bip->can_read = 0; ResetEvent( bip->evt_read ); @@ -1071,22 +1071,22 @@ Exit: bip->can_write = 1; SetEvent( bip->evt_write ); } - + BIPDUMP( (const unsigned char*)dst - count, count ); BIPD(( "bip_buffer_read: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n", bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read )); LeaveCriticalSection( &bip->lock ); - + return count; } - + typedef struct SocketPairRec_ { BipBufferRec a2b_bip; BipBufferRec b2a_bip; FH a_fd; int used; - + } SocketPairRec; void _fh_socketpair_init( FH f ) @@ -1103,7 +1103,7 @@ _fh_socketpair_close( FH f ) if ( f == pair->a_fd ) { pair->a_fd = NULL; } - + bip_buffer_close( &pair->b2a_bip ); bip_buffer_close( &pair->a2b_bip ); @@ -1199,7 +1199,7 @@ int adb_socketpair( int sv[2] ) sv[0] = _fh_to_int(fa); sv[1] = _fh_to_int(fb); - + pair->a2b_bip.fdin = sv[0]; pair->a2b_bip.fdout = sv[1]; pair->b2a_bip.fdin = sv[1]; @@ -1303,7 +1303,7 @@ event_hook_alloc( FH fh ) hook->stop = NULL; hook->check = NULL; hook->peek = NULL; - + return hook; } @@ -1324,7 +1324,7 @@ event_hook_signal( EventHook hook ) FH f = hook->fh; int fd = _fh_to_int(f); fdevent* fde = fd_table[ fd - WIN32_FH_BASE ]; - + if (fde != NULL && fde->fd == fd) { if ((fde->state & FDE_PENDING) == 0) { fde->state |= FDE_PENDING; @@ -1365,7 +1365,7 @@ event_looper_hook( EventLooper looper, int fd, int events ) FH f = _fh_from_int(fd); EventHook *pnode; EventHook node; - + if (f == NULL) /* invalid arg */ { D("event_looper_hook: invalid fd=%d\n", fd); return; @@ -1397,7 +1397,7 @@ event_looper_unhook( EventLooper looper, int fd, int events ) FH fh = _fh_from_int(fd); EventHook *pnode = event_looper_find_p( looper, fh ); EventHook node = *pnode; - + if (node != NULL) { int events2 = events & node->wanted; if ( events2 == 0 ) { @@ -1424,7 +1424,7 @@ static void fdevent_connect(fdevent *fde) { EventLooper looper = &win32_looper; int events = fde->state & FDE_EVENTMASK; - + if (events != 0) event_looper_hook( looper, fde->fd, events ); } @@ -1433,7 +1433,7 @@ static void fdevent_disconnect(fdevent *fde) { EventLooper looper = &win32_looper; int events = fde->state & FDE_EVENTMASK; - + if (events != 0) event_looper_unhook( looper, fde->fd, events ); } @@ -1462,7 +1462,7 @@ static void fdevent_process() EventLooper looper = &win32_looper; EventHook hook; int gotone = 0; - + /* if we have at least one ready hook, execute it/them */ for (hook = looper->hooks; hook; hook = hook->next) { hook->ready = 0; @@ -1479,7 +1479,7 @@ static void fdevent_process() if (!gotone) { looper->htab_count = 0; - + for (hook = looper->hooks; hook; hook = hook->next) { if (hook->start && !hook->start(hook)) { @@ -1519,7 +1519,7 @@ static void fdevent_process() D( "adb_win32: wait failed, error %ld\n", GetLastError() ); } else { D( "adb_win32: got one (index %d)\n", wait_ret ); - + /* according to Cygwin, some objects like consoles wake up on "inappropriate" events * like mouse movements. we need to filter these with the "check" function */ @@ -1561,7 +1561,7 @@ static void fdevent_register(fdevent *fde) if(fd < 0) { FATAL("bogus negative fd (%d)\n", fde->fd); } - + if(fd >= fd_table_max) { int oldmax = fd_table_max; if(fde->fd > 32000) { @@ -1587,7 +1587,7 @@ static void fdevent_register(fdevent *fde) static void fdevent_unregister(fdevent *fde) { int fd = fde->fd - WIN32_FH_BASE; - + if((fd < 0) || (fd >= fd_table_max)) { FATAL("fd out of range (%d)\n", fde->fd); } @@ -1626,9 +1626,9 @@ static fdevent *fdevent_plist_dequeue(void) { fdevent *list = &list_pending; fdevent *node = list->next; - + if(node == list) return 0; - + list->next = node->next; list->next->prev = list; node->next = 0; @@ -1689,9 +1689,9 @@ void fdevent_remove(fdevent *fde) void fdevent_set(fdevent *fde, unsigned events) { events &= FDE_EVENTMASK; - + if((fde->state & FDE_EVENTMASK) == (int)events) return; - + if(fde->state & FDE_ACTIVE) { fdevent_update(fde, events); dump_fde(fde, "update"); @@ -1727,13 +1727,13 @@ void fdevent_del(fdevent *fde, unsigned events) void fdevent_loop() { fdevent *fde; - + for(;;) { #if DEBUG fprintf(stderr,"--- ---- waiting for events\n"); #endif fdevent_process(); - + while((fde = fdevent_plist_dequeue())) { unsigned events = fde->events; fde->events = 0; @@ -1793,7 +1793,7 @@ static void _event_socket_verify( EventHook hook, WSANETWORKEVENTS* evts ) static void _event_socket_prepare( EventHook hook ) { WSANETWORKEVENTS evts; - + /* look if some of the events we want already happened ? */ if (!WSAEnumNetworkEvents( hook->fh->fh_socket, NULL, &evts )) _event_socket_verify( hook, &evts ); @@ -1819,13 +1819,13 @@ static int _event_socket_start( EventHook hook ) /* create an event which we're going to wait for */ FH fh = hook->fh; long flags = _socket_wanted_to_flags( hook->wanted ); - + hook->h = fh->event; if (hook->h == INVALID_HANDLE_VALUE) { D( "_event_socket_start: no event for %s\n", fh->name ); return 0; } - + if ( flags != fh->mask ) { D( "_event_socket_start: hooking %s for %x (flags %ld)\n", hook->fh->name, hook->wanted, flags ); if ( WSAEventSelect( fh->fh_socket, hook->h, flags ) ) { @@ -1850,7 +1850,7 @@ static int _event_socket_check( EventHook hook ) int result = 0; FH fh = hook->fh; WSANETWORKEVENTS evts; - + if (!WSAEnumNetworkEvents( fh->fh_socket, hook->h, &evts ) ) { _event_socket_verify( hook, &evts ); result = (hook->ready != 0); @@ -1866,7 +1866,7 @@ static int _event_socket_peek( EventHook hook ) { WSANETWORKEVENTS evts; FH fh = hook->fh; - + /* look if some of the events we want already happened ? */ if (!WSAEnumNetworkEvents( fh->fh_socket, NULL, &evts )) { _event_socket_verify( hook, &evts ); @@ -1886,40 +1886,40 @@ static void _fh_socket_hook( FH f, int events, EventHook hook ) hook->stop = _event_socket_stop; hook->check = _event_socket_check; hook->peek = _event_socket_peek; - + _event_socket_start( hook ); } /** SOCKETPAIR EVENT HOOKS **/ - + static void _event_socketpair_prepare( EventHook hook ) { FH fh = hook->fh; SocketPair pair = fh->fh_pair; BipBuffer rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip; BipBuffer wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip; - + if (hook->wanted & FDE_READ && rbip->can_read) hook->ready |= FDE_READ; - + if (hook->wanted & FDE_WRITE && wbip->can_write) hook->ready |= FDE_WRITE; } - + static int _event_socketpair_start( EventHook hook ) { FH fh = hook->fh; SocketPair pair = fh->fh_pair; BipBuffer rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip; BipBuffer wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip; - + if (hook->wanted == FDE_READ) hook->h = rbip->evt_read; - + else if (hook->wanted == FDE_WRITE) hook->h = wbip->evt_write; - + else { D("_event_socketpair_start: can't handle FDE_READ+FDE_WRITE\n" ); return 0; diff --git a/adb/transport_local.c b/adb/transport_local.c index 0e8b73246..be01f290c 100644 --- a/adb/transport_local.c +++ b/adb/transport_local.c @@ -76,7 +76,7 @@ static int remote_read(apacket *p, atransport *t) } if(check_data(p)) { - D("bad data: terminated (data)\n"); + D("bad data: terminated (data)\n"); return -1; } @@ -107,15 +107,16 @@ int local_connect(int port) char buf[64]; int fd = -1; - fd = socket_loopback_client(port, SOCK_STREAM); #if ADB_HOST - if(fd < 0) { - const char *host = getenv("ADBHOST"); - if(host) { - fd = socket_network_client(host, port, SOCK_STREAM); - } + const char *host = getenv("ADBHOST"); + if (host) { + fd = socket_network_client(host, port, SOCK_STREAM); } #endif + if (fd < 0) { + fd = socket_loopback_client(port, SOCK_STREAM); + } + if (fd >= 0) { D("client: connected on remote on fd %d\n", fd); close_on_exec(fd); diff --git a/adb/transport_usb.c b/adb/transport_usb.c index 4da7e8b65..01c4a7ede 100644 --- a/adb/transport_usb.c +++ b/adb/transport_usb.c @@ -55,7 +55,7 @@ static int remote_read(apacket *p, atransport *t) } fix_endians(p); - + if(check_header(p)) { D("remote usb: check_header failed\n"); return -1; @@ -79,9 +79,9 @@ static int remote_read(apacket *p, atransport *t) static int remote_write(apacket *p, atransport *t) { unsigned size = p->msg.data_length; - + fix_endians(p); - + if(usb_write(t->usb, &p->msg, sizeof(amessage))) { D("remote usb: 1 - write terminated\n"); return -1; @@ -91,7 +91,7 @@ static int remote_write(apacket *p, atransport *t) D("remote usb: 2 - write terminated\n"); return -1; } - + return 0; } @@ -117,7 +117,7 @@ void init_usb_transport(atransport *t, usb_handle *h) t->connection_state = CS_OFFLINE; t->type = kTransportUsb; t->usb = h; - + #if ADB_HOST HOST = 1; #else @@ -135,7 +135,7 @@ int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_ /* not supported */ return 0; } - + /* class:vendor (0xff) subclass:android (0x42) proto:adb (0x01) */ if(usb_class == 0xff) { if((usb_subclass == 0x42) && (usb_protocol == 0x01)) { diff --git a/adb/usb_linux_client.c b/adb/usb_linux_client.c index e265a1c22..530bd04db 100644 --- a/adb/usb_linux_client.c +++ b/adb/usb_linux_client.c @@ -118,7 +118,7 @@ void usb_init() h->fd = -1; adb_cond_init(&h->notify, 0); adb_mutex_init(&h->lock, 0); - + // Open the file /dev/android_adb_enable to trigger // the enabling of the adb USB function in the kernel. // We never touch this file again - just leave it open diff --git a/adb/usb_osx.c b/adb/usb_osx.c index 8ea84f926..49e1eef5b 100644 --- a/adb/usb_osx.c +++ b/adb/usb_osx.c @@ -72,13 +72,13 @@ InitUSB() CFRunLoopSourceRef runLoopSource; SInt32 vendor, product; int i; - + //* To set up asynchronous notifications, create a notification port and //* add its run loop event source to the program's run loop notificationPort = IONotificationPortCreate(kIOMasterPortDefault); runLoopSource = IONotificationPortGetRunLoopSource(notificationPort); CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode); - + memset(notificationIterators, 0, sizeof(notificationIterators)); //* loop through all supported vendor/product pairs @@ -86,19 +86,19 @@ InitUSB() //* Create our matching dictionary to find the Android device //* IOServiceAddMatchingNotification consumes the reference, so we do not need to release this matchingDict = IOServiceMatching(kIOUSBDeviceClassName); - + if (!matchingDict) { DBG("ERR: Couldn't create USB matching dictionary.\n"); return -1; } - + //* Set up two matching dictionaries, one for each product ID we support. //* This will cause the kernel to notify us only if the vendor and product IDs match. vendor = kSupportedDevices[i].vid; product = kSupportedDevices[i].pid; - CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vendor)); - CFDictionarySetValue(matchingDict, CFSTR(kUSBProductID), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &product)); - + CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vendor)); + CFDictionarySetValue(matchingDict, CFSTR(kUSBProductID), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &product)); + //* Now set up two notifications: one to be called when a raw device //* is first matched by the I/O Kit and another to be called when the //* device is terminated. @@ -110,7 +110,7 @@ InitUSB() AndroidDeviceAdded, NULL, ¬ificationIterators[i]); - + //* Iterate over set of matching devices to access already-present devices //* and to arm the notification AndroidDeviceAdded(NULL, notificationIterators[i]); @@ -173,7 +173,7 @@ AndroidDeviceAdded(void *refCon, io_iterator_t iterator) if (kr == kIOReturnSuccess && req.wLenDone > 0) { int i, count; - + // skip first word, and copy the rest to the serial string, changing shorts to bytes. count = (req.wLenDone - 1) / 2; for (i = 0; i < count; i++) @@ -401,8 +401,8 @@ void* RunLoopThread(void* unused) currentRunLoop = 0; for (i = 0; i < kSupportedDeviceCount; i++) { - IOObjectRelease(notificationIterators[i]); - } + IOObjectRelease(notificationIterators[i]); + } IONotificationPortDestroy(notificationPort); DBG("RunLoopThread done\n"); diff --git a/adb/usb_windows.c b/adb/usb_windows.c index 5b0f11fa6..7ddaa0c18 100644 --- a/adb/usb_windows.c +++ b/adb/usb_windows.c @@ -308,11 +308,11 @@ int usb_read(usb_handle *handle, void* data, int len) { while (len > 0) { int xfer = (len > 4096) ? 4096 : len; - ret = AdbReadEndpointSync(handle->adb_read_pipe, - (void*)data, - (unsigned long)xfer, - &read, - time_out); + ret = AdbReadEndpointSync(handle->adb_read_pipe, + (void*)data, + (unsigned long)xfer, + &read, + time_out); errno = GetLastError(); D("usb_write got: %ld, expected: %d, errno: %d\n", read, xfer, errno); if (ret) { @@ -475,11 +475,11 @@ void find_devices() { // Lets see if we already have this device in the list if (!known_device(interf_name)) { // This seems to be a new device. Open it! - handle = do_usb_open(next_interface->device_name); - if (NULL != handle) { + handle = do_usb_open(next_interface->device_name); + if (NULL != handle) { // Lets see if this interface (device) belongs to us if (recognized_device(handle)) { - D("adding a new device %s\n", interf_name); + D("adding a new device %s\n", interf_name); char serial_number[512]; unsigned long serial_number_len = sizeof(serial_number); if (AdbGetSerialNumber(handle->adb_interface, @@ -488,7 +488,7 @@ void find_devices() { true)) { // Lets make sure that we don't duplicate this device if (register_new_device(handle)) { - register_usb_transport(handle, serial_number); + register_usb_transport(handle, serial_number); } else { D("register_new_device failed for %s\n", interf_name); usb_cleanup_handle(handle); diff --git a/adb/utils.c b/adb/utils.c new file mode 100644 index 000000000..91518bab6 --- /dev/null +++ b/adb/utils.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "utils.h" +#include +#include +#include + +char* +buff_addc (char* buff, char* buffEnd, int c) +{ + int avail = buffEnd - buff; + + if (avail <= 0) /* already in overflow mode */ + return buff; + + if (avail == 1) { /* overflowing, the last byte is reserved for zero */ + buff[0] = 0; + return buff + 1; + } + + buff[0] = (char) c; /* add char and terminating zero */ + buff[1] = 0; + return buff + 1; +} + +char* +buff_adds (char* buff, char* buffEnd, const char* s) +{ + int slen = strlen(s); + + return buff_addb(buff, buffEnd, s, slen); +} + +char* +buff_addb (char* buff, char* buffEnd, const void* data, int len) +{ + int avail = (buffEnd - buff); + + if (avail <= 0 || len <= 0) /* already overflowing */ + return buff; + + if (len > avail) + len = avail; + + memcpy(buff, data, len); + + buff += len; + + /* ensure there is a terminating zero */ + if (buff >= buffEnd) { /* overflow */ + buff[-1] = 0; + } else + buff[0] = 0; + + return buff; +} + +char* +buff_add (char* buff, char* buffEnd, const char* format, ... ) +{ + int avail; + + avail = (buffEnd - buff); + + if (avail > 0) { + va_list args; + int nn; + + va_start(args, format); + nn = vsnprintf( buff, avail, format, args); + va_end(args); + + if (nn < 0) { + /* some C libraries return -1 in case of overflow, + * but they will also do that if the format spec is + * invalid. We assume ADB is not buggy enough to + * trigger that last case. */ + nn = avail; + } + else if (nn > avail) { + nn = avail; + } + + buff += nn; + + /* ensure that there is a terminating zero */ + if (buff >= buffEnd) + buff[-1] = 0; + else + buff[0] = 0; + } + return buff; +} diff --git a/adb/utils.h b/adb/utils.h new file mode 100644 index 000000000..f70ecd24d --- /dev/null +++ b/adb/utils.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _ADB_UTILS_H +#define _ADB_UTILS_H + +/* bounded buffer functions */ + +/* all these functions are used to append data to a bounded buffer. + * + * after each operation, the buffer is guaranteed to be zero-terminated, + * even in the case of an overflow. they all return the new buffer position + * which allows one to use them in succession, only checking for overflows + * at the end. For example: + * + * BUFF_DECL(temp,p,end,1024); + * char* p; + * + * p = buff_addc(temp, end, '"'); + * p = buff_adds(temp, end, string); + * p = buff_addc(temp, end, '"'); + * + * if (p >= end) { + * overflow detected. note that 'temp' is + * zero-terminated for safety. + * } + * return strdup(temp); + */ + +/* tries to add a character to the buffer, in case of overflow + * this will only write a terminating zero and return buffEnd. + */ +char* buff_addc (char* buff, char* buffEnd, int c); + +/* tries to add a string to the buffer */ +char* buff_adds (char* buff, char* buffEnd, const char* s); + +/* tries to add a bytes to the buffer. the input can contain zero bytes, + * but a terminating zero will always be appended at the end anyway + */ +char* buff_addb (char* buff, char* buffEnd, const void* data, int len); + +/* tries to add a formatted string to a bounded buffer */ +char* buff_add (char* buff, char* buffEnd, const char* format, ... ); + +/* convenience macro used to define a bounded buffer, as well as + * a 'cursor' and 'end' variables all in one go. + * + * note: this doesn't place an initial terminating zero in the buffer, + * you need to use one of the buff_ functions for this. or simply + * do _cursor[0] = 0 manually. + */ +#define BUFF_DECL(_buff,_cursor,_end,_size) \ + char _buff[_size], *_cursor=_buff, *_end = _cursor + (_size) + +#endif /* _ADB_UTILS_H */ diff --git a/cpio/Android.mk b/cpio/Android.mk index f340fd69a..8d01852b0 100644 --- a/cpio/Android.mk +++ b/cpio/Android.mk @@ -10,4 +10,4 @@ LOCAL_MODULE := mkbootfs include $(BUILD_HOST_EXECUTABLE) -$(call dist-for-goals,user userdebug droid,$(LOCAL_BUILT_MODULE)) +$(call dist-for-goals,droid,$(LOCAL_BUILT_MODULE)) diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk index 03046bd5e..b22e1a81d 100644 --- a/debuggerd/Android.mk +++ b/debuggerd/Android.mk @@ -1,5 +1,7 @@ # Copyright 2005 The Android Open Source Project +ifeq ($(TARGET_ARCH),arm) + LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) @@ -20,3 +22,5 @@ LOCAL_MODULE_TAGS := eng #LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_SHARED_LIBRARIES := libcutils libc include $(BUILD_EXECUTABLE) + +endif # TARGET_ARCH == arm diff --git a/debuggerd/unwind-arm.c b/debuggerd/unwind-arm.c index fdc0a6aa6..9642d2e4a 100644 --- a/debuggerd/unwind-arm.c +++ b/debuggerd/unwind-arm.c @@ -381,13 +381,15 @@ get_eit_entry (_Unwind_Control_Block *ucbp, _uw return_address, pid_t pid, } /* Print out the current call level, pc, and module name in the crash log */ -static _Unwind_Reason_Code log_function(_Unwind_Context *context, int tfd, +static _Unwind_Reason_Code log_function(_Unwind_Context *context, pid_t pid, + int tfd, int stack_level, mapinfo *map, unsigned int sp_list[], bool at_fault) { _uw pc; + _uw rel_pc; phase2_vrs *vrs = (phase2_vrs*) context; const mapinfo *mi; bool only_in_tombstone = !at_fault; @@ -404,19 +406,53 @@ static _Unwind_Reason_Code log_function(_Unwind_Context *context, int tfd, // For deeper framers, rollback pc by one instruction else { pc = vrs->core.r[R_PC]; - // Thumb mode + /* Thumb mode - need to check whether the bl(x) has long offset or not. + * Examples: + * + * arm blx in the middle of thumb: + * 187ae: 2300 movs r3, #0 + * 187b0: f7fe ee1c blx 173ec + * 187b4: 2c00 cmp r4, #0 + * + * arm bl in the middle of thumb: + * 187d8: 1c20 adds r0, r4, #0 + * 187da: f136 fd15 bl 14f208 + * 187de: 2800 cmp r0, #0 + * + * pure thumb: + * 18894: 189b adds r3, r3, r2 + * 18896: 4798 blx r3 + * 18898: b001 add sp, #4 + */ if (pc & 1) { - pc = (pc & ~1) - 2; + _uw prev_word; + pc = (pc & ~1); + prev_word = get_remote_word(pid, (void *) pc-4); + // Long offset + if ((prev_word & 0xf0000000) == 0xf0000000 && + (prev_word & 0x0000e000) == 0x0000e000) { + pc -= 4; + } + else { + pc -= 2; + } } else { pc -= 4; } } - mi = pc_to_mapinfo(map, pc); + /* We used to print the absolute PC in the back trace, and mask out the top + * 3 bits to guesstimate the offset in the .so file. This is not working for + * non-prelinked libraries since the starting offset may not be aligned on + * 1MB boundaries, and the library may be larger than 1MB. So for .so + * addresses we print the relative offset in back trace. + */ + rel_pc = pc; + mi = pc_to_mapinfo(map, pc, &rel_pc); _LOG(tfd, only_in_tombstone, - " #%02d pc %08x %s\n", stack_level, pc, + " #%02d pc %08x %s\n", stack_level, rel_pc, mi ? mi->name : ""); return _URC_NO_REASON; @@ -459,7 +495,7 @@ int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map, */ if (get_eitp(saved_vrs.core.r[R_PC], pid, map, NULL) == NULL) { *frame0_pc_sane = 0; - log_function ((_Unwind_Context *) &saved_vrs, tfd, stack_level, + log_function ((_Unwind_Context *) &saved_vrs, pid, tfd, stack_level, map, sp_list, at_fault); saved_vrs.core.r[R_PC] = saved_vrs.core.r[R_LR]; stack_level++; @@ -493,7 +529,7 @@ int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map, _Unwind_SetGR((_Unwind_Context *)&saved_vrs, 12, (_Unwind_Ptr) ucbp); /* Call log function. */ - if (log_function ((_Unwind_Context *) &saved_vrs, tfd, stack_level, + if (log_function ((_Unwind_Context *) &saved_vrs, pid, tfd, stack_level, map, sp_list, at_fault) != _URC_NO_REASON) { code = _URC_FAILURE; break; diff --git a/debuggerd/utility.c b/debuggerd/utility.c index 0cb790fd6..8f3931c1f 100644 --- a/debuggerd/utility.c +++ b/debuggerd/utility.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "utility.h" @@ -66,10 +67,14 @@ const char *map_to_name(mapinfo *mi, unsigned pc, const char* def) } /* Find the containing map info for the pc */ -const mapinfo *pc_to_mapinfo(mapinfo *mi, unsigned pc) +const mapinfo *pc_to_mapinfo(mapinfo *mi, unsigned pc, unsigned *rel_pc) { while(mi) { if((pc >= mi->start) && (pc < mi->end)){ + // Only calculate the relative offset for shared libraries + if (strstr(mi->name, ".so")) { + *rel_pc = pc - mi->start; + } return mi; } mi = mi->next; diff --git a/debuggerd/utility.h b/debuggerd/utility.h index 802e3ad2e..49f59518e 100644 --- a/debuggerd/utility.h +++ b/debuggerd/utility.h @@ -45,7 +45,7 @@ extern int get_remote_word(int pid, void *src); extern void get_remote_struct(int pid, void *src, void *dst, size_t size); /* Find the containing map for the pc */ -const mapinfo *pc_to_mapinfo (mapinfo *mi, unsigned pc); +const mapinfo *pc_to_mapinfo (mapinfo *mi, unsigned pc, unsigned *rel_pc); /* Map a pc address to the name of the containing ELF file */ const char *map_to_name(mapinfo *mi, unsigned pc, const char* def); diff --git a/fastboot/Android.mk b/fastboot/Android.mk index 5e9941de9..7a9d35f28 100644 --- a/fastboot/Android.mk +++ b/fastboot/Android.mk @@ -43,7 +43,7 @@ endif LOCAL_STATIC_LIBRARIES := $(EXTRA_STATIC_LIBS) libzipfile libunz include $(BUILD_HOST_EXECUTABLE) -$(call dist-for-goals,user userdebug droid,$(LOCAL_BUILT_MODULE)) +$(call dist-for-goals,droid,$(LOCAL_BUILT_MODULE)) ifeq ($(HOST_OS),linux) include $(CLEAR_VARS) diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c index 64a4045c5..4079a3894 100644 --- a/fastboot/fastboot.c +++ b/fastboot/fastboot.c @@ -47,6 +47,7 @@ static const char *serial = 0; static const char *product = 0; static const char *cmdline = 0; static int wipe_data = 0; +static unsigned short vendor_id = 0; void die(const char *fmt, ...) { @@ -135,7 +136,8 @@ oops: int match_fastboot(usb_ifc_info *info) { - if((info->dev_vendor != 0x18d1) && + if(!(vendor_id && (info->dev_vendor == vendor_id)) && + (info->dev_vendor != 0x18d1) && (info->dev_vendor != 0x0bb4)) return -1; if(info->ifc_class != 0xff) return -1; if(info->ifc_subclass != 0x42) return -1; @@ -208,6 +210,7 @@ void usage(void) " -s specify device serial number\n" " -p specify product name\n" " -c override kernel commandline\n" + " -i specify a custom USB vendor id\n" ); exit(1); } @@ -553,6 +556,16 @@ int main(int argc, char **argv) require(2); cmdline = argv[1]; skip(2); + } else if(!strcmp(*argv, "-i")) { + char *endptr = NULL; + unsigned long val; + + require(2); + val = strtoul(argv[1], &endptr, 0); + if (!endptr || *endptr != '\0' || (val & ~0xffff)) + die("invalid vendor id '%s'", argv[1]); + vendor_id = (unsigned short)val; + skip(2); } else if(!strcmp(*argv, "getvar")) { require(2); fb_queue_display(argv[1], argv[1]); diff --git a/include/arch/linux-arm/AndroidConfig.h b/include/arch/linux-arm/AndroidConfig.h index d7e182a93..b2dabf210 100644 --- a/include/arch/linux-arm/AndroidConfig.h +++ b/include/arch/linux-arm/AndroidConfig.h @@ -47,6 +47,12 @@ #define HAVE_FUTEX +/* + * Define if we already have the futex wrapper functions defined. Yes if + * compiling against bionic. + */ +#define HAVE_FUTEX_WRAPPERS 1 + /* * Process creation model. Choose one: * diff --git a/include/arch/target_linux-x86/AndroidConfig.h b/include/arch/target_linux-x86/AndroidConfig.h new file mode 100644 index 000000000..4aa44f883 --- /dev/null +++ b/include/arch/target_linux-x86/AndroidConfig.h @@ -0,0 +1,296 @@ +/* + * Copyright 2005 The Android Open Source Project + * + * Android config -- "target_linux-x86". Used for x86 linux target devices. + */ +#ifndef _ANDROID_CONFIG_H +#define _ANDROID_CONFIG_H + +/* + * =========================================================================== + * !!! IMPORTANT !!! + * =========================================================================== + * + * This file is included by ALL C/C++ source files. Don't put anything in + * here unless you are absolutely certain it can't go anywhere else. + * + * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" + * comments. + */ + +/* + * Threading model. Choose one: + * + * HAVE_PTHREADS - use the pthreads library. + * HAVE_WIN32_THREADS - use Win32 thread primitives. + * -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX + */ +#define HAVE_PTHREADS + +/* + * Do we have the futex syscall? + */ + +#define HAVE_FUTEX + +/* + * Define if we already have the futex wrapper functions defined. Yes if + * compiling against bionic. + */ +#define HAVE_FUTEX_WRAPPERS 1 + +/* + * Process creation model. Choose one: + * + * HAVE_FORKEXEC - use fork() and exec() + * HAVE_WIN32_PROC - use CreateProcess() + */ +#define HAVE_FORKEXEC + +/* + * Process out-of-memory adjustment. Set if running on Linux, + * where we can write to /proc//oom_adj to modify the out-of-memory + * badness adjustment. + */ +#define HAVE_OOM_ADJ + +/* + * IPC model. Choose one: + * + * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). + * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). + * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). + * HAVE_ANDROID_IPC - use Android versions (?, mmap). + */ +#define HAVE_ANDROID_IPC 1 + +/* + * Memory-mapping model. Choose one: + * + * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h + * HAVE_WIN32_FILEMAP - use Win32 filemaps + */ +#define HAVE_POSIX_FILEMAP 1 + +/* + * Define this if you have + */ +#define HAVE_TERMIO_H 1 + +/* + * Define this if you build against have Microsoft C runtime (MSVCRT.DLL) + */ +/* #define HAVE_MS_C_RUNTIME */ + +/* + * Define this if you have sys/uio.h + */ +#define HAVE_SYS_UIO_H 1 + +/* + * Define this if your platforms implements symbolic links + * in its filesystems + */ +#define HAVE_SYMLINKS 1 + +/* + * Define this if we have localtime_r(). + */ +/* #define HAVE_LOCALTIME_R */ + +/* + * Define this if we have gethostbyname_r(). + */ +/* #define HAVE_GETHOSTBYNAME_R */ + +/* + * Define this if we have ioctl(). + */ +#define HAVE_IOCTL + +/* + * Define this if we want to use WinSock. + */ +/* #define HAVE_WINSOCK */ + +/* + * Define this if have clock_gettime() and friends + * + */ +#define HAVE_POSIX_CLOCKS + +/* + * Define this if we have pthread_cond_timedwait_monotonic() and + * clock_gettime(CLOCK_MONOTONIC). + */ +#define HAVE_TIMEDWAIT_MONOTONIC + +/* + * Define this if we have linux style epoll() + */ +#define HAVE_EPOLL + +/* + * Endianness of the target machine. Choose one: + * + * HAVE_ENDIAN_H -- have endian.h header we can include. + * HAVE_LITTLE_ENDIAN -- we are little endian. + * HAVE_BIG_ENDIAN -- we are big endian. + */ +#define HAVE_ENDIAN_H +#define HAVE_LITTLE_ENDIAN + +/* + * We need to choose between 32-bit and 64-bit off_t. All of our code should + * agree on the same size. For desktop systems, use 64-bit values, + * because some of our libraries (e.g. wxWidgets) expect to be built that way. + */ +/* + * #define _FILE_OFFSET_BITS 64 + * #define _LARGEFILE_SOURCE 1 + */ + +/* + * Defined if we have the backtrace() call for retrieving a stack trace. + * Needed for CallStack to operate; if not defined, CallStack is + * non-functional. + */ +#define HAVE_BACKTRACE 0 + +/* + * Defined if we have the dladdr() call for retrieving the symbol associated + * with a memory address. If not defined, stack crawls will not have symbolic + * information. + */ +#define HAVE_DLADDR 0 + +/* + * Defined if we have the cxxabi.h header for demangling C++ symbols. If + * not defined, stack crawls will be displayed with raw mangled symbols + */ +#define HAVE_CXXABI 0 + +/* + * Defined if we have the gettid() system call. + */ +#define HAVE_GETTID + +/* + * Defined if we have the sched_setscheduler() call + */ +#define HAVE_SCHED_SETSCHEDULER + +/* + * Add any extra platform-specific defines here. + */ +#ifndef __linux__ +#define __linux__ +#endif + +/* + * Define if we have header + */ +#define HAVE_MALLOC_H + +/* + * Define if we're running on *our* linux on device or emulator. + */ +#define HAVE_ANDROID_OS 1 + +/* + * Define if we have Linux-style non-filesystem Unix Domain Sockets + */ +#define HAVE_LINUX_LOCAL_SOCKET_NAMESPACE 1 + +/* + * Define if we have Linux's inotify in . + */ +#define HAVE_INOTIFY 1 + +/* + * Define if we have madvise() in + */ +#define HAVE_MADVISE 1 + +/* + * Define if we have Linux's dbus + */ +#define HAVE_DBUS 1 + +/* + * Define if tm struct has tm_gmtoff field + */ +#define HAVE_TM_GMTOFF 1 + +/* + * Define if dirent struct has d_type field + */ +#define HAVE_DIRENT_D_TYPE 1 + +/* + * Define if libc includes Android system properties implementation. + */ +#define HAVE_LIBC_SYSTEM_PROPERTIES 1 + +/* + * Define if system provides a system property server (should be + * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES). + */ +/* #define HAVE_SYSTEM_PROPERTY_SERVER */ + +/* + * What CPU architecture does this platform use? + */ +#define ARCH_X86 + +/* + * sprintf() format string for shared library naming. + */ +#define OS_SHARED_LIB_FORMAT_STR "lib%s.so" + +/* + * Do we have __memcmp16()? + */ +/* #define HAVE__MEMCMP16 1 */ + +/* + * type for the third argument to mincore(). + */ +#define MINCORE_POINTER_TYPE unsigned char * + +/* + * Do we have the sigaction flag SA_NOCLDWAIT? + */ +#define HAVE_SA_NOCLDWAIT + +/* + * The default path separator for the platform + */ +#define OS_PATH_SEPARATOR '/' + +/* + * Is the filesystem case sensitive? + */ +#define OS_CASE_SENSITIVE + +/* + * Define if exists. + */ +#define HAVE_SYS_SOCKET_H 1 + +/* + * Define if the strlcpy() function exists on the system. + */ +#define HAVE_STRLCPY 1 + +/* + * Define if prctl() exists + */ +#define HAVE_PRCTL 1 + +/* + * Whether or not _Unwind_Context is defined as a struct. + */ +#define HAVE_UNWIND_CONTEXT_STRUCT + +#endif /* _ANDROID_CONFIG_H */ diff --git a/include/cutils/tztime.h b/include/cutils/tztime.h index 59c0670d6..9b3ece84c 100644 --- a/include/cutils/tztime.h +++ b/include/cutils/tztime.h @@ -24,6 +24,21 @@ extern "C" { time_t mktime_tz(struct tm * const tmp, char const * tz); void localtime_tz(const time_t * const timep, struct tm * tmp, const char* tz); +struct strftime_locale { + const char *mon[12]; /* short names */ + const char *month[12]; /* long names */ + const char *wday[7]; /* short names */ + const char *weekday[7]; /* long names */ + const char *X_fmt; + const char *x_fmt; + const char *c_fmt; + const char *am; + const char *pm; + const char *date_fmt; +}; + +size_t strftime_tz(char *s, size_t max, const char *format, const struct tm *tm, const struct strftime_locale *locale); + #ifdef __cplusplus } #endif diff --git a/include/pixelflinger/format.h b/include/pixelflinger/format.h index ad0d29d84..308e56002 100644 --- a/include/pixelflinger/format.h +++ b/include/pixelflinger/format.h @@ -30,6 +30,7 @@ enum GGLPixelFormat { GGL_PIXEL_FORMAT_RGBX_8888 = 2, // 3x8-bit RGB stored in 32-bit chunks GGL_PIXEL_FORMAT_RGB_888 = 3, // 3x8-bit RGB GGL_PIXEL_FORMAT_RGB_565 = 4, // 16-bit RGB + GGL_PIXEL_FORMAT_BGRA_8888 = 5, // 4x8-bit BGRA GGL_PIXEL_FORMAT_RGBA_5551 = 6, // 16-bit RGBA GGL_PIXEL_FORMAT_RGBA_4444 = 7, // 16-bit RGBA @@ -38,9 +39,11 @@ enum GGLPixelFormat { GGL_PIXEL_FORMAT_LA_88 = 0xA, // 16-bit LA GGL_PIXEL_FORMAT_RGB_332 = 0xB, // 8-bit RGB (non paletted) - // YCbCr formats + // YCbCr formats (SP=semi-planar, P=planar) GGL_PIXEL_FORMAT_YCbCr_422_SP= 0x10, GGL_PIXEL_FORMAT_YCbCr_420_SP= 0x11, + GGL_PIXEL_FORMAT_YCbCr_422_P = 0x14, + GGL_PIXEL_FORMAT_YCbCr_420_P = 0x15, // reserved/special formats GGL_PIXEL_FORMAT_Z_16 = 0x18, diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h index 13e134bba..c8551872a 100644 --- a/include/private/android_filesystem_config.h +++ b/include/private/android_filesystem_config.h @@ -150,8 +150,10 @@ static struct fs_path_config android_files[] = { { 00550, AID_ROOT, AID_SHELL, "system/etc/init.testmenu" }, { 00550, AID_ROOT, AID_SHELL, "system/etc/init.gprs-pppd" }, { 00550, AID_DHCP, AID_SHELL, "system/etc/dhcpcd/dhcpcd-run-hooks" }, - { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/hcid.conf" }, { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/dbus.conf" }, + { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluez/hcid.conf" }, + { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluez/input.conf" }, + { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluez/audio.conf" }, { 00440, AID_RADIO, AID_AUDIO, "/system/etc/AudioPara4.csv" }, { 00644, AID_SYSTEM, AID_SYSTEM, "data/app/*" }, { 00644, AID_SYSTEM, AID_SYSTEM, "data/app-private/*" }, diff --git a/include/private/pixelflinger/ggl_context.h b/include/private/pixelflinger/ggl_context.h index 241a0abdb..3a030c526 100644 --- a/include/private/pixelflinger/ggl_context.h +++ b/include/private/pixelflinger/ggl_context.h @@ -147,11 +147,11 @@ GGL_RESERVE_NEEDS( P_FOG, 9, 1 ) GGL_RESERVE_NEEDS( P_RESERVED1, 10,22 ) GGL_RESERVE_NEEDS( T_FORMAT, 0, 6 ) -GGL_RESERVE_NEEDS( T_RESERVED0, 6, 2 ) +GGL_RESERVE_NEEDS( T_RESERVED0, 6, 1 ) +GGL_RESERVE_NEEDS( T_POT, 7, 1 ) GGL_RESERVE_NEEDS( T_S_WRAP, 8, 2 ) GGL_RESERVE_NEEDS( T_T_WRAP, 10, 2 ) -GGL_RESERVE_NEEDS( T_ENV, 12, 2 ) -GGL_RESERVE_NEEDS( T_POT, 14, 1 ) +GGL_RESERVE_NEEDS( T_ENV, 12, 3 ) GGL_RESERVE_NEEDS( T_LINEAR, 15, 1 ) const int GGL_NEEDS_WRAP_CLAMP_TO_EDGE = 0; @@ -182,12 +182,14 @@ inline uint32_t ggl_env_to_needs(uint32_t e) { case GGL_MODULATE: return 1; case GGL_DECAL: return 2; case GGL_BLEND: return 3; + case GGL_ADD: return 4; } return 0; } inline uint32_t ggl_needs_to_env(uint32_t n) { - const uint32_t envs[] = { GGL_REPLACE, GGL_MODULATE, GGL_DECAL, GGL_BLEND }; + const uint32_t envs[] = { GGL_REPLACE, GGL_MODULATE, + GGL_DECAL, GGL_BLEND, GGL_ADD }; return envs[n]; } diff --git a/init/README.BOOTCHART b/init/README.BOOTCHART index 489936973..70cf2c39b 100644 --- a/init/README.BOOTCHART +++ b/init/README.BOOTCHART @@ -1,32 +1,50 @@ -this version of init contains code to perform "bootcharting", i.e. generating log +This version of init contains code to perform "bootcharting", i.e. generating log files that can be later processed by the tools provided by www.bootchart.org. -to activate it, you need to define build 'init' with the INIT_BOOTCHART environment -variable defined to 'true', then create a file on the /data partition with a command -like the following: +To activate it, you need to define build 'init' with the INIT_BOOTCHART environment +variable defined to 'true', for example: - adb shell 'echo 1 > /data/bootchart' + touch system/init/init.c + m INIT_BOOTCHART=true -if the '/data/bootchart' file doesn't exist, or doesn't contain a '1' in its first -byte, init will proceed normally. +On the emulator, use the new -bootchart option to boot with bootcharting +activated for seconds. -by default, the bootchart log stops after 2 minutes, but you can stop it earlier -with the following command while the device is booting: +Otherwise, flash your device, and start it. Then create a file on the /data partition +with a command like the following: + + adb shell 'echo $TIMEOUT > /data/bootchart-start' + +Where the value of $TIMEOUT corresponds to the wanted bootcharted period in seconds; +for example, to bootchart for 2 minutes, do: + + adb shell 'echo 120 > /data/bootchart-start' + +Reboot your device, bootcharting will begin and stop after the period you gave. +You can also stop the bootcharting at any moment by doing the following: adb shell 'echo 1 > /data/bootchart-stop' -note that /data/bootchart-stop is deleted automatically by init at the end of the -bootcharting. this is not the case of /data/bootchart, so don't forget to delete it +Note that /data/bootchart-stop is deleted automatically by init at the end of the +bootcharting. This is not the case of /data/bootchart-start, so don't forget to delete it when you're done collecting data: - adb shell rm /data/bootchart + adb shell rm /data/bootchart-start -the log files are placed in /tmp/bootchart/. you must run the script tools/grab-bootchart.sh +The log files are placed in /data/bootchart/. you must run the script tools/grab-bootchart.sh which will use ADB to retrieve them and create a bootchart.tgz file that can be used with the bootchart parser/renderer, or even uploaded directly to the form located at: http://www.bootchart.org/download.html +NOTE: the bootchart.org webform doesn't seem to work at the moment, you can generate an + image on your machine by doing the following: + + 1/ download the sources from www.bootchart.org + 2/ unpack them + 3/ in the source directory, type 'ant' to build the bootchart program + 4/ type 'java -jar bootchart.jar /path/to/bootchart.tgz + technical note: this implementation of bootcharting does use the 'bootchartd' script provided by diff --git a/init/bootchart.c b/init/bootchart.c index 2afe98b6b..f72fcaaca 100644 --- a/init/bootchart.c +++ b/init/bootchart.c @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + /* this code is used to generate a boot sequence profile that can be used * with the 'bootchart' graphics generation tool. see www.bootchart.org * note that unlike the original bootchartd, this is not a Bash script but @@ -16,17 +32,18 @@ #include #include #include +#include "bootchart.h" #define VERSION "0.8" #define SAMPLE_PERIOD 0.2 -#define LOG_ROOT "/tmp/bootchart" +#define LOG_ROOT "/data/bootchart" #define LOG_STAT LOG_ROOT"/proc_stat.log" #define LOG_PROCS LOG_ROOT"/proc_ps.log" #define LOG_DISK LOG_ROOT"/proc_diskstats.log" #define LOG_HEADER LOG_ROOT"/header" #define LOG_ACCT LOG_ROOT"/kernel_pacct" -#define LOG_STARTFILE "/data/bootchart" +#define LOG_STARTFILE "/data/bootchart-start" #define LOG_STOPFILE "/data/bootchart-stop" static int @@ -54,12 +71,11 @@ proc_read(const char* filename, char* buff, size_t buffsize) len = unix_read(fd, buff, buffsize-1); close(fd); } - buff[len] = 0; + buff[len > 0 ? len : 0] = 0; return len; } #define FILE_BUFF_SIZE 65536 -#define FILE_BUFF_RESERVE (FILE_BUFF_SIZE - 4096) typedef struct { int count; @@ -81,7 +97,7 @@ file_buff_write( FileBuff buff, const void* src, int len ) int avail = sizeof(buff->data) - buff->count; if (avail > len) avail = len; - + memcpy( buff->data + buff->count, src, avail ); len -= avail; src = (char*)src + avail; @@ -115,7 +131,7 @@ log_header(void) time_t now_t = time(NULL); struct tm now = *localtime(&now_t); strftime(date, sizeof(date), "%x %X", &now); - + out = fopen( LOG_HEADER, "w" ); if (out == NULL) return; @@ -123,7 +139,7 @@ log_header(void) proc_read("/proc/cmdline", cmdline, sizeof(cmdline)); proc_read("/proc/version", uname, sizeof(uname)); proc_read("/proc/cpuinfo", cpuinfo, sizeof(cpuinfo)); - + cpu = strchr( cpuinfo, ':' ); if (cpu) { char* p = strchr(cpu, '\n'); @@ -131,7 +147,7 @@ log_header(void) if (p) *p = 0; } - + fprintf(out, "version = %s\n", VERSION); fprintf(out, "title = Boot chart for Android ( %s )\n", date); fprintf(out, "system.uname = %s\n", uname); @@ -174,7 +190,6 @@ do_log_uptime(FileBuff log) fd = open("/proc/uptime",O_RDONLY); if (fd >= 0) { int ret; - close_on_exec(fd); ret = unix_read(fd, buff, 64); close(fd); buff[64] = 0; @@ -212,7 +227,7 @@ do_log_file(FileBuff log, const char* procfile) ret = unix_read(fd, buff, sizeof(buff)); if (ret <= 0) break; - + file_buff_write(log, buff, ret); if (ret < (int)sizeof(buff)) break; @@ -230,7 +245,7 @@ do_log_procs(FileBuff log) struct dirent* entry; do_log_uptime(log); - + while ((entry = readdir(dir)) != NULL) { /* only match numeric values */ char* end; @@ -241,7 +256,7 @@ do_log_procs(FileBuff log) char cmdline[1024]; int len; int fd; - + /* read command line and extract program name */ snprintf(filename,sizeof(filename),"/proc/%d/cmdline",pid); proc_read(filename, cmdline, sizeof(cmdline)); @@ -285,11 +300,36 @@ int bootchart_init( void ) { int ret; char buff[4]; - + int timeout = 0, count = 0; + buff[0] = 0; proc_read( LOG_STARTFILE, buff, sizeof(buff) ); - if (buff[0] != '1') - return -1; + if (buff[0] != 0) { + timeout = atoi(buff); + } + else { + /* when running with emulator, androidboot.bootchart= + * might be passed by as kernel parameters to specify the bootchart + * timeout. this is useful when using -wipe-data since the /data + * partition is fresh + */ + char cmdline[1024]; + char* s; +#define KERNEL_OPTION "androidboot.bootchart=" + proc_read( "/proc/cmdline", cmdline, sizeof(cmdline) ); + s = strstr(cmdline, KERNEL_OPTION); + if (s) { + s += sizeof(KERNEL_OPTION)-1; + timeout = atoi(s); + } + } + if (timeout == 0) + return 0; + + if (timeout > BOOTCHART_MAX_TIME_SEC) + timeout = BOOTCHART_MAX_TIME_SEC; + + count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS; do {ret=mkdir(LOG_ROOT,0755);}while (ret < 0 && errno == EINTR); @@ -307,7 +347,7 @@ int bootchart_init( void ) } log_header(); - return 0; + return count; } /* called each time you want to perform a bootchart sampling op */ @@ -324,6 +364,7 @@ int bootchart_step( void ) return -1; } } + return 0; } diff --git a/init/bootchart.h b/init/bootchart.h new file mode 100644 index 000000000..39d2d4f20 --- /dev/null +++ b/init/bootchart.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _BOOTCHART_H +#define _BOOTCHART_H + +#ifndef BOOTCHART +# define BOOTCHART 0 +#endif + +#if BOOTCHART + +extern int bootchart_init(void); +extern int bootchart_step(void); +extern void bootchart_finish(void); + +# define BOOTCHART_POLLING_MS 200 /* polling period in ms */ +# define BOOTCHART_DEFAULT_TIME_SEC (2*60) /* default polling time in seconds */ +# define BOOTCHART_MAX_TIME_SEC (10*60) /* max polling time in seconds */ + +#endif /* BOOTCHART */ + +#endif /* _BOOTCHART_H */ diff --git a/init/builtins.c b/init/builtins.c index bbee7b2d0..bcdfee1e5 100644 --- a/init/builtins.c +++ b/init/builtins.c @@ -65,7 +65,7 @@ static int write_file(const char *path, const char *value) } } -static int insmod(const char *filename) +static int insmod(const char *filename, char *options) { void *module; unsigned size; @@ -75,7 +75,7 @@ static int insmod(const char *filename) if (!module) return -1; - ret = init_module(module, size, ""); + ret = init_module(module, size, options); free(module); @@ -173,9 +173,35 @@ int do_ifup(int nargs, char **args) return __ifupdown(args[1], 1); } + +static int do_insmod_inner(int nargs, char **args, int opt_len) +{ + char options[opt_len + 1]; + int i; + + options[0] = '\0'; + if (nargs > 2) { + strcpy(options, args[2]); + for (i = 3; i < nargs; ++i) { + strcat(options, " "); + strcat(options, args[i]); + } + } + + return insmod(args[1], options); +} + int do_insmod(int nargs, char **args) { - return insmod(args[1]); + int i; + int size = 0; + + if (nargs > 2) { + for (i = 2; i < nargs; ++i) + size += strlen(args[i]) + 1; + } + + return do_insmod_inner(nargs, args, size); } int do_import(int nargs, char **args) @@ -382,6 +408,20 @@ int do_symlink(int nargs, char **args) return symlink(args[1], args[2]); } +int do_sysclktz(int nargs, char **args) +{ + struct timezone tz; + + if (nargs != 2) + return -1; + + memset(&tz, 0, sizeof(tz)); + tz.tz_minuteswest = atoi(args[1]); + if (settimeofday(NULL, &tz)) + return -1; + return 0; +} + int do_write(int nargs, char **args) { return write_file(args[1], args[2]); diff --git a/init/devices.c b/init/devices.c index 35cd30db4..3cb1cb821 100644 --- a/init/devices.c +++ b/init/devices.c @@ -98,9 +98,12 @@ static struct perms_ devperms[] = { /* these should not be world writable */ { "/dev/android_adb", 0660, AID_ADB, AID_ADB, 0 }, { "/dev/android_adb_enable", 0660, AID_ADB, AID_ADB, 0 }, - { "/dev/ttyMSM0", 0660, AID_BLUETOOTH, AID_BLUETOOTH, 0 }, + /* TODO: remove legacy ttyMSM0 */ + { "/dev/ttyMSM0", 0600, AID_BLUETOOTH, AID_BLUETOOTH, 0 }, + { "/dev/ttyHS0", 0600, AID_BLUETOOTH, AID_BLUETOOTH, 0 }, + { "/dev/uinput", 0600, AID_BLUETOOTH, AID_BLUETOOTH, 0 }, { "/dev/alarm", 0664, AID_SYSTEM, AID_RADIO, 0 }, - { "/dev/tty0", 0666, AID_ROOT, AID_SYSTEM, 0 }, + { "/dev/tty0", 0660, AID_ROOT, AID_SYSTEM, 0 }, { "/dev/graphics/", 0660, AID_ROOT, AID_GRAPHICS, 1 }, { "/dev/hw3d", 0660, AID_SYSTEM, AID_GRAPHICS, 0 }, { "/dev/input/", 0660, AID_ROOT, AID_INPUT, 1 }, @@ -362,32 +365,32 @@ static void handle_device_event(struct uevent *uevent) return; /* are we block or char? where should we live? */ - if(!strncmp(uevent->path, "/block", 6)) { + if(!strncmp(uevent->subsystem, "block", 5)) { block = 1; base = "/dev/block/"; mkdir(base, 0755); } else { block = 0; /* this should probably be configurable somehow */ - if(!strncmp(uevent->path, "/class/graphics/", 16)) { + if(!strncmp(uevent->subsystem, "graphics", 8)) { base = "/dev/graphics/"; mkdir(base, 0755); - } else if (!strncmp(uevent->path, "/class/oncrpc/", 14)) { + } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) { base = "/dev/oncrpc/"; mkdir(base, 0755); - } else if (!strncmp(uevent->path, "/class/adsp/", 12)) { + } else if (!strncmp(uevent->subsystem, "adsp", 4)) { base = "/dev/adsp/"; mkdir(base, 0755); - } else if(!strncmp(uevent->path, "/class/input/", 13)) { + } else if(!strncmp(uevent->subsystem, "input", 5)) { base = "/dev/input/"; mkdir(base, 0755); - } else if(!strncmp(uevent->path, "/class/mtd/", 11)) { + } else if(!strncmp(uevent->subsystem, "mtd", 3)) { base = "/dev/mtd/"; mkdir(base, 0755); } else if(!strncmp(uevent->subsystem, "sound", 5)) { base = "/dev/snd/"; mkdir(base, 0755); - } else if(!strncmp(uevent->path, "/class/misc/", 12) && + } else if(!strncmp(uevent->subsystem, "misc", 4) && !strncmp(name, "log_", 4)) { base = "/dev/log/"; mkdir(base, 0755); diff --git a/init/grab-bootchart.sh b/init/grab-bootchart.sh index 57c955688..7fe890436 100755 --- a/init/grab-bootchart.sh +++ b/init/grab-bootchart.sh @@ -9,13 +9,13 @@ TMPDIR=/tmp/android-bootchart rm -rf $TMPDIR mkdir -p $TMPDIR -LOGROOT=/tmp/bootchart +LOGROOT=/data/bootchart TARBALL=bootchart.tgz FILES="header proc_stat.log proc_ps.log proc_diskstats.log kernel_pacct" for f in $FILES; do - adb pull $LOGROOT/$f $TMPDIR/$f &> /dev/null + adb pull $LOGROOT/$f $TMPDIR/$f 2>&1 > /dev/null done (cd $TMPDIR && tar -czf $TARBALL $FILES) cp -f $TMPDIR/$TARBALL ./$TARBALL diff --git a/init/init.c b/init/init.c index fd1b75c57..55ee9ab1c 100644 --- a/init/init.c +++ b/init/init.c @@ -43,21 +43,12 @@ #include "devices.h" #include "init.h" #include "property_service.h" - -#ifndef BOOTCHART -# define BOOTCHART 0 -#endif +#include "bootchart.h" static int property_triggers_enabled = 0; #if BOOTCHART static int bootchart_count; -extern int bootchart_init(void); -extern int bootchart_step(void); -extern void bootchart_finish(void); -# define BOOTCHART_POLLING_MS 200 /* polling period in ms */ -# define BOOTCHART_MAX_TIME_MS (2*60*1000) /* max polling time from boot */ -# define BOOTCHART_MAX_COUNT (BOOTCHART_MAX_TIME_MS/BOOTCHART_POLLING_MS) #endif static char console[32]; @@ -836,11 +827,13 @@ int main(int argc, char **argv) ufds[2].events = POLLIN; #if BOOTCHART - if (bootchart_init() < 0) + bootchart_count = bootchart_init(); + if (bootchart_count < 0) { ERROR("bootcharting init failure\n"); - else { - NOTICE("bootcharting started\n"); - bootchart_count = BOOTCHART_MAX_COUNT; + } else if (bootchart_count > 0) { + NOTICE("bootcharting started (period=%d ms)\n", bootchart_count*BOOTCHART_POLLING_MS); + } else { + NOTICE("bootcharting ignored\n"); } #endif diff --git a/init/keywords.h b/init/keywords.h index f09bad242..058996e3b 100644 --- a/init/keywords.h +++ b/init/keywords.h @@ -19,6 +19,7 @@ int do_start(int nargs, char **args); int do_stop(int nargs, char **args); int do_trigger(int nargs, char **args); int do_symlink(int nargs, char **args); +int do_sysclktz(int nargs, char **args); int do_write(int nargs, char **args); int do_chown(int nargs, char **args); int do_chmod(int nargs, char **args); @@ -60,6 +61,7 @@ enum { KEYWORD(stop, COMMAND, 1, do_stop) KEYWORD(trigger, COMMAND, 1, do_trigger) KEYWORD(symlink, COMMAND, 1, do_symlink) + KEYWORD(sysclktz, COMMAND, 1, do_sysclktz) KEYWORD(user, OPTION, 0, 0) KEYWORD(write, COMMAND, 2, do_write) KEYWORD(chown, COMMAND, 2, do_chown) diff --git a/init/parser.c b/init/parser.c index 95bf01791..a51691be2 100644 --- a/init/parser.c +++ b/init/parser.c @@ -183,6 +183,7 @@ int lookup_keyword(const char *s) if (!strcmp(s, "tart")) return K_start; if (!strcmp(s, "top")) return K_stop; if (!strcmp(s, "ymlink")) return K_symlink; + if (!strcmp(s, "ysclktz")) return K_sysclktz; break; case 't': if (!strcmp(s, "rigger")) return K_trigger; diff --git a/init/property_service.c b/init/property_service.c index 0bc403f63..7a6416b5e 100644 --- a/init/property_service.c +++ b/init/property_service.c @@ -343,7 +343,7 @@ void handle_property_set_fd(int fd) socklen_t addr_size = sizeof(addr); socklen_t cr_size = sizeof(cr); - if ((s = accept(fd, &addr, &addr_size)) < 0) { + if ((s = accept(fd, (struct sockaddr *) &addr, &addr_size)) < 0) { return; } diff --git a/init/readme.txt b/init/readme.txt index 360a1b709..665090bad 100644 --- a/init/readme.txt +++ b/init/readme.txt @@ -194,6 +194,9 @@ stop symlink Create a symbolic link at with the value +sysclktz + Set the system clock base (0 if system clock ticks in GMT) + trigger Trigger an event. Used to queue an action from another action. diff --git a/libcutils/Android.mk b/libcutils/Android.mk index 3d0c12b1d..a43f7e3e4 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -60,6 +60,7 @@ else selector.c \ fdevent.c \ tztime.c \ + tzstrftime.c \ adb_networking.c \ zygote.c endif @@ -91,8 +92,14 @@ else #!sim # ======================================================== include $(CLEAR_VARS) LOCAL_MODULE := libcutils -LOCAL_SRC_FILES := $(commonSources) memset32.S atomic-android-arm.S mq.c \ - ashmem-dev.c +LOCAL_SRC_FILES := $(commonSources) ashmem-dev.c mq.c + +ifeq ($(TARGET_ARCH),arm) +LOCAL_SRC_FILES += memset32.S atomic-android-arm.S +else # !arm +LOCAL_SRC_FILES += memory.c +endif # !arm + LOCAL_C_INCLUDES := $(KERNEL_HEADERS) LOCAL_STATIC_LIBRARIES := liblog include $(BUILD_STATIC_LIBRARY) diff --git a/libcutils/tzstrftime.c b/libcutils/tzstrftime.c new file mode 100644 index 000000000..29c50154a --- /dev/null +++ b/libcutils/tzstrftime.c @@ -0,0 +1,834 @@ +#ifndef lint +#ifndef NOID +static char elsieid[] = "@(#)strftime.c 8.1"; +/* +** Based on the UCB version with the ID appearing below. +** This is ANSIish only when "multibyte character == plain character". +*/ +#endif /* !defined NOID */ +#endif /* !defined lint */ + +#include +#include +#include +#include + +/* +** Copyright (c) 1989 The Regents of the University of California. +** All rights reserved. +** +** Redistribution and use in source and binary forms are permitted +** provided that the above copyright notice and this paragraph are +** duplicated in all such forms and that any documentation, +** advertising materials, and other materials related to such +** distribution and use acknowledge that the software was developed +** by the University of California, Berkeley. The name of the +** University may not be used to endorse or promote products derived +** from this software without specific prior written permission. +** THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#ifndef LIBC_SCCS +#ifndef lint +static const char sccsid[] = "@(#)strftime.c 5.4 (Berkeley) 3/14/89"; +#endif /* !defined lint */ +#endif /* !defined LIBC_SCCS */ + +#include + +#define P(x) x + +static char * _add P((const char *, char *, const char *, int)); +static char * _conv P((int, const char *, char *, const char *)); +static char * _fmt P((const char *, const struct tm *, char *, const char *, + int *, const struct strftime_locale *Locale)); +static char * _yconv P((int, int, int, int, char *, const char *, int)); +static char * getformat P((int, char *, char *, char *, char *)); + +extern char * tzname[]; + + + + + +/* from private.h */ + +#ifndef TYPE_BIT +#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT) +#endif /* !defined TYPE_BIT */ + +#ifndef TYPE_SIGNED +#define TYPE_SIGNED(type) (((type) -1) < 0) +#endif /* !defined TYPE_SIGNED */ + +#ifndef INT_STRLEN_MAXIMUM +/* + * ** 302 / 1000 is log10(2.0) rounded up. + * ** Subtract one for the sign bit if the type is signed; + * ** add one for integer division truncation; + * ** add one more for a minus sign if the type is signed. + * */ +#define INT_STRLEN_MAXIMUM(type) \ + ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \ + 1 + TYPE_SIGNED(type)) +#endif /* !defined INT_STRLEN_MAXIMUM */ + +/* end of part from private.h */ + + + + +#ifndef YEAR_2000_NAME +#define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS" +#endif /* !defined YEAR_2000_NAME */ + +#define IN_NONE 0 +#define IN_SOME 1 +#define IN_THIS 2 +#define IN_ALL 3 + +#define FORCE_LOWER_CASE 0x100 + +size_t +strftime_tz(s, maxsize, format, t, Locale) +char * const s; +const size_t maxsize; +const char * const format; +const struct tm * const t; +const struct strftime_locale *Locale; +{ + char * p; + int warn; + + warn = IN_NONE; + p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn, Locale); +#if 0 + if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) { + (void) fprintf(stderr, "\n"); + if (format == NULL) + (void) fprintf(stderr, "NULL strftime format "); + else (void) fprintf(stderr, "strftime format \"%s\" ", + format); + (void) fprintf(stderr, "yields only two digits of years in "); + if (warn == IN_SOME) + (void) fprintf(stderr, "some locales"); + else if (warn == IN_THIS) + (void) fprintf(stderr, "the current locale"); + else (void) fprintf(stderr, "all locales"); + (void) fprintf(stderr, "\n"); + } +#endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */ + if (p == s + maxsize) + return 0; + *p = '\0'; + return p - s; +} + +static char *getformat(int modifier, char *normal, char *underscore, + char *dash, char *zero) { + switch (modifier) { + case '_': + return underscore; + + case '-': + return dash; + + case '0': + return zero; + } + + return normal; +} + +static char * +_fmt(format, t, pt, ptlim, warnp, Locale) +const char * format; +const struct tm * const t; +char * pt; +const char * const ptlim; +int * warnp; +const struct strftime_locale *Locale; +{ + for ( ; *format; ++format) { + if (*format == '%') { + int modifier = 0; +label: + switch (*++format) { + case '\0': + --format; + break; + case 'A': + pt = _add((t->tm_wday < 0 || + t->tm_wday >= DAYSPERWEEK) ? + "?" : Locale->weekday[t->tm_wday], + pt, ptlim, modifier); + continue; + case 'a': + pt = _add((t->tm_wday < 0 || + t->tm_wday >= DAYSPERWEEK) ? + "?" : Locale->wday[t->tm_wday], + pt, ptlim, modifier); + continue; + case 'B': + pt = _add((t->tm_mon < 0 || + t->tm_mon >= MONSPERYEAR) ? + "?" : Locale->month[t->tm_mon], + pt, ptlim, modifier); + continue; + case 'b': + case 'h': + pt = _add((t->tm_mon < 0 || + t->tm_mon >= MONSPERYEAR) ? + "?" : Locale->mon[t->tm_mon], + pt, ptlim, modifier); + continue; + case 'C': + /* + ** %C used to do a... + ** _fmt("%a %b %e %X %Y", t); + ** ...whereas now POSIX 1003.2 calls for + ** something completely different. + ** (ado, 1993-05-24) + */ + pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0, + pt, ptlim, modifier); + continue; + case 'c': + { + int warn2 = IN_SOME; + + pt = _fmt(Locale->c_fmt, t, pt, ptlim, warnp, Locale); + if (warn2 == IN_ALL) + warn2 = IN_THIS; + if (warn2 > *warnp) + *warnp = warn2; + } + continue; + case 'D': + pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp, Locale); + continue; + case 'd': + pt = _conv(t->tm_mday, + getformat(modifier, "%02d", + "%2d", "%d", "%02d"), + pt, ptlim); + continue; + case 'E': + case 'O': + /* + ** C99 locale modifiers. + ** The sequences + ** %Ec %EC %Ex %EX %Ey %EY + ** %Od %oe %OH %OI %Om %OM + ** %OS %Ou %OU %OV %Ow %OW %Oy + ** are supposed to provide alternate + ** representations. + */ + goto label; + case '_': + case '-': + case '0': + case '^': + case '#': + modifier = *format; + goto label; + case 'e': + pt = _conv(t->tm_mday, + getformat(modifier, "%2d", + "%2d", "%d", "%02d"), + pt, ptlim); + continue; + case 'F': + pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp, Locale); + continue; + case 'H': + pt = _conv(t->tm_hour, + getformat(modifier, "%02d", + "%2d", "%d", "%02d"), + pt, ptlim); + continue; + case 'I': + pt = _conv((t->tm_hour % 12) ? + (t->tm_hour % 12) : 12, + getformat(modifier, "%02d", + "%2d", "%d", "%02d"), + pt, ptlim); + continue; + case 'j': + pt = _conv(t->tm_yday + 1, + getformat(modifier, "%03d", "%3d", "%d", "%03d"), + pt, ptlim); + continue; + case 'k': + /* + ** This used to be... + ** _conv(t->tm_hour % 12 ? + ** t->tm_hour % 12 : 12, 2, ' '); + ** ...and has been changed to the below to + ** match SunOS 4.1.1 and Arnold Robbins' + ** strftime version 3.0. That is, "%k" and + ** "%l" have been swapped. + ** (ado, 1993-05-24) + */ + pt = _conv(t->tm_hour, + getformat(modifier, "%2d", + "%2d", "%d", "%02d"), + pt, ptlim); + continue; +#ifdef KITCHEN_SINK + case 'K': + /* + ** After all this time, still unclaimed! + */ + pt = _add("kitchen sink", pt, ptlim, modifier); + continue; +#endif /* defined KITCHEN_SINK */ + case 'l': + /* + ** This used to be... + ** _conv(t->tm_hour, 2, ' '); + ** ...and has been changed to the below to + ** match SunOS 4.1.1 and Arnold Robbin's + ** strftime version 3.0. That is, "%k" and + ** "%l" have been swapped. + ** (ado, 1993-05-24) + */ + pt = _conv((t->tm_hour % 12) ? + (t->tm_hour % 12) : 12, + getformat(modifier, "%2d", + "%2d", "%d", "%02d"), + pt, ptlim); + continue; + case 'M': + pt = _conv(t->tm_min, + getformat(modifier, "%02d", + "%2d", "%d", "%02d"), + pt, ptlim); + continue; + case 'm': + pt = _conv(t->tm_mon + 1, + getformat(modifier, "%02d", + "%2d", "%d", "%02d"), + pt, ptlim); + continue; + case 'n': + pt = _add("\n", pt, ptlim, modifier); + continue; + case 'p': + pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? + Locale->pm : + Locale->am, + pt, ptlim, modifier); + continue; + case 'P': + pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? + Locale->pm : + Locale->am, + pt, ptlim, FORCE_LOWER_CASE); + continue; + case 'R': + pt = _fmt("%H:%M", t, pt, ptlim, warnp, Locale); + continue; + case 'r': + pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp, Locale); + continue; + case 'S': + pt = _conv(t->tm_sec, + getformat(modifier, "%02d", + "%2d", "%d", "%02d"), + pt, ptlim); + continue; + case 's': + { + struct tm tm; + char buf[INT_STRLEN_MAXIMUM( + time_t) + 1]; + time_t mkt; + + tm = *t; + mkt = mktime(&tm); + if (TYPE_SIGNED(time_t)) + (void) sprintf(buf, "%ld", + (long) mkt); + else (void) sprintf(buf, "%lu", + (unsigned long) mkt); + pt = _add(buf, pt, ptlim, modifier); + } + continue; + case 'T': + pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp, Locale); + continue; + case 't': + pt = _add("\t", pt, ptlim, modifier); + continue; + case 'U': + pt = _conv((t->tm_yday + DAYSPERWEEK - + t->tm_wday) / DAYSPERWEEK, + getformat(modifier, "%02d", + "%2d", "%d", "%02d"), + pt, ptlim); + continue; + case 'u': + /* + ** From Arnold Robbins' strftime version 3.0: + ** "ISO 8601: Weekday as a decimal number + ** [1 (Monday) - 7]" + ** (ado, 1993-05-24) + */ + pt = _conv((t->tm_wday == 0) ? + DAYSPERWEEK : t->tm_wday, "%d", pt, ptlim); + continue; + case 'V': /* ISO 8601 week number */ + case 'G': /* ISO 8601 year (four digits) */ + case 'g': /* ISO 8601 year (two digits) */ +/* +** From Arnold Robbins' strftime version 3.0: "the week number of the +** year (the first Monday as the first day of week 1) as a decimal number +** (01-53)." +** (ado, 1993-05-24) +** +** From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn: +** "Week 01 of a year is per definition the first week which has the +** Thursday in this year, which is equivalent to the week which contains +** the fourth day of January. In other words, the first week of a new year +** is the week which has the majority of its days in the new year. Week 01 +** might also contain days from the previous year and the week before week +** 01 of a year is the last week (52 or 53) of the previous year even if +** it contains days from the new year. A week starts with Monday (day 1) +** and ends with Sunday (day 7). For example, the first week of the year +** 1997 lasts from 1996-12-30 to 1997-01-05..." +** (ado, 1996-01-02) +*/ + { + int year; + int base; + int yday; + int wday; + int w; + + year = t->tm_year; + base = TM_YEAR_BASE; + yday = t->tm_yday; + wday = t->tm_wday; + for ( ; ; ) { + int len; + int bot; + int top; + + len = isleap_sum(year, base) ? + DAYSPERLYEAR : + DAYSPERNYEAR; + /* + ** What yday (-3 ... 3) does + ** the ISO year begin on? + */ + bot = ((yday + 11 - wday) % + DAYSPERWEEK) - 3; + /* + ** What yday does the NEXT + ** ISO year begin on? + */ + top = bot - + (len % DAYSPERWEEK); + if (top < -3) + top += DAYSPERWEEK; + top += len; + if (yday >= top) { + ++base; + w = 1; + break; + } + if (yday >= bot) { + w = 1 + ((yday - bot) / + DAYSPERWEEK); + break; + } + --base; + yday += isleap_sum(year, base) ? + DAYSPERLYEAR : + DAYSPERNYEAR; + } +#ifdef XPG4_1994_04_09 + if ((w == 52 && + t->tm_mon == TM_JANUARY) || + (w == 1 && + t->tm_mon == TM_DECEMBER)) + w = 53; +#endif /* defined XPG4_1994_04_09 */ + if (*format == 'V') + pt = _conv(w, + getformat(modifier, + "%02d", + "%2d", + "%d", + "%02d"), + pt, ptlim); + else if (*format == 'g') { + *warnp = IN_ALL; + pt = _yconv(year, base, 0, 1, + pt, ptlim, modifier); + } else pt = _yconv(year, base, 1, 1, + pt, ptlim, modifier); + } + continue; + case 'v': + /* + ** From Arnold Robbins' strftime version 3.0: + ** "date as dd-bbb-YYYY" + ** (ado, 1993-05-24) + */ + pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp, Locale); + continue; + case 'W': + pt = _conv((t->tm_yday + DAYSPERWEEK - + (t->tm_wday ? + (t->tm_wday - 1) : + (DAYSPERWEEK - 1))) / DAYSPERWEEK, + getformat(modifier, "%02d", + "%2d", "%d", "%02d"), + pt, ptlim); + continue; + case 'w': + pt = _conv(t->tm_wday, "%d", pt, ptlim); + continue; + case 'X': + pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp, Locale); + continue; + case 'x': + { + int warn2 = IN_SOME; + + pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2, Locale); + if (warn2 == IN_ALL) + warn2 = IN_THIS; + if (warn2 > *warnp) + *warnp = warn2; + } + continue; + case 'y': + *warnp = IN_ALL; + pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1, + pt, ptlim, modifier); + continue; + case 'Y': + pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1, + pt, ptlim, modifier); + continue; + case 'Z': +#ifdef TM_ZONE + if (t->TM_ZONE != NULL) + pt = _add(t->TM_ZONE, pt, ptlim, + modifier); + else +#endif /* defined TM_ZONE */ + if (t->tm_isdst >= 0) + pt = _add(tzname[t->tm_isdst != 0], + pt, ptlim, modifier); + /* + ** C99 says that %Z must be replaced by the + ** empty string if the time zone is not + ** determinable. + */ + continue; + case 'z': + { + int diff; + char const * sign; + + if (t->tm_isdst < 0) + continue; +#ifdef TM_GMTOFF + diff = t->TM_GMTOFF; +#else /* !defined TM_GMTOFF */ + /* + ** C99 says that the UTC offset must + ** be computed by looking only at + ** tm_isdst. This requirement is + ** incorrect, since it means the code + ** must rely on magic (in this case + ** altzone and timezone), and the + ** magic might not have the correct + ** offset. Doing things correctly is + ** tricky and requires disobeying C99; + ** see GNU C strftime for details. + ** For now, punt and conform to the + ** standard, even though it's incorrect. + ** + ** C99 says that %z must be replaced by the + ** empty string if the time zone is not + ** determinable, so output nothing if the + ** appropriate variables are not available. + */ + if (t->tm_isdst == 0) +#ifdef USG_COMPAT + diff = -timezone; +#else /* !defined USG_COMPAT */ + continue; +#endif /* !defined USG_COMPAT */ + else +#ifdef ALTZONE + diff = -altzone; +#else /* !defined ALTZONE */ + continue; +#endif /* !defined ALTZONE */ +#endif /* !defined TM_GMTOFF */ + if (diff < 0) { + sign = "-"; + diff = -diff; + } else sign = "+"; + pt = _add(sign, pt, ptlim, modifier); + diff /= SECSPERMIN; + diff = (diff / MINSPERHOUR) * 100 + + (diff % MINSPERHOUR); + pt = _conv(diff, + getformat(modifier, "%04d", + "%4d", "%d", "%04d"), + pt, ptlim); + } + continue; + case '+': + pt = _fmt(Locale->date_fmt, t, pt, ptlim, + warnp, Locale); + continue; + case '%': + /* + ** X311J/88-090 (4.12.3.5): if conversion char is + ** undefined, behavior is undefined. Print out the + ** character itself as printf(3) also does. + */ + default: + break; + } + } + if (pt == ptlim) + break; + *pt++ = *format; + } + return pt; +} + +static char * +_conv(n, format, pt, ptlim) +const int n; +const char * const format; +char * const pt; +const char * const ptlim; +{ + char buf[INT_STRLEN_MAXIMUM(int) + 1]; + + (void) sprintf(buf, format, n); + return _add(buf, pt, ptlim, 0); +} + +static char * +_add(str, pt, ptlim, modifier) +const char * str; +char * pt; +const char * const ptlim; +int modifier; +{ + int c; + + switch (modifier) { + case FORCE_LOWER_CASE: + while (pt < ptlim && (*pt = tolower(*str++)) != '\0') { + ++pt; + } + break; + + case '^': + while (pt < ptlim && (*pt = toupper(*str++)) != '\0') { + ++pt; + } + break; + + case '#': + while (pt < ptlim && (c = *str++) != '\0') { + if (isupper(c)) { + c = tolower(c); + } else if (islower(c)) { + c = toupper(c); + } + *pt = c; + ++pt; + } + + break; + + default: + while (pt < ptlim && (*pt = *str++) != '\0') { + ++pt; + } + } + + return pt; +} + +/* +** POSIX and the C Standard are unclear or inconsistent about +** what %C and %y do if the year is negative or exceeds 9999. +** Use the convention that %C concatenated with %y yields the +** same output as %Y, and that %Y contains at least 4 bytes, +** with more only if necessary. +*/ + +static char * +_yconv(a, b, convert_top, convert_yy, pt, ptlim, modifier) +const int a; +const int b; +const int convert_top; +const int convert_yy; +char * pt; +const char * const ptlim; +int modifier; +{ + register int lead; + register int trail; + +#define DIVISOR 100 + trail = a % DIVISOR + b % DIVISOR; + lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR; + trail %= DIVISOR; + if (trail < 0 && lead > 0) { + trail += DIVISOR; + --lead; + } else if (lead < 0 && trail > 0) { + trail -= DIVISOR; + ++lead; + } + if (convert_top) { + if (lead == 0 && trail < 0) + pt = _add("-0", pt, ptlim, modifier); + else pt = _conv(lead, getformat(modifier, "%02d", + "%2d", "%d", "%02d"), + pt, ptlim); + } + if (convert_yy) + pt = _conv(((trail < 0) ? -trail : trail), + getformat(modifier, "%02d", "%2d", "%d", "%02d"), + pt, ptlim); + return pt; +} + +#ifdef LOCALE_HOME +static struct lc_time_T * +_loc P((void)) +{ + static const char locale_home[] = LOCALE_HOME; + static const char lc_time[] = "LC_TIME"; + static char * locale_buf; + + int fd; + int oldsun; /* "...ain't got nothin' to do..." */ + char * lbuf; + char * name; + char * p; + const char ** ap; + const char * plim; + char filename[FILENAME_MAX]; + struct stat st; + size_t namesize; + size_t bufsize; + + /* + ** Use localebuf.mon[0] to signal whether locale is already set up. + */ + if (localebuf.mon[0]) + return &localebuf; + name = setlocale(LC_TIME, (char *) NULL); + if (name == NULL || *name == '\0') + goto no_locale; + /* + ** If the locale name is the same as our cache, use the cache. + */ + lbuf = locale_buf; + if (lbuf != NULL && strcmp(name, lbuf) == 0) { + p = lbuf; + for (ap = (const char **) &localebuf; + ap < (const char **) (&localebuf + 1); + ++ap) + *ap = p += strlen(p) + 1; + return &localebuf; + } + /* + ** Slurp the locale file into the cache. + */ + namesize = strlen(name) + 1; + if (sizeof filename < + ((sizeof locale_home) + namesize + (sizeof lc_time))) + goto no_locale; + oldsun = 0; + (void) sprintf(filename, "%s/%s/%s", locale_home, name, lc_time); + fd = open(filename, O_RDONLY); + if (fd < 0) { + /* + ** Old Sun systems have a different naming and data convention. + */ + oldsun = 1; + (void) sprintf(filename, "%s/%s/%s", locale_home, + lc_time, name); + fd = open(filename, O_RDONLY); + if (fd < 0) + goto no_locale; + } + if (fstat(fd, &st) != 0) + goto bad_locale; + if (st.st_size <= 0) + goto bad_locale; + bufsize = namesize + st.st_size; + locale_buf = NULL; + lbuf = (lbuf == NULL) ? malloc(bufsize) : realloc(lbuf, bufsize); + if (lbuf == NULL) + goto bad_locale; + (void) strcpy(lbuf, name); + p = lbuf + namesize; + plim = p + st.st_size; + if (read(fd, p, (size_t) st.st_size) != st.st_size) + goto bad_lbuf; + if (close(fd) != 0) + goto bad_lbuf; + /* + ** Parse the locale file into localebuf. + */ + if (plim[-1] != '\n') + goto bad_lbuf; + for (ap = (const char **) &localebuf; + ap < (const char **) (&localebuf + 1); + ++ap) { + if (p == plim) + goto bad_lbuf; + *ap = p; + while (*p != '\n') + ++p; + *p++ = '\0'; + } + if (oldsun) { + /* + ** SunOS 4 used an obsolescent format; see localdtconv(3). + ** c_fmt had the ``short format for dates and times together'' + ** (SunOS 4 date, "%a %b %e %T %Z %Y" in the C locale); + ** date_fmt had the ``long format for dates'' + ** (SunOS 4 strftime %C, "%A, %B %e, %Y" in the C locale). + ** Discard the latter in favor of the former. + */ + localebuf.date_fmt = localebuf.c_fmt; + } + /* + ** Record the successful parse in the cache. + */ + locale_buf = lbuf; + + return &localebuf; + +bad_lbuf: + free(lbuf); +bad_locale: + (void) close(fd); +no_locale: + localebuf = C_time_locale; + locale_buf = NULL; + return &localebuf; +} +#endif /* defined LOCALE_HOME */ diff --git a/libnetutils/dhcp_utils.c b/libnetutils/dhcp_utils.c index 97fe76dd3..ba9893351 100644 --- a/libnetutils/dhcp_utils.c +++ b/libnetutils/dhcp_utils.c @@ -181,6 +181,22 @@ int dhcp_stop(const char *interface) return 0; } +/** + * Release the current DHCP client lease. + */ +int dhcp_release_lease(const char *interface) +{ + const char *ctrl_prop = "ctl.stop"; + const char *desired_status = "stopped"; + + /* Stop the daemon and wait until it's reported to be stopped */ + property_set(ctrl_prop, DAEMON_NAME); + if (wait_for_property(DAEMON_PROP_NAME, desired_status, 5) < 0) { + return -1; + } + return 0; +} + char *dhcp_get_errmsg() { return errmsg; } diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk index a8e5ee4eb..6edc56fec 100644 --- a/libpixelflinger/Android.mk +++ b/libpixelflinger/Android.mk @@ -85,3 +85,6 @@ ifeq ($(TARGET_ARCH),arm) LOCAL_WHOLE_STATIC_LIBRARIES := libpixelflinger_armv6 endif include $(BUILD_STATIC_LIBRARY) + + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/libpixelflinger/codeflinger/GGLAssembler.cpp b/libpixelflinger/codeflinger/GGLAssembler.cpp index 90c275e4b..1cd189c9f 100644 --- a/libpixelflinger/codeflinger/GGLAssembler.cpp +++ b/libpixelflinger/codeflinger/GGLAssembler.cpp @@ -151,6 +151,7 @@ int GGLAssembler::scanline_core(const needs_t& needs, context_t const* c) // Destination is zero (beware of logic ops) } + int fbComponents = 0; const int masking = GGL_READ_NEEDS(MASK_ARGB, needs.n); for (int i=0 ; i<4 ; i++) { const int mask = 1<>3)); + if (!mAllMasked) { + ADD(AL, 0, parts.cbPtr.reg, parts.cbPtr.reg, imm(parts.cbPtr.size>>3)); + } SUB(AL, S, parts.count.reg, parts.count.reg, imm(1<<16)); B(PL, "fragment_loop"); epilog(registerFile().touched()); @@ -370,16 +382,18 @@ void GGLAssembler::build_scanline_prolog( MOV(AL, 0, parts.count.reg, reg_imm(parts.count.reg, LSL, 16)); } - // compute dst ptr - comment("compute color-buffer pointer"); - const int cb_bits = mCbFormat.size*8; - int Rs = scratches.obtain(); - parts.cbPtr.setTo(obtainReg(), cb_bits); - CONTEXT_LOAD(Rs, state.buffers.color.stride); - CONTEXT_LOAD(parts.cbPtr.reg, state.buffers.color.data); - SMLABB(AL, Rs, Ry, Rs, Rx); // Rs = Rx + Ry*Rs - base_offset(parts.cbPtr, parts.cbPtr, Rs); - scratches.recycle(Rs); + if (!mAllMasked) { + // compute dst ptr + comment("compute color-buffer pointer"); + const int cb_bits = mCbFormat.size*8; + int Rs = scratches.obtain(); + parts.cbPtr.setTo(obtainReg(), cb_bits); + CONTEXT_LOAD(Rs, state.buffers.color.stride); + CONTEXT_LOAD(parts.cbPtr.reg, state.buffers.color.data); + SMLABB(AL, Rs, Ry, Rs, Rx); // Rs = Rx + Ry*Rs + base_offset(parts.cbPtr, parts.cbPtr, Rs); + scratches.recycle(Rs); + } // init fog const int need_fog = GGL_READ_NEEDS(P_FOG, needs.p); @@ -904,8 +918,9 @@ void GGLAssembler::build_and_immediate(int d, int s, uint32_t mask, int bits) void GGLAssembler::build_masking(pixel_t& pixel, Scratch& regs) { - if (!mMasking) + if (!mMasking || mAllMasked) { return; + } comment("color mask"); @@ -928,7 +943,7 @@ void GGLAssembler::build_masking(pixel_t& pixel, Scratch& regs) // There is no need to clear the masked components of the source // (unless we applied a logic op), because they're already zeroed - // by contruction (masked components are not computed) + // by construction (masked components are not computed) if (mLogicOp) { const needs_t& needs = mBuilderContext.needs; diff --git a/libpixelflinger/codeflinger/GGLAssembler.h b/libpixelflinger/codeflinger/GGLAssembler.h index ccaf43d3a..d1d29f0b0 100644 --- a/libpixelflinger/codeflinger/GGLAssembler.h +++ b/libpixelflinger/codeflinger/GGLAssembler.h @@ -363,6 +363,10 @@ private: const component_t& incoming, const pixel_t& texel, int component, int tmu); + void add( component_t& dest, + const component_t& incoming, + const pixel_t& texel, int component); + // load/store stuff void store(const pointer_t& addr, const pixel_t& src, uint32_t flags=0); void load(const pointer_t& addr, const pixel_t& dest, uint32_t flags=0); @@ -517,6 +521,7 @@ private: component_info_t mInfo[4]; int mBlending; int mMasking; + int mAllMasked; int mLogicOp; int mAlphaTest; int mAA; diff --git a/libpixelflinger/codeflinger/blending.cpp b/libpixelflinger/codeflinger/blending.cpp index 6d3b28201..f10217b74 100644 --- a/libpixelflinger/codeflinger/blending.cpp +++ b/libpixelflinger/codeflinger/blending.cpp @@ -50,6 +50,12 @@ void GGLAssembler::build_fog( integer_t factor(scratches.obtain(), 16, CORRUPTIBLE); CONTEXT_LOAD(factor.reg, generated_vars.f); + // clamp fog factor (TODO: see if there is a way to guarantee + // we won't overflow, when setting the iterators) + BIC(AL, 0, factor.reg, factor.reg, reg_imm(factor.reg, ASR, 31)); + CMP(AL, factor.reg, imm( 0x10000 )); + MOV(HS, 0, factor.reg, imm( 0x10000 )); + build_blendFOneMinusF(temp, factor, fragment, fogColor); } } diff --git a/libpixelflinger/codeflinger/load_store.cpp b/libpixelflinger/codeflinger/load_store.cpp index 514ce073e..93c582579 100644 --- a/libpixelflinger/codeflinger/load_store.cpp +++ b/libpixelflinger/codeflinger/load_store.cpp @@ -168,7 +168,7 @@ void GGLAssembler::expand(integer_t& d, const component_t& s, int dbits) void GGLAssembler::expand(component_t& d, const component_t& s, int dbits) { integer_t r(d.reg, 32, d.flags); - expand(r, d, dbits); + expand(r, s, dbits); d = component_t(r); } diff --git a/libpixelflinger/codeflinger/texturing.cpp b/libpixelflinger/codeflinger/texturing.cpp index 269b6c003..90e658407 100644 --- a/libpixelflinger/codeflinger/texturing.cpp +++ b/libpixelflinger/codeflinger/texturing.cpp @@ -1000,6 +1000,9 @@ void GGLAssembler::build_texture_environment( case GGL_BLEND: blend(fragment, incoming, texel, component, i); break; + case GGL_ADD: + add(fragment, incoming, texel, component); + break; } } } @@ -1202,6 +1205,46 @@ void GGLAssembler::blend( build_blendOneMinusFF(dest, factor, incomingNorm, color); } +void GGLAssembler::add( + component_t& dest, + const component_t& incoming, + const pixel_t& incomingTexel, int component) +{ + // RGBA: + // Cv = Cf + Ct; + Scratch locals(registerFile()); + + component_t incomingTemp(incoming); + + // use "dest" as a temporary for extracting the texel, unless "dest" + // overlaps "incoming". + integer_t texel(dest.reg, 32, CORRUPTIBLE); + if (dest.reg == incomingTemp.reg) + texel.reg = locals.obtain(); + extract(texel, incomingTexel, component); + + if (texel.s < incomingTemp.size()) { + expand(texel, texel, incomingTemp.size()); + } else if (texel.s > incomingTemp.size()) { + if (incomingTemp.flags & CORRUPTIBLE) { + expand(incomingTemp, incomingTemp, texel.s); + } else { + incomingTemp.reg = locals.obtain(); + expand(incomingTemp, incoming, texel.s); + } + } + + if (incomingTemp.l) { + ADD(AL, 0, dest.reg, texel.reg, + reg_imm(incomingTemp.reg, LSR, incomingTemp.l)); + } else { + ADD(AL, 0, dest.reg, texel.reg, incomingTemp.reg); + } + dest.l = 0; + dest.h = texel.size(); + component_sat(dest); +} + // ---------------------------------------------------------------------------- }; // namespace android diff --git a/libpixelflinger/format.cpp b/libpixelflinger/format.cpp index c77eadaba..cbbd91a0e 100644 --- a/libpixelflinger/format.cpp +++ b/libpixelflinger/format.cpp @@ -21,13 +21,13 @@ namespace android { static GGLFormat const gPixelFormatInfos[] = -{ +{ // Alpha Red Green Blue { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE { 4, 32, {{32,24, 8, 0, 16, 8, 24,16 }}, GGL_RGBA }, // PIXEL_FORMAT_RGBA_8888 { 4, 24, {{ 0, 0, 8, 0, 16, 8, 24,16 }}, GGL_RGB }, // PIXEL_FORMAT_RGBX_8888 { 3, 24, {{ 0, 0, 8, 0, 16, 8, 24,16 }}, GGL_RGB }, // PIXEL_FORMAT_RGB_888 { 2, 16, {{ 0, 0, 16,11, 11, 5, 5, 0 }}, GGL_RGB }, // PIXEL_FORMAT_RGB_565 - { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE + { 4, 32, {{32,24, 24,16, 16, 8, 8, 0 }}, GGL_RGBA }, // PIXEL_FORMAT_BGRA_8888 { 2, 16, {{ 1, 0, 16,11, 11, 6, 6, 1 }}, GGL_RGBA }, // PIXEL_FORMAT_RGBA_5551 { 2, 16, {{ 4, 0, 16,12, 12, 8, 8, 4 }}, GGL_RGBA }, // PIXEL_FORMAT_RGBA_4444 { 1, 8, {{ 8, 0, 0, 0, 0, 0, 0, 0 }}, GGL_ALPHA}, // PIXEL_FORMAT_A8 diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp index d24c98805..75b668d37 100644 --- a/libpixelflinger/scanline.cpp +++ b/libpixelflinger/scanline.cpp @@ -55,9 +55,11 @@ # define ANDROID_ARM_CODEGEN 0 #endif - #define DEBUG__CODEGEN_ONLY 0 + +#define ASSEMBLY_SCRATCH_SIZE 2048 + // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- @@ -247,7 +249,8 @@ static void pick_scanline(context_t* c) sp assembly = gCodeCache.lookup(key); if (assembly == 0) { // create a new assembly region - sp a = new ScanlineAssembly(c->state.needs, 1024); + sp a = new ScanlineAssembly(c->state.needs, + ASSEMBLY_SCRATCH_SIZE); // initialize our assembler GGLAssembler assembler( new ARMAssembler(a) ); //GGLAssembler assembler( @@ -676,6 +679,12 @@ void scanline(context_t* c) Cf = ((((1<>st; } break; + case GGL_ADD: + if (st) { + rescale(Cf, sf, Ct, st); + Cf += Ct; + } + break; } } } @@ -1473,7 +1482,7 @@ extern "C" void ggl_test_codegen(uint32_t n, uint32_t p, uint32_t t0, uint32_t t needs.p = p; needs.t[0] = t0; needs.t[1] = t1; - sp a(new ScanlineAssembly(needs, 1024)); + sp a(new ScanlineAssembly(needs, ASSEMBLY_SCRATCH_SIZE)); GGLAssembler assembler( new ARMAssembler(a) ); int err = assembler.scanline(needs, (context_t*)c); if (err != 0) { diff --git a/libpixelflinger/tests/Android.mk b/libpixelflinger/tests/Android.mk new file mode 100644 index 000000000..65711611c --- /dev/null +++ b/libpixelflinger/tests/Android.mk @@ -0,0 +1 @@ +include $(all-subdir-makefiles) diff --git a/libpixelflinger/tests/codegen/Android.mk b/libpixelflinger/tests/codegen/Android.mk new file mode 100644 index 000000000..1bc421487 --- /dev/null +++ b/libpixelflinger/tests/codegen/Android.mk @@ -0,0 +1,15 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + codegen.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libpixelflinger + +LOCAL_MODULE:= test-opengl-codegen + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/libpixelflinger/tests/codegen/codegen.cpp b/libpixelflinger/tests/codegen/codegen.cpp new file mode 100644 index 000000000..18658881b --- /dev/null +++ b/libpixelflinger/tests/codegen/codegen.cpp @@ -0,0 +1,21 @@ +#include +#include + +extern "C" void ggl_test_codegen( + uint32_t n, uint32_t p, uint32_t t0, uint32_t t1); + + +int main(int argc, char** argv) +{ + if (argc != 2) { + printf("usage: %s 00000117:03454504_00001501_00000000\n", argv[0]); + return 0; + } + uint32_t n; + uint32_t p; + uint32_t t0; + uint32_t t1; + sscanf(argv[1], "%08x:%08x_%08x_%08x", &p, &n, &t0, &t1); + ggl_test_codegen(n, p, t0, t1); + return 0; +} diff --git a/logcat/event-log-tags b/logcat/event-log-tags index 3ee5f8e64..3d977a5e5 100644 --- a/logcat/event-log-tags +++ b/logcat/event-log-tags @@ -244,8 +244,8 @@ 50000 menu_item_selected (Menu type where 0 is options and 1 is context|1|5),(Menu item title|3) 50001 menu_opened (Menu type where 0 is options and 1 is context|1|5) # Connectivity state changed: -# [31-11] Reserved for future use -# [10-9] Mobile network connection type (as defined by the TelephonyManager) +# [31-13] Reserved for future use +# [12- 9] Network subtype (for mobile network, as defined by TelephonyManager) # [ 8- 3] Detailed state ordinal (as defined by NetworkInfo.DetailedState) # [ 2- 0] Network type (as defined by ConnectivityManager) 50020 connectivity_state_changed (custom|1|5) @@ -271,8 +271,9 @@ 50024 wifi_interface_configuration_state_changed (IP_configuration|1|5) # Wi-Fi supplicant connection state changed: -# [31- 1] Reserved for future use -# [ 0- 0] Connected to supplicant (1) or disconnected from supplicant (0) +# [31- 2] Reserved for future use +# [ 1- 0] Connected to supplicant (1) or disconnected from supplicant (0), +# or supplicant died (2) 50025 wifi_supplicant_connection_state_changed (connected|1|5) # PDP Context has a bad DNS address @@ -291,6 +292,17 @@ # Reregister to data network - timed out with no incoming packets. 50104 pdp_reregister_network (out_packet_count|1|1) +# PDP Setup failures +50105 pdp_setup_fail (cause|1|5), (cid|1|5), (network_type|1|5) + +# Call drops +50106 call_drop (cause|1|5), (cid|1|5), (network_type|1|5) + +# Data network registration failed after successful voice registration +50107 data_network_registration_fail (op_numeric|1|5), (cid|1|5) + +# Suspicious status of data connection while radio poweroff +50108 data_network_status_on_radio_off (dc_state|3), (enable|1|5) # Do not change these names without updating tag in: #//device/dalvik/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.c diff --git a/logwrapper/logwrapper.c b/logwrapper/logwrapper.c index 1989e0f2a..f00bfbf3d 100644 --- a/logwrapper/logwrapper.c +++ b/logwrapper/logwrapper.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "private/android_filesystem_config.h" #include "cutils/log.h" @@ -33,48 +34,74 @@ void fatal(const char *msg) { void usage() { fatal( - "Usage: logwrapper BINARY [ARGS ...]\n" + "Usage: logwrapper [-x] BINARY [ARGS ...]\n" "\n" "Forks and executes BINARY ARGS, redirecting stdout and stderr to\n" "the Android logging system. Tag is set to BINARY, priority is\n" - "always LOG_INFO.\n"); + "always LOG_INFO.\n" + "\n" + "-x: Causes logwrapper to SIGSEGV when BINARY terminates\n" + " fault address is set to the status of wait()\n"); } -void parent(const char *tag, int parent_read) { +void parent(const char *tag, int seg_fault_on_exit, int parent_read) { int status; - char buffer[1024]; + char buffer[4096]; int a = 0; // start index of unprocessed data int b = 0; // end index of unprocessed data int sz; - while ((sz = read(parent_read, &buffer[b], 1023 - b)) > 0) { + while ((sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b)) > 0) { + + sz += b; // Log one line at a time - for (b = a; b < sz; b++) { - if (buffer[b] == '\n') { + for (b = 0; b < sz; b++) { + if (buffer[b] == '\r') { + buffer[b] = '\0'; + } else if (buffer[b] == '\n') { buffer[b] = '\0'; LOG(LOG_INFO, tag, &buffer[a]); a = b + 1; } } - if (a == 0 && b == 1023) { + if (a == 0 && b == sizeof(buffer) - 1) { // buffer is full, flush buffer[b] = '\0'; LOG(LOG_INFO, tag, &buffer[a]); b = 0; - } else { + } else if (a != b) { // Keep left-overs - b = sz - a; + b -= a; memmove(buffer, &buffer[a], b); a = 0; + } else { + a = 0; + b = 0; } + } // Flush remaining data if (a != b) { buffer[b] = '\0'; LOG(LOG_INFO, tag, &buffer[a]); } - wait(&status); // Wait for child + status = 0xAAAA; + if (wait(&status) != -1) { // Wait for child + if (WIFEXITED(status)) + LOG(LOG_INFO, "logwrapper", "%s terminated by exit(%d)", tag, + WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + LOG(LOG_INFO, "logwrapper", "%s terminated by signal %d", tag, + WTERMSIG(status)); + else if (WIFSTOPPED(status)) + LOG(LOG_INFO, "logwrapper", "%s stopped by signal %d", tag, + WSTOPSIG(status)); + } else + LOG(LOG_INFO, "logwrapper", "%s wait() failed: %s (%d)", tag, + strerror(errno), errno); + if (seg_fault_on_exit) + *(int *)status = 0; // causes SIGSEGV with fault_address = status } void child(int argc, char* argv[]) { @@ -92,41 +119,62 @@ void child(int argc, char* argv[]) { int main(int argc, char* argv[]) { pid_t pid; + int seg_fault_on_exit = 0; - int pipe_fds[2]; - int *parent_read = &pipe_fds[0]; - int *child_write = &pipe_fds[1]; + int parent_ptty; + int child_ptty; + char *child_devname = NULL; if (argc < 2) { usage(); } - if (pipe(pipe_fds) < 0) { - fatal("Cannot create pipe\n"); + if (strncmp(argv[1], "-d", 2) == 0) { + seg_fault_on_exit = 1; + argc--; + argv++; + } + + if (argc < 2) { + usage(); + } + + /* Use ptty instead of socketpair so that STDOUT is not buffered */ + parent_ptty = open("/dev/ptmx", O_RDWR); + if (parent_ptty < 0) { + fatal("Cannot create parent ptty\n"); + } + + if (grantpt(parent_ptty) || unlockpt(parent_ptty) || + ((child_devname = (char*)ptsname(parent_ptty)) == 0)) { + fatal("Problem with /dev/ptmx\n"); } pid = fork(); if (pid < 0) { fatal("Failed to fork\n"); } else if (pid == 0) { + child_ptty = open(child_devname, O_RDWR); + if (child_ptty < 0) { + fatal("Problem with child ptty\n"); + } + // redirect stdout and stderr - close(*parent_read); - dup2(*child_write, 1); - dup2(*child_write, 2); - close(*child_write); + close(parent_ptty); + dup2(child_ptty, 1); + dup2(child_ptty, 2); + close(child_ptty); child(argc - 1, &argv[1]); } else { - close(*child_write); - // switch user and group to "log" // this may fail if we are not root, // but in that case switching user/group is unnecessary setgid(AID_LOG); setuid(AID_LOG); - parent(argv[1], *parent_read); + parent(argv[1], seg_fault_on_exit, parent_ptty); } return 0; diff --git a/mkbootimg/Android.mk b/mkbootimg/Android.mk index e44bd99a0..a579de0b6 100644 --- a/mkbootimg/Android.mk +++ b/mkbootimg/Android.mk @@ -8,4 +8,4 @@ LOCAL_MODULE := mkbootimg include $(BUILD_HOST_EXECUTABLE) -$(call dist-for-goals,user userdebug droid,$(LOCAL_BUILT_MODULE)) +$(call dist-for-goals,droid,$(LOCAL_BUILT_MODULE)) diff --git a/mountd/ASEC.c b/mountd/ASEC.c new file mode 100644 index 000000000..3d8e50e0a --- /dev/null +++ b/mountd/ASEC.c @@ -0,0 +1,770 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* +** Android Secure External Cache +*/ + +#include "mountd.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include "ASEC.h" +#include "dm-ioctl.h" + +extern int init_module(void *, unsigned long, const char *); +extern int delete_module(const char *, unsigned int); + +struct asec_context +{ + char *name; // Device mapper volume name + char *srcPath; // Path to the source (original) mount + char *backingFile; // Name of the image file + unsigned int sectors; // Number of sectors + char *dstPath; // Destination mount point + char *crypt; // Crypt options + + boolean needs_format; + boolean started; + int cacheFd; + int lo_num; + int dm_num; + unsigned char key[16]; +}; + +static const char *MODULES[] = { "dm_mod", "crypto", "crypto_algapi", "crypto_blkcipher", + "cryptomgr", "dm_crypt", "jbd", + "twofish_common", "twofish", "cbc", + "mbcache", "ext3", + NULL }; +static const char KEY_PATH[] = "/data/system/asec.key"; +static const char MODULE_PATH[] = "/system/lib/modules"; +static const char MKE2FS_PATH[] = "/system/bin/mke2fs"; +static const char E2FSCK_PATH[] = "/system/bin/e2fsck"; + +boolean AsecIsStarted(void *Handle) +{ + struct asec_context *ctx = (struct asec_context *) Handle; + + return ctx->started; +} + +const char *AsecMountPoint(void *Handle) +{ + struct asec_context *ctx = (struct asec_context *) Handle; + + return ctx->dstPath; +} + +static boolean AsecIsEnabled() +{ + char value[PROPERTY_VALUE_MAX]; + int enabled; + + property_get(ASEC_ENABLED, value, "0"); + + if (atoi(value) == 1) + return true; + return false; +} + +void *AsecInit(const char *Name, const char *SrcPath, const char *BackingFile, + const char *Size, const char *DstPath, const char *Crypt) +{ + struct asec_context *ctx; + + LOG_ASEC("AsecInit(%s, %s, %s, %s, %s, %s):\n", + Name, SrcPath, BackingFile, Size, DstPath, Crypt); + + if (!AsecIsEnabled()) { + LOG_ERROR("AsecInit(): Disabled\n"); + return NULL; + } + + if (!Name || !SrcPath || !BackingFile || !Size || !DstPath || !Crypt) { + LOG_ERROR("AsecInit(): Invalid arguments\n"); + return NULL; + } + + if (!(ctx = malloc(sizeof(struct asec_context)))) { + LOG_ERROR("AsecInit(): Out of memory\n"); + return NULL; + } + + memset(ctx, 0, sizeof(struct asec_context)); + ctx->name = strdup(Name); + ctx->srcPath = strdup(SrcPath); + ctx->backingFile = strdup(BackingFile); + ctx->sectors = atoi(Size); + ctx->dstPath = strdup(DstPath); + ctx->crypt = strdup(Crypt); + return ctx; +} + +void AsecDeinit(void *Handle) +{ + struct asec_context *ctx = (struct asec_context *) Handle; + + free(ctx->name); + free(ctx->srcPath); + free(ctx->backingFile); + free(ctx->dstPath); + free(ctx->crypt); + + free(ctx); +} + +static int AsecLoadModules() +{ + int i; + + for (i = 0; MODULES[i] != NULL; i++) { + const char *moduleName = MODULES[i]; + char moduleFile[255]; + int rc = 0; + void *module; + unsigned int size; + + sprintf(moduleFile, "%s/%s.ko", MODULE_PATH, moduleName); + module = load_file(moduleFile, &size); + if (!module) { + LOG_ERROR("Failed to load module %s\n", moduleFile); + return -1; + } + + rc = init_module(module, size, ""); + free(module); + if (rc && errno != EEXIST) { + LOG_ERROR("Failed to init module %s (%d)\n", moduleFile, errno); + return -errno; + } + } + return 0; +} + +static int AsecUnloadModules() +{ + int i, j, rc; + + for (i = 0; MODULES[i] != NULL; i++); + + for (j = (i - 1); j >= 0; j--) { + const char *moduleName = MODULES[j]; + int maxretry = 10; + while(maxretry-- > 0) { + rc = delete_module(moduleName, O_NONBLOCK | O_EXCL); + if (rc < 0 && errno == EAGAIN) + usleep(500000); + else + break; + } + if (rc != 0) { + LOG_ERROR("Failed to unload module %s\n", moduleName); + return -errno; + } + } + return 0; +} + +static int AsecGenerateKey(struct asec_context *ctx) +{ + LOG_ASEC("AsecGenerateKey():\n"); + + memset((void *) ctx->key, 0x69, sizeof(ctx->key)); + return 0; +} + +static int AsecLoadGenerateKey(struct asec_context *ctx) +{ + int fd; + int rc = 0; + + if ((fd = open(KEY_PATH, O_RDWR | O_CREAT, 0600)) < 0) { + LOG_ERROR("Error opening / creating keyfile (%d)\n", errno); + return -errno; + } + + if (read(fd, ctx->key, sizeof(ctx->key)) != sizeof(ctx->key)) { + LOG_ASEC("Generating key\n"); + if ((rc = AsecGenerateKey(ctx)) < 0) { + LOG_ERROR("Error generating key (%d)\n", rc); + goto out; + } + if (write(fd, ctx->key, sizeof(ctx->key)) != sizeof(ctx->key)) { + LOG_ERROR("Error writing keyfile (%d)\n", errno); + rc = -1; + goto out; + } + } + + out: + close (fd); + return rc; +} + +static int AsecFormatFilesystem(struct asec_context *ctx) +{ + char cmdline[255]; + int rc; + + sprintf(cmdline, + "%s -b 4096 -m 1 -j -L \"%s\" /dev/block/dm-%d", + MKE2FS_PATH, ctx->name, ctx->dm_num); + + LOG_ASEC("Formatting filesystem (%s)\n", cmdline); + // XXX: PROTECT FROM VIKING KILLER + if ((rc = system(cmdline)) < 0) { + LOG_ERROR("Error executing format command (%d)\n", errno); + return -errno; + } + + rc = WEXITSTATUS(rc); + + if (!rc) { + LOG_ASEC("Format completed\n"); + } else { + LOG_ASEC("Format failed (%d)\n", rc); + } + + return rc; +} + +static int AsecCheckFilesystem(struct asec_context *ctx) +{ + char cmdline[255]; + int rc; + + sprintf(cmdline, "%s -p /dev/block/dm-%d", E2FSCK_PATH, ctx->dm_num); + + LOG_ASEC("Checking filesystem (%s)\n", cmdline); + // XXX: PROTECT FROM VIKING KILLER + if ((rc = system(cmdline)) < 0) { + LOG_ERROR("Error executing check command (%d)\n", errno); + return -errno; + } + + rc = WEXITSTATUS(rc); + + if (rc == 0) { + LOG_ASEC("ASEC volume '%s' had no errors\n", ctx->name); + } else if (rc == 1) { + LOG_ASEC("ASEC volume '%s' had corrected errors\n", ctx->name); + rc = 0; + } else if (rc == 2) { + LOG_ERROR("ASEC volume '%s' had corrected errors (system should be rebooted)\n", ctx->name); + } else if (rc == 4) { + LOG_ERROR("ASEC volume '%s' had uncorrectable errors\n", ctx->name); + } else if (rc == 8) { + LOG_ERROR("Operational error while checking volume '%s'\n", ctx->name); + } else { + LOG_ERROR("Unknown e2fsck exit code (%d)\n", rc); + } + return rc; +} + +static int AsecOpenCreateCache(struct asec_context *ctx) +{ + char filepath[255]; + + sprintf(filepath, "%s/%s", ctx->srcPath, ctx->backingFile); + + if ((ctx->cacheFd = open(filepath, O_RDWR)) < 0) { + if (errno == ENOENT) { + int rc = 0; + + LOG_ASEC("Creating cache file (%u sectors)\n", ctx->sectors); + if ((ctx->cacheFd = creat(filepath, 0600)) < 0) { + LOG_ERROR("Error creating cache (%d)\n", errno); + return -errno; + } + if (ftruncate(ctx->cacheFd, ctx->sectors * 512) < 0) { + LOG_ERROR("Error truncating cache (%d)\n", errno); + close(ctx->cacheFd); + unlink(filepath); + return -errno; + } + LOG_ASEC("Cache created (%u sectors) \n", ctx->sectors); + close(ctx->cacheFd); // creat() is WRONLY + + if ((ctx->cacheFd = open(filepath, O_RDWR)) < 0) { + LOG_ERROR("Error opening cache file (%d)\n", errno); + close(ctx->cacheFd); + unlink(filepath); + return -errno; + } + + ctx->needs_format = 1; + } else + return -errno; + } else { + struct stat stat_buf; + + if (fstat(ctx->cacheFd, &stat_buf) < 0) { + LOG_ERROR("Failed to fstat cache (%d)\n", errno); + close(ctx->cacheFd); + return -errno; + } + if (stat_buf.st_size != ctx->sectors * 512) { + LOG_ERROR("Cache size %lld != configured size %u\n", + stat_buf.st_size, ctx->sectors * 512); + } + + // XXX: Verify volume label matches ctx->name + } + + return 0; +} + +static void AsecCloseCache(struct asec_context *ctx) +{ + close(ctx->cacheFd); +} + +static void *_align(void *ptr, unsigned int a) +{ + register unsigned long agn = --a; + + return (void *) (((unsigned long) ptr + agn) & ~agn); +} + +static struct dm_ioctl *_dm_ioctl_setup(struct asec_context *ctx, int flags) +{ + void *buffer; + void *p; + const size_t min_size = 16 * 1024; + size_t len = sizeof(struct dm_ioctl); + struct dm_ioctl *io; + struct dm_target_spec *tgt; + int i; + char params[1024]; + char key[80]; + + key[0] = '\0'; + + for (i = 0; i < (int) sizeof(ctx->key); i++) { + char tmp[8]; + + sprintf(tmp, "%02x", ctx->key[i]); + strcat(key, tmp); + } + + // XXX: Handle ctx->crypt + sprintf(params, "twofish %s 0 /dev/block/loop%d 0", key, ctx->lo_num); + + if (len < min_size) + len = min_size; + + if (!(buffer = malloc(len))) { + LOG_ERROR("Unable to allocate memory\n"); + return NULL; + } + + memset(buffer, 0, len); + io = buffer; + tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)]; + + io->version[0] = 4; + io->version[1] = 0; + io->version[2] = 0; + + io->data_size = len; + io->data_start = sizeof(struct dm_ioctl); + + io->flags = flags; + io->dev = 0; + + io->target_count = 1; + io->event_nr = 1; + strncpy(io->name, ctx->name, sizeof(io->name)); + + tgt->status = 0; + tgt->sector_start = 0; + tgt->length = ctx->sectors; + strncpy(tgt->target_type, "crypt", sizeof(tgt->target_type)); + + p = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec); + strcpy((char *) p, params); + p+= strlen(params) + 1; + + p = _align(p, 8); + tgt->next = p - buffer; + + return io; +} + +static int FindNextAvailableDm() +{ + int i; + + for (i = 0; i < 8; i++) { + char path[255]; + sprintf(path, "/dev/block/dm-%d", i); + if ((access(path, F_OK) < 0) && (errno == ENOENT)) + return i; + } + + LOG_ERROR("Out of device mapper numbers\n"); + return -1; +} + +static int AsecCreateDeviceMapping(struct asec_context *ctx) +{ + struct dm_ioctl *io; + int dmFd; + int rc = 0; + + ctx->dm_num = FindNextAvailableDm(); + + if ((dmFd = open("/dev/device-mapper", O_RDWR)) < 0) { + LOG_ERROR("Error opening device mapper (%d)\n", errno); + return -errno; + } + + if (!(io = _dm_ioctl_setup(ctx, 0))) { + LOG_ERROR("Unable to setup ioctl (out of memory)\n"); + close(dmFd); + return -ENOMEM; + } + + if ((rc = ioctl(dmFd, DM_DEV_CREATE, io)) < 0) { + LOG_ERROR("device-mapper create ioctl failed (%d)\n", errno); + rc = -errno; + goto out_free; + } + + free(io); + + if (!(io = _dm_ioctl_setup(ctx, DM_STATUS_TABLE_FLAG))) { + LOG_ERROR("Unable to setup ioctl (out of memory)\n"); + rc = -ENOMEM; + goto out_nofree; + } + + if ((rc = ioctl(dmFd, DM_TABLE_LOAD, io)) < 0) { + LOG_ERROR("device-mapper load ioctl failed (%d)\n", errno); + rc = -errno; + goto out_free; + } + + free(io); + + if (!(io = _dm_ioctl_setup(ctx, 0))) { + LOG_ERROR("Unable to setup ioctl (out of memory)\n"); + rc = -ENOMEM; + goto out_nofree; + } + + if ((rc = ioctl(dmFd, DM_DEV_SUSPEND, io)) < 0) { + LOG_ERROR("device-mapper resume ioctl failed (%d)\n", errno); + rc = -errno; + goto out_free; + } + +out_free: + free (io); +out_nofree: + close (dmFd); + return rc; +} + +static int AsecDestroyDeviceMapping(struct asec_context *ctx) +{ + struct dm_ioctl *io; + int dmFd; + int rc = 0; + + if ((dmFd = open("/dev/device-mapper", O_RDWR)) < 0) { + LOG_ERROR("Error opening device mapper (%d)\n", errno); + return -errno; + } + + if (!(io = _dm_ioctl_setup(ctx, DM_PERSISTENT_DEV_FLAG))) { + LOG_ERROR("Unable to setup ioctl (out of memory)\n"); + rc = -ENOMEM; + goto out_nofree; + } + + if ((rc = ioctl(dmFd, DM_DEV_REMOVE, io)) < 0) { + LOG_ERROR("device-mapper remove ioctl failed (%d)\n", errno); + rc = -errno; + goto out_free; + } + +out_free: + free (io); +out_nofree: + close (dmFd); + return rc; +} + +static int AsecMountCache(struct asec_context *ctx) +{ + int flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_NOATIME | MS_NODIRATIME; + char devname[255]; + + if (access(ctx->dstPath, R_OK)) { + LOG_ERROR("Destination mount point '%s' unavailable (%d)\n", ctx->dstPath, errno); + return -errno; + } + + sprintf(devname, "/dev/block/dm-%d", ctx->dm_num); + + if (mount(devname, ctx->dstPath, "ext3", flags, NULL)) { + LOG_ERROR("ASEC mount failed (%d)\n", errno); + return -errno; + } + + return 0; +} + +static int AsecUnmountCache(struct asec_context *ctx) +{ + if (umount(ctx->dstPath)) { + if (errno == EBUSY) { + LOG_ASEC("ASEC volume '%s' still busy\n", ctx->name); + } else { + LOG_ERROR("ASEC umount failed (%d)\n", errno); + } + return -errno; + } + LOG_ASEC("ASEC volume '%s' unmounted\n", ctx->name); + return 0; +} + +static int FindNextAvailableLoop() +{ + int i; + + for (i = 0; i < MAX_LOOP; i++) { + struct loop_info info; + char devname[255]; + int fd; + + sprintf(devname, "/dev/block/loop%d", i); + + if ((fd = open(devname, O_RDONLY)) < 0) { + LOG_ERROR("Unable to open %s (%d)\n", devname, errno); + return -errno; + } + + if (ioctl(fd, LOOP_GET_STATUS, &info) < 0) { + close(fd); + + if (errno == ENXIO) + return i; + + LOG_ERROR("Unable to get loop status for %s (%d)\n", devname, errno); + return -errno; + } + close(fd); + } + return -ENXIO; +} + +static int AsecCreateLoop(struct asec_context *ctx) +{ + char devname[255]; + int device_fd; + int rc = 0; + + ctx->lo_num = FindNextAvailableLoop(); + if (ctx->lo_num < 0) { + LOG_ERROR("No loop devices available\n"); + return -ENXIO; + } + + sprintf(devname, "/dev/block/loop%d", ctx->lo_num); + device_fd = open(devname, O_RDWR); + if (device_fd < 0) { + LOG_ERROR("failed to open loop device (%d)\n", errno); + return -errno; + } + + if (ioctl(device_fd, LOOP_SET_FD, ctx->cacheFd) < 0) { + LOG_ERROR("loop_set_fd ioctl failed (%d)\n", errno); + rc = -errno; + } + close(device_fd); + return rc; +} + +static int AsecDestroyLoop(struct asec_context *ctx) +{ + char devname[255]; + int device_fd; + int rc = 0; + + sprintf(devname, "/dev/block/loop%d", ctx->lo_num); + device_fd = open(devname, O_RDONLY); + if (device_fd < 0) { + LOG_ERROR("Failed to open loop (%d)\n", errno); + return -errno; + } + + if (ioctl(device_fd, LOOP_CLR_FD, 0) < 0) { + LOG_ERROR("Failed to destroy loop (%d)\n", errno); + rc = -errno; + } + + close(device_fd); + return rc; +} + +int AsecStart(void *Handle) +{ + struct asec_context *ctx = (struct asec_context *) Handle; + char value[PROPERTY_VALUE_MAX]; + int rc = 0; + + if (!ctx) + return -EINVAL; + + if (ctx->started) + return -EBUSY; + + LOG_ASEC("AsecStart(%s):\n", ctx->name); + + NotifyAsecState(ASEC_BUSY, ctx->dstPath); + + if ((rc = AsecLoadModules()) < 0) { + LOG_ERROR("AsecStart: Failed to load kernel modules\n"); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + return rc; + } + + if ((rc = AsecLoadGenerateKey(ctx))) { + LOG_ERROR("AsecStart: Failed to load / generate key\n"); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + return rc; + } + + if ((rc = AsecOpenCreateCache(ctx)) < 0) { + LOG_ERROR("AsecStart: Failed to open / create cache\n"); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + return rc; + } + + if ((rc = AsecCreateLoop(ctx)) < 0) { + LOG_ERROR("AsecStart: Failed to create loop\n"); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + goto fail_closecache; + } + + if ((rc = AsecCreateDeviceMapping(ctx)) < 0) { + LOG_ERROR("AsecStart: Failed to create devmapping (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + goto fail_destroyloop; + } + + if (ctx->needs_format) { + if ((rc = AsecFormatFilesystem(ctx))) { + LOG_ERROR("AsecStart: Failed to format cache (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + goto fail_destroydm; + } + ctx->needs_format = 0; + } else { + if ((rc = AsecCheckFilesystem(ctx))) { + LOG_ERROR("AsecStart: Failed to check filesystem (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + goto fail_destroydm; + } + } + + if ((rc = AsecMountCache(ctx)) < 0) { + LOG_ERROR("AsecStart: Failed to mount cache (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + goto fail_destroydm; + } + + NotifyAsecState(ASEC_AVAILABLE, ctx->dstPath); + ctx->started = true; + + return rc; + + fail_destroydm: + AsecDestroyDeviceMapping(ctx); + fail_destroyloop: + AsecDestroyLoop(ctx); + fail_closecache: + AsecCloseCache(ctx); + return rc; +} + +int AsecStop(void *Handle) +{ + struct asec_context *ctx = (struct asec_context *) Handle; + int rc = 0; + + if (!ctx->started) + return -EINVAL; + + LOG_ASEC("AsecStop(%s):\n", ctx->name); + + NotifyAsecState(ASEC_BUSY, ctx->dstPath); + + if ((rc = AsecUnmountCache(ctx)) < 0) { + LOG_ERROR("AsecStop: Failed to unmount cache (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + return rc; + } + + if ((rc = AsecDestroyDeviceMapping(ctx)) < 0) { + LOG_ERROR("AsecStop: Failed to destroy devmapping (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + return rc; + } + + if ((rc = AsecDestroyLoop(ctx)) < 0) { + LOG_ERROR("AsecStop: Failed to destroy loop device (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + return rc; + } + + AsecCloseCache(ctx); + + if ((rc = AsecUnloadModules()) < 0) { + if (rc == -EAGAIN) { + LOG_ASEC("AsecStop: Kernel modules still in use\n"); + } else { + LOG_ERROR("AsecStop: Failed to unload kernel modules (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + return rc; + } + } + + ctx->started = false; + NotifyAsecState(ASEC_DISABLED, ctx->dstPath); + return rc; +} diff --git a/mountd/ASEC.h b/mountd/ASEC.h new file mode 100644 index 000000000..c87b28877 --- /dev/null +++ b/mountd/ASEC.h @@ -0,0 +1,66 @@ +#ifndef _ASEC_H +#define _ASEC_H + +#define ASEC_STORES_MAX 4 +#define MAX_LOOP 8 + +typedef enum AsecState { + // Feature disabled + ASEC_DISABLED, + + // Feature enabled and operational + ASEC_AVAILABLE, + + // Busy + ASEC_BUSY, + + // Internal Error + ASEC_FAILED_INTERR, + + // No media available + ASEC_FAILED_NOMEDIA, + + // Media is corrupt + ASEC_FAILED_BADMEDIA, + + // Key mismatch + ASEC_FAILED_BADKEY, +} AsecState; + +/* + * ASEC commands + */ +#define ASEC_CMD_SEND_STATUS "asec_send_status" +#define ASEC_CMD_ENABLE "asec_enable" +#define ASEC_CMD_DISABLE "asec_disable" + +/* + * ASEC events + */ + +// These events correspond to the states in the AsecState enum. +// A path to the ASEC mount point follows the colon +#define ASEC_EVENT_DISABLED "asec_disabled:" +#define ASEC_EVENT_AVAILABLE "asec_available:" +#define ASEC_EVENT_BUSY "asec_busy:" +#define ASEC_EVENT_FAILED_INTERR "asec_failed_interror:" +#define ASEC_EVENT_FAILED_NOMEDIA "asec_failed_nomedia" +#define ASEC_EVENT_FAILED_BADMEDIA "asec_failed_badmedia:" +#define ASEC_EVENT_FAILED_BADKEY "asec_failed_badkey:" + +/* + * System Properties + */ + +#define ASEC_ENABLED "asec.enabled" + +#define ASEC_STATUS "ro.asec.status" +#define ASEC_STATUS_DISABLED "disabled" +#define ASEC_STATUS_AVAILABLE "available" +#define ASEC_STATUS_BUSY "busy" +#define ASEC_STATUS_FAILED_INTERR "internal_error" +#define ASEC_STATUS_FAILED_NOMEDIA "no_media" +#define ASEC_STATUS_FAILED_BADMEDIA "bad_media" +#define ASEC_STATUS_FAILED_BADKEY "bad_key" + +#endif diff --git a/mountd/Android.mk b/mountd/Android.mk index 87bcef3ec..bc128ac0e 100644 --- a/mountd/Android.mk +++ b/mountd/Android.mk @@ -6,7 +6,8 @@ LOCAL_SRC_FILES:= \ AutoMount.c \ ProcessKiller.c \ Server.c \ - mountd.c + mountd.c \ + ASEC.c LOCAL_MODULE:= mountd diff --git a/mountd/AutoMount.c b/mountd/AutoMount.c index 09e97590a..0aac871d5 100644 --- a/mountd/AutoMount.c +++ b/mountd/AutoMount.c @@ -76,17 +76,20 @@ typedef struct MountPoint { // mount point for device const char* mountPoint; + + // path to the UMS driver file for specifying the block device path + const char* driverStorePath; // true if device can be shared via // USB mass storage boolean enableUms; - + + // Array of ASEC handles + void *asecHandles[ASEC_STORES_MAX]; + // true if the device is being shared via USB mass storage boolean umsActive; - // logical unit number (for UMS) - int lun; - // current state of the mount point MountState state; @@ -100,11 +103,13 @@ typedef struct MountPoint { // list of our mount points (does not change after initialization) static MountPoint* sMountPointList = NULL; -static int sNextLun = 0; boolean gMassStorageEnabled = false; boolean gMassStorageConnected = false; static pthread_t sAutoMountThread = 0; +static pid_t gExcludedPids[2] = {-1, -1}; + +static const char FSCK_MSDOS_PATH[] = "/system/bin/fsck_msdos"; // number of mount points that have timeouts pending static int sRetriesPending = 0; @@ -116,15 +121,18 @@ static pthread_mutex_t sMutex = PTHREAD_MUTEX_INITIALIZER; // via USB mass storage. static void SetBackingStore(MountPoint* mp, boolean enable) { - char path[PATH_MAX]; int fd; + if (!mp->driverStorePath) { + LOG_ERROR("no driver_store_path specified in config file for %s", mp->device); + return; + } + LOG_MOUNT("SetBackingStore enable: %s\n", (enable ? "true" : "false")); - snprintf(path, sizeof(path), "/sys/devices/platform/usb_mass_storage/lun%d/file", mp->lun); - fd = open(path, O_WRONLY); + fd = open(mp->driverStorePath, O_WRONLY); if (fd < 0) { - LOG_ERROR("could not open %s\n", path); + LOG_ERROR("could not open driver_store_path %s\n", mp->driverStorePath); } else { @@ -192,6 +200,56 @@ static boolean IsLoopMounted(const char* path) return result; } +static int CheckFilesystem(const char *device) +{ + char cmdline[255]; + int rc; + + // XXX: SAN: Check for FAT signature + + int result = access(FSCK_MSDOS_PATH, X_OK); + if (result != 0) { + LOG_MOUNT("CheckFilesystem(%s): fsck_msdos not found (skipping checks)\n", device); + return 0; + } + + sprintf(cmdline, "%s -p %s", FSCK_MSDOS_PATH, device); + LOG_MOUNT("Checking filesystem (%s)\n", cmdline); + + // XXX: Notify framework we're disk checking + + // XXX: PROTECT FROM VIKING KILLER + if ((rc = system(cmdline)) < 0) { + LOG_ERROR("Error executing disk check command (%d)\n", errno); + return -errno; + } + + rc = WEXITSTATUS(rc); + + if (rc == 0) { + LOG_MOUNT("Filesystem check completed OK\n"); + return 0; + } else if (rc == 1) { + LOG_MOUNT("Filesystem check failed (invalid usage)\n"); + return -EINVAL; + } else if (rc == 2) { + LOG_MOUNT("Filesystem check failed (unresolved issues)\n"); + return -EIO; + } else if (rc == 4) { + LOG_MOUNT("Filesystem check failed (root changed)\n"); + return -EIO; + } else if (rc == 8) { + LOG_MOUNT("Filesystem check failed (general failure)\n"); + return -EIO; + } else if (rc == 12) { + LOG_MOUNT("Filesystem check failed (exit signaled)\n"); + return -EIO; + } else { + LOG_MOUNT("Filesystem check failed (unknown exit code %d)\n", rc); + return -EIO; + } +} + static int DoMountDevice(const char* device, const char* mountPoint) { LOG_MOUNT("mounting %s at %s\n", device, mountPoint); @@ -237,6 +295,17 @@ static int DoMountDevice(const char* device, const char* mountPoint) if (result != 0) return result; + if ((result = CheckFilesystem(device))) { + LOG_ERROR("Not mounting filesystem due to check failure (%d)\n", result); + // XXX: Notify framework - need a new SDCARD state for the following: + // - SD cards which are not present + // - SD cards with no partition table + // - SD cards with no filesystem + // - SD cards with bad filesystem + return result; + } + + // Extra safety measures: flags |= MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC; // Also, set fmask = 711 so that files cannot be marked executable, @@ -254,6 +323,27 @@ static int DoMountDevice(const char* device, const char* mountPoint) if (result == 0) { NotifyMediaState(mountPoint, MEDIA_MOUNTED, (flags & MS_RDONLY) != 0); + + MountPoint* mp = sMountPointList; + while (mp) { + if (!strcmp(mountPoint, mp->mountPoint)) { + int i; + + for (i = 0; i < ASEC_STORES_MAX; i++) { + if (mp->asecHandles[i] != NULL) { + int a_result; + if ((a_result = AsecStart(mp->asecHandles[i])) < 0) { + LOG_ERROR("ASEC start failure (%d)\n", a_result); + } + } + } + break; + } + mp = mp -> next; + } + } else if (errno == EBUSY) { + // ignore EBUSY, since it usually means the device is already mounted + result = 0; } else { #if CREATE_MOUNT_POINTS rmdir(mountPoint); @@ -264,32 +354,39 @@ static int DoMountDevice(const char* device, const char* mountPoint) return result; } -static int DoUnmountDevice(const char* mountPoint) +static int DoUnmountDevice(MountPoint *mp) { - boolean loop = IsLoopMounted(mountPoint); - int result = umount(mountPoint); + boolean loop = IsLoopMounted(mp->mountPoint); + int i; + + for (i = 0; i < ASEC_STORES_MAX; i++) { + if (mp->asecHandles[i] && AsecIsStarted(mp->asecHandles[i])) + AsecStop(mp->asecHandles[i]); + } + + int result = umount(mp->mountPoint); LOG_MOUNT("umount returned %d errno: %d\n", result, errno); if (result == 0) { - if (loop) - { - // free the loop device - int loop_fd = open(LOOP_DEVICE, O_RDONLY); - if (loop_fd < -1) { - LOG_ERROR("open loop device failed\n"); - } - if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) { - LOG_ERROR("ioctl LOOP_CLR_FD failed\n"); - } - - close(loop_fd); - } - #if CREATE_MOUNT_POINTS rmdir(mountPoint); #endif - NotifyMediaState(mountPoint, MEDIA_UNMOUNTED, false); + NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTED, false); + } + + if (loop) + { + // free the loop device + int loop_fd = open(LOOP_DEVICE, O_RDONLY); + if (loop_fd < -1) { + LOG_ERROR("open loop device failed\n"); + } + if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) { + LOG_ERROR("ioctl LOOP_CLR_FD failed\n"); + } + + close(loop_fd); } // ignore EINVAL and ENOENT, since it usually means the device is already unmounted @@ -405,7 +502,7 @@ static void RequestUnmount(MountPoint* mp, MountState retryState) sync(); DropSystemCaches(); - if (DoUnmountDevice(mp->mountPoint) == 0) + if (DoUnmountDevice(mp) == 0) { SetState(mp, kUnmounted); if (retryState == kUnmountingForUms) @@ -499,6 +596,8 @@ static void HandleMediaInserted(const char* device) { MountPoint* mp = sMountPointList; + LOG_MOUNT("HandleMediaInserted(%s):\n", device); + while (mp) { // see if the device matches mount point's block device @@ -570,7 +669,7 @@ static void HandleRetries() } else if (mp->state == kUnmountingForEject || mp->state == kUnmountingForUms) { - if (DoUnmountDevice(mp->mountPoint) == 0) + if (DoUnmountDevice(mp) == 0) { // unmounting succeeded // start mass storage, if state is kUnmountingForUms @@ -591,8 +690,25 @@ static void HandleRetries() // send SIGKILL instead of SIGTERM if the first attempt did not succeed boolean sigkill = (mp->retryCount > MAX_UNMOUNT_RETRIES); + int i; + + for (i = 0; i < ASEC_STORES_MAX; i++) { + if (mp->asecHandles[i] && AsecIsStarted(mp->asecHandles[i])) { + LOG_MOUNT("Killing processes for ASEC path '%s'\n", + AsecMountPoint(mp->asecHandles[i])); + KillProcessesWithOpenFiles(AsecMountPoint(mp->asecHandles[i]), + sigkill, + gExcludedPids, sizeof(gExcludedPids) / sizeof(pid_t)); + + // Now that we've killed the processes, try to stop the volume again + AsecStop(mp->asecHandles[i]); + } + } + // unmounting the device is failing, so start killing processes - KillProcessesWithOpenFiles(mp->mountPoint, sigkill); + KillProcessesWithOpenFiles(mp->mountPoint, sigkill, gExcludedPids, + sizeof(gExcludedPids) / sizeof(pid_t)); + } } } @@ -673,6 +789,8 @@ static void* AutoMountThread(void* arg) int id; struct sigaction actions; + gExcludedPids[1] = getpid(); + memset(&actions, 0, sizeof(actions)); sigemptyset(&actions.sa_mask); actions.sa_flags = 0; @@ -826,7 +944,9 @@ void EnableMassStorage(boolean enable) void MountMedia(const char* mountPoint) { MountPoint* mp = sMountPointList; - + + LOG_MOUNT("MountMedia(%s)\n", mountPoint); + pthread_mutex_lock(&sMutex); while (mp) { @@ -880,27 +1000,48 @@ boolean IsMassStorageConnected() * ***********************************************/ -void AddMountPoint(const char* device, const char* mountPoint, boolean enableUms) +void *AddMountPoint(const char* device, const char* mountPoint, const char * driverStorePath, boolean enableUms) { MountPoint* newMountPoint; - LOG_MOUNT("AddMountPoint device: %s, mountPoint: %s\n", device, mountPoint); + LOG_MOUNT("AddMountPoint device: %s, mountPoint: %s driverStorePath: %s\n", device, mountPoint, driverStorePath); // add a new MountPoint to the head of our linked list newMountPoint = (MountPoint *)malloc(sizeof(MountPoint)); newMountPoint->device = device; newMountPoint->mountPoint = mountPoint; + newMountPoint->driverStorePath = driverStorePath; newMountPoint->enableUms = enableUms; newMountPoint->umsActive = false; - if (enableUms) - newMountPoint->lun = sNextLun++; newMountPoint->state = kUnmounted; newMountPoint->retryCount = 0; // add to linked list newMountPoint->next = sMountPointList; sMountPointList = newMountPoint; + return newMountPoint; } +int AddAsecToMountPoint(void *Mp, const char *name, const char *backing_file, const char *size, + const char *mount_point, const char *crypt) +{ + MountPoint *mp = (MountPoint *) Mp; + int i; + + for (i = 0; i < ASEC_STORES_MAX; i++) { + if (!mp->asecHandles[i]) + break; + } + + if (i == ASEC_STORES_MAX) { + LOG_ERROR("Maximum # of ASEC stores exceeded\n"); + return -EINVAL; + } + + if (!(mp->asecHandles[i] = AsecInit(name, mp->mountPoint, backing_file, size, mount_point, crypt))) + return -1; + + return 0; +} static void MountDevices() { MountPoint* mp = sMountPointList; @@ -913,6 +1054,8 @@ static void MountDevices() void StartAutoMounter() { + gExcludedPids[0] = getpid(); + gMassStorageConnected = ReadMassStorageState(); LOG_MOUNT(gMassStorageConnected ? "USB online\n" : "USB offline\n"); diff --git a/mountd/ProcessKiller.c b/mountd/ProcessKiller.c index 3ce7aa81c..e37777446 100644 --- a/mountd/ProcessKiller.c +++ b/mountd/ProcessKiller.c @@ -177,7 +177,7 @@ static int get_pid(const char* s) } // hunt down and kill processes that have files open on the given mount point -void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill) +void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill, int *excluded, int num_excluded) { DIR* dir; struct dirent* de; @@ -200,8 +200,21 @@ void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill) || CheckSymLink(pid, mountPoint, "exe", "executable path") // check executable path ) { - LOG_ERROR("Killing process %d\n", pid); - kill(pid, (sigkill ? SIGKILL : SIGTERM)); + int i; + boolean hit = false; + + for (i = 0; i < num_excluded; i++) { + if (pid == excluded[i]) { + LOG_ERROR("I just need a little more TIME captain!\n"); + hit = true; + break; + } + } + + if (!hit) { + LOG_ERROR("Killing process %d\n", pid); + kill(pid, (sigkill ? SIGKILL : SIGTERM)); + } } } diff --git a/mountd/Server.c b/mountd/Server.c index 14b383001..64459bd88 100644 --- a/mountd/Server.c +++ b/mountd/Server.c @@ -19,6 +19,7 @@ */ #include "mountd.h" +#include "ASEC.h" #include #include @@ -43,6 +44,10 @@ static pthread_mutex_t sWriteMutex = PTHREAD_MUTEX_INITIALIZER; // path for media that failed to mount before the runtime is connected static char* sDeferredUnmountableMediaPath = NULL; +// last asec msg before the runtime was connected +static char* sAsecDeferredMessage = NULL; +static char* sAsecDeferredArgument = NULL; + static int Write(const char* message) { int result = -1; @@ -107,6 +112,18 @@ static void DoCommand(const char* command) { const char* path = command + strlen(MOUNTD_EJECT_MEDIA); UnmountMedia(path); + } + else if (strncmp(command, ASEC_CMD_ENABLE, strlen(ASEC_CMD_ENABLE)) == 0) { + LOG_ASEC("Got ASEC_CMD_ENABLE\n"); + // XXX: SAN: Impliment + } + else if (strncmp(command, ASEC_CMD_DISABLE, strlen(ASEC_CMD_DISABLE)) == 0) { + LOG_ASEC("Got ASEC_CMD_DISABLE\n"); + // XXX: SAN: Impliment + } + else if (strncmp(command, ASEC_CMD_SEND_STATUS, strlen(ASEC_CMD_SEND_STATUS)) == 0) { + LOG_ASEC("Got ASEC_CMD_SEND_STATUS\n"); + // XXX: SAN: Impliment } else LOGE("unknown command %s\n", command); @@ -145,6 +162,15 @@ int RunServer() sDeferredUnmountableMediaPath = NULL; } + if (sAsecDeferredMessage) { + + if (Write2(sAsecDeferredMessage, sAsecDeferredArgument) < 0) + LOG_ERROR("Failed to deliver deferred ASEC msg to framework\n"); + free(sAsecDeferredMessage); + free(sAsecDeferredArgument); + sAsecDeferredMessage = sAsecDeferredArgument = NULL; + } + while (1) { char buffer[101]; @@ -187,6 +213,61 @@ void SendUnmountRequest(const char* path) Write2(MOUNTD_REQUEST_EJECT, path); } +void NotifyAsecState(AsecState state, const char *argument) +{ + const char *event = NULL; + const char *status = NULL; + boolean deferr = true;; + + switch (state) { + case ASEC_DISABLED: + event = ASEC_EVENT_DISABLED; + status = ASEC_STATUS_DISABLED; + break; + case ASEC_AVAILABLE: + event = ASEC_EVENT_AVAILABLE; + status = ASEC_STATUS_AVAILABLE; + break; + case ASEC_BUSY: + event = ASEC_EVENT_BUSY; + status = ASEC_STATUS_BUSY; + deferr = false; + break; + case ASEC_FAILED_INTERR: + event = ASEC_EVENT_FAILED_INTERR; + status = ASEC_STATUS_FAILED_INTERR; + break; + case ASEC_FAILED_NOMEDIA: + event = ASEC_EVENT_FAILED_NOMEDIA; + status = ASEC_STATUS_FAILED_NOMEDIA; + break; + case ASEC_FAILED_BADMEDIA: + event = ASEC_EVENT_FAILED_BADMEDIA; + status = ASEC_STATUS_FAILED_BADMEDIA; + break; + case ASEC_FAILED_BADKEY: + event = ASEC_EVENT_FAILED_BADKEY; + status = ASEC_STATUS_FAILED_BADKEY; + break; + default: + LOG_ERROR("unknown AsecState %d in NotifyAsecState\n", state); + return; + } + + property_set(ASEC_STATUS, status); + + int result = Write2(event, argument); + if ((result < 0) && deferr) { + if (sAsecDeferredMessage) + free(sAsecDeferredMessage); + sAsecDeferredMessage = strdup(event); + if (sAsecDeferredArgument) + free(sAsecDeferredArgument); + sAsecDeferredArgument = strdup(argument); + LOG_ASEC("Deferring event '%s' arg '%s' until framework connects\n", event, argument); + } +} + void NotifyMediaState(const char* path, MediaState state, boolean readOnly) { const char* event = NULL; diff --git a/mountd/dm-ioctl.h b/mountd/dm-ioctl.h new file mode 100644 index 000000000..ee5c3508b --- /dev/null +++ b/mountd/dm-ioctl.h @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2001 - 2003 Sistina Software (UK) Limited. + * Copyright (C) 2004 - 2005 Red Hat, Inc. All rights reserved. + * + * This file is released under the LGPL. + */ + +#ifndef _LINUX_DM_IOCTL_V4_H +#define _LINUX_DM_IOCTL_V4_H + +#ifdef linux +# include +#endif + +#define DM_DIR "mapper" /* Slashes not supported */ +#define DM_MAX_TYPE_NAME 16 +#define DM_NAME_LEN 128 +#define DM_UUID_LEN 129 + +/* + * A traditional ioctl interface for the device mapper. + * + * Each device can have two tables associated with it, an + * 'active' table which is the one currently used by io passing + * through the device, and an 'inactive' one which is a table + * that is being prepared as a replacement for the 'active' one. + * + * DM_VERSION: + * Just get the version information for the ioctl interface. + * + * DM_REMOVE_ALL: + * Remove all dm devices, destroy all tables. Only really used + * for debug. + * + * DM_LIST_DEVICES: + * Get a list of all the dm device names. + * + * DM_DEV_CREATE: + * Create a new device, neither the 'active' or 'inactive' table + * slots will be filled. The device will be in suspended state + * after creation, however any io to the device will get errored + * since it will be out-of-bounds. + * + * DM_DEV_REMOVE: + * Remove a device, destroy any tables. + * + * DM_DEV_RENAME: + * Rename a device. + * + * DM_SUSPEND: + * This performs both suspend and resume, depending which flag is + * passed in. + * Suspend: This command will not return until all pending io to + * the device has completed. Further io will be deferred until + * the device is resumed. + * Resume: It is no longer an error to issue this command on an + * unsuspended device. If a table is present in the 'inactive' + * slot, it will be moved to the active slot, then the old table + * from the active slot will be _destroyed_. Finally the device + * is resumed. + * + * DM_DEV_STATUS: + * Retrieves the status for the table in the 'active' slot. + * + * DM_DEV_WAIT: + * Wait for a significant event to occur to the device. This + * could either be caused by an event triggered by one of the + * targets of the table in the 'active' slot, or a table change. + * + * DM_TABLE_LOAD: + * Load a table into the 'inactive' slot for the device. The + * device does _not_ need to be suspended prior to this command. + * + * DM_TABLE_CLEAR: + * Destroy any table in the 'inactive' slot (ie. abort). + * + * DM_TABLE_DEPS: + * Return a set of device dependencies for the 'active' table. + * + * DM_TABLE_STATUS: + * Return the targets status for the 'active' table. + * + * DM_TARGET_MSG: + * Pass a message string to the target at a specific offset of a device. + * + * DM_DEV_SET_GEOMETRY: + * Set the geometry of a device by passing in a string in this format: + * + * "cylinders heads sectors_per_track start_sector" + * + * Beware that CHS geometry is nearly obsolete and only provided + * for compatibility with dm devices that can be booted by a PC + * BIOS. See struct hd_geometry for range limits. Also note that + * the geometry is erased if the device size changes. + */ + +/* + * All ioctl arguments consist of a single chunk of memory, with + * this structure at the start. If a uuid is specified any + * lookup (eg. for a DM_INFO) will be done on that, *not* the + * name. + */ +struct dm_ioctl { + /* + * The version number is made up of three parts: + * major - no backward or forward compatibility, + * minor - only backwards compatible, + * patch - both backwards and forwards compatible. + * + * All clients of the ioctl interface should fill in the + * version number of the interface that they were + * compiled with. + * + * All recognised ioctl commands (ie. those that don't + * return -ENOTTY) fill out this field, even if the + * command failed. + */ + uint32_t version[3]; /* in/out */ + uint32_t data_size; /* total size of data passed in + * including this struct */ + + uint32_t data_start; /* offset to start of data + * relative to start of this struct */ + + uint32_t target_count; /* in/out */ + int32_t open_count; /* out */ + uint32_t flags; /* in/out */ + uint32_t event_nr; /* in/out */ + uint32_t padding; + + uint64_t dev; /* in/out */ + + char name[DM_NAME_LEN]; /* device name */ + char uuid[DM_UUID_LEN]; /* unique identifier for + * the block device */ + char data[7]; /* padding or data */ +}; + +/* + * Used to specify tables. These structures appear after the + * dm_ioctl. + */ +struct dm_target_spec { + uint64_t sector_start; + uint64_t length; + int32_t status; /* used when reading from kernel only */ + + /* + * Location of the next dm_target_spec. + * - When specifying targets on a DM_TABLE_LOAD command, this value is + * the number of bytes from the start of the "current" dm_target_spec + * to the start of the "next" dm_target_spec. + * - When retrieving targets on a DM_TABLE_STATUS command, this value + * is the number of bytes from the start of the first dm_target_spec + * (that follows the dm_ioctl struct) to the start of the "next" + * dm_target_spec. + */ + uint32_t next; + + char target_type[DM_MAX_TYPE_NAME]; + + /* + * Parameter string starts immediately after this object. + * Be careful to add padding after string to ensure correct + * alignment of subsequent dm_target_spec. + */ +}; + +/* + * Used to retrieve the target dependencies. + */ +struct dm_target_deps { + uint32_t count; /* Array size */ + uint32_t padding; /* unused */ + uint64_t dev[0]; /* out */ +}; + +/* + * Used to get a list of all dm devices. + */ +struct dm_name_list { + uint64_t dev; + uint32_t next; /* offset to the next record from + the _start_ of this */ + char name[0]; +}; + +/* + * Used to retrieve the target versions + */ +struct dm_target_versions { + uint32_t next; + uint32_t version[3]; + + char name[0]; +}; + +/* + * Used to pass message to a target + */ +struct dm_target_msg { + uint64_t sector; /* Device sector */ + + char message[0]; +}; + +/* + * If you change this make sure you make the corresponding change + * to dm-ioctl.c:lookup_ioctl() + */ +enum { + /* Top level cmds */ + DM_VERSION_CMD = 0, + DM_REMOVE_ALL_CMD, + DM_LIST_DEVICES_CMD, + + /* device level cmds */ + DM_DEV_CREATE_CMD, + DM_DEV_REMOVE_CMD, + DM_DEV_RENAME_CMD, + DM_DEV_SUSPEND_CMD, + DM_DEV_STATUS_CMD, + DM_DEV_WAIT_CMD, + + /* Table level cmds */ + DM_TABLE_LOAD_CMD, + DM_TABLE_CLEAR_CMD, + DM_TABLE_DEPS_CMD, + DM_TABLE_STATUS_CMD, + + /* Added later */ + DM_LIST_VERSIONS_CMD, + DM_TARGET_MSG_CMD, + DM_DEV_SET_GEOMETRY_CMD +}; + +#define DM_IOCTL 0xfd + +#define DM_VERSION _IOWR(DM_IOCTL, DM_VERSION_CMD, struct dm_ioctl) +#define DM_REMOVE_ALL _IOWR(DM_IOCTL, DM_REMOVE_ALL_CMD, struct dm_ioctl) +#define DM_LIST_DEVICES _IOWR(DM_IOCTL, DM_LIST_DEVICES_CMD, struct dm_ioctl) + +#define DM_DEV_CREATE _IOWR(DM_IOCTL, DM_DEV_CREATE_CMD, struct dm_ioctl) +#define DM_DEV_REMOVE _IOWR(DM_IOCTL, DM_DEV_REMOVE_CMD, struct dm_ioctl) +#define DM_DEV_RENAME _IOWR(DM_IOCTL, DM_DEV_RENAME_CMD, struct dm_ioctl) +#define DM_DEV_SUSPEND _IOWR(DM_IOCTL, DM_DEV_SUSPEND_CMD, struct dm_ioctl) +#define DM_DEV_STATUS _IOWR(DM_IOCTL, DM_DEV_STATUS_CMD, struct dm_ioctl) +#define DM_DEV_WAIT _IOWR(DM_IOCTL, DM_DEV_WAIT_CMD, struct dm_ioctl) + +#define DM_TABLE_LOAD _IOWR(DM_IOCTL, DM_TABLE_LOAD_CMD, struct dm_ioctl) +#define DM_TABLE_CLEAR _IOWR(DM_IOCTL, DM_TABLE_CLEAR_CMD, struct dm_ioctl) +#define DM_TABLE_DEPS _IOWR(DM_IOCTL, DM_TABLE_DEPS_CMD, struct dm_ioctl) +#define DM_TABLE_STATUS _IOWR(DM_IOCTL, DM_TABLE_STATUS_CMD, struct dm_ioctl) + +#define DM_LIST_VERSIONS _IOWR(DM_IOCTL, DM_LIST_VERSIONS_CMD, struct dm_ioctl) + +#define DM_TARGET_MSG _IOWR(DM_IOCTL, DM_TARGET_MSG_CMD, struct dm_ioctl) +#define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) + +#define DM_VERSION_MAJOR 4 +#define DM_VERSION_MINOR 13 +#define DM_VERSION_PATCHLEVEL 0 +#define DM_VERSION_EXTRA "-ioctl (2007-10-18)" + +/* Status bits */ +#define DM_READONLY_FLAG (1 << 0) /* In/Out */ +#define DM_SUSPEND_FLAG (1 << 1) /* In/Out */ +#define DM_PERSISTENT_DEV_FLAG (1 << 3) /* In */ + +/* + * Flag passed into ioctl STATUS command to get table information + * rather than current status. + */ +#define DM_STATUS_TABLE_FLAG (1 << 4) /* In */ + +/* + * Flags that indicate whether a table is present in either of + * the two table slots that a device has. + */ +#define DM_ACTIVE_PRESENT_FLAG (1 << 5) /* Out */ +#define DM_INACTIVE_PRESENT_FLAG (1 << 6) /* Out */ + +/* + * Indicates that the buffer passed in wasn't big enough for the + * results. + */ +#define DM_BUFFER_FULL_FLAG (1 << 8) /* Out */ + +/* + * This flag is now ignored. + */ +#define DM_SKIP_BDGET_FLAG (1 << 9) /* In */ + +/* + * Set this to avoid attempting to freeze any filesystem when suspending. + */ +#define DM_SKIP_LOCKFS_FLAG (1 << 10) /* In */ + +/* + * Set this to suspend without flushing queued ios. + */ +#define DM_NOFLUSH_FLAG (1 << 11) /* In */ + +#endif /* _LINUX_DM_IOCTL_H */ diff --git a/mountd/mountd.c b/mountd/mountd.c index fb54fe68d..27ec8de90 100644 --- a/mountd/mountd.c +++ b/mountd/mountd.c @@ -39,6 +39,54 @@ FILE* logFile; #endif +struct asec_cfg { + const char *name; + const char *backing_file; + const char *size; + const char *mount_point; + const char *crypt; +}; + +static int ProcessAsecData(cnode *node, struct asec_cfg *stores, int idx) +{ + cnode *child = node->first_child; + const char *name = NULL; + const char *file = NULL; + const char *size = NULL; + const char *mp = NULL; + const char *crypt = NULL; + + LOG_ASEC("ProcessAsecData(%s, %p, %d)\n", node->name, stores, idx); + + while (child) { + if (!strcmp(child->name, "name")) + name = child->value; + else if (!strcmp(child->name, "backing_file")) + file = child->value; + else if (!strcmp(child->name, "size")) + size = child->value; + else if (!strcmp(child->name, "mount_point")) + mp = child->value; + else if (!strcmp(child->name, "crypt")) + crypt = child->value; + child = child->next; + } + + if (!name || !file || !size || !mp || !crypt) { + LOG_ERROR("Missing required token from config. Skipping ASEC volume\n"); + return -1; + } else if (idx == ASEC_STORES_MAX) { + LOG_ERROR("Maximum # of ASEC stores already defined\n"); + return -1; + } + + stores[idx].name = name; + stores[idx].backing_file = file; + stores[idx].size = size; + stores[idx].mount_point = mp; + stores[idx].crypt = crypt; + return ++idx; +} static void ReadConfigFile(const char* path) { @@ -54,18 +102,31 @@ static void ReadConfigFile(const char* path) { const char* block_device = NULL; const char* mount_point = NULL; + const char* driver_store_path = NULL; boolean enable_ums = false; cnode* child = node->first_child; - + struct asec_cfg asec_stores[ASEC_STORES_MAX]; + int asec_idx = 0; + + memset(asec_stores, 0, sizeof(asec_stores)); + while (child) { const char* name = child->name; const char* value = child->value; - - if (strcmp(name, "block_device") == 0) + + if (!strncmp(name, "asec_", 5)) { + int rc = ProcessAsecData(child, asec_stores, asec_idx); + if (rc < 0) { + LOG_ERROR("Error processing ASEC cfg data\n"); + } else + asec_idx = rc; + } else if (strcmp(name, "block_device") == 0) block_device = value; else if (strcmp(name, "mount_point") == 0) mount_point = value; + else if (strcmp(name, "driver_store_path") == 0) + driver_store_path = value; else if (strcmp(name, "enable_ums") == 0 && strcmp(value, "true") == 0) enable_ums = true; @@ -76,7 +137,14 @@ static void ReadConfigFile(const char* path) // mount point and removable fields are optional if (block_device && mount_point) { - AddMountPoint(block_device, mount_point, enable_ums); + void *mp = AddMountPoint(block_device, mount_point, driver_store_path, enable_ums); + int i; + + for (i = 0; i < asec_idx; i++) { + AddAsecToMountPoint(mp, asec_stores[i].name, asec_stores[i].backing_file, + asec_stores[i].size, asec_stores[i].mount_point, + asec_stores[i].crypt); + } } } diff --git a/mountd/mountd.h b/mountd/mountd.h index 746a41483..9b6248479 100644 --- a/mountd/mountd.h +++ b/mountd/mountd.h @@ -20,21 +20,28 @@ #define LOG_TAG "mountd" #include "cutils/log.h" +#include "ASEC.h" + typedef int boolean; enum { false = 0, true = 1 }; +#define WEXITSTATUS(status) (((status) & 0xff00) >> 8) + // Set this for logging error messages #define ENABLE_LOG_ERROR // set this to log automounter events -//#define ENABLE_LOG_MOUNT +#define ENABLE_LOG_MOUNT // set this to log server events //#define ENABLE_LOG_SERVER +// set this to log ASEC events +#define ENABLE_LOG_ASEC + #ifdef ENABLE_LOG_ERROR #define LOG_ERROR(fmt, args...) \ { LOGE(fmt , ## args); } @@ -59,6 +66,14 @@ enum { do { } while (0) #endif /* ENABLE_LOG_SERVER */ +#ifdef ENABLE_LOG_ASEC +#define LOG_ASEC(fmt, args...) \ + { LOGD(fmt , ## args); } +#else +#define LOG_ASEC(fmt, args...) \ + do { } while (0) +#endif /* ENABLE_LOG_ASEC */ + typedef enum MediaState { // no media in SD card slot @@ -135,7 +150,11 @@ void UnmountMedia(const char* mountPoint); void EnableMassStorage(boolean enable); // call this before StartAutoMounter() to add a mount point to monitor -void AddMountPoint(const char* device, const char* mountPoint, boolean enableUms); +void *AddMountPoint(const char* device, const char* mountPoint, const char* driverStorePath, + boolean enableUms); + +int AddAsecToMountPoint(void *Mp, const char *name, const char *backing_file, + const char *size, const char *mount_point, const char *crypt); // start automounter thread void StartAutoMounter(); @@ -144,9 +163,19 @@ void StartAutoMounter(); void NotifyExistingMounts(); +// ASEC.c + +void *AsecInit(const char *Name, const char *SrcPath, const char *BackingFile, + const char *Size, const char *DstPath, const char *Crypt); +int AsecStart(void *Handle); +int AsecStop(void *Handle); +void AsecDeinit(void *Handle); +boolean AsecIsStarted(void *Handle); +const char *AsecMountPoint(void *Handle); + // ProcessKiller.c -void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill); +void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill, pid_t *excluded, int num_excluded); // Server.c @@ -155,5 +184,5 @@ int RunServer(); void SendMassStorageConnected(boolean connected); void SendUnmountRequest(const char* path); void NotifyMediaState(const char* path, MediaState state, boolean readOnly); - +void NotifyAsecState(AsecState state, const char *argument); #endif // MOUNTD_H__ diff --git a/rootdir/Android.mk b/rootdir/Android.mk index 756005b30..7ff1a74a1 100644 --- a/rootdir/Android.mk +++ b/rootdir/Android.mk @@ -7,8 +7,7 @@ copy_from := \ etc/mountd.conf \ etc/dbus.conf \ etc/init.goldfish.sh \ - etc/hosts \ - etc/hcid.conf + etc/hosts dont_copy := \ etc/init.gprs-pppd \ @@ -28,10 +27,13 @@ ALL_PREBUILT += $(copy_to) # files that live under /... +# Only copy init.rc if the target doesn't have its own. +ifneq ($(TARGET_PROVIDES_INIT_RC),true) file := $(TARGET_ROOT_OUT)/init.rc $(file) : $(LOCAL_PATH)/init.rc | $(ACP) $(transform-prebuilt-to-target) ALL_PREBUILT += $(file) +endif file := $(TARGET_ROOT_OUT)/init.goldfish.rc $(file) : $(LOCAL_PATH)/etc/init.goldfish.rc | $(ACP) diff --git a/rootdir/etc/dbus.conf b/rootdir/etc/dbus.conf index 8742345eb..75586b92d 100644 --- a/rootdir/etc/dbus.conf +++ b/rootdir/etc/dbus.conf @@ -5,9 +5,6 @@ system - - - EXTERNAL @@ -17,51 +14,14 @@ systems.) --> unix:path=/dev/socket/dbus + - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/rootdir/etc/hcid.conf b/rootdir/etc/hcid.conf deleted file mode 100644 index 56df63a2d..000000000 --- a/rootdir/etc/hcid.conf +++ /dev/null @@ -1,64 +0,0 @@ -# -# HCI daemon configuration file. -# - -# HCId options -options { - # Automatically initialize new devices - autoinit yes; - - # Security Manager mode - # none - Security manager disabled - # auto - Use local PIN for incoming connections - # user - Always ask user for a PIN - # - security user; - - # Pairing mode - # none - Pairing disabled - # multi - Allow pairing with already paired devices - # once - Pair once and deny successive attempts - pairing multi; -} - -# Default settings for HCI devices -device { - # Local device name - # %d - device id - # %h - host name - # %b - ro.product.brand - # %m - ro.product.model - # %n - ro.product.name - name "%m"; - - # Local device class - # 0x400000 - Service class: Telephony - # 0x000200 - Major class: Phone - # 0x00000C - Minor class: Smart phone - class 0x40020C; - - # Default packet type - #pkt_type DH1,DM1,HV1; - - # Inquiry and Page scan - iscan disable; - pscan enable; - - # Page timeout (in 0.625ms slots): 10 seconds - pageto 16384; - - # Default link mode - # none - no specific policy - # accept - always accept incoming connections - # master - become master on incoming connections, - # deny role switch on outgoing connections - lm accept; - - # Default link policy - # none - no specific policy - # rswitch - allow role switch - # hold - allow hold mode - # sniff - allow sniff mode - # park - allow park mode - lp rswitch,hold,sniff,park; -} diff --git a/rootdir/etc/init.goldfish.rc b/rootdir/etc/init.goldfish.rc index 5975974b0..96480f3e8 100644 --- a/rootdir/etc/init.goldfish.rc +++ b/rootdir/etc/init.goldfish.rc @@ -16,11 +16,11 @@ on boot stop dund stop akmd - setprop app.setupwizard.disable 1 + setprop ro.setupwizard.mode EMULATOR # enable Google-specific location features, # like NetworkLocationProvider and LocationCollector - setprop ro.com.google.enable_google_location_features 1 + setprop ro.com.google.locationfeatures 1 # For the emulator, which bypasses Setup Wizard, you can specify # account info for the device via these two properties. Google @@ -39,8 +39,9 @@ service goldfish-setup /system/etc/init.goldfish.sh oneshot service qemud /system/bin/qemud - socket qemud_gsm stream 666 - socket qemud_gps stream 666 + socket qemud_gsm stream 666 + socket qemud_gps stream 666 + socket qemud_control stream 666 oneshot # -Q is a special logcat option that forces the diff --git a/rootdir/etc/mountd.conf b/rootdir/etc/mountd.conf index d9dfdede3..094a2c7af 100644 --- a/rootdir/etc/mountd.conf +++ b/rootdir/etc/mountd.conf @@ -10,4 +10,10 @@ mount { ## true if this mount point can be shared via USB mass storage enable_ums true + + ## path to the UMS driver file for specifying the block device path + ## use this for the mass_storage function driver + driver_store_path /sys/devices/platform/usb_mass_storage/lun0/file + ## use this for android_usb composite gadget driver + ##driver_store_path /sys/devices/platform/msm_hsusb/gadget/lun0/file } diff --git a/rootdir/init.rc b/rootdir/init.rc index 23daa0b00..3f8c6a01b 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -1,6 +1,8 @@ on init +sysclktz 0 + loglevel 3 # setup the global environment @@ -118,6 +120,12 @@ on boot chown radio system /sys/android_power/acquire_full_wake_lock chown radio system /sys/android_power/acquire_partial_wake_lock chown radio system /sys/android_power/release_wake_lock + chown radio system /sys/power/state + chown radio system /sys/power/wake_lock + chown radio system /sys/power/wake_unlock + chmod 0660 /sys/power/state + chmod 0660 /sys/power/wake_lock + chmod 0660 /sys/power/wake_unlock chown system system /sys/class/timed_output/vibrator/enable chown system system /sys/class/leds/keyboard-backlight/brightness chown system system /sys/class/leds/lcd-backlight/brightness @@ -193,6 +201,7 @@ service ril-daemon /system/bin/rild 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 service media /system/bin/mediaserver user media @@ -208,7 +217,8 @@ service dbus /system/bin/dbus-daemon --system --nofork user bluetooth group bluetooth net_bt_admin -service hcid /system/bin/hcid -s -n -f /etc/hcid.conf +#STOPSHIP: disable the verbose logging +service hcid /system/bin/logwrapper /system/bin/hcid -d -s -n -f /etc/bluez/hcid.conf socket bluetooth stream 660 bluetooth bluetooth socket dbus_bluetooth stream 660 bluetooth bluetooth # init.rc does not yet support applying capabilities, so run as root and diff --git a/toolbox/Android.mk b/toolbox/Android.mk index df8ba9fb6..b0c241e7d 100644 --- a/toolbox/Android.mk +++ b/toolbox/Android.mk @@ -83,3 +83,8 @@ $(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk $(hide) ln -sf $(TOOLBOX_BINARY) $@ ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS) + +# We need this so that the installed files could be picked up based on the +# local module name +ALL_MODULES.$(LOCAL_MODULE).INSTALLED := \ + $(ALL_MODULES.$(LOCAL_MODULE).INSTALLED) $(SYMLINKS)