Merge commit 'b2042f7263c7bbacc5115de4a42c5a96b64a06f2' into HEAD
This commit is contained in:
commit
aad358fbc1
|
@ -53,3 +53,4 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc)
|
|||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/reboot)
|
||||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop)
|
||||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop)
|
||||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/lmkd_intermediates/import_includes)
|
||||
|
|
|
@ -40,13 +40,11 @@ ifeq ($(HOST_OS),windows)
|
|||
ifneq ($(strip $(USE_CYGWIN)),)
|
||||
# Pure cygwin case
|
||||
LOCAL_LDLIBS += -lpthread -lgdi32
|
||||
LOCAL_C_INCLUDES += /usr/include/w32api/ddk
|
||||
endif
|
||||
ifneq ($(strip $(USE_MINGW)),)
|
||||
# MinGW under Linux case
|
||||
LOCAL_LDLIBS += -lws2_32 -lgdi32
|
||||
USE_SYSDEPS_WIN32 := 1
|
||||
LOCAL_C_INCLUDES += /usr/i586-mingw32msvc/include/ddk
|
||||
endif
|
||||
LOCAL_C_INCLUDES += development/host/windows/usb/api/
|
||||
endif
|
||||
|
@ -175,7 +173,7 @@ LOCAL_C_INCLUDES += external/openssl/include
|
|||
|
||||
LOCAL_MODULE := adb
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libzipfile libunz libcutils
|
||||
LOCAL_STATIC_LIBRARIES := libzipfile libunz libcutils liblog
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := libcrypto
|
||||
|
||||
|
|
80
adb/adb.h
80
adb/adb.h
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <limits.h>
|
||||
|
||||
#include "adb_trace.h"
|
||||
#include "transport.h" /* readx(), writex() */
|
||||
|
||||
#define MAX_PAYLOAD 4096
|
||||
|
@ -337,85 +338,6 @@ void put_apacket(apacket *p);
|
|||
int check_header(apacket *p);
|
||||
int check_data(apacket *p);
|
||||
|
||||
/* define ADB_TRACE to 1 to enable tracing support, or 0 to disable it */
|
||||
|
||||
#define ADB_TRACE 1
|
||||
|
||||
/* IMPORTANT: if you change the following list, don't
|
||||
* forget to update the corresponding 'tags' table in
|
||||
* the adb_trace_init() function implemented in adb.c
|
||||
*/
|
||||
typedef enum {
|
||||
TRACE_ADB = 0, /* 0x001 */
|
||||
TRACE_SOCKETS,
|
||||
TRACE_PACKETS,
|
||||
TRACE_TRANSPORT,
|
||||
TRACE_RWX, /* 0x010 */
|
||||
TRACE_USB,
|
||||
TRACE_SYNC,
|
||||
TRACE_SYSDEPS,
|
||||
TRACE_JDWP, /* 0x100 */
|
||||
TRACE_SERVICES,
|
||||
TRACE_AUTH,
|
||||
} AdbTrace;
|
||||
|
||||
#if ADB_TRACE
|
||||
|
||||
#if !ADB_HOST
|
||||
/*
|
||||
* When running inside the emulator, guest's adbd can connect to 'adb-debug'
|
||||
* qemud service that can display adb trace messages (on condition that emulator
|
||||
* has been started with '-debug adb' option).
|
||||
*/
|
||||
|
||||
/* Delivers a trace message to the emulator via QEMU pipe. */
|
||||
void adb_qemu_trace(const char* fmt, ...);
|
||||
/* Macro to use to send ADB trace messages to the emulator. */
|
||||
#define DQ(...) adb_qemu_trace(__VA_ARGS__)
|
||||
#else
|
||||
#define DQ(...) ((void)0)
|
||||
#endif /* !ADB_HOST */
|
||||
|
||||
extern int adb_trace_mask;
|
||||
extern unsigned char adb_trace_output_count;
|
||||
void adb_trace_init(void);
|
||||
|
||||
# define ADB_TRACING ((adb_trace_mask & (1 << TRACE_TAG)) != 0)
|
||||
|
||||
/* you must define TRACE_TAG before using this macro */
|
||||
# define D(...) \
|
||||
do { \
|
||||
if (ADB_TRACING) { \
|
||||
int save_errno = errno; \
|
||||
adb_mutex_lock(&D_lock); \
|
||||
fprintf(stderr, "%s::%s():", \
|
||||
__FILE__, __FUNCTION__); \
|
||||
errno = save_errno; \
|
||||
fprintf(stderr, __VA_ARGS__ ); \
|
||||
fflush(stderr); \
|
||||
adb_mutex_unlock(&D_lock); \
|
||||
errno = save_errno; \
|
||||
} \
|
||||
} while (0)
|
||||
# define DR(...) \
|
||||
do { \
|
||||
if (ADB_TRACING) { \
|
||||
int save_errno = errno; \
|
||||
adb_mutex_lock(&D_lock); \
|
||||
errno = save_errno; \
|
||||
fprintf(stderr, __VA_ARGS__ ); \
|
||||
fflush(stderr); \
|
||||
adb_mutex_unlock(&D_lock); \
|
||||
errno = save_errno; \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
# define D(...) ((void)0)
|
||||
# define DR(...) ((void)0)
|
||||
# define ADB_TRACING 0
|
||||
#endif /* ADB_TRACE */
|
||||
|
||||
|
||||
#if !DEBUG_PACKETS
|
||||
#define print_packet(tag,p) do {} while (0)
|
||||
#endif
|
||||
|
|
|
@ -175,7 +175,7 @@ static void adb_auth_event(int fd, unsigned events, void *data)
|
|||
|
||||
if (events & FDE_READ) {
|
||||
ret = unix_read(fd, response, sizeof(response));
|
||||
if (ret < 0) {
|
||||
if (ret <= 0) {
|
||||
D("Framework disconnect\n");
|
||||
if (usb_transport)
|
||||
fdevent_remove(&usb_transport->auth_fde);
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright (C) 2014 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_TRACE_H
|
||||
#define __ADB_TRACE_H
|
||||
|
||||
#if !ADB_HOST
|
||||
#include <android/log.h>
|
||||
#endif
|
||||
|
||||
/* define ADB_TRACE to 1 to enable tracing support, or 0 to disable it */
|
||||
#define ADB_TRACE 1
|
||||
|
||||
/* IMPORTANT: if you change the following list, don't
|
||||
* forget to update the corresponding 'tags' table in
|
||||
* the adb_trace_init() function implemented in adb.c
|
||||
*/
|
||||
typedef enum {
|
||||
TRACE_ADB = 0, /* 0x001 */
|
||||
TRACE_SOCKETS,
|
||||
TRACE_PACKETS,
|
||||
TRACE_TRANSPORT,
|
||||
TRACE_RWX, /* 0x010 */
|
||||
TRACE_USB,
|
||||
TRACE_SYNC,
|
||||
TRACE_SYSDEPS,
|
||||
TRACE_JDWP, /* 0x100 */
|
||||
TRACE_SERVICES,
|
||||
TRACE_AUTH,
|
||||
TRACE_FDEVENT,
|
||||
} AdbTrace;
|
||||
|
||||
#if ADB_TRACE
|
||||
|
||||
#if !ADB_HOST
|
||||
/*
|
||||
* When running inside the emulator, guest's adbd can connect to 'adb-debug'
|
||||
* qemud service that can display adb trace messages (on condition that emulator
|
||||
* has been started with '-debug adb' option).
|
||||
*/
|
||||
|
||||
/* Delivers a trace message to the emulator via QEMU pipe. */
|
||||
void adb_qemu_trace(const char* fmt, ...);
|
||||
/* Macro to use to send ADB trace messages to the emulator. */
|
||||
#define DQ(...) adb_qemu_trace(__VA_ARGS__)
|
||||
#else
|
||||
#define DQ(...) ((void)0)
|
||||
#endif /* !ADB_HOST */
|
||||
|
||||
extern int adb_trace_mask;
|
||||
extern unsigned char adb_trace_output_count;
|
||||
void adb_trace_init(void);
|
||||
|
||||
# define ADB_TRACING ((adb_trace_mask & (1 << TRACE_TAG)) != 0)
|
||||
|
||||
/* you must define TRACE_TAG before using this macro */
|
||||
#if ADB_HOST
|
||||
# define D(...) \
|
||||
do { \
|
||||
if (ADB_TRACING) { \
|
||||
int save_errno = errno; \
|
||||
adb_mutex_lock(&D_lock); \
|
||||
fprintf(stderr, "%s::%s():", \
|
||||
__FILE__, __FUNCTION__); \
|
||||
errno = save_errno; \
|
||||
fprintf(stderr, __VA_ARGS__ ); \
|
||||
fflush(stderr); \
|
||||
adb_mutex_unlock(&D_lock); \
|
||||
errno = save_errno; \
|
||||
} \
|
||||
} while (0)
|
||||
# define DR(...) \
|
||||
do { \
|
||||
if (ADB_TRACING) { \
|
||||
int save_errno = errno; \
|
||||
adb_mutex_lock(&D_lock); \
|
||||
errno = save_errno; \
|
||||
fprintf(stderr, __VA_ARGS__ ); \
|
||||
fflush(stderr); \
|
||||
adb_mutex_unlock(&D_lock); \
|
||||
errno = save_errno; \
|
||||
} \
|
||||
} while (0)
|
||||
# define DD(...) \
|
||||
do { \
|
||||
int save_errno = errno; \
|
||||
adb_mutex_lock(&D_lock); \
|
||||
fprintf(stderr, "%s::%s():", \
|
||||
__FILE__, __FUNCTION__); \
|
||||
errno = save_errno; \
|
||||
fprintf(stderr, __VA_ARGS__ ); \
|
||||
fflush(stderr); \
|
||||
adb_mutex_unlock(&D_lock); \
|
||||
errno = save_errno; \
|
||||
} while (0)
|
||||
#else
|
||||
# define D(...) \
|
||||
do { \
|
||||
if (ADB_TRACING) { \
|
||||
__android_log_print( \
|
||||
ANDROID_LOG_INFO, \
|
||||
__FUNCTION__, \
|
||||
__VA_ARGS__ ); \
|
||||
} \
|
||||
} while (0)
|
||||
# define DR(...) \
|
||||
do { \
|
||||
if (ADB_TRACING) { \
|
||||
__android_log_print( \
|
||||
ANDROID_LOG_INFO, \
|
||||
__FUNCTION__, \
|
||||
__VA_ARGS__ ); \
|
||||
} \
|
||||
} while (0)
|
||||
# define DD(...) \
|
||||
do { \
|
||||
__android_log_print( \
|
||||
ANDROID_LOG_INFO, \
|
||||
__FUNCTION__, \
|
||||
__VA_ARGS__ ); \
|
||||
} while (0)
|
||||
#endif /* ADB_HOST */
|
||||
#else
|
||||
# define D(...) ((void)0)
|
||||
# define DR(...) ((void)0)
|
||||
# define DD(...) ((void)0)
|
||||
# define ADB_TRACING 0
|
||||
#endif /* ADB_TRACE */
|
||||
|
||||
#endif /* __ADB_TRACE_H */
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
@ -41,8 +42,9 @@ static int do_cmd(transport_type ttype, char* serial, char *cmd, ...);
|
|||
|
||||
void get_my_path(char *s, size_t maxLen);
|
||||
int find_sync_dirs(const char *srcarg,
|
||||
char **android_srcdir_out, char **data_srcdir_out);
|
||||
char **android_srcdir_out, char **data_srcdir_out, char **vendor_srcdir_out);
|
||||
int install_app(transport_type transport, char* serial, int argc, char** argv);
|
||||
int install_multiple_app(transport_type transport, char* serial, int argc, char** argv);
|
||||
int uninstall_app(transport_type transport, char* serial, int argc, char** argv);
|
||||
|
||||
static const char *gProductOutPath = NULL;
|
||||
|
@ -151,12 +153,15 @@ void help()
|
|||
" - remove a specific reversed socket connection\n"
|
||||
" adb reverse --remove-all - remove all reversed socket connections from device\n"
|
||||
" adb jdwp - list PIDs of processes hosting a JDWP transport\n"
|
||||
" adb install [-l] [-r] [-s] [--algo <algorithm name> --key <hex-encoded key> --iv <hex-encoded iv>] <file>\n"
|
||||
" adb install [-lrtsd] <file>\n"
|
||||
" adb install-multiple [-lrtsdp] <file...>\n"
|
||||
" - push this package file to the device and install it\n"
|
||||
" ('-l' means forward-lock the app)\n"
|
||||
" ('-r' means reinstall the app, keeping its data)\n"
|
||||
" ('-s' means install on SD card instead of internal storage)\n"
|
||||
" ('--algo', '--key', and '--iv' mean the file is encrypted already)\n"
|
||||
" (-l: forward lock application)\n"
|
||||
" (-r: replace existing application)\n"
|
||||
" (-t: allow test packages)\n"
|
||||
" (-s: install application on sdcard)\n"
|
||||
" (-d: allow version code downgrade)\n"
|
||||
" (-p: partial application install)\n"
|
||||
" adb uninstall [-k] <package> - remove this app package from the device\n"
|
||||
" ('-k' means keep the data and cache directories)\n"
|
||||
" adb bugreport - return all information from the device\n"
|
||||
|
@ -195,7 +200,7 @@ void help()
|
|||
" adb get-serialno - prints: <serial-number>\n"
|
||||
" adb get-devpath - prints: <device-path>\n"
|
||||
" adb status-window - continuously print device status for a specified device\n"
|
||||
" adb remount - remounts the /system partition on the device read-write\n"
|
||||
" adb remount - remounts the /system and /vendor (if present) partitions on the device read-write\n"
|
||||
" adb reboot [bootloader|recovery] - reboots the device, optionally into the bootloader or recovery program\n"
|
||||
" adb reboot-bootloader - reboots the device into the bootloader\n"
|
||||
" adb root - restarts the adbd daemon with root permissions\n"
|
||||
|
@ -211,9 +216,9 @@ void help()
|
|||
"adb sync notes: adb sync [ <directory> ]\n"
|
||||
" <localdir> can be interpreted in several ways:\n"
|
||||
"\n"
|
||||
" - If <directory> is not specified, both /system and /data partitions will be updated.\n"
|
||||
" - If <directory> is not specified, /system, /vendor (if present), and /data partitions will be updated.\n"
|
||||
"\n"
|
||||
" - If it is \"system\" or \"data\", only the corresponding partition\n"
|
||||
" - If it is \"system\", \"vendor\" or \"data\", only the corresponding partition\n"
|
||||
" is updated.\n"
|
||||
"\n"
|
||||
"environmental variables:\n"
|
||||
|
@ -279,6 +284,24 @@ static void read_and_dump(int fd)
|
|||
}
|
||||
}
|
||||
|
||||
static void read_status_line(int fd, char* buf, size_t count)
|
||||
{
|
||||
count--;
|
||||
while (count > 0) {
|
||||
int len = adb_read(fd, buf, count);
|
||||
if (len == 0) {
|
||||
break;
|
||||
} else if (len < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
break;
|
||||
}
|
||||
|
||||
buf += len;
|
||||
count -= len;
|
||||
}
|
||||
*buf = '\0';
|
||||
}
|
||||
|
||||
static void copy_to_file(int inFd, int outFd) {
|
||||
const size_t BUFSIZE = 32 * 1024;
|
||||
char* buf = (char*) malloc(BUFSIZE);
|
||||
|
@ -653,7 +676,12 @@ static void status_window(transport_type ttype, const char* serial)
|
|||
}
|
||||
}
|
||||
|
||||
/** Duplicate and escape given argument. */
|
||||
static int should_escape(const char c)
|
||||
{
|
||||
return (c == ' ' || c == '\'' || c == '"' || c == '\\' || c == '(' || c == ')');
|
||||
}
|
||||
|
||||
/* Duplicate and escape given argument. */
|
||||
static char *escape_arg(const char *s)
|
||||
{
|
||||
const char *ts;
|
||||
|
@ -664,7 +692,7 @@ static char *escape_arg(const char *s)
|
|||
alloc_len = 0;
|
||||
for (ts = s; *ts != '\0'; ts++) {
|
||||
alloc_len++;
|
||||
if (*ts == ' ' || *ts == '"' || *ts == '\\' || *ts == '(' || *ts == ')') {
|
||||
if (should_escape(*ts)) {
|
||||
alloc_len++;
|
||||
}
|
||||
}
|
||||
|
@ -682,7 +710,7 @@ static char *escape_arg(const char *s)
|
|||
dest = ret;
|
||||
|
||||
for (ts = s; *ts != '\0'; ts++) {
|
||||
if (*ts == ' ' || *ts == '"' || *ts == '\\' || *ts == '(' || *ts == ')') {
|
||||
if (should_escape(*ts)) {
|
||||
*dest++ = '\\';
|
||||
}
|
||||
*dest++ = *ts;
|
||||
|
@ -1575,7 +1603,7 @@ top:
|
|||
parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, &show_progress, ©_attrs);
|
||||
|
||||
if ((lpath != NULL) && (rpath != NULL)) {
|
||||
return do_sync_push(lpath, rpath, 0 /* no verify APK */, show_progress);
|
||||
return do_sync_push(lpath, rpath, show_progress);
|
||||
}
|
||||
|
||||
return usage();
|
||||
|
@ -1595,18 +1623,23 @@ top:
|
|||
return usage();
|
||||
}
|
||||
|
||||
if(!strcmp(argv[0], "install")) {
|
||||
if (!strcmp(argv[0], "install")) {
|
||||
if (argc < 2) return usage();
|
||||
return install_app(ttype, serial, argc, argv);
|
||||
}
|
||||
|
||||
if(!strcmp(argv[0], "uninstall")) {
|
||||
if (!strcmp(argv[0], "install-multiple")) {
|
||||
if (argc < 2) return usage();
|
||||
return install_multiple_app(ttype, serial, argc, argv);
|
||||
}
|
||||
|
||||
if (!strcmp(argv[0], "uninstall")) {
|
||||
if (argc < 2) return usage();
|
||||
return uninstall_app(ttype, serial, argc, argv);
|
||||
}
|
||||
|
||||
if(!strcmp(argv[0], "sync")) {
|
||||
char *srcarg, *android_srcpath, *data_srcpath;
|
||||
char *srcarg, *android_srcpath, *data_srcpath, *vendor_srcpath;
|
||||
int listonly = 0;
|
||||
|
||||
int ret;
|
||||
|
@ -1626,15 +1659,18 @@ top:
|
|||
} else {
|
||||
return usage();
|
||||
}
|
||||
ret = find_sync_dirs(srcarg, &android_srcpath, &data_srcpath);
|
||||
ret = find_sync_dirs(srcarg, &android_srcpath, &data_srcpath, &vendor_srcpath);
|
||||
if(ret != 0) return usage();
|
||||
|
||||
if(android_srcpath != NULL)
|
||||
ret = do_sync_sync(android_srcpath, "/system", listonly);
|
||||
if(ret == 0 && vendor_srcpath != NULL)
|
||||
ret = do_sync_sync(vendor_srcpath, "/vendor", listonly);
|
||||
if(ret == 0 && data_srcpath != NULL)
|
||||
ret = do_sync_sync(data_srcpath, "/data", listonly);
|
||||
|
||||
free(android_srcpath);
|
||||
free(vendor_srcpath);
|
||||
free(data_srcpath);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1748,25 +1784,30 @@ static int do_cmd(transport_type ttype, char* serial, char *cmd, ...)
|
|||
}
|
||||
|
||||
int find_sync_dirs(const char *srcarg,
|
||||
char **android_srcdir_out, char **data_srcdir_out)
|
||||
char **android_srcdir_out, char **data_srcdir_out, char **vendor_srcdir_out)
|
||||
{
|
||||
char *android_srcdir, *data_srcdir;
|
||||
char *android_srcdir = NULL, *data_srcdir = NULL, *vendor_srcdir = NULL;
|
||||
struct stat st;
|
||||
|
||||
if(srcarg == NULL) {
|
||||
android_srcdir = product_file("system");
|
||||
data_srcdir = product_file("data");
|
||||
vendor_srcdir = product_file("vendor");
|
||||
/* Check if vendor partition exists */
|
||||
if (lstat(vendor_srcdir, &st) || !S_ISDIR(st.st_mode))
|
||||
vendor_srcdir = NULL;
|
||||
} else {
|
||||
/* srcarg may be "data", "system" or NULL.
|
||||
* if srcarg is NULL, then both data and system are synced
|
||||
*/
|
||||
if(strcmp(srcarg, "system") == 0) {
|
||||
android_srcdir = product_file("system");
|
||||
data_srcdir = NULL;
|
||||
} else if(strcmp(srcarg, "data") == 0) {
|
||||
android_srcdir = NULL;
|
||||
data_srcdir = product_file("data");
|
||||
} else if(strcmp(srcarg, "vendor") == 0) {
|
||||
vendor_srcdir = product_file("vendor");
|
||||
} else {
|
||||
/* It's not "system" or "data".
|
||||
/* It's not "system", "vendor", or "data".
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
@ -1777,11 +1818,15 @@ int find_sync_dirs(const char *srcarg,
|
|||
else
|
||||
free(android_srcdir);
|
||||
|
||||
if(data_srcdir_out != NULL)
|
||||
*data_srcdir_out = data_srcdir;
|
||||
if(vendor_srcdir_out != NULL)
|
||||
*vendor_srcdir_out = vendor_srcdir;
|
||||
else
|
||||
free(data_srcdir);
|
||||
free(vendor_srcdir);
|
||||
|
||||
if(data_srcdir_out != NULL)
|
||||
*data_srcdir_out = data_srcdir;
|
||||
else
|
||||
free(data_srcdir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1826,7 +1871,7 @@ static int delete_file(transport_type transport, char* serial, char* filename)
|
|||
char buf[4096];
|
||||
char* quoted;
|
||||
|
||||
snprintf(buf, sizeof(buf), "shell:rm ");
|
||||
snprintf(buf, sizeof(buf), "shell:rm -f ");
|
||||
quoted = escape_arg(filename);
|
||||
strncat(buf, quoted, sizeof(buf)-1);
|
||||
free(quoted);
|
||||
|
@ -1846,117 +1891,186 @@ static const char* get_basename(const char* filename)
|
|||
}
|
||||
}
|
||||
|
||||
static int check_file(const char* filename)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (filename == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (stat(filename, &st) != 0) {
|
||||
fprintf(stderr, "can't find '%s' to install\n", filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!S_ISREG(st.st_mode)) {
|
||||
fprintf(stderr, "can't install '%s' because it's not a file\n", filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int install_app(transport_type transport, char* serial, int argc, char** argv)
|
||||
{
|
||||
static const char *const DATA_DEST = "/data/local/tmp/%s";
|
||||
static const char *const SD_DEST = "/sdcard/tmp/%s";
|
||||
const char* where = DATA_DEST;
|
||||
char apk_dest[PATH_MAX];
|
||||
char verification_dest[PATH_MAX];
|
||||
char* apk_file;
|
||||
char* verification_file = NULL;
|
||||
int file_arg = -1;
|
||||
int err;
|
||||
int i;
|
||||
int verify_apk = 1;
|
||||
struct stat sb;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (*argv[i] != '-') {
|
||||
file_arg = i;
|
||||
break;
|
||||
} else if (!strcmp(argv[i], "-i")) {
|
||||
// Skip the installer package name.
|
||||
i++;
|
||||
} else if (!strcmp(argv[i], "-s")) {
|
||||
if (!strcmp(argv[i], "-s")) {
|
||||
where = SD_DEST;
|
||||
} else if (!strcmp(argv[i], "--algo")) {
|
||||
verify_apk = 0;
|
||||
i++;
|
||||
} else if (!strcmp(argv[i], "--iv")) {
|
||||
verify_apk = 0;
|
||||
i++;
|
||||
} else if (!strcmp(argv[i], "--key")) {
|
||||
verify_apk = 0;
|
||||
i++;
|
||||
} else if (!strcmp(argv[i], "--abi")) {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (file_arg < 0) {
|
||||
fprintf(stderr, "can't find filename in arguments\n");
|
||||
return 1;
|
||||
} else if (file_arg + 2 < argc) {
|
||||
fprintf(stderr, "too many files specified; only takes APK file and verifier file\n");
|
||||
return 1;
|
||||
// Find last APK argument.
|
||||
// All other arguments passed through verbatim.
|
||||
int last_apk = -1;
|
||||
for (i = argc - 1; i >= 0; i--) {
|
||||
char* file = argv[i];
|
||||
char* dot = strrchr(file, '.');
|
||||
if (dot && !strcasecmp(dot, ".apk")) {
|
||||
if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
|
||||
fprintf(stderr, "Invalid APK file: %s\n", file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
last_apk = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
apk_file = argv[file_arg];
|
||||
|
||||
if (file_arg != argc - 1) {
|
||||
verification_file = argv[file_arg + 1];
|
||||
}
|
||||
|
||||
if (check_file(apk_file) || check_file(verification_file)) {
|
||||
return 1;
|
||||
if (last_apk == -1) {
|
||||
fprintf(stderr, "Missing APK file\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
char* apk_file = argv[last_apk];
|
||||
char apk_dest[PATH_MAX];
|
||||
snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file));
|
||||
if (verification_file != NULL) {
|
||||
snprintf(verification_dest, sizeof(verification_dest), where, get_basename(verification_file));
|
||||
|
||||
if (!strcmp(apk_dest, verification_dest)) {
|
||||
fprintf(stderr, "APK and verification file can't have the same name\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
err = do_sync_push(apk_file, apk_dest, verify_apk, 0 /* no show progress */);
|
||||
int err = do_sync_push(apk_file, apk_dest, 0 /* no show progress */);
|
||||
if (err) {
|
||||
goto cleanup_apk;
|
||||
} else {
|
||||
argv[file_arg] = apk_dest; /* destination name, not source location */
|
||||
}
|
||||
|
||||
if (verification_file != NULL) {
|
||||
err = do_sync_push(verification_file, verification_dest, 0 /* no verify APK */,
|
||||
0 /* no show progress */);
|
||||
if (err) {
|
||||
goto cleanup_apk;
|
||||
} else {
|
||||
argv[file_arg + 1] = verification_dest; /* destination name, not source location */
|
||||
}
|
||||
argv[last_apk] = apk_dest; /* destination name, not source location */
|
||||
}
|
||||
|
||||
pm_command(transport, serial, argc, argv);
|
||||
|
||||
cleanup_apk:
|
||||
if (verification_file != NULL) {
|
||||
delete_file(transport, serial, verification_dest);
|
||||
}
|
||||
|
||||
delete_file(transport, serial, apk_dest);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int install_multiple_app(transport_type transport, char* serial, int argc, char** argv)
|
||||
{
|
||||
char buf[1024];
|
||||
int i;
|
||||
struct stat sb;
|
||||
unsigned long long total_size = 0;
|
||||
|
||||
// Find all APK arguments starting at end.
|
||||
// All other arguments passed through verbatim.
|
||||
int first_apk = -1;
|
||||
for (i = argc - 1; i >= 0; i--) {
|
||||
char* file = argv[i];
|
||||
char* dot = strrchr(file, '.');
|
||||
if (dot && !strcasecmp(dot, ".apk")) {
|
||||
if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
|
||||
fprintf(stderr, "Invalid APK file: %s\n", file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
total_size += sb.st_size;
|
||||
first_apk = i;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (first_apk == -1) {
|
||||
fprintf(stderr, "Missing APK file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "exec:pm install-create -S %lld", total_size);
|
||||
for (i = 1; i < first_apk; i++) {
|
||||
char *quoted = escape_arg(argv[i]);
|
||||
strncat(buf, " ", sizeof(buf) - 1);
|
||||
strncat(buf, quoted, sizeof(buf) - 1);
|
||||
free(quoted);
|
||||
}
|
||||
|
||||
// Create install session
|
||||
int fd = adb_connect(buf);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Connect error for create: %s\n", adb_error());
|
||||
return -1;
|
||||
}
|
||||
read_status_line(fd, buf, sizeof(buf));
|
||||
adb_close(fd);
|
||||
|
||||
int session_id = -1;
|
||||
if (!strncmp("Success", buf, 7)) {
|
||||
char* start = strrchr(buf, '[');
|
||||
char* end = strrchr(buf, ']');
|
||||
if (start && end) {
|
||||
*end = '\0';
|
||||
session_id = strtol(start + 1, NULL, 10);
|
||||
}
|
||||
}
|
||||
if (session_id < 0) {
|
||||
fprintf(stderr, "Failed to create session\n");
|
||||
fputs(buf, stderr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Valid session, now stream the APKs
|
||||
int success = 1;
|
||||
for (i = first_apk; i < argc; i++) {
|
||||
char* file = argv[i];
|
||||
if (stat(file, &sb) == -1) {
|
||||
fprintf(stderr, "Failed to stat %s\n", file);
|
||||
success = 0;
|
||||
goto finalize_session;
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "exec:pm install-write -S %lld %d %d_%s -",
|
||||
(long long int) sb.st_size, session_id, i, get_basename(file));
|
||||
|
||||
int localFd = adb_open(file, O_RDONLY);
|
||||
if (localFd < 0) {
|
||||
fprintf(stderr, "Failed to open %s: %s\n", file, adb_error());
|
||||
success = 0;
|
||||
goto finalize_session;
|
||||
}
|
||||
|
||||
int remoteFd = adb_connect(buf);
|
||||
if (remoteFd < 0) {
|
||||
fprintf(stderr, "Connect error for write: %s\n", adb_error());
|
||||
adb_close(localFd);
|
||||
success = 0;
|
||||
goto finalize_session;
|
||||
}
|
||||
|
||||
copy_to_file(localFd, remoteFd);
|
||||
read_status_line(remoteFd, buf, sizeof(buf));
|
||||
|
||||
adb_close(localFd);
|
||||
adb_close(remoteFd);
|
||||
|
||||
if (strncmp("Success", buf, 7)) {
|
||||
fprintf(stderr, "Failed to write %s\n", file);
|
||||
fputs(buf, stderr);
|
||||
success = 0;
|
||||
goto finalize_session;
|
||||
}
|
||||
}
|
||||
|
||||
finalize_session:
|
||||
// Commit session if we streamed everything okay; otherwise abandon
|
||||
if (success) {
|
||||
snprintf(buf, sizeof(buf), "exec:pm install-commit %d", session_id);
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "exec:pm install-abandon %d", session_id);
|
||||
}
|
||||
|
||||
fd = adb_connect(buf);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Connect error for finalize: %s\n", adb_error());
|
||||
return -1;
|
||||
}
|
||||
read_status_line(fd, buf, sizeof(buf));
|
||||
adb_close(fd);
|
||||
|
||||
if (!strncmp("Success", buf, 7)) {
|
||||
fputs(buf, stderr);
|
||||
return 0;
|
||||
} else {
|
||||
fprintf(stderr, "Failed to finalize session\n");
|
||||
fputs(buf, stderr);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,10 +28,12 @@
|
|||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "adb_trace.h"
|
||||
#include "fdevent.h"
|
||||
#include "transport.h"
|
||||
#include "sysdeps.h"
|
||||
|
||||
#define TRACE_TAG TRACE_FDEVENT
|
||||
|
||||
/* !!! Do not enable DEBUG for the adb that will run as the server:
|
||||
** both stdout and stderr are used to communicate between the client
|
||||
|
@ -57,16 +59,6 @@ static void fatal(const char *fn, const char *fmt, ...)
|
|||
#define FATAL(x...) fatal(__FUNCTION__, x)
|
||||
|
||||
#if DEBUG
|
||||
#define D(...) \
|
||||
do { \
|
||||
adb_mutex_lock(&D_lock); \
|
||||
int save_errno = errno; \
|
||||
fprintf(stderr, "%s::%s():", __FILE__, __FUNCTION__); \
|
||||
errno = save_errno; \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
adb_mutex_unlock(&D_lock); \
|
||||
errno = save_errno; \
|
||||
} while(0)
|
||||
static void dump_fde(fdevent *fde, const char *info)
|
||||
{
|
||||
adb_mutex_lock(&D_lock);
|
||||
|
@ -78,7 +70,6 @@ static void dump_fde(fdevent *fde, const char *info)
|
|||
adb_mutex_unlock(&D_lock);
|
||||
}
|
||||
#else
|
||||
#define D(...) ((void)0)
|
||||
#define dump_fde(fde, info) do { } while(0)
|
||||
#endif
|
||||
|
||||
|
|
|
@ -335,7 +335,7 @@ static int write_data_link(int fd, const char *path, syncsendbuf *sbuf)
|
|||
#endif
|
||||
|
||||
static int sync_send(int fd, const char *lpath, const char *rpath,
|
||||
unsigned mtime, mode_t mode, int verifyApk, int show_progress)
|
||||
unsigned mtime, mode_t mode, int show_progress)
|
||||
{
|
||||
syncmsg msg;
|
||||
int len, r;
|
||||
|
@ -350,63 +350,6 @@ static int sync_send(int fd, const char *lpath, const char *rpath,
|
|||
snprintf(tmp, sizeof(tmp), ",%d", mode);
|
||||
r = strlen(tmp);
|
||||
|
||||
if (verifyApk) {
|
||||
int lfd;
|
||||
zipfile_t zip;
|
||||
zipentry_t entry;
|
||||
int amt;
|
||||
|
||||
// if we are transferring an APK file, then sanity check to make sure
|
||||
// we have a real zip file that contains an AndroidManifest.xml
|
||||
// this requires that we read the entire file into memory.
|
||||
lfd = adb_open(lpath, O_RDONLY);
|
||||
if(lfd < 0) {
|
||||
fprintf(stderr,"cannot open '%s': %s\n", lpath, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
size = adb_lseek(lfd, 0, SEEK_END);
|
||||
if (size == -1 || -1 == adb_lseek(lfd, 0, SEEK_SET)) {
|
||||
fprintf(stderr, "error seeking in file '%s'\n", lpath);
|
||||
adb_close(lfd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
file_buffer = (char *)malloc(size);
|
||||
if (file_buffer == NULL) {
|
||||
fprintf(stderr, "could not allocate buffer for '%s'\n",
|
||||
lpath);
|
||||
adb_close(lfd);
|
||||
return 1;
|
||||
}
|
||||
amt = adb_read(lfd, file_buffer, size);
|
||||
if (amt != size) {
|
||||
fprintf(stderr, "error reading from file: '%s'\n", lpath);
|
||||
adb_close(lfd);
|
||||
free(file_buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
adb_close(lfd);
|
||||
|
||||
zip = init_zipfile(file_buffer, size);
|
||||
if (zip == NULL) {
|
||||
fprintf(stderr, "file '%s' is not a valid zip file\n",
|
||||
lpath);
|
||||
free(file_buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
entry = lookup_zipentry(zip, "AndroidManifest.xml");
|
||||
release_zipfile(zip);
|
||||
if (entry == NULL) {
|
||||
fprintf(stderr, "file '%s' does not contain AndroidManifest.xml\n",
|
||||
lpath);
|
||||
free(file_buffer);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
msg.req.id = ID_SEND;
|
||||
msg.req.namelen = htoll(len + r);
|
||||
|
||||
|
@ -697,30 +640,33 @@ static int local_build_list(copyinfo **filelist,
|
|||
continue;
|
||||
strcpy(stat_path, lpath);
|
||||
strcat(stat_path, de->d_name);
|
||||
stat(stat_path, &st);
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
ci = mkcopyinfo(lpath, rpath, name, 1);
|
||||
ci->next = dirlist;
|
||||
dirlist = ci;
|
||||
} else {
|
||||
ci = mkcopyinfo(lpath, rpath, name, 0);
|
||||
if(lstat(ci->src, &st)) {
|
||||
fprintf(stderr,"cannot stat '%s': %s\n", ci->src, strerror(errno));
|
||||
free(ci);
|
||||
closedir(d);
|
||||
return -1;
|
||||
}
|
||||
if(!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
|
||||
fprintf(stderr, "skipping special file '%s'\n", ci->src);
|
||||
free(ci);
|
||||
if(!lstat(stat_path, &st)) {
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
ci = mkcopyinfo(lpath, rpath, name, 1);
|
||||
ci->next = dirlist;
|
||||
dirlist = ci;
|
||||
} else {
|
||||
ci->time = st.st_mtime;
|
||||
ci->mode = st.st_mode;
|
||||
ci->size = st.st_size;
|
||||
ci->next = *filelist;
|
||||
*filelist = ci;
|
||||
ci = mkcopyinfo(lpath, rpath, name, 0);
|
||||
if(lstat(ci->src, &st)) {
|
||||
fprintf(stderr,"cannot stat '%s': %s\n", ci->src, strerror(errno));
|
||||
free(ci);
|
||||
closedir(d);
|
||||
return -1;
|
||||
}
|
||||
if(!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
|
||||
fprintf(stderr, "skipping special file '%s'\n", ci->src);
|
||||
free(ci);
|
||||
} else {
|
||||
ci->time = st.st_mtime;
|
||||
ci->mode = st.st_mode;
|
||||
ci->size = st.st_size;
|
||||
ci->next = *filelist;
|
||||
*filelist = ci;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "cannot lstat '%s': %s\n",stat_path , strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -786,7 +732,7 @@ static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, i
|
|||
if(ci->flag == 0) {
|
||||
fprintf(stderr,"%spush: %s -> %s\n", listonly ? "would " : "", ci->src, ci->dst);
|
||||
if(!listonly &&
|
||||
sync_send(fd, ci->src, ci->dst, ci->time, ci->mode, 0 /* no verify APK */,
|
||||
sync_send(fd, ci->src, ci->dst, ci->time, ci->mode,
|
||||
0 /* no show progress */)) {
|
||||
return 1;
|
||||
}
|
||||
|
@ -805,7 +751,7 @@ static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, i
|
|||
}
|
||||
|
||||
|
||||
int do_sync_push(const char *lpath, const char *rpath, int verifyApk, int show_progress)
|
||||
int do_sync_push(const char *lpath, const char *rpath, int show_progress)
|
||||
{
|
||||
struct stat st;
|
||||
unsigned mode;
|
||||
|
@ -852,7 +798,7 @@ int do_sync_push(const char *lpath, const char *rpath, int verifyApk, int show_p
|
|||
rpath = tmp;
|
||||
}
|
||||
BEGIN();
|
||||
if(sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, verifyApk, show_progress)) {
|
||||
if(sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, show_progress)) {
|
||||
return 1;
|
||||
} else {
|
||||
END();
|
||||
|
|
|
@ -39,6 +39,11 @@ static bool is_on_system(const char *name) {
|
|||
return (strncmp(SYSTEM, name, strlen(SYSTEM)) == 0);
|
||||
}
|
||||
|
||||
static bool is_on_vendor(const char *name) {
|
||||
const char *VENDOR = "/vendor/";
|
||||
return (strncmp(VENDOR, name, strlen(VENDOR)) == 0);
|
||||
}
|
||||
|
||||
static int mkdirs(char *name)
|
||||
{
|
||||
int ret;
|
||||
|
@ -54,7 +59,7 @@ static int mkdirs(char *name)
|
|||
x = adb_dirstart(x);
|
||||
if(x == 0) return 0;
|
||||
*x = 0;
|
||||
if (is_on_system(name)) {
|
||||
if (is_on_system(name) || is_on_vendor(name)) {
|
||||
fs_config(name, 1, &uid, &gid, &mode, &cap);
|
||||
}
|
||||
ret = adb_mkdir(name, mode);
|
||||
|
@ -369,7 +374,7 @@ static int do_send(int s, char *path, char *buffer)
|
|||
if(*tmp == '/') {
|
||||
tmp++;
|
||||
}
|
||||
if (is_on_system(path)) {
|
||||
if (is_on_system(path) || is_on_vendor(path)) {
|
||||
fs_config(tmp, 0, &uid, &gid, &mode, &cap);
|
||||
}
|
||||
ret = handle_send_file(s, path, uid, gid, mode, buffer, do_unlink);
|
||||
|
|
|
@ -78,7 +78,7 @@ typedef union {
|
|||
|
||||
void file_sync_service(int fd, void *cookie);
|
||||
int do_sync_ls(const char *path);
|
||||
int do_sync_push(const char *lpath, const char *rpath, int verifyApk, int show_progress);
|
||||
int do_sync_push(const char *lpath, const char *rpath, int show_progress);
|
||||
int do_sync_sync(const char *lpath, const char *rpath, int listonly);
|
||||
int do_sync_pull(const char *rpath, const char *lpath, int show_progress, int pullTime);
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
|
||||
static int system_ro = 1;
|
||||
static int vendor_ro = 1;
|
||||
|
||||
/* Returns the device used to mount a directory in /proc/mounts */
|
||||
static char *find_mount(const char *dir)
|
||||
|
@ -67,18 +68,27 @@ static char *find_mount(const char *dir)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int hasVendorPartition()
|
||||
{
|
||||
struct stat info;
|
||||
if (!lstat("/vendor", &info))
|
||||
if ((info.st_mode & S_IFMT) == S_IFDIR)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Init mounts /system as read only, remount to enable writes. */
|
||||
static int remount_system()
|
||||
static int remount(const char* dir, int* dir_ro)
|
||||
{
|
||||
char *dev;
|
||||
int fd;
|
||||
int OFF = 0;
|
||||
|
||||
if (system_ro == 0) {
|
||||
if (dir_ro == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev = find_mount("/system");
|
||||
dev = find_mount(dir);
|
||||
|
||||
if (!dev)
|
||||
return -1;
|
||||
|
@ -90,11 +100,11 @@ static int remount_system()
|
|||
ioctl(fd, BLKROSET, &OFF);
|
||||
adb_close(fd);
|
||||
|
||||
system_ro = mount(dev, "/system", "none", MS_REMOUNT, NULL);
|
||||
*dir_ro = mount(dev, dir, "none", MS_REMOUNT, NULL);
|
||||
|
||||
free(dev);
|
||||
|
||||
return system_ro;
|
||||
return *dir_ro;
|
||||
}
|
||||
|
||||
static void write_string(int fd, const char* str)
|
||||
|
@ -104,16 +114,25 @@ static void write_string(int fd, const char* str)
|
|||
|
||||
void remount_service(int fd, void *cookie)
|
||||
{
|
||||
int ret = remount_system();
|
||||
|
||||
if (!ret)
|
||||
write_string(fd, "remount succeeded\n");
|
||||
else {
|
||||
char buffer[200];
|
||||
snprintf(buffer, sizeof(buffer), "remount failed: %s\n", strerror(errno));
|
||||
char buffer[200];
|
||||
if (remount("/system", &system_ro)) {
|
||||
snprintf(buffer, sizeof(buffer), "remount of system failed: %s\n",strerror(errno));
|
||||
write_string(fd, buffer);
|
||||
}
|
||||
|
||||
if (hasVendorPartition()) {
|
||||
if (remount("/vendor", &vendor_ro)) {
|
||||
snprintf(buffer, sizeof(buffer), "remount of vendor failed: %s\n",strerror(errno));
|
||||
write_string(fd, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (!system_ro && (!vendor_ro || !hasVendorPartition()))
|
||||
write_string(fd, "remount succeeded\n");
|
||||
else {
|
||||
write_string(fd, "remount failed\n");
|
||||
}
|
||||
|
||||
adb_close(fd);
|
||||
}
|
||||
|
||||
|
|
|
@ -281,9 +281,10 @@ static int create_subproc_raw(const char *cmd, const char *arg0, const char *arg
|
|||
adb_close(sv[0]);
|
||||
init_subproc_child();
|
||||
|
||||
// Only hook up stdin/stdout; drop stderr
|
||||
dup2(sv[1], STDIN_FILENO);
|
||||
dup2(sv[1], STDOUT_FILENO);
|
||||
dup2(sv[1], STDERR_FILENO);
|
||||
|
||||
adb_close(sv[1]);
|
||||
|
||||
execl(cmd, cmd, arg0, arg1, NULL);
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <process.h>
|
||||
#include <fcntl.h>
|
||||
|
@ -35,6 +35,7 @@
|
|||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <direct.h>
|
||||
|
||||
#define OS_PATH_SEPARATOR '\\'
|
||||
#define OS_PATH_SEPARATOR_STR "\\"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "sysdeps.h"
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#define TRACE_TAG TRACE_SYSDEPS
|
||||
|
@ -1549,7 +1549,7 @@ _wait_for_all(HANDLE* handles, int handles_count)
|
|||
* reset" event that will remain set once it was set. */
|
||||
main_event = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (main_event == NULL) {
|
||||
D("Unable to create main event. Error: %d", GetLastError());
|
||||
D("Unable to create main event. Error: %d", (int)GetLastError());
|
||||
free(threads);
|
||||
return (int)WAIT_FAILED;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <winerror.h>
|
||||
#include <errno.h>
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
# Copyright 2011 The Android Open Source Project
|
||||
|
||||
ifneq ($(BUILD_TINY_ANDROID),true)
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
charger.c
|
||||
|
||||
ifeq ($(strip $(BOARD_CHARGER_DISABLE_INIT_BLANK)),true)
|
||||
LOCAL_CFLAGS := -DCHARGER_DISABLE_INIT_BLANK
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
|
||||
LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND
|
||||
endif
|
||||
|
||||
LOCAL_MODULE := charger
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
|
||||
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
|
||||
|
||||
LOCAL_C_INCLUDES := bootable/recovery
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libminui libpng
|
||||
ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
|
||||
LOCAL_STATIC_LIBRARIES += libsuspend
|
||||
endif
|
||||
LOCAL_STATIC_LIBRARIES += libz libstdc++ libcutils liblog libm libc
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
define _add-charger-image
|
||||
include $$(CLEAR_VARS)
|
||||
LOCAL_MODULE := system_core_charger_$(notdir $(1))
|
||||
LOCAL_MODULE_STEM := $(notdir $(1))
|
||||
_img_modules += $$(LOCAL_MODULE)
|
||||
LOCAL_SRC_FILES := $1
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_CLASS := ETC
|
||||
LOCAL_MODULE_PATH := $$(TARGET_ROOT_OUT)/res/images/charger
|
||||
include $$(BUILD_PREBUILT)
|
||||
endef
|
||||
|
||||
_img_modules :=
|
||||
_images :=
|
||||
$(foreach _img, $(call find-subdir-subdir-files, "images", "*.png"), \
|
||||
$(eval $(call _add-charger-image,$(_img))))
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := charger_res_images
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_REQUIRED_MODULES := $(_img_modules)
|
||||
include $(BUILD_PHONY_PACKAGE)
|
||||
|
||||
_add-charger-image :=
|
||||
_img_modules :=
|
||||
|
||||
endif
|
|
@ -49,13 +49,13 @@ static void dump_process_header(log_t* log, pid_t pid) {
|
|||
struct tm tm;
|
||||
localtime_r(&t, &tm);
|
||||
char timestr[64];
|
||||
_LOG(log, logtype::BACKTRACE, "\n\nABI: '%s'\n", ABI_STRING);
|
||||
strftime(timestr, sizeof(timestr), "%F %T", &tm);
|
||||
_LOG(log, logtype::BACKTRACE, "\n----- pid %d at %s -----\n", pid, timestr);
|
||||
_LOG(log, logtype::BACKTRACE, "\n\n----- pid %d at %s -----\n", pid, timestr);
|
||||
|
||||
if (procname) {
|
||||
_LOG(log, logtype::BACKTRACE, "Cmd line: %s\n", procname);
|
||||
}
|
||||
_LOG(log, logtype::BACKTRACE, "ABI: '%s'\n", ABI_STRING);
|
||||
}
|
||||
|
||||
static void dump_process_footer(log_t* log, pid_t pid) {
|
||||
|
|
|
@ -681,7 +681,7 @@ static char* find_and_open_tombstone(int* fd) {
|
|||
if (errno != ENOENT)
|
||||
continue;
|
||||
|
||||
*fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600);
|
||||
*fd = open(path, O_CREAT | O_EXCL | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
|
||||
if (*fd < 0)
|
||||
continue; // raced ?
|
||||
|
||||
|
@ -696,7 +696,7 @@ static char* find_and_open_tombstone(int* fd) {
|
|||
|
||||
// we didn't find an available file, so we clobber the oldest one
|
||||
snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, oldest);
|
||||
*fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
|
||||
*fd = open(path, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
|
||||
if (*fd < 0) {
|
||||
ALOGE("failed to open tombstone file '%s': %s\n", path, strerror(errno));
|
||||
return NULL;
|
||||
|
|
|
@ -17,7 +17,8 @@ LOCAL_PATH:= $(call my-dir)
|
|||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg \
|
||||
$(LOCAL_PATH)/../../extras/ext4_utils
|
||||
$(LOCAL_PATH)/../../extras/ext4_utils \
|
||||
$(LOCAL_PATH)/../../extras/f2fs_utils
|
||||
LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c util.c fs.c
|
||||
LOCAL_MODULE := fastboot
|
||||
LOCAL_MODULE_TAGS := debug
|
||||
|
@ -40,13 +41,11 @@ ifeq ($(HOST_OS),windows)
|
|||
ifneq ($(strip $(USE_CYGWIN)),)
|
||||
# Pure cygwin case
|
||||
LOCAL_LDLIBS += -lpthread
|
||||
LOCAL_C_INCLUDES += /usr/include/w32api/ddk
|
||||
endif
|
||||
ifneq ($(strip $(USE_MINGW)),)
|
||||
# MinGW under Linux case
|
||||
LOCAL_LDLIBS += -lws2_32
|
||||
USE_SYSDEPS_WIN32 := 1
|
||||
LOCAL_C_INCLUDES += /usr/i586-mingw32msvc/include/ddk
|
||||
endif
|
||||
LOCAL_C_INCLUDES += development/host/windows/usb/api
|
||||
endif
|
||||
|
@ -63,10 +62,24 @@ ifneq ($(HOST_OS),windows)
|
|||
LOCAL_STATIC_LIBRARIES += libselinux
|
||||
endif # HOST_OS != windows
|
||||
|
||||
ifeq ($(HOST_OS),linux)
|
||||
# libf2fs_dlutils_host will dlopen("libf2fs_fmt_host_dyn")
|
||||
LOCAL_CFLAGS += -DUSE_F2FS
|
||||
LOCAL_LDFLAGS += -ldl -rdynamic -Wl,-rpath,.
|
||||
LOCAL_REQUIRED_MODULES := libf2fs_fmt_host_dyn
|
||||
# The following libf2fs_* are from system/extras/f2fs_utils,
|
||||
# and do not use code in external/f2fs-tools.
|
||||
LOCAL_STATIC_LIBRARIES += libf2fs_utils_host libf2fs_ioutils_host libf2fs_dlutils_host
|
||||
endif
|
||||
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
||||
|
||||
$(call dist-for-goals,dist_files sdk,$(LOCAL_BUILT_MODULE))
|
||||
my_dist_files := $(LOCAL_BUILT_MODULE)
|
||||
ifeq ($(HOST_OS),linux)
|
||||
my_dist_files += $(HOST_LIBRARY_PATH)/libf2fs_fmt_host_dyn$(HOST_SHLIB_SUFFIX)
|
||||
endif
|
||||
$(call dist-for-goals,dist_files sdk,$(my_dist_files))
|
||||
my_dist_files :=
|
||||
|
||||
|
||||
ifeq ($(HOST_OS),linux)
|
||||
|
|
|
@ -99,11 +99,11 @@ static struct {
|
|||
char sig_name[13];
|
||||
char part_name[9];
|
||||
bool is_optional;
|
||||
} images[4] = {
|
||||
} images[] = {
|
||||
{"boot.img", "boot.sig", "boot", false},
|
||||
{"recovery.img", "recovery.sig", "recovery", true},
|
||||
{"system.img", "system.sig", "system", false},
|
||||
{"tos.img", "tos.sig", "tos", true},
|
||||
{"vendor.img", "vendor.sig", "vendor", true},
|
||||
};
|
||||
|
||||
void get_my_path(char *path);
|
||||
|
@ -120,8 +120,8 @@ char *find_item(const char *item, const char *product)
|
|||
fn = "recovery.img";
|
||||
} else if(!strcmp(item,"system")) {
|
||||
fn = "system.img";
|
||||
} else if(!strcmp(item,"tos")) {
|
||||
fn = "tos.img";
|
||||
} else if(!strcmp(item,"vendor")) {
|
||||
fn = "vendor.img";
|
||||
} else if(!strcmp(item,"userdata")) {
|
||||
fn = "userdata.img";
|
||||
} else if(!strcmp(item,"cache")) {
|
||||
|
@ -287,8 +287,8 @@ void usage(void)
|
|||
"\n"
|
||||
"commands:\n"
|
||||
" update <filename> reflash device from update.zip\n"
|
||||
" flashall flash boot, system, and if found,\n"
|
||||
" recovery, tos\n"
|
||||
" flashall flash boot, system, vendor and if found,\n"
|
||||
" recovery\n"
|
||||
" flash <partition> [ <filename> ] write a file to a flash partition\n"
|
||||
" erase <partition> erase a flash partition\n"
|
||||
" format[:[<fs type>][:[<size>]] <partition> format a flash partition.\n"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "fastboot.h"
|
||||
#include "make_ext4fs.h"
|
||||
#include "make_f2fs.h"
|
||||
#include "fs.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
@ -28,15 +29,23 @@ static int generate_ext4_image(int fd, long long partSize)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef USE_F2FS
|
||||
static int generate_f2fs_image(int fd, long long partSize)
|
||||
{
|
||||
return make_f2fs_sparse_fd(fd, partSize, NULL, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct fs_generator {
|
||||
|
||||
char *fs_type; //must match what fastboot reports for partition type
|
||||
int (*generate)(int fd, long long partSize); //returns 0 or error value
|
||||
|
||||
} generators[] = {
|
||||
|
||||
{ "ext4", generate_ext4_image}
|
||||
|
||||
{ "ext4", generate_ext4_image},
|
||||
#ifdef USE_F2FS
|
||||
{ "f2fs", generate_f2fs_image},
|
||||
#endif
|
||||
};
|
||||
|
||||
const struct fs_generator* fs_get_generator(const char *fs_type)
|
||||
|
|
262
fs_mgr/fs_mgr.c
262
fs_mgr/fs_mgr.c
|
@ -31,6 +31,7 @@
|
|||
|
||||
#include <linux/loop.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
#include <cutils/android_reboot.h>
|
||||
#include <cutils/partition_utils.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <logwrap/logwrap.h>
|
||||
|
@ -46,6 +47,7 @@
|
|||
#define KEY_IN_FOOTER "footer"
|
||||
|
||||
#define E2FSCK_BIN "/system/bin/e2fsck"
|
||||
#define F2FS_FSCK_BIN "/system/bin/fsck.f2fs"
|
||||
#define MKSWAP_BIN "/system/bin/mkswap"
|
||||
|
||||
#define FSCK_LOG_FILE "/dev/fscklogs/log"
|
||||
|
@ -112,6 +114,7 @@ static void check_fs(char *blk_device, char *fs_type, char *target)
|
|||
* fix the filesystem.
|
||||
*/
|
||||
ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts);
|
||||
INFO("%s(): mount(%s,%s,%s)=%d\n", __func__, blk_device, target, fs_type, ret);
|
||||
if (!ret) {
|
||||
umount(target);
|
||||
}
|
||||
|
@ -135,6 +138,20 @@ static void check_fs(char *blk_device, char *fs_type, char *target)
|
|||
ERROR("Failed trying to run %s\n", E2FSCK_BIN);
|
||||
}
|
||||
}
|
||||
} else if (!strcmp(fs_type, "f2fs")) {
|
||||
char *f2fs_fsck_argv[] = {
|
||||
F2FS_FSCK_BIN,
|
||||
blk_device
|
||||
};
|
||||
INFO("Running %s on %s\n", F2FS_FSCK_BIN, blk_device);
|
||||
|
||||
ret = android_fork_execvp_ext(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv,
|
||||
&status, true, LOG_KLOG | LOG_FILE,
|
||||
true, FSCK_LOG_FILE);
|
||||
if (ret < 0) {
|
||||
/* No need to check for error in fork, we can't really handle it now */
|
||||
ERROR("Failed trying to run %s\n", F2FS_FSCK_BIN);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -175,16 +192,27 @@ static void fs_set_blk_ro(const char *blockdev)
|
|||
* sets the underlying block device to read-only if the mount is read-only.
|
||||
* See "man 2 mount" for return values.
|
||||
*/
|
||||
static int __mount(const char *source, const char *target,
|
||||
const char *filesystemtype, unsigned long mountflags,
|
||||
const void *data)
|
||||
static int __mount(const char *source, const char *target, const struct fstab_rec *rec)
|
||||
{
|
||||
int ret = mount(source, target, filesystemtype, mountflags, data);
|
||||
unsigned long mountflags = rec->flags;
|
||||
int ret;
|
||||
int save_errno;
|
||||
|
||||
/* We need this because sometimes we have legacy symlinks
|
||||
* that are lingering around and need cleaning up.
|
||||
*/
|
||||
struct stat info;
|
||||
if (!lstat(target, &info))
|
||||
if ((info.st_mode & S_IFMT) == S_IFLNK)
|
||||
unlink(target);
|
||||
mkdir(target, 0755);
|
||||
ret = mount(source, target, rec->fs_type, mountflags, rec->fs_options);
|
||||
save_errno = errno;
|
||||
INFO("%s(source=%s,target=%s,type=%s)=%d\n", __func__, source, target, rec->fs_type, ret);
|
||||
if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
|
||||
fs_set_blk_ro(source);
|
||||
}
|
||||
|
||||
errno = save_errno;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -208,16 +236,101 @@ static int fs_match(char *in1, char *in2)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int device_is_debuggable() {
|
||||
int ret = -1;
|
||||
char value[PROP_VALUE_MAX];
|
||||
ret = __system_property_get("ro.debuggable", value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return strcmp(value, "1") ? 0 : 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tries to mount any of the consecutive fstab entries that match
|
||||
* the mountpoint of the one given by fstab->recs[start_idx].
|
||||
*
|
||||
* end_idx: On return, will be the last rec that was looked at.
|
||||
* attempted_idx: On return, will indicate which fstab rec
|
||||
* succeeded. In case of failure, it will be the start_idx.
|
||||
* Returns
|
||||
* -1 on failure with errno set to match the 1st mount failure.
|
||||
* 0 on success.
|
||||
*/
|
||||
static int mount_with_alternatives(struct fstab *fstab, int start_idx, int *end_idx, int *attempted_idx)
|
||||
{
|
||||
int i;
|
||||
int mount_errno = 0;
|
||||
int mounted = 0;
|
||||
|
||||
if (!end_idx || !attempted_idx || start_idx >= fstab->num_entries) {
|
||||
errno = EINVAL;
|
||||
if (end_idx) *end_idx = start_idx;
|
||||
if (attempted_idx) *end_idx = start_idx;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Hunt down an fstab entry for the same mount point that might succeed */
|
||||
for (i = start_idx;
|
||||
/* We required that fstab entries for the same mountpoint be consecutive */
|
||||
i < fstab->num_entries && !strcmp(fstab->recs[start_idx].mount_point, fstab->recs[i].mount_point);
|
||||
i++) {
|
||||
/*
|
||||
* Don't try to mount/encrypt the same mount point again.
|
||||
* Deal with alternate entries for the same point which are required to be all following
|
||||
* each other.
|
||||
*/
|
||||
if (mounted) {
|
||||
ERROR("%s(): skipping fstab dup mountpoint=%s rec[%d].fs_type=%s already mounted as %s.\n", __func__,
|
||||
fstab->recs[i].mount_point, i, fstab->recs[i].fs_type, fstab->recs[*attempted_idx].fs_type);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fstab->recs[i].fs_mgr_flags & MF_CHECK) {
|
||||
check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
|
||||
fstab->recs[i].mount_point);
|
||||
}
|
||||
if (!__mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point, &fstab->recs[i])) {
|
||||
*attempted_idx = i;
|
||||
mounted = 1;
|
||||
if (i != start_idx) {
|
||||
ERROR("%s(): Mounted %s on %s with fs_type=%s instead of %s\n", __func__,
|
||||
fstab->recs[i].blk_device, fstab->recs[i].mount_point, fstab->recs[i].fs_type,
|
||||
fstab->recs[start_idx].fs_type);
|
||||
}
|
||||
} else {
|
||||
/* back up errno for crypto decisions */
|
||||
mount_errno = errno;
|
||||
}
|
||||
}
|
||||
|
||||
/* Adjust i for the case where it was still withing the recs[] */
|
||||
if (i < fstab->num_entries) --i;
|
||||
|
||||
*end_idx = i;
|
||||
if (!mounted) {
|
||||
*attempted_idx = start_idx;
|
||||
errno = mount_errno;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* When multiple fstab records share the same mount_point, it will
|
||||
* try to mount each one in turn, and ignore any duplicates after a
|
||||
* first successful mount.
|
||||
* Returns -1 on error, and FS_MGR_MNTALL_* otherwise.
|
||||
*/
|
||||
int fs_mgr_mount_all(struct fstab *fstab)
|
||||
{
|
||||
int i = 0;
|
||||
int encrypted = 0;
|
||||
int ret = -1;
|
||||
int mret;
|
||||
int mount_errno;
|
||||
int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
|
||||
int error_count = 0;
|
||||
int mret = -1;
|
||||
int mount_errno = 0;
|
||||
int attempted_idx = -1;
|
||||
|
||||
if (!fstab) {
|
||||
return ret;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < fstab->num_entries; i++) {
|
||||
|
@ -237,70 +350,92 @@ int fs_mgr_mount_all(struct fstab *fstab)
|
|||
wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
|
||||
}
|
||||
|
||||
if (fstab->recs[i].fs_mgr_flags & MF_CHECK) {
|
||||
check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
|
||||
fstab->recs[i].mount_point);
|
||||
}
|
||||
|
||||
if (fstab->recs[i].fs_mgr_flags & MF_VERIFY) {
|
||||
if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) &&
|
||||
!device_is_debuggable()) {
|
||||
if (fs_mgr_setup_verity(&fstab->recs[i]) < 0) {
|
||||
ERROR("Could not set up verified partition, skipping!");
|
||||
ERROR("Could not set up verified partition, skipping!\n");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
int last_idx_inspected;
|
||||
mret = mount_with_alternatives(fstab, i, &last_idx_inspected, &attempted_idx);
|
||||
i = last_idx_inspected;
|
||||
mount_errno = errno;
|
||||
|
||||
mret = __mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point,
|
||||
fstab->recs[i].fs_type, fstab->recs[i].flags,
|
||||
fstab->recs[i].fs_options);
|
||||
|
||||
/* Deal with encryptability. */
|
||||
if (!mret) {
|
||||
/* If this is encryptable, need to trigger encryption */
|
||||
if ((fstab->recs[attempted_idx].fs_mgr_flags & MF_FORCECRYPT)) {
|
||||
if (umount(fstab->recs[attempted_idx].mount_point) == 0) {
|
||||
if (encryptable == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
|
||||
ERROR("Will try to encrypt %s %s\n", fstab->recs[attempted_idx].mount_point,
|
||||
fstab->recs[attempted_idx].fs_type);
|
||||
encryptable = FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION;
|
||||
} else {
|
||||
ERROR("Only one encryptable/encrypted partition supported\n");
|
||||
encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
|
||||
}
|
||||
} else {
|
||||
INFO("Could not umount %s - allow continue unencrypted\n",
|
||||
fstab->recs[attempted_idx].mount_point);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/* Success! Go get the next one */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* back up errno as partition_wipe clobbers the value */
|
||||
mount_errno = errno;
|
||||
|
||||
/* mount(2) returned an error, check if it's encrypted and deal with it */
|
||||
if ((fstab->recs[i].fs_mgr_flags & MF_CRYPT) &&
|
||||
!partition_wiped(fstab->recs[i].blk_device)) {
|
||||
/* Need to mount a tmpfs at this mountpoint for now, and set
|
||||
* properties that vold will query later for decrypting
|
||||
*/
|
||||
if (mount("tmpfs", fstab->recs[i].mount_point, "tmpfs",
|
||||
MS_NOATIME | MS_NOSUID | MS_NODEV, CRYPTO_TMPFS_OPTIONS) < 0) {
|
||||
ERROR("Cannot mount tmpfs filesystem for encrypted fs at %s error: %s\n",
|
||||
fstab->recs[i].mount_point, strerror(errno));
|
||||
goto out;
|
||||
/* mount(2) returned an error, check if it's encryptable and deal with it */
|
||||
if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
|
||||
fs_mgr_is_encryptable(&fstab->recs[attempted_idx])) {
|
||||
if(partition_wiped(fstab->recs[attempted_idx].blk_device)) {
|
||||
ERROR("%s(): %s is wiped and %s %s is encryptable. Suggest recovery...\n", __func__,
|
||||
fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
|
||||
fstab->recs[attempted_idx].fs_type);
|
||||
encryptable = FS_MGR_MNTALL_DEV_NEEDS_RECOVERY;
|
||||
continue;
|
||||
} else {
|
||||
/* Need to mount a tmpfs at this mountpoint for now, and set
|
||||
* properties that vold will query later for decrypting
|
||||
*/
|
||||
ERROR("%s(): possibly an encryptable blkdev %s for mount %s type %s )\n", __func__,
|
||||
fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
|
||||
fstab->recs[attempted_idx].fs_type);
|
||||
if (fs_mgr_do_tmpfs_mount(fstab->recs[attempted_idx].mount_point) < 0) {
|
||||
++error_count;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
encrypted = 1;
|
||||
encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
|
||||
} else {
|
||||
ERROR("Failed to mount an un-encryptable or wiped partition on"
|
||||
"%s at %s options: %s error: %s\n",
|
||||
fstab->recs[i].blk_device, fstab->recs[i].mount_point,
|
||||
fstab->recs[i].fs_options, strerror(mount_errno));
|
||||
goto out;
|
||||
"%s at %s options: %s error: %s\n",
|
||||
fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
|
||||
fstab->recs[attempted_idx].fs_options, strerror(mount_errno));
|
||||
++error_count;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (encrypted) {
|
||||
ret = 1;
|
||||
if (error_count) {
|
||||
return -1;
|
||||
} else {
|
||||
ret = 0;
|
||||
return encryptable;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* If tmp_mount_point is non-null, mount the filesystem there. This is for the
|
||||
* tmp mount we do to check the user password
|
||||
* If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
|
||||
* in turn, and stop on 1st success, or no more match.
|
||||
*/
|
||||
int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device,
|
||||
char *tmp_mount_point)
|
||||
{
|
||||
int i = 0;
|
||||
int ret = -1;
|
||||
int ret = FS_MGR_DOMNT_FAILED;
|
||||
int mount_errors = 0;
|
||||
int first_mount_errno = 0;
|
||||
char *m;
|
||||
|
||||
if (!fstab) {
|
||||
|
@ -332,9 +467,10 @@ int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device,
|
|||
fstab->recs[i].mount_point);
|
||||
}
|
||||
|
||||
if (fstab->recs[i].fs_mgr_flags & MF_VERIFY) {
|
||||
if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) &&
|
||||
!device_is_debuggable()) {
|
||||
if (fs_mgr_setup_verity(&fstab->recs[i]) < 0) {
|
||||
ERROR("Could not set up verified partition, skipping!");
|
||||
ERROR("Could not set up verified partition, skipping!\n");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -345,19 +481,27 @@ int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device,
|
|||
} else {
|
||||
m = fstab->recs[i].mount_point;
|
||||
}
|
||||
if (__mount(n_blk_device, m, fstab->recs[i].fs_type,
|
||||
fstab->recs[i].flags, fstab->recs[i].fs_options)) {
|
||||
ERROR("Cannot mount filesystem on %s at %s options: %s error: %s\n",
|
||||
n_blk_device, m, fstab->recs[i].fs_options, strerror(errno));
|
||||
goto out;
|
||||
if (__mount(n_blk_device, m, &fstab->recs[i])) {
|
||||
if (!first_mount_errno) first_mount_errno = errno;
|
||||
mount_errors++;
|
||||
continue;
|
||||
} else {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* We didn't find a match, say so and return an error */
|
||||
ERROR("Cannot find mount point %s in fstab\n", fstab->recs[i].mount_point);
|
||||
if (mount_errors) {
|
||||
ERROR("Cannot mount filesystem on %s at %s. error: %s\n",
|
||||
n_blk_device, m, strerror(first_mount_errno));
|
||||
if (first_mount_errno == EBUSY) {
|
||||
ret = FS_MGR_DOMNT_BUSY;
|
||||
} else {
|
||||
ret = FS_MGR_DOMNT_FAILED;
|
||||
}
|
||||
} else {
|
||||
/* We didn't find a match, say so and return an error */
|
||||
ERROR("Cannot find mount point %s in fstab\n", fstab->recs[i].mount_point);
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
@ -437,7 +581,7 @@ int fs_mgr_swapon_all(struct fstab *fstab)
|
|||
|
||||
zram_fp = fopen(ZRAM_CONF_DEV, "r+");
|
||||
if (zram_fp == NULL) {
|
||||
ERROR("Unable to open zram conf device " ZRAM_CONF_DEV);
|
||||
ERROR("Unable to open zram conf device %s\n", ZRAM_CONF_DEV);
|
||||
ret = -1;
|
||||
continue;
|
||||
}
|
||||
|
@ -504,7 +648,7 @@ int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc, char *real_blk_dev
|
|||
if (fstab->recs[i].fs_mgr_flags & MF_VOLDMANAGED) {
|
||||
continue;
|
||||
}
|
||||
if (!(fstab->recs[i].fs_mgr_flags & MF_CRYPT)) {
|
||||
if (!(fstab->recs[i].fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ static struct flag_list fs_mgr_flags[] = {
|
|||
{ "wait", MF_WAIT },
|
||||
{ "check", MF_CHECK },
|
||||
{ "encryptable=",MF_CRYPT },
|
||||
{ "forceencrypt=",MF_FORCECRYPT },
|
||||
{ "nonremovable",MF_NONREMOVABLE },
|
||||
{ "voldmanaged=",MF_VOLDMANAGED},
|
||||
{ "length=", MF_LENGTH },
|
||||
|
@ -106,6 +107,11 @@ static int parse_flags(char *flags, struct flag_list *fl,
|
|||
* location of the keys. Get it and return it.
|
||||
*/
|
||||
flag_vals->key_loc = strdup(strchr(p, '=') + 1);
|
||||
} else if ((fl[i].flag == MF_FORCECRYPT) && flag_vals) {
|
||||
/* The forceencrypt flag is followed by an = and the
|
||||
* location of the keys. Get it and return it.
|
||||
*/
|
||||
flag_vals->key_loc = strdup(strchr(p, '=') + 1);
|
||||
} else if ((fl[i].flag == MF_LENGTH) && flag_vals) {
|
||||
/* The length flag is followed by an = and the
|
||||
* size of the partition. Get it and return it.
|
||||
|
@ -361,25 +367,47 @@ int fs_mgr_add_entry(struct fstab *fstab,
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path)
|
||||
/*
|
||||
* Returns the 1st matching fstab_rec that follows the start_rec.
|
||||
* start_rec is the result of a previous search or NULL.
|
||||
*/
|
||||
struct fstab_rec *fs_mgr_get_entry_for_mount_point_after(struct fstab_rec *start_rec, struct fstab *fstab, const char *path)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!fstab) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < fstab->num_entries; i++) {
|
||||
if (start_rec) {
|
||||
for (i = 0; i < fstab->num_entries; i++) {
|
||||
if (&fstab->recs[i] == start_rec) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
i = 0;
|
||||
}
|
||||
for (; i < fstab->num_entries; i++) {
|
||||
int len = strlen(fstab->recs[i].mount_point);
|
||||
if (strncmp(path, fstab->recs[i].mount_point, len) == 0 &&
|
||||
(path[len] == '\0' || path[len] == '/')) {
|
||||
return &fstab->recs[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the 1st matching mount point.
|
||||
* There might be more. To look for others, use fs_mgr_get_entry_for_mount_point_after()
|
||||
* and give the fstab_rec from the previous search.
|
||||
*/
|
||||
struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path)
|
||||
{
|
||||
return fs_mgr_get_entry_for_mount_point_after(NULL, fstab, path);
|
||||
}
|
||||
|
||||
int fs_mgr_is_voldmanaged(struct fstab_rec *fstab)
|
||||
{
|
||||
return fstab->fs_mgr_flags & MF_VOLDMANAGED;
|
||||
|
@ -392,7 +420,7 @@ int fs_mgr_is_nonremovable(struct fstab_rec *fstab)
|
|||
|
||||
int fs_mgr_is_encryptable(struct fstab_rec *fstab)
|
||||
{
|
||||
return fstab->fs_mgr_flags & MF_CRYPT;
|
||||
return fstab->fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT);
|
||||
}
|
||||
|
||||
int fs_mgr_is_noemulatedsd(struct fstab_rec *fstab)
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#define INFO(x...) KLOG_INFO("fs_mgr", x)
|
||||
#define ERROR(x...) KLOG_ERROR("fs_mgr", x)
|
||||
|
||||
#define CRYPTO_TMPFS_OPTIONS "size=128m,mode=0771,uid=1000,gid=1000"
|
||||
#define CRYPTO_TMPFS_OPTIONS "size=256m,mode=0771,uid=1000,gid=1000"
|
||||
|
||||
#define WAIT_TIMEOUT 20
|
||||
|
||||
|
@ -72,12 +72,9 @@
|
|||
#define MF_SWAPPRIO 0x80
|
||||
#define MF_ZRAMSIZE 0x100
|
||||
#define MF_VERIFY 0x200
|
||||
/*
|
||||
* There is no emulated sdcard daemon running on /data/media on this device,
|
||||
* so treat the physical SD card as the only external storage device,
|
||||
* a la the Nexus One.
|
||||
*/
|
||||
#define MF_NOEMULATEDSD 0x400
|
||||
#define MF_FORCECRYPT 0x400
|
||||
#define MF_NOEMULATEDSD 0x800 /* no emulated sdcard daemon, sd card is the only
|
||||
external storage */
|
||||
|
||||
#define DM_BUF_SIZE 4096
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <time.h>
|
||||
|
||||
#include <private/android_filesystem_config.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <logwrap/logwrap.h>
|
||||
|
||||
#include "mincrypt/rsa.h"
|
||||
|
@ -336,6 +337,26 @@ static int test_access(char *device) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
static int set_verified_property(char *name) {
|
||||
int ret;
|
||||
char *key;
|
||||
ret = asprintf(&key, "partition.%s.verified", name);
|
||||
if (ret < 0) {
|
||||
ERROR("Error formatting verified property");
|
||||
return ret;
|
||||
}
|
||||
ret = PROP_NAME_MAX - strlen(key);
|
||||
if (ret < 0) {
|
||||
ERROR("Verified property name is too long");
|
||||
return -1;
|
||||
}
|
||||
ret = property_set(key, "1");
|
||||
if (ret < 0)
|
||||
ERROR("Error setting verified property %s: %d", key, ret);
|
||||
free(key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fs_mgr_setup_verity(struct fstab_rec *fstab) {
|
||||
|
||||
int retval = -1;
|
||||
|
@ -352,6 +373,13 @@ int fs_mgr_setup_verity(struct fstab_rec *fstab) {
|
|||
io->flags |= 1;
|
||||
io->target_count = 1;
|
||||
|
||||
// check to ensure that the verity device is ext4
|
||||
// TODO: support non-ext4 filesystems
|
||||
if (strcmp(fstab->fs_type, "ext4")) {
|
||||
ERROR("Cannot verify non-ext4 device (%s)", fstab->fs_type);
|
||||
return retval;
|
||||
}
|
||||
|
||||
// get the device mapper fd
|
||||
int fd;
|
||||
if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
|
||||
|
@ -405,7 +433,8 @@ int fs_mgr_setup_verity(struct fstab_rec *fstab) {
|
|||
goto out;
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
// set the property indicating that the partition is verified
|
||||
retval = set_verified_property(mount_point);
|
||||
|
||||
out:
|
||||
close(fd);
|
||||
|
|
|
@ -24,6 +24,11 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The entries must be kept in the same order as they were seen in the fstab.
|
||||
* Unless explicitly requested, a lookup on mount point should always
|
||||
* return the 1st one.
|
||||
*/
|
||||
struct fstab {
|
||||
int num_entries;
|
||||
struct fstab_rec *recs;
|
||||
|
@ -48,7 +53,15 @@ struct fstab_rec {
|
|||
|
||||
struct fstab *fs_mgr_read_fstab(const char *fstab_path);
|
||||
void fs_mgr_free_fstab(struct fstab *fstab);
|
||||
|
||||
#define FS_MGR_MNTALL_DEV_NEEDS_RECOVERY 3
|
||||
#define FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION 2
|
||||
#define FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED 1
|
||||
#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTED 0
|
||||
int fs_mgr_mount_all(struct fstab *fstab);
|
||||
|
||||
#define FS_MGR_DOMNT_FAILED -1
|
||||
#define FS_MGR_DOMNT_BUSY -2
|
||||
int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device,
|
||||
char *tmp_mount_point);
|
||||
int fs_mgr_do_tmpfs_mount(char *n_name);
|
||||
|
|
|
@ -7,12 +7,15 @@ LOCAL_PATH := $(call my-dir)
|
|||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := healthd_board_default.cpp
|
||||
LOCAL_MODULE := libhealthd.default
|
||||
LOCAL_CFLAGS := -Werror
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
healthd.cpp \
|
||||
healthd_mode_android.cpp \
|
||||
healthd_mode_charger.cpp \
|
||||
BatteryMonitor.cpp \
|
||||
BatteryPropertiesRegistrar.cpp
|
||||
|
||||
|
@ -22,9 +25,57 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true
|
|||
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
|
||||
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libz libutils libstdc++ libcutils liblog libm libc
|
||||
LOCAL_CFLAGS := -D__STDC_LIMIT_MACROS -Werror
|
||||
|
||||
ifeq ($(strip $(BOARD_CHARGER_DISABLE_INIT_BLANK)),true)
|
||||
LOCAL_CFLAGS += -DCHARGER_DISABLE_INIT_BLANK
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
|
||||
LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND
|
||||
endif
|
||||
|
||||
LOCAL_C_INCLUDES := bootable/recovery
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libminui libpng libz libutils libstdc++ libcutils liblog libm libc
|
||||
|
||||
ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
|
||||
LOCAL_STATIC_LIBRARIES += libsuspend
|
||||
endif
|
||||
|
||||
LOCAL_HAL_STATIC_LIBRARIES := libhealthd
|
||||
|
||||
# Symlink /charger to /sbin/healthd
|
||||
LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT) \
|
||||
&& ln -sf /sbin/healthd $(TARGET_ROOT_OUT)/charger
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
|
||||
define _add-charger-image
|
||||
include $$(CLEAR_VARS)
|
||||
LOCAL_MODULE := system_core_charger_$(notdir $(1))
|
||||
LOCAL_MODULE_STEM := $(notdir $(1))
|
||||
_img_modules += $$(LOCAL_MODULE)
|
||||
LOCAL_SRC_FILES := $1
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_CLASS := ETC
|
||||
LOCAL_MODULE_PATH := $$(TARGET_ROOT_OUT)/res/images/charger
|
||||
include $$(BUILD_PREBUILT)
|
||||
endef
|
||||
|
||||
_img_modules :=
|
||||
_images :=
|
||||
$(foreach _img, $(call find-subdir-subdir-files, "images", "*.png"), \
|
||||
$(eval $(call _add-charger-image,$(_img))))
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := charger_res_images
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_REQUIRED_MODULES := $(_img_modules)
|
||||
include $(BUILD_PHONY_PACKAGE)
|
||||
|
||||
_add-charger-image :=
|
||||
_img_modules :=
|
||||
|
||||
endif
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
#include "healthd.h"
|
||||
#include "BatteryMonitor.h"
|
||||
#include "BatteryPropertiesRegistrar.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
|
@ -28,11 +27,16 @@
|
|||
#include <unistd.h>
|
||||
#include <batteryservice/BatteryService.h>
|
||||
#include <cutils/klog.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <sys/types.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/String8.h>
|
||||
#include <utils/Vector.h>
|
||||
|
||||
#define POWER_SUPPLY_SUBSYSTEM "power_supply"
|
||||
#define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM
|
||||
#define FAKE_BATTERY_CAPACITY 42
|
||||
#define FAKE_BATTERY_TEMPERATURE 424
|
||||
|
||||
namespace android {
|
||||
|
||||
|
@ -170,7 +174,6 @@ int BatteryMonitor::getIntField(const String8& path) {
|
|||
}
|
||||
|
||||
bool BatteryMonitor::update(void) {
|
||||
struct BatteryProperties props;
|
||||
bool logthis;
|
||||
|
||||
props.chargerAcOnline = false;
|
||||
|
@ -178,24 +181,20 @@ bool BatteryMonitor::update(void) {
|
|||
props.chargerWirelessOnline = false;
|
||||
props.batteryStatus = BATTERY_STATUS_UNKNOWN;
|
||||
props.batteryHealth = BATTERY_HEALTH_UNKNOWN;
|
||||
props.batteryCurrentNow = INT_MIN;
|
||||
props.batteryChargeCounter = INT_MIN;
|
||||
|
||||
if (!mHealthdConfig->batteryPresentPath.isEmpty())
|
||||
props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
|
||||
else
|
||||
props.batteryPresent = true;
|
||||
props.batteryPresent = mBatteryDevicePresent;
|
||||
|
||||
props.batteryLevel = getIntField(mHealthdConfig->batteryCapacityPath);
|
||||
props.batteryLevel = mBatteryFixedCapacity ?
|
||||
mBatteryFixedCapacity :
|
||||
getIntField(mHealthdConfig->batteryCapacityPath);
|
||||
props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
|
||||
|
||||
if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
|
||||
props.batteryCurrentNow = getIntField(mHealthdConfig->batteryCurrentNowPath);
|
||||
|
||||
if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
|
||||
props.batteryChargeCounter = getIntField(mHealthdConfig->batteryChargeCounterPath);
|
||||
|
||||
props.batteryTemperature = getIntField(mHealthdConfig->batteryTemperaturePath);
|
||||
props.batteryTemperature = mBatteryFixedTemperature ?
|
||||
mBatteryFixedTemperature :
|
||||
getIntField(mHealthdConfig->batteryTemperaturePath);
|
||||
|
||||
const int SIZE = 128;
|
||||
char buf[SIZE];
|
||||
|
@ -244,7 +243,9 @@ bool BatteryMonitor::update(void) {
|
|||
|
||||
if (logthis) {
|
||||
char dmesgline[256];
|
||||
snprintf(dmesgline, sizeof(dmesgline),
|
||||
|
||||
if (props.batteryPresent) {
|
||||
snprintf(dmesgline, sizeof(dmesgline),
|
||||
"battery l=%d v=%d t=%s%d.%d h=%d st=%d",
|
||||
props.batteryLevel, props.batteryVoltage,
|
||||
props.batteryTemperature < 0 ? "-" : "",
|
||||
|
@ -252,11 +253,16 @@ bool BatteryMonitor::update(void) {
|
|||
abs(props.batteryTemperature % 10), props.batteryHealth,
|
||||
props.batteryStatus);
|
||||
|
||||
if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
|
||||
char b[20];
|
||||
if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
|
||||
int c = getIntField(mHealthdConfig->batteryCurrentNowPath);
|
||||
char b[20];
|
||||
|
||||
snprintf(b, sizeof(b), " c=%d", props.batteryCurrentNow / 1000);
|
||||
strlcat(dmesgline, b, sizeof(dmesgline));
|
||||
snprintf(b, sizeof(b), " c=%d", c / 1000);
|
||||
strlcat(dmesgline, b, sizeof(dmesgline));
|
||||
}
|
||||
} else {
|
||||
snprintf(dmesgline, sizeof(dmesgline),
|
||||
"battery none");
|
||||
}
|
||||
|
||||
KLOG_INFO(LOG_TAG, "%s chg=%s%s%s\n", dmesgline,
|
||||
|
@ -265,15 +271,110 @@ bool BatteryMonitor::update(void) {
|
|||
props.chargerWirelessOnline ? "w" : "");
|
||||
}
|
||||
|
||||
if (mBatteryPropertiesRegistrar != NULL)
|
||||
mBatteryPropertiesRegistrar->notifyListeners(props);
|
||||
|
||||
healthd_mode_ops->battery_update(&props);
|
||||
return props.chargerAcOnline | props.chargerUsbOnline |
|
||||
props.chargerWirelessOnline;
|
||||
}
|
||||
|
||||
void BatteryMonitor::init(struct healthd_config *hc, bool nosvcmgr) {
|
||||
status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
|
||||
status_t ret = BAD_VALUE;
|
||||
|
||||
val->valueInt64 = LONG_MIN;
|
||||
|
||||
switch(id) {
|
||||
case BATTERY_PROP_CHARGE_COUNTER:
|
||||
if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
|
||||
val->valueInt64 =
|
||||
getIntField(mHealthdConfig->batteryChargeCounterPath);
|
||||
ret = NO_ERROR;
|
||||
} else {
|
||||
ret = NAME_NOT_FOUND;
|
||||
}
|
||||
break;
|
||||
|
||||
case BATTERY_PROP_CURRENT_NOW:
|
||||
if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
|
||||
val->valueInt64 =
|
||||
getIntField(mHealthdConfig->batteryCurrentNowPath);
|
||||
ret = NO_ERROR;
|
||||
} else {
|
||||
ret = NAME_NOT_FOUND;
|
||||
}
|
||||
break;
|
||||
|
||||
case BATTERY_PROP_CURRENT_AVG:
|
||||
if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
|
||||
val->valueInt64 =
|
||||
getIntField(mHealthdConfig->batteryCurrentAvgPath);
|
||||
ret = NO_ERROR;
|
||||
} else {
|
||||
ret = NAME_NOT_FOUND;
|
||||
}
|
||||
break;
|
||||
|
||||
case BATTERY_PROP_CAPACITY:
|
||||
if (!mHealthdConfig->batteryCapacityPath.isEmpty()) {
|
||||
val->valueInt64 =
|
||||
getIntField(mHealthdConfig->batteryCapacityPath);
|
||||
ret = NO_ERROR;
|
||||
} else {
|
||||
ret = NAME_NOT_FOUND;
|
||||
}
|
||||
break;
|
||||
|
||||
case BATTERY_PROP_ENERGY_COUNTER:
|
||||
if (mHealthdConfig->energyCounter) {
|
||||
ret = mHealthdConfig->energyCounter(&val->valueInt64);
|
||||
} else {
|
||||
ret = NAME_NOT_FOUND;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void BatteryMonitor::dumpState(int fd) {
|
||||
int v;
|
||||
char vs[128];
|
||||
|
||||
snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d\n",
|
||||
props.chargerAcOnline, props.chargerUsbOnline,
|
||||
props.chargerWirelessOnline);
|
||||
write(fd, vs, strlen(vs));
|
||||
snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n",
|
||||
props.batteryStatus, props.batteryHealth, props.batteryPresent);
|
||||
write(fd, vs, strlen(vs));
|
||||
snprintf(vs, sizeof(vs), "level: %d voltage: %d temp: %d\n",
|
||||
props.batteryLevel, props.batteryVoltage,
|
||||
props.batteryTemperature);
|
||||
write(fd, vs, strlen(vs));
|
||||
|
||||
if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
|
||||
v = getIntField(mHealthdConfig->batteryCurrentNowPath);
|
||||
snprintf(vs, sizeof(vs), "current now: %d\n", v);
|
||||
write(fd, vs, strlen(vs));
|
||||
}
|
||||
|
||||
if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
|
||||
v = getIntField(mHealthdConfig->batteryCurrentAvgPath);
|
||||
snprintf(vs, sizeof(vs), "current avg: %d\n", v);
|
||||
write(fd, vs, strlen(vs));
|
||||
}
|
||||
|
||||
if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
|
||||
v = getIntField(mHealthdConfig->batteryChargeCounterPath);
|
||||
snprintf(vs, sizeof(vs), "charge counter: %d\n", v);
|
||||
write(fd, vs, strlen(vs));
|
||||
}
|
||||
}
|
||||
|
||||
void BatteryMonitor::init(struct healthd_config *hc) {
|
||||
String8 path;
|
||||
char pval[PROPERTY_VALUE_MAX];
|
||||
|
||||
mHealthdConfig = hc;
|
||||
DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH);
|
||||
|
@ -303,6 +404,8 @@ void BatteryMonitor::init(struct healthd_config *hc, bool nosvcmgr) {
|
|||
break;
|
||||
|
||||
case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
|
||||
mBatteryDevicePresent = true;
|
||||
|
||||
if (mHealthdConfig->batteryStatusPath.isEmpty()) {
|
||||
path.clear();
|
||||
path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
|
||||
|
@ -358,6 +461,14 @@ void BatteryMonitor::init(struct healthd_config *hc, bool nosvcmgr) {
|
|||
mHealthdConfig->batteryCurrentNowPath = path;
|
||||
}
|
||||
|
||||
if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
|
||||
path.clear();
|
||||
path.appendFormat("%s/%s/current_avg",
|
||||
POWER_SUPPLY_SYSFS_PATH, name);
|
||||
if (access(path, R_OK) == 0)
|
||||
mHealthdConfig->batteryCurrentAvgPath = path;
|
||||
}
|
||||
|
||||
if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
|
||||
path.clear();
|
||||
path.appendFormat("%s/%s/charge_counter",
|
||||
|
@ -400,24 +511,31 @@ void BatteryMonitor::init(struct healthd_config *hc, bool nosvcmgr) {
|
|||
|
||||
if (!mChargerNames.size())
|
||||
KLOG_ERROR(LOG_TAG, "No charger supplies found\n");
|
||||
if (mHealthdConfig->batteryStatusPath.isEmpty())
|
||||
KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
|
||||
if (mHealthdConfig->batteryHealthPath.isEmpty())
|
||||
KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n");
|
||||
if (mHealthdConfig->batteryPresentPath.isEmpty())
|
||||
KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");
|
||||
if (mHealthdConfig->batteryCapacityPath.isEmpty())
|
||||
KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
|
||||
if (mHealthdConfig->batteryVoltagePath.isEmpty())
|
||||
KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
|
||||
if (mHealthdConfig->batteryTemperaturePath.isEmpty())
|
||||
KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
|
||||
if (mHealthdConfig->batteryTechnologyPath.isEmpty())
|
||||
KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
|
||||
if (!mBatteryDevicePresent) {
|
||||
KLOG_INFO(LOG_TAG, "No battery devices found\n");
|
||||
hc->periodic_chores_interval_fast = -1;
|
||||
hc->periodic_chores_interval_slow = -1;
|
||||
} else {
|
||||
if (mHealthdConfig->batteryStatusPath.isEmpty())
|
||||
KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
|
||||
if (mHealthdConfig->batteryHealthPath.isEmpty())
|
||||
KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n");
|
||||
if (mHealthdConfig->batteryPresentPath.isEmpty())
|
||||
KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");
|
||||
if (mHealthdConfig->batteryCapacityPath.isEmpty())
|
||||
KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
|
||||
if (mHealthdConfig->batteryVoltagePath.isEmpty())
|
||||
KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
|
||||
if (mHealthdConfig->batteryTemperaturePath.isEmpty())
|
||||
KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
|
||||
if (mHealthdConfig->batteryTechnologyPath.isEmpty())
|
||||
KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
|
||||
}
|
||||
|
||||
if (nosvcmgr == false) {
|
||||
mBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar(this);
|
||||
mBatteryPropertiesRegistrar->publish();
|
||||
if (property_get("ro.boot.fake_battery", pval, NULL) > 0
|
||||
&& strtol(pval, NULL, 10) != 0) {
|
||||
mBatteryFixedCapacity = FAKE_BATTERY_CAPACITY;
|
||||
mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,17 +17,15 @@
|
|||
#ifndef HEALTHD_BATTERYMONITOR_H
|
||||
#define HEALTHD_BATTERYMONITOR_H
|
||||
|
||||
#include <batteryservice/BatteryService.h>
|
||||
#include <binder/IInterface.h>
|
||||
#include <utils/String8.h>
|
||||
#include <utils/Vector.h>
|
||||
|
||||
#include "healthd.h"
|
||||
#include "BatteryPropertiesRegistrar.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
class BatteryPropertiesRegistrar;
|
||||
|
||||
class BatteryMonitor {
|
||||
public:
|
||||
|
||||
|
@ -39,14 +37,18 @@ class BatteryMonitor {
|
|||
ANDROID_POWER_SUPPLY_TYPE_BATTERY
|
||||
};
|
||||
|
||||
void init(struct healthd_config *hc, bool nosvcmgr);
|
||||
void init(struct healthd_config *hc);
|
||||
bool update(void);
|
||||
status_t getProperty(int id, struct BatteryProperty *val);
|
||||
void dumpState(int fd);
|
||||
|
||||
private:
|
||||
struct healthd_config *mHealthdConfig;
|
||||
Vector<String8> mChargerNames;
|
||||
|
||||
sp<BatteryPropertiesRegistrar> mBatteryPropertiesRegistrar;
|
||||
bool mBatteryDevicePresent;
|
||||
int mBatteryFixedCapacity;
|
||||
int mBatteryFixedTemperature;
|
||||
struct BatteryProperties props;
|
||||
|
||||
int getBatteryStatus(const char* status);
|
||||
int getBatteryHealth(const char* status);
|
||||
|
|
|
@ -18,19 +18,20 @@
|
|||
#include <batteryservice/BatteryService.h>
|
||||
#include <batteryservice/IBatteryPropertiesListener.h>
|
||||
#include <batteryservice/IBatteryPropertiesRegistrar.h>
|
||||
#include <binder/IPCThreadState.h>
|
||||
#include <binder/IServiceManager.h>
|
||||
#include <binder/PermissionCache.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/Mutex.h>
|
||||
#include <utils/String16.h>
|
||||
|
||||
#include "healthd.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
BatteryPropertiesRegistrar::BatteryPropertiesRegistrar(BatteryMonitor* monitor) {
|
||||
mBatteryMonitor = monitor;
|
||||
}
|
||||
|
||||
void BatteryPropertiesRegistrar::publish() {
|
||||
defaultServiceManager()->addService(String16("batterypropreg"), this);
|
||||
defaultServiceManager()->addService(String16("batteryproperties"), this);
|
||||
}
|
||||
|
||||
void BatteryPropertiesRegistrar::notifyListeners(struct BatteryProperties props) {
|
||||
|
@ -42,6 +43,8 @@ void BatteryPropertiesRegistrar::notifyListeners(struct BatteryProperties props)
|
|||
|
||||
void BatteryPropertiesRegistrar::registerListener(const sp<IBatteryPropertiesListener>& listener) {
|
||||
{
|
||||
if (listener == NULL)
|
||||
return;
|
||||
Mutex::Autolock _l(mRegistrationLock);
|
||||
// check whether this is a duplicate
|
||||
for (size_t i = 0; i < mListeners.size(); i++) {
|
||||
|
@ -53,10 +56,12 @@ void BatteryPropertiesRegistrar::registerListener(const sp<IBatteryPropertiesLis
|
|||
mListeners.add(listener);
|
||||
listener->asBinder()->linkToDeath(this);
|
||||
}
|
||||
mBatteryMonitor->update();
|
||||
healthd_battery_update();
|
||||
}
|
||||
|
||||
void BatteryPropertiesRegistrar::unregisterListener(const sp<IBatteryPropertiesListener>& listener) {
|
||||
if (listener == NULL)
|
||||
return;
|
||||
Mutex::Autolock _l(mRegistrationLock);
|
||||
for (size_t i = 0; i < mListeners.size(); i++) {
|
||||
if (mListeners[i]->asBinder() == listener->asBinder()) {
|
||||
|
@ -67,6 +72,23 @@ void BatteryPropertiesRegistrar::unregisterListener(const sp<IBatteryPropertiesL
|
|||
}
|
||||
}
|
||||
|
||||
status_t BatteryPropertiesRegistrar::getProperty(int id, struct BatteryProperty *val) {
|
||||
return healthd_get_property(id, val);
|
||||
}
|
||||
|
||||
status_t BatteryPropertiesRegistrar::dump(int fd, const Vector<String16>& /*args*/) {
|
||||
IPCThreadState* self = IPCThreadState::self();
|
||||
const int pid = self->getCallingPid();
|
||||
const int uid = self->getCallingUid();
|
||||
if ((uid != AID_SHELL) &&
|
||||
!PermissionCache::checkPermission(
|
||||
String16("android.permission.DUMP"), pid, uid))
|
||||
return PERMISSION_DENIED;
|
||||
|
||||
healthd_dump_battery_state(fd);
|
||||
return OK;
|
||||
}
|
||||
|
||||
void BatteryPropertiesRegistrar::binderDied(const wp<IBinder>& who) {
|
||||
Mutex::Autolock _l(mRegistrationLock);
|
||||
|
||||
|
|
|
@ -17,10 +17,9 @@
|
|||
#ifndef HEALTHD_BATTERYPROPERTIES_REGISTRAR_H
|
||||
#define HEALTHD_BATTERYPROPERTIES_REGISTRAR_H
|
||||
|
||||
#include "BatteryMonitor.h"
|
||||
|
||||
#include <binder/IBinder.h>
|
||||
#include <utils/Mutex.h>
|
||||
#include <utils/String16.h>
|
||||
#include <utils/Vector.h>
|
||||
#include <batteryservice/BatteryService.h>
|
||||
#include <batteryservice/IBatteryPropertiesListener.h>
|
||||
|
@ -28,22 +27,20 @@
|
|||
|
||||
namespace android {
|
||||
|
||||
class BatteryMonitor;
|
||||
|
||||
class BatteryPropertiesRegistrar : public BnBatteryPropertiesRegistrar,
|
||||
public IBinder::DeathRecipient {
|
||||
public:
|
||||
BatteryPropertiesRegistrar(BatteryMonitor* monitor);
|
||||
void publish();
|
||||
void notifyListeners(struct BatteryProperties props);
|
||||
|
||||
private:
|
||||
BatteryMonitor* mBatteryMonitor;
|
||||
Mutex mRegistrationLock;
|
||||
Vector<sp<IBatteryPropertiesListener> > mListeners;
|
||||
|
||||
void registerListener(const sp<IBatteryPropertiesListener>& listener);
|
||||
void unregisterListener(const sp<IBatteryPropertiesListener>& listener);
|
||||
status_t getProperty(int id, struct BatteryProperty *val);
|
||||
status_t dump(int fd, const Vector<String16>& args);
|
||||
void binderDied(const wp<IBinder>& who);
|
||||
};
|
||||
|
||||
|
|
|
@ -21,16 +21,17 @@
|
|||
#include "BatteryMonitor.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <batteryservice/BatteryService.h>
|
||||
#include <binder/IPCThreadState.h>
|
||||
#include <binder/ProcessState.h>
|
||||
#include <cutils/klog.h>
|
||||
#include <cutils/uevent.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <utils/Errors.h>
|
||||
|
||||
using namespace android;
|
||||
|
||||
|
@ -49,13 +50,18 @@ static struct healthd_config healthd_config = {
|
|||
.batteryTemperaturePath = String8(String8::kEmptyString),
|
||||
.batteryTechnologyPath = String8(String8::kEmptyString),
|
||||
.batteryCurrentNowPath = String8(String8::kEmptyString),
|
||||
.batteryCurrentAvgPath = String8(String8::kEmptyString),
|
||||
.batteryChargeCounterPath = String8(String8::kEmptyString),
|
||||
.energyCounter = NULL,
|
||||
};
|
||||
|
||||
static int eventct;
|
||||
static int epollfd;
|
||||
|
||||
#define POWER_SUPPLY_SUBSYSTEM "power_supply"
|
||||
|
||||
// epoll events: uevent, wakealarm, binder
|
||||
#define MAX_EPOLL_EVENTS 3
|
||||
// epoll_create() parameter is actually unused
|
||||
#define MAX_EPOLL_EVENTS 40
|
||||
static int uevent_fd;
|
||||
static int wakealarm_fd;
|
||||
static int binder_fd;
|
||||
|
@ -67,7 +73,80 @@ static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST;
|
|||
|
||||
static BatteryMonitor* gBatteryMonitor;
|
||||
|
||||
static bool nosvcmgr;
|
||||
struct healthd_mode_ops *healthd_mode_ops;
|
||||
|
||||
// Android mode
|
||||
|
||||
extern void healthd_mode_android_init(struct healthd_config *config);
|
||||
extern int healthd_mode_android_preparetowait(void);
|
||||
extern void healthd_mode_android_battery_update(
|
||||
struct android::BatteryProperties *props);
|
||||
|
||||
// Charger mode
|
||||
|
||||
extern void healthd_mode_charger_init(struct healthd_config *config);
|
||||
extern int healthd_mode_charger_preparetowait(void);
|
||||
extern void healthd_mode_charger_heartbeat(void);
|
||||
extern void healthd_mode_charger_battery_update(
|
||||
struct android::BatteryProperties *props);
|
||||
|
||||
// NOPs for modes that need no special action
|
||||
|
||||
static void healthd_mode_nop_init(struct healthd_config *config);
|
||||
static int healthd_mode_nop_preparetowait(void);
|
||||
static void healthd_mode_nop_heartbeat(void);
|
||||
static void healthd_mode_nop_battery_update(
|
||||
struct android::BatteryProperties *props);
|
||||
|
||||
static struct healthd_mode_ops android_ops = {
|
||||
.init = healthd_mode_android_init,
|
||||
.preparetowait = healthd_mode_android_preparetowait,
|
||||
.heartbeat = healthd_mode_nop_heartbeat,
|
||||
.battery_update = healthd_mode_android_battery_update,
|
||||
};
|
||||
|
||||
static struct healthd_mode_ops charger_ops = {
|
||||
.init = healthd_mode_charger_init,
|
||||
.preparetowait = healthd_mode_charger_preparetowait,
|
||||
.heartbeat = healthd_mode_charger_heartbeat,
|
||||
.battery_update = healthd_mode_charger_battery_update,
|
||||
};
|
||||
|
||||
static struct healthd_mode_ops recovery_ops = {
|
||||
.init = healthd_mode_nop_init,
|
||||
.preparetowait = healthd_mode_nop_preparetowait,
|
||||
.heartbeat = healthd_mode_nop_heartbeat,
|
||||
.battery_update = healthd_mode_nop_battery_update,
|
||||
};
|
||||
|
||||
static void healthd_mode_nop_init(struct healthd_config* /*config*/) {
|
||||
}
|
||||
|
||||
static int healthd_mode_nop_preparetowait(void) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void healthd_mode_nop_heartbeat(void) {
|
||||
}
|
||||
|
||||
static void healthd_mode_nop_battery_update(
|
||||
struct android::BatteryProperties* /*props*/) {
|
||||
}
|
||||
|
||||
int healthd_register_event(int fd, void (*handler)(uint32_t)) {
|
||||
struct epoll_event ev;
|
||||
|
||||
ev.events = EPOLLIN | EPOLLWAKEUP;
|
||||
ev.data.ptr = (void *)handler;
|
||||
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
|
||||
KLOG_ERROR(LOG_TAG,
|
||||
"epoll_ctl failed; errno=%d\n", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
eventct++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wakealarm_set_interval(int interval) {
|
||||
struct itimerspec itval;
|
||||
|
@ -89,7 +168,11 @@ static void wakealarm_set_interval(int interval) {
|
|||
KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
|
||||
}
|
||||
|
||||
static void battery_update(void) {
|
||||
status_t healthd_get_property(int id, struct BatteryProperty *val) {
|
||||
return gBatteryMonitor->getProperty(id, val);
|
||||
}
|
||||
|
||||
void healthd_battery_update(void) {
|
||||
// Fast wake interval when on charger (watch for overheat);
|
||||
// slow wake interval when on battery (watch for drained battery).
|
||||
|
||||
|
@ -113,21 +196,17 @@ static void battery_update(void) {
|
|||
-1 : healthd_config.periodic_chores_interval_fast * 1000;
|
||||
}
|
||||
|
||||
static void periodic_chores() {
|
||||
battery_update();
|
||||
void healthd_dump_battery_state(int fd) {
|
||||
gBatteryMonitor->dumpState(fd);
|
||||
fsync(fd);
|
||||
}
|
||||
|
||||
static void uevent_init(void) {
|
||||
uevent_fd = uevent_open_socket(64*1024, true);
|
||||
|
||||
if (uevent_fd >= 0)
|
||||
fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
|
||||
else
|
||||
KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
|
||||
static void periodic_chores() {
|
||||
healthd_battery_update();
|
||||
}
|
||||
|
||||
#define UEVENT_MSG_LEN 2048
|
||||
static void uevent_event(void) {
|
||||
static void uevent_event(uint32_t /*epevents*/) {
|
||||
char msg[UEVENT_MSG_LEN+2];
|
||||
char *cp;
|
||||
int n;
|
||||
|
@ -144,7 +223,7 @@ static void uevent_event(void) {
|
|||
|
||||
while (*cp) {
|
||||
if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
|
||||
battery_update();
|
||||
healthd_battery_update();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -154,6 +233,31 @@ static void uevent_event(void) {
|
|||
}
|
||||
}
|
||||
|
||||
static void uevent_init(void) {
|
||||
uevent_fd = uevent_open_socket(64*1024, true);
|
||||
|
||||
if (uevent_fd < 0) {
|
||||
KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
|
||||
if (healthd_register_event(uevent_fd, uevent_event))
|
||||
KLOG_ERROR(LOG_TAG,
|
||||
"register for uevent events failed\n");
|
||||
}
|
||||
|
||||
static void wakealarm_event(uint32_t /*epevents*/) {
|
||||
unsigned long long wakeups;
|
||||
|
||||
if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) {
|
||||
KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
periodic_chores();
|
||||
}
|
||||
|
||||
static void wakealarm_init(void) {
|
||||
wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK);
|
||||
if (wakealarm_fd == -1) {
|
||||
|
@ -161,82 +265,24 @@ static void wakealarm_init(void) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (healthd_register_event(wakealarm_fd, wakealarm_event))
|
||||
KLOG_ERROR(LOG_TAG,
|
||||
"Registration of wakealarm event failed\n");
|
||||
|
||||
wakealarm_set_interval(healthd_config.periodic_chores_interval_fast);
|
||||
}
|
||||
|
||||
static void wakealarm_event(void) {
|
||||
unsigned long long wakeups;
|
||||
|
||||
if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) {
|
||||
KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm_fd failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
periodic_chores();
|
||||
}
|
||||
|
||||
static void binder_init(void) {
|
||||
ProcessState::self()->setThreadPoolMaxThreadCount(0);
|
||||
IPCThreadState::self()->disableBackgroundScheduling(true);
|
||||
IPCThreadState::self()->setupPolling(&binder_fd);
|
||||
}
|
||||
|
||||
static void binder_event(void) {
|
||||
IPCThreadState::self()->handlePolledCommands();
|
||||
}
|
||||
|
||||
static void healthd_mainloop(void) {
|
||||
struct epoll_event ev;
|
||||
int epollfd;
|
||||
int maxevents = 0;
|
||||
|
||||
epollfd = epoll_create(MAX_EPOLL_EVENTS);
|
||||
if (epollfd == -1) {
|
||||
KLOG_ERROR(LOG_TAG,
|
||||
"healthd_mainloop: epoll_create failed; errno=%d\n",
|
||||
errno);
|
||||
return;
|
||||
}
|
||||
|
||||
if (uevent_fd >= 0) {
|
||||
ev.events = EPOLLIN | EPOLLWAKEUP;
|
||||
ev.data.ptr = (void *)uevent_event;
|
||||
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1)
|
||||
KLOG_ERROR(LOG_TAG,
|
||||
"healthd_mainloop: epoll_ctl for uevent_fd failed; errno=%d\n",
|
||||
errno);
|
||||
else
|
||||
maxevents++;
|
||||
}
|
||||
|
||||
if (wakealarm_fd >= 0) {
|
||||
ev.events = EPOLLIN | EPOLLWAKEUP;
|
||||
ev.data.ptr = (void *)wakealarm_event;
|
||||
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, wakealarm_fd, &ev) == -1)
|
||||
KLOG_ERROR(LOG_TAG,
|
||||
"healthd_mainloop: epoll_ctl for wakealarm_fd failed; errno=%d\n",
|
||||
errno);
|
||||
else
|
||||
maxevents++;
|
||||
}
|
||||
|
||||
if (binder_fd >= 0) {
|
||||
ev.events = EPOLLIN | EPOLLWAKEUP;
|
||||
ev.data.ptr= (void *)binder_event;
|
||||
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, binder_fd, &ev) == -1)
|
||||
KLOG_ERROR(LOG_TAG,
|
||||
"healthd_mainloop: epoll_ctl for binder_fd failed; errno=%d\n",
|
||||
errno);
|
||||
else
|
||||
maxevents++;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
struct epoll_event events[maxevents];
|
||||
struct epoll_event events[eventct];
|
||||
int nevents;
|
||||
int timeout = awake_poll_interval;
|
||||
int mode_timeout;
|
||||
|
||||
IPCThreadState::self()->flushCommands();
|
||||
nevents = epoll_wait(epollfd, events, maxevents, awake_poll_interval);
|
||||
mode_timeout = healthd_mode_ops->preparetowait();
|
||||
if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout))
|
||||
timeout = mode_timeout;
|
||||
nevents = epoll_wait(epollfd, events, eventct, timeout);
|
||||
|
||||
if (nevents == -1) {
|
||||
if (errno == EINTR)
|
||||
|
@ -247,39 +293,70 @@ static void healthd_mainloop(void) {
|
|||
|
||||
for (int n = 0; n < nevents; ++n) {
|
||||
if (events[n].data.ptr)
|
||||
(*(void (*)())events[n].data.ptr)();
|
||||
(*(void (*)(int))events[n].data.ptr)(events[n].events);
|
||||
}
|
||||
|
||||
if (!nevents)
|
||||
periodic_chores();
|
||||
|
||||
healthd_mode_ops->heartbeat();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int ch;
|
||||
|
||||
klog_set_level(KLOG_LEVEL);
|
||||
|
||||
while ((ch = getopt(argc, argv, "n")) != -1) {
|
||||
switch (ch) {
|
||||
case 'n':
|
||||
nosvcmgr = true;
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
KLOG_WARNING(LOG_TAG, "Unrecognized healthd option: %c\n", ch);
|
||||
}
|
||||
static int healthd_init() {
|
||||
epollfd = epoll_create(MAX_EPOLL_EVENTS);
|
||||
if (epollfd == -1) {
|
||||
KLOG_ERROR(LOG_TAG,
|
||||
"epoll_create failed; errno=%d\n",
|
||||
errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
healthd_mode_ops->init(&healthd_config);
|
||||
healthd_board_init(&healthd_config);
|
||||
wakealarm_init();
|
||||
uevent_init();
|
||||
binder_init();
|
||||
gBatteryMonitor = new BatteryMonitor();
|
||||
gBatteryMonitor->init(&healthd_config, nosvcmgr);
|
||||
|
||||
healthd_mainloop();
|
||||
gBatteryMonitor->init(&healthd_config);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int ch;
|
||||
int ret;
|
||||
|
||||
klog_set_level(KLOG_LEVEL);
|
||||
healthd_mode_ops = &android_ops;
|
||||
|
||||
if (!strcmp(basename(argv[0]), "charger")) {
|
||||
healthd_mode_ops = &charger_ops;
|
||||
} else {
|
||||
while ((ch = getopt(argc, argv, "cr")) != -1) {
|
||||
switch (ch) {
|
||||
case 'c':
|
||||
healthd_mode_ops = &charger_ops;
|
||||
break;
|
||||
case 'r':
|
||||
healthd_mode_ops = &recovery_ops;
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
KLOG_ERROR(LOG_TAG, "Unrecognized healthd option: %c\n",
|
||||
optopt);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = healthd_init();
|
||||
if (ret) {
|
||||
KLOG_ERROR("Initialization failed, exiting\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
healthd_mainloop();
|
||||
KLOG_ERROR("Main loop terminated, exiting\n");
|
||||
return 3;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#define _HEALTHD_H_
|
||||
|
||||
#include <batteryservice/BatteryService.h>
|
||||
#include <sys/types.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/String8.h>
|
||||
|
||||
// periodic_chores_interval_fast, periodic_chores_interval_slow: intervals at
|
||||
|
@ -61,9 +63,37 @@ struct healthd_config {
|
|||
android::String8 batteryTemperaturePath;
|
||||
android::String8 batteryTechnologyPath;
|
||||
android::String8 batteryCurrentNowPath;
|
||||
android::String8 batteryCurrentAvgPath;
|
||||
android::String8 batteryChargeCounterPath;
|
||||
|
||||
int (*energyCounter)(int64_t *);
|
||||
};
|
||||
|
||||
// Global helper functions
|
||||
|
||||
int healthd_register_event(int fd, void (*handler)(uint32_t));
|
||||
void healthd_battery_update();
|
||||
android::status_t healthd_get_property(int id,
|
||||
struct android::BatteryProperty *val);
|
||||
void healthd_dump_battery_state(int fd);
|
||||
|
||||
struct healthd_mode_ops {
|
||||
void (*init)(struct healthd_config *config);
|
||||
int (*preparetowait)(void);
|
||||
void (*heartbeat)(void);
|
||||
void (*battery_update)(struct android::BatteryProperties *props);
|
||||
};
|
||||
|
||||
extern struct healthd_mode_ops *healthd_mode_ops;
|
||||
|
||||
// Charger mode
|
||||
|
||||
void healthd_mode_charger_init(struct healthd_config *config);
|
||||
int healthd_mode_charger_preparetowait(void);
|
||||
void healthd_mode_charger_heartbeat(void);
|
||||
void healthd_mode_charger_battery_update(
|
||||
struct android::BatteryProperties *props);
|
||||
|
||||
// The following are implemented in libhealthd_board to handle board-specific
|
||||
// behavior.
|
||||
//
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (C) 2013 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "healthd-android"
|
||||
|
||||
#include "healthd.h"
|
||||
#include "BatteryPropertiesRegistrar.h"
|
||||
|
||||
#include <binder/IPCThreadState.h>
|
||||
#include <binder/ProcessState.h>
|
||||
#include <cutils/klog.h>
|
||||
#include <sys/epoll.h>
|
||||
|
||||
using namespace android;
|
||||
|
||||
static int gBinderFd;
|
||||
static sp<BatteryPropertiesRegistrar> gBatteryPropertiesRegistrar;
|
||||
|
||||
void healthd_mode_android_battery_update(
|
||||
struct android::BatteryProperties *props) {
|
||||
if (gBatteryPropertiesRegistrar != NULL)
|
||||
gBatteryPropertiesRegistrar->notifyListeners(*props);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int healthd_mode_android_preparetowait(void) {
|
||||
IPCThreadState::self()->flushCommands();
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void binder_event(uint32_t /*epevents*/) {
|
||||
IPCThreadState::self()->handlePolledCommands();
|
||||
}
|
||||
|
||||
void healthd_mode_android_init(struct healthd_config* /*config*/) {
|
||||
ProcessState::self()->setThreadPoolMaxThreadCount(0);
|
||||
IPCThreadState::self()->disableBackgroundScheduling(true);
|
||||
IPCThreadState::self()->setupPolling(&gBinderFd);
|
||||
|
||||
if (gBinderFd >= 0) {
|
||||
if (healthd_register_event(gBinderFd, binder_event))
|
||||
KLOG_ERROR(LOG_TAG,
|
||||
"Register for binder events failed\n");
|
||||
}
|
||||
|
||||
gBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar();
|
||||
gBatteryPropertiesRegistrar->publish();
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
* Copyright (C) 2011-2013 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.
|
||||
|
@ -14,18 +14,16 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//#define DEBUG_UEVENTS
|
||||
#define CHARGER_KLOG_LEVEL 6
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <linux/input.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
|
@ -35,9 +33,9 @@
|
|||
#include <sys/socket.h>
|
||||
#include <linux/netlink.h>
|
||||
|
||||
#include <batteryservice/BatteryService.h>
|
||||
#include <cutils/android_reboot.h>
|
||||
#include <cutils/klog.h>
|
||||
#include <cutils/list.h>
|
||||
#include <cutils/misc.h>
|
||||
#include <cutils/uevent.h>
|
||||
#include <cutils/properties.h>
|
||||
|
@ -48,6 +46,8 @@
|
|||
|
||||
#include "minui/minui.h"
|
||||
|
||||
#include "healthd.h"
|
||||
|
||||
char *locale;
|
||||
|
||||
#ifndef max
|
||||
|
@ -68,8 +68,10 @@ char *locale;
|
|||
#define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)
|
||||
|
||||
#define BATTERY_FULL_THRESH 95
|
||||
#define SCREEN_ON_BATTERY_THRESH 0
|
||||
|
||||
#define LAST_KMSG_PATH "/proc/last_kmsg"
|
||||
#define LAST_KMSG_PSTORE_PATH "/sys/fs/pstore/console-ramoops"
|
||||
#define LAST_KMSG_MAX_SZ (32 * 1024)
|
||||
|
||||
#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0)
|
||||
|
@ -82,15 +84,6 @@ struct key_state {
|
|||
int64_t timestamp;
|
||||
};
|
||||
|
||||
struct power_supply {
|
||||
struct listnode list;
|
||||
char name[256];
|
||||
char type[32];
|
||||
bool online;
|
||||
bool valid;
|
||||
char cap_path[PATH_MAX];
|
||||
};
|
||||
|
||||
struct frame {
|
||||
int disp_time;
|
||||
int min_capacity;
|
||||
|
@ -114,72 +107,73 @@ struct animation {
|
|||
};
|
||||
|
||||
struct charger {
|
||||
bool have_battery_state;
|
||||
bool charger_connected;
|
||||
int capacity;
|
||||
int64_t next_screen_transition;
|
||||
int64_t next_key_check;
|
||||
int64_t next_pwr_check;
|
||||
|
||||
struct key_state keys[KEY_MAX + 1];
|
||||
int uevent_fd;
|
||||
|
||||
struct listnode supplies;
|
||||
int num_supplies;
|
||||
int num_supplies_online;
|
||||
|
||||
struct animation *batt_anim;
|
||||
gr_surface surf_unknown;
|
||||
|
||||
struct power_supply *battery;
|
||||
};
|
||||
|
||||
struct uevent {
|
||||
const char *action;
|
||||
const char *path;
|
||||
const char *subsystem;
|
||||
const char *ps_name;
|
||||
const char *ps_type;
|
||||
const char *ps_online;
|
||||
};
|
||||
|
||||
static struct frame batt_anim_frames[] = {
|
||||
{
|
||||
.disp_time = 750,
|
||||
.min_capacity = 0,
|
||||
.level_only = false,
|
||||
.surface = NULL,
|
||||
},
|
||||
{
|
||||
.disp_time = 750,
|
||||
.min_capacity = 20,
|
||||
.level_only = false,
|
||||
.surface = NULL,
|
||||
},
|
||||
{
|
||||
.disp_time = 750,
|
||||
.min_capacity = 40,
|
||||
.level_only = false,
|
||||
.surface = NULL,
|
||||
},
|
||||
{
|
||||
.disp_time = 750,
|
||||
.min_capacity = 60,
|
||||
.level_only = false,
|
||||
.surface = NULL,
|
||||
},
|
||||
{
|
||||
.disp_time = 750,
|
||||
.min_capacity = 80,
|
||||
.level_only = true,
|
||||
.surface = NULL,
|
||||
},
|
||||
{
|
||||
.disp_time = 750,
|
||||
.min_capacity = BATTERY_FULL_THRESH,
|
||||
.level_only = false,
|
||||
.surface = NULL,
|
||||
},
|
||||
};
|
||||
|
||||
static struct animation battery_animation = {
|
||||
.run = false,
|
||||
.frames = batt_anim_frames,
|
||||
.cur_frame = 0,
|
||||
.num_frames = ARRAY_SIZE(batt_anim_frames),
|
||||
.cur_cycle = 0,
|
||||
.num_cycles = 3,
|
||||
.capacity = 0,
|
||||
};
|
||||
|
||||
static struct charger charger_state = {
|
||||
.batt_anim = &battery_animation,
|
||||
};
|
||||
static struct charger charger_state;
|
||||
|
||||
static int char_width;
|
||||
static int char_height;
|
||||
static bool minui_inited;
|
||||
|
||||
/* current time in milliseconds */
|
||||
static int64_t curr_time_ms(void)
|
||||
|
@ -207,10 +201,14 @@ static void dump_last_kmsg(void)
|
|||
LOGI("\n");
|
||||
LOGI("*************** LAST KMSG ***************\n");
|
||||
LOGI("\n");
|
||||
buf = load_file(LAST_KMSG_PATH, &sz);
|
||||
buf = (char *)load_file(LAST_KMSG_PSTORE_PATH, &sz);
|
||||
|
||||
if (!buf || !sz) {
|
||||
LOGI("last_kmsg not found. Cold reset?\n");
|
||||
goto out;
|
||||
buf = (char *)load_file(LAST_KMSG_PATH, &sz);
|
||||
if (!buf || !sz) {
|
||||
LOGI("last_kmsg not found. Cold reset?\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
len = min(sz, LAST_KMSG_MAX_SZ);
|
||||
|
@ -221,7 +219,7 @@ static void dump_last_kmsg(void)
|
|||
char yoink;
|
||||
char *nl;
|
||||
|
||||
nl = memrchr(ptr, '\n', cnt - 1);
|
||||
nl = (char *)memrchr(ptr, '\n', cnt - 1);
|
||||
if (nl)
|
||||
cnt = nl - ptr + 1;
|
||||
|
||||
|
@ -242,114 +240,9 @@ out:
|
|||
LOGI("\n");
|
||||
}
|
||||
|
||||
static int read_file(const char *path, char *buf, size_t sz)
|
||||
static int get_battery_capacity()
|
||||
{
|
||||
int fd;
|
||||
size_t cnt;
|
||||
|
||||
fd = open(path, O_RDONLY, 0);
|
||||
if (fd < 0)
|
||||
goto err;
|
||||
|
||||
cnt = read(fd, buf, sz - 1);
|
||||
if (cnt <= 0)
|
||||
goto err;
|
||||
buf[cnt] = '\0';
|
||||
if (buf[cnt - 1] == '\n') {
|
||||
cnt--;
|
||||
buf[cnt] = '\0';
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return cnt;
|
||||
|
||||
err:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int read_file_int(const char *path, int *val)
|
||||
{
|
||||
char buf[32];
|
||||
int ret;
|
||||
int tmp;
|
||||
char *end;
|
||||
|
||||
ret = read_file(path, buf, sizeof(buf));
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
tmp = strtol(buf, &end, 0);
|
||||
if (end == buf ||
|
||||
((end < buf+sizeof(buf)) && (*end != '\n' && *end != '\0')))
|
||||
goto err;
|
||||
|
||||
*val = tmp;
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int get_battery_capacity(struct charger *charger)
|
||||
{
|
||||
int ret;
|
||||
int batt_cap = -1;
|
||||
|
||||
if (!charger->battery)
|
||||
return -1;
|
||||
|
||||
ret = read_file_int(charger->battery->cap_path, &batt_cap);
|
||||
if (ret < 0 || batt_cap > 100) {
|
||||
batt_cap = -1;
|
||||
}
|
||||
|
||||
return batt_cap;
|
||||
}
|
||||
|
||||
static struct power_supply *find_supply(struct charger *charger,
|
||||
const char *name)
|
||||
{
|
||||
struct listnode *node;
|
||||
struct power_supply *supply;
|
||||
|
||||
list_for_each(node, &charger->supplies) {
|
||||
supply = node_to_item(node, struct power_supply, list);
|
||||
if (!strncmp(name, supply->name, sizeof(supply->name)))
|
||||
return supply;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct power_supply *add_supply(struct charger *charger,
|
||||
const char *name, const char *type,
|
||||
const char *path, bool online)
|
||||
{
|
||||
struct power_supply *supply;
|
||||
|
||||
supply = calloc(1, sizeof(struct power_supply));
|
||||
if (!supply)
|
||||
return NULL;
|
||||
|
||||
strlcpy(supply->name, name, sizeof(supply->name));
|
||||
strlcpy(supply->type, type, sizeof(supply->type));
|
||||
snprintf(supply->cap_path, sizeof(supply->cap_path),
|
||||
"/sys/%s/capacity", path);
|
||||
supply->online = online;
|
||||
list_add_tail(&charger->supplies, &supply->list);
|
||||
charger->num_supplies++;
|
||||
LOGV("... added %s %s %d\n", supply->name, supply->type, online);
|
||||
return supply;
|
||||
}
|
||||
|
||||
static void remove_supply(struct charger *charger, struct power_supply *supply)
|
||||
{
|
||||
if (!supply)
|
||||
return;
|
||||
list_remove(&supply->list);
|
||||
charger->num_supplies--;
|
||||
free(supply);
|
||||
return charger_state.capacity;
|
||||
}
|
||||
|
||||
#ifdef CHARGER_ENABLE_SUSPEND
|
||||
|
@ -361,243 +254,12 @@ static int request_suspend(bool enable)
|
|||
return autosuspend_disable();
|
||||
}
|
||||
#else
|
||||
static int request_suspend(bool enable)
|
||||
static int request_suspend(bool /*enable*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void parse_uevent(const char *msg, struct uevent *uevent)
|
||||
{
|
||||
uevent->action = "";
|
||||
uevent->path = "";
|
||||
uevent->subsystem = "";
|
||||
uevent->ps_name = "";
|
||||
uevent->ps_online = "";
|
||||
uevent->ps_type = "";
|
||||
|
||||
/* currently ignoring SEQNUM */
|
||||
while (*msg) {
|
||||
#ifdef DEBUG_UEVENTS
|
||||
LOGV("uevent str: %s\n", msg);
|
||||
#endif
|
||||
if (!strncmp(msg, "ACTION=", 7)) {
|
||||
msg += 7;
|
||||
uevent->action = msg;
|
||||
} else if (!strncmp(msg, "DEVPATH=", 8)) {
|
||||
msg += 8;
|
||||
uevent->path = msg;
|
||||
} else if (!strncmp(msg, "SUBSYSTEM=", 10)) {
|
||||
msg += 10;
|
||||
uevent->subsystem = msg;
|
||||
} else if (!strncmp(msg, "POWER_SUPPLY_NAME=", 18)) {
|
||||
msg += 18;
|
||||
uevent->ps_name = msg;
|
||||
} else if (!strncmp(msg, "POWER_SUPPLY_ONLINE=", 20)) {
|
||||
msg += 20;
|
||||
uevent->ps_online = msg;
|
||||
} else if (!strncmp(msg, "POWER_SUPPLY_TYPE=", 18)) {
|
||||
msg += 18;
|
||||
uevent->ps_type = msg;
|
||||
}
|
||||
|
||||
/* advance to after the next \0 */
|
||||
while (*msg++)
|
||||
;
|
||||
}
|
||||
|
||||
LOGV("event { '%s', '%s', '%s', '%s', '%s', '%s' }\n",
|
||||
uevent->action, uevent->path, uevent->subsystem,
|
||||
uevent->ps_name, uevent->ps_type, uevent->ps_online);
|
||||
}
|
||||
|
||||
static void process_ps_uevent(struct charger *charger, struct uevent *uevent)
|
||||
{
|
||||
int online;
|
||||
char ps_type[32];
|
||||
struct power_supply *supply = NULL;
|
||||
int i;
|
||||
bool was_online = false;
|
||||
bool battery = false;
|
||||
|
||||
if (uevent->ps_type[0] == '\0') {
|
||||
char *path;
|
||||
int ret;
|
||||
|
||||
if (uevent->path[0] == '\0')
|
||||
return;
|
||||
ret = asprintf(&path, "/sys/%s/type", uevent->path);
|
||||
if (ret <= 0)
|
||||
return;
|
||||
ret = read_file(path, ps_type, sizeof(ps_type));
|
||||
free(path);
|
||||
if (ret < 0)
|
||||
return;
|
||||
} else {
|
||||
strlcpy(ps_type, uevent->ps_type, sizeof(ps_type));
|
||||
}
|
||||
|
||||
if (!strncmp(ps_type, "Battery", 7))
|
||||
battery = true;
|
||||
|
||||
online = atoi(uevent->ps_online);
|
||||
supply = find_supply(charger, uevent->ps_name);
|
||||
if (supply) {
|
||||
was_online = supply->online;
|
||||
supply->online = online;
|
||||
}
|
||||
|
||||
if (!strcmp(uevent->action, "add")) {
|
||||
if (!supply) {
|
||||
supply = add_supply(charger, uevent->ps_name, ps_type, uevent->path,
|
||||
online);
|
||||
if (!supply) {
|
||||
LOGE("cannot add supply '%s' (%s %d)\n", uevent->ps_name,
|
||||
uevent->ps_type, online);
|
||||
return;
|
||||
}
|
||||
/* only pick up the first battery for now */
|
||||
if (battery && !charger->battery)
|
||||
charger->battery = supply;
|
||||
} else {
|
||||
LOGE("supply '%s' already exists..\n", uevent->ps_name);
|
||||
}
|
||||
} else if (!strcmp(uevent->action, "remove")) {
|
||||
if (supply) {
|
||||
if (charger->battery == supply)
|
||||
charger->battery = NULL;
|
||||
remove_supply(charger, supply);
|
||||
supply = NULL;
|
||||
}
|
||||
} else if (!strcmp(uevent->action, "change")) {
|
||||
if (!supply) {
|
||||
LOGE("power supply '%s' not found ('%s' %d)\n",
|
||||
uevent->ps_name, ps_type, online);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
/* allow battery to be managed in the supply list but make it not
|
||||
* contribute to online power supplies. */
|
||||
if (!battery) {
|
||||
if (was_online && !online)
|
||||
charger->num_supplies_online--;
|
||||
else if (supply && !was_online && online)
|
||||
charger->num_supplies_online++;
|
||||
}
|
||||
|
||||
LOGI("power supply %s (%s) %s (action=%s num_online=%d num_supplies=%d)\n",
|
||||
uevent->ps_name, ps_type, battery ? "" : online ? "online" : "offline",
|
||||
uevent->action, charger->num_supplies_online, charger->num_supplies);
|
||||
}
|
||||
|
||||
static void process_uevent(struct charger *charger, struct uevent *uevent)
|
||||
{
|
||||
if (!strcmp(uevent->subsystem, "power_supply"))
|
||||
process_ps_uevent(charger, uevent);
|
||||
}
|
||||
|
||||
#define UEVENT_MSG_LEN 1024
|
||||
static int handle_uevent_fd(struct charger *charger, int fd)
|
||||
{
|
||||
char msg[UEVENT_MSG_LEN+2];
|
||||
int n;
|
||||
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
while (true) {
|
||||
struct uevent uevent;
|
||||
|
||||
n = uevent_kernel_multicast_recv(fd, msg, UEVENT_MSG_LEN);
|
||||
if (n <= 0)
|
||||
break;
|
||||
if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
|
||||
continue;
|
||||
|
||||
msg[n] = '\0';
|
||||
msg[n+1] = '\0';
|
||||
|
||||
parse_uevent(msg, &uevent);
|
||||
process_uevent(charger, &uevent);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uevent_callback(int fd, short revents, void *data)
|
||||
{
|
||||
struct charger *charger = data;
|
||||
|
||||
if (!(revents & POLLIN))
|
||||
return -1;
|
||||
return handle_uevent_fd(charger, fd);
|
||||
}
|
||||
|
||||
/* force the kernel to regenerate the change events for the existing
|
||||
* devices, if valid */
|
||||
static void do_coldboot(struct charger *charger, DIR *d, const char *event,
|
||||
bool follow_links, int max_depth)
|
||||
{
|
||||
struct dirent *de;
|
||||
int dfd, fd;
|
||||
|
||||
dfd = dirfd(d);
|
||||
|
||||
fd = openat(dfd, "uevent", O_WRONLY);
|
||||
if (fd >= 0) {
|
||||
write(fd, event, strlen(event));
|
||||
close(fd);
|
||||
handle_uevent_fd(charger, charger->uevent_fd);
|
||||
}
|
||||
|
||||
while ((de = readdir(d)) && max_depth > 0) {
|
||||
DIR *d2;
|
||||
|
||||
LOGV("looking at '%s'\n", de->d_name);
|
||||
|
||||
if ((de->d_type != DT_DIR && !(de->d_type == DT_LNK && follow_links)) ||
|
||||
de->d_name[0] == '.') {
|
||||
LOGV("skipping '%s' type %d (depth=%d follow=%d)\n",
|
||||
de->d_name, de->d_type, max_depth, follow_links);
|
||||
continue;
|
||||
}
|
||||
LOGV("can descend into '%s'\n", de->d_name);
|
||||
|
||||
fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
|
||||
if (fd < 0) {
|
||||
LOGE("cannot openat %d '%s' (%d: %s)\n", dfd, de->d_name,
|
||||
errno, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
d2 = fdopendir(fd);
|
||||
if (d2 == 0)
|
||||
close(fd);
|
||||
else {
|
||||
LOGV("opened '%s'\n", de->d_name);
|
||||
do_coldboot(charger, d2, event, follow_links, max_depth - 1);
|
||||
closedir(d2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void coldboot(struct charger *charger, const char *path,
|
||||
const char *event)
|
||||
{
|
||||
char str[256];
|
||||
|
||||
LOGV("doing coldboot '%s' in '%s'\n", event, path);
|
||||
DIR *d = opendir(path);
|
||||
if (d) {
|
||||
snprintf(str, sizeof(str), "%s\n", event);
|
||||
do_coldboot(charger, d, str, true, 1);
|
||||
closedir(d);
|
||||
}
|
||||
}
|
||||
|
||||
static int draw_text(const char *str, int x, int y)
|
||||
{
|
||||
int str_len_px = gr_measure(str);
|
||||
|
@ -617,7 +279,7 @@ static void android_green(void)
|
|||
}
|
||||
|
||||
/* returns the last y-offset of where the surface ends */
|
||||
static int draw_surface_centered(struct charger *charger, gr_surface surface)
|
||||
static int draw_surface_centered(struct charger* /*charger*/, gr_surface surface)
|
||||
{
|
||||
int w;
|
||||
int h;
|
||||
|
@ -694,13 +356,34 @@ static void update_screen_state(struct charger *charger, int64_t now)
|
|||
if (!batt_anim->run || now < charger->next_screen_transition)
|
||||
return;
|
||||
|
||||
if (!minui_inited) {
|
||||
int batt_cap = get_battery_capacity();
|
||||
|
||||
if (batt_cap < SCREEN_ON_BATTERY_THRESH) {
|
||||
LOGV("[%" PRId64 "] level %d, leave screen off\n", now, batt_cap);
|
||||
batt_anim->run = false;
|
||||
charger->next_screen_transition = -1;
|
||||
if (charger->charger_connected)
|
||||
request_suspend(true);
|
||||
return;
|
||||
}
|
||||
|
||||
gr_init();
|
||||
gr_font_size(&char_width, &char_height);
|
||||
|
||||
#ifndef CHARGER_DISABLE_INIT_BLANK
|
||||
gr_fb_blank(true);
|
||||
#endif
|
||||
minui_inited = true;
|
||||
}
|
||||
|
||||
/* animation is over, blank screen and leave */
|
||||
if (batt_anim->cur_cycle == batt_anim->num_cycles) {
|
||||
reset_animation(batt_anim);
|
||||
charger->next_screen_transition = -1;
|
||||
gr_fb_blank(true);
|
||||
LOGV("[%lld] animation done\n", now);
|
||||
if (charger->num_supplies_online > 0)
|
||||
LOGV("[%" PRId64 "] animation done\n", now);
|
||||
if (charger->charger_connected)
|
||||
request_suspend(true);
|
||||
return;
|
||||
}
|
||||
|
@ -712,8 +395,8 @@ static void update_screen_state(struct charger *charger, int64_t now)
|
|||
int batt_cap;
|
||||
int ret;
|
||||
|
||||
LOGV("[%lld] animation starting\n", now);
|
||||
batt_cap = get_battery_capacity(charger);
|
||||
LOGV("[%" PRId64 "] animation starting\n", now);
|
||||
batt_cap = get_battery_capacity();
|
||||
if (batt_cap >= 0 && batt_anim->num_frames != 0) {
|
||||
int i;
|
||||
|
||||
|
@ -742,7 +425,7 @@ static void update_screen_state(struct charger *charger, int64_t now)
|
|||
* the cycle counter and exit
|
||||
*/
|
||||
if (batt_anim->num_frames == 0 || batt_anim->capacity < 0) {
|
||||
LOGV("[%lld] animation missing or unknown battery status\n", now);
|
||||
LOGV("[%" PRId64 "] animation missing or unknown battery status\n", now);
|
||||
charger->next_screen_transition = now + BATTERY_UNKNOWN_TIME;
|
||||
batt_anim->cur_cycle++;
|
||||
return;
|
||||
|
@ -751,30 +434,39 @@ static void update_screen_state(struct charger *charger, int64_t now)
|
|||
/* schedule next screen transition */
|
||||
charger->next_screen_transition = now + disp_time;
|
||||
|
||||
/* advance frame cntr to the next valid frame
|
||||
/* advance frame cntr to the next valid frame only if we are charging
|
||||
* if necessary, advance cycle cntr, and reset frame cntr
|
||||
*/
|
||||
batt_anim->cur_frame++;
|
||||
|
||||
/* if the frame is used for level-only, that is only show it when it's
|
||||
* the current level, skip it during the animation.
|
||||
*/
|
||||
while (batt_anim->cur_frame < batt_anim->num_frames &&
|
||||
batt_anim->frames[batt_anim->cur_frame].level_only)
|
||||
if (charger->charger_connected) {
|
||||
batt_anim->cur_frame++;
|
||||
if (batt_anim->cur_frame >= batt_anim->num_frames) {
|
||||
batt_anim->cur_cycle++;
|
||||
batt_anim->cur_frame = 0;
|
||||
|
||||
/* don't reset the cycle counter, since we use that as a signal
|
||||
* in a test above to check if animation is over
|
||||
/* if the frame is used for level-only, that is only show it when it's
|
||||
* the current level, skip it during the animation.
|
||||
*/
|
||||
while (batt_anim->cur_frame < batt_anim->num_frames &&
|
||||
batt_anim->frames[batt_anim->cur_frame].level_only)
|
||||
batt_anim->cur_frame++;
|
||||
if (batt_anim->cur_frame >= batt_anim->num_frames) {
|
||||
batt_anim->cur_cycle++;
|
||||
batt_anim->cur_frame = 0;
|
||||
|
||||
/* don't reset the cycle counter, since we use that as a signal
|
||||
* in a test above to check if animation is over
|
||||
*/
|
||||
}
|
||||
} else {
|
||||
/* Stop animating if we're not charging.
|
||||
* If we stop it immediately instead of going through this loop, then
|
||||
* the animation would stop somewhere in the middle.
|
||||
*/
|
||||
batt_anim->cur_frame = 0;
|
||||
batt_anim->cur_cycle++;
|
||||
}
|
||||
}
|
||||
|
||||
static int set_key_callback(int code, int value, void *data)
|
||||
{
|
||||
struct charger *charger = data;
|
||||
struct charger *charger = (struct charger *)data;
|
||||
int64_t now = curr_time_ms();
|
||||
int down = !!value;
|
||||
|
||||
|
@ -792,13 +484,13 @@ static int set_key_callback(int code, int value, void *data)
|
|||
charger->keys[code].down = down;
|
||||
charger->keys[code].pending = true;
|
||||
if (down) {
|
||||
LOGV("[%lld] key[%d] down\n", now, code);
|
||||
LOGV("[%" PRId64 "] key[%d] down\n", now, code);
|
||||
} else {
|
||||
int64_t duration = now - charger->keys[code].timestamp;
|
||||
int64_t secs = duration / 1000;
|
||||
int64_t msecs = duration - secs * 1000;
|
||||
LOGV("[%lld] key[%d] up (was down for %lld.%lldsec)\n", now,
|
||||
code, secs, msecs);
|
||||
LOGV("[%" PRId64 "] key[%d] up (was down for %" PRId64 ".%" PRId64 "sec)\n",
|
||||
now, code, secs, msecs);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -835,10 +527,10 @@ static void process_key(struct charger *charger, int code, int64_t now)
|
|||
all devices. Check the property and continue booting or reboot
|
||||
accordingly. */
|
||||
if (property_get_bool("ro.enable_boot_charger_mode", false)) {
|
||||
LOGI("[%lld] booting from charger mode\n", now);
|
||||
LOGI("[%" PRId64 "] booting from charger mode\n", now);
|
||||
property_set("sys.boot_from_charger_mode", "1");
|
||||
} else {
|
||||
LOGI("[%lld] rebooting\n", now);
|
||||
LOGI("[%" PRId64 "] rebooting\n", now);
|
||||
android_reboot(ANDROID_RB_RESTART, 0, 0);
|
||||
}
|
||||
} else {
|
||||
|
@ -869,14 +561,17 @@ static void handle_input_state(struct charger *charger, int64_t now)
|
|||
|
||||
static void handle_power_supply_state(struct charger *charger, int64_t now)
|
||||
{
|
||||
if (charger->num_supplies_online == 0) {
|
||||
if (!charger->have_battery_state)
|
||||
return;
|
||||
|
||||
if (!charger->charger_connected) {
|
||||
request_suspend(false);
|
||||
if (charger->next_pwr_check == -1) {
|
||||
charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME;
|
||||
LOGI("[%lld] device unplugged: shutting down in %lld (@ %lld)\n",
|
||||
now, UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check);
|
||||
LOGI("[%" PRId64 "] device unplugged: shutting down in %" PRId64 " (@ %" PRId64 ")\n",
|
||||
now, (int64_t)UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check);
|
||||
} else if (now >= charger->next_pwr_check) {
|
||||
LOGI("[%lld] shutting down\n", now);
|
||||
LOGI("[%" PRId64 "] shutting down\n", now);
|
||||
android_reboot(ANDROID_RB_POWEROFF, 0, 0);
|
||||
} else {
|
||||
/* otherwise we already have a shutdown timer scheduled */
|
||||
|
@ -884,21 +579,56 @@ static void handle_power_supply_state(struct charger *charger, int64_t now)
|
|||
} else {
|
||||
/* online supply present, reset shutdown timer if set */
|
||||
if (charger->next_pwr_check != -1) {
|
||||
LOGI("[%lld] device plugged in: shutdown cancelled\n", now);
|
||||
LOGI("[%" PRId64 "] device plugged in: shutdown cancelled\n", now);
|
||||
kick_animation(charger->batt_anim);
|
||||
}
|
||||
charger->next_pwr_check = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void wait_next_event(struct charger *charger, int64_t now)
|
||||
void healthd_mode_charger_heartbeat()
|
||||
{
|
||||
struct charger *charger = &charger_state;
|
||||
int64_t now = curr_time_ms();
|
||||
int ret;
|
||||
|
||||
handle_input_state(charger, now);
|
||||
handle_power_supply_state(charger, now);
|
||||
|
||||
/* do screen update last in case any of the above want to start
|
||||
* screen transitions (animations, etc)
|
||||
*/
|
||||
update_screen_state(charger, now);
|
||||
}
|
||||
|
||||
void healthd_mode_charger_battery_update(
|
||||
struct android::BatteryProperties *props)
|
||||
{
|
||||
struct charger *charger = &charger_state;
|
||||
|
||||
charger->charger_connected =
|
||||
props->chargerAcOnline || props->chargerUsbOnline ||
|
||||
props->chargerWirelessOnline;
|
||||
charger->capacity = props->batteryLevel;
|
||||
|
||||
if (!charger->have_battery_state) {
|
||||
charger->have_battery_state = true;
|
||||
charger->next_screen_transition = curr_time_ms() - 1;
|
||||
reset_animation(charger->batt_anim);
|
||||
kick_animation(charger->batt_anim);
|
||||
}
|
||||
}
|
||||
|
||||
int healthd_mode_charger_preparetowait(void)
|
||||
{
|
||||
struct charger *charger = &charger_state;
|
||||
int64_t now = curr_time_ms();
|
||||
int64_t next_event = INT64_MAX;
|
||||
int64_t timeout;
|
||||
struct input_event ev;
|
||||
int ret;
|
||||
|
||||
LOGV("[%lld] next screen: %lld next key: %lld next pwr: %lld\n", now,
|
||||
LOGV("[%" PRId64 "] next screen: %" PRId64 " next key: %" PRId64 " next pwr: %" PRId64 "\n", now,
|
||||
charger->next_screen_transition, charger->next_key_check,
|
||||
charger->next_pwr_check);
|
||||
|
||||
|
@ -913,74 +643,48 @@ static void wait_next_event(struct charger *charger, int64_t now)
|
|||
timeout = max(0, next_event - now);
|
||||
else
|
||||
timeout = -1;
|
||||
LOGV("[%lld] blocking (%lld)\n", now, timeout);
|
||||
ret = ev_wait((int)timeout);
|
||||
if (!ret)
|
||||
ev_dispatch();
|
||||
|
||||
return (int)timeout;
|
||||
}
|
||||
|
||||
static int input_callback(int fd, short revents, void *data)
|
||||
static int input_callback(int fd, unsigned int epevents, void *data)
|
||||
{
|
||||
struct charger *charger = data;
|
||||
struct charger *charger = (struct charger *)data;
|
||||
struct input_event ev;
|
||||
int ret;
|
||||
|
||||
ret = ev_get_input(fd, revents, &ev);
|
||||
ret = ev_get_input(fd, epevents, &ev);
|
||||
if (ret)
|
||||
return -1;
|
||||
update_input_state(charger, &ev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void event_loop(struct charger *charger)
|
||||
static void charger_event_handler(uint32_t /*epevents*/)
|
||||
{
|
||||
int ret;
|
||||
|
||||
while (true) {
|
||||
int64_t now = curr_time_ms();
|
||||
|
||||
LOGV("[%lld] event_loop()\n", now);
|
||||
handle_input_state(charger, now);
|
||||
handle_power_supply_state(charger, now);
|
||||
|
||||
/* do screen update last in case any of the above want to start
|
||||
* screen transitions (animations, etc)
|
||||
*/
|
||||
update_screen_state(charger, now);
|
||||
|
||||
wait_next_event(charger, now);
|
||||
}
|
||||
ret = ev_wait(-1);
|
||||
if (!ret)
|
||||
ev_dispatch();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
void healthd_mode_charger_init(struct healthd_config* /*config*/)
|
||||
{
|
||||
int ret;
|
||||
struct charger *charger = &charger_state;
|
||||
int64_t now = curr_time_ms() - 1;
|
||||
int fd;
|
||||
int i;
|
||||
|
||||
list_init(&charger->supplies);
|
||||
|
||||
klog_init();
|
||||
klog_set_level(CHARGER_KLOG_LEVEL);
|
||||
int epollfd;
|
||||
|
||||
dump_last_kmsg();
|
||||
|
||||
LOGI("--------------- STARTING CHARGER MODE ---------------\n");
|
||||
|
||||
gr_init();
|
||||
gr_font_size(&char_width, &char_height);
|
||||
|
||||
ev_init(input_callback, charger);
|
||||
|
||||
fd = uevent_open_socket(64*1024, true);
|
||||
if (fd >= 0) {
|
||||
fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
ev_add_fd(fd, uevent_callback, charger);
|
||||
ret = ev_init(input_callback, charger);
|
||||
if (!ret) {
|
||||
epollfd = ev_get_epollfd();
|
||||
healthd_register_event(epollfd, charger_event_handler);
|
||||
}
|
||||
charger->uevent_fd = fd;
|
||||
coldboot(charger, "/sys/class/power_supply", "add");
|
||||
|
||||
ret = res_create_display_surface("charger/battery_fail", &charger->surf_unknown);
|
||||
if (ret < 0) {
|
||||
|
@ -1010,17 +714,7 @@ int main(int argc, char **argv)
|
|||
|
||||
ev_sync_key_state(set_key_callback, charger);
|
||||
|
||||
#ifndef CHARGER_DISABLE_INIT_BLANK
|
||||
gr_fb_blank(true);
|
||||
#endif
|
||||
|
||||
charger->next_screen_transition = now - 1;
|
||||
charger->next_screen_transition = -1;
|
||||
charger->next_key_check = -1;
|
||||
charger->next_pwr_check = -1;
|
||||
reset_animation(charger->batt_anim);
|
||||
kick_animation(charger->batt_anim);
|
||||
|
||||
event_loop(charger);
|
||||
|
||||
return 0;
|
||||
}
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 463 B After Width: | Height: | Size: 463 B |
|
@ -25,7 +25,7 @@ __BEGIN_DECLS
|
|||
void klog_init(void);
|
||||
int klog_get_level(void);
|
||||
void klog_set_level(int level);
|
||||
void klog_close(void);
|
||||
/* TODO: void klog_close(void); - and make klog_fd users thread safe. */
|
||||
void klog_write(int level, const char *fmt, ...)
|
||||
__attribute__ ((format(printf, 2, 3)));
|
||||
void klog_vwrite(int level, const char *fmt, va_list ap);
|
||||
|
|
|
@ -34,6 +34,12 @@ int str_parms_add_int(struct str_parms *str_parms, const char *key, int value);
|
|||
int str_parms_add_float(struct str_parms *str_parms, const char *key,
|
||||
float value);
|
||||
|
||||
// Returns non-zero if the str_parms contains the specified key.
|
||||
int str_parms_has_key(struct str_parms *str_parms, const char *key);
|
||||
|
||||
// Gets value associated with the specified key (if present), placing it in the buffer
|
||||
// pointed to by the out_val parameter. Returns the length of the returned string value.
|
||||
// If 'key' isn't in the parms, then return -ENOENT (-2) and leave 'out_val' untouched.
|
||||
int str_parms_get_str(struct str_parms *str_parms, const char *key,
|
||||
char *out_val, int len);
|
||||
int str_parms_get_int(struct str_parms *str_parms, const char *key,
|
||||
|
|
|
@ -70,7 +70,8 @@ __BEGIN_DECLS
|
|||
#define ATRACE_TAG_DALVIK (1<<14)
|
||||
#define ATRACE_TAG_RS (1<<15)
|
||||
#define ATRACE_TAG_BIONIC (1<<16)
|
||||
#define ATRACE_TAG_LAST ATRACE_TAG_BIONIC
|
||||
#define ATRACE_TAG_POWER (1<<17)
|
||||
#define ATRACE_TAG_LAST ATRACE_TAG_POWER
|
||||
|
||||
// Reserved for initialization.
|
||||
#define ATRACE_TAG_NOT_READY (1LL<<63)
|
||||
|
|
|
@ -36,6 +36,7 @@ extern int ifc_disable(const char *ifname);
|
|||
|
||||
#define RESET_IPV4_ADDRESSES 0x01
|
||||
#define RESET_IPV6_ADDRESSES 0x02
|
||||
#define RESET_IGNORE_INTERFACE_ADDRESS 0x04
|
||||
#define RESET_ALL_ADDRESSES (RESET_IPV4_ADDRESSES | RESET_IPV6_ADDRESSES)
|
||||
extern int ifc_reset_connections(const char *ifname, const int reset_mask);
|
||||
|
||||
|
@ -49,19 +50,8 @@ extern int ifc_set_prefixLength(const char *name, int prefixLength);
|
|||
extern int ifc_set_hwaddr(const char *name, const void *ptr);
|
||||
extern int ifc_clear_addresses(const char *name);
|
||||
|
||||
/* This function is deprecated. Use ifc_add_route instead. */
|
||||
extern int ifc_add_host_route(const char *name, in_addr_t addr);
|
||||
extern int ifc_remove_host_routes(const char *name);
|
||||
extern int ifc_get_default_route(const char *ifname);
|
||||
/* This function is deprecated. Use ifc_add_route instead */
|
||||
extern int ifc_set_default_route(const char *ifname, in_addr_t gateway);
|
||||
/* This function is deprecated. Use ifc_add_route instead */
|
||||
extern int ifc_create_default_route(const char *name, in_addr_t addr);
|
||||
extern int ifc_remove_default_route(const char *ifname);
|
||||
extern int ifc_add_route(const char *name, const char *addr, int prefix_length,
|
||||
const char *gw);
|
||||
extern int ifc_remove_route(const char *ifname, const char *dst,
|
||||
int prefix_length, const char *gw);
|
||||
extern int ifc_get_info(const char *name, in_addr_t *addr, int *prefixLength,
|
||||
unsigned *flags);
|
||||
|
||||
|
|
|
@ -254,6 +254,8 @@ static const struct fs_path_config android_files[] = {
|
|||
/* the following files have enhanced capabilities and ARE included in user builds. */
|
||||
{ 00750, AID_ROOT, AID_SHELL, (1 << CAP_SETUID) | (1 << CAP_SETGID), "system/bin/run-as" },
|
||||
|
||||
{ 00750, AID_ROOT, AID_ROOT, 0, "system/bin/uncrypt" },
|
||||
{ 00750, AID_ROOT, AID_ROOT, 0, "system/bin/install-recovery.sh" },
|
||||
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" },
|
||||
{ 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" },
|
||||
{ 00755, AID_ROOT, AID_ROOT, 0, "system/lib64/valgrind/*" },
|
||||
|
@ -263,7 +265,6 @@ static const struct fs_path_config android_files[] = {
|
|||
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin/*" },
|
||||
{ 00755, AID_ROOT, AID_ROOT, 0, "bin/*" },
|
||||
{ 00750, AID_ROOT, AID_SHELL, 0, "init*" },
|
||||
{ 00750, AID_ROOT, AID_SHELL, 0, "charger*" },
|
||||
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin/fs_mgr" },
|
||||
{ 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
|
||||
{ 00644, AID_ROOT, AID_ROOT, 0, 0 },
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -44,6 +44,7 @@ typedef enum {
|
|||
AUDIO_POLICY_FORCE_DIGITAL_DOCK,
|
||||
AUDIO_POLICY_FORCE_NO_BT_A2DP, /* A2DP sink is not preferred to speaker or wired HS */
|
||||
AUDIO_POLICY_FORCE_SYSTEM_ENFORCED,
|
||||
AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED,
|
||||
|
||||
AUDIO_POLICY_FORCE_CFG_CNT,
|
||||
AUDIO_POLICY_FORCE_CFG_MAX = AUDIO_POLICY_FORCE_CFG_CNT - 1,
|
||||
|
@ -58,6 +59,7 @@ typedef enum {
|
|||
AUDIO_POLICY_FORCE_FOR_RECORD,
|
||||
AUDIO_POLICY_FORCE_FOR_DOCK,
|
||||
AUDIO_POLICY_FORCE_FOR_SYSTEM,
|
||||
AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO,
|
||||
|
||||
AUDIO_POLICY_FORCE_USE_CNT,
|
||||
AUDIO_POLICY_FORCE_USE_MAX = AUDIO_POLICY_FORCE_USE_CNT - 1,
|
||||
|
|
|
@ -165,24 +165,104 @@ enum {
|
|||
/*
|
||||
* Android RAW sensor format:
|
||||
*
|
||||
* This format is exposed outside of the HAL to applications.
|
||||
* This format is exposed outside of the camera HAL to applications.
|
||||
*
|
||||
* RAW_SENSOR is a single-channel 16-bit format, typically representing raw
|
||||
* Bayer-pattern images from an image sensor, with minimal processing.
|
||||
* RAW_SENSOR is a single-channel, 16-bit, little endian format, typically
|
||||
* representing raw Bayer-pattern images from an image sensor, with minimal
|
||||
* processing.
|
||||
*
|
||||
* The exact pixel layout of the data in the buffer is sensor-dependent, and
|
||||
* needs to be queried from the camera device.
|
||||
*
|
||||
* Generally, not all 16 bits are used; more common values are 10 or 12
|
||||
* bits. All parameters to interpret the raw data (black and white points,
|
||||
* bits. If not all bits are used, the lower-order bits are filled first.
|
||||
* All parameters to interpret the raw data (black and white points,
|
||||
* color space, etc) must be queried from the camera device.
|
||||
*
|
||||
* This format assumes
|
||||
* - an even width
|
||||
* - an even height
|
||||
* - a horizontal stride multiple of 16 pixels (32 bytes).
|
||||
* - a horizontal stride multiple of 16 pixels
|
||||
* - a vertical stride equal to the height
|
||||
* - strides are specified in pixels, not in bytes
|
||||
*
|
||||
* size = stride * height * 2
|
||||
*
|
||||
* This format must be accepted by the gralloc module when used with the
|
||||
* following usage flags:
|
||||
* - GRALLOC_USAGE_HW_CAMERA_*
|
||||
* - GRALLOC_USAGE_SW_*
|
||||
* - GRALLOC_USAGE_RENDERSCRIPT
|
||||
*/
|
||||
HAL_PIXEL_FORMAT_RAW_SENSOR = 0x20,
|
||||
HAL_PIXEL_FORMAT_RAW16 = 0x20,
|
||||
HAL_PIXEL_FORMAT_RAW_SENSOR = 0x20, // TODO(rubenbrunk): Remove RAW_SENSOR.
|
||||
|
||||
/*
|
||||
* Android RAW10 format:
|
||||
*
|
||||
* This format is exposed outside of the camera HAL to applications.
|
||||
*
|
||||
* RAW10 is a single-channel, 10-bit per pixel, densely packed in each row,
|
||||
* unprocessed format, usually representing raw Bayer-pattern images coming from
|
||||
* an image sensor.
|
||||
*
|
||||
* In an image buffer with this format, starting from the first pixel of each
|
||||
* row, each 4 consecutive pixels are packed into 5 bytes (40 bits). Each one
|
||||
* of the first 4 bytes contains the top 8 bits of each pixel, The fifth byte
|
||||
* contains the 2 least significant bits of the 4 pixels, the exact layout data
|
||||
* for each 4 consecutive pixels is illustrated below (Pi[j] stands for the jth
|
||||
* bit of the ith pixel):
|
||||
*
|
||||
* bit 7 bit 0
|
||||
* =====|=====|=====|=====|=====|=====|=====|=====|
|
||||
* Byte 0: |P0[9]|P0[8]|P0[7]|P0[6]|P0[5]|P0[4]|P0[3]|P0[2]|
|
||||
* |-----|-----|-----|-----|-----|-----|-----|-----|
|
||||
* Byte 1: |P1[9]|P1[8]|P1[7]|P1[6]|P1[5]|P1[4]|P1[3]|P1[2]|
|
||||
* |-----|-----|-----|-----|-----|-----|-----|-----|
|
||||
* Byte 2: |P2[9]|P2[8]|P2[7]|P2[6]|P2[5]|P2[4]|P2[3]|P2[2]|
|
||||
* |-----|-----|-----|-----|-----|-----|-----|-----|
|
||||
* Byte 3: |P3[9]|P3[8]|P3[7]|P3[6]|P3[5]|P3[4]|P3[3]|P3[2]|
|
||||
* |-----|-----|-----|-----|-----|-----|-----|-----|
|
||||
* Byte 4: |P3[1]|P3[0]|P2[1]|P2[0]|P1[1]|P1[0]|P0[1]|P0[0]|
|
||||
* ===============================================
|
||||
*
|
||||
* This format assumes
|
||||
* - a width multiple of 4 pixels
|
||||
* - an even height
|
||||
* - a vertical stride equal to the height
|
||||
* - strides are specified in bytes, not in pixels
|
||||
*
|
||||
* size = stride * height
|
||||
*
|
||||
* When stride is equal to width * (10 / 8), there will be no padding bytes at
|
||||
* the end of each row, the entire image data is densely packed. When stride is
|
||||
* larger than width * (10 / 8), padding bytes will be present at the end of each
|
||||
* row (including the last row).
|
||||
*
|
||||
* This format must be accepted by the gralloc module when used with the
|
||||
* following usage flags:
|
||||
* - GRALLOC_USAGE_HW_CAMERA_*
|
||||
* - GRALLOC_USAGE_SW_*
|
||||
* - GRALLOC_USAGE_RENDERSCRIPT
|
||||
*/
|
||||
HAL_PIXEL_FORMAT_RAW10 = 0x25,
|
||||
|
||||
/*
|
||||
* Android opaque RAW format:
|
||||
*
|
||||
* This format is exposed outside of the camera HAL to applications.
|
||||
*
|
||||
* RAW_OPAQUE is a format for unprocessed raw image buffers coming from an
|
||||
* image sensor. The actual structure of buffers of this format is
|
||||
* implementation-dependent.
|
||||
*
|
||||
* This format must be accepted by the gralloc module when used with the
|
||||
* following usage flags:
|
||||
* - GRALLOC_USAGE_HW_CAMERA_*
|
||||
* - GRALLOC_USAGE_SW_*
|
||||
* - GRALLOC_USAGE_RENDERSCRIPT
|
||||
*/
|
||||
HAL_PIXEL_FORMAT_RAW_OPAQUE = 0x24,
|
||||
|
||||
/*
|
||||
* Android binary blob graphics buffer format:
|
||||
|
@ -297,6 +377,136 @@ enum {
|
|||
HAL_TRANSFORM_RESERVED = 0x08,
|
||||
};
|
||||
|
||||
/**
|
||||
* Colorspace Definitions
|
||||
* ======================
|
||||
*
|
||||
* Colorspace is the definition of how pixel values should be interpreted.
|
||||
* It includes primaries (including white point) and the transfer
|
||||
* characteristic function, which describes both gamma curve and numeric
|
||||
* range (within the bit depth).
|
||||
*/
|
||||
|
||||
enum {
|
||||
/*
|
||||
* Arbitrary colorspace with manually defined characteristics.
|
||||
* Colorspace definition must be communicated separately.
|
||||
*
|
||||
* This is used when specifying primaries, transfer characteristics,
|
||||
* etc. separately.
|
||||
*
|
||||
* A typical use case is in video encoding parameters (e.g. for H.264),
|
||||
* where a colorspace can have separately defined primaries, transfer
|
||||
* characteristics, etc.
|
||||
*/
|
||||
HAL_COLORSPACE_ARBITRARY = 0x1,
|
||||
|
||||
/*
|
||||
* YCbCr Colorspaces
|
||||
* -----------------
|
||||
*
|
||||
* Primaries are given using (x,y) coordinates in the CIE 1931 definition
|
||||
* of x and y specified by ISO 11664-1.
|
||||
*
|
||||
* Transfer characteristics are the opto-electronic transfer characteristic
|
||||
* at the source as a function of linear optical intensity (luminance).
|
||||
*/
|
||||
|
||||
/*
|
||||
* JPEG File Interchange Format (JFIF)
|
||||
*
|
||||
* Same model as BT.601-625, but all values (Y, Cb, Cr) range from 0 to 255
|
||||
*
|
||||
* Transfer characteristic curve:
|
||||
* E = 1.099 * L ^ 0.45 - 0.099, 1.00 >= L >= 0.018
|
||||
* E = 4.500 L, 0.018 > L >= 0
|
||||
* L - luminance of image 0 <= L <= 1 for conventional colorimetry
|
||||
* E - corresponding electrical signal
|
||||
*
|
||||
* Primaries: x y
|
||||
* green 0.290 0.600
|
||||
* blue 0.150 0.060
|
||||
* red 0.640 0.330
|
||||
* white (D65) 0.3127 0.3290
|
||||
*/
|
||||
HAL_COLORSPACE_JFIF = 0x101,
|
||||
|
||||
/*
|
||||
* ITU-R Recommendation 601 (BT.601) - 625-line
|
||||
*
|
||||
* Standard-definition television, 625 Lines (PAL)
|
||||
*
|
||||
* For 8-bit-depth formats:
|
||||
* Luma (Y) samples should range from 16 to 235, inclusive
|
||||
* Chroma (Cb, Cr) samples should range from 16 to 240, inclusive
|
||||
*
|
||||
* For 10-bit-depth formats:
|
||||
* Luma (Y) samples should range from 64 to 940, inclusive
|
||||
* Chroma (Cb, Cr) samples should range from 64 to 960, inclusive
|
||||
*
|
||||
* Transfer characteristic curve:
|
||||
* E = 1.099 * L ^ 0.45 - 0.099, 1.00 >= L >= 0.018
|
||||
* E = 4.500 L, 0.018 > L >= 0
|
||||
* L - luminance of image 0 <= L <= 1 for conventional colorimetry
|
||||
* E - corresponding electrical signal
|
||||
*
|
||||
* Primaries: x y
|
||||
* green 0.290 0.600
|
||||
* blue 0.150 0.060
|
||||
* red 0.640 0.330
|
||||
* white (D65) 0.3127 0.3290
|
||||
*/
|
||||
HAL_COLORSPACE_BT601_625 = 0x102,
|
||||
|
||||
/*
|
||||
* ITU-R Recommendation 601 (BT.601) - 525-line
|
||||
*
|
||||
* Standard-definition television, 525 Lines (NTSC)
|
||||
*
|
||||
* For 8-bit-depth formats:
|
||||
* Luma (Y) samples should range from 16 to 235, inclusive
|
||||
* Chroma (Cb, Cr) samples should range from 16 to 240, inclusive
|
||||
*
|
||||
* For 10-bit-depth formats:
|
||||
* Luma (Y) samples should range from 64 to 940, inclusive
|
||||
* Chroma (Cb, Cr) samples should range from 64 to 960, inclusive
|
||||
*
|
||||
* Transfer characteristic curve:
|
||||
* E = 1.099 * L ^ 0.45 - 0.099, 1.00 >= L >= 0.018
|
||||
* E = 4.500 L, 0.018 > L >= 0
|
||||
* L - luminance of image 0 <= L <= 1 for conventional colorimetry
|
||||
* E - corresponding electrical signal
|
||||
*
|
||||
* Primaries: x y
|
||||
* green 0.310 0.595
|
||||
* blue 0.155 0.070
|
||||
* red 0.630 0.340
|
||||
* white (D65) 0.3127 0.3290
|
||||
*/
|
||||
HAL_COLORSPACE_BT601_525 = 0x103,
|
||||
|
||||
/*
|
||||
* ITU-R Recommendation 709 (BT.709)
|
||||
*
|
||||
* High-definition television
|
||||
*
|
||||
* For 8-bit-depth formats:
|
||||
* Luma (Y) samples should range from 16 to 235, inclusive
|
||||
* Chroma (Cb, Cr) samples should range from 16 to 240, inclusive
|
||||
*
|
||||
* For 10-bit-depth formats:
|
||||
* Luma (Y) samples should range from 64 to 940, inclusive
|
||||
* Chroma (Cb, Cr) samples should range from 64 to 960, inclusive
|
||||
*
|
||||
* Primaries: x y
|
||||
* green 0.300 0.600
|
||||
* blue 0.150 0.060
|
||||
* red 0.640 0.330
|
||||
* white (D65) 0.3127 0.3290
|
||||
*/
|
||||
HAL_COLORSPACE_BT709 = 0x104,
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
* Copyright (C) 2014 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 ANDROID_SOUND_TRIGGER_H
|
||||
#define ANDROID_SOUND_TRIGGER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <system/audio.h>
|
||||
|
||||
#define SOUND_TRIGGER_MAX_STRING_LEN 64 /* max length of strings in properties or
|
||||
descriptor structs */
|
||||
#define SOUND_TRIGGER_MAX_LOCALE_LEN 6 /* max length of locale string. e.g en_US */
|
||||
#define SOUND_TRIGGER_MAX_USERS 10 /* max number of concurrent users */
|
||||
#define SOUND_TRIGGER_MAX_PHRASES 10 /* max number of concurrent phrases */
|
||||
|
||||
typedef enum {
|
||||
SOUND_TRIGGER_STATE_NO_INIT = -1, /* The sound trigger service is not initialized */
|
||||
SOUND_TRIGGER_STATE_ENABLED = 0, /* The sound trigger service is enabled */
|
||||
SOUND_TRIGGER_STATE_DISABLED = 1 /* The sound trigger service is disabled */
|
||||
} sound_trigger_service_state_t;
|
||||
|
||||
#define RECOGNITION_MODE_VOICE_TRIGGER 0x1 /* simple voice trigger */
|
||||
#define RECOGNITION_MODE_USER_IDENTIFICATION 0x2 /* trigger only if one user in model identified */
|
||||
#define RECOGNITION_MODE_USER_AUTHENTICATION 0x4 /* trigger only if one user in mode
|
||||
authenticated */
|
||||
#define RECOGNITION_STATUS_SUCCESS 0
|
||||
#define RECOGNITION_STATUS_ABORT 1
|
||||
#define RECOGNITION_STATUS_FAILURE 2
|
||||
|
||||
#define SOUND_MODEL_STATUS_UPDATED 0
|
||||
|
||||
typedef enum {
|
||||
SOUND_MODEL_TYPE_UNKNOWN = -1, /* use for unspecified sound model type */
|
||||
SOUND_MODEL_TYPE_KEYPHRASE = 0 /* use for key phrase sound models */
|
||||
} sound_trigger_sound_model_type_t;
|
||||
|
||||
typedef struct sound_trigger_uuid_s {
|
||||
unsigned int timeLow;
|
||||
unsigned short timeMid;
|
||||
unsigned short timeHiAndVersion;
|
||||
unsigned short clockSeq;
|
||||
unsigned char node[6];
|
||||
} sound_trigger_uuid_t;
|
||||
|
||||
/*
|
||||
* sound trigger implementation descriptor read by the framework via get_properties().
|
||||
* Used by SoundTrigger service to report to applications and manage concurrency and policy.
|
||||
*/
|
||||
struct sound_trigger_properties {
|
||||
char implementor[SOUND_TRIGGER_MAX_STRING_LEN]; /* implementor name */
|
||||
char description[SOUND_TRIGGER_MAX_STRING_LEN]; /* implementation description */
|
||||
unsigned int version; /* implementation version */
|
||||
sound_trigger_uuid_t uuid; /* unique implementation ID.
|
||||
Must change with version each version */
|
||||
unsigned int max_sound_models; /* maximum number of concurrent sound models
|
||||
loaded */
|
||||
unsigned int max_key_phrases; /* maximum number of key phrases */
|
||||
unsigned int max_users; /* maximum number of concurrent users detected */
|
||||
unsigned int recognition_modes; /* all supported modes.
|
||||
e.g RECOGNITION_MODE_VOICE_TRIGGER */
|
||||
bool capture_transition; /* supports seamless transition from detection
|
||||
to capture */
|
||||
unsigned int max_buffer_ms; /* maximum buffering capacity in ms if
|
||||
capture_transition is true*/
|
||||
bool concurrent_capture; /* supports capture by other use cases while
|
||||
detection is active */
|
||||
bool trigger_in_event; /* returns the trigger capture in event */
|
||||
unsigned int power_consumption_mw; /* Rated power consumption when detection is active
|
||||
with TDB silence/sound/speech ratio */
|
||||
};
|
||||
|
||||
typedef int sound_trigger_module_handle_t;
|
||||
|
||||
struct sound_trigger_module_descriptor {
|
||||
sound_trigger_module_handle_t handle;
|
||||
struct sound_trigger_properties properties;
|
||||
};
|
||||
|
||||
typedef int sound_model_handle_t;
|
||||
|
||||
/*
|
||||
* Generic sound model descriptor. This struct is the header of a larger block passed to
|
||||
* load_sound_model() and containing the binary data of the sound model.
|
||||
* Proprietary representation of users in binary data must match information indicated
|
||||
* by users field
|
||||
*/
|
||||
struct sound_trigger_sound_model {
|
||||
sound_trigger_sound_model_type_t type; /* model type. e.g. SOUND_MODEL_TYPE_KEYPHRASE */
|
||||
sound_trigger_uuid_t uuid; /* unique sound model ID. */
|
||||
sound_trigger_uuid_t vendor_uuid; /* unique vendor ID. Identifies the engine the
|
||||
sound model was build for */
|
||||
unsigned int data_size; /* size of opaque model data */
|
||||
unsigned int data_offset; /* offset of opaque data start from head of struct
|
||||
(e.g sizeof struct sound_trigger_sound_model) */
|
||||
};
|
||||
|
||||
/* key phrase descriptor */
|
||||
struct sound_trigger_phrase {
|
||||
unsigned int id; /* keyphrase ID */
|
||||
unsigned int recognition_mode; /* recognition modes supported by this key phrase */
|
||||
unsigned int num_users; /* number of users in the key phrase */
|
||||
unsigned int users[SOUND_TRIGGER_MAX_USERS]; /* users ids: (not uid_t but sound trigger
|
||||
specific IDs */
|
||||
char locale[SOUND_TRIGGER_MAX_LOCALE_LEN]; /* locale - JAVA Locale style (e.g. en_US) */
|
||||
char text[SOUND_TRIGGER_MAX_STRING_LEN]; /* phrase text in UTF-8 format. */
|
||||
};
|
||||
|
||||
/*
|
||||
* Specialized sound model for key phrase detection.
|
||||
* Proprietary representation of key phrases in binary data must match information indicated
|
||||
* by phrases field
|
||||
*/
|
||||
struct sound_trigger_phrase_sound_model {
|
||||
struct sound_trigger_sound_model common;
|
||||
unsigned int num_phrases; /* number of key phrases in model */
|
||||
struct sound_trigger_phrase phrases[SOUND_TRIGGER_MAX_PHRASES];
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Generic recognition event sent via recognition callback
|
||||
*/
|
||||
struct sound_trigger_recognition_event {
|
||||
int status; /* recognition status e.g.
|
||||
RECOGNITION_STATUS_SUCCESS */
|
||||
sound_trigger_sound_model_type_t type; /* event type, same as sound model type.
|
||||
e.g. SOUND_MODEL_TYPE_KEYPHRASE */
|
||||
sound_model_handle_t model; /* loaded sound model that triggered the
|
||||
event */
|
||||
bool capture_available; /* it is possible to capture audio from this
|
||||
utterance buffered by the
|
||||
implementation */
|
||||
int capture_session; /* audio session ID. framework use */
|
||||
int capture_delay_ms; /* delay in ms between end of model
|
||||
detection and start of audio available
|
||||
for capture. A negative value is possible
|
||||
(e.g. if key phrase is also available for
|
||||
capture */
|
||||
int capture_preamble_ms; /* duration in ms of audio captured
|
||||
before the start of the trigger.
|
||||
0 if none. */
|
||||
bool trigger_in_data; /* the opaque data is the capture of
|
||||
the trigger sound */
|
||||
audio_config_t audio_config; /* audio format of either the trigger in
|
||||
event data or to use for capture of the
|
||||
rest of the utterance */
|
||||
unsigned int data_size; /* size of opaque event data */
|
||||
unsigned int data_offset; /* offset of opaque data start from start of
|
||||
this struct (e.g sizeof struct
|
||||
sound_trigger_phrase_recognition_event) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Confidence level for each user in struct sound_trigger_phrase_recognition_extra
|
||||
*/
|
||||
struct sound_trigger_confidence_level {
|
||||
unsigned int user_id; /* user ID */
|
||||
unsigned int level; /* confidence level in percent (0 - 100).
|
||||
- min level for recognition configuration
|
||||
- detected level for recognition event */
|
||||
};
|
||||
|
||||
/*
|
||||
* Specialized recognition event for key phrase detection
|
||||
*/
|
||||
struct sound_trigger_phrase_recognition_extra {
|
||||
unsigned int id; /* keyphrase ID */
|
||||
unsigned int recognition_modes; /* recognition modes used for this keyphrase */
|
||||
unsigned int confidence_level; /* confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER */
|
||||
unsigned int num_levels; /* number of user confidence levels */
|
||||
struct sound_trigger_confidence_level levels[SOUND_TRIGGER_MAX_USERS];
|
||||
};
|
||||
|
||||
struct sound_trigger_phrase_recognition_event {
|
||||
struct sound_trigger_recognition_event common;
|
||||
unsigned int num_phrases;
|
||||
struct sound_trigger_phrase_recognition_extra phrase_extras[SOUND_TRIGGER_MAX_PHRASES];
|
||||
};
|
||||
|
||||
/*
|
||||
* configuration for sound trigger capture session passed to start_recognition()
|
||||
*/
|
||||
struct sound_trigger_recognition_config {
|
||||
audio_io_handle_t capture_handle; /* IO handle that will be used for capture.
|
||||
N/A if capture_requested is false */
|
||||
audio_devices_t capture_device; /* input device requested for detection capture */
|
||||
bool capture_requested; /* capture and buffer audio for this recognition
|
||||
instance */
|
||||
unsigned int num_phrases; /* number of key phrases recognition extras */
|
||||
struct sound_trigger_phrase_recognition_extra phrases[SOUND_TRIGGER_MAX_PHRASES];
|
||||
/* configuration for each key phrase */
|
||||
unsigned int data_size; /* size of opaque capture configuration data */
|
||||
unsigned int data_offset; /* offset of opaque data start from start of this struct
|
||||
(e.g sizeof struct sound_trigger_recognition_config) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Event sent via load sound model callback
|
||||
*/
|
||||
struct sound_trigger_model_event {
|
||||
int status; /* sound model status e.g. SOUND_MODEL_STATUS_UPDATED */
|
||||
sound_model_handle_t model; /* loaded sound model that triggered the event */
|
||||
unsigned int data_size; /* size of event data if any. Size of updated sound model if
|
||||
status is SOUND_MODEL_STATUS_UPDATED */
|
||||
unsigned int data_offset; /* offset of data start from start of this struct
|
||||
(e.g sizeof struct sound_trigger_model_event) */
|
||||
};
|
||||
|
||||
|
||||
#endif // ANDROID_SOUND_TRIGGER_H
|
|
@ -242,7 +242,26 @@ enum {
|
|||
* The consumer gralloc usage bits currently set by the consumer.
|
||||
* The values are defined in hardware/libhardware/include/gralloc.h.
|
||||
*/
|
||||
NATIVE_WINDOW_CONSUMER_USAGE_BITS = 10
|
||||
NATIVE_WINDOW_CONSUMER_USAGE_BITS = 10,
|
||||
|
||||
/**
|
||||
* Transformation that will by applied to buffers by the hwcomposer.
|
||||
* This must not be set or checked by producer endpoints, and will
|
||||
* disable the transform hint set in SurfaceFlinger (see
|
||||
* NATIVE_WINDOW_TRANSFORM_HINT).
|
||||
*
|
||||
* INTENDED USE:
|
||||
* Temporary - Please do not use this. This is intended only to be used
|
||||
* by the camera's LEGACY mode.
|
||||
*
|
||||
* In situations where a SurfaceFlinger client wishes to set a transform
|
||||
* that is not visible to the producer, and will always be applied in the
|
||||
* hardware composer, the client can set this flag with
|
||||
* native_window_set_buffers_sticky_transform. This can be used to rotate
|
||||
* and flip buffers consumed by hardware composer without actually changing
|
||||
* the aspect ratio of the buffers produced.
|
||||
*/
|
||||
NATIVE_WINDOW_STICKY_TRANSFORM = 11,
|
||||
};
|
||||
|
||||
/* Valid operations for the (*perform)() hook.
|
||||
|
@ -273,6 +292,8 @@ enum {
|
|||
NATIVE_WINDOW_API_DISCONNECT = 14, /* private */
|
||||
NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS = 15, /* private */
|
||||
NATIVE_WINDOW_SET_POST_TRANSFORM_CROP = 16, /* private */
|
||||
NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM = 17,/* private */
|
||||
NATIVE_WINDOW_SET_SIDEBAND_STREAM = 18,
|
||||
};
|
||||
|
||||
/* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */
|
||||
|
@ -790,6 +811,23 @@ static inline int native_window_set_buffers_transform(
|
|||
transform);
|
||||
}
|
||||
|
||||
/*
|
||||
* native_window_set_buffers_sticky_transform(..., int transform)
|
||||
* All buffers queued after this call will be displayed transformed according
|
||||
* to the transform parameter specified applied on top of the regular buffer
|
||||
* transform. Setting this transform will disable the transform hint.
|
||||
*
|
||||
* Temporary - This is only intended to be used by the LEGACY camera mode, do
|
||||
* not use this for anything else.
|
||||
*/
|
||||
static inline int native_window_set_buffers_sticky_transform(
|
||||
struct ANativeWindow* window,
|
||||
int transform)
|
||||
{
|
||||
return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM,
|
||||
transform);
|
||||
}
|
||||
|
||||
/*
|
||||
* native_window_set_buffers_timestamp(..., int64_t timestamp)
|
||||
* All buffers queued after this call will be associated with the timestamp
|
||||
|
@ -856,6 +894,17 @@ static inline int native_window_dequeue_buffer_and_wait(ANativeWindow *anw,
|
|||
return anw->dequeueBuffer_DEPRECATED(anw, anb);
|
||||
}
|
||||
|
||||
/*
|
||||
* native_window_set_sideband_stream(..., native_handle_t*)
|
||||
* Attach a sideband buffer stream to a native window.
|
||||
*/
|
||||
static inline int native_window_set_sideband_stream(
|
||||
struct ANativeWindow* window,
|
||||
native_handle_t* sidebandHandle)
|
||||
{
|
||||
return window->perform(window, NATIVE_WINDOW_SET_SIDEBAND_STREAM,
|
||||
sidebandHandle);
|
||||
}
|
||||
|
||||
__END_DECLS
|
||||
|
||||
|
|
|
@ -37,6 +37,8 @@ public:
|
|||
const static int NlActionAddressUpdated;
|
||||
const static int NlActionAddressRemoved;
|
||||
const static int NlActionRdnss;
|
||||
const static int NlActionRouteUpdated;
|
||||
const static int NlActionRouteRemoved;
|
||||
|
||||
NetlinkEvent();
|
||||
virtual ~NetlinkEvent();
|
||||
|
@ -52,8 +54,11 @@ public:
|
|||
protected:
|
||||
bool parseBinaryNetlinkMessage(char *buffer, int size);
|
||||
bool parseAsciiNetlinkMessage(char *buffer, int size);
|
||||
bool parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, int rtasize);
|
||||
bool parseNdUserOptMessage(struct nduseroptmsg *msg, int optsize);
|
||||
bool parseIfInfoMessage(const struct nlmsghdr *nh);
|
||||
bool parseIfAddrMessage(const struct nlmsghdr *nh);
|
||||
bool parseUlogPacketMessage(const struct nlmsghdr *nh);
|
||||
bool parseRtMessage(const struct nlmsghdr *nh);
|
||||
bool parseNdUserOptMessage(const struct nlmsghdr *nh);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -189,6 +189,13 @@ int usb_device_release_interface(struct usb_device *device, unsigned int interfa
|
|||
int usb_device_connect_kernel_driver(struct usb_device *device,
|
||||
unsigned int interface, int connect);
|
||||
|
||||
/* Sets the current configuration for the device to the specified configuration */
|
||||
int usb_device_set_configuration(struct usb_device *device, int configuration);
|
||||
|
||||
/* Sets the specified interface of a USB device */
|
||||
int usb_device_set_interface(struct usb_device *device, unsigned int interface,
|
||||
unsigned int alt_setting);
|
||||
|
||||
/* Sends a control message to the specified device on endpoint zero */
|
||||
int usb_device_control_transfer(struct usb_device *device,
|
||||
int requestType,
|
||||
|
|
|
@ -30,73 +30,103 @@ namespace android {
|
|||
struct BitSet32 {
|
||||
uint32_t value;
|
||||
|
||||
inline BitSet32() : value(0) { }
|
||||
inline BitSet32() : value(0UL) { }
|
||||
explicit inline BitSet32(uint32_t value) : value(value) { }
|
||||
|
||||
// Gets the value associated with a particular bit index.
|
||||
static inline uint32_t valueForBit(uint32_t n) { return 0x80000000 >> n; }
|
||||
static inline uint32_t valueForBit(uint32_t n) { return 0x80000000UL >> n; }
|
||||
|
||||
// Clears the bit set.
|
||||
inline void clear() { value = 0; }
|
||||
inline void clear() { clear(value); }
|
||||
|
||||
static inline void clear(uint32_t& value) { value = 0UL; }
|
||||
|
||||
// Returns the number of marked bits in the set.
|
||||
inline uint32_t count() const { return __builtin_popcount(value); }
|
||||
inline uint32_t count() const { return count(value); }
|
||||
|
||||
static inline uint32_t count(uint32_t value) { return __builtin_popcountl(value); }
|
||||
|
||||
// Returns true if the bit set does not contain any marked bits.
|
||||
inline bool isEmpty() const { return ! value; }
|
||||
inline bool isEmpty() const { return isEmpty(value); }
|
||||
|
||||
static inline bool isEmpty(uint32_t value) { return ! value; }
|
||||
|
||||
// Returns true if the bit set does not contain any unmarked bits.
|
||||
inline bool isFull() const { return value == 0xffffffff; }
|
||||
inline bool isFull() const { return isFull(value); }
|
||||
|
||||
static inline bool isFull(uint32_t value) { return value == 0xffffffffUL; }
|
||||
|
||||
// Returns true if the specified bit is marked.
|
||||
inline bool hasBit(uint32_t n) const { return value & valueForBit(n); }
|
||||
inline bool hasBit(uint32_t n) const { return hasBit(value, n); }
|
||||
|
||||
static inline bool hasBit(uint32_t value, uint32_t n) { return value & valueForBit(n); }
|
||||
|
||||
// Marks the specified bit.
|
||||
inline void markBit(uint32_t n) { value |= valueForBit(n); }
|
||||
inline void markBit(uint32_t n) { markBit(value, n); }
|
||||
|
||||
static inline void markBit (uint32_t& value, uint32_t n) { value |= valueForBit(n); }
|
||||
|
||||
// Clears the specified bit.
|
||||
inline void clearBit(uint32_t n) { value &= ~ valueForBit(n); }
|
||||
inline void clearBit(uint32_t n) { clearBit(value, n); }
|
||||
|
||||
static inline void clearBit(uint32_t& value, uint32_t n) { value &= ~ valueForBit(n); }
|
||||
|
||||
// Finds the first marked bit in the set.
|
||||
// Result is undefined if all bits are unmarked.
|
||||
inline uint32_t firstMarkedBit() const { return __builtin_clz(value); }
|
||||
inline uint32_t firstMarkedBit() const { return firstMarkedBit(value); }
|
||||
|
||||
static uint32_t firstMarkedBit(uint32_t value) { return clz_checked(value); }
|
||||
|
||||
// Finds the first unmarked bit in the set.
|
||||
// Result is undefined if all bits are marked.
|
||||
inline uint32_t firstUnmarkedBit() const { return __builtin_clz(~ value); }
|
||||
inline uint32_t firstUnmarkedBit() const { return firstUnmarkedBit(value); }
|
||||
|
||||
static inline uint32_t firstUnmarkedBit(uint32_t value) { return clz_checked(~ value); }
|
||||
|
||||
// Finds the last marked bit in the set.
|
||||
// Result is undefined if all bits are unmarked.
|
||||
inline uint32_t lastMarkedBit() const { return 31 - __builtin_ctz(value); }
|
||||
inline uint32_t lastMarkedBit() const { return lastMarkedBit(value); }
|
||||
|
||||
static inline uint32_t lastMarkedBit(uint32_t value) { return 31 - ctz_checked(value); }
|
||||
|
||||
// Finds the first marked bit in the set and clears it. Returns the bit index.
|
||||
// Result is undefined if all bits are unmarked.
|
||||
inline uint32_t clearFirstMarkedBit() {
|
||||
uint32_t n = firstMarkedBit();
|
||||
clearBit(n);
|
||||
inline uint32_t clearFirstMarkedBit() { return clearFirstMarkedBit(value); }
|
||||
|
||||
static inline uint32_t clearFirstMarkedBit(uint32_t& value) {
|
||||
uint32_t n = firstMarkedBit(value);
|
||||
clearBit(value, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
// Finds the first unmarked bit in the set and marks it. Returns the bit index.
|
||||
// Result is undefined if all bits are marked.
|
||||
inline uint32_t markFirstUnmarkedBit() {
|
||||
uint32_t n = firstUnmarkedBit();
|
||||
markBit(n);
|
||||
inline uint32_t markFirstUnmarkedBit() { return markFirstUnmarkedBit(value); }
|
||||
|
||||
static inline uint32_t markFirstUnmarkedBit(uint32_t& value) {
|
||||
uint32_t n = firstUnmarkedBit(value);
|
||||
markBit(value, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
// Finds the last marked bit in the set and clears it. Returns the bit index.
|
||||
// Result is undefined if all bits are unmarked.
|
||||
inline uint32_t clearLastMarkedBit() {
|
||||
uint32_t n = lastMarkedBit();
|
||||
clearBit(n);
|
||||
inline uint32_t clearLastMarkedBit() { return clearLastMarkedBit(value); }
|
||||
|
||||
static inline uint32_t clearLastMarkedBit(uint32_t& value) {
|
||||
uint32_t n = lastMarkedBit(value);
|
||||
clearBit(value, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
// Gets the index of the specified bit in the set, which is the number of
|
||||
// marked bits that appear before the specified bit.
|
||||
inline uint32_t getIndexOfBit(uint32_t n) const {
|
||||
return __builtin_popcount(value & ~(0xffffffffUL >> n));
|
||||
return getIndexOfBit(value, n);
|
||||
}
|
||||
|
||||
static inline uint32_t getIndexOfBit(uint32_t value, uint32_t n) {
|
||||
return __builtin_popcountl(value & ~(0xffffffffUL >> n));
|
||||
}
|
||||
|
||||
inline bool operator== (const BitSet32& other) const { return value == other.value; }
|
||||
|
@ -115,10 +145,150 @@ struct BitSet32 {
|
|||
value |= other.value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
// We use these helpers as the signature of __builtin_c{l,t}z has "unsigned int" for the
|
||||
// input, which is only guaranteed to be 16b, not 32. The compiler should optimize this away.
|
||||
static inline uint32_t clz_checked(uint32_t value) {
|
||||
if (sizeof(unsigned int) == sizeof(uint32_t)) {
|
||||
return __builtin_clz(value);
|
||||
} else {
|
||||
return __builtin_clzl(value);
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t ctz_checked(uint32_t value) {
|
||||
if (sizeof(unsigned int) == sizeof(uint32_t)) {
|
||||
return __builtin_ctz(value);
|
||||
} else {
|
||||
return __builtin_ctzl(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ANDROID_BASIC_TYPES_TRAITS(BitSet32)
|
||||
|
||||
// A simple set of 64 bits that can be individually marked or cleared.
|
||||
struct BitSet64 {
|
||||
uint64_t value;
|
||||
|
||||
inline BitSet64() : value(0ULL) { }
|
||||
explicit inline BitSet64(uint64_t value) : value(value) { }
|
||||
|
||||
// Gets the value associated with a particular bit index.
|
||||
static inline uint64_t valueForBit(uint32_t n) { return 0x8000000000000000ULL >> n; }
|
||||
|
||||
// Clears the bit set.
|
||||
inline void clear() { clear(value); }
|
||||
|
||||
static inline void clear(uint64_t& value) { value = 0ULL; }
|
||||
|
||||
// Returns the number of marked bits in the set.
|
||||
inline uint32_t count() const { return count(value); }
|
||||
|
||||
static inline uint32_t count(uint64_t value) { return __builtin_popcountll(value); }
|
||||
|
||||
// Returns true if the bit set does not contain any marked bits.
|
||||
inline bool isEmpty() const { return isEmpty(value); }
|
||||
|
||||
static inline bool isEmpty(uint64_t value) { return ! value; }
|
||||
|
||||
// Returns true if the bit set does not contain any unmarked bits.
|
||||
inline bool isFull() const { return isFull(value); }
|
||||
|
||||
static inline bool isFull(uint64_t value) { return value == 0xffffffffffffffffULL; }
|
||||
|
||||
// Returns true if the specified bit is marked.
|
||||
inline bool hasBit(uint32_t n) const { return hasBit(value, n); }
|
||||
|
||||
static inline bool hasBit(uint64_t value, uint32_t n) { return value & valueForBit(n); }
|
||||
|
||||
// Marks the specified bit.
|
||||
inline void markBit(uint32_t n) { markBit(value, n); }
|
||||
|
||||
static inline void markBit(uint64_t& value, uint32_t n) { value |= valueForBit(n); }
|
||||
|
||||
// Clears the specified bit.
|
||||
inline void clearBit(uint32_t n) { clearBit(value, n); }
|
||||
|
||||
static inline void clearBit(uint64_t& value, uint32_t n) { value &= ~ valueForBit(n); }
|
||||
|
||||
// Finds the first marked bit in the set.
|
||||
// Result is undefined if all bits are unmarked.
|
||||
inline uint32_t firstMarkedBit() const { return firstMarkedBit(value); }
|
||||
|
||||
static inline uint32_t firstMarkedBit(uint64_t value) { return __builtin_clzll(value); }
|
||||
|
||||
// Finds the first unmarked bit in the set.
|
||||
// Result is undefined if all bits are marked.
|
||||
inline uint32_t firstUnmarkedBit() const { return firstUnmarkedBit(value); }
|
||||
|
||||
static inline uint32_t firstUnmarkedBit(uint64_t value) { return __builtin_clzll(~ value); }
|
||||
|
||||
// Finds the last marked bit in the set.
|
||||
// Result is undefined if all bits are unmarked.
|
||||
inline uint32_t lastMarkedBit() const { return lastMarkedBit(value); }
|
||||
|
||||
static inline uint32_t lastMarkedBit(uint64_t value) { return 63 - __builtin_ctzll(value); }
|
||||
|
||||
// Finds the first marked bit in the set and clears it. Returns the bit index.
|
||||
// Result is undefined if all bits are unmarked.
|
||||
inline uint32_t clearFirstMarkedBit() { return clearFirstMarkedBit(value); }
|
||||
|
||||
static inline uint32_t clearFirstMarkedBit(uint64_t& value) {
|
||||
uint64_t n = firstMarkedBit(value);
|
||||
clearBit(value, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
// Finds the first unmarked bit in the set and marks it. Returns the bit index.
|
||||
// Result is undefined if all bits are marked.
|
||||
inline uint32_t markFirstUnmarkedBit() { return markFirstUnmarkedBit(value); }
|
||||
|
||||
static inline uint32_t markFirstUnmarkedBit(uint64_t& value) {
|
||||
uint64_t n = firstUnmarkedBit(value);
|
||||
markBit(value, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
// Finds the last marked bit in the set and clears it. Returns the bit index.
|
||||
// Result is undefined if all bits are unmarked.
|
||||
inline uint32_t clearLastMarkedBit() { return clearLastMarkedBit(value); }
|
||||
|
||||
static inline uint32_t clearLastMarkedBit(uint64_t& value) {
|
||||
uint64_t n = lastMarkedBit(value);
|
||||
clearBit(value, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
// Gets the index of the specified bit in the set, which is the number of
|
||||
// marked bits that appear before the specified bit.
|
||||
inline uint32_t getIndexOfBit(uint32_t n) const { return getIndexOfBit(value, n); }
|
||||
|
||||
static inline uint32_t getIndexOfBit(uint64_t value, uint32_t n) {
|
||||
return __builtin_popcountll(value & ~(0xffffffffffffffffULL >> n));
|
||||
}
|
||||
|
||||
inline bool operator== (const BitSet64& other) const { return value == other.value; }
|
||||
inline bool operator!= (const BitSet64& other) const { return value != other.value; }
|
||||
inline BitSet64 operator& (const BitSet64& other) const {
|
||||
return BitSet64(value & other.value);
|
||||
}
|
||||
inline BitSet64& operator&= (const BitSet64& other) {
|
||||
value &= other.value;
|
||||
return *this;
|
||||
}
|
||||
inline BitSet64 operator| (const BitSet64& other) const {
|
||||
return BitSet64(value | other.value);
|
||||
}
|
||||
inline BitSet64& operator|= (const BitSet64& other) {
|
||||
value |= other.value;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
ANDROID_BASIC_TYPES_TRAITS(BitSet64)
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // UTILS_BITSET_H
|
||||
|
|
|
@ -60,7 +60,7 @@ public:
|
|||
status_t wait(Mutex& mutex);
|
||||
// same with relative timeout
|
||||
status_t waitRelative(Mutex& mutex, nsecs_t reltime);
|
||||
// Signal the condition variable, allowing one thread to continue.
|
||||
// Signal the condition variable, allowing exactly one thread to continue.
|
||||
void signal();
|
||||
// Signal the condition variable, allowing one or all threads to continue.
|
||||
void signal(WakeUpType type) {
|
||||
|
@ -132,6 +132,17 @@ inline status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) {
|
|||
#endif // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE
|
||||
}
|
||||
inline void Condition::signal() {
|
||||
/*
|
||||
* POSIX says pthread_cond_signal wakes up "one or more" waiting threads.
|
||||
* However bionic follows the glibc guarantee which wakes up "exactly one"
|
||||
* waiting thread.
|
||||
*
|
||||
* man 3 pthread_cond_signal
|
||||
* pthread_cond_signal restarts one of the threads that are waiting on
|
||||
* the condition variable cond. If no threads are waiting on cond,
|
||||
* nothing happens. If several threads are waiting on cond, exactly one
|
||||
* is restarted, but it is not specified which.
|
||||
*/
|
||||
pthread_cond_signal(&mCond);
|
||||
}
|
||||
inline void Condition::broadcast() {
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
#ifndef ANDROID_UTILS_LRU_CACHE_H
|
||||
#define ANDROID_UTILS_LRU_CACHE_H
|
||||
|
||||
#include <UniquePtr.h>
|
||||
#include <utils/BasicHashtable.h>
|
||||
#include <utils/UniquePtr.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
|
@ -48,6 +48,7 @@ public:
|
|||
bool remove(const TKey& key);
|
||||
bool removeOldest();
|
||||
void clear();
|
||||
const TValue& peekOldestValue();
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
|
@ -183,6 +184,14 @@ bool LruCache<TKey, TValue>::removeOldest() {
|
|||
return false;
|
||||
}
|
||||
|
||||
template <typename TKey, typename TValue>
|
||||
const TValue& LruCache<TKey, TValue>::peekOldestValue() {
|
||||
if (mOldest) {
|
||||
return mOldest->value;
|
||||
}
|
||||
return mNullValue;
|
||||
}
|
||||
|
||||
template <typename TKey, typename TValue>
|
||||
void LruCache<TKey, TValue>::clear() {
|
||||
if (mListener) {
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2014 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 ANDROID_NATIVE_HANDLE_H
|
||||
#define ANDROID_NATIVE_HANDLE_H
|
||||
|
||||
#include <utils/RefBase.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
|
||||
typedef struct native_handle native_handle_t;
|
||||
|
||||
namespace android {
|
||||
|
||||
class NativeHandle: public LightRefBase<NativeHandle> {
|
||||
public:
|
||||
// Create a refcounted wrapper around a native_handle_t, and declare
|
||||
// whether the wrapper owns the handle (so that it should clean up the
|
||||
// handle upon destruction) or not.
|
||||
// If handle is NULL, no NativeHandle will be created.
|
||||
static sp<NativeHandle> create(native_handle_t* handle, bool ownsHandle);
|
||||
|
||||
const native_handle_t* handle() const {
|
||||
return mHandle;
|
||||
}
|
||||
|
||||
private:
|
||||
// for access to the destructor
|
||||
friend class LightRefBase<NativeHandle>;
|
||||
|
||||
NativeHandle(native_handle_t* handle, bool ownsHandle);
|
||||
virtual ~NativeHandle();
|
||||
|
||||
native_handle_t* mHandle;
|
||||
bool mOwnsHandle;
|
||||
|
||||
// non-copyable
|
||||
NativeHandle(const NativeHandle&);
|
||||
NativeHandle& operator=(const NativeHandle&);
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_NATIVE_HANDLE_H
|
|
@ -203,6 +203,13 @@ private:
|
|||
mutable volatile int32_t mCount;
|
||||
};
|
||||
|
||||
// This is a wrapper around LightRefBase that simply enforces a virtual
|
||||
// destructor to eliminate the template requirement of LightRefBase
|
||||
class VirtualLightRefBase : public LightRefBase<VirtualLightRefBase> {
|
||||
public:
|
||||
virtual ~VirtualLightRefBase() {}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
template <typename T>
|
||||
|
|
|
@ -130,11 +130,19 @@ public:
|
|||
// start, or -1 if not found
|
||||
ssize_t find(const char* other, size_t start = 0) const;
|
||||
|
||||
// return true if this string contains the specified substring
|
||||
inline bool contains(const char* other) const;
|
||||
|
||||
// removes all occurrence of the specified substring
|
||||
// returns true if any were found and removed
|
||||
bool removeAll(const char* other);
|
||||
|
||||
void toLower();
|
||||
void toLower(size_t start, size_t numChars);
|
||||
void toUpper();
|
||||
void toUpper(size_t start, size_t numChars);
|
||||
|
||||
|
||||
/*
|
||||
* These methods operate on the string as if it were a path name.
|
||||
*/
|
||||
|
@ -280,6 +288,11 @@ inline const SharedBuffer* String8::sharedBuffer() const
|
|||
return SharedBuffer::bufferFromData(mString);
|
||||
}
|
||||
|
||||
inline bool String8::contains(const char* other) const
|
||||
{
|
||||
return find(other) >= 0;
|
||||
}
|
||||
|
||||
inline String8& String8::operator=(const String8& other)
|
||||
{
|
||||
setTo(other);
|
||||
|
|
|
@ -1,239 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 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.
|
||||
*/
|
||||
|
||||
/* === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE ===
|
||||
*
|
||||
* THIS IS A COPY OF libcore/include/UniquePtr.h AND AS SUCH THAT IS THE
|
||||
* CANONICAL SOURCE OF THIS FILE. PLEASE KEEP THEM IN SYNC.
|
||||
*
|
||||
* === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE ===
|
||||
*/
|
||||
|
||||
#ifndef UNIQUE_PTR_H_included
|
||||
#define UNIQUE_PTR_H_included
|
||||
|
||||
#include <cstdlib> // For NULL.
|
||||
|
||||
// Default deleter for pointer types.
|
||||
template <typename T>
|
||||
struct DefaultDelete {
|
||||
enum { type_must_be_complete = sizeof(T) };
|
||||
DefaultDelete() {}
|
||||
void operator()(T* p) const {
|
||||
delete p;
|
||||
}
|
||||
};
|
||||
|
||||
// Default deleter for array types.
|
||||
template <typename T>
|
||||
struct DefaultDelete<T[]> {
|
||||
enum { type_must_be_complete = sizeof(T) };
|
||||
void operator()(T* p) const {
|
||||
delete[] p;
|
||||
}
|
||||
};
|
||||
|
||||
// A smart pointer that deletes the given pointer on destruction.
|
||||
// Equivalent to C++0x's std::unique_ptr (a combination of boost::scoped_ptr
|
||||
// and boost::scoped_array).
|
||||
// Named to be in keeping with Android style but also to avoid
|
||||
// collision with any other implementation, until we can switch over
|
||||
// to unique_ptr.
|
||||
// Use thus:
|
||||
// UniquePtr<C> c(new C);
|
||||
template <typename T, typename D = DefaultDelete<T> >
|
||||
class UniquePtr {
|
||||
public:
|
||||
// Construct a new UniquePtr, taking ownership of the given raw pointer.
|
||||
explicit UniquePtr(T* ptr = NULL) : mPtr(ptr) {
|
||||
}
|
||||
|
||||
~UniquePtr() {
|
||||
reset();
|
||||
}
|
||||
|
||||
// Accessors.
|
||||
T& operator*() const { return *mPtr; }
|
||||
T* operator->() const { return mPtr; }
|
||||
T* get() const { return mPtr; }
|
||||
|
||||
// Returns the raw pointer and hands over ownership to the caller.
|
||||
// The pointer will not be deleted by UniquePtr.
|
||||
T* release() __attribute__((warn_unused_result)) {
|
||||
T* result = mPtr;
|
||||
mPtr = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Takes ownership of the given raw pointer.
|
||||
// If this smart pointer previously owned a different raw pointer, that
|
||||
// raw pointer will be freed.
|
||||
void reset(T* ptr = NULL) {
|
||||
if (ptr != mPtr) {
|
||||
D()(mPtr);
|
||||
mPtr = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// The raw pointer.
|
||||
T* mPtr;
|
||||
|
||||
// Comparing unique pointers is probably a mistake, since they're unique.
|
||||
template <typename T2> bool operator==(const UniquePtr<T2>& p) const;
|
||||
template <typename T2> bool operator!=(const UniquePtr<T2>& p) const;
|
||||
|
||||
// Disallow copy and assignment.
|
||||
UniquePtr(const UniquePtr&);
|
||||
void operator=(const UniquePtr&);
|
||||
};
|
||||
|
||||
// Partial specialization for array types. Like std::unique_ptr, this removes
|
||||
// operator* and operator-> but adds operator[].
|
||||
template <typename T, typename D>
|
||||
class UniquePtr<T[], D> {
|
||||
public:
|
||||
explicit UniquePtr(T* ptr = NULL) : mPtr(ptr) {
|
||||
}
|
||||
|
||||
~UniquePtr() {
|
||||
reset();
|
||||
}
|
||||
|
||||
T& operator[](size_t i) const {
|
||||
return mPtr[i];
|
||||
}
|
||||
T* get() const { return mPtr; }
|
||||
|
||||
T* release() __attribute__((warn_unused_result)) {
|
||||
T* result = mPtr;
|
||||
mPtr = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
void reset(T* ptr = NULL) {
|
||||
if (ptr != mPtr) {
|
||||
D()(mPtr);
|
||||
mPtr = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
T* mPtr;
|
||||
|
||||
// Disallow copy and assignment.
|
||||
UniquePtr(const UniquePtr&);
|
||||
void operator=(const UniquePtr&);
|
||||
};
|
||||
|
||||
#if UNIQUE_PTR_TESTS
|
||||
|
||||
// Run these tests with:
|
||||
// g++ -g -DUNIQUE_PTR_TESTS -x c++ UniquePtr.h && ./a.out
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static void assert(bool b) {
|
||||
if (!b) {
|
||||
fprintf(stderr, "FAIL\n");
|
||||
abort();
|
||||
}
|
||||
fprintf(stderr, "OK\n");
|
||||
}
|
||||
static int cCount = 0;
|
||||
struct C {
|
||||
C() { ++cCount; }
|
||||
~C() { --cCount; }
|
||||
};
|
||||
static bool freed = false;
|
||||
struct Freer {
|
||||
void operator()(int* p) {
|
||||
assert(*p == 123);
|
||||
free(p);
|
||||
freed = true;
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
//
|
||||
// UniquePtr<T> tests...
|
||||
//
|
||||
|
||||
// Can we free a single object?
|
||||
{
|
||||
UniquePtr<C> c(new C);
|
||||
assert(cCount == 1);
|
||||
}
|
||||
assert(cCount == 0);
|
||||
// Does release work?
|
||||
C* rawC;
|
||||
{
|
||||
UniquePtr<C> c(new C);
|
||||
assert(cCount == 1);
|
||||
rawC = c.release();
|
||||
}
|
||||
assert(cCount == 1);
|
||||
delete rawC;
|
||||
// Does reset work?
|
||||
{
|
||||
UniquePtr<C> c(new C);
|
||||
assert(cCount == 1);
|
||||
c.reset(new C);
|
||||
assert(cCount == 1);
|
||||
}
|
||||
assert(cCount == 0);
|
||||
|
||||
//
|
||||
// UniquePtr<T[]> tests...
|
||||
//
|
||||
|
||||
// Can we free an array?
|
||||
{
|
||||
UniquePtr<C[]> cs(new C[4]);
|
||||
assert(cCount == 4);
|
||||
}
|
||||
assert(cCount == 0);
|
||||
// Does release work?
|
||||
{
|
||||
UniquePtr<C[]> c(new C[4]);
|
||||
assert(cCount == 4);
|
||||
rawC = c.release();
|
||||
}
|
||||
assert(cCount == 4);
|
||||
delete[] rawC;
|
||||
// Does reset work?
|
||||
{
|
||||
UniquePtr<C[]> c(new C[4]);
|
||||
assert(cCount == 4);
|
||||
c.reset(new C[2]);
|
||||
assert(cCount == 2);
|
||||
}
|
||||
assert(cCount == 0);
|
||||
|
||||
//
|
||||
// Custom deleter tests...
|
||||
//
|
||||
assert(!freed);
|
||||
{
|
||||
UniquePtr<int, Freer> i(reinterpret_cast<int*>(malloc(sizeof(int))));
|
||||
*i = 123;
|
||||
}
|
||||
assert(freed);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // UNIQUE_PTR_H_included
|
|
@ -473,6 +473,27 @@ exit_success:
|
|||
|
||||
}
|
||||
|
||||
static int wipe_data_via_recovery()
|
||||
{
|
||||
mkdir("/cache/recovery", 0700);
|
||||
int fd = open("/cache/recovery/command", O_RDWR|O_CREAT|O_TRUNC, 0600);
|
||||
if (fd >= 0) {
|
||||
write(fd, "--wipe_data\n", strlen("--wipe_data\n") + 1);
|
||||
write(fd, "--reason=wipe_data_via_recovery\n", strlen("--reason=wipe_data_via_recovery\n") + 1);
|
||||
close(fd);
|
||||
} else {
|
||||
ERROR("could not open /cache/recovery/command\n");
|
||||
return -1;
|
||||
}
|
||||
android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
|
||||
while (1) { pause(); } // never reached
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This function might request a reboot, in which case it will
|
||||
* not return.
|
||||
*/
|
||||
int do_mount_all(int nargs, char **args)
|
||||
{
|
||||
pid_t pid;
|
||||
|
@ -495,7 +516,12 @@ int do_mount_all(int nargs, char **args)
|
|||
pid = fork();
|
||||
if (pid > 0) {
|
||||
/* Parent. Wait for the child to return */
|
||||
waitpid(pid, &status, 0);
|
||||
int wp_ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
|
||||
if (wp_ret < 0) {
|
||||
/* Unexpected error code. We will continue anyway. */
|
||||
NOTICE("waitpid failed rc=%d, errno=%d\n", wp_ret, errno);
|
||||
}
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
ret = WEXITSTATUS(status);
|
||||
} else {
|
||||
|
@ -510,23 +536,32 @@ int do_mount_all(int nargs, char **args)
|
|||
if (child_ret == -1) {
|
||||
ERROR("fs_mgr_mount_all returned an error\n");
|
||||
}
|
||||
exit(child_ret);
|
||||
_exit(child_ret);
|
||||
} else {
|
||||
/* fork failed, return an error */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ret is 1 if the device is encrypted, 0 if not, and -1 on error */
|
||||
if (ret == 1) {
|
||||
if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
|
||||
property_set("vold.decrypt", "trigger_encryption");
|
||||
} else if (ret == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
|
||||
property_set("ro.crypto.state", "encrypted");
|
||||
property_set("vold.decrypt", "1");
|
||||
} else if (ret == 0) {
|
||||
property_set("vold.decrypt", "trigger_default_encryption");
|
||||
} else if (ret == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
|
||||
property_set("ro.crypto.state", "unencrypted");
|
||||
/* If fs_mgr determined this is an unencrypted device, then trigger
|
||||
* that action.
|
||||
*/
|
||||
action_for_each_trigger("nonencrypted", action_add_queue_tail);
|
||||
} else if (ret == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
|
||||
/* Setup a wipe via recovery, and reboot into recovery */
|
||||
ERROR("fs_mgr_mount_all suggested recovery, so wiping data via recovery.\n");
|
||||
ret = wipe_data_via_recovery();
|
||||
/* If reboot worked, there is no return. */
|
||||
} else if (ret > 0) {
|
||||
ERROR("fs_mgr_mount_all returned unexpected error %d\n", ret);
|
||||
}
|
||||
/* else ... < 0: error */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -944,7 +944,7 @@ static void handle_firmware_event(struct uevent *uevent)
|
|||
}
|
||||
}
|
||||
|
||||
#define UEVENT_MSG_LEN 1024
|
||||
#define UEVENT_MSG_LEN 2048
|
||||
void handle_device_fd()
|
||||
{
|
||||
char msg[UEVENT_MSG_LEN+2];
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <dirent.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include <sys/poll.h>
|
||||
|
||||
#include <cutils/misc.h>
|
||||
#include <cutils/sockets.h>
|
||||
|
@ -181,7 +182,6 @@ static void write_persistent_property(const char *name, const char *value)
|
|||
static bool is_legal_property_name(const char* name, size_t namelen)
|
||||
{
|
||||
size_t i;
|
||||
bool previous_was_dot = false;
|
||||
if (namelen >= PROP_NAME_MAX) return false;
|
||||
if (namelen < 1) return false;
|
||||
if (name[0] == '.') return false;
|
||||
|
@ -191,11 +191,10 @@ static bool is_legal_property_name(const char* name, size_t namelen)
|
|||
/* Don't allow ".." to appear in a property name */
|
||||
for (i = 0; i < namelen; i++) {
|
||||
if (name[i] == '.') {
|
||||
if (previous_was_dot == true) return false;
|
||||
previous_was_dot = true;
|
||||
// i=0 is guaranteed to never have a dot. See above.
|
||||
if (name[i-1] == '.') return false;
|
||||
continue;
|
||||
}
|
||||
previous_was_dot = false;
|
||||
if (name[i] == '_' || name[i] == '-') continue;
|
||||
if (name[i] >= 'a' && name[i] <= 'z') continue;
|
||||
if (name[i] >= 'A' && name[i] <= 'Z') continue;
|
||||
|
@ -268,6 +267,9 @@ void handle_property_set_fd()
|
|||
socklen_t addr_size = sizeof(addr);
|
||||
socklen_t cr_size = sizeof(cr);
|
||||
char * source_ctx = NULL;
|
||||
struct pollfd ufds[1];
|
||||
const int timeout_ms = 2 * 1000; /* Default 2 sec timeout for caller to send property. */
|
||||
int nr;
|
||||
|
||||
if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {
|
||||
return;
|
||||
|
@ -280,7 +282,21 @@ void handle_property_set_fd()
|
|||
return;
|
||||
}
|
||||
|
||||
r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), 0));
|
||||
ufds[0].fd = s;
|
||||
ufds[0].events = POLLIN;
|
||||
ufds[0].revents = 0;
|
||||
nr = TEMP_FAILURE_RETRY(poll(ufds, 1, timeout_ms));
|
||||
if (nr == 0) {
|
||||
ERROR("sys_prop: timeout waiting for uid=%d to send property message.\n", cr.uid);
|
||||
close(s);
|
||||
return;
|
||||
} else if (nr < 0) {
|
||||
ERROR("sys_prop: error waiting for uid=%d to send property message. err=%d %s\n", cr.uid, errno, strerror(errno));
|
||||
close(s);
|
||||
return;
|
||||
}
|
||||
|
||||
r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT));
|
||||
if(r != sizeof(prop_msg)) {
|
||||
ERROR("sys_prop: mis-match msg size received: %d expected: %zu errno: %d\n",
|
||||
r, sizeof(prop_msg), errno);
|
||||
|
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* Copyright (C) 2013 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 <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <log/log.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <backtrace/backtrace.h>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
|
||||
// Mac OS vmmap(1) output:
|
||||
// __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
|
||||
// 012345678901234567890123456789012345678901234567890123456789
|
||||
// 0 1 2 3 4 5
|
||||
static backtrace_map_info_t* parse_vmmap_line(const char* line) {
|
||||
unsigned long int start;
|
||||
unsigned long int end;
|
||||
char permissions[4];
|
||||
int name_pos;
|
||||
if (sscanf(line, "%*21c %lx-%lx [%*13c] %3c/%*3c SM=%*3c %n",
|
||||
&start, &end, permissions, &name_pos) != 3) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* name = line + name_pos;
|
||||
size_t name_len = strlen(name);
|
||||
|
||||
backtrace_map_info_t* mi = calloc(1, sizeof(backtrace_map_info_t) + name_len);
|
||||
if (mi != NULL) {
|
||||
mi->start = start;
|
||||
mi->end = end;
|
||||
mi->is_readable = permissions[0] == 'r';
|
||||
mi->is_writable = permissions[1] == 'w';
|
||||
mi->is_executable = permissions[2] == 'x';
|
||||
memcpy(mi->name, name, name_len);
|
||||
mi->name[name_len - 1] = '\0';
|
||||
ALOGV("Parsed map: start=0x%08x, end=0x%08x, "
|
||||
"is_readable=%d, is_writable=%d is_executable=%d, name=%s",
|
||||
mi->start, mi->end,
|
||||
mi->is_readable, mi->is_writable, mi->is_executable, mi->name);
|
||||
}
|
||||
return mi;
|
||||
}
|
||||
|
||||
backtrace_map_info_t* backtrace_create_map_info_list(pid_t pid) {
|
||||
char cmd[1024];
|
||||
if (pid < 0) {
|
||||
pid = getpid();
|
||||
}
|
||||
snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid);
|
||||
FILE* fp = popen(cmd, "r");
|
||||
if (fp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char line[1024];
|
||||
backtrace_map_info_t* milist = NULL;
|
||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||
backtrace_map_info_t* mi = parse_vmmap_line(line);
|
||||
if (mi != NULL) {
|
||||
mi->next = milist;
|
||||
milist = mi;
|
||||
}
|
||||
}
|
||||
pclose(fp);
|
||||
return milist;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// Linux /proc/<pid>/maps lines:
|
||||
// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so\n
|
||||
// 012345678901234567890123456789012345678901234567890123456789
|
||||
// 0 1 2 3 4 5
|
||||
static backtrace_map_info_t* parse_maps_line(const char* line)
|
||||
{
|
||||
unsigned long int start;
|
||||
unsigned long int end;
|
||||
char permissions[5];
|
||||
int name_pos;
|
||||
if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d%n", &start, &end,
|
||||
permissions, &name_pos) != 3) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (isspace(line[name_pos])) {
|
||||
name_pos += 1;
|
||||
}
|
||||
const char* name = line + name_pos;
|
||||
size_t name_len = strlen(name);
|
||||
if (name_len && name[name_len - 1] == '\n') {
|
||||
name_len -= 1;
|
||||
}
|
||||
|
||||
backtrace_map_info_t* mi = calloc(1, sizeof(backtrace_map_info_t) + name_len + 1);
|
||||
if (mi) {
|
||||
mi->start = start;
|
||||
mi->end = end;
|
||||
mi->is_readable = strlen(permissions) == 4 && permissions[0] == 'r';
|
||||
mi->is_writable = strlen(permissions) == 4 && permissions[1] == 'w';
|
||||
mi->is_executable = strlen(permissions) == 4 && permissions[2] == 'x';
|
||||
memcpy(mi->name, name, name_len);
|
||||
mi->name[name_len] = '\0';
|
||||
ALOGV("Parsed map: start=0x%08x, end=0x%08x, "
|
||||
"is_readable=%d, is_writable=%d, is_executable=%d, name=%s",
|
||||
mi->start, mi->end,
|
||||
mi->is_readable, mi->is_writable, mi->is_executable, mi->name);
|
||||
}
|
||||
return mi;
|
||||
}
|
||||
|
||||
backtrace_map_info_t* backtrace_create_map_info_list(pid_t tid) {
|
||||
char path[PATH_MAX];
|
||||
char line[1024];
|
||||
FILE* fp;
|
||||
backtrace_map_info_t* milist = NULL;
|
||||
|
||||
if (tid < 0) {
|
||||
tid = getpid();
|
||||
}
|
||||
snprintf(path, PATH_MAX, "/proc/%d/maps", tid);
|
||||
fp = fopen(path, "r");
|
||||
if (fp) {
|
||||
while(fgets(line, sizeof(line), fp)) {
|
||||
backtrace_map_info_t* mi = parse_maps_line(line);
|
||||
if (mi) {
|
||||
mi->next = milist;
|
||||
milist = mi;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
return milist;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void backtrace_destroy_map_info_list(backtrace_map_info_t* milist) {
|
||||
while (milist) {
|
||||
backtrace_map_info_t* next = milist->next;
|
||||
free(milist);
|
||||
milist = next;
|
||||
}
|
||||
}
|
||||
|
||||
const backtrace_map_info_t* backtrace_find_map_info(
|
||||
const backtrace_map_info_t* milist, uintptr_t addr) {
|
||||
const backtrace_map_info_t* mi = milist;
|
||||
while (mi && !(addr >= mi->start && addr < mi->end)) {
|
||||
mi = mi->next;
|
||||
}
|
||||
return mi;
|
||||
}
|
|
@ -212,7 +212,7 @@ int fs_mkdirs(const char* path, mode_t mode) {
|
|||
|
||||
/* Yay, segment is ready for us to step into */
|
||||
int next_fd;
|
||||
if ((next_fd = openat(fd, segment, 0)) == -1) {
|
||||
if ((next_fd = openat(fd, segment, O_NOFOLLOW | O_CLOEXEC)) == -1) {
|
||||
ALOGE("Failed to openat(%s): %s", buf, strerror(errno));
|
||||
res = -errno;
|
||||
goto done_close;
|
||||
|
|
|
@ -39,21 +39,20 @@ static inline SchedPolicy _policy(SchedPolicy p)
|
|||
|
||||
#if defined(HAVE_ANDROID_OS) && defined(HAVE_SCHED_H) && defined(HAVE_PTHREADS)
|
||||
|
||||
#include <sched.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#ifndef SCHED_NORMAL
|
||||
#define SCHED_NORMAL 0
|
||||
#endif
|
||||
|
||||
#ifndef SCHED_BATCH
|
||||
#define SCHED_BATCH 3
|
||||
#endif
|
||||
#include <sched.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#define POLICY_DEBUG 0
|
||||
|
||||
#define CAN_SET_SP_SYSTEM 0 // non-zero means to implement set_sched_policy(tid, SP_SYSTEM)
|
||||
|
||||
// This prctl is only available in Android kernels.
|
||||
#define PR_SET_TIMERSLACK_PID 41
|
||||
|
||||
// timer slack value in nS enforced when the thread moves to background
|
||||
#define TIMER_SLACK_BG 40000000
|
||||
|
||||
static pthread_once_t the_once = PTHREAD_ONCE_INIT;
|
||||
|
||||
static int __sys_supports_schedgroups = -1;
|
||||
|
@ -321,6 +320,8 @@ int set_sched_policy(int tid, SchedPolicy policy)
|
|||
¶m);
|
||||
}
|
||||
|
||||
prctl(PR_SET_TIMERSLACK_PID, policy == SP_BACKGROUND ? TIMER_SLACK_BG : 0, tid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -262,6 +262,10 @@ int str_parms_add_float(struct str_parms *str_parms, const char *key,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int str_parms_has_key(struct str_parms *str_parms, const char *key) {
|
||||
return hashmapGet(str_parms->map, (void *)key) != NULL;
|
||||
}
|
||||
|
||||
int str_parms_get_str(struct str_parms *str_parms, const char *key, char *val,
|
||||
int len)
|
||||
{
|
||||
|
|
|
@ -208,6 +208,26 @@ fail:
|
|||
}
|
||||
|
||||
|
||||
static struct write_list *
|
||||
mk_mbr_sig()
|
||||
{
|
||||
struct write_list *item;
|
||||
if (!(item = alloc_wl(sizeof(uint16_t)))) {
|
||||
ALOGE("Unable to allocate memory for MBR signature.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
{
|
||||
/* DO NOT DEREFERENCE */
|
||||
struct pc_boot_record *mbr = (void *)PC_MBR_DISK_OFFSET;
|
||||
/* grab the offset in mbr where to write mbr signature. */
|
||||
item->offset = (loff_t)((uintptr_t)((uint8_t *)(&mbr->mbr_sig)));
|
||||
}
|
||||
|
||||
*((uint16_t*)item->data) = PC_BIOS_BOOT_SIG;
|
||||
return item;
|
||||
}
|
||||
|
||||
struct write_list *
|
||||
config_mbr(struct disk_info *dinfo)
|
||||
{
|
||||
|
@ -276,6 +296,13 @@ config_mbr(struct disk_info *dinfo)
|
|||
wlist_add(&wr_list, temp_wr);
|
||||
}
|
||||
|
||||
if ((temp_wr = mk_mbr_sig()))
|
||||
wlist_add(&wr_list, temp_wr);
|
||||
else {
|
||||
ALOGE("Cannot set MBR signature");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return wr_list;
|
||||
|
||||
nospace:
|
||||
|
|
|
@ -58,7 +58,6 @@ static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
|
|||
#endif
|
||||
|
||||
#if FAKE_LOG_DEVICE
|
||||
#define WEAK __attribute__((weak))
|
||||
static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1, -1 };
|
||||
#else
|
||||
static int logd_fd = -1;
|
||||
|
@ -274,7 +273,7 @@ static const char *LOG_NAME[LOG_ID_MAX] = {
|
|||
[LOG_ID_CRASH] = "crash"
|
||||
};
|
||||
|
||||
const WEAK char *android_log_id_to_name(log_id_t log_id)
|
||||
const char *android_log_id_to_name(log_id_t log_id)
|
||||
{
|
||||
if (log_id >= LOG_ID_MAX) {
|
||||
log_id = LOG_ID_MAIN;
|
||||
|
|
|
@ -12,6 +12,7 @@ test_src_files := \
|
|||
ValidNameNativeBridge_test.cpp
|
||||
|
||||
shared_libraries := \
|
||||
liblog \
|
||||
libnativebridge
|
||||
|
||||
$(foreach file,$(test_src_files), \
|
||||
|
@ -32,4 +33,4 @@ $(foreach file,$(test_src_files), \
|
|||
$(eval LOCAL_SRC_FILES := $(file)) \
|
||||
$(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
|
||||
$(eval include $(BUILD_HOST_NATIVE_TEST)) \
|
||||
)
|
||||
)
|
|
@ -563,17 +563,7 @@ int ifc_create_default_route(const char *name, in_addr_t gw)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* deprecated v4-only */
|
||||
int ifc_add_host_route(const char *name, in_addr_t dst)
|
||||
{
|
||||
struct in_addr in_dst, in_gw;
|
||||
|
||||
in_dst.s_addr = dst;
|
||||
in_gw.s_addr = 0;
|
||||
|
||||
return ifc_act_on_ipv4_route(SIOCADDRT, name, in_dst, 32, in_gw);
|
||||
}
|
||||
|
||||
// Needed by code in hidden partner repositories / branches, so don't delete.
|
||||
int ifc_enable(const char *ifname)
|
||||
{
|
||||
int result;
|
||||
|
@ -584,6 +574,7 @@ int ifc_enable(const char *ifname)
|
|||
return result;
|
||||
}
|
||||
|
||||
// Needed by code in hidden partner repositories / branches, so don't delete.
|
||||
int ifc_disable(const char *ifname)
|
||||
{
|
||||
unsigned addr, count;
|
||||
|
@ -608,14 +599,16 @@ int ifc_reset_connections(const char *ifname, const int reset_mask)
|
|||
{
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
int result, success;
|
||||
in_addr_t myaddr;
|
||||
in_addr_t myaddr = 0;
|
||||
struct ifreq ifr;
|
||||
struct in6_ifreq ifr6;
|
||||
|
||||
if (reset_mask & RESET_IPV4_ADDRESSES) {
|
||||
/* IPv4. Clear connections on the IP address. */
|
||||
ifc_init();
|
||||
ifc_get_info(ifname, &myaddr, NULL, NULL);
|
||||
if (!(reset_mask & RESET_IGNORE_INTERFACE_ADDRESS)) {
|
||||
ifc_get_info(ifname, &myaddr, NULL, NULL);
|
||||
}
|
||||
ifc_init_ifr(ifname, &ifr);
|
||||
init_sockaddr_in(&ifr.ifr_addr, myaddr);
|
||||
result = ioctl(ifc_ctl_sock, SIOCKILLADDR, &ifr);
|
||||
|
@ -647,118 +640,6 @@ int ifc_reset_connections(const char *ifname, const int reset_mask)
|
|||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the routes associated with the named interface.
|
||||
*/
|
||||
int ifc_remove_host_routes(const char *name)
|
||||
{
|
||||
char ifname[64];
|
||||
in_addr_t dest, gway, mask;
|
||||
int flags, refcnt, use, metric, mtu, win, irtt;
|
||||
struct rtentry rt;
|
||||
FILE *fp;
|
||||
struct in_addr addr;
|
||||
|
||||
fp = fopen("/proc/net/route", "r");
|
||||
if (fp == NULL)
|
||||
return -1;
|
||||
/* Skip the header line */
|
||||
if (fscanf(fp, "%*[^\n]\n") < 0) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
ifc_init();
|
||||
for (;;) {
|
||||
int nread = fscanf(fp, "%63s%X%X%X%d%d%d%X%d%d%d\n",
|
||||
ifname, &dest, &gway, &flags, &refcnt, &use, &metric, &mask,
|
||||
&mtu, &win, &irtt);
|
||||
if (nread != 11) {
|
||||
break;
|
||||
}
|
||||
if ((flags & (RTF_UP|RTF_HOST)) != (RTF_UP|RTF_HOST)
|
||||
|| strcmp(ifname, name) != 0) {
|
||||
continue;
|
||||
}
|
||||
memset(&rt, 0, sizeof(rt));
|
||||
rt.rt_dev = (void *)name;
|
||||
init_sockaddr_in(&rt.rt_dst, dest);
|
||||
init_sockaddr_in(&rt.rt_gateway, gway);
|
||||
init_sockaddr_in(&rt.rt_genmask, mask);
|
||||
addr.s_addr = dest;
|
||||
if (ioctl(ifc_ctl_sock, SIOCDELRT, &rt) < 0) {
|
||||
ALOGD("failed to remove route for %s to %s: %s",
|
||||
ifname, inet_ntoa(addr), strerror(errno));
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
ifc_close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the address of the default gateway
|
||||
*
|
||||
* TODO: factor out common code from this and remove_host_routes()
|
||||
* so that we only scan /proc/net/route in one place.
|
||||
*
|
||||
* DEPRECATED
|
||||
*/
|
||||
int ifc_get_default_route(const char *ifname)
|
||||
{
|
||||
char name[64];
|
||||
in_addr_t dest, gway, mask;
|
||||
int flags, refcnt, use, metric, mtu, win, irtt;
|
||||
int result;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/proc/net/route", "r");
|
||||
if (fp == NULL)
|
||||
return 0;
|
||||
/* Skip the header line */
|
||||
if (fscanf(fp, "%*[^\n]\n") < 0) {
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
ifc_init();
|
||||
result = 0;
|
||||
for (;;) {
|
||||
int nread = fscanf(fp, "%63s%X%X%X%d%d%d%X%d%d%d\n",
|
||||
name, &dest, &gway, &flags, &refcnt, &use, &metric, &mask,
|
||||
&mtu, &win, &irtt);
|
||||
if (nread != 11) {
|
||||
break;
|
||||
}
|
||||
if ((flags & (RTF_UP|RTF_GATEWAY)) == (RTF_UP|RTF_GATEWAY)
|
||||
&& dest == 0
|
||||
&& strcmp(ifname, name) == 0) {
|
||||
result = gway;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
ifc_close();
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the specified gateway as the default route for the named interface.
|
||||
* DEPRECATED
|
||||
*/
|
||||
int ifc_set_default_route(const char *ifname, in_addr_t gateway)
|
||||
{
|
||||
struct in_addr addr;
|
||||
int result;
|
||||
|
||||
ifc_init();
|
||||
addr.s_addr = gateway;
|
||||
if ((result = ifc_create_default_route(ifname, gateway)) < 0) {
|
||||
ALOGD("failed to add %s as default route for %s: %s",
|
||||
inet_ntoa(addr), ifname, strerror(errno));
|
||||
}
|
||||
ifc_close();
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes the default route for the named interface.
|
||||
*/
|
||||
|
@ -821,151 +702,3 @@ ifc_configure(const char *ifname,
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ifc_act_on_ipv6_route(int action, const char *ifname, struct in6_addr dst, int prefix_length,
|
||||
struct in6_addr gw)
|
||||
{
|
||||
struct in6_rtmsg rtmsg;
|
||||
int result;
|
||||
int ifindex;
|
||||
|
||||
memset(&rtmsg, 0, sizeof(rtmsg));
|
||||
|
||||
ifindex = if_nametoindex(ifname);
|
||||
if (ifindex == 0) {
|
||||
printerr("if_nametoindex() failed: interface %s\n", ifname);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
rtmsg.rtmsg_ifindex = ifindex;
|
||||
rtmsg.rtmsg_dst = dst;
|
||||
rtmsg.rtmsg_dst_len = prefix_length;
|
||||
rtmsg.rtmsg_flags = RTF_UP;
|
||||
|
||||
if (prefix_length == 128) {
|
||||
rtmsg.rtmsg_flags |= RTF_HOST;
|
||||
}
|
||||
|
||||
if (memcmp(&gw, &in6addr_any, sizeof(in6addr_any))) {
|
||||
rtmsg.rtmsg_flags |= RTF_GATEWAY;
|
||||
rtmsg.rtmsg_gateway = gw;
|
||||
}
|
||||
|
||||
ifc_init6();
|
||||
|
||||
if (ifc_ctl_sock6 < 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
result = ioctl(ifc_ctl_sock6, action, &rtmsg);
|
||||
if (result < 0) {
|
||||
if (errno == EEXIST) {
|
||||
result = 0;
|
||||
} else {
|
||||
result = -errno;
|
||||
}
|
||||
}
|
||||
ifc_close6();
|
||||
return result;
|
||||
}
|
||||
|
||||
int ifc_act_on_route(int action, const char *ifname, const char *dst, int prefix_length,
|
||||
const char *gw)
|
||||
{
|
||||
int ret = 0;
|
||||
struct sockaddr_in ipv4_dst, ipv4_gw;
|
||||
struct sockaddr_in6 ipv6_dst, ipv6_gw;
|
||||
struct addrinfo hints, *addr_ai, *gw_ai;
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
|
||||
hints.ai_flags = AI_NUMERICHOST;
|
||||
|
||||
ret = getaddrinfo(dst, NULL, &hints, &addr_ai);
|
||||
|
||||
if (ret != 0) {
|
||||
printerr("getaddrinfo failed: invalid address %s\n", dst);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (gw == NULL || (strlen(gw) == 0)) {
|
||||
if (addr_ai->ai_family == AF_INET6) {
|
||||
gw = "::";
|
||||
} else if (addr_ai->ai_family == AF_INET) {
|
||||
gw = "0.0.0.0";
|
||||
}
|
||||
}
|
||||
|
||||
if (((addr_ai->ai_family == AF_INET6) && (prefix_length < 0 || prefix_length > 128)) ||
|
||||
((addr_ai->ai_family == AF_INET) && (prefix_length < 0 || prefix_length > 32))) {
|
||||
printerr("ifc_add_route: invalid prefix length");
|
||||
freeaddrinfo(addr_ai);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = getaddrinfo(gw, NULL, &hints, &gw_ai);
|
||||
if (ret != 0) {
|
||||
printerr("getaddrinfo failed: invalid gateway %s\n", gw);
|
||||
freeaddrinfo(addr_ai);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (addr_ai->ai_family != gw_ai->ai_family) {
|
||||
printerr("ifc_add_route: different address families: %s and %s\n", dst, gw);
|
||||
freeaddrinfo(addr_ai);
|
||||
freeaddrinfo(gw_ai);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (addr_ai->ai_family == AF_INET6) {
|
||||
memcpy(&ipv6_dst, addr_ai->ai_addr, sizeof(struct sockaddr_in6));
|
||||
memcpy(&ipv6_gw, gw_ai->ai_addr, sizeof(struct sockaddr_in6));
|
||||
ret = ifc_act_on_ipv6_route(action, ifname, ipv6_dst.sin6_addr,
|
||||
prefix_length, ipv6_gw.sin6_addr);
|
||||
} else if (addr_ai->ai_family == AF_INET) {
|
||||
memcpy(&ipv4_dst, addr_ai->ai_addr, sizeof(struct sockaddr_in));
|
||||
memcpy(&ipv4_gw, gw_ai->ai_addr, sizeof(struct sockaddr_in));
|
||||
ret = ifc_act_on_ipv4_route(action, ifname, ipv4_dst.sin_addr,
|
||||
prefix_length, ipv4_gw.sin_addr);
|
||||
} else {
|
||||
printerr("ifc_add_route: getaddrinfo returned un supported address family %d\n",
|
||||
addr_ai->ai_family);
|
||||
ret = -EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
freeaddrinfo(addr_ai);
|
||||
freeaddrinfo(gw_ai);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* DEPRECATED
|
||||
*/
|
||||
int ifc_add_ipv4_route(const char *ifname, struct in_addr dst, int prefix_length,
|
||||
struct in_addr gw)
|
||||
{
|
||||
int i =ifc_act_on_ipv4_route(SIOCADDRT, ifname, dst, prefix_length, gw);
|
||||
if (DBG) printerr("ifc_add_ipv4_route(%s, xx, %d, xx) = %d", ifname, prefix_length, i);
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
* DEPRECATED
|
||||
*/
|
||||
int ifc_add_ipv6_route(const char *ifname, struct in6_addr dst, int prefix_length,
|
||||
struct in6_addr gw)
|
||||
{
|
||||
return ifc_act_on_ipv6_route(SIOCADDRT, ifname, dst, prefix_length, gw);
|
||||
}
|
||||
|
||||
int ifc_add_route(const char *ifname, const char *dst, int prefix_length, const char *gw)
|
||||
{
|
||||
int i = ifc_act_on_route(SIOCADDRT, ifname, dst, prefix_length, gw);
|
||||
if (DBG) printerr("ifc_add_route(%s, %s, %d, %s) = %d", ifname, dst, prefix_length, gw, i);
|
||||
return i;
|
||||
}
|
||||
|
||||
int ifc_remove_route(const char *ifname, const char*dst, int prefix_length, const char *gw)
|
||||
{
|
||||
return ifc_act_on_route(SIOCDELRT, ifname, dst, prefix_length, gw);
|
||||
}
|
||||
|
|
|
@ -230,6 +230,8 @@ int receive_packet(int s, struct dhcp_msg *msg)
|
|||
packet.udp.check = 0;
|
||||
sum = finish_sum(checksum(&packet, nread, 0));
|
||||
packet.udp.check = temp;
|
||||
if (!sum)
|
||||
sum = finish_sum(sum);
|
||||
if (temp != sum) {
|
||||
ALOGW("UDP header checksum failure (0x%x should be 0x%x)", sum, temp);
|
||||
return -1;
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := processgroup.cpp
|
||||
LOCAL_MODULE := libprocessgroup
|
||||
LOCAL_SHARED_LIBRARIES := liblog libutils
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
|
||||
LOCAL_CFLAGS := -Wall -Werror
|
||||
LOCAL_REQUIRED_MODULE := processgroup_cleanup
|
||||
include external/libcxx/libcxx.mk
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := cleanup.cpp
|
||||
LOCAL_MODULE := processgroup_cleanup
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
LOCAL_CFLAGS := -Wall -Werror
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
LOCAL_STATIC_LIBRARIES := libc libcutils
|
||||
include $(BUILD_EXECUTABLE)
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2014 Google, Inc
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syslimits.h>
|
||||
|
||||
#include "processgroup_priv.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
if (argc != 2)
|
||||
return -1;
|
||||
|
||||
memcpy(buf, PROCESSGROUP_CGROUP_PATH, sizeof(PROCESSGROUP_CGROUP_PATH));
|
||||
strlcat(buf, argv[1], sizeof(buf));
|
||||
return rmdir(buf);
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2014 Google, Inc
|
||||
*
|
||||
* 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 _PROCESSGROUP_H_
|
||||
#define _PROCESSGROUP_H_
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
int killProcessGroup(uid_t uid, int initialPid, int signal);
|
||||
|
||||
int createProcessGroup(uid_t uid, int initialPid);
|
||||
|
||||
void removeAllProcessGroups(void);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
|
@ -0,0 +1,341 @@
|
|||
/*
|
||||
* Copyright 2014 Google, Inc
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
#define LOG_TAG "libprocessgroup"
|
||||
|
||||
#include <assert.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <log/log.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
||||
#include <utils/SystemClock.h>
|
||||
|
||||
#include <processgroup/processgroup.h>
|
||||
#include "processgroup_priv.h"
|
||||
|
||||
struct ctx {
|
||||
bool initialized;
|
||||
int fd;
|
||||
char buf[128];
|
||||
char *buf_ptr;
|
||||
size_t buf_len;
|
||||
};
|
||||
|
||||
static int convertUidToPath(char *path, size_t size, uid_t uid)
|
||||
{
|
||||
return snprintf(path, size, "%s/%s%d",
|
||||
PROCESSGROUP_CGROUP_PATH,
|
||||
PROCESSGROUP_UID_PREFIX,
|
||||
uid);
|
||||
}
|
||||
|
||||
static int convertUidPidToPath(char *path, size_t size, uid_t uid, int pid)
|
||||
{
|
||||
return snprintf(path, size, "%s/%s%d/%s%d",
|
||||
PROCESSGROUP_CGROUP_PATH,
|
||||
PROCESSGROUP_UID_PREFIX,
|
||||
uid,
|
||||
PROCESSGROUP_PID_PREFIX,
|
||||
pid);
|
||||
}
|
||||
|
||||
static int initCtx(uid_t uid, int pid, struct ctx *ctx)
|
||||
{
|
||||
int ret;
|
||||
char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
|
||||
convertUidPidToPath(path, sizeof(path), uid, pid);
|
||||
strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
|
||||
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
ret = -errno;
|
||||
SLOGW("failed to open %s: %s", path, strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctx->fd = fd;
|
||||
ctx->buf_ptr = ctx->buf;
|
||||
ctx->buf_len = 0;
|
||||
ctx->initialized = true;
|
||||
|
||||
SLOGV("Initialized context for %s", path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int refillBuffer(struct ctx *ctx)
|
||||
{
|
||||
memmove(ctx->buf, ctx->buf_ptr, ctx->buf_len);
|
||||
ctx->buf_ptr = ctx->buf;
|
||||
|
||||
ssize_t ret = read(ctx->fd, ctx->buf_ptr + ctx->buf_len,
|
||||
sizeof(ctx->buf) - ctx->buf_len - 1);
|
||||
if (ret < 0) {
|
||||
return -errno;
|
||||
} else if (ret == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctx->buf_len += ret;
|
||||
ctx->buf[ctx->buf_len] = 0;
|
||||
SLOGV("Read %d to buffer: %s", ret, ctx->buf);
|
||||
|
||||
assert(ctx->buf_len <= sizeof(ctx->buf));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static pid_t getOneAppProcess(uid_t uid, int appProcessPid, struct ctx *ctx)
|
||||
{
|
||||
if (!ctx->initialized) {
|
||||
int ret = initCtx(uid, appProcessPid, ctx);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
char *eptr;
|
||||
while ((eptr = (char *)memchr(ctx->buf_ptr, '\n', ctx->buf_len)) == NULL) {
|
||||
int ret = refillBuffer(ctx);
|
||||
if (ret == 0) {
|
||||
return -ERANGE;
|
||||
}
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
*eptr = '\0';
|
||||
char *pid_eptr = NULL;
|
||||
errno = 0;
|
||||
long pid = strtol(ctx->buf_ptr, &pid_eptr, 10);
|
||||
if (errno != 0) {
|
||||
return -errno;
|
||||
}
|
||||
if (pid_eptr != eptr) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ctx->buf_len -= (eptr - ctx->buf_ptr) + 1;
|
||||
ctx->buf_ptr = eptr + 1;
|
||||
|
||||
return (pid_t)pid;
|
||||
}
|
||||
|
||||
static int removeProcessGroup(uid_t uid, int pid)
|
||||
{
|
||||
int ret;
|
||||
char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
|
||||
|
||||
convertUidPidToPath(path, sizeof(path), uid, pid);
|
||||
ret = rmdir(path);
|
||||
|
||||
convertUidToPath(path, sizeof(path), uid);
|
||||
rmdir(path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void removeUidProcessGroups(const char *uid_path)
|
||||
{
|
||||
DIR *uid = opendir(uid_path);
|
||||
if (uid != NULL) {
|
||||
struct dirent cur;
|
||||
struct dirent *dir;
|
||||
while ((readdir_r(uid, &cur, &dir) == 0) && dir) {
|
||||
char path[PROCESSGROUP_MAX_PATH_LEN];
|
||||
|
||||
if (dir->d_type != DT_DIR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strncmp(dir->d_name, PROCESSGROUP_PID_PREFIX, strlen(PROCESSGROUP_PID_PREFIX))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", uid_path, dir->d_name);
|
||||
SLOGV("removing %s\n", path);
|
||||
rmdir(path);
|
||||
}
|
||||
closedir(uid);
|
||||
}
|
||||
}
|
||||
|
||||
void removeAllProcessGroups()
|
||||
{
|
||||
SLOGV("removeAllProcessGroups()");
|
||||
DIR *root = opendir(PROCESSGROUP_CGROUP_PATH);
|
||||
if (root == NULL) {
|
||||
SLOGE("failed to open %s: %s", PROCESSGROUP_CGROUP_PATH, strerror(errno));
|
||||
} else {
|
||||
struct dirent cur;
|
||||
struct dirent *dir;
|
||||
while ((readdir_r(root, &cur, &dir) == 0) && dir) {
|
||||
char path[PROCESSGROUP_MAX_PATH_LEN];
|
||||
|
||||
if (dir->d_type != DT_DIR) {
|
||||
continue;
|
||||
}
|
||||
if (strncmp(dir->d_name, PROCESSGROUP_UID_PREFIX, strlen(PROCESSGROUP_UID_PREFIX))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", PROCESSGROUP_CGROUP_PATH, dir->d_name);
|
||||
removeUidProcessGroups(path);
|
||||
SLOGV("removing %s\n", path);
|
||||
rmdir(path);
|
||||
}
|
||||
closedir(root);
|
||||
}
|
||||
}
|
||||
|
||||
static int killProcessGroupOnce(uid_t uid, int initialPid, int signal)
|
||||
{
|
||||
int processes = 0;
|
||||
struct ctx ctx;
|
||||
pid_t pid;
|
||||
|
||||
ctx.initialized = false;
|
||||
|
||||
while ((pid = getOneAppProcess(uid, initialPid, &ctx)) >= 0) {
|
||||
processes++;
|
||||
if (pid == 0) {
|
||||
// Should never happen... but if it does, trying to kill this
|
||||
// will boomerang right back and kill us! Let's not let that happen.
|
||||
SLOGW("Yikes, we've been told to kill pid 0! How about we don't do that.");
|
||||
continue;
|
||||
}
|
||||
if (pid != initialPid) {
|
||||
// We want to be noisy about killing processes so we can understand
|
||||
// what is going on in the log; however, don't be noisy about the base
|
||||
// process, since that it something we always kill, and we have already
|
||||
// logged elsewhere about killing it.
|
||||
SLOGI("Killing pid %d in uid %d as part of process group %d", pid, uid, initialPid);
|
||||
}
|
||||
int ret = kill(pid, signal);
|
||||
if (ret == -1) {
|
||||
SLOGW("failed to kill pid %d: %s", pid, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.initialized) {
|
||||
close(ctx.fd);
|
||||
}
|
||||
|
||||
return processes;
|
||||
}
|
||||
|
||||
int killProcessGroup(uid_t uid, int initialPid, int signal)
|
||||
{
|
||||
int processes;
|
||||
int sleep_us = 100;
|
||||
long startTime = android::uptimeMillis();
|
||||
|
||||
while ((processes = killProcessGroupOnce(uid, initialPid, signal)) > 0) {
|
||||
SLOGV("killed %d processes for processgroup %d\n", processes, initialPid);
|
||||
if (sleep_us < 128000) {
|
||||
usleep(sleep_us);
|
||||
sleep_us *= 2;
|
||||
} else {
|
||||
SLOGE("failed to kill %d processes for processgroup %d\n",
|
||||
processes, initialPid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SLOGV("Killed process group uid %d pid %d in %ldms, %d procs remain", uid, initialPid,
|
||||
android::uptimeMillis()-startTime, processes);
|
||||
|
||||
if (processes == 0) {
|
||||
return removeProcessGroup(uid, initialPid);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int mkdirAndChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = mkdir(path, 0750);
|
||||
if (ret < 0 && errno != EEXIST) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
ret = chown(path, AID_SYSTEM, AID_SYSTEM);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
rmdir(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int createProcessGroup(uid_t uid, int initialPid)
|
||||
{
|
||||
char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
|
||||
int ret;
|
||||
|
||||
convertUidToPath(path, sizeof(path), uid);
|
||||
|
||||
ret = mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM);
|
||||
if (ret < 0) {
|
||||
SLOGE("failed to make and chown %s: %s", path, strerror(-ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
convertUidPidToPath(path, sizeof(path), uid, initialPid);
|
||||
|
||||
ret = mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM);
|
||||
if (ret < 0) {
|
||||
SLOGE("failed to make and chown %s: %s", path, strerror(-ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
|
||||
|
||||
int fd = open(path, O_WRONLY);
|
||||
if (fd < 0) {
|
||||
ret = -errno;
|
||||
SLOGE("failed to open %s: %s", path, strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
|
||||
char pid[PROCESSGROUP_MAX_PID_LEN + 1] = {0};
|
||||
int len = snprintf(pid, sizeof(pid), "%d", initialPid);
|
||||
|
||||
ret = write(fd, pid, len);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
SLOGE("failed to write '%s' to %s: %s", pid, path, strerror(errno));
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright 2014 Google, Inc
|
||||
*
|
||||
* 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 _PROCESSGROUP_PRIV_H_
|
||||
#define _PROCESSGROUP_PRIV_H_
|
||||
|
||||
#define PROCESSGROUP_CGROUP_PATH "/acct"
|
||||
#define PROCESSGROUP_UID_PREFIX "uid_"
|
||||
#define PROCESSGROUP_PID_PREFIX "pid_"
|
||||
#define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
|
||||
#define PROCESSGROUP_MAX_UID_LEN 11
|
||||
#define PROCESSGROUP_MAX_PID_LEN 11
|
||||
#define PROCESSGROUP_MAX_PATH_LEN \
|
||||
(sizeof(PROCESSGROUP_CGROUP_PATH) + \
|
||||
sizeof(PROCESSGROUP_UID_PREFIX) + 1 + \
|
||||
PROCESSGROUP_MAX_UID_LEN + \
|
||||
sizeof(PROCESSGROUP_PID_PREFIX) + 1 + \
|
||||
PROCESSGROUP_MAX_PID_LEN + \
|
||||
sizeof(PROCESSGROUP_CGROUP_PROCS_FILE) + \
|
||||
1)
|
||||
|
||||
#endif
|
|
@ -88,15 +88,18 @@ LOCAL_CFLAGS := -Werror
|
|||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
|
||||
ifneq ($(HOST_OS),windows)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := simg2simg.c
|
||||
LOCAL_MODULE := simg2simg
|
||||
LOCAL_SRC_FILES := append2simg.c
|
||||
LOCAL_MODULE := append2simg
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libsparse_host \
|
||||
libz
|
||||
LOCAL_CFLAGS := -Werror
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
||||
endif
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := simg_dump.py
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Copyright (C) 2013 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.
|
||||
*/
|
||||
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#define _LARGEFILE64_SOURCE 1
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sparse/sparse.h>
|
||||
#include "sparse_file.h"
|
||||
#include "backed_block.h"
|
||||
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#define lseek64 lseek
|
||||
#endif
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#define lseek64 lseek
|
||||
#define off64_t off_t
|
||||
#endif
|
||||
|
||||
void usage()
|
||||
{
|
||||
fprintf(stderr, "Usage: append2simg <output> <input>\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int output;
|
||||
int output_block;
|
||||
char *output_path;
|
||||
struct sparse_file *sparse_output;
|
||||
|
||||
int input;
|
||||
char *input_path;
|
||||
off64_t input_len;
|
||||
|
||||
int tmp_fd;
|
||||
char *tmp_path;
|
||||
|
||||
int ret;
|
||||
|
||||
if (argc == 3) {
|
||||
output_path = argv[1];
|
||||
input_path = argv[2];
|
||||
} else {
|
||||
usage();
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
ret = asprintf(&tmp_path, "%s.append2simg", output_path);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Couldn't allocate filename\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
output = open(output_path, O_RDWR | O_BINARY);
|
||||
if (output < 0) {
|
||||
fprintf(stderr, "Couldn't open output file (%s)\n", strerror(errno));
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
sparse_output = sparse_file_import_auto(output, true);
|
||||
if (!sparse_output) {
|
||||
fprintf(stderr, "Couldn't import output file\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
input = open(input_path, O_RDONLY | O_BINARY);
|
||||
if (input < 0) {
|
||||
fprintf(stderr, "Couldn't open input file (%s)\n", strerror(errno));
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
input_len = lseek64(input, 0, SEEK_END);
|
||||
if (input_len < 0) {
|
||||
fprintf(stderr, "Couldn't get input file length (%s)\n", strerror(errno));
|
||||
exit(-1);
|
||||
} else if (input_len % sparse_output->block_size) {
|
||||
fprintf(stderr, "Input file is not a multiple of the output file's block size");
|
||||
exit(-1);
|
||||
}
|
||||
lseek64(input, 0, SEEK_SET);
|
||||
|
||||
output_block = sparse_output->len / sparse_output->block_size;
|
||||
if (sparse_file_add_fd(sparse_output, input, 0, input_len, output_block) < 0) {
|
||||
fprintf(stderr, "Couldn't add input file\n");
|
||||
exit(-1);
|
||||
}
|
||||
sparse_output->len += input_len;
|
||||
|
||||
tmp_fd = open(tmp_path, O_WRONLY | O_CREAT | O_BINARY, 0664);
|
||||
if (tmp_fd < 0) {
|
||||
fprintf(stderr, "Couldn't open temporary file (%s)\n", strerror(errno));
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
lseek64(output, 0, SEEK_SET);
|
||||
if (sparse_file_write(sparse_output, tmp_fd, false, true, false) < 0) {
|
||||
fprintf(stderr, "Failed to write sparse file\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
sparse_file_destroy(sparse_output);
|
||||
close(tmp_fd);
|
||||
close(output);
|
||||
close(input);
|
||||
|
||||
ret = rename(tmp_path, output_path);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Failed to rename temporary file (%s)\n", strerror(errno));
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
free(tmp_path);
|
||||
|
||||
exit(0);
|
||||
}
|
|
@ -38,10 +38,13 @@ static int autosuspend_init(void)
|
|||
goto out;
|
||||
}
|
||||
|
||||
/* Remove autosleep so userspace can manager suspend/resume and keep stats */
|
||||
#if 0
|
||||
autosuspend_ops = autosuspend_autosleep_init();
|
||||
if (autosuspend_ops) {
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
autosuspend_ops = autosuspend_wakeup_count_init();
|
||||
if (autosuspend_ops) {
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#define LOG_TAG "libsuspend"
|
||||
//#define LOG_NDEBUG 0
|
||||
#include <cutils/log.h>
|
||||
|
||||
#include "autosuspend_ops.h"
|
||||
|
@ -37,6 +38,7 @@ static int wakeup_count_fd;
|
|||
static pthread_t suspend_thread;
|
||||
static sem_t suspend_lockout;
|
||||
static const char *sleep_state = "mem";
|
||||
static void (*wakeup_func)(void) = NULL;
|
||||
|
||||
static void *suspend_thread_func(void *arg __attribute__((unused)))
|
||||
{
|
||||
|
@ -80,6 +82,11 @@ static void *suspend_thread_func(void *arg __attribute__((unused)))
|
|||
if (ret < 0) {
|
||||
strerror_r(errno, buf, sizeof(buf));
|
||||
ALOGE("Error writing to %s: %s\n", SYS_POWER_STATE, buf);
|
||||
} else {
|
||||
void (*func)(void) = wakeup_func;
|
||||
if (func != NULL) {
|
||||
(*func)();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,6 +138,15 @@ static int autosuspend_wakeup_count_disable(void)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void set_wakeup_callback(void (*func)(void))
|
||||
{
|
||||
if (wakeup_func != NULL) {
|
||||
ALOGE("Duplicate wakeup callback applied, keeping original");
|
||||
return;
|
||||
}
|
||||
wakeup_func = func;
|
||||
}
|
||||
|
||||
struct autosuspend_ops autosuspend_wakeup_count_ops = {
|
||||
.enable = autosuspend_wakeup_count_enable,
|
||||
.disable = autosuspend_wakeup_count_disable,
|
||||
|
|
|
@ -43,6 +43,13 @@ int autosuspend_enable(void);
|
|||
*/
|
||||
int autosuspend_disable(void);
|
||||
|
||||
/*
|
||||
* set_wakeup_callback
|
||||
*
|
||||
* Set a function to be called each time the device wakes up from suspend.
|
||||
*/
|
||||
void set_wakeup_callback(void (*func)(void));
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include <net/if.h>
|
||||
|
||||
#include <linux/if.h>
|
||||
#include <linux/if_addr.h>
|
||||
#include <linux/if_link.h>
|
||||
#include <linux/netfilter/nfnetlink.h>
|
||||
#include <linux/netfilter_ipv4/ipt_ULOG.h>
|
||||
/* From kernel's net/netfilter/xt_quota2.c */
|
||||
|
@ -46,6 +48,8 @@ const int NetlinkEvent::NlActionLinkDown = 5;
|
|||
const int NetlinkEvent::NlActionAddressUpdated = 6;
|
||||
const int NetlinkEvent::NlActionAddressRemoved = 7;
|
||||
const int NetlinkEvent::NlActionRdnss = 8;
|
||||
const int NetlinkEvent::NlActionRouteUpdated = 9;
|
||||
const int NetlinkEvent::NlActionRouteRemoved = 10;
|
||||
|
||||
NetlinkEvent::NetlinkEvent() {
|
||||
mAction = NlActionUnknown;
|
||||
|
@ -77,33 +81,110 @@ void NetlinkEvent::dump() {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the message name for a message in the NETLINK_ROUTE family, or NULL
|
||||
* if parsing that message is not supported.
|
||||
*/
|
||||
static const char *rtMessageName(int type) {
|
||||
#define NL_EVENT_RTM_NAME(rtm) case rtm: return #rtm;
|
||||
switch (type) {
|
||||
NL_EVENT_RTM_NAME(RTM_NEWLINK);
|
||||
NL_EVENT_RTM_NAME(RTM_DELLINK);
|
||||
NL_EVENT_RTM_NAME(RTM_NEWADDR);
|
||||
NL_EVENT_RTM_NAME(RTM_DELADDR);
|
||||
NL_EVENT_RTM_NAME(RTM_NEWROUTE);
|
||||
NL_EVENT_RTM_NAME(RTM_DELROUTE);
|
||||
NL_EVENT_RTM_NAME(RTM_NEWNDUSEROPT);
|
||||
NL_EVENT_RTM_NAME(QLOG_NL_EVENT);
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
#undef NL_EVENT_RTM_NAME
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks that a binary NETLINK_ROUTE message is long enough for a payload of
|
||||
* size bytes.
|
||||
*/
|
||||
static bool checkRtNetlinkLength(const struct nlmsghdr *nh, size_t size) {
|
||||
if (nh->nlmsg_len < NLMSG_LENGTH(size)) {
|
||||
SLOGE("Got a short %s message\n", rtMessageName(nh->nlmsg_type));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility function to log errors.
|
||||
*/
|
||||
static bool maybeLogDuplicateAttribute(bool isDup,
|
||||
const char *attributeName,
|
||||
const char *messageName) {
|
||||
if (isDup) {
|
||||
SLOGE("Multiple %s attributes in %s, ignoring\n", attributeName, messageName);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a RTM_NEWLINK message.
|
||||
*/
|
||||
bool NetlinkEvent::parseIfInfoMessage(const struct nlmsghdr *nh) {
|
||||
struct ifinfomsg *ifi = (struct ifinfomsg *) NLMSG_DATA(nh);
|
||||
if (!checkRtNetlinkLength(nh, sizeof(*ifi)))
|
||||
return false;
|
||||
|
||||
if ((ifi->ifi_flags & IFF_LOOPBACK) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int len = IFLA_PAYLOAD(nh);
|
||||
struct rtattr *rta;
|
||||
for (rta = IFLA_RTA(ifi); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
|
||||
switch(rta->rta_type) {
|
||||
case IFLA_IFNAME:
|
||||
asprintf(&mParams[0], "INTERFACE=%s", (char *) RTA_DATA(rta));
|
||||
mAction = (ifi->ifi_flags & IFF_LOWER_UP) ? NlActionLinkUp :
|
||||
NlActionLinkDown;
|
||||
mSubsystem = strdup("net");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a RTM_NEWADDR or RTM_DELADDR message.
|
||||
*/
|
||||
bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr,
|
||||
int rtasize) {
|
||||
struct rtattr *rta;
|
||||
bool NetlinkEvent::parseIfAddrMessage(const struct nlmsghdr *nh) {
|
||||
struct ifaddrmsg *ifaddr = (struct ifaddrmsg *) NLMSG_DATA(nh);
|
||||
struct ifa_cacheinfo *cacheinfo = NULL;
|
||||
char addrstr[INET6_ADDRSTRLEN] = "";
|
||||
char ifname[IFNAMSIZ];
|
||||
|
||||
if (!checkRtNetlinkLength(nh, sizeof(*ifaddr)))
|
||||
return false;
|
||||
|
||||
// Sanity check.
|
||||
int type = nh->nlmsg_type;
|
||||
if (type != RTM_NEWADDR && type != RTM_DELADDR) {
|
||||
SLOGE("parseIfAddrMessage on incorrect message type 0x%x\n", type);
|
||||
return false;
|
||||
}
|
||||
|
||||
// For log messages.
|
||||
const char *msgtype = (type == RTM_NEWADDR) ? "RTM_NEWADDR" : "RTM_DELADDR";
|
||||
const char *msgtype = rtMessageName(type);
|
||||
|
||||
for (rta = IFA_RTA(ifaddr); RTA_OK(rta, rtasize);
|
||||
rta = RTA_NEXT(rta, rtasize)) {
|
||||
struct rtattr *rta;
|
||||
int len = IFA_PAYLOAD(nh);
|
||||
for (rta = IFA_RTA(ifaddr); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
|
||||
if (rta->rta_type == IFA_ADDRESS) {
|
||||
// Only look at the first address, because we only support notifying
|
||||
// one change at a time.
|
||||
if (*addrstr != '\0') {
|
||||
SLOGE("Multiple IFA_ADDRESSes in %s, ignoring\n", msgtype);
|
||||
if (maybeLogDuplicateAttribute(*addrstr != '\0', "IFA_ADDRESS", msgtype))
|
||||
continue;
|
||||
}
|
||||
|
||||
// Convert the IP address to a string.
|
||||
if (ifaddr->ifa_family == AF_INET) {
|
||||
|
@ -128,28 +209,15 @@ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr,
|
|||
}
|
||||
|
||||
// Find the interface name.
|
||||
char ifname[IFNAMSIZ + 1];
|
||||
if (!if_indextoname(ifaddr->ifa_index, ifname)) {
|
||||
SLOGE("Unknown ifindex %d in %s", ifaddr->ifa_index, msgtype);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fill in interface information.
|
||||
mAction = (type == RTM_NEWADDR) ? NlActionAddressUpdated :
|
||||
NlActionAddressRemoved;
|
||||
mSubsystem = strdup("net");
|
||||
asprintf(&mParams[0], "ADDRESS=%s/%d", addrstr,
|
||||
ifaddr->ifa_prefixlen);
|
||||
asprintf(&mParams[1], "INTERFACE=%s", ifname);
|
||||
asprintf(&mParams[2], "FLAGS=%u", ifaddr->ifa_flags);
|
||||
asprintf(&mParams[3], "SCOPE=%u", ifaddr->ifa_scope);
|
||||
} else if (rta->rta_type == IFA_CACHEINFO) {
|
||||
// Address lifetime information.
|
||||
if (cacheinfo) {
|
||||
// We only support one address.
|
||||
SLOGE("Multiple IFA_CACHEINFOs in %s, ignoring\n", msgtype);
|
||||
if (maybeLogDuplicateAttribute(cacheinfo, "IFA_CACHEINFO", msgtype))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (RTA_PAYLOAD(rta) < sizeof(*cacheinfo)) {
|
||||
SLOGE("Short IFA_CACHEINFO (%zu vs. %zu bytes) in %s",
|
||||
|
@ -158,10 +226,6 @@ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr,
|
|||
}
|
||||
|
||||
cacheinfo = (struct ifa_cacheinfo *) RTA_DATA(rta);
|
||||
asprintf(&mParams[4], "PREFERRED=%u", cacheinfo->ifa_prefered);
|
||||
asprintf(&mParams[5], "VALID=%u", cacheinfo->ifa_valid);
|
||||
asprintf(&mParams[6], "CSTAMP=%u", cacheinfo->cstamp);
|
||||
asprintf(&mParams[7], "TSTAMP=%u", cacheinfo->tstamp);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,14 +234,145 @@ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr,
|
|||
return false;
|
||||
}
|
||||
|
||||
// Fill in netlink event information.
|
||||
mAction = (type == RTM_NEWADDR) ? NlActionAddressUpdated :
|
||||
NlActionAddressRemoved;
|
||||
mSubsystem = strdup("net");
|
||||
asprintf(&mParams[0], "ADDRESS=%s/%d", addrstr,
|
||||
ifaddr->ifa_prefixlen);
|
||||
asprintf(&mParams[1], "INTERFACE=%s", ifname);
|
||||
asprintf(&mParams[2], "FLAGS=%u", ifaddr->ifa_flags);
|
||||
asprintf(&mParams[3], "SCOPE=%u", ifaddr->ifa_scope);
|
||||
|
||||
if (cacheinfo) {
|
||||
asprintf(&mParams[4], "PREFERRED=%u", cacheinfo->ifa_prefered);
|
||||
asprintf(&mParams[5], "VALID=%u", cacheinfo->ifa_valid);
|
||||
asprintf(&mParams[6], "CSTAMP=%u", cacheinfo->cstamp);
|
||||
asprintf(&mParams[7], "TSTAMP=%u", cacheinfo->tstamp);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a QLOG_NL_EVENT message.
|
||||
*/
|
||||
bool NetlinkEvent::parseUlogPacketMessage(const struct nlmsghdr *nh) {
|
||||
const char *devname;
|
||||
ulog_packet_msg_t *pm = (ulog_packet_msg_t *) NLMSG_DATA(nh);
|
||||
if (!checkRtNetlinkLength(nh, sizeof(*pm)))
|
||||
return false;
|
||||
|
||||
devname = pm->indev_name[0] ? pm->indev_name : pm->outdev_name;
|
||||
asprintf(&mParams[0], "ALERT_NAME=%s", pm->prefix);
|
||||
asprintf(&mParams[1], "INTERFACE=%s", devname);
|
||||
mSubsystem = strdup("qlog");
|
||||
mAction = NlActionChange;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a RTM_NEWROUTE or RTM_DELROUTE message.
|
||||
*/
|
||||
bool NetlinkEvent::parseRtMessage(const struct nlmsghdr *nh) {
|
||||
uint8_t type = nh->nlmsg_type;
|
||||
const char *msgname = rtMessageName(type);
|
||||
|
||||
// Sanity check.
|
||||
if (type != RTM_NEWROUTE && type != RTM_DELROUTE) {
|
||||
SLOGE("%s: incorrect message type %d (%s)\n", __func__, type, msgname);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct rtmsg *rtm = (struct rtmsg *) NLMSG_DATA(nh);
|
||||
if (!checkRtNetlinkLength(nh, sizeof(*rtm)))
|
||||
return false;
|
||||
|
||||
if (// Ignore static routes we've set up ourselves.
|
||||
(rtm->rtm_protocol != RTPROT_KERNEL &&
|
||||
rtm->rtm_protocol != RTPROT_RA) ||
|
||||
// We're only interested in global unicast routes.
|
||||
(rtm->rtm_scope != RT_SCOPE_UNIVERSE) ||
|
||||
(rtm->rtm_type != RTN_UNICAST) ||
|
||||
// We don't support source routing.
|
||||
(rtm->rtm_src_len != 0) ||
|
||||
// Cloned routes aren't real routes.
|
||||
(rtm->rtm_flags & RTM_F_CLONED)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int family = rtm->rtm_family;
|
||||
int prefixLength = rtm->rtm_dst_len;
|
||||
|
||||
// Currently we only support: destination, (one) next hop, ifindex.
|
||||
char dst[INET6_ADDRSTRLEN] = "";
|
||||
char gw[INET6_ADDRSTRLEN] = "";
|
||||
char dev[IFNAMSIZ] = "";
|
||||
|
||||
size_t len = RTM_PAYLOAD(nh);
|
||||
struct rtattr *rta;
|
||||
for (rta = RTM_RTA(rtm); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
|
||||
switch (rta->rta_type) {
|
||||
case RTA_DST:
|
||||
if (maybeLogDuplicateAttribute(*dst, "RTA_DST", msgname))
|
||||
continue;
|
||||
if (!inet_ntop(family, RTA_DATA(rta), dst, sizeof(dst)))
|
||||
return false;
|
||||
continue;
|
||||
case RTA_GATEWAY:
|
||||
if (maybeLogDuplicateAttribute(*gw, "RTA_GATEWAY", msgname))
|
||||
continue;
|
||||
if (!inet_ntop(family, RTA_DATA(rta), gw, sizeof(gw)))
|
||||
return false;
|
||||
continue;
|
||||
case RTA_OIF:
|
||||
if (maybeLogDuplicateAttribute(*dev, "RTA_OIF", msgname))
|
||||
continue;
|
||||
if (!if_indextoname(* (int *) RTA_DATA(rta), dev))
|
||||
return false;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If there's no RTA_DST attribute, then:
|
||||
// - If the prefix length is zero, it's the default route.
|
||||
// - If the prefix length is nonzero, there's something we don't understand.
|
||||
// Ignore the event.
|
||||
if (!*dst && !prefixLength) {
|
||||
if (family == AF_INET) {
|
||||
strncpy(dst, "0.0.0.0", sizeof(dst));
|
||||
} else if (family == AF_INET6) {
|
||||
strncpy(dst, "::", sizeof(dst));
|
||||
}
|
||||
}
|
||||
|
||||
// A useful route must have a destination and at least either a gateway or
|
||||
// an interface.
|
||||
if (!*dst || (!*gw && !*dev))
|
||||
return false;
|
||||
|
||||
// Fill in netlink event information.
|
||||
mAction = (type == RTM_NEWROUTE) ? NlActionRouteUpdated :
|
||||
NlActionRouteRemoved;
|
||||
mSubsystem = strdup("net");
|
||||
asprintf(&mParams[0], "ROUTE=%s/%d", dst, prefixLength);
|
||||
asprintf(&mParams[1], "GATEWAY=%s", (*gw) ? gw : "");
|
||||
asprintf(&mParams[2], "INTERFACE=%s", (*dev) ? dev : "");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a RTM_NEWNDUSEROPT message.
|
||||
*/
|
||||
bool NetlinkEvent::parseNdUserOptMessage(struct nduseroptmsg *msg, int len) {
|
||||
bool NetlinkEvent::parseNdUserOptMessage(const struct nlmsghdr *nh) {
|
||||
struct nduseroptmsg *msg = (struct nduseroptmsg *) NLMSG_DATA(nh);
|
||||
if (!checkRtNetlinkLength(nh, sizeof(*msg)))
|
||||
return false;
|
||||
|
||||
// Check the length is valid.
|
||||
int len = NLMSG_PAYLOAD(nh, sizeof(*msg));
|
||||
if (msg->nduseropt_opts_len > len) {
|
||||
SLOGE("RTM_NEWNDUSEROPT invalid length %d > %d\n",
|
||||
msg->nduseropt_opts_len, len);
|
||||
|
@ -200,7 +395,7 @@ bool NetlinkEvent::parseNdUserOptMessage(struct nduseroptmsg *msg, int len) {
|
|||
}
|
||||
|
||||
// Find the interface name.
|
||||
char ifname[IFNAMSIZ + 1];
|
||||
char ifname[IFNAMSIZ];
|
||||
if (!if_indextoname(msg->nduseropt_ifindex, ifname)) {
|
||||
SLOGE("RTM_NEWNDUSEROPT on unknown ifindex %d\n",
|
||||
msg->nduseropt_ifindex);
|
||||
|
@ -273,6 +468,14 @@ bool NetlinkEvent::parseNdUserOptMessage(struct nduseroptmsg *msg, int len) {
|
|||
|
||||
/*
|
||||
* Parse a binary message from a NETLINK_ROUTE netlink socket.
|
||||
*
|
||||
* Note that this function can only parse one message, because the message's
|
||||
* content has to be stored in the class's member variables (mAction,
|
||||
* mSubsystem, etc.). Invalid or unrecognized messages are skipped, but if
|
||||
* there are multiple valid messages in the buffer, only the first one will be
|
||||
* returned.
|
||||
*
|
||||
* TODO: consider only ever looking at the first message.
|
||||
*/
|
||||
bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) {
|
||||
const struct nlmsghdr *nh;
|
||||
|
@ -281,93 +484,37 @@ bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) {
|
|||
NLMSG_OK(nh, (unsigned) size) && (nh->nlmsg_type != NLMSG_DONE);
|
||||
nh = NLMSG_NEXT(nh, size)) {
|
||||
|
||||
if (!rtMessageName(nh->nlmsg_type)) {
|
||||
SLOGD("Unexpected netlink message type %d\n", nh->nlmsg_type);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nh->nlmsg_type == RTM_NEWLINK) {
|
||||
int len = nh->nlmsg_len - sizeof(*nh);
|
||||
struct ifinfomsg *ifi;
|
||||
|
||||
if (sizeof(*ifi) > (size_t) len) {
|
||||
SLOGE("Got a short RTM_NEWLINK message\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
ifi = (ifinfomsg *)NLMSG_DATA(nh);
|
||||
if ((ifi->ifi_flags & IFF_LOOPBACK) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct rtattr *rta = (struct rtattr *)
|
||||
((char *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
|
||||
len = NLMSG_PAYLOAD(nh, sizeof(*ifi));
|
||||
|
||||
while(RTA_OK(rta, len)) {
|
||||
switch(rta->rta_type) {
|
||||
case IFLA_IFNAME:
|
||||
char buffer[16 + IFNAMSIZ];
|
||||
snprintf(buffer, sizeof(buffer), "INTERFACE=%s",
|
||||
(char *) RTA_DATA(rta));
|
||||
mParams[0] = strdup(buffer);
|
||||
mAction = (ifi->ifi_flags & IFF_LOWER_UP) ?
|
||||
NlActionLinkUp : NlActionLinkDown;
|
||||
mSubsystem = strdup("net");
|
||||
break;
|
||||
}
|
||||
|
||||
rta = RTA_NEXT(rta, len);
|
||||
}
|
||||
if (parseIfInfoMessage(nh))
|
||||
return true;
|
||||
|
||||
} else if (nh->nlmsg_type == QLOG_NL_EVENT) {
|
||||
char *devname;
|
||||
ulog_packet_msg_t *pm;
|
||||
size_t len = nh->nlmsg_len - sizeof(*nh);
|
||||
if (sizeof(*pm) > len) {
|
||||
SLOGE("Got a short QLOG message\n");
|
||||
continue;
|
||||
}
|
||||
pm = (ulog_packet_msg_t *)NLMSG_DATA(nh);
|
||||
devname = pm->indev_name[0] ? pm->indev_name : pm->outdev_name;
|
||||
asprintf(&mParams[0], "ALERT_NAME=%s", pm->prefix);
|
||||
asprintf(&mParams[1], "INTERFACE=%s", devname);
|
||||
mSubsystem = strdup("qlog");
|
||||
mAction = NlActionChange;
|
||||
if (parseUlogPacketMessage(nh))
|
||||
return true;
|
||||
|
||||
} else if (nh->nlmsg_type == RTM_NEWADDR ||
|
||||
nh->nlmsg_type == RTM_DELADDR) {
|
||||
int len = nh->nlmsg_len - sizeof(*nh);
|
||||
struct ifaddrmsg *ifa;
|
||||
if (parseIfAddrMessage(nh))
|
||||
return true;
|
||||
|
||||
if (sizeof(*ifa) > (size_t) len) {
|
||||
SLOGE("Got a short RTM_xxxADDR message\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
ifa = (ifaddrmsg *)NLMSG_DATA(nh);
|
||||
size_t rtasize = IFA_PAYLOAD(nh);
|
||||
if (!parseIfAddrMessage(nh->nlmsg_type, ifa, rtasize)) {
|
||||
continue;
|
||||
}
|
||||
} else if (nh->nlmsg_type == RTM_NEWROUTE ||
|
||||
nh->nlmsg_type == RTM_DELROUTE) {
|
||||
if (parseRtMessage(nh))
|
||||
return true;
|
||||
|
||||
} else if (nh->nlmsg_type == RTM_NEWNDUSEROPT) {
|
||||
int len = nh->nlmsg_len - sizeof(*nh);
|
||||
struct nduseroptmsg *ndmsg = (struct nduseroptmsg *) NLMSG_DATA(nh);
|
||||
if (parseNdUserOptMessage(nh))
|
||||
return true;
|
||||
|
||||
if (sizeof(*ndmsg) > (size_t) len) {
|
||||
SLOGE("Got a short RTM_NEWNDUSEROPT message\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t optsize = NLMSG_PAYLOAD(nh, sizeof(*ndmsg));
|
||||
if (!parseNdUserOptMessage(ndmsg, optsize)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
SLOGD("Unexpected netlink message. type=0x%x\n",
|
||||
nh->nlmsg_type);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If the string between 'str' and 'end' begins with 'prefixlen' characters
|
||||
|
|
|
@ -57,10 +57,12 @@ bool NetlinkListener::onDataAvailable(SocketClient *cli)
|
|||
}
|
||||
|
||||
NetlinkEvent *evt = new NetlinkEvent();
|
||||
if (!evt->decode(mBuffer, count, mFormat)) {
|
||||
SLOGE("Error decoding NetlinkEvent");
|
||||
} else {
|
||||
if (evt->decode(mBuffer, count, mFormat)) {
|
||||
onEvent(evt);
|
||||
} else if (mFormat != NETLINK_FORMAT_BINARY) {
|
||||
// Don't complain if parseBinaryNetlinkMessage returns false. That can
|
||||
// just mean that the buffer contained no messages we're interested in.
|
||||
SLOGE("Error decoding NetlinkEvent");
|
||||
}
|
||||
|
||||
delete evt;
|
||||
|
|
|
@ -454,6 +454,8 @@ char* usb_device_get_string(struct usb_device *device, int id)
|
|||
int i, result;
|
||||
int languageCount = 0;
|
||||
|
||||
if (id == 0) return NULL;
|
||||
|
||||
string[0] = 0;
|
||||
memset(languages, 0, sizeof(languages));
|
||||
|
||||
|
@ -487,31 +489,19 @@ char* usb_device_get_string(struct usb_device *device, int id)
|
|||
char* usb_device_get_manufacturer_name(struct usb_device *device)
|
||||
{
|
||||
struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
|
||||
|
||||
if (desc->iManufacturer)
|
||||
return usb_device_get_string(device, desc->iManufacturer);
|
||||
else
|
||||
return NULL;
|
||||
return usb_device_get_string(device, desc->iManufacturer);
|
||||
}
|
||||
|
||||
char* usb_device_get_product_name(struct usb_device *device)
|
||||
{
|
||||
struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
|
||||
|
||||
if (desc->iProduct)
|
||||
return usb_device_get_string(device, desc->iProduct);
|
||||
else
|
||||
return NULL;
|
||||
return usb_device_get_string(device, desc->iProduct);
|
||||
}
|
||||
|
||||
char* usb_device_get_serial(struct usb_device *device)
|
||||
{
|
||||
struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
|
||||
|
||||
if (desc->iSerialNumber)
|
||||
return usb_device_get_string(device, desc->iSerialNumber);
|
||||
else
|
||||
return NULL;
|
||||
return usb_device_get_string(device, desc->iSerialNumber);
|
||||
}
|
||||
|
||||
int usb_device_is_writeable(struct usb_device *device)
|
||||
|
@ -557,6 +547,21 @@ int usb_device_connect_kernel_driver(struct usb_device *device,
|
|||
return ioctl(device->fd, USBDEVFS_IOCTL, &ctl);
|
||||
}
|
||||
|
||||
int usb_device_set_configuration(struct usb_device *device, int configuration)
|
||||
{
|
||||
return ioctl(device->fd, USBDEVFS_SETCONFIGURATION, &configuration);
|
||||
}
|
||||
|
||||
int usb_device_set_interface(struct usb_device *device, unsigned int interface,
|
||||
unsigned int alt_setting)
|
||||
{
|
||||
struct usbdevfs_setinterface ctl;
|
||||
|
||||
ctl.interface = interface;
|
||||
ctl.altsetting = alt_setting;
|
||||
return ioctl(device->fd, USBDEVFS_SETINTERFACE, &ctl);
|
||||
}
|
||||
|
||||
int usb_device_control_transfer(struct usb_device *device,
|
||||
int requestType,
|
||||
int request,
|
||||
|
|
|
@ -26,6 +26,7 @@ commonSources:= \
|
|||
LinearAllocator.cpp \
|
||||
LinearTransform.cpp \
|
||||
Log.cpp \
|
||||
NativeHandle.cpp \
|
||||
Printer.cpp \
|
||||
ProcessCallStack.cpp \
|
||||
PropertyMap.cpp \
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace android {
|
|||
static const uint32_t blobCacheMagic = ('_' << 24) + ('B' << 16) + ('b' << 8) + '$';
|
||||
|
||||
// BlobCache::Header::mBlobCacheVersion value
|
||||
static const uint32_t blobCacheVersion = 1;
|
||||
static const uint32_t blobCacheVersion = 2;
|
||||
|
||||
// BlobCache::Header::mDeviceVersion value
|
||||
static const uint32_t blobCacheDeviceVersion = 1;
|
||||
|
@ -165,14 +165,13 @@ static inline size_t align4(size_t size) {
|
|||
}
|
||||
|
||||
size_t BlobCache::getFlattenedSize() const {
|
||||
size_t size = sizeof(Header);
|
||||
size_t size = align4(sizeof(Header));
|
||||
for (size_t i = 0; i < mCacheEntries.size(); i++) {
|
||||
const CacheEntry& e(mCacheEntries[i]);
|
||||
sp<Blob> keyBlob = e.getKey();
|
||||
sp<Blob> valueBlob = e.getValue();
|
||||
size = align4(size);
|
||||
size += sizeof(EntryHeader) + keyBlob->getSize() +
|
||||
valueBlob->getSize();
|
||||
size += align4(sizeof(EntryHeader) + keyBlob->getSize() +
|
||||
valueBlob->getSize());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
@ -200,7 +199,8 @@ status_t BlobCache::flatten(void* buffer, size_t size) const {
|
|||
size_t valueSize = valueBlob->getSize();
|
||||
|
||||
size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
|
||||
if (byteOffset + entrySize > size) {
|
||||
size_t totalSize = align4(entrySize);
|
||||
if (byteOffset + totalSize > size) {
|
||||
ALOGE("flatten: not enough room for cache entries");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
@ -213,7 +213,13 @@ status_t BlobCache::flatten(void* buffer, size_t size) const {
|
|||
memcpy(eheader->mData, keyBlob->getData(), keySize);
|
||||
memcpy(eheader->mData + keySize, valueBlob->getData(), valueSize);
|
||||
|
||||
byteOffset += align4(entrySize);
|
||||
if (totalSize > entrySize) {
|
||||
// We have padding bytes. Those will get written to storage, and contribute to the CRC,
|
||||
// so make sure we zero-them to have reproducible results.
|
||||
memset(eheader->mData + keySize + valueSize, 0, totalSize - entrySize);
|
||||
}
|
||||
|
||||
byteOffset += totalSize;
|
||||
}
|
||||
|
||||
return OK;
|
||||
|
@ -256,7 +262,8 @@ status_t BlobCache::unflatten(void const* buffer, size_t size) {
|
|||
size_t valueSize = eheader->mValueSize;
|
||||
size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
|
||||
|
||||
if (byteOffset + entrySize > size) {
|
||||
size_t totalSize = align4(entrySize);
|
||||
if (byteOffset + totalSize > size) {
|
||||
mCacheEntries.clear();
|
||||
ALOGE("unflatten: not enough room for cache entry headers");
|
||||
return BAD_VALUE;
|
||||
|
@ -265,7 +272,7 @@ status_t BlobCache::unflatten(void const* buffer, size_t size) {
|
|||
const uint8_t* data = eheader->mData;
|
||||
set(data, keySize, data + keySize, valueSize);
|
||||
|
||||
byteOffset += align4(entrySize);
|
||||
byteOffset += totalSize;
|
||||
}
|
||||
|
||||
return OK;
|
||||
|
|
|
@ -77,7 +77,6 @@ FileMap::~FileMap(void)
|
|||
if (mFileMapping != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(mFileMapping);
|
||||
}
|
||||
CloseHandle(mFileHandle);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2014 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/NativeHandle.h>
|
||||
#include <cutils/native_handle.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
sp<NativeHandle> NativeHandle::create(
|
||||
native_handle_t* handle, bool ownsHandle) {
|
||||
return handle ? new NativeHandle(handle, ownsHandle) : NULL;
|
||||
}
|
||||
|
||||
NativeHandle::NativeHandle(native_handle_t* handle, bool ownsHandle)
|
||||
: mHandle(handle), mOwnsHandle(ownsHandle)
|
||||
{}
|
||||
|
||||
NativeHandle::~NativeHandle() {
|
||||
if (mOwnsHandle) {
|
||||
native_handle_close(mHandle);
|
||||
native_handle_delete(mHandle);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android
|
|
@ -90,6 +90,11 @@ static String8 getThreadName(pid_t tid) {
|
|||
ALOGE("%s: Failed to open %s", __FUNCTION__, path);
|
||||
}
|
||||
|
||||
if (procName == NULL) {
|
||||
// Reading /proc/self/task/%d/comm failed due to a race
|
||||
return String8::format("[err-unknown-tid-%d]", tid);
|
||||
}
|
||||
|
||||
// Strip ending newline
|
||||
strtok(procName, "\n");
|
||||
|
||||
|
|
|
@ -408,6 +408,30 @@ ssize_t String8::find(const char* other, size_t start) const
|
|||
return p ? p-mString : -1;
|
||||
}
|
||||
|
||||
bool String8::removeAll(const char* other) {
|
||||
ssize_t index = find(other);
|
||||
if (index < 0) return false;
|
||||
|
||||
char* buf = lockBuffer(size());
|
||||
if (!buf) return false; // out of memory
|
||||
|
||||
size_t skip = strlen(other);
|
||||
size_t len = size();
|
||||
size_t tail = index;
|
||||
while (size_t(index) < len) {
|
||||
ssize_t next = find(other, index + skip);
|
||||
if (next < 0) {
|
||||
next = len;
|
||||
}
|
||||
|
||||
memcpy(buf + tail, buf + index + skip, next - index - skip);
|
||||
tail += next - index - skip;
|
||||
index = next;
|
||||
}
|
||||
unlockBuffer(tail);
|
||||
return true;
|
||||
}
|
||||
|
||||
void String8::toLower()
|
||||
{
|
||||
toLower(0, size());
|
||||
|
|
|
@ -342,7 +342,8 @@ void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst)
|
|||
while (cur_utf16 < end_utf16) {
|
||||
char32_t utf32;
|
||||
// surrogate pairs
|
||||
if ((*cur_utf16 & 0xFC00) == 0xD800) {
|
||||
if((*cur_utf16 & 0xFC00) == 0xD800 && (cur_utf16 + 1) < end_utf16
|
||||
&& (*(cur_utf16 + 1) & 0xFC00) == 0xDC00) {
|
||||
utf32 = (*cur_utf16++ - 0xD800) << 10;
|
||||
utf32 |= *cur_utf16++ - 0xDC00;
|
||||
utf32 += 0x10000;
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
namespace android {
|
||||
|
||||
class BitSetTest : public testing::Test {
|
||||
class BitSet32Test : public testing::Test {
|
||||
protected:
|
||||
BitSet32 b1;
|
||||
BitSet32 b2;
|
||||
|
@ -34,7 +34,7 @@ protected:
|
|||
};
|
||||
|
||||
|
||||
TEST_F(BitSetTest, BitWiseOr) {
|
||||
TEST_F(BitSet32Test, BitWiseOr) {
|
||||
b1.markBit(2);
|
||||
b2.markBit(4);
|
||||
|
||||
|
@ -49,7 +49,7 @@ TEST_F(BitSetTest, BitWiseOr) {
|
|||
EXPECT_TRUE(b1.hasBit(2) && b1.hasBit(4));
|
||||
EXPECT_TRUE(b2.hasBit(4) && b2.count() == 1u);
|
||||
}
|
||||
TEST_F(BitSetTest, BitWiseAnd_Disjoint) {
|
||||
TEST_F(BitSet32Test, BitWiseAnd_Disjoint) {
|
||||
b1.markBit(2);
|
||||
b1.markBit(4);
|
||||
b1.markBit(6);
|
||||
|
@ -65,7 +65,7 @@ TEST_F(BitSetTest, BitWiseAnd_Disjoint) {
|
|||
EXPECT_TRUE(b1.hasBit(2) && b1.hasBit(4) && b1.hasBit(6));
|
||||
}
|
||||
|
||||
TEST_F(BitSetTest, BitWiseAnd_NonDisjoint) {
|
||||
TEST_F(BitSet32Test, BitWiseAnd_NonDisjoint) {
|
||||
b1.markBit(2);
|
||||
b1.markBit(4);
|
||||
b1.markBit(6);
|
||||
|
@ -84,4 +84,187 @@ TEST_F(BitSetTest, BitWiseAnd_NonDisjoint) {
|
|||
EXPECT_EQ(b2.count(), 3u);
|
||||
EXPECT_TRUE(b2.hasBit(3) && b2.hasBit(6) && b2.hasBit(9));
|
||||
}
|
||||
|
||||
TEST_F(BitSet32Test, MarkFirstUnmarkedBit) {
|
||||
b1.markBit(1);
|
||||
|
||||
b1.markFirstUnmarkedBit();
|
||||
EXPECT_EQ(b1.count(), 2u);
|
||||
EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1));
|
||||
|
||||
b1.markFirstUnmarkedBit();
|
||||
EXPECT_EQ(b1.count(), 3u);
|
||||
EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1) && b1.hasBit(2));
|
||||
}
|
||||
|
||||
TEST_F(BitSet32Test, ClearFirstMarkedBit) {
|
||||
b1.markBit(0);
|
||||
b1.markBit(10);
|
||||
|
||||
b1.clearFirstMarkedBit();
|
||||
EXPECT_EQ(b1.count(), 1u);
|
||||
EXPECT_TRUE(b1.hasBit(10));
|
||||
|
||||
b1.markBit(30);
|
||||
b1.clearFirstMarkedBit();
|
||||
EXPECT_EQ(b1.count(), 1u);
|
||||
EXPECT_TRUE(b1.hasBit(30));
|
||||
}
|
||||
|
||||
TEST_F(BitSet32Test, ClearLastMarkedBit) {
|
||||
b1.markBit(10);
|
||||
b1.markBit(31);
|
||||
|
||||
b1.clearLastMarkedBit();
|
||||
EXPECT_EQ(b1.count(), 1u);
|
||||
EXPECT_TRUE(b1.hasBit(10));
|
||||
|
||||
b1.markBit(5);
|
||||
b1.clearLastMarkedBit();
|
||||
EXPECT_EQ(b1.count(), 1u);
|
||||
EXPECT_TRUE(b1.hasBit(5));
|
||||
}
|
||||
|
||||
TEST_F(BitSet32Test, FillAndClear) {
|
||||
EXPECT_TRUE(b1.isEmpty());
|
||||
for (size_t i = 0; i < 32; i++) {
|
||||
b1.markFirstUnmarkedBit();
|
||||
}
|
||||
EXPECT_TRUE(b1.isFull());
|
||||
b1.clear();
|
||||
EXPECT_TRUE(b1.isEmpty());
|
||||
}
|
||||
|
||||
TEST_F(BitSet32Test, GetIndexOfBit) {
|
||||
b1.markBit(1);
|
||||
b1.markBit(4);
|
||||
EXPECT_EQ(b1.getIndexOfBit(1), 0);
|
||||
EXPECT_EQ(b1.getIndexOfBit(4), 1);
|
||||
b1.markFirstUnmarkedBit();
|
||||
EXPECT_EQ(b1.getIndexOfBit(1), 1);
|
||||
EXPECT_EQ(b1.getIndexOfBit(4), 2);
|
||||
}
|
||||
|
||||
class BitSet64Test : public testing::Test {
|
||||
protected:
|
||||
BitSet64 b1;
|
||||
BitSet64 b2;
|
||||
virtual void TearDown() {
|
||||
b1.clear();
|
||||
b2.clear();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
TEST_F(BitSet64Test, BitWiseOr) {
|
||||
b1.markBit(20);
|
||||
b2.markBit(40);
|
||||
|
||||
BitSet64 tmp = b1 | b2;
|
||||
EXPECT_EQ(tmp.count(), 2u);
|
||||
EXPECT_TRUE(tmp.hasBit(20) && tmp.hasBit(40));
|
||||
// Check that the operator is symmetric
|
||||
EXPECT_TRUE((b2 | b1) == (b1 | b2));
|
||||
|
||||
b1 |= b2;
|
||||
EXPECT_EQ(b1.count(), 2u);
|
||||
EXPECT_TRUE(b1.hasBit(20) && b1.hasBit(40));
|
||||
EXPECT_TRUE(b2.hasBit(40) && b2.count() == 1u);
|
||||
}
|
||||
TEST_F(BitSet64Test, BitWiseAnd_Disjoint) {
|
||||
b1.markBit(20);
|
||||
b1.markBit(40);
|
||||
b1.markBit(60);
|
||||
|
||||
BitSet64 tmp = b1 & b2;
|
||||
EXPECT_TRUE(tmp.isEmpty());
|
||||
// Check that the operator is symmetric
|
||||
EXPECT_TRUE((b2 & b1) == (b1 & b2));
|
||||
|
||||
b2 &= b1;
|
||||
EXPECT_TRUE(b2.isEmpty());
|
||||
EXPECT_EQ(b1.count(), 3u);
|
||||
EXPECT_TRUE(b1.hasBit(20) && b1.hasBit(40) && b1.hasBit(60));
|
||||
}
|
||||
|
||||
TEST_F(BitSet64Test, BitWiseAnd_NonDisjoint) {
|
||||
b1.markBit(20);
|
||||
b1.markBit(40);
|
||||
b1.markBit(60);
|
||||
b2.markBit(30);
|
||||
b2.markBit(60);
|
||||
b2.markBit(63);
|
||||
|
||||
BitSet64 tmp = b1 & b2;
|
||||
EXPECT_EQ(tmp.count(), 1u);
|
||||
EXPECT_TRUE(tmp.hasBit(60));
|
||||
// Check that the operator is symmetric
|
||||
EXPECT_TRUE((b2 & b1) == (b1 & b2));
|
||||
|
||||
b1 &= b2;
|
||||
EXPECT_EQ(b1.count(), 1u);
|
||||
EXPECT_EQ(b2.count(), 3u);
|
||||
EXPECT_TRUE(b2.hasBit(30) && b2.hasBit(60) && b2.hasBit(63));
|
||||
}
|
||||
|
||||
TEST_F(BitSet64Test, MarkFirstUnmarkedBit) {
|
||||
b1.markBit(1);
|
||||
|
||||
b1.markFirstUnmarkedBit();
|
||||
EXPECT_EQ(b1.count(), 2u);
|
||||
EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1));
|
||||
|
||||
b1.markFirstUnmarkedBit();
|
||||
EXPECT_EQ(b1.count(), 3u);
|
||||
EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1) && b1.hasBit(2));
|
||||
}
|
||||
|
||||
TEST_F(BitSet64Test, ClearFirstMarkedBit) {
|
||||
b1.markBit(0);
|
||||
b1.markBit(10);
|
||||
|
||||
b1.clearFirstMarkedBit();
|
||||
EXPECT_EQ(b1.count(), 1u);
|
||||
EXPECT_TRUE(b1.hasBit(10));
|
||||
|
||||
b1.markBit(50);
|
||||
b1.clearFirstMarkedBit();
|
||||
EXPECT_EQ(b1.count(), 1u);
|
||||
EXPECT_TRUE(b1.hasBit(50));
|
||||
}
|
||||
|
||||
TEST_F(BitSet64Test, ClearLastMarkedBit) {
|
||||
b1.markBit(10);
|
||||
b1.markBit(63);
|
||||
|
||||
b1.clearLastMarkedBit();
|
||||
EXPECT_EQ(b1.count(), 1u);
|
||||
EXPECT_TRUE(b1.hasBit(10));
|
||||
|
||||
b1.markBit(5);
|
||||
b1.clearLastMarkedBit();
|
||||
EXPECT_EQ(b1.count(), 1u);
|
||||
EXPECT_TRUE(b1.hasBit(5));
|
||||
}
|
||||
|
||||
TEST_F(BitSet64Test, FillAndClear) {
|
||||
EXPECT_TRUE(b1.isEmpty());
|
||||
for (size_t i = 0; i < 64; i++) {
|
||||
b1.markFirstUnmarkedBit();
|
||||
}
|
||||
EXPECT_TRUE(b1.isFull());
|
||||
b1.clear();
|
||||
EXPECT_TRUE(b1.isEmpty());
|
||||
}
|
||||
|
||||
TEST_F(BitSet64Test, GetIndexOfBit) {
|
||||
b1.markBit(10);
|
||||
b1.markBit(40);
|
||||
EXPECT_EQ(b1.getIndexOfBit(10), 0);
|
||||
EXPECT_EQ(b1.getIndexOfBit(40), 1);
|
||||
b1.markFirstUnmarkedBit();
|
||||
EXPECT_EQ(b1.getIndexOfBit(10), 1);
|
||||
EXPECT_EQ(b1.getIndexOfBit(40), 2);
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
|
|
@ -42,6 +42,9 @@ LOCAL_C_INCLUDES += ${includes}
|
|||
LOCAL_STATIC_LIBRARIES := libz libutils
|
||||
LOCAL_MODULE:= libziparchive-host
|
||||
LOCAL_CFLAGS := -Werror
|
||||
ifneq ($(strip $(USE_MINGW)),)
|
||||
LOCAL_CFLAGS += -mno-ms-bitfields
|
||||
endif
|
||||
LOCAL_MULTILIB := both
|
||||
include $(BUILD_HOST_STATIC_LIBRARY)
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := lmkd.c
|
||||
LOCAL_SHARED_LIBRARIES := liblog libm libc libprocessgroup
|
||||
LOCAL_CFLAGS := -Werror
|
||||
|
||||
LOCAL_MODULE := lmkd
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
|
@ -0,0 +1,833 @@
|
|||
/*
|
||||
* Copyright (C) 2013 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "lowmemorykiller"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cutils/sockets.h>
|
||||
#include <log/log.h>
|
||||
#include <processgroup/processgroup.h>
|
||||
|
||||
#ifndef __unused
|
||||
#define __unused __attribute__((__unused__))
|
||||
#endif
|
||||
|
||||
#define MEMCG_SYSFS_PATH "/dev/memcg/"
|
||||
#define MEMPRESSURE_WATCH_LEVEL "medium"
|
||||
#define ZONEINFO_PATH "/proc/zoneinfo"
|
||||
#define LINE_MAX 128
|
||||
|
||||
#define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree"
|
||||
#define INKERNEL_ADJ_PATH "/sys/module/lowmemorykiller/parameters/adj"
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
|
||||
|
||||
enum lmk_cmd {
|
||||
LMK_TARGET,
|
||||
LMK_PROCPRIO,
|
||||
LMK_PROCREMOVE,
|
||||
};
|
||||
|
||||
#define MAX_TARGETS 6
|
||||
/*
|
||||
* longest is LMK_TARGET followed by MAX_TARGETS each minfree and minkillprio
|
||||
* values
|
||||
*/
|
||||
#define CTRL_PACKET_MAX (sizeof(int) * (MAX_TARGETS * 2 + 1))
|
||||
|
||||
/* default to old in-kernel interface if no memory pressure events */
|
||||
static int use_inkernel_interface = 1;
|
||||
|
||||
/* memory pressure level medium event */
|
||||
static int mpevfd;
|
||||
|
||||
/* control socket listen and data */
|
||||
static int ctrl_lfd;
|
||||
static int ctrl_dfd = -1;
|
||||
static int ctrl_dfd_reopened; /* did we reopen ctrl conn on this loop? */
|
||||
|
||||
/* 1 memory pressure level, 1 ctrl listen socket, 1 ctrl data socket */
|
||||
#define MAX_EPOLL_EVENTS 3
|
||||
static int epollfd;
|
||||
static int maxevents;
|
||||
|
||||
#define OOM_DISABLE (-17)
|
||||
/* inclusive */
|
||||
#define OOM_ADJUST_MIN (-16)
|
||||
#define OOM_ADJUST_MAX 15
|
||||
|
||||
/* kernel OOM score values */
|
||||
#define OOM_SCORE_ADJ_MIN (-1000)
|
||||
#define OOM_SCORE_ADJ_MAX 1000
|
||||
|
||||
static int lowmem_adj[MAX_TARGETS];
|
||||
static int lowmem_minfree[MAX_TARGETS];
|
||||
static int lowmem_targets_size;
|
||||
|
||||
struct sysmeminfo {
|
||||
int nr_free_pages;
|
||||
int nr_file_pages;
|
||||
int nr_shmem;
|
||||
int totalreserve_pages;
|
||||
};
|
||||
|
||||
struct adjslot_list {
|
||||
struct adjslot_list *next;
|
||||
struct adjslot_list *prev;
|
||||
};
|
||||
|
||||
struct proc {
|
||||
struct adjslot_list asl;
|
||||
int pid;
|
||||
uid_t uid;
|
||||
int oomadj;
|
||||
struct proc *pidhash_next;
|
||||
};
|
||||
|
||||
#define PIDHASH_SZ 1024
|
||||
static struct proc *pidhash[PIDHASH_SZ];
|
||||
#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
|
||||
|
||||
#define ADJTOSLOT(adj) (adj + -OOM_ADJUST_MIN)
|
||||
static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_ADJUST_MAX) + 1];
|
||||
|
||||
/*
|
||||
* Wait 1-2 seconds for the death report of a killed process prior to
|
||||
* considering killing more processes.
|
||||
*/
|
||||
#define KILL_TIMEOUT 2
|
||||
/* Time of last process kill we initiated, stop me before I kill again */
|
||||
static time_t kill_lasttime;
|
||||
|
||||
/* PAGE_SIZE / 1024 */
|
||||
static long page_k;
|
||||
|
||||
static ssize_t read_all(int fd, char *buf, size_t max_len)
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
|
||||
while (max_len > 0) {
|
||||
ssize_t r = read(fd, buf, max_len);
|
||||
if (r == 0) {
|
||||
break;
|
||||
}
|
||||
if (r == -1) {
|
||||
return -1;
|
||||
}
|
||||
ret += r;
|
||||
buf += r;
|
||||
max_len -= r;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lowmem_oom_adj_to_oom_score_adj(int oom_adj)
|
||||
{
|
||||
if (oom_adj == OOM_ADJUST_MAX)
|
||||
return OOM_SCORE_ADJ_MAX;
|
||||
else
|
||||
return (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;
|
||||
}
|
||||
|
||||
static struct proc *pid_lookup(int pid) {
|
||||
struct proc *procp;
|
||||
|
||||
for (procp = pidhash[pid_hashfn(pid)]; procp && procp->pid != pid;
|
||||
procp = procp->pidhash_next)
|
||||
;
|
||||
|
||||
return procp;
|
||||
}
|
||||
|
||||
static void adjslot_insert(struct adjslot_list *head, struct adjslot_list *new)
|
||||
{
|
||||
struct adjslot_list *next = head->next;
|
||||
new->prev = head;
|
||||
new->next = next;
|
||||
next->prev = new;
|
||||
head->next = new;
|
||||
}
|
||||
|
||||
static void adjslot_remove(struct adjslot_list *old)
|
||||
{
|
||||
struct adjslot_list *prev = old->prev;
|
||||
struct adjslot_list *next = old->next;
|
||||
next->prev = prev;
|
||||
prev->next = next;
|
||||
}
|
||||
|
||||
static struct adjslot_list *adjslot_tail(struct adjslot_list *head) {
|
||||
struct adjslot_list *asl = head->prev;
|
||||
|
||||
return asl == head ? NULL : asl;
|
||||
}
|
||||
|
||||
static void proc_slot(struct proc *procp) {
|
||||
int adjslot = ADJTOSLOT(procp->oomadj);
|
||||
|
||||
adjslot_insert(&procadjslot_list[adjslot], &procp->asl);
|
||||
}
|
||||
|
||||
static void proc_unslot(struct proc *procp) {
|
||||
adjslot_remove(&procp->asl);
|
||||
}
|
||||
|
||||
static void proc_insert(struct proc *procp) {
|
||||
int hval = pid_hashfn(procp->pid);
|
||||
|
||||
procp->pidhash_next = pidhash[hval];
|
||||
pidhash[hval] = procp;
|
||||
proc_slot(procp);
|
||||
}
|
||||
|
||||
static int pid_remove(int pid) {
|
||||
int hval = pid_hashfn(pid);
|
||||
struct proc *procp;
|
||||
struct proc *prevp;
|
||||
|
||||
for (procp = pidhash[hval], prevp = NULL; procp && procp->pid != pid;
|
||||
procp = procp->pidhash_next)
|
||||
prevp = procp;
|
||||
|
||||
if (!procp)
|
||||
return -1;
|
||||
|
||||
if (!prevp)
|
||||
pidhash[hval] = procp->pidhash_next;
|
||||
else
|
||||
prevp->pidhash_next = procp->pidhash_next;
|
||||
|
||||
proc_unslot(procp);
|
||||
free(procp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void writefilestring(char *path, char *s) {
|
||||
int fd = open(path, O_WRONLY);
|
||||
int len = strlen(s);
|
||||
int ret;
|
||||
|
||||
if (fd < 0) {
|
||||
ALOGE("Error opening %s; errno=%d", path, errno);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = write(fd, s, len);
|
||||
if (ret < 0) {
|
||||
ALOGE("Error writing %s; errno=%d", path, errno);
|
||||
} else if (ret < len) {
|
||||
ALOGE("Short write on %s; length=%d", path, ret);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void cmd_procprio(int pid, int uid, int oomadj) {
|
||||
struct proc *procp;
|
||||
char path[80];
|
||||
char val[20];
|
||||
|
||||
if (oomadj < OOM_DISABLE || oomadj > OOM_ADJUST_MAX) {
|
||||
ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj);
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", pid);
|
||||
snprintf(val, sizeof(val), "%d", lowmem_oom_adj_to_oom_score_adj(oomadj));
|
||||
writefilestring(path, val);
|
||||
|
||||
if (use_inkernel_interface)
|
||||
return;
|
||||
|
||||
procp = pid_lookup(pid);
|
||||
if (!procp) {
|
||||
procp = malloc(sizeof(struct proc));
|
||||
if (!procp) {
|
||||
// Oh, the irony. May need to rebuild our state.
|
||||
return;
|
||||
}
|
||||
|
||||
procp->pid = pid;
|
||||
procp->uid = uid;
|
||||
procp->oomadj = oomadj;
|
||||
proc_insert(procp);
|
||||
} else {
|
||||
proc_unslot(procp);
|
||||
procp->oomadj = oomadj;
|
||||
proc_slot(procp);
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_procremove(int pid) {
|
||||
if (use_inkernel_interface)
|
||||
return;
|
||||
|
||||
pid_remove(pid);
|
||||
kill_lasttime = 0;
|
||||
}
|
||||
|
||||
static void cmd_target(int ntargets, int *params) {
|
||||
int i;
|
||||
|
||||
if (ntargets > (int)ARRAY_SIZE(lowmem_adj))
|
||||
return;
|
||||
|
||||
for (i = 0; i < ntargets; i++) {
|
||||
lowmem_minfree[i] = ntohl(*params++);
|
||||
lowmem_adj[i] = ntohl(*params++);
|
||||
}
|
||||
|
||||
lowmem_targets_size = ntargets;
|
||||
|
||||
if (use_inkernel_interface) {
|
||||
char minfreestr[128];
|
||||
char killpriostr[128];
|
||||
|
||||
minfreestr[0] = '\0';
|
||||
killpriostr[0] = '\0';
|
||||
|
||||
for (i = 0; i < lowmem_targets_size; i++) {
|
||||
char val[40];
|
||||
|
||||
if (i) {
|
||||
strlcat(minfreestr, ",", sizeof(minfreestr));
|
||||
strlcat(killpriostr, ",", sizeof(killpriostr));
|
||||
}
|
||||
|
||||
snprintf(val, sizeof(val), "%d", lowmem_minfree[i]);
|
||||
strlcat(minfreestr, val, sizeof(minfreestr));
|
||||
snprintf(val, sizeof(val), "%d", lowmem_adj[i]);
|
||||
strlcat(killpriostr, val, sizeof(killpriostr));
|
||||
}
|
||||
|
||||
writefilestring(INKERNEL_MINFREE_PATH, minfreestr);
|
||||
writefilestring(INKERNEL_ADJ_PATH, killpriostr);
|
||||
}
|
||||
}
|
||||
|
||||
static void ctrl_data_close(void) {
|
||||
ALOGI("Closing Activity Manager data connection");
|
||||
close(ctrl_dfd);
|
||||
ctrl_dfd = -1;
|
||||
maxevents--;
|
||||
}
|
||||
|
||||
static int ctrl_data_read(char *buf, size_t bufsz) {
|
||||
int ret = 0;
|
||||
|
||||
ret = read(ctrl_dfd, buf, bufsz);
|
||||
|
||||
if (ret == -1) {
|
||||
ALOGE("control data socket read failed; errno=%d", errno);
|
||||
} else if (ret == 0) {
|
||||
ALOGE("Got EOF on control data socket");
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ctrl_command_handler(void) {
|
||||
int ibuf[CTRL_PACKET_MAX / sizeof(int)];
|
||||
int len;
|
||||
int cmd = -1;
|
||||
int nargs;
|
||||
int targets;
|
||||
|
||||
len = ctrl_data_read((char *)ibuf, CTRL_PACKET_MAX);
|
||||
if (len <= 0)
|
||||
return;
|
||||
|
||||
nargs = len / sizeof(int) - 1;
|
||||
if (nargs < 0)
|
||||
goto wronglen;
|
||||
|
||||
cmd = ntohl(ibuf[0]);
|
||||
|
||||
switch(cmd) {
|
||||
case LMK_TARGET:
|
||||
targets = nargs / 2;
|
||||
if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj))
|
||||
goto wronglen;
|
||||
cmd_target(targets, &ibuf[1]);
|
||||
break;
|
||||
case LMK_PROCPRIO:
|
||||
if (nargs != 3)
|
||||
goto wronglen;
|
||||
cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2]), ntohl(ibuf[3]));
|
||||
break;
|
||||
case LMK_PROCREMOVE:
|
||||
if (nargs != 1)
|
||||
goto wronglen;
|
||||
cmd_procremove(ntohl(ibuf[1]));
|
||||
break;
|
||||
default:
|
||||
ALOGE("Received unknown command code %d", cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
wronglen:
|
||||
ALOGE("Wrong control socket read length cmd=%d len=%d", cmd, len);
|
||||
}
|
||||
|
||||
static void ctrl_data_handler(uint32_t events) {
|
||||
if (events & EPOLLHUP) {
|
||||
ALOGI("ActivityManager disconnected");
|
||||
if (!ctrl_dfd_reopened)
|
||||
ctrl_data_close();
|
||||
} else if (events & EPOLLIN) {
|
||||
ctrl_command_handler();
|
||||
}
|
||||
}
|
||||
|
||||
static void ctrl_connect_handler(uint32_t events __unused) {
|
||||
struct sockaddr addr;
|
||||
socklen_t alen;
|
||||
struct epoll_event epev;
|
||||
|
||||
if (ctrl_dfd >= 0) {
|
||||
ctrl_data_close();
|
||||
ctrl_dfd_reopened = 1;
|
||||
}
|
||||
|
||||
alen = sizeof(addr);
|
||||
ctrl_dfd = accept(ctrl_lfd, &addr, &alen);
|
||||
|
||||
if (ctrl_dfd < 0) {
|
||||
ALOGE("lmkd control socket accept failed; errno=%d", errno);
|
||||
return;
|
||||
}
|
||||
|
||||
ALOGI("ActivityManager connected");
|
||||
maxevents++;
|
||||
epev.events = EPOLLIN;
|
||||
epev.data.ptr = (void *)ctrl_data_handler;
|
||||
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_dfd, &epev) == -1) {
|
||||
ALOGE("epoll_ctl for data connection socket failed; errno=%d", errno);
|
||||
ctrl_data_close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int zoneinfo_parse_protection(char *cp) {
|
||||
int max = 0;
|
||||
int zoneval;
|
||||
char *save_ptr;
|
||||
|
||||
for (cp = strtok_r(cp, "(), ", &save_ptr); cp; cp = strtok_r(NULL, "), ", &save_ptr)) {
|
||||
zoneval = strtol(cp, &cp, 0);
|
||||
if (zoneval > max)
|
||||
max = zoneval;
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
static void zoneinfo_parse_line(char *line, struct sysmeminfo *mip) {
|
||||
char *cp = line;
|
||||
char *ap;
|
||||
char *save_ptr;
|
||||
|
||||
cp = strtok_r(line, " ", &save_ptr);
|
||||
if (!cp)
|
||||
return;
|
||||
|
||||
ap = strtok_r(NULL, " ", &save_ptr);
|
||||
if (!ap)
|
||||
return;
|
||||
|
||||
if (!strcmp(cp, "nr_free_pages"))
|
||||
mip->nr_free_pages += strtol(ap, NULL, 0);
|
||||
else if (!strcmp(cp, "nr_file_pages"))
|
||||
mip->nr_file_pages += strtol(ap, NULL, 0);
|
||||
else if (!strcmp(cp, "nr_shmem"))
|
||||
mip->nr_shmem += strtol(ap, NULL, 0);
|
||||
else if (!strcmp(cp, "high"))
|
||||
mip->totalreserve_pages += strtol(ap, NULL, 0);
|
||||
else if (!strcmp(cp, "protection:"))
|
||||
mip->totalreserve_pages += zoneinfo_parse_protection(ap);
|
||||
}
|
||||
|
||||
static int zoneinfo_parse(struct sysmeminfo *mip) {
|
||||
int fd;
|
||||
ssize_t size;
|
||||
char buf[PAGE_SIZE];
|
||||
char *save_ptr;
|
||||
char *line;
|
||||
|
||||
memset(mip, 0, sizeof(struct sysmeminfo));
|
||||
|
||||
fd = open(ZONEINFO_PATH, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
ALOGE("%s open: errno=%d", ZONEINFO_PATH, errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size = read_all(fd, buf, sizeof(buf) - 1);
|
||||
if (size < 0) {
|
||||
ALOGE("%s read: errno=%d", ZONEINFO_PATH, errno);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
ALOG_ASSERT((size_t)size < sizeof(buf) - 1, "/proc/zoneinfo too large");
|
||||
buf[size] = 0;
|
||||
|
||||
for (line = strtok_r(buf, "\n", &save_ptr); line; line = strtok_r(NULL, "\n", &save_ptr))
|
||||
zoneinfo_parse_line(line, mip);
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proc_get_size(int pid) {
|
||||
char path[PATH_MAX];
|
||||
char line[LINE_MAX];
|
||||
int fd;
|
||||
int rss = 0;
|
||||
int total;
|
||||
ssize_t ret;
|
||||
|
||||
snprintf(path, PATH_MAX, "/proc/%d/statm", pid);
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
ret = read_all(fd, line, sizeof(line) - 1);
|
||||
if (ret < 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sscanf(line, "%d %d ", &total, &rss);
|
||||
close(fd);
|
||||
return rss;
|
||||
}
|
||||
|
||||
static char *proc_get_name(int pid) {
|
||||
char path[PATH_MAX];
|
||||
static char line[LINE_MAX];
|
||||
int fd;
|
||||
char *cp;
|
||||
ssize_t ret;
|
||||
|
||||
snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid);
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd == -1)
|
||||
return NULL;
|
||||
ret = read_all(fd, line, sizeof(line) - 1);
|
||||
close(fd);
|
||||
if (ret < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cp = strchr(line, ' ');
|
||||
if (cp)
|
||||
*cp = '\0';
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
static struct proc *proc_adj_lru(int oomadj) {
|
||||
return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]);
|
||||
}
|
||||
|
||||
/* Kill one process specified by procp. Returns the size of the process killed */
|
||||
static int kill_one_process(struct proc *procp, int other_free, int other_file,
|
||||
int minfree, int min_score_adj, bool first)
|
||||
{
|
||||
int pid = procp->pid;
|
||||
uid_t uid = procp->uid;
|
||||
char *taskname;
|
||||
int tasksize;
|
||||
int r;
|
||||
|
||||
taskname = proc_get_name(pid);
|
||||
if (!taskname) {
|
||||
pid_remove(pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tasksize = proc_get_size(pid);
|
||||
if (tasksize <= 0) {
|
||||
pid_remove(pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ALOGI("Killing '%s' (%d), uid %d, adj %d\n"
|
||||
" to free %ldkB because cache %s%ldkB is below limit %ldkB for oom_adj %d\n"
|
||||
" Free memory is %s%ldkB %s reserved",
|
||||
taskname, pid, uid, procp->oomadj, tasksize * page_k,
|
||||
first ? "" : "~", other_file * page_k, minfree * page_k, min_score_adj,
|
||||
first ? "" : "~", other_free * page_k, other_free >= 0 ? "above" : "below");
|
||||
r = kill(pid, SIGKILL);
|
||||
killProcessGroup(uid, pid, SIGKILL);
|
||||
pid_remove(pid);
|
||||
|
||||
if (r) {
|
||||
ALOGE("kill(%d): errno=%d", procp->pid, errno);
|
||||
return -1;
|
||||
} else {
|
||||
return tasksize;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a process to kill based on the current (possibly estimated) free memory
|
||||
* and cached memory sizes. Returns the size of the killed processes.
|
||||
*/
|
||||
static int find_and_kill_process(int other_free, int other_file, bool first)
|
||||
{
|
||||
int i;
|
||||
int r;
|
||||
int min_score_adj = OOM_ADJUST_MAX + 1;
|
||||
int minfree = 0;
|
||||
int killed_size = 0;
|
||||
|
||||
for (i = 0; i < lowmem_targets_size; i++) {
|
||||
minfree = lowmem_minfree[i];
|
||||
if (other_free < minfree && other_file < minfree) {
|
||||
min_score_adj = lowmem_adj[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (min_score_adj == OOM_ADJUST_MAX + 1)
|
||||
return 0;
|
||||
|
||||
for (i = OOM_ADJUST_MAX; i >= min_score_adj; i--) {
|
||||
struct proc *procp;
|
||||
|
||||
retry:
|
||||
procp = proc_adj_lru(i);
|
||||
|
||||
if (procp) {
|
||||
killed_size = kill_one_process(procp, other_free, other_file, minfree, min_score_adj, first);
|
||||
if (killed_size < 0) {
|
||||
goto retry;
|
||||
} else {
|
||||
return killed_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mp_event(uint32_t events __unused) {
|
||||
int i;
|
||||
int ret;
|
||||
unsigned long long evcount;
|
||||
struct sysmeminfo mi;
|
||||
int other_free;
|
||||
int other_file;
|
||||
int killed_size;
|
||||
bool first = true;
|
||||
|
||||
ret = read(mpevfd, &evcount, sizeof(evcount));
|
||||
if (ret < 0)
|
||||
ALOGE("Error reading memory pressure event fd; errno=%d",
|
||||
errno);
|
||||
|
||||
if (time(NULL) - kill_lasttime < KILL_TIMEOUT)
|
||||
return;
|
||||
|
||||
while (zoneinfo_parse(&mi) < 0) {
|
||||
// Failed to read /proc/zoneinfo, assume ENOMEM and kill something
|
||||
find_and_kill_process(0, 0, true);
|
||||
}
|
||||
|
||||
other_free = mi.nr_free_pages - mi.totalreserve_pages;
|
||||
other_file = mi.nr_file_pages - mi.nr_shmem;
|
||||
|
||||
do {
|
||||
killed_size = find_and_kill_process(other_free, other_file, first);
|
||||
if (killed_size > 0) {
|
||||
first = false;
|
||||
other_free += killed_size;
|
||||
other_file += killed_size;
|
||||
}
|
||||
} while (killed_size > 0);
|
||||
}
|
||||
|
||||
static int init_mp(char *levelstr, void *event_handler)
|
||||
{
|
||||
int mpfd;
|
||||
int evfd;
|
||||
int evctlfd;
|
||||
char buf[256];
|
||||
struct epoll_event epev;
|
||||
int ret;
|
||||
|
||||
mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY);
|
||||
if (mpfd < 0) {
|
||||
ALOGI("No kernel memory.pressure_level support (errno=%d)", errno);
|
||||
goto err_open_mpfd;
|
||||
}
|
||||
|
||||
evctlfd = open(MEMCG_SYSFS_PATH "cgroup.event_control", O_WRONLY);
|
||||
if (evctlfd < 0) {
|
||||
ALOGI("No kernel memory cgroup event control (errno=%d)", errno);
|
||||
goto err_open_evctlfd;
|
||||
}
|
||||
|
||||
evfd = eventfd(0, EFD_NONBLOCK);
|
||||
if (evfd < 0) {
|
||||
ALOGE("eventfd failed for level %s; errno=%d", levelstr, errno);
|
||||
goto err_eventfd;
|
||||
}
|
||||
|
||||
ret = snprintf(buf, sizeof(buf), "%d %d %s", evfd, mpfd, levelstr);
|
||||
if (ret >= (ssize_t)sizeof(buf)) {
|
||||
ALOGE("cgroup.event_control line overflow for level %s", levelstr);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = write(evctlfd, buf, strlen(buf) + 1);
|
||||
if (ret == -1) {
|
||||
ALOGE("cgroup.event_control write failed for level %s; errno=%d",
|
||||
levelstr, errno);
|
||||
goto err;
|
||||
}
|
||||
|
||||
epev.events = EPOLLIN;
|
||||
epev.data.ptr = event_handler;
|
||||
ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, evfd, &epev);
|
||||
if (ret == -1) {
|
||||
ALOGE("epoll_ctl for level %s failed; errno=%d", levelstr, errno);
|
||||
goto err;
|
||||
}
|
||||
maxevents++;
|
||||
mpevfd = evfd;
|
||||
return 0;
|
||||
|
||||
err:
|
||||
close(evfd);
|
||||
err_eventfd:
|
||||
close(evctlfd);
|
||||
err_open_evctlfd:
|
||||
close(mpfd);
|
||||
err_open_mpfd:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int init(void) {
|
||||
struct epoll_event epev;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
page_k = sysconf(_SC_PAGESIZE);
|
||||
if (page_k == -1)
|
||||
page_k = PAGE_SIZE;
|
||||
page_k /= 1024;
|
||||
|
||||
epollfd = epoll_create(MAX_EPOLL_EVENTS);
|
||||
if (epollfd == -1) {
|
||||
ALOGE("epoll_create failed (errno=%d)", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctrl_lfd = android_get_control_socket("lmkd");
|
||||
if (ctrl_lfd < 0) {
|
||||
ALOGE("get lmkd control socket failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = listen(ctrl_lfd, 1);
|
||||
if (ret < 0) {
|
||||
ALOGE("lmkd control socket listen failed (errno=%d)", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
epev.events = EPOLLIN;
|
||||
epev.data.ptr = (void *)ctrl_connect_handler;
|
||||
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_lfd, &epev) == -1) {
|
||||
ALOGE("epoll_ctl for lmkd control socket failed (errno=%d)", errno);
|
||||
return -1;
|
||||
}
|
||||
maxevents++;
|
||||
|
||||
use_inkernel_interface = !access(INKERNEL_MINFREE_PATH, W_OK);
|
||||
|
||||
if (use_inkernel_interface) {
|
||||
ALOGI("Using in-kernel low memory killer interface");
|
||||
} else {
|
||||
ret = init_mp(MEMPRESSURE_WATCH_LEVEL, (void *)&mp_event);
|
||||
if (ret)
|
||||
ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
|
||||
}
|
||||
|
||||
for (i = 0; i <= ADJTOSLOT(OOM_ADJUST_MAX); i++) {
|
||||
procadjslot_list[i].next = &procadjslot_list[i];
|
||||
procadjslot_list[i].prev = &procadjslot_list[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mainloop(void) {
|
||||
while (1) {
|
||||
struct epoll_event events[maxevents];
|
||||
int nevents;
|
||||
int i;
|
||||
|
||||
ctrl_dfd_reopened = 0;
|
||||
nevents = epoll_wait(epollfd, events, maxevents, -1);
|
||||
|
||||
if (nevents == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
ALOGE("epoll_wait failed (errno=%d)", errno);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = 0; i < nevents; ++i) {
|
||||
if (events[i].events & EPOLLERR)
|
||||
ALOGD("EPOLLERR on event #%d", i);
|
||||
if (events[i].data.ptr)
|
||||
(*(void (*)(uint32_t))events[i].data.ptr)(events[i].events);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc __unused, char **argv __unused) {
|
||||
struct sched_param param = {
|
||||
.sched_priority = 1,
|
||||
};
|
||||
|
||||
mlockall(MCL_FUTURE);
|
||||
sched_setscheduler(0, SCHED_FIFO, ¶m);
|
||||
if (!init())
|
||||
mainloop();
|
||||
|
||||
ALOGI("exiting");
|
||||
return 0;
|
||||
}
|
|
@ -134,5 +134,7 @@
|
|||
# libcore failure logging
|
||||
90100 exp_det_cert_pin_failure (certs|4)
|
||||
|
||||
1397638484 snet_event_log (subtag|3) (uid|1) (message|3)
|
||||
|
||||
# NOTE - the range 1000000-2000000 is reserved for partners and others who
|
||||
# want to define their own log tags without conflicting with the core platform.
|
||||
|
|
|
@ -102,7 +102,6 @@ struct
|
|||
{ "dhcp", 1, do_dhcp },
|
||||
{ "up", 1, ifc_up },
|
||||
{ "down", 1, ifc_down },
|
||||
{ "flhosts", 1, ifc_remove_host_routes },
|
||||
{ "deldefault", 1, ifc_remove_default_route },
|
||||
{ "hwaddr", 2, set_hwaddr },
|
||||
{ 0, 0, 0 },
|
||||
|
|
|
@ -31,7 +31,7 @@ LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
|
|||
include $(BUILD_SYSTEM)/base_rules.mk
|
||||
|
||||
# Regenerate init.environ.rc if PRODUCT_BOOTCLASSPATH has changed.
|
||||
bcp_md5 := $(word 1, $(shell echo $(PRODUCT_BOOTCLASSPATH) | $(MD5SUM)))
|
||||
bcp_md5 := $(word 1, $(shell echo $(PRODUCT_BOOTCLASSPATH) $(PRODUCT_SYSTEM_SERVER_CLASSPATH) | $(MD5SUM)))
|
||||
bcp_dep := $(intermediates)/$(bcp_md5).bcp.dep
|
||||
$(bcp_dep) :
|
||||
$(hide) mkdir -p $(dir $@) && rm -rf $(dir $@)*.bcp.dep && touch $@
|
||||
|
@ -40,6 +40,7 @@ $(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in $(bcp_dep)
|
|||
@echo "Generate: $< -> $@"
|
||||
@mkdir -p $(dir $@)
|
||||
$(hide) sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g' $< >$@
|
||||
$(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@
|
||||
|
||||
bcp_md5 :=
|
||||
bcp_dep :=
|
||||
|
|
|
@ -9,3 +9,4 @@ on init
|
|||
export ASEC_MOUNTPOINT /mnt/asec
|
||||
export LOOP_MOUNTPOINT /mnt/obb
|
||||
export BOOTCLASSPATH %BOOTCLASSPATH%
|
||||
export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
|
||||
|
|
|
@ -12,7 +12,7 @@ import /init.trace.rc
|
|||
|
||||
on early-init
|
||||
# Set init and its forked children's oom_adj.
|
||||
write /proc/1/oom_adj -16
|
||||
write /proc/1/oom_score_adj -1000
|
||||
|
||||
# Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls.
|
||||
write /sys/fs/selinux/checkreqprot 0
|
||||
|
@ -88,6 +88,10 @@ on init
|
|||
mkdir /mnt/obb 0700 root system
|
||||
mount tmpfs tmpfs /mnt/obb mode=0755,gid=1000
|
||||
|
||||
# memory control cgroup
|
||||
mkdir /dev/memcg 0700 root system
|
||||
mount cgroup none /dev/memcg memory
|
||||
|
||||
write /proc/sys/kernel/panic_on_oops 1
|
||||
write /proc/sys/kernel/hung_task_timeout_secs 0
|
||||
write /proc/cpu/alignment 4
|
||||
|
@ -267,6 +271,7 @@ on post-fs-data
|
|||
mkdir /data/misc/wifi 0770 wifi wifi
|
||||
mkdir /data/misc/wifi/sockets 0770 wifi wifi
|
||||
mkdir /data/misc/wifi/wpa_supplicant 0770 wifi wifi
|
||||
mkdir /data/misc/ethernet 0770 system system
|
||||
mkdir /data/misc/dhcp 0770 dhcp dhcp
|
||||
mkdir /data/misc/user 0771 root root
|
||||
# give system access to wpa_supplicant.conf for backup and restore
|
||||
|
@ -339,9 +344,9 @@ on boot
|
|||
write /proc/sys/vm/overcommit_memory 1
|
||||
write /proc/sys/vm/min_free_order_shift 4
|
||||
chown root system /sys/module/lowmemorykiller/parameters/adj
|
||||
chmod 0664 /sys/module/lowmemorykiller/parameters/adj
|
||||
chmod 0220 /sys/module/lowmemorykiller/parameters/adj
|
||||
chown root system /sys/module/lowmemorykiller/parameters/minfree
|
||||
chmod 0664 /sys/module/lowmemorykiller/parameters/minfree
|
||||
chmod 0220 /sys/module/lowmemorykiller/parameters/minfree
|
||||
|
||||
# Tweak background writeout
|
||||
write /proc/sys/vm/dirty_expire_centisecs 200
|
||||
|
@ -411,30 +416,22 @@ on boot
|
|||
chown system system /sys/kernel/ipv4/tcp_rmem_max
|
||||
chown root radio /proc/cmdline
|
||||
|
||||
# Define TCP buffer sizes for various networks
|
||||
# ReadMin, ReadInitial, ReadMax, WriteMin, WriteInitial, WriteMax,
|
||||
setprop net.tcp.buffersize.default 4096,87380,110208,4096,16384,110208
|
||||
setprop net.tcp.buffersize.wifi 524288,1048576,2097152,262144,524288,1048576
|
||||
setprop net.tcp.buffersize.ethernet 524288,1048576,3145728,524288,1048576,2097152
|
||||
setprop net.tcp.buffersize.lte 524288,1048576,2097152,262144,524288,1048576
|
||||
setprop net.tcp.buffersize.umts 58254,349525,1048576,58254,349525,1048576
|
||||
setprop net.tcp.buffersize.hspa 40778,244668,734003,16777,100663,301990
|
||||
setprop net.tcp.buffersize.hsupa 40778,244668,734003,16777,100663,301990
|
||||
setprop net.tcp.buffersize.hsdpa 61167,367002,1101005,8738,52429,262114
|
||||
setprop net.tcp.buffersize.hspap 122334,734003,2202010,32040,192239,576717
|
||||
setprop net.tcp.buffersize.edge 4093,26280,70800,4096,16384,70800
|
||||
setprop net.tcp.buffersize.gprs 4092,8760,48000,4096,8760,48000
|
||||
setprop net.tcp.buffersize.evdo 4094,87380,262144,4096,16384,262144
|
||||
|
||||
# Define default initial receive window size in segments.
|
||||
setprop net.tcp.default_init_rwnd 60
|
||||
|
||||
class_start core
|
||||
class_start main
|
||||
|
||||
on nonencrypted
|
||||
class_start main
|
||||
class_start late_start
|
||||
|
||||
on property:vold.decrypt=trigger_default_encryption
|
||||
start defaultcrypto
|
||||
|
||||
on property:vold.decrypt=trigger_encryption
|
||||
start surfaceflinger
|
||||
start encrypt
|
||||
|
||||
on property:sys.init_log_level=*
|
||||
loglevel ${sys.init_log_level}
|
||||
|
||||
|
@ -494,11 +491,6 @@ service healthd /sbin/healthd
|
|||
critical
|
||||
seclabel u:r:healthd:s0
|
||||
|
||||
service healthd-charger /sbin/healthd -n
|
||||
class charger
|
||||
critical
|
||||
seclabel u:r:healthd:s0
|
||||
|
||||
service console /system/bin/sh
|
||||
class core
|
||||
console
|
||||
|
@ -521,6 +513,11 @@ service adbd /sbin/adbd --root_seclabel=u:r:su:s0
|
|||
on property:ro.kernel.qemu=1
|
||||
start adbd
|
||||
|
||||
service lmkd /system/bin/lmkd
|
||||
class core
|
||||
critical
|
||||
socket lmkd seqpacket 0660 system system
|
||||
|
||||
service servicemanager /system/bin/servicemanager
|
||||
class core
|
||||
user system
|
||||
|
@ -558,7 +555,7 @@ service ril-daemon /system/bin/rild
|
|||
group radio cache inet misc audio log
|
||||
|
||||
service surfaceflinger /system/bin/surfaceflinger
|
||||
class main
|
||||
class core
|
||||
user system
|
||||
group graphics drmrpc
|
||||
onrestart restart zygote
|
||||
|
@ -574,10 +571,24 @@ service media /system/bin/mediaserver
|
|||
group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm
|
||||
ioprio rt 4
|
||||
|
||||
# One shot invocation to deal with encrypted volume.
|
||||
service defaultcrypto /system/bin/vdc --wait cryptfs mountdefaultencrypted
|
||||
disabled
|
||||
oneshot
|
||||
# vold will set vold.decrypt to trigger_restart_framework (default
|
||||
# encryption) or trigger_restart_min_framework (other encryption)
|
||||
|
||||
# One shot invocation to encrypt unencrypted volumes
|
||||
service encrypt /system/bin/vdc --wait cryptfs enablecrypto inplace default
|
||||
disabled
|
||||
oneshot
|
||||
# vold will set vold.decrypt to trigger_restart_framework (default
|
||||
# encryption)
|
||||
|
||||
service bootanim /system/bin/bootanimation
|
||||
class main
|
||||
class core
|
||||
user graphics
|
||||
group graphics
|
||||
group graphics audio
|
||||
disabled
|
||||
oneshot
|
||||
|
||||
|
@ -585,8 +596,9 @@ service installd /system/bin/installd
|
|||
class main
|
||||
socket installd stream 600 system system
|
||||
|
||||
service flash_recovery /system/etc/install-recovery.sh
|
||||
service flash_recovery /system/bin/install-recovery.sh
|
||||
class main
|
||||
seclabel u:r:install_recovery:s0
|
||||
oneshot
|
||||
|
||||
service racoon /system/bin/racoon
|
||||
|
@ -623,3 +635,8 @@ service mdnsd /system/bin/mdnsd
|
|||
socket mdnsd stream 0660 mdnsr inet
|
||||
disabled
|
||||
oneshot
|
||||
|
||||
service pre-recovery /system/bin/uncrypt
|
||||
class main
|
||||
disabled
|
||||
oneshot
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
## Permissions to allow system-wide tracing to the kernel trace buffer.
|
||||
##
|
||||
on boot
|
||||
on early-boot
|
||||
|
||||
# Allow writing to the kernel trace log.
|
||||
chmod 0222 /sys/kernel/debug/tracing/trace_marker
|
||||
|
|
|
@ -17,12 +17,12 @@ on property:sys.usb.config=none
|
|||
setprop sys.usb.state ${sys.usb.config}
|
||||
|
||||
# adb only USB configuration
|
||||
# This should only be used during device bringup
|
||||
# and as a fallback if the USB manager fails to set a standard configuration
|
||||
# This is the fallback configuration if the
|
||||
# USB manager fails to set a standard configuration
|
||||
on property:sys.usb.config=adb
|
||||
write /sys/class/android_usb/android0/enable 0
|
||||
write /sys/class/android_usb/android0/idVendor 18d1
|
||||
write /sys/class/android_usb/android0/idProduct D002
|
||||
write /sys/class/android_usb/android0/idProduct 4EE7
|
||||
write /sys/class/android_usb/android0/functions ${sys.usb.config}
|
||||
write /sys/class/android_usb/android0/enable 1
|
||||
start adbd
|
||||
|
|
|
@ -144,6 +144,8 @@ typedef enum {
|
|||
PERM_ANDROID_DATA,
|
||||
/* This node is "/Android/obb" */
|
||||
PERM_ANDROID_OBB,
|
||||
/* This node is "/Android/media" */
|
||||
PERM_ANDROID_MEDIA,
|
||||
/* This node is "/Android/user" */
|
||||
PERM_ANDROID_USER,
|
||||
} perm_t;
|
||||
|
@ -480,6 +482,10 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent,
|
|||
/* Single OBB directory is always shared */
|
||||
node->graft_path = fuse->obbpath;
|
||||
node->graft_pathlen = strlen(fuse->obbpath);
|
||||
} else if (!strcasecmp(node->name, "media")) {
|
||||
/* App-specific directories inside; let anyone traverse */
|
||||
node->perm = PERM_ANDROID_MEDIA;
|
||||
node->mode = 0771;
|
||||
} else if (!strcasecmp(node->name, "user")) {
|
||||
/* User directories must only be accessible to system, protected
|
||||
* by sdcard_all. Zygote will bind mount the appropriate user-
|
||||
|
@ -491,6 +497,7 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent,
|
|||
break;
|
||||
case PERM_ANDROID_DATA:
|
||||
case PERM_ANDROID_OBB:
|
||||
case PERM_ANDROID_MEDIA:
|
||||
appid = (appid_t) (uintptr_t) hashmapGet(fuse->package_to_appid, node->name);
|
||||
if (appid != 0) {
|
||||
node->uid = multiuser_get_uid(parent->userid, appid);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue