am 49f2e1ce: am cc39d421: am 936032b4: Merge "Add USB Vendor ID for MSI"
* commit '49f2e1cedfcf1dac72efe6e3634a3cd2db299e30':
Add USB Vendor ID for MSI
diff --git a/adb/services.c b/adb/services.c
index f0d5878..951048e 100644
--- a/adb/services.c
+++ b/adb/services.c
@@ -144,7 +144,11 @@
if (ret < 0) {
snprintf(buf, sizeof(buf), "reboot failed: %d\n", ret);
writex(fd, buf, strlen(buf));
+ goto cleanup;
}
+ // Don't return early. Give the reboot command time to take effect
+ // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
+ while(1) { pause(); }
cleanup:
free(arg);
adb_close(fd);
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index 82c5798..f432f6a 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -53,6 +53,8 @@
#define E2FSCK_BIN "/system/bin/e2fsck"
#define MKSWAP_BIN "/system/bin/mkswap"
+#define FSCK_LOG_FILE "/dev/fscklogs/log"
+
#define ZRAM_CONF_DEV "/sys/block/zram0/disksize"
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
@@ -92,6 +94,7 @@
{ "swapprio=", MF_SWAPPRIO },
{ "zramsize=", MF_ZRAMSIZE },
{ "verify", MF_VERIFY },
+ { "noemulatedsd", MF_NOEMULATEDSD },
{ "defaults", 0 },
{ 0, 0 },
};
@@ -427,6 +430,10 @@
{
int i;
+ if (!fstab) {
+ return;
+ }
+
for (i = 0; i < fstab->num_entries; i++) {
/* Free the pointers return by strdup(3) */
free(fstab->recs[i].blk_device);
@@ -483,7 +490,8 @@
INFO("Running %s on %s\n", E2FSCK_BIN, blk_device);
ret = android_fork_execvp_ext(ARRAY_SIZE(e2fsck_argv), e2fsck_argv,
- &status, true, LOG_KLOG, true);
+ &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 */
@@ -800,7 +808,7 @@
/* Initialize the swap area */
mkswap_argv[1] = fstab->recs[i].blk_device;
err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv), mkswap_argv,
- &status, true, LOG_KLOG, false);
+ &status, true, LOG_KLOG, false, NULL);
if (err) {
ERROR("mkswap failed for %s\n", fstab->recs[i].blk_device);
ret = -1;
@@ -931,3 +939,8 @@
{
return fstab->fs_mgr_flags & MF_CRYPT;
}
+
+int fs_mgr_is_noemulatedsd(struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
+}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index f284ca6..59ffd78 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -72,6 +72,12 @@
#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 DM_BUF_SIZE 4096
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 384d195..0f90c32 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -62,6 +62,7 @@
int fs_mgr_is_voldmanaged(struct fstab_rec *fstab);
int fs_mgr_is_nonremovable(struct fstab_rec *fstab);
int fs_mgr_is_encryptable(struct fstab_rec *fstab);
+int fs_mgr_is_noemulatedsd(struct fstab_rec *fstab);
int fs_mgr_swapon_all(struct fstab *fstab);
#ifdef __cplusplus
}
diff --git a/healthd/Android.mk b/healthd/Android.mk
index cff7a3b..473d375 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -3,6 +3,12 @@
ifneq ($(BUILD_TINY_ANDROID),true)
LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := healthd_board_default.cpp
+LOCAL_MODULE := libhealthd.default
+include $(BUILD_STATIC_LIBRARY)
+
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
@@ -17,12 +23,7 @@
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libz libutils libstdc++ libcutils liblog libm libc
-
-ifdef BOARD_LIB_HEALTHD
-LOCAL_STATIC_LIBRARIES += $(BOARD_LIB_HEALTHD)
-else
-LOCAL_SRC_FILES += healthd_board_default.cpp
-endif
+LOCAL_HAL_STATIC_LIBRARIES := libhealthd
include $(BUILD_EXECUTABLE)
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 8ff5357..688c7ff 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -181,33 +181,33 @@
props.batteryCurrentNow = INT_MIN;
props.batteryChargeCounter = INT_MIN;
- if (!mBatteryPresentPath.isEmpty())
- props.batteryPresent = getBooleanField(mBatteryPresentPath);
+ if (!mHealthdConfig->batteryPresentPath.isEmpty())
+ props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
else
props.batteryPresent = true;
- props.batteryLevel = getIntField(mBatteryCapacityPath);
- props.batteryVoltage = getIntField(mBatteryVoltagePath) / 1000;
+ props.batteryLevel = getIntField(mHealthdConfig->batteryCapacityPath);
+ props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
- if (!mBatteryCurrentNowPath.isEmpty())
- props.batteryCurrentNow = getIntField(mBatteryCurrentNowPath);
+ if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
+ props.batteryCurrentNow = getIntField(mHealthdConfig->batteryCurrentNowPath);
- if (!mBatteryChargeCounterPath.isEmpty())
- props.batteryChargeCounter = getIntField(mBatteryChargeCounterPath);
+ if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
+ props.batteryChargeCounter = getIntField(mHealthdConfig->batteryChargeCounterPath);
- props.batteryTemperature = getIntField(mBatteryTemperaturePath);
+ props.batteryTemperature = getIntField(mHealthdConfig->batteryTemperaturePath);
const int SIZE = 128;
char buf[SIZE];
String8 btech;
- if (readFromFile(mBatteryStatusPath, buf, SIZE) > 0)
+ if (readFromFile(mHealthdConfig->batteryStatusPath, buf, SIZE) > 0)
props.batteryStatus = getBatteryStatus(buf);
- if (readFromFile(mBatteryHealthPath, buf, SIZE) > 0)
+ if (readFromFile(mHealthdConfig->batteryHealthPath, buf, SIZE) > 0)
props.batteryHealth = getBatteryHealth(buf);
- if (readFromFile(mBatteryTechnologyPath, buf, SIZE) > 0)
+ if (readFromFile(mHealthdConfig->batteryTechnologyPath, buf, SIZE) > 0)
props.batteryTechnology = String8(buf);
unsigned int i;
@@ -252,7 +252,7 @@
abs(props.batteryTemperature % 10), props.batteryHealth,
props.batteryStatus);
- if (!mBatteryCurrentNowPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
char b[20];
snprintf(b, sizeof(b), " c=%d", props.batteryCurrentNow / 1000);
@@ -272,9 +272,10 @@
props.chargerWirelessOnline;
}
-void BatteryMonitor::init(bool nosvcmgr) {
+void BatteryMonitor::init(struct healthd_config *hc, bool nosvcmgr) {
String8 path;
+ mHealthdConfig = hc;
DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH);
if (dir == NULL) {
KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);
@@ -302,59 +303,92 @@
break;
case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
- path.clear();
- path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
- mBatteryStatusPath = path;
- path.clear();
- path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
- mBatteryHealthPath = path;
- path.clear();
- path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
- mBatteryPresentPath = path;
- path.clear();
- path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
- mBatteryCapacityPath = path;
-
- path.clear();
- path.appendFormat("%s/%s/voltage_now", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0) {
- mBatteryVoltagePath = path;
- } else {
+ if (mHealthdConfig->batteryStatusPath.isEmpty()) {
path.clear();
- path.appendFormat("%s/%s/batt_vol", POWER_SUPPLY_SYSFS_PATH, name);
+ path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
+ name);
if (access(path, R_OK) == 0)
- mBatteryVoltagePath = path;
+ mHealthdConfig->batteryStatusPath = path;
}
- path.clear();
- path.appendFormat("%s/%s/current_now", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
- mBatteryCurrentNowPath = path;
-
- path.clear();
- path.appendFormat("%s/%s/charge_counter", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
- mBatteryChargeCounterPath = path;
-
- path.clear();
- path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0) {
- mBatteryTemperaturePath = path;
- } else {
+ if (mHealthdConfig->batteryHealthPath.isEmpty()) {
path.clear();
- path.appendFormat("%s/%s/batt_temp", POWER_SUPPLY_SYSFS_PATH, name);
+ path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
+ name);
if (access(path, R_OK) == 0)
- mBatteryTemperaturePath = path;
+ mHealthdConfig->batteryHealthPath = path;
}
- path.clear();
- path.appendFormat("%s/%s/technology", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
- mBatteryTechnologyPath = path;
+ if (mHealthdConfig->batteryPresentPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
+ name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryPresentPath = path;
+ }
+
+ if (mHealthdConfig->batteryCapacityPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
+ name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryCapacityPath = path;
+ }
+
+ if (mHealthdConfig->batteryVoltagePath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/voltage_now",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0) {
+ mHealthdConfig->batteryVoltagePath = path;
+ } else {
+ path.clear();
+ path.appendFormat("%s/%s/batt_vol",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryVoltagePath = path;
+ }
+ }
+
+ if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/current_now",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryCurrentNowPath = path;
+ }
+
+ if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/charge_counter",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryChargeCounterPath = path;
+ }
+
+ if (mHealthdConfig->batteryTemperaturePath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,
+ name);
+ if (access(path, R_OK) == 0) {
+ mHealthdConfig->batteryTemperaturePath = path;
+ } else {
+ path.clear();
+ path.appendFormat("%s/%s/batt_temp",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryTemperaturePath = path;
+ }
+ }
+
+ if (mHealthdConfig->batteryTechnologyPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/technology",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryTechnologyPath = path;
+ }
+
break;
case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
@@ -366,19 +400,19 @@
if (!mChargerNames.size())
KLOG_ERROR(LOG_TAG, "No charger supplies found\n");
- if (mBatteryStatusPath.isEmpty())
+ if (mHealthdConfig->batteryStatusPath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
- if (mBatteryHealthPath.isEmpty())
- KLOG_WARNING("BatteryHealthPath not found\n");
- if (mBatteryPresentPath.isEmpty())
+ 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 (mBatteryCapacityPath.isEmpty())
+ if (mHealthdConfig->batteryCapacityPath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
- if (mBatteryVoltagePath.isEmpty())
+ if (mHealthdConfig->batteryVoltagePath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
- if (mBatteryTemperaturePath.isEmpty())
+ if (mHealthdConfig->batteryTemperaturePath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
- if (mBatteryTechnologyPath.isEmpty())
+ if (mHealthdConfig->batteryTechnologyPath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
if (nosvcmgr == false) {
diff --git a/healthd/BatteryMonitor.h b/healthd/BatteryMonitor.h
index ff1ea1e..ba291af 100644
--- a/healthd/BatteryMonitor.h
+++ b/healthd/BatteryMonitor.h
@@ -21,6 +21,7 @@
#include <utils/String8.h>
#include <utils/Vector.h>
+#include "healthd.h"
#include "BatteryPropertiesRegistrar.h"
namespace android {
@@ -38,20 +39,11 @@
ANDROID_POWER_SUPPLY_TYPE_BATTERY
};
- void init(bool nosvcmgr);
+ void init(struct healthd_config *hc, bool nosvcmgr);
bool update(void);
private:
- String8 mBatteryStatusPath;
- String8 mBatteryHealthPath;
- String8 mBatteryPresentPath;
- String8 mBatteryCapacityPath;
- String8 mBatteryVoltagePath;
- String8 mBatteryTemperaturePath;
- String8 mBatteryTechnologyPath;
- String8 mBatteryCurrentNowPath;
- String8 mBatteryChargeCounterPath;
-
+ struct healthd_config *mHealthdConfig;
Vector<String8> mChargerNames;
sp<BatteryPropertiesRegistrar> mBatteryPropertiesRegistrar;
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp
index 158274c..9b84c3e 100644
--- a/healthd/healthd.cpp
+++ b/healthd/healthd.cpp
@@ -41,6 +41,15 @@
static struct healthd_config healthd_config = {
.periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST,
.periodic_chores_interval_slow = DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW,
+ .batteryStatusPath = String8(String8::kEmptyString),
+ .batteryHealthPath = String8(String8::kEmptyString),
+ .batteryPresentPath = String8(String8::kEmptyString),
+ .batteryCapacityPath = String8(String8::kEmptyString),
+ .batteryVoltagePath = String8(String8::kEmptyString),
+ .batteryTemperaturePath = String8(String8::kEmptyString),
+ .batteryTechnologyPath = String8(String8::kEmptyString),
+ .batteryCurrentNowPath = String8(String8::kEmptyString),
+ .batteryChargeCounterPath = String8(String8::kEmptyString),
};
#define POWER_SUPPLY_SUBSYSTEM "power_supply"
@@ -240,6 +249,9 @@
if (events[n].data.ptr)
(*(void (*)())events[n].data.ptr)();
}
+
+ if (!nevents)
+ periodic_chores();
}
return;
@@ -266,7 +278,7 @@
uevent_init();
binder_init();
gBatteryMonitor = new BatteryMonitor();
- gBatteryMonitor->init(nosvcmgr);
+ gBatteryMonitor->init(&healthd_config, nosvcmgr);
healthd_mainloop();
return 0;
diff --git a/healthd/healthd.h b/healthd/healthd.h
index 895be40..5374fb1 100644
--- a/healthd/healthd.h
+++ b/healthd/healthd.h
@@ -18,6 +18,7 @@
#define _HEALTHD_H_
#include <batteryservice/BatteryService.h>
+#include <utils/String8.h>
// periodic_chores_interval_fast, periodic_chores_interval_slow: intervals at
// which healthd wakes up to poll health state and perform periodic chores,
@@ -32,18 +33,45 @@
// not connected to a charger (to watch for a battery drained to zero
// remaining capacity). The default value is 600 (10 minutes). Value -1
// tuns off periodic chores (and wakeups) in these conditions.
+//
+// power_supply sysfs attribute file paths. Set these to specific paths
+// to use for the associated battery parameters. healthd will search for
+// appropriate power_supply attribute files to use for any paths left empty:
+//
+// batteryStatusPath: charging status (POWER_SUPPLY_PROP_STATUS)
+// batteryHealthPath: battery health (POWER_SUPPLY_PROP_HEALTH)
+// batteryPresentPath: battery present (POWER_SUPPLY_PROP_PRESENT)
+// batteryCapacityPath: remaining capacity (POWER_SUPPLY_PROP_CAPACITY)
+// batteryVoltagePath: battery voltage (POWER_SUPPLY_PROP_VOLTAGE_NOW)
+// batteryTemperaturePath: battery temperature (POWER_SUPPLY_PROP_TEMP)
+// batteryTechnologyPath: battery technology (POWER_SUPPLY_PROP_TECHNOLOGY)
+// batteryCurrentNowPath: battery current (POWER_SUPPLY_PROP_CURRENT_NOW)
+// batteryChargeCounterPath: battery accumulated charge
+// (POWER_SUPPLY_PROP_CHARGE_COUNTER)
struct healthd_config {
int periodic_chores_interval_fast;
int periodic_chores_interval_slow;
+
+ android::String8 batteryStatusPath;
+ android::String8 batteryHealthPath;
+ android::String8 batteryPresentPath;
+ android::String8 batteryCapacityPath;
+ android::String8 batteryVoltagePath;
+ android::String8 batteryTemperaturePath;
+ android::String8 batteryTechnologyPath;
+ android::String8 batteryCurrentNowPath;
+ android::String8 batteryChargeCounterPath;
};
// The following are implemented in libhealthd_board to handle board-specific
// behavior.
//
-//
-// To use the default values, this function can simply return without
-// modifying the parameters.
+// healthd_board_init() is called at startup time to modify healthd's
+// configuration according to board-specific requirements. config
+// points to the healthd configuration values described above. To use default
+// values, this function can simply return without modifying the fields of the
+// config parameter.
void healthd_board_init(struct healthd_config *config);
diff --git a/include/cutils/fs.h b/include/cutils/fs.h
index fd5296b..d1d4cf2 100644
--- a/include/cutils/fs.h
+++ b/include/cutils/fs.h
@@ -55,6 +55,14 @@
*/
extern int fs_write_atomic_int(const char* path, int value);
+/*
+ * Ensure that all directories along given path exist, creating parent
+ * directories as needed. Validates that given path is absolute and that
+ * it contains no relative "." or ".." paths or symlinks. Last path segment
+ * is treated as filename and ignored, unless the path ends with "/".
+ */
+extern int fs_mkdirs(const char* path, mode_t mode);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/cutils/trace.h b/include/cutils/trace.h
index a0dd1e0..1c8f107 100644
--- a/include/cutils/trace.h
+++ b/include/cutils/trace.h
@@ -259,6 +259,23 @@
}
}
+/**
+ * Traces a 64-bit integer counter value. name is used to identify the
+ * counter. This can be used to track how a value changes over time.
+ */
+#define ATRACE_INT64(name, value) atrace_int64(ATRACE_TAG, name, value)
+static inline void atrace_int64(uint64_t tag, const char* name, int64_t value)
+{
+ if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
+ char buf[ATRACE_MESSAGE_LENGTH];
+ size_t len;
+
+ len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%lld",
+ getpid(), name, value);
+ write(atrace_marker_fd, buf, len);
+ }
+}
+
#else // not HAVE_ANDROID_OS
#define ATRACE_INIT()
diff --git a/include/memtrack/memtrack.h b/include/memtrack/memtrack.h
new file mode 100644
index 0000000..0f1f85e
--- /dev/null
+++ b/include/memtrack/memtrack.h
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBMEMTRACK_MEMTRACK_H_
+#define _LIBMEMTRACK_MEMTRACK_H_
+
+#include <sys/types.h>
+#include <stddef.h>
+#include <cutils/compiler.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * struct memtrack_proc
+ *
+ * an opaque handle to the memory stats on a process.
+ * Created with memtrack_proc_new, destroyed by
+ * memtrack_proc_destroy. Can be reused multiple times with
+ * memtrack_proc_get.
+ */
+struct memtrack_proc;
+
+/**
+ * memtrack_init
+ *
+ * Must be called once before calling any other functions. After this function
+ * is called, everything else is thread-safe.
+ *
+ * Returns 0 on success, -errno on error.
+ */
+int memtrack_init(void);
+
+/**
+ * memtrack_proc_new
+ *
+ * Return a new handle to hold process memory stats.
+ *
+ * Returns NULL on error.
+ */
+struct memtrack_proc *memtrack_proc_new(void);
+
+/**
+ * memtrack_proc_destroy
+ *
+ * Free all memory associated with a process memory stats handle.
+ */
+void memtrack_proc_destroy(struct memtrack_proc *p);
+
+/**
+ * memtrack_proc_get
+ *
+ * Fill a process memory stats handle with data about the given pid. Can be
+ * called on a handle that was just allocated with memtrack_proc_new,
+ * or on a handle that has been previously passed to memtrack_proc_get
+ * to replace the data with new data on the same or another process. It is
+ * expected that the second call on the same handle should not require
+ * allocating any new memory.
+ *
+ * Returns 0 on success, -errno on error.
+ */
+int memtrack_proc_get(struct memtrack_proc *p, pid_t pid);
+
+/**
+ * memtrack_proc_graphics_total
+ *
+ * Return total amount of memory that has been allocated for use as window
+ * buffers. Does not differentiate between memory that has already been
+ * accounted for by reading /proc/pid/smaps and memory that has not been
+ * accounted for.
+ *
+ * Returns non-negative size in bytes on success, -errno on error.
+ */
+ssize_t memtrack_proc_graphics_total(struct memtrack_proc *p);
+
+/**
+ * memtrack_proc_graphics_pss
+ *
+ * Return total amount of memory that has been allocated for use as window
+ * buffers, but has not already been accounted for by reading /proc/pid/smaps.
+ * Memory that is shared across processes may already be divided by the
+ * number of processes that share it (preferred), or may be charged in full to
+ * every process that shares it, depending on the capabilities of the driver.
+ *
+ * Returns non-negative size in bytes on success, -errno on error.
+ */
+ssize_t memtrack_proc_graphics_pss(struct memtrack_proc *p);
+
+/**
+ * memtrack_proc_gl_total
+ *
+ * Same as memtrack_proc_graphics_total, but counts GL memory (which
+ * should not overlap with graphics memory) instead of graphics memory.
+ *
+ * Returns non-negative size in bytes on success, -errno on error.
+ */
+ssize_t memtrack_proc_gl_total(struct memtrack_proc *p);
+
+/**
+ * memtrack_proc_gl_pss
+ *
+ * Same as memtrack_proc_graphics_total, but counts GL memory (which
+ * should not overlap with graphics memory) instead of graphics memory.
+ *
+ * Returns non-negative size in bytes on success, -errno on error.
+ */
+ssize_t memtrack_proc_gl_pss(struct memtrack_proc *p);
+
+/**
+ * memtrack_proc_gl_total
+ *
+ * Same as memtrack_proc_graphics_total, but counts miscellaneous memory
+ * not tracked by gl or graphics calls above.
+ *
+ * Returns non-negative size in bytes on success, -errno on error.
+ */
+ssize_t memtrack_proc_other_total(struct memtrack_proc *p);
+
+/**
+ * memtrack_proc_gl_pss
+ *
+ * Same as memtrack_proc_graphics_total, but counts miscellaneous memory
+ * not tracked by gl or graphics calls above.
+ *
+ * Returns non-negative size in bytes on success, -errno on error.
+ */
+ssize_t memtrack_proc_other_pss(struct memtrack_proc *p);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/system/audio.h b/include/system/audio.h
index abd2990..aa7ac02 100644
--- a/include/system/audio.h
+++ b/include/system/audio.h
@@ -31,6 +31,9 @@
* frameworks/base/include/media/AudioSystem.h
*/
+/* device address used to refer to the standard remote submix */
+#define AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS "0"
+
typedef int audio_io_handle_t;
/* Audio stream types */
@@ -69,6 +72,11 @@
/* play the mix captured by this audio source. */
AUDIO_SOURCE_CNT,
AUDIO_SOURCE_MAX = AUDIO_SOURCE_CNT - 1,
+ AUDIO_SOURCE_HOTWORD = 1999, /* A low-priority, preemptible audio source for
+ for background software hotword detection.
+ Same tuning as AUDIO_SOURCE_VOICE_RECOGNITION.
+ Used only internally to the framework. Not exposed
+ at the audio HAL. */
} audio_source_t;
/* special audio session values
@@ -481,7 +489,8 @@
static inline bool audio_is_remote_submix_device(audio_devices_t device)
{
- if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX))
+ if ((device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX) == AUDIO_DEVICE_OUT_REMOTE_SUBMIX
+ || (device & AUDIO_DEVICE_IN_REMOTE_SUBMIX) == AUDIO_DEVICE_IN_REMOTE_SUBMIX)
return true;
else
return false;
diff --git a/include/system/graphics.h b/include/system/graphics.h
index 5d883be..be86ae4 100644
--- a/include/system/graphics.h
+++ b/include/system/graphics.h
@@ -75,7 +75,7 @@
*
*/
HAL_PIXEL_FORMAT_sRGB_A_8888 = 0xC,
- HAL_PIXEL_FORMAT_sRGB_888 = 0xD,
+ HAL_PIXEL_FORMAT_sRGB_X_8888 = 0xD,
/*
* 0x100 - 0x1FF
@@ -293,6 +293,8 @@
HAL_TRANSFORM_ROT_180 = 0x03,
/* rotate source image 270 degrees clockwise */
HAL_TRANSFORM_ROT_270 = 0x07,
+ /* don't use. see system/window.h */
+ HAL_TRANSFORM_RESERVED = 0x08,
};
#ifdef __cplusplus
diff --git a/include/system/window.h b/include/system/window.h
index 7ce59e0..649bd71 100644
--- a/include/system/window.h
+++ b/include/system/window.h
@@ -296,12 +296,15 @@
NATIVE_WINDOW_TRANSFORM_FLIP_H = HAL_TRANSFORM_FLIP_H ,
/* flip source image vertically */
NATIVE_WINDOW_TRANSFORM_FLIP_V = HAL_TRANSFORM_FLIP_V,
- /* rotate source image 90 degrees clock-wise */
+ /* rotate source image 90 degrees clock-wise, and is applied after TRANSFORM_FLIP_{H|V} */
NATIVE_WINDOW_TRANSFORM_ROT_90 = HAL_TRANSFORM_ROT_90,
/* rotate source image 180 degrees */
NATIVE_WINDOW_TRANSFORM_ROT_180 = HAL_TRANSFORM_ROT_180,
/* rotate source image 270 degrees clock-wise */
NATIVE_WINDOW_TRANSFORM_ROT_270 = HAL_TRANSFORM_ROT_270,
+ /* transforms source by the inverse transform of the screen it is displayed onto. This
+ * transform is applied last */
+ NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY = 0x08
};
/* parameter for NATIVE_WINDOW_SET_SCALING_MODE */
diff --git a/include/sysutils/NetlinkEvent.h b/include/sysutils/NetlinkEvent.h
index 25a56f7..f3501cf 100644
--- a/include/sysutils/NetlinkEvent.h
+++ b/include/sysutils/NetlinkEvent.h
@@ -34,6 +34,8 @@
const static int NlActionChange;
const static int NlActionLinkDown;
const static int NlActionLinkUp;
+ const static int NlActionAddressUpdated;
+ const static int NlActionAddressRemoved;
NetlinkEvent();
virtual ~NetlinkEvent();
@@ -47,6 +49,7 @@
void dump();
protected:
+ bool parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, int rtasize);
bool parseBinaryNetlinkMessage(char *buffer, int size);
bool parseAsciiNetlinkMessage(char *buffer, int size);
};
diff --git a/include/utils/CallStack.h b/include/utils/CallStack.h
index 61dc832..2056751 100644
--- a/include/utils/CallStack.h
+++ b/include/utils/CallStack.h
@@ -17,50 +17,72 @@
#ifndef ANDROID_CALLSTACK_H
#define ANDROID_CALLSTACK_H
-#include <stdint.h>
-#include <sys/types.h>
-
+#include <android/log.h>
#include <utils/String8.h>
#include <corkscrew/backtrace.h>
-// ---------------------------------------------------------------------------
+#include <stdint.h>
+#include <sys/types.h>
namespace android {
-class CallStack
-{
+class Printer;
+
+// Collect/print the call stack (function, file, line) traces for a single thread.
+class CallStack {
public:
enum {
- MAX_DEPTH = 31
+ // Prune the lowest-most stack frames until we have at most MAX_DEPTH.
+ MAX_DEPTH = 31,
+ // Placeholder for specifying the current thread when updating the stack.
+ CURRENT_THREAD = -1,
};
+ // Create an empty call stack. No-op.
CallStack();
+ // Create a callstack with the current thread's stack trace.
+ // Immediately dump it to logcat using the given logtag.
CallStack(const char* logtag, int32_t ignoreDepth=1,
int32_t maxDepth=MAX_DEPTH);
+ // Copy the existing callstack (no other side effects).
CallStack(const CallStack& rhs);
~CallStack();
+ // Copy the existing callstack (no other side effects).
CallStack& operator = (const CallStack& rhs);
-
+
+ // Compare call stacks by their backtrace frame memory.
bool operator == (const CallStack& rhs) const;
bool operator != (const CallStack& rhs) const;
bool operator < (const CallStack& rhs) const;
bool operator >= (const CallStack& rhs) const;
bool operator > (const CallStack& rhs) const;
bool operator <= (const CallStack& rhs) const;
-
+
+ // Get the PC address for the stack frame specified by index.
const void* operator [] (int index) const;
-
+
+ // Reset the stack frames (same as creating an empty call stack).
void clear();
- void update(int32_t ignoreDepth=1, int32_t maxDepth=MAX_DEPTH);
+ // Immediately collect the stack traces for the specified thread.
+ void update(int32_t ignoreDepth=1, int32_t maxDepth=MAX_DEPTH, pid_t tid=CURRENT_THREAD);
- // Dump a stack trace to the log using the supplied logtag
- void dump(const char* logtag, const char* prefix = 0) const;
+ // Dump a stack trace to the log using the supplied logtag.
+ void log(const char* logtag,
+ android_LogPriority priority = ANDROID_LOG_DEBUG,
+ const char* prefix = 0) const;
- // Return a string (possibly very long) containing the complete stack trace
+ // Dump a stack trace to the specified file descriptor.
+ void dump(int fd, int indent = 0, const char* prefix = 0) const;
+
+ // Return a string (possibly very long) containing the complete stack trace.
String8 toString(const char* prefix = 0) const;
-
+
+ // Dump a serialized representation of the stack trace to the specified printer.
+ void print(Printer& printer) const;
+
+ // Get the count of stack frames that are in this call stack.
size_t size() const { return mCount; }
private:
@@ -70,7 +92,4 @@
}; // namespace android
-
-// ---------------------------------------------------------------------------
-
#endif // ANDROID_CALLSTACK_H
diff --git a/include/utils/Printer.h b/include/utils/Printer.h
new file mode 100644
index 0000000..bb66287
--- /dev/null
+++ b/include/utils/Printer.h
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_PRINTER_H
+#define ANDROID_PRINTER_H
+
+#include <android/log.h>
+
+namespace android {
+
+// Interface for printing to an arbitrary data stream
+class Printer {
+public:
+ // Print a new line specified by 'string'. \n is appended automatically.
+ // -- Assumes that the string has no new line in it.
+ virtual void printLine(const char* string = "") = 0;
+
+ // Print a new line specified by the format string. \n is appended automatically.
+ // -- Assumes that the resulting string has no new line in it.
+ virtual void printFormatLine(const char* format, ...) __attribute__((format (printf, 2, 3)));
+
+protected:
+ Printer();
+ virtual ~Printer();
+}; // class Printer
+
+// Print to logcat
+class LogPrinter : public Printer {
+public:
+ // Create a printer using the specified logcat and log priority
+ // - Unless ignoreBlankLines is false, print blank lines to logcat
+ // (Note that the default ALOG behavior is to ignore blank lines)
+ LogPrinter(const char* logtag,
+ android_LogPriority priority = ANDROID_LOG_DEBUG,
+ const char* prefix = 0,
+ bool ignoreBlankLines = false);
+
+ // Print the specified line to logcat. No \n at the end is necessary.
+ virtual void printLine(const char* string);
+
+private:
+ void printRaw(const char* string);
+
+ const char* mLogTag;
+ android_LogPriority mPriority;
+ const char* mPrefix;
+ bool mIgnoreBlankLines;
+}; // class LogPrinter
+
+// Print to a file descriptor
+class FdPrinter : public Printer {
+public:
+ // Create a printer using the specified file descriptor.
+ // - Each line will be prefixed with 'indent' number of blank spaces.
+ // - In addition, each line will be prefixed with the 'prefix' string.
+ FdPrinter(int fd, unsigned int indent = 0, const char* prefix = 0);
+
+ // Print the specified line to the file descriptor. \n is appended automatically.
+ virtual void printLine(const char* string);
+
+private:
+ enum {
+ MAX_FORMAT_STRING = 20,
+ };
+
+ int mFd;
+ unsigned int mIndent;
+ const char* mPrefix;
+ char mFormatString[MAX_FORMAT_STRING];
+}; // class FdPrinter
+
+class String8;
+
+// Print to a String8
+class String8Printer : public Printer {
+public:
+ // Create a printer using the specified String8 as the target.
+ // - In addition, each line will be prefixed with the 'prefix' string.
+ // - target's memory lifetime must be a superset of this String8Printer.
+ String8Printer(String8* target, const char* prefix = 0);
+
+ // Append the specified line to the String8. \n is appended automatically.
+ virtual void printLine(const char* string);
+
+private:
+ String8* mTarget;
+ const char* mPrefix;
+}; // class String8Printer
+
+// Print to an existing Printer by adding a prefix to each line
+class PrefixPrinter : public Printer {
+public:
+ // Create a printer using the specified printer as the target.
+ PrefixPrinter(Printer& printer, const char* prefix);
+
+ // Print the line (prefixed with prefix) using the printer.
+ virtual void printLine(const char* string);
+
+private:
+ Printer& mPrinter;
+ const char* mPrefix;
+};
+
+}; // namespace android
+
+#endif // ANDROID_PRINTER_H
diff --git a/include/utils/ProcessCallStack.h b/include/utils/ProcessCallStack.h
new file mode 100644
index 0000000..4a86869
--- /dev/null
+++ b/include/utils/ProcessCallStack.h
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_PROCESS_CALLSTACK_H
+#define ANDROID_PROCESS_CALLSTACK_H
+
+#include <utils/CallStack.h>
+#include <android/log.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+
+#include <time.h>
+#include <sys/types.h>
+
+namespace android {
+
+class Printer;
+
+// Collect/print the call stack (function, file, line) traces for all threads in a process.
+class ProcessCallStack {
+public:
+ // Create an empty call stack. No-op.
+ ProcessCallStack();
+ // Copy the existing process callstack (no other side effects).
+ ProcessCallStack(const ProcessCallStack& rhs);
+ ~ProcessCallStack();
+
+ // Immediately collect the stack traces for all threads.
+ void update(int32_t maxDepth = CallStack::MAX_DEPTH);
+
+ // Print all stack traces to the log using the supplied logtag.
+ void log(const char* logtag, android_LogPriority priority = ANDROID_LOG_DEBUG,
+ const char* prefix = 0) const;
+
+ // Dump all stack traces to the specified file descriptor.
+ void dump(int fd, int indent = 0, const char* prefix = 0) const;
+
+ // Return a string (possibly very long) containing all the stack traces.
+ String8 toString(const char* prefix = 0) const;
+
+ // Dump a serialized representation of all the stack traces to the specified printer.
+ void print(Printer& printer) const;
+
+ // Get the number of threads whose stack traces were collected.
+ size_t size() const;
+
+private:
+ void printInternal(Printer& printer, Printer& csPrinter) const;
+
+ // Reset the process's stack frames and metadata.
+ void clear();
+
+ struct ThreadInfo {
+ CallStack callStack;
+ String8 threadName;
+ };
+
+ // tid -> ThreadInfo
+ KeyedVector<pid_t, ThreadInfo> mThreadMap;
+ // Time that update() was last called
+ struct tm mTimeUpdated;
+};
+
+}; // namespace android
+
+#endif // ANDROID_PROCESS_CALLSTACK_H
diff --git a/init/builtins.c b/init/builtins.c
index bfc0ddb..e8c8f91 100644
--- a/init/builtins.c
+++ b/init/builtins.c
@@ -57,7 +57,7 @@
{
int fd, ret, len;
- fd = open(path, O_WRONLY|O_CREAT, 0622);
+ fd = open(path, O_WRONLY|O_CREAT|O_NOFOLLOW, 0600);
if (fd < 0)
return -errno;
diff --git a/init/init.c b/init/init.c
index b7e34d0..94a2011 100644
--- a/init/init.c
+++ b/init/init.c
@@ -42,6 +42,7 @@
#include <cutils/android_reboot.h>
#include <cutils/sockets.h>
#include <cutils/iosched_policy.h>
+#include <cutils/fs.h>
#include <private/android_filesystem_config.h>
#include <termios.h>
@@ -559,6 +560,84 @@
return ret;
}
+/*
+ * Writes 512 bytes of output from Hardware RNG (/dev/hw_random, backed
+ * by Linux kernel's hw_random framework) into Linux RNG's via /dev/urandom.
+ * Does nothing if Hardware RNG is not present.
+ *
+ * Since we don't yet trust the quality of Hardware RNG, these bytes are not
+ * mixed into the primary pool of Linux RNG and the entropy estimate is left
+ * unmodified.
+ *
+ * If the HW RNG device /dev/hw_random is present, we require that at least
+ * 512 bytes read from it are written into Linux RNG. QA is expected to catch
+ * devices/configurations where these I/O operations are blocking for a long
+ * time. We do not reboot or halt on failures, as this is a best-effort
+ * attempt.
+ */
+static int mix_hwrng_into_linux_rng_action(int nargs, char **args)
+{
+ int result = -1;
+ int hwrandom_fd = -1;
+ int urandom_fd = -1;
+ char buf[512];
+ ssize_t chunk_size;
+ size_t total_bytes_written = 0;
+
+ hwrandom_fd = TEMP_FAILURE_RETRY(
+ open("/dev/hw_random", O_RDONLY | O_NOFOLLOW));
+ if (hwrandom_fd == -1) {
+ if (errno == ENOENT) {
+ ERROR("/dev/hw_random not found\n");
+ /* It's not an error to not have a Hardware RNG. */
+ result = 0;
+ } else {
+ ERROR("Failed to open /dev/hw_random: %s\n", strerror(errno));
+ }
+ goto ret;
+ }
+
+ urandom_fd = TEMP_FAILURE_RETRY(
+ open("/dev/urandom", O_WRONLY | O_NOFOLLOW));
+ if (urandom_fd == -1) {
+ ERROR("Failed to open /dev/urandom: %s\n", strerror(errno));
+ goto ret;
+ }
+
+ while (total_bytes_written < sizeof(buf)) {
+ chunk_size = TEMP_FAILURE_RETRY(
+ read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
+ if (chunk_size == -1) {
+ ERROR("Failed to read from /dev/hw_random: %s\n", strerror(errno));
+ goto ret;
+ } else if (chunk_size == 0) {
+ ERROR("Failed to read from /dev/hw_random: EOF\n");
+ goto ret;
+ }
+
+ chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
+ if (chunk_size == -1) {
+ ERROR("Failed to write to /dev/urandom: %s\n", strerror(errno));
+ goto ret;
+ }
+ total_bytes_written += chunk_size;
+ }
+
+ INFO("Mixed %d bytes from /dev/hw_random into /dev/urandom",
+ total_bytes_written);
+ result = 0;
+
+ret:
+ if (hwrandom_fd != -1) {
+ close(hwrandom_fd);
+ }
+ if (urandom_fd != -1) {
+ close(urandom_fd);
+ }
+ memset(buf, 0, sizeof(buf));
+ return result;
+}
+
static int keychord_init_action(int nargs, char **args)
{
keychord_init();
@@ -962,6 +1041,7 @@
action_for_each_trigger("early-init", action_add_queue_tail);
queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
+ queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
queue_builtin_action(keychord_init_action, "keychord_init");
queue_builtin_action(console_init_action, "console_init");
@@ -976,6 +1056,11 @@
action_for_each_trigger("post-fs-data", action_add_queue_tail);
}
+ /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
+ * wasn't ready immediately after wait_for_coldboot_done
+ */
+ queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
+
queue_builtin_action(property_service_init_action, "property_service_init");
queue_builtin_action(signal_init_action, "signal_init");
queue_builtin_action(check_startup_action, "check_startup");
diff --git a/init/property_service.c b/init/property_service.c
index 9afc756..9ac2781 100644
--- a/init/property_service.c
+++ b/init/property_service.c
@@ -276,6 +276,34 @@
}
}
+static bool is_legal_property_name(const char* name, size_t namelen)
+{
+ size_t i;
+ bool previous_was_dot = false;
+ if (namelen >= PROP_NAME_MAX) return false;
+ if (namelen < 1) return false;
+ if (name[0] == '.') return false;
+ if (name[namelen - 1] == '.') return false;
+
+ /* Only allow alphanumeric, plus '.', '-', or '_' */
+ /* Don't allow ".." to appear in a property name */
+ for (i = 0; i < namelen; i++) {
+ if (name[i] == '.') {
+ if (previous_was_dot == true) return false;
+ previous_was_dot = true;
+ continue;
+ }
+ previous_was_dot = false;
+ if (name[i] == '_' || name[i] == '-') continue;
+ if (name[i] >= 'a' && name[i] <= 'z') continue;
+ if (name[i] >= 'A' && name[i] <= 'Z') continue;
+ if (name[i] >= '0' && name[i] <= '9') continue;
+ return false;
+ }
+
+ return true;
+}
+
int property_set(const char *name, const char *value)
{
prop_info *pi;
@@ -284,9 +312,8 @@
size_t namelen = strlen(name);
size_t valuelen = strlen(value);
- if(namelen >= PROP_NAME_MAX) return -1;
- if(valuelen >= PROP_VALUE_MAX) return -1;
- if(namelen < 1) return -1;
+ if (!is_legal_property_name(name, namelen)) return -1;
+ if (valuelen >= PROP_VALUE_MAX) return -1;
pi = (prop_info*) __system_property_find(name);
@@ -298,7 +325,7 @@
} else {
ret = __system_property_add(name, namelen, value, valuelen);
if (ret < 0) {
- ERROR("Failed to set '%s'='%s'", name, value);
+ ERROR("Failed to set '%s'='%s'\n", name, value);
return ret;
}
}
@@ -364,6 +391,12 @@
msg.name[PROP_NAME_MAX-1] = 0;
msg.value[PROP_VALUE_MAX-1] = 0;
+ if (!is_legal_property_name(msg.name, strlen(msg.name))) {
+ ERROR("sys_prop: illegal property name. Got: \"%s\"\n", msg.name);
+ close(s);
+ return;
+ }
+
getpeercon(s, &source_ctx);
if(memcmp(msg.name,"ctl.",4) == 0) {
diff --git a/libcutils/fs.c b/libcutils/fs.c
index 116526d..286a8eb 100644
--- a/libcutils/fs.c
+++ b/libcutils/fs.c
@@ -16,6 +16,11 @@
#define LOG_TAG "cutils"
+/* These defines are only needed because prebuilt headers are out of date */
+#define __USE_XOPEN2K8 1
+#define _ATFILE_SOURCE 1
+#define _GNU_SOURCE 1
+
#include <cutils/fs.h>
#include <cutils/log.h>
@@ -27,6 +32,7 @@
#include <string.h>
#include <limits.h>
#include <stdlib.h>
+#include <dirent.h>
#define ALL_PERMS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
#define BUF_SIZE 64
@@ -141,3 +147,91 @@
unlink(temp);
return -1;
}
+
+#ifndef __APPLE__
+
+int fs_mkdirs(const char* path, mode_t mode) {
+ int res = 0;
+ int fd = 0;
+ struct stat sb;
+ char* buf = strdup(path);
+
+ if (*buf != '/') {
+ ALOGE("Relative paths are not allowed: %s", buf);
+ res = -EINVAL;
+ goto done;
+ }
+
+ if ((fd = open("/", 0)) == -1) {
+ ALOGE("Failed to open(/): %s", strerror(errno));
+ res = -errno;
+ goto done;
+ }
+
+ char* segment = buf + 1;
+ char* p = segment;
+ while (*p != '\0') {
+ if (*p == '/') {
+ *p = '\0';
+
+ if (!strcmp(segment, "..") || !strcmp(segment, ".") || !strcmp(segment, "")) {
+ ALOGE("Invalid path: %s", buf);
+ res = -EINVAL;
+ goto done_close;
+ }
+
+ if (fstatat(fd, segment, &sb, AT_SYMLINK_NOFOLLOW) != 0) {
+ if (errno == ENOENT) {
+ /* Nothing there yet; let's create it! */
+ if (mkdirat(fd, segment, mode) != 0) {
+ if (errno == EEXIST) {
+ /* We raced with someone; ignore */
+ } else {
+ ALOGE("Failed to mkdirat(%s): %s", buf, strerror(errno));
+ res = -errno;
+ goto done_close;
+ }
+ }
+ } else {
+ ALOGE("Failed to fstatat(%s): %s", buf, strerror(errno));
+ res = -errno;
+ goto done_close;
+ }
+ } else {
+ if (S_ISLNK(sb.st_mode)) {
+ ALOGE("Symbolic links are not allowed: %s", buf);
+ res = -ELOOP;
+ goto done_close;
+ }
+ if (!S_ISDIR(sb.st_mode)) {
+ ALOGE("Existing segment not a directory: %s", buf);
+ res = -ENOTDIR;
+ goto done_close;
+ }
+ }
+
+ /* Yay, segment is ready for us to step into */
+ int next_fd;
+ if ((next_fd = openat(fd, segment, 0)) == -1) {
+ ALOGE("Failed to openat(%s): %s", buf, strerror(errno));
+ res = -errno;
+ goto done_close;
+ }
+
+ close(fd);
+ fd = next_fd;
+
+ *p = '/';
+ segment = p + 1;
+ }
+ p++;
+ }
+
+done_close:
+ close(fd);
+done:
+ free(buf);
+ return res;
+}
+
+#endif
diff --git a/libmemtrack/Android.mk b/libmemtrack/Android.mk
new file mode 100644
index 0000000..a8fb3eb
--- /dev/null
+++ b/libmemtrack/Android.mk
@@ -0,0 +1,19 @@
+# Copyright 2013 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := memtrack.c
+LOCAL_MODULE := libmemtrack
+LOCAL_C_INCLUDES += hardware/libhardware/include
+LOCAL_SHARED_LIBRARIES := libhardware liblog
+LOCAL_CFLAGS := -Wall -Werror
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := memtrack_test.c
+LOCAL_MODULE := memtrack_test
+LOCAL_C_INCLUDES := $(call include-path-for, libpagemap)
+LOCAL_SHARED_LIBRARIES := libmemtrack libpagemap
+LOCAL_CFLAGS := -Wall -Werror
+include $(BUILD_EXECUTABLE)
diff --git a/libmemtrack/memtrack.c b/libmemtrack/memtrack.c
new file mode 100644
index 0000000..9a656df
--- /dev/null
+++ b/libmemtrack/memtrack.c
@@ -0,0 +1,200 @@
+/*
+ * 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 <memtrack/memtrack.h>
+
+#define LOG_TAG "memtrack"
+
+#include <log/log.h>
+
+#include <hardware/memtrack.h>
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+
+static const memtrack_module_t *module;
+
+struct memtrack_proc {
+ pid_t pid;
+ struct memtrack_proc_type {
+ enum memtrack_type type;
+ size_t num_records;
+ size_t allocated_records;
+ struct memtrack_record *records;
+ } types[MEMTRACK_NUM_TYPES];
+};
+
+int memtrack_init(void)
+{
+ int err;
+
+ if (module) {
+ return 0;
+ }
+
+ err = hw_get_module(MEMTRACK_HARDWARE_MODULE_ID,
+ (hw_module_t const**)&module);
+ if (err) {
+ ALOGE("Couldn't load %s module (%s)", MEMTRACK_HARDWARE_MODULE_ID,
+ strerror(-err));
+ return err;
+ }
+
+ return module->init(module);
+}
+
+struct memtrack_proc *memtrack_proc_new(void)
+{
+ if (!module) {
+ return NULL;
+ }
+
+ return calloc(sizeof(struct memtrack_proc), 1);
+}
+
+void memtrack_proc_destroy(struct memtrack_proc *p)
+{
+ enum memtrack_type i;
+
+ if (p) {
+ for (i = 0; i < MEMTRACK_NUM_TYPES; i++) {
+ free(p->types[i].records);
+ }
+ }
+ free(p);
+}
+
+static int memtrack_proc_get_type(struct memtrack_proc_type *t,
+ pid_t pid, enum memtrack_type type)
+{
+ size_t num_records = t->num_records;
+ int ret;
+
+retry:
+ ret = module->getMemory(module, pid, type, t->records, &num_records);
+ if (ret) {
+ t->num_records = 0;
+ return ret;
+ }
+ if (num_records > t->allocated_records) {
+ /* Need more records than allocated */
+ free(t->records);
+ t->records = calloc(sizeof(*t->records), num_records);
+ if (!t->records) {
+ return -ENOMEM;
+ }
+ t->allocated_records = num_records;
+ goto retry;
+ }
+ t->num_records = num_records;
+
+ return 0;
+}
+
+/* TODO: sanity checks on return values from HALs:
+ * make sure no records have invalid flags set
+ * - unknown flags
+ * - too many flags of a single category
+ * - missing ACCOUNTED/UNACCOUNTED
+ * make sure there are not overlapping SHARED and SHARED_PSS records
+ */
+static int memtrack_proc_sanity_check(struct memtrack_proc *p)
+{
+ (void)p;
+ return 0;
+}
+
+int memtrack_proc_get(struct memtrack_proc *p, pid_t pid)
+{
+ enum memtrack_type i;
+
+ if (!module) {
+ return -EINVAL;
+ }
+
+ if (!p) {
+ return -EINVAL;
+ }
+
+ p->pid = pid;
+ for (i = 0; i < MEMTRACK_NUM_TYPES; i++) {
+ memtrack_proc_get_type(&p->types[i], pid, i);
+ }
+
+ return memtrack_proc_sanity_check(p);
+}
+
+static ssize_t memtrack_proc_sum(struct memtrack_proc *p,
+ enum memtrack_type types[], size_t num_types,
+ unsigned int flags)
+{
+ ssize_t sum = 0;
+ size_t i;
+ size_t j;
+
+ for (i = 0; i < num_types; i++) {
+ enum memtrack_type type = types[i];
+ for (j = 0; j < p->types[type].num_records; j++) {
+ if ((p->types[type].records[j].flags & flags) == flags) {
+ sum += p->types[type].records[j].size_in_bytes;
+ }
+ }
+ }
+
+ return sum;
+}
+
+ssize_t memtrack_proc_graphics_total(struct memtrack_proc *p)
+{
+ enum memtrack_type types[] = { MEMTRACK_TYPE_GRAPHICS };
+ return memtrack_proc_sum(p, types, ARRAY_SIZE(types), 0);
+}
+
+ssize_t memtrack_proc_graphics_pss(struct memtrack_proc *p)
+{
+ enum memtrack_type types[] = { MEMTRACK_TYPE_GRAPHICS };
+ return memtrack_proc_sum(p, types, ARRAY_SIZE(types),
+ MEMTRACK_FLAG_SMAPS_UNACCOUNTED);
+}
+
+ssize_t memtrack_proc_gl_total(struct memtrack_proc *p)
+{
+ enum memtrack_type types[] = { MEMTRACK_TYPE_GL };
+ return memtrack_proc_sum(p, types, ARRAY_SIZE(types), 0);
+}
+
+ssize_t memtrack_proc_gl_pss(struct memtrack_proc *p)
+{
+ enum memtrack_type types[] = { MEMTRACK_TYPE_GL };
+ return memtrack_proc_sum(p, types, ARRAY_SIZE(types),
+ MEMTRACK_FLAG_SMAPS_UNACCOUNTED);
+}
+
+ssize_t memtrack_proc_other_total(struct memtrack_proc *p)
+{
+ enum memtrack_type types[] = { MEMTRACK_TYPE_MULTIMEDIA,
+ MEMTRACK_TYPE_CAMERA,
+ MEMTRACK_TYPE_OTHER };
+ return memtrack_proc_sum(p, types, ARRAY_SIZE(types), 0);
+}
+
+ssize_t memtrack_proc_other_pss(struct memtrack_proc *p)
+{
+ enum memtrack_type types[] = { MEMTRACK_TYPE_MULTIMEDIA,
+ MEMTRACK_TYPE_CAMERA,
+ MEMTRACK_TYPE_OTHER };
+ return memtrack_proc_sum(p, types, ARRAY_SIZE(types),
+ MEMTRACK_FLAG_SMAPS_UNACCOUNTED);
+}
diff --git a/libmemtrack/memtrack_test.c b/libmemtrack/memtrack_test.c
new file mode 100644
index 0000000..cd94bc5
--- /dev/null
+++ b/libmemtrack/memtrack_test.c
@@ -0,0 +1,145 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <memtrack/memtrack.h>
+
+#include <pagemap/pagemap.h>
+
+#define DIV_ROUND_UP(x,y) (((x) + (y) - 1) / (y))
+
+static int getprocname(pid_t pid, char *buf, int len) {
+ char *filename;
+ FILE *f;
+ int rc = 0;
+ static const char* unknown_cmdline = "<unknown>";
+
+ if (len <= 0) {
+ return -1;
+ }
+
+ if (asprintf(&filename, "/proc/%zd/cmdline", pid) < 0) {
+ rc = 1;
+ goto exit;
+ }
+
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ rc = 2;
+ goto releasefilename;
+ }
+
+ if (fgets(buf, len, f) == NULL) {
+ rc = 3;
+ goto closefile;
+ }
+
+closefile:
+ (void) fclose(f);
+releasefilename:
+ free(filename);
+exit:
+ if (rc != 0) {
+ /*
+ * The process went away before we could read its process name. Try
+ * to give the user "<unknown>" here, but otherwise they get to look
+ * at a blank.
+ */
+ if (strlcpy(buf, unknown_cmdline, (size_t)len) >= (size_t)len) {
+ rc = 4;
+ }
+ }
+
+ return rc;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+ pm_kernel_t *ker;
+ size_t num_procs;
+ pid_t *pids;
+ struct memtrack_proc *p;
+ size_t i;
+
+ (void)argc;
+ (void)argv;
+
+ ret = memtrack_init();
+ if (ret < 0) {
+ fprintf(stderr, "failed to initialize HAL: %s (%d)\n", strerror(-ret), ret);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = pm_kernel_create(&ker);
+ if (ret) {
+ fprintf(stderr, "Error creating kernel interface -- "
+ "does this kernel have pagemap?\n");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = pm_kernel_pids(ker, &pids, &num_procs);
+ if (ret) {
+ fprintf(stderr, "Error listing processes.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ p = memtrack_proc_new();
+ if (ret) {
+ fprintf(stderr, "failed to create memtrack process handle\n");
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < num_procs; i++) {
+ pid_t pid = pids[i];
+ char cmdline[256];
+ size_t v1;
+ size_t v2;
+ size_t v3;
+ size_t v4;
+ size_t v5;
+ size_t v6;
+
+ getprocname(pid, cmdline, (int)sizeof(cmdline));
+
+ ret = memtrack_proc_get(p, pid);
+ if (ret) {
+ fprintf(stderr, "failed to get memory info for pid %d: %s (%d)\n",
+ pid, strerror(-ret), ret);
+ continue;
+ }
+
+ v1 = DIV_ROUND_UP(memtrack_proc_graphics_total(p), 1024);
+ v2 = DIV_ROUND_UP(memtrack_proc_graphics_pss(p), 1024);
+ v3 = DIV_ROUND_UP(memtrack_proc_gl_total(p), 1024);
+ v4 = DIV_ROUND_UP(memtrack_proc_gl_pss(p), 1024);
+ v5 = DIV_ROUND_UP(memtrack_proc_other_total(p), 1024);
+ v6 = DIV_ROUND_UP(memtrack_proc_other_pss(p), 1024);
+
+ if (v1 | v2 | v3 | v4 | v5 | v6) {
+ printf("%5d %6zu %6zu %6zu %6zu %6zu %6zu %s\n", pid,
+ v1, v2, v3, v4, v5, v6, cmdline);
+ }
+ }
+
+ memtrack_proc_destroy(p);
+
+ return 0;
+}
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 4beebb7..aae2ae7 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -23,6 +23,10 @@
#include <sys/types.h>
#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
#include <linux/if.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter_ipv4/ipt_ULOG.h>
@@ -38,6 +42,8 @@
const int NetlinkEvent::NlActionChange = 3;
const int NetlinkEvent::NlActionLinkUp = 4;
const int NetlinkEvent::NlActionLinkDown = 5;
+const int NetlinkEvent::NlActionAddressUpdated = 6;
+const int NetlinkEvent::NlActionAddressRemoved = 7;
NetlinkEvent::NetlinkEvent() {
mAction = NlActionUnknown;
@@ -70,13 +76,110 @@
}
/*
+ * Decode a RTM_NEWADDR or RTM_DELADDR message.
+ */
+bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr,
+ int rtasize) {
+ struct rtattr *rta;
+ struct ifa_cacheinfo *cacheinfo = NULL;
+ char addrstr[INET6_ADDRSTRLEN] = "";
+
+ // Sanity check.
+ 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";
+
+ for (rta = IFA_RTA(ifaddr); RTA_OK(rta, rtasize);
+ rta = RTA_NEXT(rta, rtasize)) {
+ 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);
+ continue;
+ }
+
+ // Convert the IP address to a string.
+ if (ifaddr->ifa_family == AF_INET) {
+ struct in_addr *addr4 = (struct in_addr *) RTA_DATA(rta);
+ if (RTA_PAYLOAD(rta) < sizeof(*addr4)) {
+ SLOGE("Short IPv4 address (%d bytes) in %s",
+ RTA_PAYLOAD(rta), msgtype);
+ continue;
+ }
+ inet_ntop(AF_INET, addr4, addrstr, sizeof(addrstr));
+ } else if (ifaddr->ifa_family == AF_INET6) {
+ struct in6_addr *addr6 = (struct in6_addr *) RTA_DATA(rta);
+ if (RTA_PAYLOAD(rta) < sizeof(*addr6)) {
+ SLOGE("Short IPv6 address (%d bytes) in %s",
+ RTA_PAYLOAD(rta), msgtype);
+ continue;
+ }
+ inet_ntop(AF_INET6, addr6, addrstr, sizeof(addrstr));
+ } else {
+ SLOGE("Unknown address family %d\n", ifaddr->ifa_family);
+ continue;
+ }
+
+ // 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);
+ continue;
+ }
+
+ if (RTA_PAYLOAD(rta) < sizeof(*cacheinfo)) {
+ SLOGE("Short IFA_CACHEINFO (%d vs. %d bytes) in %s",
+ RTA_PAYLOAD(rta), sizeof(cacheinfo), msgtype);
+ continue;
+ }
+
+ 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);
+ }
+ }
+
+ if (addrstr[0] == '\0') {
+ SLOGE("No IFA_ADDRESS in %s\n", msgtype);
+ return false;
+ }
+
+ return true;
+}
+
+/*
* Parse an binary message from a NETLINK_ROUTE netlink socket.
*/
bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) {
- size_t sz = size;
- const struct nlmsghdr *nh = (struct nlmsghdr *) buffer;
+ const struct nlmsghdr *nh;
- while (NLMSG_OK(nh, sz) && (nh->nlmsg_type != NLMSG_DONE)) {
+ for (nh = (struct nlmsghdr *) buffer;
+ NLMSG_OK(nh, size) && (nh->nlmsg_type != NLMSG_DONE);
+ nh = NLMSG_NEXT(nh, size)) {
if (nh->nlmsg_type == RTM_NEWLINK) {
int len = nh->nlmsg_len - sizeof(*nh);
@@ -127,10 +230,24 @@
mSubsystem = strdup("qlog");
mAction = NlActionChange;
+ } else if (nh->nlmsg_type == RTM_NEWADDR ||
+ nh->nlmsg_type == RTM_DELADDR) {
+ int len = nh->nlmsg_len - sizeof(*nh);
+ struct ifaddrmsg *ifa;
+
+ 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 {
SLOGD("Unexpected netlink message. type=0x%x\n", nh->nlmsg_type);
}
- nh = NLMSG_NEXT(nh, size);
}
return true;
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index b967342..8be393e 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -51,7 +51,8 @@
#include "usbhost/usbhost.h"
#define DEV_DIR "/dev"
-#define USB_FS_DIR DEV_DIR "/bus/usb"
+#define DEV_BUS_DIR DEV_DIR "/bus"
+#define USB_FS_DIR DEV_BUS_DIR "/usb"
#define USB_FS_ID_SCANNER USB_FS_DIR "/%d/%d"
#define USB_FS_ID_FORMAT USB_FS_DIR "/%03d/%03d"
@@ -68,6 +69,7 @@
void *data;
int wds[MAX_USBFS_WD_COUNT];
int wdd;
+ int wddbus;
};
struct usb_device {
@@ -195,6 +197,7 @@
D("Created device discovery thread\n");
/* watch for files added and deleted within USB_FS_DIR */
+ context->wddbus = -1;
for (i = 0; i < MAX_USBFS_WD_COUNT; i++)
context->wds[i] = -1;
@@ -228,15 +231,25 @@
ret = read(context->fd, event_buf, sizeof(event_buf));
if (ret >= (int)sizeof(struct inotify_event)) {
- while (offset < ret) {
+ while (offset < ret && !done) {
event = (struct inotify_event*)&event_buf[offset];
done = 0;
wd = event->wd;
if (wd == context->wdd) {
if ((event->mask & IN_CREATE) && !strcmp(event->name, "bus")) {
+ context->wddbus = inotify_add_watch(context->fd, DEV_BUS_DIR, IN_CREATE | IN_DELETE);
+ if (context->wddbus < 0) {
+ done = 1;
+ } else {
+ watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);
+ done = find_existing_devices(context->cb_added, context->data);
+ }
+ }
+ } else if (wd == context->wddbus) {
+ if ((event->mask & IN_CREATE) && !strcmp(event->name, "usb")) {
watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);
done = find_existing_devices(context->cb_added, context->data);
- } else if ((event->mask & IN_DELETE) && !strcmp(event->name, "bus")) {
+ } else if ((event->mask & IN_DELETE) && !strcmp(event->name, "usb")) {
for (i = 0; i < MAX_USBFS_WD_COUNT; i++) {
if (context->wds[i] >= 0) {
inotify_rm_watch(context->fd, context->wds[i]);
diff --git a/libutils/Android.mk b/libutils/Android.mk
index 7e6b1be..720443e 100644
--- a/libutils/Android.mk
+++ b/libutils/Android.mk
@@ -26,6 +26,8 @@
LinearAllocator.cpp \
LinearTransform.cpp \
Log.cpp \
+ Printer.cpp \
+ ProcessCallStack.cpp \
PropertyMap.cpp \
RefBase.cpp \
SharedBuffer.cpp \
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
index e60f5d8..4ceaa7c 100644
--- a/libutils/CallStack.cpp
+++ b/libutils/CallStack.cpp
@@ -16,14 +16,12 @@
#define LOG_TAG "CallStack"
-#include <string.h>
-
-#include <utils/Log.h>
-#include <utils/Errors.h>
#include <utils/CallStack.h>
+#include <utils/Printer.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
#include <corkscrew/backtrace.h>
-/*****************************************************************************/
namespace android {
CallStack::CallStack() :
@@ -31,8 +29,8 @@
}
CallStack::CallStack(const char* logtag, int32_t ignoreDepth, int32_t maxDepth) {
- this->update(ignoreDepth+1, maxDepth);
- this->dump(logtag);
+ this->update(ignoreDepth+1, maxDepth, CURRENT_THREAD);
+ this->log(logtag);
}
CallStack::CallStack(const CallStack& rhs) :
@@ -93,31 +91,44 @@
mCount = 0;
}
-void CallStack::update(int32_t ignoreDepth, int32_t maxDepth) {
+void CallStack::update(int32_t ignoreDepth, int32_t maxDepth, pid_t tid) {
if (maxDepth > MAX_DEPTH) {
maxDepth = MAX_DEPTH;
}
- ssize_t count = unwind_backtrace(mStack, ignoreDepth + 1, maxDepth);
+ ssize_t count;
+
+ if (tid >= 0) {
+ count = unwind_backtrace_thread(tid, mStack, ignoreDepth + 1, maxDepth);
+ } else if (tid == CURRENT_THREAD) {
+ count = unwind_backtrace(mStack, ignoreDepth + 1, maxDepth);
+ } else {
+ ALOGE("%s: Invalid tid specified (%d)", __FUNCTION__, tid);
+ count = 0;
+ }
+
mCount = count > 0 ? count : 0;
}
-void CallStack::dump(const char* logtag, const char* prefix) const {
- backtrace_symbol_t symbols[mCount];
+void CallStack::log(const char* logtag, android_LogPriority priority, const char* prefix) const {
+ LogPrinter printer(logtag, priority, prefix, /*ignoreBlankLines*/false);
+ print(printer);
+}
- get_backtrace_symbols(mStack, mCount, symbols);
- for (size_t i = 0; i < mCount; i++) {
- char line[MAX_BACKTRACE_LINE_LENGTH];
- format_backtrace_line(i, &mStack[i], &symbols[i],
- line, MAX_BACKTRACE_LINE_LENGTH);
- ALOG(LOG_DEBUG, logtag, "%s%s",
- prefix ? prefix : "",
- line);
- }
- free_backtrace_symbols(symbols, mCount);
+void CallStack::dump(int fd, int indent, const char* prefix) const {
+ FdPrinter printer(fd, indent, prefix);
+ print(printer);
}
String8 CallStack::toString(const char* prefix) const {
String8 str;
+
+ String8Printer printer(&str, prefix);
+ print(printer);
+
+ return str;
+}
+
+void CallStack::print(Printer& printer) const {
backtrace_symbol_t symbols[mCount];
get_backtrace_symbols(mStack, mCount, symbols);
@@ -125,14 +136,9 @@
char line[MAX_BACKTRACE_LINE_LENGTH];
format_backtrace_line(i, &mStack[i], &symbols[i],
line, MAX_BACKTRACE_LINE_LENGTH);
- if (prefix) {
- str.append(prefix);
- }
- str.append(line);
- str.append("\n");
+ printer.printLine(line);
}
free_backtrace_symbols(symbols, mCount);
- return str;
}
}; // namespace android
diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp
new file mode 100644
index 0000000..ac729e0
--- /dev/null
+++ b/libutils/Printer.cpp
@@ -0,0 +1,163 @@
+/*
+ * 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 "Printer"
+// #define LOG_NDEBUG 0
+
+#include <utils/Printer.h>
+#include <utils/String8.h>
+#include <utils/Log.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef __BIONIC__
+#define fdprintf dprintf
+#endif
+
+namespace android {
+
+/*
+ * Implementation of Printer
+ */
+Printer::Printer() {
+ // Intentionally left empty
+}
+
+Printer::~Printer() {
+ // Intentionally left empty
+}
+
+void Printer::printFormatLine(const char* format, ...) {
+ va_list arglist;
+ va_start(arglist, format);
+
+ char* formattedString;
+
+#ifndef USE_MINGW
+ if (vasprintf(&formattedString, format, arglist) < 0) { // returns -1 on error
+ ALOGE("%s: Failed to format string", __FUNCTION__);
+ return;
+ }
+#else
+ return;
+#endif
+
+ va_end(arglist);
+
+ printLine(formattedString);
+ free(formattedString);
+}
+
+/*
+ * Implementation of LogPrinter
+ */
+LogPrinter::LogPrinter(const char* logtag,
+ android_LogPriority priority,
+ const char* prefix,
+ bool ignoreBlankLines) :
+ mLogTag(logtag),
+ mPriority(priority),
+ mPrefix(prefix ?: ""),
+ mIgnoreBlankLines(ignoreBlankLines) {
+}
+
+void LogPrinter::printLine(const char* string) {
+ if (string == NULL) {
+ ALOGW("%s: NULL string passed in", __FUNCTION__);
+ return;
+ }
+
+ if (mIgnoreBlankLines || (*string)) {
+ // Simple case: Line is not blank, or we don't care about printing blank lines
+ printRaw(string);
+ } else {
+ // Force logcat to print empty lines by adding prefixing with a space
+ printRaw(" ");
+ }
+}
+
+void LogPrinter::printRaw(const char* string) {
+ __android_log_print(mPriority, mLogTag, "%s%s", mPrefix, string);
+}
+
+
+/*
+ * Implementation of FdPrinter
+ */
+FdPrinter::FdPrinter(int fd, unsigned int indent, const char* prefix) :
+ mFd(fd), mIndent(indent), mPrefix(prefix ?: "") {
+
+ if (fd < 0) {
+ ALOGW("%s: File descriptor out of range (%d)", __FUNCTION__, fd);
+ }
+
+ // <indent><prefix><line> -- e.g. '%-4s%s\n' for indent=4
+ snprintf(mFormatString, sizeof(mFormatString), "%%-%us%%s\n", mIndent);
+}
+
+void FdPrinter::printLine(const char* string) {
+ if (string == NULL) {
+ ALOGW("%s: NULL string passed in", __FUNCTION__);
+ return;
+ } else if (mFd < 0) {
+ ALOGW("%s: File descriptor out of range (%d)", __FUNCTION__, mFd);
+ return;
+ }
+
+#ifndef USE_MINGW
+ fdprintf(mFd, mFormatString, mPrefix, string);
+#endif
+}
+
+/*
+ * Implementation of String8Printer
+ */
+String8Printer::String8Printer(String8* target, const char* prefix) :
+ mTarget(target),
+ mPrefix(prefix ?: "") {
+
+ if (target == NULL) {
+ ALOGW("%s: Target string was NULL", __FUNCTION__);
+ }
+}
+
+void String8Printer::printLine(const char* string) {
+ if (string == NULL) {
+ ALOGW("%s: NULL string passed in", __FUNCTION__);
+ return;
+ } else if (mTarget == NULL) {
+ ALOGW("%s: Target string was NULL", __FUNCTION__);
+ return;
+ }
+
+ mTarget->append(string);
+ mTarget->append("\n");
+}
+
+/*
+ * Implementation of PrefixPrinter
+ */
+PrefixPrinter::PrefixPrinter(Printer& printer, const char* prefix) :
+ mPrinter(printer), mPrefix(prefix ?: "") {
+}
+
+void PrefixPrinter::printLine(const char* string) {
+ mPrinter.printFormatLine("%s%s", mPrefix, string);
+}
+
+}; //namespace android
diff --git a/libutils/ProcessCallStack.cpp b/libutils/ProcessCallStack.cpp
new file mode 100644
index 0000000..f9340c5
--- /dev/null
+++ b/libutils/ProcessCallStack.cpp
@@ -0,0 +1,260 @@
+/*
+ * 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 "ProcessCallStack"
+// #define LOG_NDEBUG 0
+
+#include <string.h>
+#include <stdio.h>
+#include <dirent.h>
+
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <utils/ProcessCallStack.h>
+#include <utils/Printer.h>
+
+#include <limits.h>
+
+namespace android {
+
+enum {
+ // Max sizes for various dynamically generated strings
+ MAX_TIME_STRING = 64,
+ MAX_PROC_PATH = 1024,
+
+ // Dump related prettiness constants
+ IGNORE_DEPTH_CURRENT_THREAD = 2,
+};
+
+static const char* CALL_STACK_PREFIX = " ";
+static const char* PATH_THREAD_NAME = "/proc/self/task/%d/comm";
+static const char* PATH_SELF_TASK = "/proc/self/task";
+
+static void dumpProcessHeader(Printer& printer, pid_t pid, const char* timeStr) {
+ if (timeStr == NULL) {
+ ALOGW("%s: timeStr was NULL", __FUNCTION__);
+ return;
+ }
+
+ char path[PATH_MAX];
+ char procNameBuf[MAX_PROC_PATH];
+ char* procName = NULL;
+ FILE* fp;
+
+ snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
+ if ((fp = fopen(path, "r"))) {
+ procName = fgets(procNameBuf, sizeof(procNameBuf), fp);
+ fclose(fp);
+ }
+
+ if (!procName) {
+ procName = const_cast<char*>("<unknown>");
+ }
+
+ printer.printLine();
+ printer.printLine();
+ printer.printFormatLine("----- pid %d at %s -----", pid, timeStr);
+ printer.printFormatLine("Cmd line: %s", procName);
+}
+
+static void dumpProcessFooter(Printer& printer, pid_t pid) {
+ printer.printLine();
+ printer.printFormatLine("----- end %d -----", pid);
+ printer.printLine();
+}
+
+static String8 getThreadName(pid_t tid) {
+ char path[PATH_MAX];
+ char* procName = NULL;
+ char procNameBuf[MAX_PROC_PATH];
+ FILE* fp;
+
+ snprintf(path, sizeof(path), PATH_THREAD_NAME, tid);
+ if ((fp = fopen(path, "r"))) {
+ procName = fgets(procNameBuf, sizeof(procNameBuf), fp);
+ fclose(fp);
+ } else {
+ ALOGE("%s: Failed to open %s", __FUNCTION__, path);
+ }
+
+ // Strip ending newline
+ strtok(procName, "\n");
+
+ return String8(procName);
+}
+
+static String8 getTimeString(struct tm tm) {
+ char timestr[MAX_TIME_STRING];
+ // i.e. '2013-10-22 14:42:05'
+ strftime(timestr, sizeof(timestr), "%F %T", &tm);
+
+ return String8(timestr);
+}
+
+/*
+ * Implementation of ProcessCallStack
+ */
+ProcessCallStack::ProcessCallStack() {
+}
+
+ProcessCallStack::ProcessCallStack(const ProcessCallStack& rhs) :
+ mThreadMap(rhs.mThreadMap),
+ mTimeUpdated(rhs.mTimeUpdated) {
+}
+
+ProcessCallStack::~ProcessCallStack() {
+}
+
+void ProcessCallStack::clear() {
+ mThreadMap.clear();
+ mTimeUpdated = tm();
+}
+
+void ProcessCallStack::update(int32_t maxDepth) {
+ DIR *dp;
+ struct dirent *ep;
+ struct dirent entry;
+
+ dp = opendir(PATH_SELF_TASK);
+ if (dp == NULL) {
+ ALOGE("%s: Failed to update the process's call stacks (errno = %d, '%s')",
+ __FUNCTION__, errno, strerror(errno));
+ return;
+ }
+
+ pid_t selfPid = getpid();
+
+ clear();
+
+ // Get current time.
+#ifndef USE_MINGW
+ {
+ time_t t = time(NULL);
+ struct tm tm;
+ localtime_r(&t, &tm);
+
+ mTimeUpdated = tm;
+ }
+
+ /*
+ * Each tid is a directory inside of /proc/self/task
+ * - Read every file in directory => get every tid
+ */
+ int code;
+ while ((code = readdir_r(dp, &entry, &ep)) == 0 && ep != NULL) {
+ pid_t tid = -1;
+ sscanf(ep->d_name, "%d", &tid);
+
+ if (tid < 0) {
+ // Ignore '.' and '..'
+ ALOGV("%s: Failed to read tid from %s/%s",
+ __FUNCTION__, PATH_SELF_TASK, ep->d_name);
+ continue;
+ }
+
+ ssize_t idx = mThreadMap.add(tid, ThreadInfo());
+ if (idx < 0) { // returns negative error value on error
+ ALOGE("%s: Failed to add new ThreadInfo (errno = %zd, '%s')",
+ __FUNCTION__, idx, strerror(-idx));
+ continue;
+ }
+
+ ThreadInfo& threadInfo = mThreadMap.editValueAt(static_cast<size_t>(idx));
+
+ /*
+ * Ignore CallStack::update and ProcessCallStack::update for current thread
+ * - Every other thread doesn't need this since we call update off-thread
+ */
+ int ignoreDepth = (selfPid == tid) ? IGNORE_DEPTH_CURRENT_THREAD : 0;
+
+ // Update thread's call stacks
+ CallStack& cs = threadInfo.callStack;
+ cs.update(ignoreDepth, maxDepth, tid);
+
+ // Read/save thread name
+ threadInfo.threadName = getThreadName(tid);
+
+ ALOGV("%s: Got call stack for tid %d (size %zu)",
+ __FUNCTION__, tid, cs.size());
+ }
+ if (code != 0) { // returns positive error value on error
+ ALOGE("%s: Failed to readdir from %s (errno = %d, '%s')",
+ __FUNCTION__, PATH_SELF_TASK, -code, strerror(code));
+ }
+#endif
+
+ closedir(dp);
+}
+
+void ProcessCallStack::log(const char* logtag, android_LogPriority priority,
+ const char* prefix) const {
+ LogPrinter printer(logtag, priority, prefix, /*ignoreBlankLines*/false);
+ print(printer);
+}
+
+void ProcessCallStack::print(Printer& printer) const {
+ /*
+ * Print the header/footer with the regular printer.
+ * Print the callstack with an additional two spaces as the prefix for legibility.
+ */
+ PrefixPrinter csPrinter(printer, CALL_STACK_PREFIX);
+ printInternal(printer, csPrinter);
+}
+
+void ProcessCallStack::printInternal(Printer& printer, Printer& csPrinter) const {
+ dumpProcessHeader(printer, getpid(),
+ getTimeString(mTimeUpdated).string());
+
+ for (size_t i = 0; i < mThreadMap.size(); ++i) {
+ pid_t tid = mThreadMap.keyAt(i);
+ const ThreadInfo& threadInfo = mThreadMap.valueAt(i);
+ const CallStack& cs = threadInfo.callStack;
+ const String8& threadName = threadInfo.threadName;
+
+ printer.printLine("");
+ printer.printFormatLine("\"%s\" sysTid=%d", threadName.string(), tid);
+
+ cs.print(csPrinter);
+ }
+
+ dumpProcessFooter(printer, getpid());
+}
+
+void ProcessCallStack::dump(int fd, int indent, const char* prefix) const {
+
+ if (indent < 0) {
+ ALOGW("%s: Bad indent (%d)", __FUNCTION__, indent);
+ return;
+ }
+
+ FdPrinter printer(fd, static_cast<unsigned int>(indent), prefix);
+ print(printer);
+}
+
+String8 ProcessCallStack::toString(const char* prefix) const {
+
+ String8 dest;
+ String8Printer printer(&dest, prefix);
+ print(printer);
+
+ return dest;
+}
+
+size_t ProcessCallStack::size() const {
+ return mThreadMap.size();
+}
+
+}; //namespace android
diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp
index 4b74889..ac8da88 100644
--- a/libutils/SystemClock.cpp
+++ b/libutils/SystemClock.cpp
@@ -61,12 +61,20 @@
#define METHOD_IOCTL 1
#define METHOD_SYSTEMTIME 2
+/*
+ * To debug/verify the timestamps returned by the kernel, change
+ * DEBUG_TIMESTAMP to 1 and call the timestamp routine from a single thread
+ * in the test program. b/10899829
+ */
+#define DEBUG_TIMESTAMP 0
+
static const char *gettime_method_names[] = {
"clock_gettime",
"ioctl",
"systemTime",
};
+#if DEBUG_TIMESTAMP
static inline void checkTimeStamps(int64_t timestamp,
int64_t volatile *prevTimestampPtr,
int volatile *prevMethodPtr,
@@ -93,6 +101,9 @@
*prevMethodPtr = curMethod;
#endif
}
+#else
+#define checkTimeStamps(timestamp, prevTimestampPtr, prevMethodPtr, curMethod)
+#endif
/*
* native public static long elapsedRealtimeNano();
@@ -103,8 +114,10 @@
struct timespec ts;
int result;
int64_t timestamp;
+#if DEBUG_TIMESTAMP
static volatile int64_t prevTimestamp;
static volatile int prevMethod;
+#endif
#if 0
/*
diff --git a/logcat/event.logtags b/logcat/event.logtags
index 3a1b281..a325692 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -90,10 +90,6 @@
# Logged when the supplicant switches to a new state
50023 wifi_supplicant_state_changed (supplicant_state|1|5)
-# Do not change these names without updating tag in:
-#//device/dalvik/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.c
-51000 socket_stats (send|1|2),(recv|1|2),(ip|1|5),(port|1|5),(close|1|5)
-
# Database operation samples.
# db: the filename of the database
# sql: the executed query (without query args)
@@ -136,7 +132,7 @@
80310 bionic_event_resolver_wrong_query (uid|1)
# libcore failure logging
-90100 cert_pin_failure (certs|4)
+90100 exp_det_cert_pin_failure (certs|4)
# 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.
diff --git a/logwrapper/include/logwrap/logwrap.h b/logwrapper/include/logwrap/logwrap.h
index 8087f0a..4307a30 100644
--- a/logwrapper/include/logwrap/logwrap.h
+++ b/logwrapper/include/logwrap/logwrap.h
@@ -44,11 +44,15 @@
* send a signal twice to signal the caller (once for the child, and
* once for the caller)
* log_target: Specify where to log the output of the child, either LOG_NONE,
- * LOG_ALOG (for the Android system log) or LOG_KLOG (for the kernel
- * log).
+ * LOG_ALOG (for the Android system log), LOG_KLOG (for the kernel
+ * log), or LOG_FILE (and you need to specify a pathname in the
+ * file_path argument, otherwise pass NULL). These are bit fields,
+ * and can be OR'ed together to log to multiple places.
* abbreviated: If true, capture up to the first 100 lines and last 4K of
* output from the child. The abbreviated output is not dumped to
* the specified log until the child has exited.
+ * file_path: if log_target has the LOG_FILE bit set, then this parameter
+ * must be set to the pathname of the file to log to.
*
* Return value:
* 0 when logwrap successfully run the child process and captured its status
@@ -58,13 +62,14 @@
*
*/
-/* Values for the log_target parameter android_fork_exec_ext() */
+/* Values for the log_target parameter android_fork_execvp_ext() */
#define LOG_NONE 0
#define LOG_ALOG 1
#define LOG_KLOG 2
+#define LOG_FILE 4
int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,
- int log_target, bool abbreviated);
+ int log_target, bool abbreviated, char *file_path);
/* Similar to above, except abbreviated logging is not available, and if logwrap
* is true, logging is to the Android system log, and if false, there is no
@@ -74,10 +79,9 @@
bool ignore_int_quit, bool logwrap)
{
return android_fork_execvp_ext(argc, argv, status, ignore_int_quit,
- (logwrap ? LOG_ALOG : LOG_NONE), false);
+ (logwrap ? LOG_ALOG : LOG_NONE), false, NULL);
}
-
__END_DECLS
#endif /* __LIBS_LOGWRAP_H */
diff --git a/logwrapper/logwrap.c b/logwrapper/logwrap.c
index 01cc9a1..4ca1db4 100644
--- a/logwrapper/logwrap.c
+++ b/logwrapper/logwrap.c
@@ -93,6 +93,7 @@
char klog_fmt[MAX_KLOG_TAG * 2];
char *btag;
bool abbreviated;
+ FILE *fp;
struct abbr_buf a_buf;
};
@@ -158,11 +159,15 @@
/* Log directly to the specified log */
static void do_log_line(struct log_info *log_info, char *line) {
- if (log_info->log_target == LOG_KLOG) {
+ if (log_info->log_target & LOG_KLOG) {
klog_write(6, log_info->klog_fmt, line);
- } else if (log_info->log_target == LOG_ALOG) {
+ }
+ if (log_info->log_target & LOG_ALOG) {
ALOG(LOG_INFO, log_info->btag, "%s", line);
}
+ if (log_info->log_target & LOG_FILE) {
+ fprintf(log_info->fp, "%s\n", line);
+ }
}
/* Log to either the abbreviated buf, or directly to the specified log
@@ -290,7 +295,7 @@
}
static int parent(const char *tag, int parent_read, pid_t pid,
- int *chld_sts, int log_target, bool abbreviated) {
+ int *chld_sts, int log_target, bool abbreviated, char *file_path) {
int status = 0;
char buffer[4096];
struct pollfd poll_fds[] = {
@@ -300,6 +305,7 @@
},
};
int rc = 0;
+ int fd;
struct log_info log_info;
@@ -309,8 +315,6 @@
bool found_child = false;
char tmpbuf[256];
- log_info.log_target = log_target;
- log_info.abbreviated = abbreviated;
log_info.btag = basename(tag);
if (!log_info.btag) {
log_info.btag = (char*) tag;
@@ -323,11 +327,30 @@
init_abbr_buf(&log_info.a_buf);
}
- if (log_target == LOG_KLOG) {
+ if (log_target & LOG_KLOG) {
snprintf(log_info.klog_fmt, sizeof(log_info.klog_fmt),
"<6>%.*s: %%s", MAX_KLOG_TAG, log_info.btag);
}
+ if ((log_target & LOG_FILE) && !file_path) {
+ /* No file_path specified, clear the LOG_FILE bit */
+ log_target &= ~LOG_FILE;
+ }
+
+ if (log_target & LOG_FILE) {
+ fd = open(file_path, O_WRONLY | O_CREAT, 0664);
+ if (fd < 0) {
+ ERROR("Cannot log to file %s\n", file_path);
+ log_target &= ~LOG_FILE;
+ } else {
+ lseek(fd, 0, SEEK_END);
+ log_info.fp = fdopen(fd, "a");
+ }
+ }
+
+ log_info.log_target = log_target;
+ log_info.abbreviated = abbreviated;
+
while (!found_child) {
if (TEMP_FAILURE_RETRY(poll(poll_fds, ARRAY_SIZE(poll_fds), -1)) < 0) {
ERROR("poll failed\n");
@@ -432,6 +455,9 @@
err_waitpid:
err_poll:
+ if (log_target & LOG_FILE) {
+ fclose(log_info.fp); /* Also closes underlying fd */
+ }
if (abbreviated) {
free_abbr_buf(&log_info.a_buf);
}
@@ -451,7 +477,7 @@
}
int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,
- int log_target, bool abbreviated) {
+ int log_target, bool abbreviated, char *file_path) {
pid_t pid;
int parent_ptty;
int child_ptty;
@@ -523,7 +549,8 @@
sigaction(SIGQUIT, &ignact, &quitact);
}
- rc = parent(argv[0], parent_ptty, pid, status, log_target, abbreviated);
+ rc = parent(argv[0], parent_ptty, pid, status, log_target,
+ abbreviated, file_path);
}
if (ignore_int_quit) {
diff --git a/logwrapper/logwrapper.c b/logwrapper/logwrapper.c
index d1c6240..d0d8d14 100644
--- a/logwrapper/logwrapper.c
+++ b/logwrapper/logwrapper.c
@@ -80,7 +80,7 @@
}
rc = android_fork_execvp_ext(argc, &argv[0], &status, true,
- log_target, abbreviated);
+ log_target, abbreviated, NULL);
if (!rc) {
if (WIFEXITED(status))
rc = WEXITSTATUS(status);
diff --git a/reboot/reboot.c b/reboot/reboot.c
index 0e5170d..d9a4227 100644
--- a/reboot/reboot.c
+++ b/reboot/reboot.c
@@ -68,6 +68,11 @@
perror("reboot");
exit(EXIT_FAILURE);
}
+
+ // Don't return early. Give the reboot command time to take effect
+ // to avoid messing up scripts which do "adb shell reboot && adb wait-for-device"
+ while(1) { pause(); }
+
fprintf(stderr, "Done\n");
return 0;
}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 729879a..21ebccb 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -42,13 +42,13 @@
mkdir /acct/uid
# Create cgroup mount point for memory
- mount tmpfs none /sys/fs/cgroup
- mkdir /sys/fs/cgroup/memory
+ mount tmpfs none /sys/fs/cgroup mode=0750,uid=0,gid=1000
+ mkdir /sys/fs/cgroup/memory 0750 root system
mount cgroup none /sys/fs/cgroup/memory memory
write /sys/fs/cgroup/memory/memory.move_charge_at_immigrate 1
chown root system /sys/fs/cgroup/memory/tasks
chmod 0660 /sys/fs/cgroup/memory/tasks
- mkdir /sys/fs/cgroup/memory/sw
+ mkdir /sys/fs/cgroup/memory/sw 0750 root system
write /sys/fs/cgroup/memory/sw/memory.swappiness 100
write /sys/fs/cgroup/memory/sw/memory.move_charge_at_immigrate 1
chown root system /sys/fs/cgroup/memory/sw/tasks
@@ -61,12 +61,11 @@
# See storage config details at http://source.android.com/tech/storage/
mkdir /mnt/shell 0700 shell shell
- mkdir /storage 0050 root sdcard_r
+ mkdir /mnt/media_rw 0700 media_rw media_rw
+ mkdir /storage 0751 root sdcard_r
# Directory for putting things only root should see.
mkdir /mnt/secure 0700 root root
- # Create private mountpoint so we can MS_MOVE from staging
- mount tmpfs tmpfs /mnt/secure mode=0700,uid=0,gid=0
# Directory for staging bindmounts
mkdir /mnt/secure/staging 0700 root root
@@ -133,12 +132,20 @@
# This is needed by any process that uses socket tagging.
chmod 0644 /dev/xt_qtaguid
+# Create location for fs_mgr to store abbreviated output from filesystem
+# checker programs.
+ mkdir /dev/fscklogs 0770 root system
+
+# pstore/ramoops previous console log
+ mount pstore pstore /sys/fs/pstore
+ chown system log /sys/fs/pstore/console-ramoops
+ chmod 0440 /sys/fs/pstore/console-ramoops
+
on post-fs
# once everything is setup, no need to modify /
mount rootfs rootfs / ro remount
# mount shared so changes propagate into child namespaces
mount rootfs rootfs / shared rec
- mount tmpfs tmpfs /mnt/secure private rec
# We chown/chmod /cache again so because mount is run as root + defaults
chown system cache /cache
@@ -177,6 +184,9 @@
# We restorecon /data in case the userdata partition has been reset.
restorecon /data
+ # Avoid predictable entropy pool. Carry over entropy from previous boot.
+ copy /data/system/entropy.dat /dev/urandom
+
# Create dump dir and collect dumps.
# Do this before we mount cache so eventually we can use cache for
# storing dumps on platforms which do not have a dedicated dump partition.
@@ -446,7 +456,6 @@
onrestart restart zygote
onrestart restart media
onrestart restart surfaceflinger
- onrestart restart inputflinger
onrestart restart drm
service vold /system/bin/vold
@@ -476,12 +485,6 @@
group graphics drmrpc
onrestart restart zygote
-service inputflinger /system/bin/inputflinger
- class main
- user system
- group input
- onrestart restart zygote
-
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
diff --git a/run-as/package.c b/run-as/package.c
index 27fc1eb..901e9e3 100644
--- a/run-as/package.c
+++ b/run-as/package.c
@@ -90,7 +90,7 @@
*/
oldegid = getegid();
- if (setegid(AID_SYSTEM) < 0) {
+ if (setegid(AID_PACKAGE_INFO) < 0) {
return NULL;
}
@@ -111,7 +111,7 @@
goto EXIT;
/* Ensure that the file is owned by the system user */
- if ((st.st_uid != AID_SYSTEM) || (st.st_gid != AID_SYSTEM)) {
+ if ((st.st_uid != AID_SYSTEM) || (st.st_gid != AID_PACKAGE_INFO)) {
goto EXIT;
}
diff --git a/run-as/run-as.c b/run-as/run-as.c
index 3c0ecc4..cc05e63 100644
--- a/run-as/run-as.c
+++ b/run-as/run-as.c
@@ -36,9 +36,9 @@
/*
* WARNING WARNING WARNING WARNING
*
- * This program runs as set-uid root on Android production devices.
- * Be very conservative when modifying it to avoid any serious
- * security issue. Keep in mind the following:
+ * This program runs with CAP_SETUID and CAP_SETGID capabilities on Android
+ * production devices. Be very conservative when modifying it to avoid any
+ * serious security issue. Keep in mind the following:
*
* - This program should only run for the 'root' or 'shell' users
*
@@ -61,14 +61,19 @@
*
* run-as <package-name> <command> <args>
*
- * The 'run-as' binary is setuid, but will check the following:
+ * The 'run-as' binary is installed with CAP_SETUID and CAP_SETGID file
+ * capabilities, but will check the following:
*
* - that it is invoked from the 'shell' or 'root' user (abort otherwise)
* - that '<package-name>' is the name of an installed and debuggable package
* - that the package's data directory is well-formed (see package.c)
*
- * If so, it will cd to the package's data directory, drop to the application's
- * user id / group id then run the command there.
+ * If so, it will drop to the application's user id / group id, cd to the
+ * package's data directory, then run the command there.
+ *
+ * NOTE: In the future it might not be possible to cd to the package's data
+ * directory under that package's user id / group id, in which case this
+ * utility will need to be changed accordingly.
*
* This can be useful for a number of different things on production devices:
*
@@ -141,19 +146,6 @@
return 1;
}
- /* then move to it */
- {
- int ret;
- do {
- ret = chdir(info.dataDir);
- } while (ret < 0 && errno == EINTR);
-
- if (ret < 0) {
- panic("Could not cd to package's data directory: %s\n", strerror(errno));
- return 1;
- }
- }
-
/* Ensure that we change all real/effective/saved IDs at the
* same time to avoid nasty surprises.
*/
@@ -168,6 +160,19 @@
return 1;
}
+ /* cd into the data directory */
+ {
+ int ret;
+ do {
+ ret = chdir(info.dataDir);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0) {
+ panic("Could not cd to package's data directory: %s\n", strerror(errno));
+ return 1;
+ }
+ }
+
/* User specified command for exec. */
if (argc >= 3 ) {
if (execvp(argv[2], argv+2) < 0) {
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
index 377c008..05fbfba 100644
--- a/sdcard/sdcard.c
+++ b/sdcard/sdcard.c
@@ -32,6 +32,7 @@
#include <sys/resource.h>
#include <sys/inotify.h>
+#include <cutils/fs.h>
#include <cutils/hashmap.h>
#include <cutils/multiuser.h>
@@ -126,14 +127,30 @@
/* Permission mode for a specific node. Controls how file permissions
* are derived for children nodes. */
typedef enum {
+ /* Nothing special; this node should just inherit from its parent. */
PERM_INHERIT,
+ /* This node is one level above a normal root; used for legacy layouts
+ * which use the first level to represent user_id. */
+ PERM_LEGACY_PRE_ROOT,
+ /* This node is "/" */
PERM_ROOT,
+ /* This node is "/Android" */
PERM_ANDROID,
+ /* This node is "/Android/data" */
PERM_ANDROID_DATA,
+ /* This node is "/Android/obb" */
PERM_ANDROID_OBB,
+ /* This node is "/Android/user" */
PERM_ANDROID_USER,
} perm_t;
+/* Permissions structure to derive */
+typedef enum {
+ DERIVE_NONE,
+ DERIVE_LEGACY,
+ DERIVE_UNIFIED,
+} derive_t;
+
struct handle {
int fd;
};
@@ -142,10 +159,6 @@
DIR *d;
};
-struct package {
- appid_t appid;
-};
-
struct node {
__u32 refcount;
__u64 nid;
@@ -170,14 +183,28 @@
* namelen for both fields.
*/
char *actual_name;
+
+ /* If non-null, an exact underlying path that should be grafted into this
+ * position. Used to support things like OBB. */
+ char* graft_path;
+ size_t graft_pathlen;
};
static int str_hash(void *key) {
return hashmapHash(key, strlen(key));
}
-static bool str_equals(void *keyA, void *keyB) {
- return strcmp(keyA, keyB) == 0;
+/** Test if two string keys are equal ignoring case */
+static bool str_icase_equals(void *keyA, void *keyB) {
+ return strcasecmp(keyA, keyB) == 0;
+}
+
+static int int_hash(void *key) {
+ return (int) key;
+}
+
+static bool int_equals(void *keyA, void *keyB) {
+ return keyA == keyB;
}
/* Global data structure shared by all fuse handlers. */
@@ -186,11 +213,14 @@
__u64 next_generation;
int fd;
- bool derive_perms;
+ derive_t derive;
+ bool split_perms;
+ gid_t write_gid;
struct node root;
- char rootpath[PATH_MAX];
+ char obbpath[PATH_MAX];
- Hashmap *package_to_appid;
+ Hashmap* package_to_appid;
+ Hashmap* appid_with_rw;
};
/* Private data used by a single fuse handler. */
@@ -275,15 +305,26 @@
* Populates 'buf' with the path and returns the length of the path on success,
* or returns -1 if the path is too long for the provided buffer.
*/
-static ssize_t get_node_path_locked(struct node* node, char* buf, size_t bufsize)
-{
- size_t namelen = node->namelen;
+static ssize_t get_node_path_locked(struct node* node, char* buf, size_t bufsize) {
+ const char* name;
+ size_t namelen;
+ if (node->graft_path) {
+ name = node->graft_path;
+ namelen = node->graft_pathlen;
+ } else if (node->actual_name) {
+ name = node->actual_name;
+ namelen = node->namelen;
+ } else {
+ name = node->name;
+ namelen = node->namelen;
+ }
+
if (bufsize < namelen + 1) {
return -1;
}
ssize_t pathlen = 0;
- if (node->parent) {
+ if (node->parent && node->graft_path == NULL) {
pathlen = get_node_path_locked(node->parent, buf, bufsize - namelen - 2);
if (pathlen < 0) {
return -1;
@@ -291,7 +332,6 @@
buf[pathlen++] = '/';
}
- const char* name = node->actual_name ? node->actual_name : node->name;
memcpy(buf + pathlen, name, namelen + 1); /* include trailing \0 */
return pathlen + namelen;
}
@@ -364,9 +404,23 @@
attr->mode = (attr->mode & S_IFMT) | filtered_mode;
}
+static int touch(char* path, mode_t mode) {
+ int fd = open(path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, mode);
+ if (fd == -1) {
+ if (errno == EEXIST) {
+ return 0;
+ } else {
+ ERROR("Failed to open(%s): %s\n", path, strerror(errno));
+ return -1;
+ }
+ }
+ close(fd);
+ return 0;
+}
+
static void derive_permissions_locked(struct fuse* fuse, struct node *parent,
struct node *node) {
- struct package* package;
+ appid_t appid;
/* By default, each node inherits from its parent */
node->perm = PERM_INHERIT;
@@ -375,7 +429,7 @@
node->gid = parent->gid;
node->mode = parent->mode;
- if (!fuse->derive_perms) {
+ if (fuse->derive == DERIVE_NONE) {
return;
}
@@ -384,35 +438,45 @@
case PERM_INHERIT:
/* Already inherited above */
break;
+ case PERM_LEGACY_PRE_ROOT:
+ /* Legacy internal layout places users at top level */
+ node->perm = PERM_ROOT;
+ node->userid = strtoul(node->name, NULL, 10);
+ break;
case PERM_ROOT:
- if (!strcmp(node->name, "Android")) {
+ /* Assume masked off by default. */
+ node->mode = 0770;
+ if (!strcasecmp(node->name, "Android")) {
/* App-specific directories inside; let anyone traverse */
node->perm = PERM_ANDROID;
node->mode = 0771;
- } else if (!strcmp(node->name, "DCIM")
- || !strcmp(node->name, "Pictures")) {
- node->gid = AID_SDCARD_PICS;
- node->mode = 0770;
- } else if (!strcmp(node->name, "Alarms")
- || !strcmp(node->name, "Movies")
- || !strcmp(node->name, "Music")
- || !strcmp(node->name, "Notifications")
- || !strcmp(node->name, "Podcasts")
- || !strcmp(node->name, "Ringtones")) {
- node->gid = AID_SDCARD_AV;
- node->mode = 0770;
+ } else if (fuse->split_perms) {
+ if (!strcasecmp(node->name, "DCIM")
+ || !strcasecmp(node->name, "Pictures")) {
+ node->gid = AID_SDCARD_PICS;
+ } else if (!strcasecmp(node->name, "Alarms")
+ || !strcasecmp(node->name, "Movies")
+ || !strcasecmp(node->name, "Music")
+ || !strcasecmp(node->name, "Notifications")
+ || !strcasecmp(node->name, "Podcasts")
+ || !strcasecmp(node->name, "Ringtones")) {
+ node->gid = AID_SDCARD_AV;
+ }
}
break;
case PERM_ANDROID:
- if (!strcmp(node->name, "data")) {
+ if (!strcasecmp(node->name, "data")) {
/* App-specific directories inside; let anyone traverse */
node->perm = PERM_ANDROID_DATA;
node->mode = 0771;
- } else if (!strcmp(node->name, "obb")) {
+ } else if (!strcasecmp(node->name, "obb")) {
/* App-specific directories inside; let anyone traverse */
node->perm = PERM_ANDROID_OBB;
node->mode = 0771;
- } else if (!strcmp(node->name, "user")) {
+ /* Single OBB directory is always shared */
+ node->graft_path = fuse->obbpath;
+ node->graft_pathlen = strlen(fuse->obbpath);
+ } 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-
* specific path. */
@@ -423,22 +487,78 @@
break;
case PERM_ANDROID_DATA:
case PERM_ANDROID_OBB:
- package = hashmapGet(fuse->package_to_appid, node->name);
- if (package != NULL) {
- node->uid = multiuser_get_uid(parent->userid, package->appid);
+ appid = (appid_t) hashmapGet(fuse->package_to_appid, node->name);
+ if (appid != 0) {
+ node->uid = multiuser_get_uid(parent->userid, appid);
}
node->mode = 0770;
break;
case PERM_ANDROID_USER:
/* Root of a secondary user */
node->perm = PERM_ROOT;
- node->userid = atoi(node->name);
- node->gid = AID_SDCARD_RW;
+ node->userid = strtoul(node->name, NULL, 10);
+ node->gid = AID_SDCARD_R;
node->mode = 0771;
break;
}
}
+/* Return if the calling UID holds sdcard_rw. */
+static bool get_caller_has_rw_locked(struct fuse* fuse, const struct fuse_in_header *hdr) {
+ /* No additional permissions enforcement */
+ if (fuse->derive == DERIVE_NONE) {
+ return true;
+ }
+
+ appid_t appid = multiuser_get_app_id(hdr->uid);
+ return hashmapContainsKey(fuse->appid_with_rw, (void*) appid);
+}
+
+/* Kernel has already enforced everything we returned through
+ * derive_permissions_locked(), so this is used to lock down access
+ * even further, such as enforcing that apps hold sdcard_rw. */
+static bool check_caller_access_to_name(struct fuse* fuse,
+ const struct fuse_in_header *hdr, const struct node* parent_node,
+ const char* name, int mode, bool has_rw) {
+ /* Always block security-sensitive files at root */
+ if (parent_node && parent_node->perm == PERM_ROOT) {
+ if (!strcasecmp(name, "autorun.inf")
+ || !strcasecmp(name, ".android_secure")
+ || !strcasecmp(name, "android_secure")) {
+ return false;
+ }
+ }
+
+ /* No additional permissions enforcement */
+ if (fuse->derive == DERIVE_NONE) {
+ return true;
+ }
+
+ /* Root always has access; access for any other UIDs should always
+ * be controlled through packages.list. */
+ if (hdr->uid == 0) {
+ return true;
+ }
+
+ /* If asking to write, verify that caller either owns the
+ * parent or holds sdcard_rw. */
+ if (mode & W_OK) {
+ if (parent_node && hdr->uid == parent_node->uid) {
+ return true;
+ }
+
+ return has_rw;
+ }
+
+ /* No extra permissions to enforce */
+ return true;
+}
+
+static bool check_caller_access_to_node(struct fuse* fuse,
+ const struct fuse_in_header *hdr, const struct node* node, int mode, bool has_rw) {
+ return check_caller_access_to_name(fuse, hdr, node->parent, node->name, mode, has_rw);
+}
+
struct node *create_node_locked(struct fuse* fuse,
struct node *parent, const char *name, const char* actual_name)
{
@@ -561,32 +681,55 @@
return child;
}
-static void fuse_init(struct fuse *fuse, int fd, const char *source_path, bool derive_perms)
-{
+static void fuse_init(struct fuse *fuse, int fd, const char *source_path,
+ gid_t write_gid, derive_t derive, bool split_perms) {
pthread_mutex_init(&fuse->lock, NULL);
fuse->fd = fd;
fuse->next_generation = 0;
- fuse->derive_perms = derive_perms;
+ fuse->derive = derive;
+ fuse->split_perms = split_perms;
+ fuse->write_gid = write_gid;
memset(&fuse->root, 0, sizeof(fuse->root));
fuse->root.nid = FUSE_ROOT_ID; /* 1 */
fuse->root.refcount = 2;
fuse->root.namelen = strlen(source_path);
fuse->root.name = strdup(source_path);
-
- fuse->root.perm = PERM_ROOT;
fuse->root.userid = 0;
fuse->root.uid = AID_ROOT;
- fuse->root.gid = AID_SDCARD_RW;
- if (derive_perms) {
- fuse->root.mode = 0771;
- } else {
- fuse->root.mode = 0775;
- }
- if (derive_perms) {
- fuse->package_to_appid = hashmapCreate(256, str_hash, str_equals);
+ /* Set up root node for various modes of operation */
+ switch (derive) {
+ case DERIVE_NONE:
+ /* Traditional behavior that treats entire device as being accessible
+ * to sdcard_rw, and no permissions are derived. */
+ fuse->root.perm = PERM_ROOT;
+ fuse->root.mode = 0775;
+ fuse->root.gid = AID_SDCARD_RW;
+ break;
+ case DERIVE_LEGACY:
+ /* Legacy behavior used to support internal multiuser layout which
+ * places user_id at the top directory level, with the actual roots
+ * just below that. Shared OBB path is also at top level. */
+ fuse->root.perm = PERM_LEGACY_PRE_ROOT;
+ fuse->root.mode = 0771;
+ fuse->root.gid = AID_SDCARD_R;
+ fuse->package_to_appid = hashmapCreate(256, str_hash, str_icase_equals);
+ fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals);
+ snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/obb", source_path);
+ fs_prepare_dir(fuse->obbpath, 0775, getuid(), getgid());
+ break;
+ case DERIVE_UNIFIED:
+ /* Unified multiuser layout which places secondary user_id under
+ * /Android/user and shared OBB path under /Android/obb. */
+ fuse->root.perm = PERM_ROOT;
+ fuse->root.mode = 0771;
+ fuse->root.gid = AID_SDCARD_R;
+ fuse->package_to_appid = hashmapCreate(256, str_hash, str_icase_equals);
+ fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals);
+ snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/Android/obb", source_path);
+ break;
}
}
@@ -629,36 +772,7 @@
struct stat s;
if (lstat(path, &s) < 0) {
- /* But wait! We'll automatically create a directory if its
- * a valid package name under data or obb, since apps may not
- * have enough permissions to create for themselves. */
- if (errno == ENOENT && (parent->perm == PERM_ANDROID_DATA
- || parent->perm == PERM_ANDROID_OBB)) {
- TRACE("automatically creating %s\n", path);
-
- pthread_mutex_lock(&fuse->lock);
- bool validPackage = hashmapContainsKey(fuse->package_to_appid, name);
- pthread_mutex_unlock(&fuse->lock);
-
- if (!validPackage) {
- return -ENOENT;
- }
- if (mkdir(path, 0775) == -1) {
- /* We might have raced with ourselves and already created */
- if (errno != EEXIST) {
- ERROR("failed to mkdir(%s): %s\n", name, strerror(errno));
- return -ENOENT;
- }
- }
-
- /* It should exist this time around! */
- if (lstat(path, &s) < 0) {
- ERROR("failed to lstat(%s): %s\n", name, strerror(errno));
- return -errno;
- }
- } else {
- return -errno;
- }
+ return -errno;
}
pthread_mutex_lock(&fuse->lock);
@@ -713,6 +827,10 @@
child_path, sizeof(child_path), 1))) {
return -ENOENT;
}
+ if (!check_caller_access_to_name(fuse, hdr, parent_node, name, R_OK, false)) {
+ return -EACCES;
+ }
+
return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
}
@@ -750,17 +868,23 @@
if (!node) {
return -ENOENT;
}
+ if (!check_caller_access_to_node(fuse, hdr, node, R_OK, false)) {
+ return -EACCES;
+ }
+
return fuse_reply_attr(fuse, hdr->unique, node, path);
}
static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler,
const struct fuse_in_header *hdr, const struct fuse_setattr_in *req)
{
+ bool has_rw;
struct node* node;
char path[PATH_MAX];
struct timespec times[2];
pthread_mutex_lock(&fuse->lock);
+ has_rw = get_caller_has_rw_locked(fuse, hdr);
node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
TRACE("[%d] SETATTR fh=%llx valid=%x @ %llx (%s)\n", handler->token,
req->fh, req->valid, hdr->nodeid, node ? node->name : "?");
@@ -769,6 +893,9 @@
if (!node) {
return -ENOENT;
}
+ if (!check_caller_access_to_node(fuse, hdr, node, W_OK, has_rw)) {
+ return -EACCES;
+ }
/* XXX: incomplete implementation on purpose.
* chmod/chown should NEVER be implemented.*/
@@ -814,12 +941,14 @@
static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler,
const struct fuse_in_header* hdr, const struct fuse_mknod_in* req, const char* name)
{
+ bool has_rw;
struct node* parent_node;
char parent_path[PATH_MAX];
char child_path[PATH_MAX];
const char* actual_name;
pthread_mutex_lock(&fuse->lock);
+ has_rw = get_caller_has_rw_locked(fuse, hdr);
parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
parent_path, sizeof(parent_path));
TRACE("[%d] MKNOD %s 0%o @ %llx (%s)\n", handler->token,
@@ -830,6 +959,9 @@
child_path, sizeof(child_path), 1))) {
return -ENOENT;
}
+ if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) {
+ return -EACCES;
+ }
__u32 mode = (req->mode & (~0777)) | 0664;
if (mknod(child_path, mode, req->rdev) < 0) {
return -errno;
@@ -840,12 +972,14 @@
static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler,
const struct fuse_in_header* hdr, const struct fuse_mkdir_in* req, const char* name)
{
+ bool has_rw;
struct node* parent_node;
char parent_path[PATH_MAX];
char child_path[PATH_MAX];
const char* actual_name;
pthread_mutex_lock(&fuse->lock);
+ has_rw = get_caller_has_rw_locked(fuse, hdr);
parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
parent_path, sizeof(parent_path));
TRACE("[%d] MKDIR %s 0%o @ %llx (%s)\n", handler->token,
@@ -856,21 +990,45 @@
child_path, sizeof(child_path), 1))) {
return -ENOENT;
}
+ if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) {
+ return -EACCES;
+ }
__u32 mode = (req->mode & (~0777)) | 0775;
if (mkdir(child_path, mode) < 0) {
return -errno;
}
+
+ /* When creating /Android/data and /Android/obb, mark them as .nomedia */
+ if (parent_node->perm == PERM_ANDROID && !strcasecmp(name, "data")) {
+ char nomedia[PATH_MAX];
+ snprintf(nomedia, PATH_MAX, "%s/.nomedia", child_path);
+ if (touch(nomedia, 0664) != 0) {
+ ERROR("Failed to touch(%s): %s\n", nomedia, strerror(errno));
+ return -ENOENT;
+ }
+ }
+ if (parent_node->perm == PERM_ANDROID && !strcasecmp(name, "obb")) {
+ char nomedia[PATH_MAX];
+ snprintf(nomedia, PATH_MAX, "%s/.nomedia", fuse->obbpath);
+ if (touch(nomedia, 0664) != 0) {
+ ERROR("Failed to touch(%s): %s\n", nomedia, strerror(errno));
+ return -ENOENT;
+ }
+ }
+
return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
}
static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler,
const struct fuse_in_header* hdr, const char* name)
{
+ bool has_rw;
struct node* parent_node;
char parent_path[PATH_MAX];
char child_path[PATH_MAX];
pthread_mutex_lock(&fuse->lock);
+ has_rw = get_caller_has_rw_locked(fuse, hdr);
parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
parent_path, sizeof(parent_path));
TRACE("[%d] UNLINK %s @ %llx (%s)\n", handler->token,
@@ -881,6 +1039,9 @@
child_path, sizeof(child_path), 1)) {
return -ENOENT;
}
+ if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) {
+ return -EACCES;
+ }
if (unlink(child_path) < 0) {
return -errno;
}
@@ -890,11 +1051,13 @@
static int handle_rmdir(struct fuse* fuse, struct fuse_handler* handler,
const struct fuse_in_header* hdr, const char* name)
{
+ bool has_rw;
struct node* parent_node;
char parent_path[PATH_MAX];
char child_path[PATH_MAX];
pthread_mutex_lock(&fuse->lock);
+ has_rw = get_caller_has_rw_locked(fuse, hdr);
parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
parent_path, sizeof(parent_path));
TRACE("[%d] RMDIR %s @ %llx (%s)\n", handler->token,
@@ -905,6 +1068,9 @@
child_path, sizeof(child_path), 1)) {
return -ENOENT;
}
+ if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) {
+ return -EACCES;
+ }
if (rmdir(child_path) < 0) {
return -errno;
}
@@ -915,6 +1081,7 @@
const struct fuse_in_header* hdr, const struct fuse_rename_in* req,
const char* old_name, const char* new_name)
{
+ bool has_rw;
struct node* old_parent_node;
struct node* new_parent_node;
struct node* child_node;
@@ -926,6 +1093,7 @@
int res;
pthread_mutex_lock(&fuse->lock);
+ has_rw = get_caller_has_rw_locked(fuse, hdr);
old_parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
old_parent_path, sizeof(old_parent_path));
new_parent_node = lookup_node_and_path_by_id_locked(fuse, req->newdir,
@@ -938,6 +1106,14 @@
res = -ENOENT;
goto lookup_error;
}
+ if (!check_caller_access_to_name(fuse, hdr, old_parent_node, old_name, W_OK, has_rw)) {
+ res = -EACCES;
+ goto lookup_error;
+ }
+ if (!check_caller_access_to_name(fuse, hdr, new_parent_node, new_name, W_OK, has_rw)) {
+ res = -EACCES;
+ goto lookup_error;
+ }
child_node = lookup_child_by_name_locked(old_parent_node, old_name);
if (!child_node || get_node_path_locked(child_node,
old_child_path, sizeof(old_child_path)) < 0) {
@@ -983,15 +1159,28 @@
return res;
}
+static int open_flags_to_access_mode(int open_flags) {
+ if ((open_flags & O_ACCMODE) == O_RDONLY) {
+ return R_OK;
+ } else if ((open_flags & O_ACCMODE) == O_WRONLY) {
+ return W_OK;
+ } else {
+ /* Probably O_RDRW, but treat as default to be safe */
+ return R_OK | W_OK;
+ }
+}
+
static int handle_open(struct fuse* fuse, struct fuse_handler* handler,
const struct fuse_in_header* hdr, const struct fuse_open_in* req)
{
+ bool has_rw;
struct node* node;
char path[PATH_MAX];
struct fuse_open_out out;
struct handle *h;
pthread_mutex_lock(&fuse->lock);
+ has_rw = get_caller_has_rw_locked(fuse, hdr);
node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
TRACE("[%d] OPEN 0%o @ %llx (%s)\n", handler->token,
req->flags, hdr->nodeid, node ? node->name : "?");
@@ -1000,6 +1189,10 @@
if (!node) {
return -ENOENT;
}
+ if (!check_caller_access_to_node(fuse, hdr, node,
+ open_flags_to_access_mode(req->flags), has_rw)) {
+ return -EACCES;
+ }
h = malloc(sizeof(*h));
if (!h) {
return -ENOMEM;
@@ -1144,6 +1337,9 @@
if (!node) {
return -ENOENT;
}
+ if (!check_caller_access_to_node(fuse, hdr, node, R_OK, false)) {
+ return -EACCES;
+ }
h = malloc(sizeof(*h));
if (!h) {
return -ENOMEM;
@@ -1155,6 +1351,8 @@
return -errno;
}
out.fh = ptr_to_id(h);
+ out.open_flags = 0;
+ out.padding = 0;
fuse_reply(fuse, hdr->unique, &out, sizeof(out));
return NO_STATUS;
}
@@ -1389,18 +1587,24 @@
return NULL;
}
-static bool hashmap_remove(void *key, void *value, void *context) {
+static bool remove_str_to_int(void *key, void *value, void *context) {
Hashmap* map = context;
hashmapRemove(map, key);
free(key);
- free(value);
+ return true;
+}
+
+static bool remove_int_to_null(void *key, void *value, void *context) {
+ Hashmap* map = context;
+ hashmapRemove(map, key);
return true;
}
static int read_package_list(struct fuse *fuse) {
pthread_mutex_lock(&fuse->lock);
- hashmapForEach(fuse->package_to_appid, hashmap_remove, fuse->package_to_appid);
+ hashmapForEach(fuse->package_to_appid, remove_str_to_int, fuse->package_to_appid);
+ hashmapForEach(fuse->appid_with_rw, remove_int_to_null, fuse->appid_with_rw);
FILE* file = fopen(kPackagesListFile, "r");
if (!file) {
@@ -1413,20 +1617,26 @@
while (fgets(buf, sizeof(buf), file) != NULL) {
char package_name[512];
int appid;
- if (sscanf(buf, "%s %d", package_name, &appid) == 2) {
- char* package_name_dup = strdup(package_name);
- struct package* package = malloc(sizeof(struct package));
- if (!package_name_dup || !package) {
- ERROR("cannot allocate package details\n");
- return -ENOMEM;
- }
+ char gids[512];
- package->appid = appid;
- hashmapPut(fuse->package_to_appid, package_name_dup, package);
+ if (sscanf(buf, "%s %d %*d %*s %*s %s", package_name, &appid, gids) == 3) {
+ char* package_name_dup = strdup(package_name);
+ hashmapPut(fuse->package_to_appid, package_name_dup, (void*) appid);
+
+ char* token = strtok(gids, ",");
+ while (token != NULL) {
+ if (strtoul(token, NULL, 10) == fuse->write_gid) {
+ hashmapPut(fuse->appid_with_rw, (void*) appid, (void*) 1);
+ break;
+ }
+ token = strtok(NULL, ",");
+ }
}
}
- TRACE("read_package_list: found %d packages\n", hashmapSize(fuse->package_to_appid));
+ TRACE("read_package_list: found %d packages, %d with write_gid\n",
+ hashmapSize(fuse->package_to_appid),
+ hashmapSize(fuse->appid_with_rw));
fclose(file);
pthread_mutex_unlock(&fuse->lock);
return 0;
@@ -1512,7 +1722,7 @@
/* When deriving permissions, this thread is used to process inotify events,
* otherwise it becomes one of the FUSE handlers. */
- i = fuse->derive_perms ? 0 : 1;
+ i = (fuse->derive == DERIVE_NONE) ? 1 : 0;
for (; i < num_threads; i++) {
pthread_t thread;
int res = pthread_create(&thread, NULL, start_handler, &handlers[i]);
@@ -1522,10 +1732,10 @@
}
}
- if (fuse->derive_perms) {
- watch_package_list(fuse);
- } else {
+ if (fuse->derive == DERIVE_NONE) {
handle_fuse_requests(&handlers[0]);
+ } else {
+ watch_package_list(fuse);
}
ERROR("terminated prematurely\n");
@@ -1541,14 +1751,18 @@
ERROR("usage: sdcard [OPTIONS] <source_path> <dest_path>\n"
" -u: specify UID to run as\n"
" -g: specify GID to run as\n"
+ " -w: specify GID required to write (default sdcard_rw, requires -d or -l)\n"
" -t: specify number of threads to use (default %d)\n"
" -d: derive file permissions based on path\n"
+ " -l: derive file permissions based on legacy internal layout\n"
+ " -s: split derived permissions for pics, av\n"
"\n", DEFAULT_NUM_THREADS);
return 1;
}
-static int run(const char* source_path, const char* dest_path, uid_t uid, gid_t gid,
- int num_threads, bool derive_perms) {
+static int run(const char* source_path, const char* dest_path, uid_t uid,
+ gid_t gid, gid_t write_gid, int num_threads, derive_t derive,
+ bool split_perms) {
int fd;
char opts[256];
int res;
@@ -1591,7 +1805,7 @@
goto error;
}
- fuse_init(&fuse, fd, source_path, derive_perms);
+ fuse_init(&fuse, fd, source_path, write_gid, derive, split_perms);
umask(0);
res = ignite_fuse(&fuse, num_threads);
@@ -1611,13 +1825,15 @@
const char *dest_path = NULL;
uid_t uid = 0;
gid_t gid = 0;
+ gid_t write_gid = AID_SDCARD_RW;
int num_threads = DEFAULT_NUM_THREADS;
- bool derive_perms = false;
+ derive_t derive = DERIVE_NONE;
+ bool split_perms = false;
int i;
struct rlimit rlim;
int opt;
- while ((opt = getopt(argc, argv, "u:g:t:d")) != -1) {
+ while ((opt = getopt(argc, argv, "u:g:w:t:dls")) != -1) {
switch (opt) {
case 'u':
uid = strtoul(optarg, NULL, 10);
@@ -1625,11 +1841,20 @@
case 'g':
gid = strtoul(optarg, NULL, 10);
break;
+ case 'w':
+ write_gid = strtoul(optarg, NULL, 10);
+ break;
case 't':
num_threads = strtoul(optarg, NULL, 10);
break;
case 'd':
- derive_perms = true;
+ derive = DERIVE_UNIFIED;
+ break;
+ case 'l':
+ derive = DERIVE_LEGACY;
+ break;
+ case 's':
+ split_perms = true;
break;
case '?':
default:
@@ -1669,6 +1894,10 @@
ERROR("number of threads must be at least 1\n");
return usage();
}
+ if (split_perms && derive == DERIVE_NONE) {
+ ERROR("cannot split permissions without deriving\n");
+ return usage();
+ }
rlim.rlim_cur = 8192;
rlim.rlim_max = 8192;
@@ -1676,6 +1905,6 @@
ERROR("Error setting RLIMIT_NOFILE, errno = %d\n", errno);
}
- res = run(source_path, dest_path, uid, gid, num_threads, derive_perms);
+ res = run(source_path, dest_path, uid, gid, write_gid, num_threads, derive, split_perms);
return res < 0 ? 1 : 0;
}
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index f60037d..75ce53f 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -68,7 +68,8 @@
load_policy \
swapon \
swapoff \
- mkswap
+ mkswap \
+ readlink
ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
TOOLS += r
diff --git a/toolbox/readlink.c b/toolbox/readlink.c
new file mode 100644
index 0000000..d114e20
--- /dev/null
+++ b/toolbox/readlink.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2013, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static int skip_newline, quiet_errors, canonicalize;
+
+static void usage(char* name) {
+ fprintf(stderr, "Usage: %s [OPTION]... FILE\n", name);
+}
+
+int readlink_main(int argc, char* argv[]) {
+ int c;
+ while ((c = getopt(argc, argv, "nfqs")) != -1) {
+ switch (c) {
+ case 'n':
+ skip_newline = 1;
+ break;
+ case 'f':
+ canonicalize = 1;
+ break;
+ case 'q':
+ case 's':
+ quiet_errors = 1;
+ break;
+ case '?':
+ default:
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+ }
+ int index = optind;
+ if (argc - index != 1) {
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ char name[PATH_MAX+1];
+ if (canonicalize) {
+ if(!realpath(argv[optind], name)) {
+ if (!quiet_errors) {
+ perror("readlink");
+ }
+ return EXIT_FAILURE;
+ }
+ } else {
+ ssize_t len = readlink(argv[1], name, PATH_MAX);
+
+ if (len < 0) {
+ if (!quiet_errors) {
+ perror("readlink");
+ }
+ return EXIT_FAILURE;
+ }
+ name[len] = '\0';
+ }
+
+ fputs(name, stdout);
+ if (!skip_newline) {
+ fputs("\n", stdout);
+ }
+
+ return EXIT_SUCCESS;
+}