diff options
82 files changed, 3938 insertions, 2807 deletions
diff --git a/adb/commandline.cpp b/adb/commandline.cpp index d0ca67aea..5e5ca7fa6 100644 --- a/adb/commandline.cpp +++ b/adb/commandline.cpp @@ -146,8 +146,15 @@ static void help() { " - remove a specific reversed socket connection\n" " adb reverse --remove-all - remove all reversed socket connections from device\n" " adb jdwp - list PIDs of processes hosting a JDWP transport\n" - " adb install [-lrtsd] <file>\n" - " adb install-multiple [-lrtsdp] <file...>\n" + " adb install [-lrtsdg] <file>\n" + " - push this package file to the device and install it\n" + " (-l: forward lock application)\n" + " (-r: replace existing application)\n" + " (-t: allow test packages)\n" + " (-s: install application on sdcard)\n" + " (-d: allow version code downgrade)\n" + " (-g: grant all runtime permissions)\n" + " adb install-multiple [-lrtsdpg] <file...>\n" " - push this package file to the device and install it\n" " (-l: forward lock application)\n" " (-r: replace existing application)\n" @@ -155,6 +162,7 @@ static void help() { " (-s: install application on sdcard)\n" " (-d: allow version code downgrade)\n" " (-p: partial application install)\n" + " (-g: grant all runtime permissions)\n" " adb uninstall [-k] <package> - remove this app package from the device\n" " ('-k' means keep the data and cache directories)\n" " adb bugreport - return all information from the device\n" diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp index f588b40fb..9b5639297 100644 --- a/adb/file_sync_service.cpp +++ b/adb/file_sync_service.cpp @@ -61,7 +61,7 @@ static bool secure_mkdirs(const std::string& path) { partial_path += path_component; if (should_use_fs_config(partial_path)) { - fs_config(partial_path.c_str(), 1, &uid, &gid, &mode, &cap); + fs_config(partial_path.c_str(), 1, nullptr, &uid, &gid, &mode, &cap); } if (adb_mkdir(partial_path.c_str(), mode) == -1) { if (errno != EEXIST) { @@ -308,7 +308,7 @@ static bool do_send(int s, const std::string& spec, std::vector<char>& buffer) { uint64_t cap = 0; if (should_use_fs_config(path)) { unsigned int broken_api_hack = mode; - fs_config(path.c_str(), 0, &uid, &gid, &broken_api_hack, &cap); + fs_config(path.c_str(), 0, nullptr, &uid, &gid, &broken_api_hack, &cap); mode = broken_api_hack; } return handle_send_file(s, path.c_str(), uid, gid, mode, buffer, do_unlink); diff --git a/cpio/mkbootfs.c b/cpio/mkbootfs.c index 7175749b1..0e3532304 100644 --- a/cpio/mkbootfs.c +++ b/cpio/mkbootfs.c @@ -41,6 +41,7 @@ struct fs_config_entry { }; static struct fs_config_entry* canned_config = NULL; +static char *target_out_path = NULL; /* Each line in the canned file should be a path plus three ints (uid, * gid, mode). */ @@ -79,7 +80,8 @@ static void fix_stat(const char *path, struct stat *s) } else { // Use the compiled-in fs_config() function. unsigned st_mode = s->st_mode; - fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &st_mode, &capabilities); + fs_config(path, S_ISDIR(s->st_mode), target_out_path, + &s->st_uid, &s->st_gid, &st_mode, &capabilities); s->st_mode = (typeof(s->st_mode)) st_mode; } } @@ -328,6 +330,12 @@ int main(int argc, char *argv[]) argc--; argv++; + if (argc > 1 && strcmp(argv[0], "-d") == 0) { + target_out_path = argv[1]; + argc -= 2; + argv += 2; + } + if (argc > 1 && strcmp(argv[0], "-f") == 0) { read_canned_config(argv[1]); argc -= 2; diff --git a/fingerprintd/Android.mk b/fingerprintd/Android.mk new file mode 100644 index 000000000..48b95258a --- /dev/null +++ b/fingerprintd/Android.mk @@ -0,0 +1,33 @@ +# +# Copyright (C) 2015 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. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_CFLAGS := -Wall -Wextra -Werror -Wunused +LOCAL_SRC_FILES := \ + FingerprintDaemonProxy.cpp \ + IFingerprintDaemon.cpp \ + IFingerprintDaemonCallback.cpp \ + fingerprintd.cpp +LOCAL_MODULE := fingerprintd +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + liblog \ + libhardware \ + libutils \ + libkeystore_binder +include $(BUILD_EXECUTABLE) diff --git a/fingerprintd/FingerprintDaemonProxy.cpp b/fingerprintd/FingerprintDaemonProxy.cpp new file mode 100644 index 000000000..beb95de73 --- /dev/null +++ b/fingerprintd/FingerprintDaemonProxy.cpp @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2015 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 "fingerprintd" + +#include <binder/IServiceManager.h> +#include <hardware/hardware.h> +#include <hardware/fingerprint.h> +#include <hardware/hw_auth_token.h> +#include <keystore/IKeystoreService.h> +#include <keystore/keystore.h> // for error codes +#include <utils/Log.h> + +#include "FingerprintDaemonProxy.h" + +namespace android { + +FingerprintDaemonProxy* FingerprintDaemonProxy::sInstance = NULL; + +// Supported fingerprint HAL version +static const uint16_t kVersion = HARDWARE_MODULE_API_VERSION(2, 0); + +FingerprintDaemonProxy::FingerprintDaemonProxy() : mModule(NULL), mDevice(NULL), mCallback(NULL) { + +} + +FingerprintDaemonProxy::~FingerprintDaemonProxy() { + closeHal(); +} + +void FingerprintDaemonProxy::hal_notify_callback(const fingerprint_msg_t *msg) { + FingerprintDaemonProxy* instance = FingerprintDaemonProxy::getInstance(); + const sp<IFingerprintDaemonCallback> callback = instance->mCallback; + if (callback == NULL) { + ALOGE("Invalid callback object"); + return; + } + const int64_t device = (int64_t) instance->mDevice; + switch (msg->type) { + case FINGERPRINT_ERROR: + ALOGD("onError(%d)", msg->data.error); + callback->onError(device, msg->data.error); + break; + case FINGERPRINT_ACQUIRED: + ALOGD("onAcquired(%d)", msg->data.acquired.acquired_info); + callback->onAcquired(device, msg->data.acquired.acquired_info); + break; + case FINGERPRINT_AUTHENTICATED: + ALOGD("onAuthenticated(fid=%d, gid=%d)", + msg->data.authenticated.finger.fid, + msg->data.authenticated.finger.gid); + if (msg->data.authenticated.finger.fid != 0) { + const uint8_t* hat = reinterpret_cast<const uint8_t *>(&msg->data.authenticated.hat); + instance->notifyKeystore(hat, sizeof(msg->data.authenticated.hat)); + } + callback->onAuthenticated(device, + msg->data.authenticated.finger.fid, + msg->data.authenticated.finger.gid); + break; + case FINGERPRINT_TEMPLATE_ENROLLING: + ALOGD("onEnrollResult(fid=%d, gid=%d, rem=%d)", + msg->data.enroll.finger.fid, + msg->data.enroll.finger.gid, + msg->data.enroll.samples_remaining); + callback->onEnrollResult(device, + msg->data.enroll.finger.fid, + msg->data.enroll.finger.gid, + msg->data.enroll.samples_remaining); + break; + case FINGERPRINT_TEMPLATE_REMOVED: + ALOGD("onRemove(fid=%d, gid=%d)", + msg->data.removed.finger.fid, + msg->data.removed.finger.gid); + callback->onRemoved(device, + msg->data.removed.finger.fid, + msg->data.removed.finger.gid); + break; + default: + ALOGE("invalid msg type: %d", msg->type); + return; + } +} + +void FingerprintDaemonProxy::notifyKeystore(const uint8_t *auth_token, const size_t auth_token_length) { + if (auth_token != NULL && auth_token_length > 0) { + // TODO: cache service? + sp < IServiceManager > sm = defaultServiceManager(); + sp < IBinder > binder = sm->getService(String16("android.security.keystore")); + sp < IKeystoreService > service = interface_cast < IKeystoreService > (binder); + if (service != NULL) { + status_t ret = service->addAuthToken(auth_token, auth_token_length); + if (ret != ResponseCode::NO_ERROR) { + ALOGE("Falure sending auth token to KeyStore: %d", ret); + } + } else { + ALOGE("Unable to communicate with KeyStore"); + } + } +} + +void FingerprintDaemonProxy::init(const sp<IFingerprintDaemonCallback>& callback) { + if (mCallback != NULL && IInterface::asBinder(callback) != IInterface::asBinder(mCallback)) { + IInterface::asBinder(mCallback)->unlinkToDeath(this); + } + IInterface::asBinder(callback)->linkToDeath(this); + mCallback = callback; +} + +int32_t FingerprintDaemonProxy::enroll(const uint8_t* token, ssize_t tokenSize, int32_t groupId, + int32_t timeout) { + ALOG(LOG_VERBOSE, LOG_TAG, "enroll(gid=%d, timeout=%d)\n", groupId, timeout); + if (tokenSize != sizeof(hw_auth_token_t) ) { + ALOG(LOG_VERBOSE, LOG_TAG, "enroll() : invalid token size %zu\n", tokenSize); + return -1; + } + const hw_auth_token_t* authToken = reinterpret_cast<const hw_auth_token_t*>(token); + return mDevice->enroll(mDevice, authToken, groupId, timeout); +} + +uint64_t FingerprintDaemonProxy::preEnroll() { + return mDevice->pre_enroll(mDevice); +} + +int32_t FingerprintDaemonProxy::postEnroll() { + return mDevice->post_enroll(mDevice); +} + +int32_t FingerprintDaemonProxy::stopEnrollment() { + ALOG(LOG_VERBOSE, LOG_TAG, "stopEnrollment()\n"); + return mDevice->cancel(mDevice); +} + +int32_t FingerprintDaemonProxy::authenticate(uint64_t sessionId, uint32_t groupId) { + ALOG(LOG_VERBOSE, LOG_TAG, "authenticate(sid=%" PRId64 ", gid=%d)\n", sessionId, groupId); + return mDevice->authenticate(mDevice, sessionId, groupId); +} + +int32_t FingerprintDaemonProxy::stopAuthentication() { + ALOG(LOG_VERBOSE, LOG_TAG, "stopAuthentication()\n"); + return mDevice->cancel(mDevice); +} + +int32_t FingerprintDaemonProxy::remove(int32_t fingerId, int32_t groupId) { + ALOG(LOG_VERBOSE, LOG_TAG, "remove(fid=%d, gid=%d)\n", fingerId, groupId); + return mDevice->remove(mDevice, groupId, fingerId); +} + +uint64_t FingerprintDaemonProxy::getAuthenticatorId() { + return mDevice->get_authenticator_id(mDevice); +} + +int32_t FingerprintDaemonProxy::setActiveGroup(int32_t groupId, const uint8_t* path, + ssize_t pathlen) { + if (pathlen >= PATH_MAX || pathlen <= 0) { + ALOGE("Bad path length: %zd", pathlen); + return -1; + } + // Convert to null-terminated string + char path_name[PATH_MAX]; + memcpy(path_name, path, pathlen); + path_name[pathlen] = '\0'; + ALOG(LOG_VERBOSE, LOG_TAG, "setActiveGroup(%d, %s, %zu)", groupId, path_name, pathlen); + return mDevice->set_active_group(mDevice, groupId, path_name); +} + +int64_t FingerprintDaemonProxy::openHal() { + ALOG(LOG_VERBOSE, LOG_TAG, "nativeOpenHal()\n"); + int err; + const hw_module_t *hw_module = NULL; + if (0 != (err = hw_get_module(FINGERPRINT_HARDWARE_MODULE_ID, &hw_module))) { + ALOGE("Can't open fingerprint HW Module, error: %d", err); + return 0; + } + if (NULL == hw_module) { + ALOGE("No valid fingerprint module"); + return 0; + } + + mModule = reinterpret_cast<const fingerprint_module_t*>(hw_module); + + if (mModule->common.methods->open == NULL) { + ALOGE("No valid open method"); + return 0; + } + + hw_device_t *device = NULL; + + if (0 != (err = mModule->common.methods->open(hw_module, NULL, &device))) { + ALOGE("Can't open fingerprint methods, error: %d", err); + return 0; + } + + if (kVersion != device->version) { + ALOGE("Wrong fp version. Expected %d, got %d", kVersion, device->version); + // return 0; // FIXME + } + + mDevice = reinterpret_cast<fingerprint_device_t*>(device); + err = mDevice->set_notify(mDevice, hal_notify_callback); + if (err < 0) { + ALOGE("Failed in call to set_notify(), err=%d", err); + return 0; + } + + // Sanity check - remove + if (mDevice->notify != hal_notify_callback) { + ALOGE("NOTIFY not set properly: %p != %p", mDevice->notify, hal_notify_callback); + } + + ALOG(LOG_VERBOSE, LOG_TAG, "fingerprint HAL successfully initialized"); + return reinterpret_cast<int64_t>(mDevice); // This is just a handle +} + +int32_t FingerprintDaemonProxy::closeHal() { + ALOG(LOG_VERBOSE, LOG_TAG, "nativeCloseHal()\n"); + if (mDevice == NULL) { + ALOGE("No valid device"); + return -ENOSYS; + } + int err; + if (0 != (err = mDevice->common.close(reinterpret_cast<hw_device_t*>(mDevice)))) { + ALOGE("Can't close fingerprint module, error: %d", err); + return err; + } + mDevice = NULL; + return 0; +} + +void FingerprintDaemonProxy::binderDied(const wp<IBinder>& who) { + ALOGD("binder died"); + int err; + if (0 != (err = closeHal())) { + ALOGE("Can't close fingerprint device, error: %d", err); + } + if (IInterface::asBinder(mCallback) == who) { + mCallback = NULL; + } +} + +} diff --git a/fingerprintd/FingerprintDaemonProxy.h b/fingerprintd/FingerprintDaemonProxy.h new file mode 100644 index 000000000..871c0e62a --- /dev/null +++ b/fingerprintd/FingerprintDaemonProxy.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2015 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 FINGERPRINT_DAEMON_PROXY_H_ +#define FINGERPRINT_DAEMON_PROXY_H_ + +#include "IFingerprintDaemon.h" +#include "IFingerprintDaemonCallback.h" + +namespace android { + +class FingerprintDaemonProxy : public BnFingerprintDaemon { + public: + static FingerprintDaemonProxy* getInstance() { + if (sInstance == NULL) { + sInstance = new FingerprintDaemonProxy(); + } + return sInstance; + } + + // These reflect binder methods. + virtual void init(const sp<IFingerprintDaemonCallback>& callback); + virtual int32_t enroll(const uint8_t* token, ssize_t tokenLength, int32_t groupId, int32_t timeout); + virtual uint64_t preEnroll(); + virtual int32_t postEnroll(); + virtual int32_t stopEnrollment(); + virtual int32_t authenticate(uint64_t sessionId, uint32_t groupId); + virtual int32_t stopAuthentication(); + virtual int32_t remove(int32_t fingerId, int32_t groupId); + virtual uint64_t getAuthenticatorId(); + virtual int32_t setActiveGroup(int32_t groupId, const uint8_t* path, ssize_t pathLen); + virtual int64_t openHal(); + virtual int32_t closeHal(); + + private: + FingerprintDaemonProxy(); + virtual ~FingerprintDaemonProxy(); + void binderDied(const wp<IBinder>& who); + void notifyKeystore(const uint8_t *auth_token, const size_t auth_token_length); + static void hal_notify_callback(const fingerprint_msg_t *msg); + + static FingerprintDaemonProxy* sInstance; + fingerprint_module_t const* mModule; + fingerprint_device_t* mDevice; + sp<IFingerprintDaemonCallback> mCallback; +}; + +} // namespace android + +#endif // FINGERPRINT_DAEMON_PROXY_H_ diff --git a/fingerprintd/IFingerprintDaemon.cpp b/fingerprintd/IFingerprintDaemon.cpp new file mode 100644 index 000000000..71317932c --- /dev/null +++ b/fingerprintd/IFingerprintDaemon.cpp @@ -0,0 +1,195 @@ +/* + * Copyright 2015, 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 <inttypes.h> + +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/PermissionCache.h> +#include <utils/String16.h> +#include <utils/Looper.h> +#include <keystore/IKeystoreService.h> +#include <keystore/keystore.h> // for error code +#include <hardware/hardware.h> +#include <hardware/fingerprint.h> +#include <hardware/hw_auth_token.h> +#include "IFingerprintDaemon.h" +#include "IFingerprintDaemonCallback.h" + +namespace android { + +static const String16 USE_FINGERPRINT_PERMISSION("android.permission.USE_FINGERPRINT"); +static const String16 MANAGE_FINGERPRINT_PERMISSION("android.permission.MANAGE_FINGERPRINT"); +static const String16 HAL_FINGERPRINT_PERMISSION("android.permission.MANAGE_FINGERPRINT"); // TODO +static const String16 DUMP_PERMISSION("android.permission.DUMP"); + +const android::String16 +IFingerprintDaemon::descriptor("android.hardware.fingerprint.IFingerprintDaemon"); + +const android::String16& +IFingerprintDaemon::getInterfaceDescriptor() const { + return IFingerprintDaemon::descriptor; +} + +status_t BnFingerprintDaemon::onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags) { + switch(code) { + case AUTHENTICATE: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + const uint64_t sessionId = data.readInt64(); + const uint32_t groupId = data.readInt32(); + const int32_t ret = authenticate(sessionId, groupId); + reply->writeNoException(); + reply->writeInt32(ret); + return NO_ERROR; + }; + case CANCEL_AUTHENTICATION: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + const int32_t ret = stopAuthentication(); + reply->writeNoException(); + reply->writeInt32(ret); + return NO_ERROR; + } + case ENROLL: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + const ssize_t tokenSize = data.readInt32(); + const uint8_t* token = static_cast<const uint8_t *>(data.readInplace(tokenSize)); + const int32_t groupId = data.readInt32(); + const int32_t timeout = data.readInt32(); + const int32_t ret = enroll(token, tokenSize, groupId, timeout); + reply->writeNoException(); + reply->writeInt32(ret); + return NO_ERROR; + } + case CANCEL_ENROLLMENT: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + const int32_t ret = stopEnrollment(); + reply->writeNoException(); + reply->writeInt32(ret); + return NO_ERROR; + } + case PRE_ENROLL: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + const uint64_t ret = preEnroll(); + reply->writeNoException(); + reply->writeInt64(ret); + return NO_ERROR; + } + case POST_ENROLL: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + const int32_t ret = postEnroll(); + reply->writeNoException(); + reply->writeInt32(ret); + return NO_ERROR; + } + case REMOVE: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + const int32_t fingerId = data.readInt32(); + const int32_t groupId = data.readInt32(); + const int32_t ret = remove(fingerId, groupId); + reply->writeNoException(); + reply->writeInt32(ret); + return NO_ERROR; + } + case GET_AUTHENTICATOR_ID: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + const uint64_t ret = getAuthenticatorId(); + reply->writeNoException(); + reply->writeInt64(ret); + return NO_ERROR; + } + case SET_ACTIVE_GROUP: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + const int32_t group = data.readInt32(); + const ssize_t pathSize = data.readInt32(); + const uint8_t* path = static_cast<const uint8_t *>(data.readInplace(pathSize)); + const int32_t ret = setActiveGroup(group, path, pathSize); + reply->writeNoException(); + reply->writeInt32(ret); + return NO_ERROR; + } + case OPEN_HAL: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + const int64_t ret = openHal(); + reply->writeNoException(); + reply->writeInt64(ret); + return NO_ERROR; + } + case CLOSE_HAL: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + const int32_t ret = closeHal(); + reply->writeNoException(); + reply->writeInt32(ret); + return NO_ERROR; + } + case INIT: { + CHECK_INTERFACE(IFingerprintDaemon, data, reply); + if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) { + return PERMISSION_DENIED; + } + sp<IFingerprintDaemonCallback> callback = + interface_cast<IFingerprintDaemonCallback>(data.readStrongBinder()); + init(callback); + reply->writeNoException(); + return NO_ERROR; + } + default: + return BBinder::onTransact(code, data, reply, flags); + } +}; + +bool BnFingerprintDaemon::checkPermission(const String16& permission) { + const IPCThreadState* ipc = IPCThreadState::self(); + const int calling_pid = ipc->getCallingPid(); + const int calling_uid = ipc->getCallingUid(); + return PermissionCache::checkPermission(permission, calling_pid, calling_uid); +} + + +}; // namespace android diff --git a/fingerprintd/IFingerprintDaemon.h b/fingerprintd/IFingerprintDaemon.h new file mode 100644 index 000000000..1eb4ac179 --- /dev/null +++ b/fingerprintd/IFingerprintDaemon.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2015 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 IFINGERPRINT_DAEMON_H_ +#define IFINGERPRINT_DAEMON_H_ + +#include <binder/IInterface.h> +#include <binder/Parcel.h> + +namespace android { + +class IFingerprintDaemonCallback; + +/* +* Abstract base class for native implementation of FingerprintService. +* +* Note: This must be kept manually in sync with IFingerprintDaemon.aidl +*/ +class IFingerprintDaemon : public IInterface, public IBinder::DeathRecipient { + public: + enum { + AUTHENTICATE = IBinder::FIRST_CALL_TRANSACTION + 0, + CANCEL_AUTHENTICATION = IBinder::FIRST_CALL_TRANSACTION + 1, + ENROLL = IBinder::FIRST_CALL_TRANSACTION + 2, + CANCEL_ENROLLMENT = IBinder::FIRST_CALL_TRANSACTION + 3, + PRE_ENROLL = IBinder::FIRST_CALL_TRANSACTION + 4, + REMOVE = IBinder::FIRST_CALL_TRANSACTION + 5, + GET_AUTHENTICATOR_ID = IBinder::FIRST_CALL_TRANSACTION + 6, + SET_ACTIVE_GROUP = IBinder::FIRST_CALL_TRANSACTION + 7, + OPEN_HAL = IBinder::FIRST_CALL_TRANSACTION + 8, + CLOSE_HAL = IBinder::FIRST_CALL_TRANSACTION + 9, + INIT = IBinder::FIRST_CALL_TRANSACTION + 10, + POST_ENROLL = IBinder::FIRST_CALL_TRANSACTION + 11, + }; + + IFingerprintDaemon() { } + virtual ~IFingerprintDaemon() { } + virtual const android::String16& getInterfaceDescriptor() const; + + // Binder interface methods + virtual void init(const sp<IFingerprintDaemonCallback>& callback) = 0; + virtual int32_t enroll(const uint8_t* token, ssize_t tokenLength, int32_t groupId, + int32_t timeout) = 0; + virtual uint64_t preEnroll() = 0; + virtual int32_t postEnroll() = 0; + virtual int32_t stopEnrollment() = 0; + virtual int32_t authenticate(uint64_t sessionId, uint32_t groupId) = 0; + virtual int32_t stopAuthentication() = 0; + virtual int32_t remove(int32_t fingerId, int32_t groupId) = 0; + virtual uint64_t getAuthenticatorId() = 0; + virtual int32_t setActiveGroup(int32_t groupId, const uint8_t* path, ssize_t pathLen) = 0; + virtual int64_t openHal() = 0; + virtual int32_t closeHal() = 0; + + // DECLARE_META_INTERFACE - C++ client interface not needed + static const android::String16 descriptor; + static void hal_notify_callback(const fingerprint_msg_t *msg); +}; + +// ---------------------------------------------------------------------------- + +class BnFingerprintDaemon: public BnInterface<IFingerprintDaemon> { + public: + virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags = 0); + private: + bool checkPermission(const String16& permission); +}; + +} // namespace android + +#endif // IFINGERPRINT_DAEMON_H_ + diff --git a/fingerprintd/IFingerprintDaemonCallback.cpp b/fingerprintd/IFingerprintDaemonCallback.cpp new file mode 100644 index 000000000..44d8020ae --- /dev/null +++ b/fingerprintd/IFingerprintDaemonCallback.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2015 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 "IFingerprintDaemonCallback" +#include <stdint.h> +#include <sys/types.h> +#include <utils/Log.h> +#include <binder/Parcel.h> + +#include "IFingerprintDaemonCallback.h" + +namespace android { + +class BpFingerprintDaemonCallback : public BpInterface<IFingerprintDaemonCallback> +{ +public: + BpFingerprintDaemonCallback(const sp<IBinder>& impl) : + BpInterface<IFingerprintDaemonCallback>(impl) { + } + virtual status_t onEnrollResult(int64_t devId, int32_t fpId, int32_t gpId, int32_t rem) { + Parcel data, reply; + data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor()); + data.writeInt64(devId); + data.writeInt32(fpId); + data.writeInt32(gpId); + data.writeInt32(rem); + return remote()->transact(ON_ENROLL_RESULT, data, &reply, IBinder::FLAG_ONEWAY); + } + + virtual status_t onAcquired(int64_t devId, int32_t acquiredInfo) { + Parcel data, reply; + data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor()); + data.writeInt64(devId); + data.writeInt32(acquiredInfo); + return remote()->transact(ON_ACQUIRED, data, &reply, IBinder::FLAG_ONEWAY); + } + + virtual status_t onAuthenticated(int64_t devId, int32_t fpId, int32_t gpId) { + Parcel data, reply; + data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor()); + data.writeInt64(devId); + data.writeInt32(fpId); + data.writeInt32(gpId); + return remote()->transact(ON_AUTHENTICATED, data, &reply, IBinder::FLAG_ONEWAY); + } + + virtual status_t onError(int64_t devId, int32_t error) { + Parcel data, reply; + data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor()); + data.writeInt64(devId); + data.writeInt32(error); + return remote()->transact(ON_ERROR, data, &reply, IBinder::FLAG_ONEWAY); + } + + virtual status_t onRemoved(int64_t devId, int32_t fpId, int32_t gpId) { + Parcel data, reply; + data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor()); + data.writeInt64(devId); + data.writeInt32(fpId); + data.writeInt32(gpId); + return remote()->transact(ON_REMOVED, data, &reply, IBinder::FLAG_ONEWAY); + } + + virtual status_t onEnumerate(int64_t devId, const int32_t* fpIds, const int32_t* gpIds, + int32_t sz) { + Parcel data, reply; + data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor()); + data.writeInt64(devId); + data.writeInt32Array(sz, fpIds); + data.writeInt32Array(sz, gpIds); + return remote()->transact(ON_ENUMERATE, data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(FingerprintDaemonCallback, + "android.hardware.fingerprint.IFingerprintDaemonCallback"); + +}; // namespace android diff --git a/fingerprintd/IFingerprintDaemonCallback.h b/fingerprintd/IFingerprintDaemonCallback.h new file mode 100644 index 000000000..6e32213b4 --- /dev/null +++ b/fingerprintd/IFingerprintDaemonCallback.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2015 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 IFINGERPRINT_DAEMON_CALLBACK_H_ +#define IFINGERPRINT_DAEMON_CALLBACK_H_ + +#include <inttypes.h> +#include <utils/Errors.h> +#include <binder/IInterface.h> +#include <binder/Parcel.h> + +namespace android { + +/* +* Communication channel back to FingerprintService.java +*/ +class IFingerprintDaemonCallback : public IInterface { + public: + // must be kept in sync with IFingerprintService.aidl + enum { + ON_ENROLL_RESULT = IBinder::FIRST_CALL_TRANSACTION + 0, + ON_ACQUIRED = IBinder::FIRST_CALL_TRANSACTION + 1, + ON_AUTHENTICATED = IBinder::FIRST_CALL_TRANSACTION + 2, + ON_ERROR = IBinder::FIRST_CALL_TRANSACTION + 3, + ON_REMOVED = IBinder::FIRST_CALL_TRANSACTION + 4, + ON_ENUMERATE = IBinder::FIRST_CALL_TRANSACTION + 5, + }; + + virtual status_t onEnrollResult(int64_t devId, int32_t fpId, int32_t gpId, int32_t rem) = 0; + virtual status_t onAcquired(int64_t devId, int32_t acquiredInfo) = 0; + virtual status_t onAuthenticated(int64_t devId, int32_t fingerId, int32_t groupId) = 0; + virtual status_t onError(int64_t devId, int32_t error) = 0; + virtual status_t onRemoved(int64_t devId, int32_t fingerId, int32_t groupId) = 0; + virtual status_t onEnumerate(int64_t devId, const int32_t* fpIds, const int32_t* gpIds, + int32_t sz) = 0; + + DECLARE_META_INTERFACE(FingerprintDaemonCallback); +}; + +}; // namespace android + +#endif // IFINGERPRINT_DAEMON_CALLBACK_H_ diff --git a/fingerprintd/fingerprintd.cpp b/fingerprintd/fingerprintd.cpp new file mode 100644 index 000000000..8fa7ed18e --- /dev/null +++ b/fingerprintd/fingerprintd.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2015 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 "fingerprintd" + +#include <cutils/log.h> +#include <utils/Log.h> + +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/PermissionCache.h> +#include <utils/String16.h> + +#include <keystore/IKeystoreService.h> +#include <keystore/keystore.h> // for error codes + +#include <hardware/hardware.h> +#include <hardware/fingerprint.h> +#include <hardware/hw_auth_token.h> + +#include "FingerprintDaemonProxy.h" + +int main() { + ALOGI("Starting " LOG_TAG); + android::sp<android::IServiceManager> serviceManager = android::defaultServiceManager(); + android::sp<android::FingerprintDaemonProxy> proxy = + android::FingerprintDaemonProxy::getInstance(); + android::status_t ret = serviceManager->addService( + android::FingerprintDaemonProxy::descriptor, proxy); + if (ret != android::OK) { + ALOGE("Couldn't register " LOG_TAG " binder service!"); + return -1; + } + + /* + * We're the only thread in existence, so we're just going to process + * Binder transaction as a single-threaded program. + */ + android::IPCThreadState::self()->joinThreadPool(); + ALOGI("Done"); + return 0; +} diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk index cf2965e6a..5e6c6f94f 100644 --- a/fs_mgr/Android.mk +++ b/fs_mgr/Android.mk @@ -3,9 +3,13 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES:= fs_mgr.c fs_mgr_verity.c fs_mgr_fstab.c fs_mgr_slotselect.c +LOCAL_SRC_FILES:= fs_mgr.c fs_mgr_verity.c fs_mgr_fstab.c +LOCAL_SRC_FILES += fs_mgr_format.c fs_mgr_slotselect.c -LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \ + system/vold \ + system/extras/ext4_utils \ + external/openssl/include LOCAL_MODULE:= libfs_mgr LOCAL_STATIC_LIBRARIES := liblogwrap libmincrypt libext4_utils_static libsquashfs_utils @@ -36,6 +40,7 @@ LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)/sbin LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED) LOCAL_STATIC_LIBRARIES := libfs_mgr liblogwrap libcutils liblog libc libmincrypt libext4_utils_static libsquashfs_utils +LOCAL_STATIC_LIBRARIES += libsparse_static libz libselinux LOCAL_CXX_STL := libc++_static LOCAL_CFLAGS := -Werror diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c index 044c89525..c47a58591 100644 --- a/fs_mgr/fs_mgr.c +++ b/fs_mgr/fs_mgr.c @@ -44,6 +44,9 @@ #include "mincrypt/sha.h" #include "mincrypt/sha256.h" +#include "ext4_utils.h" +#include "wipe.h" + #include "fs_mgr_priv.h" #include "fs_mgr_priv_verity.h" @@ -557,6 +560,8 @@ int fs_mgr_mount_all(struct fstab *fstab) } } int last_idx_inspected; + int top_idx = i; + mret = mount_with_alternatives(fstab, i, &last_idx_inspected, &attempted_idx); i = last_idx_inspected; mount_errno = errno; @@ -582,10 +587,38 @@ int fs_mgr_mount_all(struct fstab *fstab) continue; } - /* mount(2) returned an error, check if it's encryptable and deal with it */ + /* mount(2) returned an error, handle the encryptable/formattable case */ + bool wiped = partition_wiped(fstab->recs[top_idx].blk_device); + if (mret && mount_errno != EBUSY && mount_errno != EACCES && + fs_mgr_is_formattable(&fstab->recs[top_idx]) && wiped) { + /* top_idx and attempted_idx point at the same partition, but sometimes + * at two different lines in the fstab. Use the top one for formatting + * as that is the preferred one. + */ + ERROR("%s(): %s is wiped and %s %s is formattable. Format it.\n", __func__, + fstab->recs[top_idx].blk_device, fstab->recs[top_idx].mount_point, + fstab->recs[top_idx].fs_type); + if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) && + strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) { + int fd = open(fstab->recs[top_idx].key_loc, O_WRONLY, 0644); + if (fd >= 0) { + INFO("%s(): also wipe %s\n", __func__, fstab->recs[top_idx].key_loc); + wipe_block_device(fd, get_file_size(fd)); + close(fd); + } else { + ERROR("%s(): %s wouldn't open (%s)\n", __func__, + fstab->recs[top_idx].key_loc, strerror(errno)); + } + } + if (fs_mgr_do_format(&fstab->recs[top_idx]) == 0) { + /* Let's replay the mount actions. */ + i = top_idx - 1; + continue; + } + } if (mret && mount_errno != EBUSY && mount_errno != EACCES && fs_mgr_is_encryptable(&fstab->recs[attempted_idx])) { - if(partition_wiped(fstab->recs[attempted_idx].blk_device)) { + if (wiped) { ERROR("%s(): %s is wiped and %s %s is encryptable. Suggest recovery...\n", __func__, fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point, fstab->recs[attempted_idx].fs_type); diff --git a/fs_mgr/fs_mgr_format.c b/fs_mgr/fs_mgr_format.c new file mode 100644 index 000000000..c73045d44 --- /dev/null +++ b/fs_mgr/fs_mgr_format.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2015 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 <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <errno.h> +#include <cutils/partition_utils.h> +#include <sys/mount.h> +#include "ext4_utils.h" +#include "ext4.h" +#include "make_ext4fs.h" +#include "fs_mgr_priv.h" + +extern struct fs_info info; /* magic global from ext4_utils */ +extern void reset_ext4fs_info(); + +static int format_ext4(char *fs_blkdev, char *fs_mnt_point) +{ + unsigned int nr_sec; + int fd, rc = 0; + + if ((fd = open(fs_blkdev, O_WRONLY, 0644)) < 0) { + ERROR("Cannot open block device. %s\n", strerror(errno)); + return -1; + } + + if ((ioctl(fd, BLKGETSIZE, &nr_sec)) == -1) { + ERROR("Cannot get block device size. %s\n", strerror(errno)); + close(fd); + return -1; + } + + /* Format the partition using the calculated length */ + reset_ext4fs_info(); + info.len = ((off64_t)nr_sec * 512); + + /* Use make_ext4fs_internal to avoid wiping an already-wiped partition. */ + rc = make_ext4fs_internal(fd, NULL, NULL, fs_mnt_point, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL); + if (rc) { + ERROR("make_ext4fs returned %d.\n", rc); + } + close(fd); + + return rc; +} + +static int format_f2fs(char *fs_blkdev) +{ + char * args[3]; + int pid; + int rc = 0; + + args[0] = (char *)"/sbin/mkfs.f2fs"; + args[1] = fs_blkdev; + args[2] = (char *)0; + + pid = fork(); + if (pid < 0) { + return pid; + } + if (!pid) { + /* This doesn't return */ + execv("/sbin/mkfs.f2fs", args); + exit(1); + } + for(;;) { + pid_t p = waitpid(pid, &rc, 0); + if (p != pid) { + ERROR("Error waiting for child process - %d\n", p); + rc = -1; + break; + } + if (WIFEXITED(rc)) { + rc = WEXITSTATUS(rc); + INFO("%s done, status %d\n", args[0], rc); + if (rc) { + rc = -1; + } + break; + } + ERROR("Still waiting for %s...\n", args[0]); + } + + return rc; +} + +int fs_mgr_do_format(struct fstab_rec *fstab) +{ + int rc = -EINVAL; + + ERROR("%s: Format %s as '%s'.\n", __func__, fstab->blk_device, fstab->fs_type); + + if (!strncmp(fstab->fs_type, "f2fs", 4)) { + rc = format_f2fs(fstab->blk_device); + } else if (!strncmp(fstab->fs_type, "ext4", 4)) { + rc = format_ext4(fstab->blk_device, fstab->mount_point); + } else { + ERROR("File system type '%s' is not supported\n", fstab->fs_type); + } + + return rc; +} diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c index 4f18339fa..cf35b3f42 100644 --- a/fs_mgr/fs_mgr_fstab.c +++ b/fs_mgr/fs_mgr_fstab.c @@ -15,10 +15,12 @@ */ #include <ctype.h> +#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mount.h> +#include <unistd.h> #include "fs_mgr_priv.h" @@ -70,11 +72,26 @@ static struct flag_list fs_mgr_flags[] = { { "zramsize=", MF_ZRAMSIZE }, { "verify", MF_VERIFY }, { "noemulatedsd", MF_NOEMULATEDSD }, + { "notrim", MF_NOTRIM }, + { "formattable", MF_FORMATTABLE }, { "slotselect", MF_SLOTSELECT }, { "defaults", 0 }, { 0, 0 }, }; +static uint64_t calculate_zram_size(unsigned int percentage) +{ + uint64_t total; + + total = sysconf(_SC_PHYS_PAGES); + total *= percentage; + total /= 100; + + total *= sysconf(_SC_PAGESIZE); + + return total; +} + static int parse_flags(char *flags, struct flag_list *fl, struct fs_mgr_flag_values *flag_vals, char *fs_options, int fs_options_len) @@ -156,7 +173,12 @@ static int parse_flags(char *flags, struct flag_list *fl, } else if ((fl[i].flag == MF_SWAPPRIO) && flag_vals) { flag_vals->swap_prio = strtoll(strchr(p, '=') + 1, NULL, 0); } else if ((fl[i].flag == MF_ZRAMSIZE) && flag_vals) { - flag_vals->zram_size = strtoll(strchr(p, '=') + 1, NULL, 0); + int is_percent = !!strrchr(p, '%'); + unsigned int val = strtoll(strchr(p, '=') + 1, NULL, 0); + if (is_percent) + flag_vals->zram_size = calculate_zram_size(val); + else + flag_vals->zram_size = val; } break; } @@ -455,6 +477,16 @@ int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab) return fstab->fs_mgr_flags & MF_NOEMULATEDSD; } +int fs_mgr_is_notrim(struct fstab_rec *fstab) +{ + return fstab->fs_mgr_flags & MF_NOTRIM; +} + +int fs_mgr_is_formattable(struct fstab_rec *fstab) +{ + return fstab->fs_mgr_flags & (MF_FORMATTABLE); +} + int fs_mgr_is_slotselect(struct fstab_rec *fstab) { return fstab->fs_mgr_flags & MF_SLOTSELECT; diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h index 992b5443b..367ab6e28 100644 --- a/fs_mgr/fs_mgr_priv.h +++ b/fs_mgr/fs_mgr_priv.h @@ -76,7 +76,9 @@ #define MF_FORCECRYPT 0x400 #define MF_NOEMULATEDSD 0x800 /* no emulated sdcard daemon, sd card is the only external storage */ +#define MF_NOTRIM 0x1000 #define MF_FILEENCRYPTION 0x2000 +#define MF_FORMATTABLE 0x4000 #define MF_SLOTSELECT 0x8000 #define DM_BUF_SIZE 4096 @@ -85,4 +87,3 @@ int fs_mgr_set_blk_ro(const char *blockdev); int fs_mgr_update_for_slotselect(struct fstab *fstab); #endif /* __CORE_FS_MGR_PRIV_H */ - diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h index c58a888cb..27fccf72e 100644 --- a/fs_mgr/include/fs_mgr.h +++ b/fs_mgr/include/fs_mgr.h @@ -103,10 +103,14 @@ int fs_mgr_is_verified(const struct fstab_rec *fstab); int fs_mgr_is_encryptable(const struct fstab_rec *fstab); int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab); int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab); +int fs_mgr_is_notrim(struct fstab_rec *fstab); +int fs_mgr_is_formattable(struct fstab_rec *fstab); int fs_mgr_swapon_all(struct fstab *fstab); + +int fs_mgr_do_format(struct fstab_rec *fstab); + #ifdef __cplusplus } #endif #endif /* __CORE_FS_MGR_H */ - diff --git a/gatekeeperd/Android.mk b/gatekeeperd/Android.mk new file mode 100644 index 000000000..3f7895554 --- /dev/null +++ b/gatekeeperd/Android.mk @@ -0,0 +1,42 @@ +# +# Copyright (C) 2015 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. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_CFLAGS := -Wall -Wextra -Werror -Wunused +LOCAL_SRC_FILES := \ + SoftGateKeeperDevice.cpp \ + IGateKeeperService.cpp \ + gatekeeperd.cpp \ + IUserManager.cpp + +LOCAL_MODULE := gatekeeperd +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + libgatekeeper \ + liblog \ + libhardware \ + libbase \ + libutils \ + libcrypto \ + libkeystore_binder +LOCAL_STATIC_LIBRARIES := libscrypt_static +LOCAL_C_INCLUDES := external/scrypt/lib/crypto +LOCAL_INIT_RC := gatekeeperd.rc +include $(BUILD_EXECUTABLE) + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/gatekeeperd/IGateKeeperService.cpp b/gatekeeperd/IGateKeeperService.cpp new file mode 100644 index 000000000..95fbfd10b --- /dev/null +++ b/gatekeeperd/IGateKeeperService.cpp @@ -0,0 +1,167 @@ +/* + * Copyright 2015, 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 "GateKeeperService" +#include <utils/Log.h> + +#include "IGateKeeperService.h" + +namespace android { + +const android::String16 IGateKeeperService::descriptor("android.service.gatekeeper.IGateKeeperService"); +const android::String16& IGateKeeperService::getInterfaceDescriptor() const { + return IGateKeeperService::descriptor; +} + +status_t BnGateKeeperService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + switch(code) { + case ENROLL: { + CHECK_INTERFACE(IGateKeeperService, data, reply); + uint32_t uid = data.readInt32(); + + ssize_t currentPasswordHandleSize = data.readInt32(); + const uint8_t *currentPasswordHandle = + static_cast<const uint8_t *>(data.readInplace(currentPasswordHandleSize)); + if (!currentPasswordHandle) currentPasswordHandleSize = 0; + + ssize_t currentPasswordSize = data.readInt32(); + const uint8_t *currentPassword = + static_cast<const uint8_t *>(data.readInplace(currentPasswordSize)); + if (!currentPassword) currentPasswordSize = 0; + + ssize_t desiredPasswordSize = data.readInt32(); + const uint8_t *desiredPassword = + static_cast<const uint8_t *>(data.readInplace(desiredPasswordSize)); + if (!desiredPassword) desiredPasswordSize = 0; + + uint8_t *out = NULL; + uint32_t outSize = 0; + int ret = enroll(uid, currentPasswordHandle, currentPasswordHandleSize, + currentPassword, currentPasswordSize, desiredPassword, + desiredPasswordSize, &out, &outSize); + + reply->writeNoException(); + reply->writeInt32(1); + if (ret == 0 && outSize > 0 && out != NULL) { + reply->writeInt32(GATEKEEPER_RESPONSE_OK); + reply->writeInt32(0); + reply->writeInt32(outSize); + reply->writeInt32(outSize); + void *buf = reply->writeInplace(outSize); + memcpy(buf, out, outSize); + delete[] out; + } else if (ret > 0) { + reply->writeInt32(GATEKEEPER_RESPONSE_RETRY); + reply->writeInt32(ret); + } else { + reply->writeInt32(GATEKEEPER_RESPONSE_ERROR); + } + return NO_ERROR; + } + case VERIFY: { + CHECK_INTERFACE(IGateKeeperService, data, reply); + uint32_t uid = data.readInt32(); + ssize_t currentPasswordHandleSize = data.readInt32(); + const uint8_t *currentPasswordHandle = + static_cast<const uint8_t *>(data.readInplace(currentPasswordHandleSize)); + if (!currentPasswordHandle) currentPasswordHandleSize = 0; + + ssize_t currentPasswordSize = data.readInt32(); + const uint8_t *currentPassword = + static_cast<const uint8_t *>(data.readInplace(currentPasswordSize)); + if (!currentPassword) currentPasswordSize = 0; + + bool request_reenroll = false; + int ret = verify(uid, (uint8_t *) currentPasswordHandle, + currentPasswordHandleSize, (uint8_t *) currentPassword, currentPasswordSize, + &request_reenroll); + + reply->writeNoException(); + reply->writeInt32(1); + if (ret == 0) { + reply->writeInt32(GATEKEEPER_RESPONSE_OK); + reply->writeInt32(request_reenroll ? 1 : 0); + reply->writeInt32(0); // no payload returned from this call + } else if (ret > 0) { + reply->writeInt32(GATEKEEPER_RESPONSE_RETRY); + reply->writeInt32(ret); + } else { + reply->writeInt32(GATEKEEPER_RESPONSE_ERROR); + } + return NO_ERROR; + } + case VERIFY_CHALLENGE: { + CHECK_INTERFACE(IGateKeeperService, data, reply); + uint32_t uid = data.readInt32(); + uint64_t challenge = data.readInt64(); + ssize_t currentPasswordHandleSize = data.readInt32(); + const uint8_t *currentPasswordHandle = + static_cast<const uint8_t *>(data.readInplace(currentPasswordHandleSize)); + if (!currentPasswordHandle) currentPasswordHandleSize = 0; + + ssize_t currentPasswordSize = data.readInt32(); + const uint8_t *currentPassword = + static_cast<const uint8_t *>(data.readInplace(currentPasswordSize)); + if (!currentPassword) currentPasswordSize = 0; + + + uint8_t *out = NULL; + uint32_t outSize = 0; + bool request_reenroll = false; + int ret = verifyChallenge(uid, challenge, (uint8_t *) currentPasswordHandle, + currentPasswordHandleSize, (uint8_t *) currentPassword, currentPasswordSize, + &out, &outSize, &request_reenroll); + reply->writeNoException(); + reply->writeInt32(1); + if (ret == 0 && outSize > 0 && out != NULL) { + reply->writeInt32(GATEKEEPER_RESPONSE_OK); + reply->writeInt32(request_reenroll ? 1 : 0); + reply->writeInt32(outSize); + reply->writeInt32(outSize); + void *buf = reply->writeInplace(outSize); + memcpy(buf, out, outSize); + delete[] out; + } else if (ret > 0) { + reply->writeInt32(GATEKEEPER_RESPONSE_RETRY); + reply->writeInt32(ret); + } else { + reply->writeInt32(GATEKEEPER_RESPONSE_ERROR); + } + return NO_ERROR; + } + case GET_SECURE_USER_ID: { + CHECK_INTERFACE(IGateKeeperService, data, reply); + uint32_t uid = data.readInt32(); + uint64_t sid = getSecureUserId(uid); + reply->writeNoException(); + reply->writeInt64(sid); + return NO_ERROR; + } + case CLEAR_SECURE_USER_ID: { + CHECK_INTERFACE(IGateKeeperService, data, reply); + uint32_t uid = data.readInt32(); + clearSecureUserId(uid); + reply->writeNoException(); + return NO_ERROR; + } + default: + return BBinder::onTransact(code, data, reply, flags); + } +}; + + +}; // namespace android diff --git a/gatekeeperd/IGateKeeperService.h b/gatekeeperd/IGateKeeperService.h new file mode 100644 index 000000000..f070486cd --- /dev/null +++ b/gatekeeperd/IGateKeeperService.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2015 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 IGATEKEEPER_SERVICE_H_ +#define IGATEKEEPER_SERVICE_H_ + +#include <binder/IInterface.h> +#include <binder/Parcel.h> + +namespace android { + +/* + * This must be kept manually in sync with frameworks/base's IGateKeeperService.aidl + */ +class IGateKeeperService : public IInterface { +public: + enum { + ENROLL = IBinder::FIRST_CALL_TRANSACTION + 0, + VERIFY = IBinder::FIRST_CALL_TRANSACTION + 1, + VERIFY_CHALLENGE = IBinder::FIRST_CALL_TRANSACTION + 2, + GET_SECURE_USER_ID = IBinder::FIRST_CALL_TRANSACTION + 3, + CLEAR_SECURE_USER_ID = IBinder::FIRST_CALL_TRANSACTION + 4, + }; + + enum { + GATEKEEPER_RESPONSE_OK = 0, + GATEKEEPER_RESPONSE_RETRY = 1, + GATEKEEPER_RESPONSE_ERROR = -1, + }; + + // DECLARE_META_INTERFACE - C++ client interface not needed + static const android::String16 descriptor; + virtual const android::String16& getInterfaceDescriptor() const; + IGateKeeperService() {} + virtual ~IGateKeeperService() {} + + /** + * Enrolls a password with the GateKeeper. Returns 0 on success, negative on failure. + * Returns: + * - 0 on success + * - A timestamp T > 0 if the call has failed due to throttling and should not + * be reattempted until T milliseconds have elapsed + * - -1 on failure + */ + virtual int enroll(uint32_t uid, + const uint8_t *current_password_handle, uint32_t current_password_handle_length, + const uint8_t *current_password, uint32_t current_password_length, + const uint8_t *desired_password, uint32_t desired_password_length, + uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) = 0; + + /** + * Verifies a password previously enrolled with the GateKeeper. + * Returns: + * - 0 on success + * - A timestamp T > 0 if the call has failed due to throttling and should not + * be reattempted until T milliseconds have elapsed + * - -1 on failure + */ + virtual int verify(uint32_t uid, const uint8_t *enrolled_password_handle, + uint32_t enrolled_password_handle_length, + const uint8_t *provided_password, uint32_t provided_password_length, + bool *request_reenroll) = 0; + + /** + * Verifies a password previously enrolled with the GateKeeper. + * Returns: + * - 0 on success + * - A timestamp T > 0 if the call has failed due to throttling and should not + * be reattempted until T milliseconds have elapsed + * - -1 on failure + */ + virtual int verifyChallenge(uint32_t uid, uint64_t challenge, + const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length, + const uint8_t *provided_password, uint32_t provided_password_length, + uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) = 0; + /** + * Returns the secure user ID for the provided android user + */ + virtual uint64_t getSecureUserId(uint32_t uid) = 0; + + /** + * Clears the secure user ID associated with the user. + */ + virtual void clearSecureUserId(uint32_t uid) = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnGateKeeperService: public BnInterface<IGateKeeperService> { +public: + virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags = 0); +}; + +} // namespace android + +#endif + diff --git a/gatekeeperd/IUserManager.cpp b/gatekeeperd/IUserManager.cpp new file mode 100644 index 000000000..8645fc2c9 --- /dev/null +++ b/gatekeeperd/IUserManager.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 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 "IUserManager" +#include <stdint.h> +#include <sys/types.h> +#include <utils/Log.h> +#include <binder/Parcel.h> + +#include "IUserManager.h" + +namespace android { + +class BpUserManager : public BpInterface<IUserManager> +{ +public: + BpUserManager(const sp<IBinder>& impl) : + BpInterface<IUserManager>(impl) { + } + virtual int32_t getCredentialOwnerProfile(int32_t user_id) { + Parcel data, reply; + data.writeInterfaceToken(IUserManager::getInterfaceDescriptor()); + data.writeInt32(user_id); + status_t rc = remote()->transact(GET_CREDENTIAL_OWNER_PROFILE, data, &reply, 0); + if (rc != NO_ERROR) { + ALOGE("%s: failed (%d)\n", __func__, rc); + return -1; + } + + int32_t exception = reply.readExceptionCode(); + if (exception != 0) { + ALOGE("%s: got exception (%d)\n", __func__, exception); + return -1; + } + + return reply.readInt32(); + } + +}; + +IMPLEMENT_META_INTERFACE(UserManager, "android.os.IUserManager"); + +}; // namespace android + diff --git a/gatekeeperd/IUserManager.h b/gatekeeperd/IUserManager.h new file mode 100644 index 000000000..640e9b511 --- /dev/null +++ b/gatekeeperd/IUserManager.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015 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 IUSERMANAGER_H_ +#define IUSERMANAGER_H_ + +#include <inttypes.h> +#include <utils/Errors.h> +#include <binder/IInterface.h> +#include <binder/Parcel.h> +#include <utils/Vector.h> + +namespace android { + +/* +* Communication channel to UserManager +*/ +class IUserManager : public IInterface { + public: + // must be kept in sync with IUserManager.aidl + enum { + GET_CREDENTIAL_OWNER_PROFILE = IBinder::FIRST_CALL_TRANSACTION + 0, + }; + + virtual int32_t getCredentialOwnerProfile(int32_t user_id) = 0; + + DECLARE_META_INTERFACE(UserManager); +}; + +}; // namespace android + +#endif // IUSERMANAGER_H_ + diff --git a/gatekeeperd/SoftGateKeeper.h b/gatekeeperd/SoftGateKeeper.h new file mode 100644 index 000000000..75fe11dc1 --- /dev/null +++ b/gatekeeperd/SoftGateKeeper.h @@ -0,0 +1,180 @@ +/* + * Copyright 2015 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 SOFT_GATEKEEPER_H_ +#define SOFT_GATEKEEPER_H_ + +extern "C" { +#include <openssl/rand.h> +#include <openssl/sha.h> + +#include <crypto_scrypt.h> +} + +#include <UniquePtr.h> +#include <gatekeeper/gatekeeper.h> +#include <iostream> +#include <unordered_map> + +namespace gatekeeper { + +struct fast_hash_t { + uint64_t salt; + uint8_t digest[SHA256_DIGEST_LENGTH]; +}; + +class SoftGateKeeper : public GateKeeper { +public: + static const uint32_t SIGNATURE_LENGTH_BYTES = 32; + + // scrypt params + static const uint64_t N = 16384; + static const uint32_t r = 8; + static const uint32_t p = 1; + + static const int MAX_UINT_32_CHARS = 11; + + SoftGateKeeper() { + key_.reset(new uint8_t[SIGNATURE_LENGTH_BYTES]); + memset(key_.get(), 0, SIGNATURE_LENGTH_BYTES); + } + + virtual ~SoftGateKeeper() { + } + + virtual bool GetAuthTokenKey(const uint8_t **auth_token_key, + uint32_t *length) const { + if (auth_token_key == NULL || length == NULL) return false; + uint8_t *auth_token_key_copy = new uint8_t[SIGNATURE_LENGTH_BYTES]; + memcpy(auth_token_key_copy, key_.get(), SIGNATURE_LENGTH_BYTES); + + *auth_token_key = auth_token_key_copy; + *length = SIGNATURE_LENGTH_BYTES; + return true; + } + + virtual void GetPasswordKey(const uint8_t **password_key, uint32_t *length) { + if (password_key == NULL || length == NULL) return; + uint8_t *password_key_copy = new uint8_t[SIGNATURE_LENGTH_BYTES]; + memcpy(password_key_copy, key_.get(), SIGNATURE_LENGTH_BYTES); + + *password_key = password_key_copy; + *length = SIGNATURE_LENGTH_BYTES; + } + + virtual void ComputePasswordSignature(uint8_t *signature, uint32_t signature_length, + const uint8_t *, uint32_t, const uint8_t *password, + uint32_t password_length, salt_t salt) const { + if (signature == NULL) return; + crypto_scrypt(password, password_length, reinterpret_cast<uint8_t *>(&salt), + sizeof(salt), N, r, p, signature, signature_length); + } + + virtual void GetRandom(void *random, uint32_t requested_length) const { + if (random == NULL) return; + RAND_pseudo_bytes((uint8_t *) random, requested_length); + } + + virtual void ComputeSignature(uint8_t *signature, uint32_t signature_length, + const uint8_t *, uint32_t, const uint8_t *, const uint32_t) const { + if (signature == NULL) return; + memset(signature, 0, signature_length); + } + + virtual uint64_t GetMillisecondsSinceBoot() const { + struct timespec time; + int res = clock_gettime(CLOCK_BOOTTIME, &time); + if (res < 0) return 0; + return (time.tv_sec * 1000) + (time.tv_nsec / 1000 / 1000); + } + + virtual bool IsHardwareBacked() const { + return false; + } + + virtual bool GetFailureRecord(uint32_t uid, secure_id_t user_id, failure_record_t *record, + bool /* secure */) { + failure_record_t *stored = &failure_map_[uid]; + if (user_id != stored->secure_user_id) { + stored->secure_user_id = user_id; + stored->last_checked_timestamp = 0; + stored->failure_counter = 0; + } + memcpy(record, stored, sizeof(*record)); + return true; + } + + virtual bool ClearFailureRecord(uint32_t uid, secure_id_t user_id, bool /* secure */) { + failure_record_t *stored = &failure_map_[uid]; + stored->secure_user_id = user_id; + stored->last_checked_timestamp = 0; + stored->failure_counter = 0; + return true; + } + + virtual bool WriteFailureRecord(uint32_t uid, failure_record_t *record, bool /* secure */) { + failure_map_[uid] = *record; + return true; + } + + fast_hash_t ComputeFastHash(const SizedBuffer &password, uint64_t salt) { + fast_hash_t fast_hash; + size_t digest_size = password.length + sizeof(salt); + std::unique_ptr<uint8_t[]> digest(new uint8_t[digest_size]); + memcpy(digest.get(), &salt, sizeof(salt)); + memcpy(digest.get() + sizeof(salt), password.buffer.get(), password.length); + + SHA256(digest.get(), digest_size, (uint8_t *) &fast_hash.digest); + + fast_hash.salt = salt; + return fast_hash; + } + + bool VerifyFast(const fast_hash_t &fast_hash, const SizedBuffer &password) { + fast_hash_t computed = ComputeFastHash(password, fast_hash.salt); + return memcmp(computed.digest, fast_hash.digest, SHA256_DIGEST_LENGTH) == 0; + } + + bool DoVerify(const password_handle_t *expected_handle, const SizedBuffer &password) { + FastHashMap::const_iterator it = fast_hash_map_.find(expected_handle->user_id); + if (it != fast_hash_map_.end() && VerifyFast(it->second, password)) { + return true; + } else { + if (GateKeeper::DoVerify(expected_handle, password)) { + uint64_t salt; + GetRandom(&salt, sizeof(salt)); + fast_hash_map_[expected_handle->user_id] = ComputeFastHash(password, salt); + return true; + } + } + + return false; + } + +private: + + typedef std::unordered_map<uint32_t, failure_record_t> FailureRecordMap; + typedef std::unordered_map<uint64_t, fast_hash_t> FastHashMap; + + UniquePtr<uint8_t[]> key_; + FailureRecordMap failure_map_; + FastHashMap fast_hash_map_; +}; +} + +#endif // SOFT_GATEKEEPER_H_ + diff --git a/gatekeeperd/SoftGateKeeperDevice.cpp b/gatekeeperd/SoftGateKeeperDevice.cpp new file mode 100644 index 000000000..f5e2ce631 --- /dev/null +++ b/gatekeeperd/SoftGateKeeperDevice.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2015 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 "SoftGateKeeper.h" +#include "SoftGateKeeperDevice.h" + +namespace android { + +int SoftGateKeeperDevice::enroll(uint32_t uid, + const uint8_t *current_password_handle, uint32_t current_password_handle_length, + const uint8_t *current_password, uint32_t current_password_length, + const uint8_t *desired_password, uint32_t desired_password_length, + uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) { + + if (enrolled_password_handle == NULL || enrolled_password_handle_length == NULL || + desired_password == NULL || desired_password_length == 0) + return -EINVAL; + + // Current password and current password handle go together + if (current_password_handle == NULL || current_password_handle_length == 0 || + current_password == NULL || current_password_length == 0) { + current_password_handle = NULL; + current_password_handle_length = 0; + current_password = NULL; + current_password_length = 0; + } + + SizedBuffer desired_password_buffer(desired_password_length); + memcpy(desired_password_buffer.buffer.get(), desired_password, desired_password_length); + + SizedBuffer current_password_handle_buffer(current_password_handle_length); + if (current_password_handle) { + memcpy(current_password_handle_buffer.buffer.get(), current_password_handle, + current_password_handle_length); + } + + SizedBuffer current_password_buffer(current_password_length); + if (current_password) { + memcpy(current_password_buffer.buffer.get(), current_password, current_password_length); + } + + EnrollRequest request(uid, ¤t_password_handle_buffer, &desired_password_buffer, + ¤t_password_buffer); + EnrollResponse response; + + impl_->Enroll(request, &response); + + if (response.error == ERROR_RETRY) { + return response.retry_timeout; + } else if (response.error != ERROR_NONE) { + return -EINVAL; + } + + *enrolled_password_handle = response.enrolled_password_handle.buffer.release(); + *enrolled_password_handle_length = response.enrolled_password_handle.length; + return 0; +} + +int SoftGateKeeperDevice::verify(uint32_t uid, + uint64_t challenge, const uint8_t *enrolled_password_handle, + uint32_t enrolled_password_handle_length, const uint8_t *provided_password, + uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length, + bool *request_reenroll) { + + if (enrolled_password_handle == NULL || + provided_password == NULL) { + return -EINVAL; + } + + SizedBuffer password_handle_buffer(enrolled_password_handle_length); + memcpy(password_handle_buffer.buffer.get(), enrolled_password_handle, + enrolled_password_handle_length); + SizedBuffer provided_password_buffer(provided_password_length); + memcpy(provided_password_buffer.buffer.get(), provided_password, provided_password_length); + + VerifyRequest request(uid, challenge, &password_handle_buffer, &provided_password_buffer); + VerifyResponse response; + + impl_->Verify(request, &response); + + if (response.error == ERROR_RETRY) { + return response.retry_timeout; + } else if (response.error != ERROR_NONE) { + return -EINVAL; + } + + if (auth_token != NULL && auth_token_length != NULL) { + *auth_token = response.auth_token.buffer.release(); + *auth_token_length = response.auth_token.length; + } + + if (request_reenroll != NULL) { + *request_reenroll = response.request_reenroll; + } + + return 0; +} +} // namespace android diff --git a/gatekeeperd/SoftGateKeeperDevice.h b/gatekeeperd/SoftGateKeeperDevice.h new file mode 100644 index 000000000..3463c29ff --- /dev/null +++ b/gatekeeperd/SoftGateKeeperDevice.h @@ -0,0 +1,76 @@ +/* + * Copyright 2015 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 SOFT_GATEKEEPER_DEVICE_H_ +#define SOFT_GATEKEEPER_DEVICE_H_ + +#include "SoftGateKeeper.h" + +#include <UniquePtr.h> + +using namespace gatekeeper; + +namespace android { + +/** + * Software based GateKeeper implementation + */ +class SoftGateKeeperDevice { +public: + SoftGateKeeperDevice() { + impl_.reset(new SoftGateKeeper()); + } + + // Wrappers to translate the gatekeeper HAL API to the Kegyuard Messages API. + + /** + * Enrolls password_payload, which should be derived from a user selected pin or password, + * with the authentication factor private key used only for enrolling authentication + * factor data. + * + * Returns: 0 on success or an error code less than 0 on error. + * On error, enrolled_password_handle will not be allocated. + */ + int enroll(uint32_t uid, + const uint8_t *current_password_handle, uint32_t current_password_handle_length, + const uint8_t *current_password, uint32_t current_password_length, + const uint8_t *desired_password, uint32_t desired_password_length, + uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length); + + /** + * Verifies provided_password matches enrolled_password_handle. + * + * Implementations of this module may retain the result of this call + * to attest to the recency of authentication. + * + * On success, writes the address of a verification token to auth_token, + * usable to attest password verification to other trusted services. Clients + * may pass NULL for this value. + * + * Returns: 0 on success or an error code less than 0 on error + * On error, verification token will not be allocated + */ + int verify(uint32_t uid, uint64_t challenge, + const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length, + const uint8_t *provided_password, uint32_t provided_password_length, + uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll); +private: + UniquePtr<SoftGateKeeper> impl_; +}; + +} // namespace gatekeeper + +#endif //SOFT_GATEKEEPER_DEVICE_H_ diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp new file mode 100644 index 000000000..b4fdab0c5 --- /dev/null +++ b/gatekeeperd/gatekeeperd.cpp @@ -0,0 +1,351 @@ +/* + * Copyright (C) 2015 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 "gatekeeperd" + +#include "IGateKeeperService.h" + +#include <errno.h> +#include <stdint.h> +#include <inttypes.h> +#include <fcntl.h> +#include <unistd.h> + +#include <cutils/log.h> +#include <utils/Log.h> + +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/PermissionCache.h> +#include <utils/String16.h> +#include <utils/Log.h> + +#include <keystore/IKeystoreService.h> +#include <keystore/keystore.h> // For error code +#include <gatekeeper/password_handle.h> // for password_handle_t +#include <hardware/gatekeeper.h> +#include <hardware/hw_auth_token.h> + +#include "SoftGateKeeperDevice.h" +#include "IUserManager.h" + +namespace android { + +static const String16 KEYGUARD_PERMISSION("android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"); +static const String16 DUMP_PERMISSION("android.permission.DUMP"); + +class GateKeeperProxy : public BnGateKeeperService { +public: + GateKeeperProxy() { + int ret = hw_get_module_by_class(GATEKEEPER_HARDWARE_MODULE_ID, NULL, &module); + device = NULL; + + if (ret < 0) { + ALOGW("falling back to software GateKeeper"); + soft_device.reset(new SoftGateKeeperDevice()); + } else { + ret = gatekeeper_open(module, &device); + if (ret < 0) + LOG_ALWAYS_FATAL_IF(ret < 0, "Unable to open GateKeeper HAL"); + } + + if (mark_cold_boot()) { + ALOGI("cold boot: clearing state"); + if (device != NULL && device->delete_all_users != NULL) { + device->delete_all_users(device); + } + } + } + + virtual ~GateKeeperProxy() { + if (device) gatekeeper_close(device); + } + + void store_sid(uint32_t uid, uint64_t sid) { + char filename[21]; + sprintf(filename, "%u", uid); + int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR); + if (fd < 0) { + ALOGE("could not open file: %s: %s", filename, strerror(errno)); + return; + } + write(fd, &sid, sizeof(sid)); + close(fd); + } + + bool mark_cold_boot() { + const char *filename = ".coldboot"; + if (access(filename, F_OK) == -1) { + int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR); + if (fd < 0) { + ALOGE("could not open file: %s : %s", filename, strerror(errno)); + return false; + } + close(fd); + return true; + } + return false; + } + + void maybe_store_sid(uint32_t uid, uint64_t sid) { + char filename[21]; + sprintf(filename, "%u", uid); + if (access(filename, F_OK) == -1) { + store_sid(uid, sid); + } + } + + uint64_t read_sid(uint32_t uid) { + char filename[21]; + uint64_t sid; + sprintf(filename, "%u", uid); + int fd = open(filename, O_RDONLY); + if (fd < 0) return 0; + read(fd, &sid, sizeof(sid)); + close(fd); + return sid; + } + + void clear_sid(uint32_t uid) { + char filename[21]; + sprintf(filename, "%u", uid); + if (remove(filename) < 0) { + ALOGE("%s: could not remove file [%s], attempting 0 write", __func__, strerror(errno)); + store_sid(uid, 0); + } + } + + virtual int enroll(uint32_t uid, + const uint8_t *current_password_handle, uint32_t current_password_handle_length, + const uint8_t *current_password, uint32_t current_password_length, + const uint8_t *desired_password, uint32_t desired_password_length, + uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) { + IPCThreadState* ipc = IPCThreadState::self(); + const int calling_pid = ipc->getCallingPid(); + const int calling_uid = ipc->getCallingUid(); + if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) { + return PERMISSION_DENIED; + } + + // need a desired password to enroll + if (desired_password_length == 0) return -EINVAL; + + int ret; + if (device) { + const gatekeeper::password_handle_t *handle = + reinterpret_cast<const gatekeeper::password_handle_t *>(current_password_handle); + + if (handle != NULL && handle->version != 0 && !handle->hardware_backed) { + // handle is being re-enrolled from a software version. HAL probably won't accept + // the handle as valid, so we nullify it and enroll from scratch + current_password_handle = NULL; + current_password_handle_length = 0; + current_password = NULL; + current_password_length = 0; + } + + ret = device->enroll(device, uid, current_password_handle, current_password_handle_length, + current_password, current_password_length, + desired_password, desired_password_length, + enrolled_password_handle, enrolled_password_handle_length); + } else { + ret = soft_device->enroll(uid, + current_password_handle, current_password_handle_length, + current_password, current_password_length, + desired_password, desired_password_length, + enrolled_password_handle, enrolled_password_handle_length); + } + + if (ret == 0) { + gatekeeper::password_handle_t *handle = + reinterpret_cast<gatekeeper::password_handle_t *>(*enrolled_password_handle); + store_sid(uid, handle->user_id); + bool rr; + + // immediately verify this password so we don't ask the user to enter it again + // if they just created it. + verify(uid, *enrolled_password_handle, sizeof(password_handle_t), desired_password, + desired_password_length, &rr); + } + + return ret; + } + + virtual int verify(uint32_t uid, + const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length, + const uint8_t *provided_password, uint32_t provided_password_length, bool *request_reenroll) { + uint8_t *auth_token; + uint32_t auth_token_length; + return verifyChallenge(uid, 0, enrolled_password_handle, enrolled_password_handle_length, + provided_password, provided_password_length, + &auth_token, &auth_token_length, request_reenroll); + } + + virtual int verifyChallenge(uint32_t uid, uint64_t challenge, + const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length, + const uint8_t *provided_password, uint32_t provided_password_length, + uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) { + IPCThreadState* ipc = IPCThreadState::self(); + const int calling_pid = ipc->getCallingPid(); + const int calling_uid = ipc->getCallingUid(); + if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) { + return PERMISSION_DENIED; + } + + // can't verify if we're missing either param + if ((enrolled_password_handle_length | provided_password_length) == 0) + return -EINVAL; + + int ret; + if (device) { + const gatekeeper::password_handle_t *handle = + reinterpret_cast<const gatekeeper::password_handle_t *>(enrolled_password_handle); + // handle version 0 does not have hardware backed flag, and thus cannot be upgraded to + // a HAL if there was none before + if (handle->version == 0 || handle->hardware_backed) { + ret = device->verify(device, uid, challenge, + enrolled_password_handle, enrolled_password_handle_length, + provided_password, provided_password_length, auth_token, auth_token_length, + request_reenroll); + } else { + // upgrade scenario, a HAL has been added to this device where there was none before + SoftGateKeeperDevice soft_dev; + ret = soft_dev.verify(uid, challenge, + enrolled_password_handle, enrolled_password_handle_length, + provided_password, provided_password_length, auth_token, auth_token_length, + request_reenroll); + + if (ret == 0) { + // success! re-enroll with HAL + *request_reenroll = true; + } + } + } else { + ret = soft_device->verify(uid, challenge, + enrolled_password_handle, enrolled_password_handle_length, + provided_password, provided_password_length, auth_token, auth_token_length, + request_reenroll); + } + + if (ret == 0 && *auth_token != NULL && *auth_token_length > 0) { + // TODO: cache service? + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder = sm->getService(String16("android.security.keystore")); + sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder); + if (service != NULL) { + status_t ret = service->addAuthToken(*auth_token, *auth_token_length); + if (ret != ResponseCode::NO_ERROR) { + ALOGE("Falure sending auth token to KeyStore: %d", ret); + } + } else { + ALOGE("Unable to communicate with KeyStore"); + } + } + + if (ret == 0) { + maybe_store_sid(uid, reinterpret_cast<const gatekeeper::password_handle_t *>( + enrolled_password_handle)->user_id); + } + + return ret; + } + + virtual uint64_t getSecureUserId(uint32_t uid) { + uint64_t sid = read_sid(uid); + if (sid == 0) { + // might be a work profile, look up the parent + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder = sm->getService(String16("user")); + sp<IUserManager> um = interface_cast<IUserManager>(binder); + int32_t parent = um->getCredentialOwnerProfile(uid); + if (parent < 0) { + return 0; + } else if (parent != (int32_t) uid) { + return read_sid(parent); + } + } + return sid; + + } + + virtual void clearSecureUserId(uint32_t uid) { + IPCThreadState* ipc = IPCThreadState::self(); + const int calling_pid = ipc->getCallingPid(); + const int calling_uid = ipc->getCallingUid(); + if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) { + ALOGE("%s: permission denied for [%d:%d]", __func__, calling_pid, calling_uid); + return; + } + clear_sid(uid); + + if (device != NULL && device->delete_user != NULL) { + device->delete_user(device, uid); + } + } + + virtual status_t dump(int fd, const Vector<String16> &) { + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + if (!PermissionCache::checkPermission(DUMP_PERMISSION, pid, uid)) { + return PERMISSION_DENIED; + } + + if (device == NULL) { + const char *result = "Device not available"; + write(fd, result, strlen(result) + 1); + } else { + const char *result = "OK"; + write(fd, result, strlen(result) + 1); + } + + return NO_ERROR; + } + +private: + gatekeeper_device_t *device; + UniquePtr<SoftGateKeeperDevice> soft_device; + const hw_module_t *module; +}; +}// namespace android + +int main(int argc, char* argv[]) { + ALOGI("Starting gatekeeperd..."); + if (argc < 2) { + ALOGE("A directory must be specified!"); + return 1; + } + if (chdir(argv[1]) == -1) { + ALOGE("chdir: %s: %s", argv[1], strerror(errno)); + return 1; + } + + android::sp<android::IServiceManager> sm = android::defaultServiceManager(); + android::sp<android::GateKeeperProxy> proxy = new android::GateKeeperProxy(); + android::status_t ret = sm->addService( + android::String16("android.service.gatekeeper.IGateKeeperService"), proxy); + if (ret != android::OK) { + ALOGE("Couldn't register binder service!"); + return -1; + } + + /* + * We're the only thread in existence, so we're just going to process + * Binder transaction as a single-threaded program. + */ + android::IPCThreadState::self()->joinThreadPool(); + return 0; +} diff --git a/gatekeeperd/gatekeeperd.rc b/gatekeeperd/gatekeeperd.rc new file mode 100644 index 000000000..3f1b92d80 --- /dev/null +++ b/gatekeeperd/gatekeeperd.rc @@ -0,0 +1,3 @@ +service gatekeeperd /system/bin/gatekeeperd /data/misc/gatekeeper + class late_start + user system diff --git a/gatekeeperd/tests/Android.mk b/gatekeeperd/tests/Android.mk new file mode 100644 index 000000000..6fc4ac075 --- /dev/null +++ b/gatekeeperd/tests/Android.mk @@ -0,0 +1,29 @@ +# +# Copyright (C) 2015 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. +# + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := gatekeeperd-unit-tests +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers +LOCAL_SHARED_LIBRARIES := libgatekeeper libcrypto +LOCAL_STATIC_LIBRARIES := libscrypt_static +LOCAL_C_INCLUDES := external/scrypt/lib/crypto +LOCAL_SRC_FILES := \ + gatekeeper_test.cpp +include $(BUILD_NATIVE_TEST) + diff --git a/gatekeeperd/tests/gatekeeper_test.cpp b/gatekeeperd/tests/gatekeeper_test.cpp new file mode 100644 index 000000000..c504f9228 --- /dev/null +++ b/gatekeeperd/tests/gatekeeper_test.cpp @@ -0,0 +1,206 @@ +/* + * Copyright 2015 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 <arpa/inet.h> +#include <iostream> + +#include <gtest/gtest.h> +#include <UniquePtr.h> + +#include <hardware/hw_auth_token.h> + +#include "../SoftGateKeeper.h" + +using ::gatekeeper::SizedBuffer; +using ::testing::Test; +using ::gatekeeper::EnrollRequest; +using ::gatekeeper::EnrollResponse; +using ::gatekeeper::VerifyRequest; +using ::gatekeeper::VerifyResponse; +using ::gatekeeper::SoftGateKeeper; +using ::gatekeeper::secure_id_t; + +static void do_enroll(SoftGateKeeper &gatekeeper, EnrollResponse *response) { + SizedBuffer password; + + password.buffer.reset(new uint8_t[16]); + password.length = 16; + memset(password.buffer.get(), 0, 16); + EnrollRequest request(0, NULL, &password, NULL); + + gatekeeper.Enroll(request, response); +} + +TEST(GateKeeperTest, EnrollSuccess) { + SoftGateKeeper gatekeeper; + EnrollResponse response; + do_enroll(gatekeeper, &response); + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error); +} + +TEST(GateKeeperTest, EnrollBogusData) { + SoftGateKeeper gatekeeper; + SizedBuffer password; + EnrollResponse response; + + EnrollRequest request(0, NULL, &password, NULL); + + gatekeeper.Enroll(request, &response); + + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_INVALID, response.error); +} + +TEST(GateKeeperTest, VerifySuccess) { + SoftGateKeeper gatekeeper; + SizedBuffer provided_password; + EnrollResponse enroll_response; + + provided_password.buffer.reset(new uint8_t[16]); + provided_password.length = 16; + memset(provided_password.buffer.get(), 0, 16); + + do_enroll(gatekeeper, &enroll_response); + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error); + VerifyRequest request(0, 1, &enroll_response.enrolled_password_handle, + &provided_password); + VerifyResponse response; + + gatekeeper.Verify(request, &response); + + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error); + + hw_auth_token_t *auth_token = + reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get()); + + ASSERT_EQ((uint32_t) HW_AUTH_PASSWORD, ntohl(auth_token->authenticator_type)); + ASSERT_EQ((uint64_t) 1, auth_token->challenge); + ASSERT_NE(~((uint32_t) 0), auth_token->timestamp); + ASSERT_NE((uint64_t) 0, auth_token->user_id); + ASSERT_NE((uint64_t) 0, auth_token->authenticator_id); +} + +TEST(GateKeeperTest, TrustedReEnroll) { + SoftGateKeeper gatekeeper; + SizedBuffer provided_password; + EnrollResponse enroll_response; + SizedBuffer password_handle; + + // do_enroll enrolls an all 0 password + provided_password.buffer.reset(new uint8_t[16]); + provided_password.length = 16; + memset(provided_password.buffer.get(), 0, 16); + do_enroll(gatekeeper, &enroll_response); + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error); + + // keep a copy of the handle + password_handle.buffer.reset(new uint8_t[enroll_response.enrolled_password_handle.length]); + password_handle.length = enroll_response.enrolled_password_handle.length; + memcpy(password_handle.buffer.get(), enroll_response.enrolled_password_handle.buffer.get(), + password_handle.length); + + // verify first password + VerifyRequest request(0, 0, &enroll_response.enrolled_password_handle, + &provided_password); + VerifyResponse response; + gatekeeper.Verify(request, &response); + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error); + hw_auth_token_t *auth_token = + reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get()); + + secure_id_t secure_id = auth_token->user_id; + + // enroll new password + provided_password.buffer.reset(new uint8_t[16]); + provided_password.length = 16; + memset(provided_password.buffer.get(), 0, 16); + SizedBuffer password; + password.buffer.reset(new uint8_t[16]); + memset(password.buffer.get(), 1, 16); + password.length = 16; + EnrollRequest enroll_request(0, &password_handle, &password, &provided_password); + gatekeeper.Enroll(enroll_request, &enroll_response); + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error); + + // verify new password + password.buffer.reset(new uint8_t[16]); + memset(password.buffer.get(), 1, 16); + password.length = 16; + VerifyRequest new_request(0, 0, &enroll_response.enrolled_password_handle, + &password); + gatekeeper.Verify(new_request, &response); + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error); + ASSERT_EQ(secure_id, + reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get())->user_id); +} + + +TEST(GateKeeperTest, UntrustedReEnroll) { + SoftGateKeeper gatekeeper; + SizedBuffer provided_password; + EnrollResponse enroll_response; + + // do_enroll enrolls an all 0 password + provided_password.buffer.reset(new uint8_t[16]); + provided_password.length = 16; + memset(provided_password.buffer.get(), 0, 16); + do_enroll(gatekeeper, &enroll_response); + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error); + + // verify first password + VerifyRequest request(0, 0, &enroll_response.enrolled_password_handle, + &provided_password); + VerifyResponse response; + gatekeeper.Verify(request, &response); + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error); + hw_auth_token_t *auth_token = + reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get()); + + secure_id_t secure_id = auth_token->user_id; + + // enroll new password + SizedBuffer password; + password.buffer.reset(new uint8_t[16]); + memset(password.buffer.get(), 1, 16); + password.length = 16; + EnrollRequest enroll_request(0, NULL, &password, NULL); + gatekeeper.Enroll(enroll_request, &enroll_response); + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error); + + // verify new password + password.buffer.reset(new uint8_t[16]); + memset(password.buffer.get(), 1, 16); + password.length = 16; + VerifyRequest new_request(0, 0, &enroll_response.enrolled_password_handle, + &password); + gatekeeper.Verify(new_request, &response); + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error); + ASSERT_NE(secure_id, + reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get())->user_id); +} + + +TEST(GateKeeperTest, VerifyBogusData) { + SoftGateKeeper gatekeeper; + SizedBuffer provided_password; + SizedBuffer password_handle; + VerifyResponse response; + + VerifyRequest request(0, 0, &provided_password, &password_handle); + + gatekeeper.Verify(request, &response); + + ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_INVALID, response.error); +} diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp index cc6824ab5..85888c3ca 100644 --- a/healthd/healthd.cpp +++ b/healthd/healthd.cpp @@ -55,6 +55,7 @@ static struct healthd_config healthd_config = { .batteryFullChargePath = String8(String8::kEmptyString), .batteryCycleCountPath = String8(String8::kEmptyString), .energyCounter = NULL, + .boot_min_cap = 0, .screen_on = NULL, }; diff --git a/healthd/healthd.h b/healthd/healthd.h index 048fcd5e8..34ea55f66 100644 --- a/healthd/healthd.h +++ b/healthd/healthd.h @@ -69,6 +69,7 @@ struct healthd_config { android::String8 batteryCycleCountPath; int (*energyCounter)(int64_t *); + int boot_min_cap; bool (*screen_on)(android::BatteryProperties *props); }; diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp index 78f840369..6800ad277 100644 --- a/healthd/healthd_mode_charger.cpp +++ b/healthd/healthd_mode_charger.cpp @@ -116,6 +116,7 @@ struct charger { struct animation *batt_anim; GRSurface* surf_unknown; + int boot_min_cap; }; static struct frame batt_anim_frames[] = { @@ -520,19 +521,29 @@ static void process_key(struct charger *charger, int code, int64_t now) LOGW("[%" PRId64 "] booting from charger mode\n", now); property_set("sys.boot_from_charger_mode", "1"); } else { - LOGW("[%" PRId64 "] rebooting\n", now); - android_reboot(ANDROID_RB_RESTART, 0, 0); + if (charger->batt_anim->capacity >= charger->boot_min_cap) { + LOGW("[%" PRId64 "] rebooting\n", now); + android_reboot(ANDROID_RB_RESTART, 0, 0); + } else { + LOGV("[%" PRId64 "] ignore power-button press, battery level " + "less than minimum\n", now); + } } } else { /* if the key is pressed but timeout hasn't expired, * make sure we wake up at the right-ish time to check */ set_next_key_check(charger, key, POWER_ON_KEY_TIME); + + /* Turn on the display and kick animation on power-key press + * rather than on key release + */ + kick_animation(charger->batt_anim); + request_suspend(false); } } else { /* if the power key got released, force screen state cycle */ if (key->pending) { - request_suspend(false); kick_animation(charger->batt_anim); } } @@ -555,6 +566,11 @@ static void handle_power_supply_state(struct charger *charger, int64_t now) return; if (!charger->charger_connected) { + + /* Last cycle would have stopped at the extreme top of battery-icon + * Need to show the correct level corresponding to capacity. + */ + kick_animation(charger->batt_anim); request_suspend(false); if (charger->next_pwr_check == -1) { charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME; @@ -705,4 +721,5 @@ void healthd_mode_charger_init(struct healthd_config* config) charger->next_key_check = -1; charger->next_pwr_check = -1; healthd_config = config; + charger->boot_min_cap = config->boot_min_cap; } diff --git a/include/cutils/sched_policy.h b/include/cutils/sched_policy.h index ba84ce32a..6a8d570b2 100644 --- a/include/cutils/sched_policy.h +++ b/include/cutils/sched_policy.h @@ -34,6 +34,8 @@ typedef enum { SP_SYSTEM_DEFAULT = SP_FOREGROUND, } SchedPolicy; +extern int set_cpuset_policy(int tid, SchedPolicy policy); + /* Assign thread tid to the cgroup associated with the specified policy. * If the thread is a thread group leader, that is it's gettid() == getpid(), * then the other threads in the same thread group are _not_ affected. diff --git a/include/netutils/dhcp.h b/include/netutils/dhcp.h index de6bc82f4..008dbd887 100644 --- a/include/netutils/dhcp.h +++ b/include/netutils/dhcp.h @@ -23,26 +23,18 @@ __BEGIN_DECLS extern int do_dhcp(char *iname); -extern int dhcp_do_request(const char *ifname, - char *ipaddr, - char *gateway, - uint32_t *prefixLength, - char *dns[], - char *server, - uint32_t *lease, - char *vendorInfo, - char *domain, - char *mtu); -extern int dhcp_do_request_renew(const char *ifname, - char *ipaddr, - char *gateway, - uint32_t *prefixLength, - char *dns[], - char *server, - uint32_t *lease, - char *vendorInfo, - char *domain, - char *mtu); +extern int dhcp_start(const char *ifname); +extern int dhcp_start_renew(const char *ifname); +extern int dhcp_get_results(const char *ifname, + char *ipaddr, + char *gateway, + uint32_t *prefixLength, + char *dns[], + char *server, + uint32_t *lease, + char *vendorInfo, + char *domain, + char *mtu); extern int dhcp_stop(const char *ifname); extern int dhcp_release_lease(const char *ifname); extern char *dhcp_get_errmsg(); diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h index 3e8d62af3..a5e91c6d5 100644 --- a/include/private/android_filesystem_config.h +++ b/include/private/android_filesystem_config.h @@ -214,13 +214,13 @@ __BEGIN_DECLS * Used in: * build/tools/fs_config/fs_config.c * build/tools/fs_get_stats/fs_get_stats.c - * external/genext2fs/genext2fs.c + * system/extras/ext4_utils/make_ext4fs_main.c * external/squashfs-tools/squashfs-tools/android.c * system/core/cpio/mkbootfs.c * system/core/adb/file_sync_service.cpp * system/extras/ext4_utils/canned_fs_config.c */ -void fs_config(const char *path, int dir, +void fs_config(const char *path, int dir, const char *target_out_path, unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities); ssize_t fs_config_generate(char *buffer, size_t length, const struct fs_path_config *pc); diff --git a/include/system/audio.h b/include/system/audio.h deleted file mode 100644 index ced200565..000000000 --- a/include/system/audio.h +++ /dev/null @@ -1,1375 +0,0 @@ -/* - * Copyright (C) 2011 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_AUDIO_CORE_H -#define ANDROID_AUDIO_CORE_H - -#include <stdbool.h> -#include <stdint.h> -#include <stdio.h> -#include <sys/cdefs.h> -#include <sys/types.h> - -#include <cutils/bitops.h> - -__BEGIN_DECLS - -/* The enums were moved here mostly from - * frameworks/base/include/media/AudioSystem.h - */ - -/* device address used to refer to the standard remote submix */ -#define AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS "0" - -/* AudioFlinger and AudioPolicy services use I/O handles to identify audio sources and sinks */ -typedef int audio_io_handle_t; -#define AUDIO_IO_HANDLE_NONE 0 - -/* Audio stream types */ -typedef enum { - /* These values must kept in sync with - * frameworks/base/media/java/android/media/AudioSystem.java - */ - AUDIO_STREAM_DEFAULT = -1, - AUDIO_STREAM_MIN = 0, - AUDIO_STREAM_VOICE_CALL = 0, - AUDIO_STREAM_SYSTEM = 1, - AUDIO_STREAM_RING = 2, - AUDIO_STREAM_MUSIC = 3, - AUDIO_STREAM_ALARM = 4, - AUDIO_STREAM_NOTIFICATION = 5, - AUDIO_STREAM_BLUETOOTH_SCO = 6, - AUDIO_STREAM_ENFORCED_AUDIBLE = 7, /* Sounds that cannot be muted by user - * and must be routed to speaker - */ - AUDIO_STREAM_DTMF = 8, - AUDIO_STREAM_TTS = 9, /* Transmitted Through Speaker. - * Plays over speaker only, silent on other devices. - */ - AUDIO_STREAM_ACCESSIBILITY = 10, /* For accessibility talk back prompts */ - AUDIO_STREAM_REROUTING = 11, /* For dynamic policy output mixes */ - AUDIO_STREAM_PATCH = 12, /* For internal audio flinger tracks. Fixed volume */ - AUDIO_STREAM_PUBLIC_CNT = AUDIO_STREAM_TTS + 1, - AUDIO_STREAM_CNT = AUDIO_STREAM_PATCH + 1, -} audio_stream_type_t; - -/* Do not change these values without updating their counterparts - * in frameworks/base/media/java/android/media/AudioAttributes.java - */ -typedef enum { - AUDIO_CONTENT_TYPE_UNKNOWN = 0, - AUDIO_CONTENT_TYPE_SPEECH = 1, - AUDIO_CONTENT_TYPE_MUSIC = 2, - AUDIO_CONTENT_TYPE_MOVIE = 3, - AUDIO_CONTENT_TYPE_SONIFICATION = 4, - - AUDIO_CONTENT_TYPE_CNT, - AUDIO_CONTENT_TYPE_MAX = AUDIO_CONTENT_TYPE_CNT - 1, -} audio_content_type_t; - -/* Do not change these values without updating their counterparts - * in frameworks/base/media/java/android/media/AudioAttributes.java - */ -typedef enum { - AUDIO_USAGE_UNKNOWN = 0, - AUDIO_USAGE_MEDIA = 1, - AUDIO_USAGE_VOICE_COMMUNICATION = 2, - AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING = 3, - AUDIO_USAGE_ALARM = 4, - AUDIO_USAGE_NOTIFICATION = 5, - AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE = 6, - AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7, - AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8, - AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9, - AUDIO_USAGE_NOTIFICATION_EVENT = 10, - AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY = 11, - AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12, - AUDIO_USAGE_ASSISTANCE_SONIFICATION = 13, - AUDIO_USAGE_GAME = 14, - AUDIO_USAGE_VIRTUAL_SOURCE = 15, - - AUDIO_USAGE_CNT, - AUDIO_USAGE_MAX = AUDIO_USAGE_CNT - 1, -} audio_usage_t; - -typedef uint32_t audio_flags_mask_t; - -/* Do not change these values without updating their counterparts - * in frameworks/base/media/java/android/media/AudioAttributes.java - */ -enum { - AUDIO_FLAG_AUDIBILITY_ENFORCED = 0x1, - AUDIO_FLAG_SECURE = 0x2, - AUDIO_FLAG_SCO = 0x4, - AUDIO_FLAG_BEACON = 0x8, - AUDIO_FLAG_HW_AV_SYNC = 0x10, - AUDIO_FLAG_HW_HOTWORD = 0x20, -}; - -/* Do not change these values without updating their counterparts - * in frameworks/base/media/java/android/media/MediaRecorder.java, - * frameworks/av/services/audiopolicy/AudioPolicyService.cpp, - * and system/media/audio_effects/include/audio_effects/audio_effects_conf.h! - */ -typedef enum { - AUDIO_SOURCE_DEFAULT = 0, - AUDIO_SOURCE_MIC = 1, - AUDIO_SOURCE_VOICE_UPLINK = 2, - AUDIO_SOURCE_VOICE_DOWNLINK = 3, - AUDIO_SOURCE_VOICE_CALL = 4, - AUDIO_SOURCE_CAMCORDER = 5, - AUDIO_SOURCE_VOICE_RECOGNITION = 6, - AUDIO_SOURCE_VOICE_COMMUNICATION = 7, - AUDIO_SOURCE_REMOTE_SUBMIX = 8, /* Source for the mix to be presented remotely. */ - /* An example of remote presentation is Wifi Display */ - /* where a dongle attached to a TV can be used to */ - /* play the mix captured by this audio source. */ - AUDIO_SOURCE_CNT, - AUDIO_SOURCE_MAX = AUDIO_SOURCE_CNT - 1, - AUDIO_SOURCE_FM_TUNER = 1998, - 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; - -/* Audio attributes */ -#define AUDIO_ATTRIBUTES_TAGS_MAX_SIZE 256 -typedef struct { - audio_content_type_t content_type; - audio_usage_t usage; - audio_source_t source; - audio_flags_mask_t flags; - char tags[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE]; /* UTF8 */ -} audio_attributes_t; - -/* special audio session values - * (XXX: should this be living in the audio effects land?) - */ -typedef enum { - /* session for effects attached to a particular output stream - * (value must be less than 0) - */ - AUDIO_SESSION_OUTPUT_STAGE = -1, - - /* session for effects applied to output mix. These effects can - * be moved by audio policy manager to another output stream - * (value must be 0) - */ - AUDIO_SESSION_OUTPUT_MIX = 0, - - /* application does not specify an explicit session ID to be used, - * and requests a new session ID to be allocated - * TODO use unique values for AUDIO_SESSION_OUTPUT_MIX and AUDIO_SESSION_ALLOCATE, - * after all uses have been updated from 0 to the appropriate symbol, and have been tested. - */ - AUDIO_SESSION_ALLOCATE = 0, -} audio_session_t; - -/* a unique ID allocated by AudioFlinger for use as a audio_io_handle_t or audio_session_t */ -typedef int audio_unique_id_t; - -#define AUDIO_UNIQUE_ID_ALLOCATE AUDIO_SESSION_ALLOCATE - -/* Audio sub formats (see enum audio_format). */ - -/* PCM sub formats */ -typedef enum { - /* All of these are in native byte order */ - AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1, /* DO NOT CHANGE - PCM signed 16 bits */ - AUDIO_FORMAT_PCM_SUB_8_BIT = 0x2, /* DO NOT CHANGE - PCM unsigned 8 bits */ - AUDIO_FORMAT_PCM_SUB_32_BIT = 0x3, /* PCM signed .31 fixed point */ - AUDIO_FORMAT_PCM_SUB_8_24_BIT = 0x4, /* PCM signed 7.24 fixed point */ - AUDIO_FORMAT_PCM_SUB_FLOAT = 0x5, /* PCM single-precision floating point */ - AUDIO_FORMAT_PCM_SUB_24_BIT_PACKED = 0x6, /* PCM signed .23 fixed point packed in 3 bytes */ -} audio_format_pcm_sub_fmt_t; - -/* The audio_format_*_sub_fmt_t declarations are not currently used */ - -/* MP3 sub format field definition : can use 11 LSBs in the same way as MP3 - * frame header to specify bit rate, stereo mode, version... - */ -typedef enum { - AUDIO_FORMAT_MP3_SUB_NONE = 0x0, -} audio_format_mp3_sub_fmt_t; - -/* AMR NB/WB sub format field definition: specify frame block interleaving, - * bandwidth efficient or octet aligned, encoding mode for recording... - */ -typedef enum { - AUDIO_FORMAT_AMR_SUB_NONE = 0x0, -} audio_format_amr_sub_fmt_t; - -/* AAC sub format field definition: specify profile or bitrate for recording... */ -typedef enum { - AUDIO_FORMAT_AAC_SUB_MAIN = 0x1, - AUDIO_FORMAT_AAC_SUB_LC = 0x2, - AUDIO_FORMAT_AAC_SUB_SSR = 0x4, - AUDIO_FORMAT_AAC_SUB_LTP = 0x8, - AUDIO_FORMAT_AAC_SUB_HE_V1 = 0x10, - AUDIO_FORMAT_AAC_SUB_SCALABLE = 0x20, - AUDIO_FORMAT_AAC_SUB_ERLC = 0x40, - AUDIO_FORMAT_AAC_SUB_LD = 0x80, - AUDIO_FORMAT_AAC_SUB_HE_V2 = 0x100, - AUDIO_FORMAT_AAC_SUB_ELD = 0x200, -} audio_format_aac_sub_fmt_t; - -/* VORBIS sub format field definition: specify quality for recording... */ -typedef enum { - AUDIO_FORMAT_VORBIS_SUB_NONE = 0x0, -} audio_format_vorbis_sub_fmt_t; - -/* Audio format consists of a main format field (upper 8 bits) and a sub format - * field (lower 24 bits). - * - * The main format indicates the main codec type. The sub format field - * indicates options and parameters for each format. The sub format is mainly - * used for record to indicate for instance the requested bitrate or profile. - * It can also be used for certain formats to give informations not present in - * the encoded audio stream (e.g. octet alignement for AMR). - */ -typedef enum { - AUDIO_FORMAT_INVALID = 0xFFFFFFFFUL, - AUDIO_FORMAT_DEFAULT = 0, - AUDIO_FORMAT_PCM = 0x00000000UL, /* DO NOT CHANGE */ - AUDIO_FORMAT_MP3 = 0x01000000UL, - AUDIO_FORMAT_AMR_NB = 0x02000000UL, - AUDIO_FORMAT_AMR_WB = 0x03000000UL, - AUDIO_FORMAT_AAC = 0x04000000UL, - AUDIO_FORMAT_HE_AAC_V1 = 0x05000000UL, /* Deprecated, Use AUDIO_FORMAT_AAC_HE_V1*/ - AUDIO_FORMAT_HE_AAC_V2 = 0x06000000UL, /* Deprecated, Use AUDIO_FORMAT_AAC_HE_V2*/ - AUDIO_FORMAT_VORBIS = 0x07000000UL, - AUDIO_FORMAT_OPUS = 0x08000000UL, - AUDIO_FORMAT_AC3 = 0x09000000UL, - AUDIO_FORMAT_E_AC3 = 0x0A000000UL, - AUDIO_FORMAT_MAIN_MASK = 0xFF000000UL, - AUDIO_FORMAT_SUB_MASK = 0x00FFFFFFUL, - - /* Aliases */ - /* note != AudioFormat.ENCODING_PCM_16BIT */ - AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM | - AUDIO_FORMAT_PCM_SUB_16_BIT), - /* note != AudioFormat.ENCODING_PCM_8BIT */ - AUDIO_FORMAT_PCM_8_BIT = (AUDIO_FORMAT_PCM | - AUDIO_FORMAT_PCM_SUB_8_BIT), - AUDIO_FORMAT_PCM_32_BIT = (AUDIO_FORMAT_PCM | - AUDIO_FORMAT_PCM_SUB_32_BIT), - AUDIO_FORMAT_PCM_8_24_BIT = (AUDIO_FORMAT_PCM | - AUDIO_FORMAT_PCM_SUB_8_24_BIT), - AUDIO_FORMAT_PCM_FLOAT = (AUDIO_FORMAT_PCM | - AUDIO_FORMAT_PCM_SUB_FLOAT), - AUDIO_FORMAT_PCM_24_BIT_PACKED = (AUDIO_FORMAT_PCM | - AUDIO_FORMAT_PCM_SUB_24_BIT_PACKED), - AUDIO_FORMAT_AAC_MAIN = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_MAIN), - AUDIO_FORMAT_AAC_LC = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_LC), - AUDIO_FORMAT_AAC_SSR = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_SSR), - AUDIO_FORMAT_AAC_LTP = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_LTP), - AUDIO_FORMAT_AAC_HE_V1 = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_HE_V1), - AUDIO_FORMAT_AAC_SCALABLE = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_SCALABLE), - AUDIO_FORMAT_AAC_ERLC = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_ERLC), - AUDIO_FORMAT_AAC_LD = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_LD), - AUDIO_FORMAT_AAC_HE_V2 = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_HE_V2), - AUDIO_FORMAT_AAC_ELD = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_ELD), -} audio_format_t; - -/* For the channel mask for position assignment representation */ -enum { - -/* These can be a complete audio_channel_mask_t. */ - - AUDIO_CHANNEL_NONE = 0x0, - AUDIO_CHANNEL_INVALID = 0xC0000000, - -/* These can be the bits portion of an audio_channel_mask_t - * with representation AUDIO_CHANNEL_REPRESENTATION_POSITION. - * Using these bits as a complete audio_channel_mask_t is deprecated. - */ - - /* output channels */ - AUDIO_CHANNEL_OUT_FRONT_LEFT = 0x1, - AUDIO_CHANNEL_OUT_FRONT_RIGHT = 0x2, - AUDIO_CHANNEL_OUT_FRONT_CENTER = 0x4, - AUDIO_CHANNEL_OUT_LOW_FREQUENCY = 0x8, - AUDIO_CHANNEL_OUT_BACK_LEFT = 0x10, - AUDIO_CHANNEL_OUT_BACK_RIGHT = 0x20, - AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x40, - AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x80, - AUDIO_CHANNEL_OUT_BACK_CENTER = 0x100, - AUDIO_CHANNEL_OUT_SIDE_LEFT = 0x200, - AUDIO_CHANNEL_OUT_SIDE_RIGHT = 0x400, - AUDIO_CHANNEL_OUT_TOP_CENTER = 0x800, - AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT = 0x1000, - AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER = 0x2000, - AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT = 0x4000, - AUDIO_CHANNEL_OUT_TOP_BACK_LEFT = 0x8000, - AUDIO_CHANNEL_OUT_TOP_BACK_CENTER = 0x10000, - AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT = 0x20000, - -/* TODO: should these be considered complete channel masks, or only bits? */ - - AUDIO_CHANNEL_OUT_MONO = AUDIO_CHANNEL_OUT_FRONT_LEFT, - AUDIO_CHANNEL_OUT_STEREO = (AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT), - AUDIO_CHANNEL_OUT_QUAD = (AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT | - AUDIO_CHANNEL_OUT_BACK_LEFT | - AUDIO_CHANNEL_OUT_BACK_RIGHT), - AUDIO_CHANNEL_OUT_QUAD_BACK = AUDIO_CHANNEL_OUT_QUAD, - /* like AUDIO_CHANNEL_OUT_QUAD_BACK with *_SIDE_* instead of *_BACK_* */ - AUDIO_CHANNEL_OUT_QUAD_SIDE = (AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT | - AUDIO_CHANNEL_OUT_SIDE_LEFT | - AUDIO_CHANNEL_OUT_SIDE_RIGHT), - AUDIO_CHANNEL_OUT_5POINT1 = (AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT | - AUDIO_CHANNEL_OUT_FRONT_CENTER | - AUDIO_CHANNEL_OUT_LOW_FREQUENCY | - AUDIO_CHANNEL_OUT_BACK_LEFT | - AUDIO_CHANNEL_OUT_BACK_RIGHT), - AUDIO_CHANNEL_OUT_5POINT1_BACK = AUDIO_CHANNEL_OUT_5POINT1, - /* like AUDIO_CHANNEL_OUT_5POINT1_BACK with *_SIDE_* instead of *_BACK_* */ - AUDIO_CHANNEL_OUT_5POINT1_SIDE = (AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT | - AUDIO_CHANNEL_OUT_FRONT_CENTER | - AUDIO_CHANNEL_OUT_LOW_FREQUENCY | - AUDIO_CHANNEL_OUT_SIDE_LEFT | - AUDIO_CHANNEL_OUT_SIDE_RIGHT), - // matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND definition for 7.1 - AUDIO_CHANNEL_OUT_7POINT1 = (AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT | - AUDIO_CHANNEL_OUT_FRONT_CENTER | - AUDIO_CHANNEL_OUT_LOW_FREQUENCY | - AUDIO_CHANNEL_OUT_BACK_LEFT | - AUDIO_CHANNEL_OUT_BACK_RIGHT | - AUDIO_CHANNEL_OUT_SIDE_LEFT | - AUDIO_CHANNEL_OUT_SIDE_RIGHT), - AUDIO_CHANNEL_OUT_ALL = (AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT | - AUDIO_CHANNEL_OUT_FRONT_CENTER | - AUDIO_CHANNEL_OUT_LOW_FREQUENCY | - AUDIO_CHANNEL_OUT_BACK_LEFT | - AUDIO_CHANNEL_OUT_BACK_RIGHT | - AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER | - AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER | - AUDIO_CHANNEL_OUT_BACK_CENTER| - AUDIO_CHANNEL_OUT_SIDE_LEFT| - AUDIO_CHANNEL_OUT_SIDE_RIGHT| - AUDIO_CHANNEL_OUT_TOP_CENTER| - AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT| - AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER| - AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT| - AUDIO_CHANNEL_OUT_TOP_BACK_LEFT| - AUDIO_CHANNEL_OUT_TOP_BACK_CENTER| - AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT), - -/* These are bits only, not complete values */ - - /* input channels */ - AUDIO_CHANNEL_IN_LEFT = 0x4, - AUDIO_CHANNEL_IN_RIGHT = 0x8, - AUDIO_CHANNEL_IN_FRONT = 0x10, - AUDIO_CHANNEL_IN_BACK = 0x20, - AUDIO_CHANNEL_IN_LEFT_PROCESSED = 0x40, - AUDIO_CHANNEL_IN_RIGHT_PROCESSED = 0x80, - AUDIO_CHANNEL_IN_FRONT_PROCESSED = 0x100, - AUDIO_CHANNEL_IN_BACK_PROCESSED = 0x200, - AUDIO_CHANNEL_IN_PRESSURE = 0x400, - AUDIO_CHANNEL_IN_X_AXIS = 0x800, - AUDIO_CHANNEL_IN_Y_AXIS = 0x1000, - AUDIO_CHANNEL_IN_Z_AXIS = 0x2000, - AUDIO_CHANNEL_IN_VOICE_UPLINK = 0x4000, - AUDIO_CHANNEL_IN_VOICE_DNLINK = 0x8000, - -/* TODO: should these be considered complete channel masks, or only bits, or deprecated? */ - - AUDIO_CHANNEL_IN_MONO = AUDIO_CHANNEL_IN_FRONT, - AUDIO_CHANNEL_IN_STEREO = (AUDIO_CHANNEL_IN_LEFT | AUDIO_CHANNEL_IN_RIGHT), - AUDIO_CHANNEL_IN_FRONT_BACK = (AUDIO_CHANNEL_IN_FRONT | AUDIO_CHANNEL_IN_BACK), - AUDIO_CHANNEL_IN_ALL = (AUDIO_CHANNEL_IN_LEFT | - AUDIO_CHANNEL_IN_RIGHT | - AUDIO_CHANNEL_IN_FRONT | - AUDIO_CHANNEL_IN_BACK| - AUDIO_CHANNEL_IN_LEFT_PROCESSED | - AUDIO_CHANNEL_IN_RIGHT_PROCESSED | - AUDIO_CHANNEL_IN_FRONT_PROCESSED | - AUDIO_CHANNEL_IN_BACK_PROCESSED| - AUDIO_CHANNEL_IN_PRESSURE | - AUDIO_CHANNEL_IN_X_AXIS | - AUDIO_CHANNEL_IN_Y_AXIS | - AUDIO_CHANNEL_IN_Z_AXIS | - AUDIO_CHANNEL_IN_VOICE_UPLINK | - AUDIO_CHANNEL_IN_VOICE_DNLINK), -}; - -/* A channel mask per se only defines the presence or absence of a channel, not the order. - * But see AUDIO_INTERLEAVE_* below for the platform convention of order. - * - * audio_channel_mask_t is an opaque type and its internal layout should not - * be assumed as it may change in the future. - * Instead, always use the functions declared in this header to examine. - * - * These are the current representations: - * - * AUDIO_CHANNEL_REPRESENTATION_POSITION - * is a channel mask representation for position assignment. - * Each low-order bit corresponds to the spatial position of a transducer (output), - * or interpretation of channel (input). - * The user of a channel mask needs to know the context of whether it is for output or input. - * The constants AUDIO_CHANNEL_OUT_* or AUDIO_CHANNEL_IN_* apply to the bits portion. - * It is not permitted for no bits to be set. - * - * AUDIO_CHANNEL_REPRESENTATION_INDEX - * is a channel mask representation for index assignment. - * Each low-order bit corresponds to a selected channel. - * There is no platform interpretation of the various bits. - * There is no concept of output or input. - * It is not permitted for no bits to be set. - * - * All other representations are reserved for future use. - * - * Warning: current representation distinguishes between input and output, but this will not the be - * case in future revisions of the platform. Wherever there is an ambiguity between input and output - * that is currently resolved by checking the channel mask, the implementer should look for ways to - * fix it with additional information outside of the mask. - */ -typedef uint32_t audio_channel_mask_t; - -/* Maximum number of channels for all representations */ -#define AUDIO_CHANNEL_COUNT_MAX 30 - -/* log(2) of maximum number of representations, not part of public API */ -#define AUDIO_CHANNEL_REPRESENTATION_LOG2 2 - -/* Representations */ -typedef enum { - AUDIO_CHANNEL_REPRESENTATION_POSITION = 0, // must be zero for compatibility - // 1 is reserved for future use - AUDIO_CHANNEL_REPRESENTATION_INDEX = 2, - // 3 is reserved for future use -} audio_channel_representation_t; - -/* The return value is undefined if the channel mask is invalid. */ -static inline uint32_t audio_channel_mask_get_bits(audio_channel_mask_t channel) -{ - return channel & ((1 << AUDIO_CHANNEL_COUNT_MAX) - 1); -} - -/* The return value is undefined if the channel mask is invalid. */ -static inline audio_channel_representation_t audio_channel_mask_get_representation( - audio_channel_mask_t channel) -{ - // The right shift should be sufficient, but also "and" for safety in case mask is not 32 bits - return (audio_channel_representation_t) - ((channel >> AUDIO_CHANNEL_COUNT_MAX) & ((1 << AUDIO_CHANNEL_REPRESENTATION_LOG2) - 1)); -} - -/* Returns true if the channel mask is valid, - * or returns false for AUDIO_CHANNEL_NONE, AUDIO_CHANNEL_INVALID, and other invalid values. - * This function is unable to determine whether a channel mask for position assignment - * is invalid because an output mask has an invalid output bit set, - * or because an input mask has an invalid input bit set. - * All other APIs that take a channel mask assume that it is valid. - */ -static inline bool audio_channel_mask_is_valid(audio_channel_mask_t channel) -{ - uint32_t bits = audio_channel_mask_get_bits(channel); - audio_channel_representation_t representation = audio_channel_mask_get_representation(channel); - switch (representation) { - case AUDIO_CHANNEL_REPRESENTATION_POSITION: - case AUDIO_CHANNEL_REPRESENTATION_INDEX: - break; - default: - bits = 0; - break; - } - return bits != 0; -} - -/* Not part of public API */ -static inline audio_channel_mask_t audio_channel_mask_from_representation_and_bits( - audio_channel_representation_t representation, uint32_t bits) -{ - return (audio_channel_mask_t) ((representation << AUDIO_CHANNEL_COUNT_MAX) | bits); -} - -/* Expresses the convention when stereo audio samples are stored interleaved - * in an array. This should improve readability by allowing code to use - * symbolic indices instead of hard-coded [0] and [1]. - * - * For multi-channel beyond stereo, the platform convention is that channels - * are interleaved in order from least significant channel mask bit - * to most significant channel mask bit, with unused bits skipped. - * Any exceptions to this convention will be noted at the appropriate API. - */ -enum { - AUDIO_INTERLEAVE_LEFT = 0, - AUDIO_INTERLEAVE_RIGHT = 1, -}; - -typedef enum { - AUDIO_MODE_INVALID = -2, - AUDIO_MODE_CURRENT = -1, - AUDIO_MODE_NORMAL = 0, - AUDIO_MODE_RINGTONE = 1, - AUDIO_MODE_IN_CALL = 2, - AUDIO_MODE_IN_COMMUNICATION = 3, - - AUDIO_MODE_CNT, - AUDIO_MODE_MAX = AUDIO_MODE_CNT - 1, -} audio_mode_t; - -/* This enum is deprecated */ -typedef enum { - AUDIO_IN_ACOUSTICS_NONE = 0, - AUDIO_IN_ACOUSTICS_AGC_ENABLE = 0x0001, - AUDIO_IN_ACOUSTICS_AGC_DISABLE = 0, - AUDIO_IN_ACOUSTICS_NS_ENABLE = 0x0002, - AUDIO_IN_ACOUSTICS_NS_DISABLE = 0, - AUDIO_IN_ACOUSTICS_TX_IIR_ENABLE = 0x0004, - AUDIO_IN_ACOUSTICS_TX_DISABLE = 0, -} audio_in_acoustics_t; - -enum { - AUDIO_DEVICE_NONE = 0x0, - /* reserved bits */ - AUDIO_DEVICE_BIT_IN = 0x80000000, - AUDIO_DEVICE_BIT_DEFAULT = 0x40000000, - /* output devices */ - AUDIO_DEVICE_OUT_EARPIECE = 0x1, - AUDIO_DEVICE_OUT_SPEAKER = 0x2, - AUDIO_DEVICE_OUT_WIRED_HEADSET = 0x4, - AUDIO_DEVICE_OUT_WIRED_HEADPHONE = 0x8, - AUDIO_DEVICE_OUT_BLUETOOTH_SCO = 0x10, - AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20, - AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40, - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP = 0x80, - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100, - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200, - AUDIO_DEVICE_OUT_AUX_DIGITAL = 0x400, - AUDIO_DEVICE_OUT_HDMI = AUDIO_DEVICE_OUT_AUX_DIGITAL, - /* uses an analog connection (multiplexed over the USB connector pins for instance) */ - AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800, - AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000, - /* USB accessory mode: your Android device is a USB device and the dock is a USB host */ - AUDIO_DEVICE_OUT_USB_ACCESSORY = 0x2000, - /* USB host mode: your Android device is a USB host and the dock is a USB device */ - AUDIO_DEVICE_OUT_USB_DEVICE = 0x4000, - AUDIO_DEVICE_OUT_REMOTE_SUBMIX = 0x8000, - /* Telephony voice TX path */ - AUDIO_DEVICE_OUT_TELEPHONY_TX = 0x10000, - /* Analog jack with line impedance detected */ - AUDIO_DEVICE_OUT_LINE = 0x20000, - /* HDMI Audio Return Channel */ - AUDIO_DEVICE_OUT_HDMI_ARC = 0x40000, - /* S/PDIF out */ - AUDIO_DEVICE_OUT_SPDIF = 0x80000, - /* FM transmitter out */ - AUDIO_DEVICE_OUT_FM = 0x100000, - /* Line out for av devices */ - AUDIO_DEVICE_OUT_AUX_LINE = 0x200000, - /* limited-output speaker device for acoustic safety */ - AUDIO_DEVICE_OUT_SPEAKER_SAFE = 0x400000, - AUDIO_DEVICE_OUT_DEFAULT = AUDIO_DEVICE_BIT_DEFAULT, - AUDIO_DEVICE_OUT_ALL = (AUDIO_DEVICE_OUT_EARPIECE | - AUDIO_DEVICE_OUT_SPEAKER | - AUDIO_DEVICE_OUT_WIRED_HEADSET | - AUDIO_DEVICE_OUT_WIRED_HEADPHONE | - AUDIO_DEVICE_OUT_BLUETOOTH_SCO | - AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET | - AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT | - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | - AUDIO_DEVICE_OUT_HDMI | - AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET | - AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET | - AUDIO_DEVICE_OUT_USB_ACCESSORY | - AUDIO_DEVICE_OUT_USB_DEVICE | - AUDIO_DEVICE_OUT_REMOTE_SUBMIX | - AUDIO_DEVICE_OUT_TELEPHONY_TX | - AUDIO_DEVICE_OUT_LINE | - AUDIO_DEVICE_OUT_HDMI_ARC | - AUDIO_DEVICE_OUT_SPDIF | - AUDIO_DEVICE_OUT_FM | - AUDIO_DEVICE_OUT_AUX_LINE | - AUDIO_DEVICE_OUT_SPEAKER_SAFE | - AUDIO_DEVICE_OUT_DEFAULT), - AUDIO_DEVICE_OUT_ALL_A2DP = (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER), - AUDIO_DEVICE_OUT_ALL_SCO = (AUDIO_DEVICE_OUT_BLUETOOTH_SCO | - AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET | - AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT), - AUDIO_DEVICE_OUT_ALL_USB = (AUDIO_DEVICE_OUT_USB_ACCESSORY | - AUDIO_DEVICE_OUT_USB_DEVICE), - - /* input devices */ - AUDIO_DEVICE_IN_COMMUNICATION = AUDIO_DEVICE_BIT_IN | 0x1, - AUDIO_DEVICE_IN_AMBIENT = AUDIO_DEVICE_BIT_IN | 0x2, - AUDIO_DEVICE_IN_BUILTIN_MIC = AUDIO_DEVICE_BIT_IN | 0x4, - AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET = AUDIO_DEVICE_BIT_IN | 0x8, - AUDIO_DEVICE_IN_WIRED_HEADSET = AUDIO_DEVICE_BIT_IN | 0x10, - AUDIO_DEVICE_IN_AUX_DIGITAL = AUDIO_DEVICE_BIT_IN | 0x20, - AUDIO_DEVICE_IN_HDMI = AUDIO_DEVICE_IN_AUX_DIGITAL, - /* Telephony voice RX path */ - AUDIO_DEVICE_IN_VOICE_CALL = AUDIO_DEVICE_BIT_IN | 0x40, - AUDIO_DEVICE_IN_TELEPHONY_RX = AUDIO_DEVICE_IN_VOICE_CALL, - AUDIO_DEVICE_IN_BACK_MIC = AUDIO_DEVICE_BIT_IN | 0x80, - AUDIO_DEVICE_IN_REMOTE_SUBMIX = AUDIO_DEVICE_BIT_IN | 0x100, - AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET = AUDIO_DEVICE_BIT_IN | 0x200, - AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET = AUDIO_DEVICE_BIT_IN | 0x400, - AUDIO_DEVICE_IN_USB_ACCESSORY = AUDIO_DEVICE_BIT_IN | 0x800, - AUDIO_DEVICE_IN_USB_DEVICE = AUDIO_DEVICE_BIT_IN | 0x1000, - /* FM tuner input */ - AUDIO_DEVICE_IN_FM_TUNER = AUDIO_DEVICE_BIT_IN | 0x2000, - /* TV tuner input */ - AUDIO_DEVICE_IN_TV_TUNER = AUDIO_DEVICE_BIT_IN | 0x4000, - /* Analog jack with line impedance detected */ - AUDIO_DEVICE_IN_LINE = AUDIO_DEVICE_BIT_IN | 0x8000, - /* S/PDIF in */ - AUDIO_DEVICE_IN_SPDIF = AUDIO_DEVICE_BIT_IN | 0x10000, - AUDIO_DEVICE_IN_BLUETOOTH_A2DP = AUDIO_DEVICE_BIT_IN | 0x20000, - AUDIO_DEVICE_IN_LOOPBACK = AUDIO_DEVICE_BIT_IN | 0x40000, - AUDIO_DEVICE_IN_DEFAULT = AUDIO_DEVICE_BIT_IN | AUDIO_DEVICE_BIT_DEFAULT, - - AUDIO_DEVICE_IN_ALL = (AUDIO_DEVICE_IN_COMMUNICATION | - AUDIO_DEVICE_IN_AMBIENT | - AUDIO_DEVICE_IN_BUILTIN_MIC | - AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET | - AUDIO_DEVICE_IN_WIRED_HEADSET | - AUDIO_DEVICE_IN_HDMI | - AUDIO_DEVICE_IN_TELEPHONY_RX | - AUDIO_DEVICE_IN_BACK_MIC | - AUDIO_DEVICE_IN_REMOTE_SUBMIX | - AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET | - AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET | - AUDIO_DEVICE_IN_USB_ACCESSORY | - AUDIO_DEVICE_IN_USB_DEVICE | - AUDIO_DEVICE_IN_FM_TUNER | - AUDIO_DEVICE_IN_TV_TUNER | - AUDIO_DEVICE_IN_LINE | - AUDIO_DEVICE_IN_SPDIF | - AUDIO_DEVICE_IN_BLUETOOTH_A2DP | - AUDIO_DEVICE_IN_LOOPBACK | - AUDIO_DEVICE_IN_DEFAULT), - AUDIO_DEVICE_IN_ALL_SCO = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, - AUDIO_DEVICE_IN_ALL_USB = (AUDIO_DEVICE_IN_USB_ACCESSORY | - AUDIO_DEVICE_IN_USB_DEVICE), -}; - -typedef uint32_t audio_devices_t; - -/* the audio output flags serve two purposes: - * - when an AudioTrack is created they indicate a "wish" to be connected to an - * output stream with attributes corresponding to the specified flags - * - when present in an output profile descriptor listed for a particular audio - * hardware module, they indicate that an output stream can be opened that - * supports the attributes indicated by the flags. - * the audio policy manager will try to match the flags in the request - * (when getOuput() is called) to an available output stream. - */ -typedef enum { - AUDIO_OUTPUT_FLAG_NONE = 0x0, // no attributes - AUDIO_OUTPUT_FLAG_DIRECT = 0x1, // this output directly connects a track - // to one output stream: no software mixer - AUDIO_OUTPUT_FLAG_PRIMARY = 0x2, // this output is the primary output of - // the device. It is unique and must be - // present. It is opened by default and - // receives routing, audio mode and volume - // controls related to voice calls. - AUDIO_OUTPUT_FLAG_FAST = 0x4, // output supports "fast tracks", - // defined elsewhere - AUDIO_OUTPUT_FLAG_DEEP_BUFFER = 0x8, // use deep audio buffers - AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD = 0x10, // offload playback of compressed - // streams to hardware codec - AUDIO_OUTPUT_FLAG_NON_BLOCKING = 0x20, // use non-blocking write - AUDIO_OUTPUT_FLAG_HW_AV_SYNC = 0x40 // output uses a hardware A/V synchronization source -} audio_output_flags_t; - -/* The audio input flags are analogous to audio output flags. - * Currently they are used only when an AudioRecord is created, - * to indicate a preference to be connected to an input stream with - * attributes corresponding to the specified flags. - */ -typedef enum { - AUDIO_INPUT_FLAG_NONE = 0x0, // no attributes - AUDIO_INPUT_FLAG_FAST = 0x1, // prefer an input that supports "fast tracks" - AUDIO_INPUT_FLAG_HW_HOTWORD = 0x2, // prefer an input that captures from hw hotword source -} audio_input_flags_t; - -/* Additional information about compressed streams offloaded to - * hardware playback - * The version and size fields must be initialized by the caller by using - * one of the constants defined here. - */ -typedef struct { - uint16_t version; // version of the info structure - uint16_t size; // total size of the structure including version and size - uint32_t sample_rate; // sample rate in Hz - audio_channel_mask_t channel_mask; // channel mask - audio_format_t format; // audio format - audio_stream_type_t stream_type; // stream type - uint32_t bit_rate; // bit rate in bits per second - int64_t duration_us; // duration in microseconds, -1 if unknown - bool has_video; // true if stream is tied to a video stream - bool is_streaming; // true if streaming, false if local playback -} audio_offload_info_t; - -#define AUDIO_MAKE_OFFLOAD_INFO_VERSION(maj,min) \ - ((((maj) & 0xff) << 8) | ((min) & 0xff)) - -#define AUDIO_OFFLOAD_INFO_VERSION_0_1 AUDIO_MAKE_OFFLOAD_INFO_VERSION(0, 1) -#define AUDIO_OFFLOAD_INFO_VERSION_CURRENT AUDIO_OFFLOAD_INFO_VERSION_0_1 - -static const audio_offload_info_t AUDIO_INFO_INITIALIZER = { - version: AUDIO_OFFLOAD_INFO_VERSION_CURRENT, - size: sizeof(audio_offload_info_t), - sample_rate: 0, - channel_mask: 0, - format: AUDIO_FORMAT_DEFAULT, - stream_type: AUDIO_STREAM_VOICE_CALL, - bit_rate: 0, - duration_us: 0, - has_video: false, - is_streaming: false -}; - -/* common audio stream configuration parameters - * You should memset() the entire structure to zero before use to - * ensure forward compatibility - */ -struct audio_config { - uint32_t sample_rate; - audio_channel_mask_t channel_mask; - audio_format_t format; - audio_offload_info_t offload_info; - size_t frame_count; -}; -typedef struct audio_config audio_config_t; - -static const audio_config_t AUDIO_CONFIG_INITIALIZER = { - sample_rate: 0, - channel_mask: AUDIO_CHANNEL_NONE, - format: AUDIO_FORMAT_DEFAULT, - offload_info: { - version: AUDIO_OFFLOAD_INFO_VERSION_CURRENT, - size: sizeof(audio_offload_info_t), - sample_rate: 0, - channel_mask: 0, - format: AUDIO_FORMAT_DEFAULT, - stream_type: AUDIO_STREAM_VOICE_CALL, - bit_rate: 0, - duration_us: 0, - has_video: false, - is_streaming: false - }, - frame_count: 0, -}; - - -/* audio hw module handle functions or structures referencing a module */ -typedef int audio_module_handle_t; - -/****************************** - * Volume control - *****************************/ - -/* If the audio hardware supports gain control on some audio paths, - * the platform can expose them in the audio_policy.conf file. The audio HAL - * will then implement gain control functions that will use the following data - * structures. */ - -/* Type of gain control exposed by an audio port */ -#define AUDIO_GAIN_MODE_JOINT 0x1 /* supports joint channel gain control */ -#define AUDIO_GAIN_MODE_CHANNELS 0x2 /* supports separate channel gain control */ -#define AUDIO_GAIN_MODE_RAMP 0x4 /* supports gain ramps */ - -typedef uint32_t audio_gain_mode_t; - - -/* An audio_gain struct is a representation of a gain stage. - * A gain stage is always attached to an audio port. */ -struct audio_gain { - audio_gain_mode_t mode; /* e.g. AUDIO_GAIN_MODE_JOINT */ - audio_channel_mask_t channel_mask; /* channels which gain an be controlled. - N/A if AUDIO_GAIN_MODE_CHANNELS is not supported */ - int min_value; /* minimum gain value in millibels */ - int max_value; /* maximum gain value in millibels */ - int default_value; /* default gain value in millibels */ - unsigned int step_value; /* gain step in millibels */ - unsigned int min_ramp_ms; /* minimum ramp duration in ms */ - unsigned int max_ramp_ms; /* maximum ramp duration in ms */ -}; - -/* The gain configuration structure is used to get or set the gain values of a - * given port */ -struct audio_gain_config { - int index; /* index of the corresponding audio_gain in the - audio_port gains[] table */ - audio_gain_mode_t mode; /* mode requested for this command */ - audio_channel_mask_t channel_mask; /* channels which gain value follows. - N/A in joint mode */ - int values[sizeof(audio_channel_mask_t) * 8]; /* gain values in millibels - for each channel ordered from LSb to MSb in - channel mask. The number of values is 1 in joint - mode or popcount(channel_mask) */ - unsigned int ramp_duration_ms; /* ramp duration in ms */ -}; - -/****************************** - * Routing control - *****************************/ - -/* Types defined here are used to describe an audio source or sink at internal - * framework interfaces (audio policy, patch panel) or at the audio HAL. - * Sink and sources are grouped in a concept of “audio port” representing an - * audio end point at the edge of the system managed by the module exposing - * the interface. */ - -/* Audio port role: either source or sink */ -typedef enum { - AUDIO_PORT_ROLE_NONE, - AUDIO_PORT_ROLE_SOURCE, - AUDIO_PORT_ROLE_SINK, -} audio_port_role_t; - -/* Audio port type indicates if it is a session (e.g AudioTrack), - * a mix (e.g PlaybackThread output) or a physical device - * (e.g AUDIO_DEVICE_OUT_SPEAKER) */ -typedef enum { - AUDIO_PORT_TYPE_NONE, - AUDIO_PORT_TYPE_DEVICE, - AUDIO_PORT_TYPE_MIX, - AUDIO_PORT_TYPE_SESSION, -} audio_port_type_t; - -/* Each port has a unique ID or handle allocated by policy manager */ -typedef int audio_port_handle_t; -#define AUDIO_PORT_HANDLE_NONE 0 - - -/* maximum audio device address length */ -#define AUDIO_DEVICE_MAX_ADDRESS_LEN 32 - -/* extension for audio port configuration structure when the audio port is a - * hardware device */ -struct audio_port_config_device_ext { - audio_module_handle_t hw_module; /* module the device is attached to */ - audio_devices_t type; /* device type (e.g AUDIO_DEVICE_OUT_SPEAKER) */ - char address[AUDIO_DEVICE_MAX_ADDRESS_LEN]; /* device address. "" if N/A */ -}; - -/* extension for audio port configuration structure when the audio port is a - * sub mix */ -struct audio_port_config_mix_ext { - audio_module_handle_t hw_module; /* module the stream is attached to */ - audio_io_handle_t handle; /* I/O handle of the input/output stream */ - union { - //TODO: change use case for output streams: use strategy and mixer attributes - audio_stream_type_t stream; - audio_source_t source; - } usecase; -}; - -/* extension for audio port configuration structure when the audio port is an - * audio session */ -struct audio_port_config_session_ext { - audio_session_t session; /* audio session */ -}; - -/* Flags indicating which fields are to be considered in struct audio_port_config */ -#define AUDIO_PORT_CONFIG_SAMPLE_RATE 0x1 -#define AUDIO_PORT_CONFIG_CHANNEL_MASK 0x2 -#define AUDIO_PORT_CONFIG_FORMAT 0x4 -#define AUDIO_PORT_CONFIG_GAIN 0x8 -#define AUDIO_PORT_CONFIG_ALL (AUDIO_PORT_CONFIG_SAMPLE_RATE | \ - AUDIO_PORT_CONFIG_CHANNEL_MASK | \ - AUDIO_PORT_CONFIG_FORMAT | \ - AUDIO_PORT_CONFIG_GAIN) - -/* audio port configuration structure used to specify a particular configuration of - * an audio port */ -struct audio_port_config { - audio_port_handle_t id; /* port unique ID */ - audio_port_role_t role; /* sink or source */ - audio_port_type_t type; /* device, mix ... */ - unsigned int config_mask; /* e.g AUDIO_PORT_CONFIG_ALL */ - unsigned int sample_rate; /* sampling rate in Hz */ - audio_channel_mask_t channel_mask; /* channel mask if applicable */ - audio_format_t format; /* format if applicable */ - struct audio_gain_config gain; /* gain to apply if applicable */ - union { - struct audio_port_config_device_ext device; /* device specific info */ - struct audio_port_config_mix_ext mix; /* mix specific info */ - struct audio_port_config_session_ext session; /* session specific info */ - } ext; -}; - - -/* max number of sampling rates in audio port */ -#define AUDIO_PORT_MAX_SAMPLING_RATES 16 -/* max number of channel masks in audio port */ -#define AUDIO_PORT_MAX_CHANNEL_MASKS 16 -/* max number of audio formats in audio port */ -#define AUDIO_PORT_MAX_FORMATS 16 -/* max number of gain controls in audio port */ -#define AUDIO_PORT_MAX_GAINS 16 - -/* extension for audio port structure when the audio port is a hardware device */ -struct audio_port_device_ext { - audio_module_handle_t hw_module; /* module the device is attached to */ - audio_devices_t type; /* device type (e.g AUDIO_DEVICE_OUT_SPEAKER) */ - char address[AUDIO_DEVICE_MAX_ADDRESS_LEN]; -}; - -/* Latency class of the audio mix */ -typedef enum { - AUDIO_LATENCY_LOW, - AUDIO_LATENCY_NORMAL, -} audio_mix_latency_class_t; - -/* extension for audio port structure when the audio port is a sub mix */ -struct audio_port_mix_ext { - audio_module_handle_t hw_module; /* module the stream is attached to */ - audio_io_handle_t handle; /* I/O handle of the input.output stream */ - audio_mix_latency_class_t latency_class; /* latency class */ - // other attributes: routing strategies -}; - -/* extension for audio port structure when the audio port is an audio session */ -struct audio_port_session_ext { - audio_session_t session; /* audio session */ -}; - - -struct audio_port { - audio_port_handle_t id; /* port unique ID */ - audio_port_role_t role; /* sink or source */ - audio_port_type_t type; /* device, mix ... */ - unsigned int num_sample_rates; /* number of sampling rates in following array */ - unsigned int sample_rates[AUDIO_PORT_MAX_SAMPLING_RATES]; - unsigned int num_channel_masks; /* number of channel masks in following array */ - audio_channel_mask_t channel_masks[AUDIO_PORT_MAX_CHANNEL_MASKS]; - unsigned int num_formats; /* number of formats in following array */ - audio_format_t formats[AUDIO_PORT_MAX_FORMATS]; - unsigned int num_gains; /* number of gains in following array */ - struct audio_gain gains[AUDIO_PORT_MAX_GAINS]; - struct audio_port_config active_config; /* current audio port configuration */ - union { - struct audio_port_device_ext device; - struct audio_port_mix_ext mix; - struct audio_port_session_ext session; - } ext; -}; - -/* An audio patch represents a connection between one or more source ports and - * one or more sink ports. Patches are connected and disconnected by audio policy manager or by - * applications via framework APIs. - * Each patch is identified by a handle at the interface used to create that patch. For instance, - * when a patch is created by the audio HAL, the HAL allocates and returns a handle. - * This handle is unique to a given audio HAL hardware module. - * But the same patch receives another system wide unique handle allocated by the framework. - * This unique handle is used for all transactions inside the framework. - */ -typedef int audio_patch_handle_t; -#define AUDIO_PATCH_HANDLE_NONE 0 - -#define AUDIO_PATCH_PORTS_MAX 16 - -struct audio_patch { - audio_patch_handle_t id; /* patch unique ID */ - unsigned int num_sources; /* number of sources in following array */ - struct audio_port_config sources[AUDIO_PATCH_PORTS_MAX]; - unsigned int num_sinks; /* number of sinks in following array */ - struct audio_port_config sinks[AUDIO_PATCH_PORTS_MAX]; -}; - - - -/* a HW synchronization source returned by the audio HAL */ -typedef uint32_t audio_hw_sync_t; - -/* an invalid HW synchronization source indicating an error */ -#define AUDIO_HW_SYNC_INVALID 0 - -static inline bool audio_is_output_device(audio_devices_t device) -{ - if (((device & AUDIO_DEVICE_BIT_IN) == 0) && - (popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL) == 0)) - return true; - else - return false; -} - -static inline bool audio_is_input_device(audio_devices_t device) -{ - if ((device & AUDIO_DEVICE_BIT_IN) != 0) { - device &= ~AUDIO_DEVICE_BIT_IN; - if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_ALL) == 0)) - return true; - } - return false; -} - -static inline bool audio_is_output_devices(audio_devices_t device) -{ - return (device & AUDIO_DEVICE_BIT_IN) == 0; -} - -static inline bool audio_is_a2dp_in_device(audio_devices_t device) -{ - if ((device & AUDIO_DEVICE_BIT_IN) != 0) { - device &= ~AUDIO_DEVICE_BIT_IN; - if ((popcount(device) == 1) && (device & AUDIO_DEVICE_IN_BLUETOOTH_A2DP)) - return true; - } - return false; -} - -static inline bool audio_is_a2dp_out_device(audio_devices_t device) -{ - if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_A2DP)) - return true; - else - return false; -} - -// Deprecated - use audio_is_a2dp_out_device() instead -static inline bool audio_is_a2dp_device(audio_devices_t device) -{ - return audio_is_a2dp_out_device(device); -} - -static inline bool audio_is_bluetooth_sco_device(audio_devices_t device) -{ - if ((device & AUDIO_DEVICE_BIT_IN) == 0) { - if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL_SCO) == 0)) - return true; - } else { - device &= ~AUDIO_DEVICE_BIT_IN; - if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) == 0)) - return true; - } - - return false; -} - -static inline bool audio_is_usb_out_device(audio_devices_t device) -{ - return ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_USB)); -} - -static inline bool audio_is_usb_in_device(audio_devices_t device) -{ - if ((device & AUDIO_DEVICE_BIT_IN) != 0) { - device &= ~AUDIO_DEVICE_BIT_IN; - if (popcount(device) == 1 && (device & AUDIO_DEVICE_IN_ALL_USB) != 0) - return true; - } - return false; -} - -/* OBSOLETE - use audio_is_usb_out_device() instead. */ -static inline bool audio_is_usb_device(audio_devices_t device) -{ - return audio_is_usb_out_device(device); -} - -static inline bool audio_is_remote_submix_device(audio_devices_t device) -{ - if ((audio_is_output_devices(device) && - (device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX) == AUDIO_DEVICE_OUT_REMOTE_SUBMIX) - || (!audio_is_output_devices(device) && - (device & AUDIO_DEVICE_IN_REMOTE_SUBMIX) == AUDIO_DEVICE_IN_REMOTE_SUBMIX)) - return true; - else - return false; -} - -/* Returns true if: - * representation is valid, and - * there is at least one channel bit set which _could_ correspond to an input channel, and - * there are no channel bits set which could _not_ correspond to an input channel. - * Otherwise returns false. - */ -static inline bool audio_is_input_channel(audio_channel_mask_t channel) -{ - uint32_t bits = audio_channel_mask_get_bits(channel); - switch (audio_channel_mask_get_representation(channel)) { - case AUDIO_CHANNEL_REPRESENTATION_POSITION: - if (bits & ~AUDIO_CHANNEL_IN_ALL) { - bits = 0; - } - // fall through - case AUDIO_CHANNEL_REPRESENTATION_INDEX: - return bits != 0; - default: - return false; - } -} - -/* Returns true if: - * representation is valid, and - * there is at least one channel bit set which _could_ correspond to an output channel, and - * there are no channel bits set which could _not_ correspond to an output channel. - * Otherwise returns false. - */ -static inline bool audio_is_output_channel(audio_channel_mask_t channel) -{ - uint32_t bits = audio_channel_mask_get_bits(channel); - switch (audio_channel_mask_get_representation(channel)) { - case AUDIO_CHANNEL_REPRESENTATION_POSITION: - if (bits & ~AUDIO_CHANNEL_OUT_ALL) { - bits = 0; - } - // fall through - case AUDIO_CHANNEL_REPRESENTATION_INDEX: - return bits != 0; - default: - return false; - } -} - -/* Returns the number of channels from an input channel mask, - * used in the context of audio input or recording. - * If a channel bit is set which could _not_ correspond to an input channel, - * it is excluded from the count. - * Returns zero if the representation is invalid. - */ -static inline uint32_t audio_channel_count_from_in_mask(audio_channel_mask_t channel) -{ - uint32_t bits = audio_channel_mask_get_bits(channel); - switch (audio_channel_mask_get_representation(channel)) { - case AUDIO_CHANNEL_REPRESENTATION_POSITION: - // TODO: We can now merge with from_out_mask and remove anding - bits &= AUDIO_CHANNEL_IN_ALL; - // fall through - case AUDIO_CHANNEL_REPRESENTATION_INDEX: - return popcount(bits); - default: - return 0; - } -} - -/* Returns the number of channels from an output channel mask, - * used in the context of audio output or playback. - * If a channel bit is set which could _not_ correspond to an output channel, - * it is excluded from the count. - * Returns zero if the representation is invalid. - */ -static inline uint32_t audio_channel_count_from_out_mask(audio_channel_mask_t channel) -{ - uint32_t bits = audio_channel_mask_get_bits(channel); - switch (audio_channel_mask_get_representation(channel)) { - case AUDIO_CHANNEL_REPRESENTATION_POSITION: - // TODO: We can now merge with from_in_mask and remove anding - bits &= AUDIO_CHANNEL_OUT_ALL; - // fall through - case AUDIO_CHANNEL_REPRESENTATION_INDEX: - return popcount(bits); - default: - return 0; - } -} - -/* Derive an output channel mask for position assignment from a channel count. - * This is to be used when the content channel mask is unknown. The 1, 2, 4, 5, 6, 7 and 8 channel - * cases are mapped to the standard game/home-theater layouts, but note that 4 is mapped to quad, - * and not stereo + FC + mono surround. A channel count of 3 is arbitrarily mapped to stereo + FC - * for continuity with stereo. - * Returns the matching channel mask, - * or AUDIO_CHANNEL_NONE if the channel count is zero, - * or AUDIO_CHANNEL_INVALID if the channel count exceeds that of the - * configurations for which a default output channel mask is defined. - */ -static inline audio_channel_mask_t audio_channel_out_mask_from_count(uint32_t channel_count) -{ - uint32_t bits; - switch (channel_count) { - case 0: - return AUDIO_CHANNEL_NONE; - case 1: - bits = AUDIO_CHANNEL_OUT_MONO; - break; - case 2: - bits = AUDIO_CHANNEL_OUT_STEREO; - break; - case 3: - bits = AUDIO_CHANNEL_OUT_STEREO | AUDIO_CHANNEL_OUT_FRONT_CENTER; - break; - case 4: // 4.0 - bits = AUDIO_CHANNEL_OUT_QUAD; - break; - case 5: // 5.0 - bits = AUDIO_CHANNEL_OUT_QUAD | AUDIO_CHANNEL_OUT_FRONT_CENTER; - break; - case 6: // 5.1 - bits = AUDIO_CHANNEL_OUT_5POINT1; - break; - case 7: // 6.1 - bits = AUDIO_CHANNEL_OUT_5POINT1 | AUDIO_CHANNEL_OUT_BACK_CENTER; - break; - case 8: - bits = AUDIO_CHANNEL_OUT_7POINT1; - break; - default: - return AUDIO_CHANNEL_INVALID; - } - return audio_channel_mask_from_representation_and_bits( - AUDIO_CHANNEL_REPRESENTATION_POSITION, bits); -} - -/* Derive an input channel mask for position assignment from a channel count. - * Currently handles only mono and stereo. - * Returns the matching channel mask, - * or AUDIO_CHANNEL_NONE if the channel count is zero, - * or AUDIO_CHANNEL_INVALID if the channel count exceeds that of the - * configurations for which a default input channel mask is defined. - */ -static inline audio_channel_mask_t audio_channel_in_mask_from_count(uint32_t channel_count) -{ - uint32_t bits; - switch (channel_count) { - case 0: - return AUDIO_CHANNEL_NONE; - case 1: - bits = AUDIO_CHANNEL_IN_MONO; - break; - case 2: - bits = AUDIO_CHANNEL_IN_STEREO; - break; - default: - return AUDIO_CHANNEL_INVALID; - } - return audio_channel_mask_from_representation_and_bits( - AUDIO_CHANNEL_REPRESENTATION_POSITION, bits); -} - -/* Derive a channel mask for index assignment from a channel count. - * Returns the matching channel mask, - * or AUDIO_CHANNEL_NONE if the channel count is zero, - * or AUDIO_CHANNEL_INVALID if the channel count exceeds AUDIO_CHANNEL_COUNT_MAX. - */ -static inline audio_channel_mask_t audio_channel_mask_for_index_assignment_from_count( - uint32_t channel_count) -{ - if (channel_count == 0) { - return AUDIO_CHANNEL_NONE; - } - if (channel_count > AUDIO_CHANNEL_COUNT_MAX) { - return AUDIO_CHANNEL_INVALID; - } - uint32_t bits = (1 << channel_count) - 1; - return audio_channel_mask_from_representation_and_bits( - AUDIO_CHANNEL_REPRESENTATION_INDEX, bits); -} - -static inline bool audio_is_valid_format(audio_format_t format) -{ - switch (format & AUDIO_FORMAT_MAIN_MASK) { - case AUDIO_FORMAT_PCM: - switch (format) { - case AUDIO_FORMAT_PCM_16_BIT: - case AUDIO_FORMAT_PCM_8_BIT: - case AUDIO_FORMAT_PCM_32_BIT: - case AUDIO_FORMAT_PCM_8_24_BIT: - case AUDIO_FORMAT_PCM_FLOAT: - case AUDIO_FORMAT_PCM_24_BIT_PACKED: - return true; - default: - return false; - } - /* not reached */ - case AUDIO_FORMAT_MP3: - case AUDIO_FORMAT_AMR_NB: - case AUDIO_FORMAT_AMR_WB: - case AUDIO_FORMAT_AAC: - case AUDIO_FORMAT_HE_AAC_V1: - case AUDIO_FORMAT_HE_AAC_V2: - case AUDIO_FORMAT_VORBIS: - case AUDIO_FORMAT_OPUS: - case AUDIO_FORMAT_AC3: - case AUDIO_FORMAT_E_AC3: - return true; - default: - return false; - } -} - -static inline bool audio_is_linear_pcm(audio_format_t format) -{ - return ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM); -} - -static inline size_t audio_bytes_per_sample(audio_format_t format) -{ - size_t size = 0; - - switch (format) { - case AUDIO_FORMAT_PCM_32_BIT: - case AUDIO_FORMAT_PCM_8_24_BIT: - size = sizeof(int32_t); - break; - case AUDIO_FORMAT_PCM_24_BIT_PACKED: - size = sizeof(uint8_t) * 3; - break; - case AUDIO_FORMAT_PCM_16_BIT: - size = sizeof(int16_t); - break; - case AUDIO_FORMAT_PCM_8_BIT: - size = sizeof(uint8_t); - break; - case AUDIO_FORMAT_PCM_FLOAT: - size = sizeof(float); - break; - default: - break; - } - return size; -} - -/* converts device address to string sent to audio HAL via set_parameters */ -static inline char *audio_device_address_to_parameter(audio_devices_t device, const char *address) -{ - const size_t kSize = AUDIO_DEVICE_MAX_ADDRESS_LEN + sizeof("a2dp_sink_address="); - char param[kSize]; - - if (device & AUDIO_DEVICE_OUT_ALL_A2DP) - snprintf(param, kSize, "%s=%s", "a2dp_sink_address", address); - else if (device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX) - snprintf(param, kSize, "%s=%s", "mix", address); - else - snprintf(param, kSize, "%s", address); - - return strdup(param); -} - - -__END_DECLS - -#endif // ANDROID_AUDIO_CORE_H diff --git a/include/system/audio_policy.h b/include/system/audio_policy.h deleted file mode 100644 index 288110495..000000000 --- a/include/system/audio_policy.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2011 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_AUDIO_POLICY_CORE_H -#define ANDROID_AUDIO_POLICY_CORE_H - -#include <stdint.h> -#include <sys/cdefs.h> -#include <sys/types.h> - -#include <cutils/bitops.h> - -__BEGIN_DECLS - -/* The enums were moved here mostly from - * frameworks/base/include/media/AudioSystem.h - */ - -/* device categories used for audio_policy->set_force_use() */ -typedef enum { - AUDIO_POLICY_FORCE_NONE, - AUDIO_POLICY_FORCE_SPEAKER, - AUDIO_POLICY_FORCE_HEADPHONES, - AUDIO_POLICY_FORCE_BT_SCO, - AUDIO_POLICY_FORCE_BT_A2DP, - AUDIO_POLICY_FORCE_WIRED_ACCESSORY, - AUDIO_POLICY_FORCE_BT_CAR_DOCK, - AUDIO_POLICY_FORCE_BT_DESK_DOCK, - AUDIO_POLICY_FORCE_ANALOG_DOCK, - AUDIO_POLICY_FORCE_DIGITAL_DOCK, - AUDIO_POLICY_FORCE_NO_BT_A2DP, /* A2DP sink is not preferred to speaker or wired HS */ - AUDIO_POLICY_FORCE_SYSTEM_ENFORCED, - AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED, - - AUDIO_POLICY_FORCE_CFG_CNT, - AUDIO_POLICY_FORCE_CFG_MAX = AUDIO_POLICY_FORCE_CFG_CNT - 1, - - AUDIO_POLICY_FORCE_DEFAULT = AUDIO_POLICY_FORCE_NONE, -} audio_policy_forced_cfg_t; - -/* usages used for audio_policy->set_force_use() */ -typedef enum { - AUDIO_POLICY_FORCE_FOR_COMMUNICATION, - AUDIO_POLICY_FORCE_FOR_MEDIA, - AUDIO_POLICY_FORCE_FOR_RECORD, - AUDIO_POLICY_FORCE_FOR_DOCK, - AUDIO_POLICY_FORCE_FOR_SYSTEM, - AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO, - - AUDIO_POLICY_FORCE_USE_CNT, - AUDIO_POLICY_FORCE_USE_MAX = AUDIO_POLICY_FORCE_USE_CNT - 1, -} audio_policy_force_use_t; - -/* device connection states used for audio_policy->set_device_connection_state() - */ -typedef enum { - AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - AUDIO_POLICY_DEVICE_STATE_AVAILABLE, - - AUDIO_POLICY_DEVICE_STATE_CNT, - AUDIO_POLICY_DEVICE_STATE_MAX = AUDIO_POLICY_DEVICE_STATE_CNT - 1, -} audio_policy_dev_state_t; - -typedef enum { - /* Used to generate a tone to notify the user of a - * notification/alarm/ringtone while they are in a call. */ - AUDIO_POLICY_TONE_IN_CALL_NOTIFICATION = 0, - - AUDIO_POLICY_TONE_CNT, - AUDIO_POLICY_TONE_MAX = AUDIO_POLICY_TONE_CNT - 1, -} audio_policy_tone_t; - - -static inline bool audio_is_low_visibility(audio_stream_type_t stream) -{ - switch (stream) { - case AUDIO_STREAM_SYSTEM: - case AUDIO_STREAM_NOTIFICATION: - case AUDIO_STREAM_RING: - return true; - default: - return false; - } -} - - -__END_DECLS - -#endif // ANDROID_AUDIO_POLICY_CORE_H diff --git a/include/system/camera.h b/include/system/camera.h index 7a4dd5340..5d0873ac4 100644 --- a/include/system/camera.h +++ b/include/system/camera.h @@ -174,6 +174,22 @@ enum { * count is non-positive or too big to be realized. */ CAMERA_CMD_SET_VIDEO_BUFFER_COUNT = 10, + + /** + * Configure an explicit format to use for video recording metadata mode. + * This can be used to switch the format from the + * default IMPLEMENTATION_DEFINED gralloc format to some other + * device-supported format, and the default dataspace from the BT_709 color + * space to some other device-supported dataspace. arg1 is the HAL pixel + * format, and arg2 is the HAL dataSpace. This command returns + * INVALID_OPERATION error if it is sent after video recording is started, + * or the command is not supported at all. + * + * If the gralloc format is set to a format other than + * IMPLEMENTATION_DEFINED, then HALv3 devices will use gralloc usage flags + * of SW_READ_OFTEN. + */ + CAMERA_CMD_SET_VIDEO_FORMAT = 11 }; /** camera fatal errors */ @@ -194,7 +210,12 @@ enum { /** The facing of the camera is opposite to that of the screen. */ CAMERA_FACING_BACK = 0, /** The facing of the camera is the same as that of the screen. */ - CAMERA_FACING_FRONT = 1 + CAMERA_FACING_FRONT = 1, + /** + * The facing of the camera is not fixed relative to the screen. + * The cameras with this facing are external cameras, e.g. USB cameras. + */ + CAMERA_FACING_EXTERNAL = 2 }; enum { diff --git a/include/system/graphics.h b/include/system/graphics.h index efd48cb0e..afd9f7bdb 100644 --- a/include/system/graphics.h +++ b/include/system/graphics.h @@ -58,11 +58,6 @@ enum { HAL_PIXEL_FORMAT_RGB_565 = 4, HAL_PIXEL_FORMAT_BGRA_8888 = 5, - // Deprecated sRGB formats for source code compatibility - // Not for use in new code - HAL_PIXEL_FORMAT_sRGB_A_8888 = 0xC, - HAL_PIXEL_FORMAT_sRGB_X_8888 = 0xD, - /* * 0x100 - 0x1FF * @@ -152,7 +147,8 @@ enum { * When used with ANativeWindow, the dataSpace field describes the color * space of the buffer, except that dataSpace field * HAL_DATASPACE_DEPTH indicates that this buffer contains a depth - * image where each sample is a distance value measured by a depth camera. + * image where each sample is a distance value measured by a depth camera, + * plus an associated confidence value. */ HAL_PIXEL_FORMAT_Y16 = 0x20363159, @@ -194,9 +190,6 @@ enum { */ HAL_PIXEL_FORMAT_RAW16 = 0x20, - // Temporary alias for source code compatibility; do not use in new code - HAL_PIXEL_FORMAT_RAW_SENSOR = HAL_PIXEL_FORMAT_RAW16, - /* * Android RAW10 format: * @@ -252,6 +245,56 @@ enum { HAL_PIXEL_FORMAT_RAW10 = 0x25, /* + * Android RAW12 format: + * + * This format is exposed outside of camera HAL to applications. + * + * RAW12 is a single-channel, 12-bit per pixel, densely packed in each row, + * unprocessed format, usually representing raw Bayer-pattern images coming from + * an image sensor. + * + * In an image buffer with this format, starting from the first pixel of each + * row, each two consecutive pixels are packed into 3 bytes (24 bits). The first + * and second byte contains the top 8 bits of first and second pixel. The third + * byte contains the 4 least significant bits of the two pixels, the exact layout + * data for each two consecutive pixels is illustrated below (Pi[j] stands for + * the jth bit of the ith pixel): + * + * bit 7 bit 0 + * ======|======|======|======|======|======|======|======| + * Byte 0: |P0[11]|P0[10]|P0[ 9]|P0[ 8]|P0[ 7]|P0[ 6]|P0[ 5]|P0[ 4]| + * |------|------|------|------|------|------|------|------| + * Byte 1: |P1[11]|P1[10]|P1[ 9]|P1[ 8]|P1[ 7]|P1[ 6]|P1[ 5]|P1[ 4]| + * |------|------|------|------|------|------|------|------| + * Byte 2: |P1[ 3]|P1[ 2]|P1[ 1]|P1[ 0]|P0[ 3]|P0[ 2]|P0[ 1]|P0[ 0]| + * ======================================================= + * + * This format assumes: + * - a width multiple of 4 pixels + * - an even height + * - a vertical stride equal to the height + * - strides are specified in bytes, not in pixels + * + * size = stride * height + * + * When stride is equal to width * (12 / 8), there will be no padding bytes at + * the end of each row, the entire image data is densely packed. When stride is + * larger than width * (12 / 8), padding bytes will be present at the end of + * each row (including the last row). + * + * This format must be accepted by the gralloc module when used with the + * following usage flags: + * - GRALLOC_USAGE_HW_CAMERA_* + * - GRALLOC_USAGE_SW_* + * - GRALLOC_USAGE_RENDERSCRIPT + * + * When used with ANativeWindow, the dataSpace field should be + * HAL_DATASPACE_ARBITRARY, as raw image sensor buffers require substantial + * extra metadata to define. + */ + HAL_PIXEL_FORMAT_RAW12 = 0x26, + + /* * Android opaque RAW format: * * This format is exposed outside of the camera HAL to applications. @@ -316,18 +359,18 @@ enum { HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22, /* - * Android flexible YCbCr formats + * Android flexible YCbCr 4:2:0 formats * - * This format allows platforms to use an efficient YCbCr/YCrCb buffer - * layout, while still describing the buffer layout in a way accessible to - * the CPU in a device-independent manner. While called YCbCr, it can be + * This format allows platforms to use an efficient YCbCr/YCrCb 4:2:0 + * buffer layout, while still describing the general format in a + * layout-independent manner. While called YCbCr, it can be * used to describe formats with either chromatic ordering, as well as * whole planar or semiplanar layouts. * * struct android_ycbcr (below) is the the struct used to describe it. * * This format must be accepted by the gralloc module when - * USAGE_HW_CAMERA_WRITE and USAGE_SW_READ_* are set. + * USAGE_SW_WRITE_* or USAGE_SW_READ_* are set. * * This format is locked for use by gralloc's (*lock_ycbcr) method, and * locking with the (*lock) method will return an error. @@ -337,6 +380,62 @@ enum { */ HAL_PIXEL_FORMAT_YCbCr_420_888 = 0x23, + /* + * Android flexible YCbCr 4:2:2 formats + * + * This format allows platforms to use an efficient YCbCr/YCrCb 4:2:2 + * buffer layout, while still describing the general format in a + * layout-independent manner. While called YCbCr, it can be + * used to describe formats with either chromatic ordering, as well as + * whole planar or semiplanar layouts. + * + * This format is currently only used by SW readable buffers + * produced by MediaCodecs, so the gralloc module can ignore this format. + */ + HAL_PIXEL_FORMAT_YCbCr_422_888 = 0x27, + + /* + * Android flexible YCbCr 4:4:4 formats + * + * This format allows platforms to use an efficient YCbCr/YCrCb 4:4:4 + * buffer layout, while still describing the general format in a + * layout-independent manner. While called YCbCr, it can be + * used to describe formats with either chromatic ordering, as well as + * whole planar or semiplanar layouts. + * + * This format is currently only used by SW readable buffers + * produced by MediaCodecs, so the gralloc module can ignore this format. + */ + HAL_PIXEL_FORMAT_YCbCr_444_888 = 0x28, + + /* + * Android flexible RGB 888 formats + * + * This format allows platforms to use an efficient RGB/BGR/RGBX/BGRX + * buffer layout, while still describing the general format in a + * layout-independent manner. While called RGB, it can be + * used to describe formats with either color ordering and optional + * padding, as well as whole planar layout. + * + * This format is currently only used by SW readable buffers + * produced by MediaCodecs, so the gralloc module can ignore this format. + */ + HAL_PIXEL_FORMAT_FLEX_RGB_888 = 0x29, + + /* + * Android flexible RGBA 8888 formats + * + * This format allows platforms to use an efficient RGBA/BGRA/ARGB/ABGR + * buffer layout, while still describing the general format in a + * layout-independent manner. While called RGBA, it can be + * used to describe formats with any of the component orderings, as + * well as whole planar layout. + * + * This format is currently only used by SW readable buffers + * produced by MediaCodecs, so the gralloc module can ignore this format. + */ + HAL_PIXEL_FORMAT_FLEX_RGBA_8888 = 0x2A, + /* Legacy formats (deprecated), used by ImageFormat.java */ HAL_PIXEL_FORMAT_YCbCr_422_SP = 0x10, // NV16 HAL_PIXEL_FORMAT_YCrCb_420_SP = 0x11, // NV21 @@ -383,25 +482,31 @@ struct android_ycbcr { * When locking a native buffer of the above format and dataSpace value, * the vaddr pointer can be cast to this structure. * - * A variable-length list of (x,y,z) 3D points, as floats. + * A variable-length list of (x,y,z, confidence) 3D points, as floats. (x, y, + * z) represents a measured point's position, with the coordinate system defined + * by the data source. Confidence represents the estimated likelihood that this + * measurement is correct. It is between 0.f and 1.f, inclusive, with 1.f == + * 100% confidence. * * @num_points is the number of points in the list * * @xyz_points is the flexible array of floating-point values. - * It contains (num_points) * 3 floats. + * It contains (num_points) * 4 floats. * * For example: * android_depth_points d = get_depth_buffer(); * struct { - * float x; float y; float z; + * float x; float y; float z; float confidence; * } firstPoint, lastPoint; * - * firstPoint.x = d.xyz_points[0]; - * firstPoint.y = d.xyz_points[1]; - * firstPoint.z = d.xyz_points[2]; - * lastPoint.x = d.xyz_points[(d.num_points - 1) * 3 + 0]; - * lastPoint.y = d.xyz_points[(d.num_points - 1) * 3 + 1]; - * lastPoint.z = d.xyz_points[(d.num_points - 1) * 3 + 2]; + * firstPoint.x = d.xyzc_points[0]; + * firstPoint.y = d.xyzc_points[1]; + * firstPoint.z = d.xyzc_points[2]; + * firstPoint.confidence = d.xyzc_points[3]; + * lastPoint.x = d.xyzc_points[(d.num_points - 1) * 4 + 0]; + * lastPoint.y = d.xyzc_points[(d.num_points - 1) * 4 + 1]; + * lastPoint.z = d.xyzc_points[(d.num_points - 1) * 4 + 2]; + * lastPoint.confidence = d.xyzc_points[(d.num_points - 1) * 4 + 3]; */ struct android_depth_points { @@ -410,7 +515,7 @@ struct android_depth_points { /** reserved for future use, set to 0 by gralloc's (*lock)() */ uint32_t reserved[8]; - float xyz_points[]; + float xyzc_points[]; }; /** @@ -632,9 +737,18 @@ typedef enum android_dataspace { /* * The buffer contains depth ranging measurements from a depth camera. * This value is valid with formats: - * HAL_PIXEL_FORMAT_Y16: 16-bit single channel depth image. + * HAL_PIXEL_FORMAT_Y16: 16-bit samples, consisting of a depth measurement + * and an associated confidence value. The 3 MSBs of the sample make + * up the confidence value, and the low 13 LSBs of the sample make up + * the depth measurement. + * For the confidence section, 0 means 100% confidence, 1 means 0% + * confidence. The mapping to a linear float confidence value between + * 0.f and 1.f can be obtained with + * float confidence = (((depthSample >> 13) - 1) & 0x7) / 7.0f; + * The depth measurement can be extracted simply with + * uint16_t range = (depthSample & 0x1FFF); * HAL_PIXEL_FORMAT_BLOB: A depth point cloud, as - * a variable-length float (x,y,z) coordinate point list. + * a variable-length float (x,y,z, confidence) coordinate point list. * The point cloud will be represented with the android_depth_points * structure. */ diff --git a/include/system/radio.h b/include/system/radio.h new file mode 100644 index 000000000..a08852604 --- /dev/null +++ b/include/system/radio.h @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2015 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_RADIO_H +#define ANDROID_RADIO_H + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <sys/cdefs.h> +#include <sys/types.h> + + +#define RADIO_NUM_BANDS_MAX 16 +#define RADIO_NUM_SPACINGS_MAX 16 +#define RADIO_STRING_LEN_MAX 128 + +/* + * Radio hardware module class. A given radio hardware module HAL is of one class + * only. The platform can not have more than one hardware module of each class. + * Current version of the framework only supports RADIO_CLASS_AM_FM. + */ +typedef enum { + RADIO_CLASS_AM_FM = 0, /* FM (including HD radio) and AM */ + RADIO_CLASS_SAT = 1, /* Satellite Radio */ + RADIO_CLASS_DT = 2, /* Digital Radio (DAB) */ +} radio_class_t; + +/* value for field "type" of radio band described in struct radio_hal_band_config */ +typedef enum { + RADIO_BAND_AM = 0, /* Amplitude Modulation band: LW, MW, SW */ + RADIO_BAND_FM = 1, /* Frequency Modulation band: FM */ + RADIO_BAND_FM_HD = 2, /* FM HD Radio / DRM (IBOC) */ + RADIO_BAND_AM_HD = 3, /* AM HD Radio / DRM (IBOC) */ +} radio_band_t; + +/* RDS variant implemented. A struct radio_hal_fm_band_config can list none or several. */ +enum { + RADIO_RDS_NONE = 0x0, + RADIO_RDS_WORLD = 0x01, + RADIO_RDS_US = 0x02, +}; +typedef unsigned int radio_rds_t; + +/* FM deemphasis variant implemented. A struct radio_hal_fm_band_config can list one or more. */ +enum { + RADIO_DEEMPHASIS_50 = 0x1, + RADIO_DEEMPHASIS_75 = 0x2, +}; +typedef unsigned int radio_deemphasis_t; + +/* Region a particular radio band configuration corresponds to. Not used at the HAL. + * Derived by the framework when converting the band descriptors retrieved from the HAL to + * individual band descriptors for each supported region. */ +typedef enum { + RADIO_REGION_NONE = -1, + RADIO_REGION_ITU_1 = 0, + RADIO_REGION_ITU_2 = 1, + RADIO_REGION_OIRT = 2, + RADIO_REGION_JAPAN = 3, + RADIO_REGION_KOREA = 4, +} radio_region_t; + +/* scanning direction for scan() and step() tuner APIs */ +typedef enum { + RADIO_DIRECTION_UP, + RADIO_DIRECTION_DOWN +} radio_direction_t; + +/* unique handle allocated to a radio module */ +typedef unsigned int radio_handle_t; + +/* Opaque meta data structure used by radio meta data API (see system/radio_metadata.h) */ +typedef struct radio_medtadata radio_metadata_t; + + +/* Additional attributes for an FM band configuration */ +typedef struct radio_hal_fm_band_config { + radio_deemphasis_t deemphasis; /* deemphasis variant */ + bool stereo; /* stereo supported */ + radio_rds_t rds; /* RDS variants supported */ + bool ta; /* Traffic Announcement supported */ + bool af; /* Alternate Frequency supported */ +} radio_hal_fm_band_config_t; + +/* Additional attributes for an AM band configuration */ +typedef struct radio_hal_am_band_config { + bool stereo; /* stereo supported */ +} radio_hal_am_band_config_t; + +/* Radio band configuration. Describes a given band supported by the radio module. + * The HAL can expose only one band per type with the the maximum range supported and all options. + * THe framework will derive the actual regions were this module can operate and expose separate + * band configurations for applications to chose from. */ +typedef struct radio_hal_band_config { + radio_band_t type; + bool antenna_connected; + unsigned int lower_limit; + unsigned int upper_limit; + unsigned int num_spacings; + unsigned int spacings[RADIO_NUM_SPACINGS_MAX]; + union { + radio_hal_fm_band_config_t fm; + radio_hal_am_band_config_t am; + }; +} radio_hal_band_config_t; + +/* Used internally by the framework to represent a band for s specific region */ +typedef struct radio_band_config { + radio_region_t region; + radio_hal_band_config_t band; +} radio_band_config_t; + + +/* Exposes properties of a given hardware radio module. + * NOTE: current framework implementation supports only one audio source (num_audio_sources = 1). + * The source corresponds to AUDIO_DEVICE_IN_FM_TUNER. + * If more than one tuner is supported (num_tuners > 1), only one can be connected to the audio + * source. */ +typedef struct radio_hal_properties { + radio_class_t class_id; /* Class of this module. E.g RADIO_CLASS_AM_FM */ + char implementor[RADIO_STRING_LEN_MAX]; /* implementor name */ + char product[RADIO_STRING_LEN_MAX]; /* product name */ + char version[RADIO_STRING_LEN_MAX]; /* product version */ + char serial[RADIO_STRING_LEN_MAX]; /* serial number (for subscription services) */ + unsigned int num_tuners; /* number of tuners controllable independently */ + unsigned int num_audio_sources; /* number of audio sources driven simultaneously */ + bool supports_capture; /* the hardware supports capture of audio source audio HAL */ + unsigned int num_bands; /* number of band descriptors */ + radio_hal_band_config_t bands[RADIO_NUM_BANDS_MAX]; /* band descriptors */ +} radio_hal_properties_t; + +/* Used internally by the framework. Same information as in struct radio_hal_properties plus a + * unique handle and one band configuration per region. */ +typedef struct radio_properties { + radio_handle_t handle; + radio_class_t class_id; + char implementor[RADIO_STRING_LEN_MAX]; + char product[RADIO_STRING_LEN_MAX]; + char version[RADIO_STRING_LEN_MAX]; + char serial[RADIO_STRING_LEN_MAX]; + unsigned int num_tuners; + unsigned int num_audio_sources; + bool supports_capture; + unsigned int num_bands; + radio_band_config_t bands[RADIO_NUM_BANDS_MAX]; +} radio_properties_t; + +/* Radio program information. Returned by the HAL with event RADIO_EVENT_TUNED. + * Contains information on currently tuned channel. + */ +typedef struct radio_program_info { + unsigned int channel; /* current channel. (e.g kHz for band type RADIO_BAND_FM) */ + unsigned int sub_channel; /* current sub channel. (used for RADIO_BAND_FM_HD) */ + bool tuned; /* tuned to a program or not */ + bool stereo; /* program is stereo or not */ + bool digital; /* digital program or not (e.g HD Radio program) */ + unsigned int signal_strength; /* signal strength from 0 to 100 */ + radio_metadata_t *metadata; /* non null if meta data are present (e.g PTY, song title ...) */ +} radio_program_info_t; + + +/* Events sent to the framework via the HAL callback. An event can notify the completion of an + * asynchronous command (configuration, tune, scan ...) or a spontaneous change (antenna connection, + * failure, AF switching, meta data reception... */ +enum { + RADIO_EVENT_HW_FAILURE = 0, /* hardware module failure. Requires reopening the tuner */ + RADIO_EVENT_CONFIG = 1, /* configuration change completed */ + RADIO_EVENT_ANTENNA = 2, /* Antenna connected, disconnected */ + RADIO_EVENT_TUNED = 3, /* tune, step, scan completed */ + RADIO_EVENT_METADATA = 4, /* New meta data received */ + RADIO_EVENT_TA = 5, /* Traffic announcement start or stop */ + RADIO_EVENT_AF_SWITCH = 6, /* Switch to Alternate Frequency */ + // begin framework only events + RADIO_EVENT_CONTROL = 100, /* loss/gain of tuner control */ + RADIO_EVENT_SERVER_DIED = 101, /* radio service died */ +}; +typedef unsigned int radio_event_type_t; + +/* Event passed to the framework by the HAL callback */ +typedef struct radio_hal_event { + radio_event_type_t type; /* event type */ + int status; /* used by RADIO_EVENT_CONFIG, RADIO_EVENT_TUNED */ + union { + bool on; /* RADIO_EVENT_ANTENNA, RADIO_EVENT_TA */ + radio_hal_band_config_t config; /* RADIO_EVENT_CONFIG */ + radio_program_info_t info; /* RADIO_EVENT_TUNED, RADIO_EVENT_AF_SWITCH */ + radio_metadata_t *metadata; /* RADIO_EVENT_METADATA */ + }; +} radio_hal_event_t; + +/* Used internally by the framework. Same information as in struct radio_hal_event */ +typedef struct radio_event { + radio_event_type_t type; + int status; + union { + bool on; + radio_band_config_t config; + radio_program_info_t info; + radio_metadata_t *metadata; /* offset from start of struct when in shared memory */ + }; +} radio_event_t; + + +static radio_rds_t radio_rds_for_region(bool rds, radio_region_t region) { + if (!rds) + return RADIO_RDS_NONE; + switch(region) { + case RADIO_REGION_ITU_1: + case RADIO_REGION_OIRT: + case RADIO_REGION_JAPAN: + case RADIO_REGION_KOREA: + return RADIO_RDS_WORLD; + case RADIO_REGION_ITU_2: + return RADIO_RDS_US; + default: + return RADIO_REGION_NONE; + } +} + +static radio_deemphasis_t radio_demephasis_for_region(radio_region_t region) { + switch(region) { + case RADIO_REGION_KOREA: + case RADIO_REGION_ITU_2: + return RADIO_DEEMPHASIS_75; + case RADIO_REGION_ITU_1: + case RADIO_REGION_OIRT: + case RADIO_REGION_JAPAN: + default: + return RADIO_DEEMPHASIS_50; + } +} + +#endif // ANDROID_RADIO_H diff --git a/include/system/sound_trigger.h b/include/system/sound_trigger.h deleted file mode 100644 index 773e4f7d0..000000000 --- a/include/system/sound_trigger.h +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_SOUND_TRIGGER_H -#define ANDROID_SOUND_TRIGGER_H - -#include <stdbool.h> -#include <system/audio.h> - -#define SOUND_TRIGGER_MAX_STRING_LEN 64 /* max length of strings in properties or - descriptor structs */ -#define SOUND_TRIGGER_MAX_LOCALE_LEN 6 /* max length of locale string. e.g en_US */ -#define SOUND_TRIGGER_MAX_USERS 10 /* max number of concurrent users */ -#define SOUND_TRIGGER_MAX_PHRASES 10 /* max number of concurrent phrases */ - -typedef enum { - SOUND_TRIGGER_STATE_NO_INIT = -1, /* The sound trigger service is not initialized */ - SOUND_TRIGGER_STATE_ENABLED = 0, /* The sound trigger service is enabled */ - SOUND_TRIGGER_STATE_DISABLED = 1 /* The sound trigger service is disabled */ -} sound_trigger_service_state_t; - -#define RECOGNITION_MODE_VOICE_TRIGGER 0x1 /* simple voice trigger */ -#define RECOGNITION_MODE_USER_IDENTIFICATION 0x2 /* trigger only if one user in model identified */ -#define RECOGNITION_MODE_USER_AUTHENTICATION 0x4 /* trigger only if one user in mode - authenticated */ -#define RECOGNITION_STATUS_SUCCESS 0 -#define RECOGNITION_STATUS_ABORT 1 -#define RECOGNITION_STATUS_FAILURE 2 - -#define SOUND_MODEL_STATUS_UPDATED 0 - -typedef enum { - SOUND_MODEL_TYPE_UNKNOWN = -1, /* use for unspecified sound model type */ - SOUND_MODEL_TYPE_KEYPHRASE = 0 /* use for key phrase sound models */ -} sound_trigger_sound_model_type_t; - -typedef struct sound_trigger_uuid_s { - unsigned int timeLow; - unsigned short timeMid; - unsigned short timeHiAndVersion; - unsigned short clockSeq; - unsigned char node[6]; -} sound_trigger_uuid_t; - -/* - * sound trigger implementation descriptor read by the framework via get_properties(). - * Used by SoundTrigger service to report to applications and manage concurrency and policy. - */ -struct sound_trigger_properties { - char implementor[SOUND_TRIGGER_MAX_STRING_LEN]; /* implementor name */ - char description[SOUND_TRIGGER_MAX_STRING_LEN]; /* implementation description */ - unsigned int version; /* implementation version */ - sound_trigger_uuid_t uuid; /* unique implementation ID. - Must change with version each version */ - unsigned int max_sound_models; /* maximum number of concurrent sound models - loaded */ - unsigned int max_key_phrases; /* maximum number of key phrases */ - unsigned int max_users; /* maximum number of concurrent users detected */ - unsigned int recognition_modes; /* all supported modes. - e.g RECOGNITION_MODE_VOICE_TRIGGER */ - bool capture_transition; /* supports seamless transition from detection - to capture */ - unsigned int max_buffer_ms; /* maximum buffering capacity in ms if - capture_transition is true*/ - bool concurrent_capture; /* supports capture by other use cases while - detection is active */ - bool trigger_in_event; /* returns the trigger capture in event */ - unsigned int power_consumption_mw; /* Rated power consumption when detection is active - with TDB silence/sound/speech ratio */ -}; - -typedef int sound_trigger_module_handle_t; - -struct sound_trigger_module_descriptor { - sound_trigger_module_handle_t handle; - struct sound_trigger_properties properties; -}; - -typedef int sound_model_handle_t; - -/* - * Generic sound model descriptor. This struct is the header of a larger block passed to - * load_sound_model() and containing the binary data of the sound model. - * Proprietary representation of users in binary data must match information indicated - * by users field - */ -struct sound_trigger_sound_model { - sound_trigger_sound_model_type_t type; /* model type. e.g. SOUND_MODEL_TYPE_KEYPHRASE */ - sound_trigger_uuid_t uuid; /* unique sound model ID. */ - sound_trigger_uuid_t vendor_uuid; /* unique vendor ID. Identifies the engine the - sound model was build for */ - unsigned int data_size; /* size of opaque model data */ - unsigned int data_offset; /* offset of opaque data start from head of struct - (e.g sizeof struct sound_trigger_sound_model) */ -}; - -/* key phrase descriptor */ -struct sound_trigger_phrase { - unsigned int id; /* keyphrase ID */ - unsigned int recognition_mode; /* recognition modes supported by this key phrase */ - unsigned int num_users; /* number of users in the key phrase */ - unsigned int users[SOUND_TRIGGER_MAX_USERS]; /* users ids: (not uid_t but sound trigger - specific IDs */ - char locale[SOUND_TRIGGER_MAX_LOCALE_LEN]; /* locale - JAVA Locale style (e.g. en_US) */ - char text[SOUND_TRIGGER_MAX_STRING_LEN]; /* phrase text in UTF-8 format. */ -}; - -/* - * Specialized sound model for key phrase detection. - * Proprietary representation of key phrases in binary data must match information indicated - * by phrases field - */ -struct sound_trigger_phrase_sound_model { - struct sound_trigger_sound_model common; - unsigned int num_phrases; /* number of key phrases in model */ - struct sound_trigger_phrase phrases[SOUND_TRIGGER_MAX_PHRASES]; -}; - - -/* - * Generic recognition event sent via recognition callback - */ -struct sound_trigger_recognition_event { - int status; /* recognition status e.g. - RECOGNITION_STATUS_SUCCESS */ - sound_trigger_sound_model_type_t type; /* event type, same as sound model type. - e.g. SOUND_MODEL_TYPE_KEYPHRASE */ - sound_model_handle_t model; /* loaded sound model that triggered the - event */ - bool capture_available; /* it is possible to capture audio from this - utterance buffered by the - implementation */ - int capture_session; /* audio session ID. framework use */ - int capture_delay_ms; /* delay in ms between end of model - detection and start of audio available - for capture. A negative value is possible - (e.g. if key phrase is also available for - capture */ - int capture_preamble_ms; /* duration in ms of audio captured - before the start of the trigger. - 0 if none. */ - bool trigger_in_data; /* the opaque data is the capture of - the trigger sound */ - audio_config_t audio_config; /* audio format of either the trigger in - event data or to use for capture of the - rest of the utterance */ - unsigned int data_size; /* size of opaque event data */ - unsigned int data_offset; /* offset of opaque data start from start of - this struct (e.g sizeof struct - sound_trigger_phrase_recognition_event) */ -}; - -/* - * Confidence level for each user in struct sound_trigger_phrase_recognition_extra - */ -struct sound_trigger_confidence_level { - unsigned int user_id; /* user ID */ - unsigned int level; /* confidence level in percent (0 - 100). - - min level for recognition configuration - - detected level for recognition event */ -}; - -/* - * Specialized recognition event for key phrase detection - */ -struct sound_trigger_phrase_recognition_extra { - unsigned int id; /* keyphrase ID */ - unsigned int recognition_modes; /* recognition modes used for this keyphrase */ - unsigned int confidence_level; /* confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER */ - unsigned int num_levels; /* number of user confidence levels */ - struct sound_trigger_confidence_level levels[SOUND_TRIGGER_MAX_USERS]; -}; - -struct sound_trigger_phrase_recognition_event { - struct sound_trigger_recognition_event common; - unsigned int num_phrases; - struct sound_trigger_phrase_recognition_extra phrase_extras[SOUND_TRIGGER_MAX_PHRASES]; -}; - -/* - * configuration for sound trigger capture session passed to start_recognition() - */ -struct sound_trigger_recognition_config { - audio_io_handle_t capture_handle; /* IO handle that will be used for capture. - N/A if capture_requested is false */ - audio_devices_t capture_device; /* input device requested for detection capture */ - bool capture_requested; /* capture and buffer audio for this recognition - instance */ - unsigned int num_phrases; /* number of key phrases recognition extras */ - struct sound_trigger_phrase_recognition_extra phrases[SOUND_TRIGGER_MAX_PHRASES]; - /* configuration for each key phrase */ - unsigned int data_size; /* size of opaque capture configuration data */ - unsigned int data_offset; /* offset of opaque data start from start of this struct - (e.g sizeof struct sound_trigger_recognition_config) */ -}; - -/* - * Event sent via load sound model callback - */ -struct sound_trigger_model_event { - int status; /* sound model status e.g. SOUND_MODEL_STATUS_UPDATED */ - sound_model_handle_t model; /* loaded sound model that triggered the event */ - unsigned int data_size; /* size of event data if any. Size of updated sound model if - status is SOUND_MODEL_STATUS_UPDATED */ - unsigned int data_offset; /* offset of data start from start of this struct - (e.g sizeof struct sound_trigger_model_event) */ -}; - - -#endif // ANDROID_SOUND_TRIGGER_H diff --git a/include/sysutils/NetlinkEvent.h b/include/sysutils/NetlinkEvent.h index 4fa49c52a..b80f3ea44 100644 --- a/include/sysutils/NetlinkEvent.h +++ b/include/sysutils/NetlinkEvent.h @@ -21,25 +21,29 @@ #define NL_PARAMS_MAX 32 class NetlinkEvent { +public: + enum class Action { + kUnknown = 0, + kAdd = 1, + kRemove = 2, + kChange = 3, + kLinkUp = 4, + kLinkDown = 5, + kAddressUpdated = 6, + kAddressRemoved = 7, + kRdnss = 8, + kRouteUpdated = 9, + kRouteRemoved = 10, + }; + +private: int mSeq; char *mPath; - int mAction; + Action mAction; char *mSubsystem; char *mParams[NL_PARAMS_MAX]; public: - const static int NlActionUnknown; - const static int NlActionAdd; - const static int NlActionRemove; - const static int NlActionChange; - const static int NlActionLinkDown; - const static int NlActionLinkUp; - const static int NlActionAddressUpdated; - const static int NlActionAddressRemoved; - const static int NlActionRdnss; - const static int NlActionRouteUpdated; - const static int NlActionRouteRemoved; - NetlinkEvent(); virtual ~NetlinkEvent(); @@ -47,7 +51,7 @@ public: const char *findParam(const char *paramName); const char *getSubsystem() { return mSubsystem; } - int getAction() { return mAction; } + Action getAction() { return mAction; } void dump(); diff --git a/include/usbhost/usbhost.h b/include/usbhost/usbhost.h index d26e93175..4350ec103 100644 --- a/include/usbhost/usbhost.h +++ b/include/usbhost/usbhost.h @@ -156,6 +156,10 @@ char* usb_device_get_manufacturer_name(struct usb_device *device); */ char* usb_device_get_product_name(struct usb_device *device); +/* Returns the version number for the USB device. + */ +int usb_device_get_version(struct usb_device *device); + /* Returns the USB serial number for the USB device. * Call free() to free the result when you are done with it. */ diff --git a/include/utils/BlobCache.h b/include/utils/BlobCache.h index 7d621e438..65dca9fb4 100644 --- a/include/utils/BlobCache.h +++ b/include/utils/BlobCache.h @@ -185,6 +185,12 @@ private: // mNumEntries is number of cache entries following the header in the // data. size_t mNumEntries; + + // mBuildId is the build id of the device when the cache was created. + // When an update to the build happens (via an OTA or other update) this + // is used to invalidate the cache. + int mBuildIdLength; + char mBuildId[]; }; // An EntryHeader is the header for a serialized cache entry. No need to diff --git a/include/utils/LinearAllocator.h b/include/utils/LinearAllocator.h deleted file mode 100644 index 4772bc8f7..000000000 --- a/include/utils/LinearAllocator.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2012, The Android Open Source Project - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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. - */ - -#ifndef ANDROID_LINEARALLOCATOR_H -#define ANDROID_LINEARALLOCATOR_H - -#include <stddef.h> - -namespace android { - -/** - * A memory manager that internally allocates multi-kbyte buffers for placing objects in. It avoids - * the overhead of malloc when many objects are allocated. It is most useful when creating many - * small objects with a similar lifetime, and doesn't add significant overhead for large - * allocations. - */ -class LinearAllocator { -public: - LinearAllocator(); - ~LinearAllocator(); - - /** - * Reserves and returns a region of memory of at least size 'size', aligning as needed. - * Typically this is used in an object's overridden new() method or as a replacement for malloc. - * - * The lifetime of the returned buffers is tied to that of the LinearAllocator. If calling - * delete() on an object stored in a buffer is needed, it should be overridden to use - * rewindIfLastAlloc() - */ - void* alloc(size_t size); - - /** - * Attempt to deallocate the given buffer, with the LinearAllocator attempting to rewind its - * state if possible. No destructors are called. - */ - void rewindIfLastAlloc(void* ptr, size_t allocSize); - - /** - * Dump memory usage statistics to the log (allocated and wasted space) - */ - void dumpMemoryStats(const char* prefix = ""); - - /** - * The number of bytes used for buffers allocated in the LinearAllocator (does not count space - * wasted) - */ - size_t usedSize() const { return mTotalAllocated - mWastedSpace; } - -private: - LinearAllocator(const LinearAllocator& other); - - class Page; - - Page* newPage(size_t pageSize); - bool fitsInCurrentPage(size_t size); - void ensureNext(size_t size); - void* start(Page *p); - void* end(Page* p); - - size_t mPageSize; - size_t mMaxAllocSize; - void* mNext; - Page* mCurrentPage; - Page* mPages; - - // Memory usage tracking - size_t mTotalAllocated; - size_t mWastedSpace; - size_t mPageCount; - size_t mDedicatedPageCount; -}; - -}; // namespace android - -#endif // ANDROID_LINEARALLOCATOR_H diff --git a/include/utils/Looper.h b/include/utils/Looper.h index 15c989176..da2d5f2b5 100644 --- a/include/utils/Looper.h +++ b/include/utils/Looper.h @@ -386,11 +386,12 @@ public: void removeMessages(const sp<MessageHandler>& handler, int what); /** - * Return whether this looper's thread is currently idling -- that is, whether it - * stopped waiting for more work to do. Note that this is intrinsically racy, since - * its state can change before you get the result back. + * Returns whether this looper's thread is currently polling for more work to do. + * This is a good signal that the loop is still alive rather than being stuck + * handling a callback. Note that this method is intrinsically racy, since the + * state of the loop can change before you get the result back. */ - bool isIdling() const; + bool isPolling() const; /** * Prepares a looper associated with the calling thread, and returns it. @@ -419,8 +420,12 @@ private: struct Request { int fd; int ident; + int events; + int seq; sp<LooperCallback> callback; void* data; + + void initEventItem(struct epoll_event* eventItem) const; }; struct Response { @@ -442,8 +447,7 @@ private: const bool mAllowNonCallbacks; // immutable - int mWakeReadPipeFd; // immutable - int mWakeWritePipeFd; // immutable + int mWakeEventFd; // immutable Mutex mLock; Vector<MessageEnvelope> mMessageEnvelopes; // guarded by mLock @@ -451,12 +455,14 @@ private: // Whether we are currently waiting for work. Not protected by a lock, // any use of it is racy anyway. - volatile bool mIdling; + volatile bool mPolling; - int mEpollFd; // immutable + int mEpollFd; // guarded by mLock but only modified on the looper thread + bool mEpollRebuildRequired; // guarded by mLock // Locked list of file descriptor monitoring requests. KeyedVector<int, Request> mRequests; // guarded by mLock + int mNextRequestSeq; // This state is only used privately by pollOnce and does not require a lock since // it runs on a single thread. @@ -465,11 +471,15 @@ private: nsecs_t mNextMessageUptime; // set to LLONG_MAX when none int pollInner(int timeoutMillis); + int removeFd(int fd, int seq); void awoken(); void pushResponse(int events, const Request& request); + void rebuildEpollLocked(); + void scheduleEpollRebuildLocked(); static void initTLSKey(); static void threadDestructor(void *st); + static void initEpollEvent(struct epoll_event* eventItem); }; } // namespace android diff --git a/init/Android.mk b/init/Android.mk index 76709513c..c1cdc1ae9 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -95,7 +95,9 @@ LOCAL_STATIC_LIBRARIES := \ libselinux \ libmincrypt \ libc++_static \ - libdl + libdl \ + libsparse_static \ + libz # Create symlinks LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \ diff --git a/init/devices.cpp b/init/devices.cpp index 3652c579e..d556e307c 100644 --- a/init/devices.cpp +++ b/init/devices.cpp @@ -528,7 +528,7 @@ static void handle_device(const char *action, const char *devpath, make_device(devpath, path, block, major, minor, (const char **)links); if (links) { for (i = 0; links[i]; i++) - make_link(devpath, links[i]); + make_link_init(devpath, links[i]); } } diff --git a/init/property_service.cpp b/init/property_service.cpp index aa939a58d..719482075 100644 --- a/init/property_service.cpp +++ b/init/property_service.cpp @@ -203,6 +203,16 @@ static int property_set_impl(const char* name, const char* value) { if (!is_legal_property_name(name, namelen)) return -1; if (valuelen >= PROP_VALUE_MAX) return -1; + if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) { + if (selinux_reload_policy() != 0) { + ERROR("Failed to reload policy\n"); + } + } else if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) { + if (restorecon_recursive(value) != 0) { + ERROR("Failed to restorecon_recursive %s\n", value); + } + } + prop_info* pi = (prop_info*) __system_property_find(name); if(pi != 0) { @@ -234,9 +244,6 @@ static int property_set_impl(const char* name, const char* value) { * to prevent them from being overwritten by default values. */ write_persistent_property(name, value); - } else if (strcmp("selinux.reload_policy", name) == 0 && - strcmp("1", value) == 0) { - selinux_reload_policy(); } property_changed(name, value); return 0; diff --git a/init/util.cpp b/init/util.cpp index 1eb90e065..b316d6ec4 100644 --- a/init/util.cpp +++ b/init/util.cpp @@ -337,7 +337,7 @@ void sanitize(char *s) } } -void make_link(const char *oldpath, const char *newpath) +void make_link_init(const char *oldpath, const char *newpath) { int ret; char buf[256]; @@ -376,10 +376,10 @@ void remove_link(const char *oldpath, const char *newpath) int wait_for_file(const char *filename, int timeout) { struct stat info; - time_t timeout_time = gettime() + timeout; + uint64_t timeout_time_ns = gettime_ns() + timeout * UINT64_C(1000000000); int ret = -1; - while (gettime() < timeout_time && ((ret = stat(filename, &info)) < 0)) + while (gettime_ns() < timeout_time_ns && ((ret = stat(filename, &info)) < 0)) usleep(10000); return ret; diff --git a/init/util.h b/init/util.h index a33209ee2..c2efb0145 100644 --- a/init/util.h +++ b/init/util.h @@ -54,7 +54,7 @@ unsigned int decode_uid(const char *s); int mkdir_recursive(const char *pathname, mode_t mode); void sanitize(char *p); -void make_link(const char *oldpath, const char *newpath); +void make_link_init(const char *oldpath, const char *newpath); void remove_link(const char *oldpath, const char *newpath); int wait_for_file(const char *filename, int timeout); void open_devnull_stdio(void); diff --git a/libcutils/Android.mk b/libcutils/Android.mk index 015bd3ff7..3ca544f04 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -112,6 +112,9 @@ LOCAL_SRC_FILES_x86_64 += \ LOCAL_C_INCLUDES := $(libcutils_c_includes) LOCAL_STATIC_LIBRARIES := liblog +ifneq ($(ENABLE_CPUSETS),) +LOCAL_CFLAGS += -DUSE_CPUSETS +endif LOCAL_CFLAGS += -Werror -Wall -Wextra -std=gnu90 LOCAL_CLANG := true LOCAL_SANITIZE := integer @@ -123,6 +126,9 @@ LOCAL_MODULE := libcutils # liblog symbols present in libcutils. LOCAL_WHOLE_STATIC_LIBRARIES := libcutils liblog LOCAL_SHARED_LIBRARIES := liblog +ifneq ($(ENABLE_CPUSETS),) +LOCAL_CFLAGS += -DUSE_CPUSETS +endif LOCAL_CFLAGS += -Werror -Wall -Wextra LOCAL_C_INCLUDES := $(libcutils_c_includes) LOCAL_CLANG := true diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c index 27981ff61..681c57f0e 100644 --- a/libcutils/fs_config.c +++ b/libcutils/fs_config.c @@ -154,14 +154,21 @@ static const struct fs_path_config android_files[] = { { 00644, AID_ROOT, AID_ROOT, 0, 0 }, }; -static int fs_config_open(int dir) +static int fs_config_open(int dir, const char *target_out_path) { int fd = -1; - const char *out = getenv("OUT"); - if (out && *out) { + if (target_out_path && *target_out_path) { + /* target_out_path is the path to the directory holding content of system partition + but as we cannot guaranty it ends with '/system' we need this below skip_len logic */ char *name = NULL; - if (asprintf(&name, "%s%s", out, dir ? conf_dir : conf_file) != -1) { + int target_out_path_len = strlen(target_out_path); + int skip_len = strlen("/system"); + + if (target_out_path[target_out_path_len] == '/') { + skip_len++; + } + if (asprintf(&name, "%s%s", target_out_path, (dir ? conf_dir : conf_file) + skip_len) != -1) { fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_BINARY)); free(name); } @@ -191,7 +198,7 @@ static bool fs_config_cmp(bool dir, const char *prefix, size_t len, return !strncmp(prefix, path, len); } -void fs_config(const char *path, int dir, +void fs_config(const char *path, int dir, const char *target_out_path, unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities) { const struct fs_path_config *pc; @@ -203,7 +210,7 @@ void fs_config(const char *path, int dir, plen = strlen(path); - fd = fs_config_open(dir); + fd = fs_config_open(dir, target_out_path); if (fd >= 0) { struct fs_path_config_from_file header; diff --git a/libcutils/native_handle.c b/libcutils/native_handle.c index 9a4a5bb38..61fa38ed4 100644 --- a/libcutils/native_handle.c +++ b/libcutils/native_handle.c @@ -25,11 +25,17 @@ #include <cutils/log.h> #include <cutils/native_handle.h> +static const int kMaxNativeFds = 1024; +static const int kMaxNativeInts = 1024; + native_handle_t* native_handle_create(int numFds, int numInts) { - native_handle_t* h = malloc( - sizeof(native_handle_t) + sizeof(int)*(numFds+numInts)); + if (numFds < 0 || numInts < 0 || numFds > kMaxNativeFds || numInts > kMaxNativeInts) { + return NULL; + } + size_t mallocSize = sizeof(native_handle_t) + (sizeof(int) * (numFds + numInts)); + native_handle_t* h = malloc(mallocSize); if (h) { h->version = sizeof(native_handle_t); h->numFds = numFds; diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c index 103ff66b3..298e3dac7 100644 --- a/libcutils/sched_policy.c +++ b/libcutils/sched_policy.c @@ -1,16 +1,16 @@ /* ** Copyright 2007, 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 +** 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 +** 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 +** 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. */ @@ -50,6 +50,7 @@ static inline SchedPolicy _policy(SchedPolicy p) // timer slack value in nS enforced when the thread moves to background #define TIMER_SLACK_BG 40000000 +#define TIMER_SLACK_FG 50000 static pthread_once_t the_once = PTHREAD_ONCE_INIT; @@ -59,27 +60,18 @@ static int __sys_supports_schedgroups = -1; static int bg_cgroup_fd = -1; static int fg_cgroup_fd = -1; +#ifdef USE_CPUSETS +// File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error +static int bg_cpuset_fd = -1; +static int fg_cpuset_fd = -1; +#endif + /* Add tid to the scheduling group defined by the policy */ -static int add_tid_to_cgroup(int tid, SchedPolicy policy) +static int add_tid_to_cgroup(int tid, int fd) { - int fd; - - switch (policy) { - case SP_BACKGROUND: - fd = bg_cgroup_fd; - break; - case SP_FOREGROUND: - case SP_AUDIO_APP: - case SP_AUDIO_SYS: - fd = fg_cgroup_fd; - break; - default: - fd = -1; - break; - } - if (fd < 0) { - SLOGE("add_tid_to_cgroup failed; policy=%d\n", policy); + SLOGE("add_tid_to_cgroup failed; fd=%d\n", fd); + errno = EINVAL; return -1; } @@ -100,8 +92,9 @@ static int add_tid_to_cgroup(int tid, SchedPolicy policy) */ if (errno == ESRCH) return 0; - SLOGW("add_tid_to_cgroup failed to write '%s' (%s); policy=%d\n", - ptr, strerror(errno), policy); + SLOGW("add_tid_to_cgroup failed to write '%s' (%s); fd=%d\n", + ptr, strerror(errno), fd); + errno = EINVAL; return -1; } @@ -127,6 +120,17 @@ static void __initialize(void) { } else { __sys_supports_schedgroups = 0; } + +#ifdef USE_CPUSETS + if (!access("/dev/cpuset/tasks", F_OK)) { + + filename = "/dev/cpuset/foreground/tasks"; + fg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC); + filename = "/dev/cpuset/background/tasks"; + bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC); + } +#endif + } /* @@ -236,6 +240,42 @@ int get_sched_policy(int tid, SchedPolicy *policy) return 0; } +int set_cpuset_policy(int tid, SchedPolicy policy) +{ + // in the absence of cpusets, use the old sched policy +#ifndef USE_CPUSETS + return set_sched_policy(tid, policy); +#else + if (tid == 0) { + tid = gettid(); + } + policy = _policy(policy); + pthread_once(&the_once, __initialize); + + int fd; + switch (policy) { + case SP_BACKGROUND: + fd = bg_cpuset_fd; + break; + case SP_FOREGROUND: + case SP_AUDIO_APP: + case SP_AUDIO_SYS: + fd = fg_cpuset_fd; + break; + default: + fd = -1; + break; + } + + if (add_tid_to_cgroup(tid, fd) != 0) { + if (errno != ESRCH && errno != ENOENT) + return -errno; + } + + return 0; +#endif +} + int set_sched_policy(int tid, SchedPolicy policy) { if (tid == 0) { @@ -286,7 +326,23 @@ int set_sched_policy(int tid, SchedPolicy policy) #endif if (__sys_supports_schedgroups) { - if (add_tid_to_cgroup(tid, policy)) { + int fd; + switch (policy) { + case SP_BACKGROUND: + fd = bg_cgroup_fd; + break; + case SP_FOREGROUND: + case SP_AUDIO_APP: + case SP_AUDIO_SYS: + fd = fg_cgroup_fd; + break; + default: + fd = -1; + break; + } + + + if (add_tid_to_cgroup(tid, fd) != 0) { if (errno != ESRCH && errno != ENOENT) return -errno; } @@ -296,11 +352,12 @@ int set_sched_policy(int tid, SchedPolicy policy) param.sched_priority = 0; sched_setscheduler(tid, (policy == SP_BACKGROUND) ? - SCHED_BATCH : SCHED_NORMAL, + SCHED_BATCH : SCHED_NORMAL, ¶m); } - prctl(PR_SET_TIMERSLACK_PID, policy == SP_BACKGROUND ? TIMER_SLACK_BG : 0, tid); + prctl(PR_SET_TIMERSLACK_PID, + policy == SP_BACKGROUND ? TIMER_SLACK_BG : TIMER_SLACK_FG, tid); return 0; } @@ -337,4 +394,3 @@ const char *get_sched_policy_name(SchedPolicy policy) else return "error"; } - diff --git a/libion/ion.c b/libion/ion.c index 490893258..d1984bd06 100644 --- a/libion/ion.c +++ b/libion/ion.c @@ -91,6 +91,7 @@ int ion_map(int fd, ion_user_handle_t handle, size_t length, int prot, int flags, off_t offset, unsigned char **ptr, int *map_fd) { int ret; + unsigned char *tmp_ptr; struct ion_fd_data data = { .handle = handle, }; @@ -103,16 +104,17 @@ int ion_map(int fd, ion_user_handle_t handle, size_t length, int prot, ret = ion_ioctl(fd, ION_IOC_MAP, &data); if (ret < 0) return ret; - *map_fd = data.fd; - if (*map_fd < 0) { + if (data.fd < 0) { ALOGE("map ioctl returned negative fd\n"); return -EINVAL; } - *ptr = mmap(NULL, length, prot, flags, *map_fd, offset); - if (*ptr == MAP_FAILED) { + tmp_ptr = mmap(NULL, length, prot, flags, data.fd, offset); + if (tmp_ptr == MAP_FAILED) { ALOGE("mmap failed: %s\n", strerror(errno)); return -errno; } + *map_fd = data.fd; + *ptr = tmp_ptr; return ret; } @@ -129,11 +131,11 @@ int ion_share(int fd, ion_user_handle_t handle, int *share_fd) ret = ion_ioctl(fd, ION_IOC_SHARE, &data); if (ret < 0) return ret; - *share_fd = data.fd; - if (*share_fd < 0) { + if (data.fd < 0) { ALOGE("share ioctl returned negative fd\n"); return -EINVAL; } + *share_fd = data.fd; return ret; } diff --git a/libnetutils/dhcp_utils.c b/libnetutils/dhcp_utils.c index 70e37c676..c6b9fe491 100644 --- a/libnetutils/dhcp_utils.c +++ b/libnetutils/dhcp_utils.c @@ -1,16 +1,16 @@ /* * Copyright 2008, The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 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 + * 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 + * 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. */ @@ -33,7 +33,7 @@ static const char DHCP_CONFIG_PATH[] = "/system/etc/dhcpcd/dhcpcd.conf"; static const int NAP_TIME = 200; /* wait for 200ms at a time */ /* when polling for property values */ static const char DAEMON_NAME_RENEW[] = "iprenew"; -static char errmsg[100]; +static char errmsg[100] = "\0"; /* interface length for dhcpcd daemon start (dhcpcd_<interface> as defined in init.rc file) * or for filling up system properties dhcpcd.<interface>.ipaddress, dhcpcd.<interface>.dns1 * and other properties on a successful bind @@ -74,7 +74,7 @@ static int wait_for_property(const char *name, const char *desired_value, int ma while (maxnaps-- >= 0) { if (property_get(name, value, NULL)) { - if (desired_value == NULL || + if (desired_value == NULL || strcmp(value, desired_value) == 0) { return 0; } @@ -169,6 +169,47 @@ static int fill_ip_info(const char *interface, } /* + * Get any available DHCP results. + */ +int dhcp_get_results(const char *interface, + char *ipaddr, + char *gateway, + uint32_t *prefixLength, + char *dns[], + char *server, + uint32_t *lease, + char *vendorInfo, + char *domain, + char *mtu) +{ + char result_prop_name[PROPERTY_KEY_MAX]; + char prop_value[PROPERTY_VALUE_MAX]; + + /* Interface name after converting p2p0-p2p0-X to p2p to reuse system properties */ + char p2p_interface[MAX_INTERFACE_LENGTH]; + get_p2p_interface_replacement(interface, p2p_interface); + snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result", + DHCP_PROP_NAME_PREFIX, + p2p_interface); + + memset(prop_value, '\0', PROPERTY_VALUE_MAX); + if (!property_get(result_prop_name, prop_value, NULL)) { + snprintf(errmsg, sizeof(errmsg), "%s", "DHCP result property was not set"); + return -1; + } + if (strcmp(prop_value, "ok") == 0) { + if (fill_ip_info(interface, ipaddr, gateway, prefixLength, dns, + server, lease, vendorInfo, domain, mtu) == -1) { + return -1; + } + return 0; + } else { + snprintf(errmsg, sizeof(errmsg), "DHCP result was %s", prop_value); + return -1; + } +} + +/* * Start the dhcp client daemon, and wait for it to finish * configuring the interface. * @@ -177,16 +218,7 @@ static int fill_ip_info(const char *interface, * Example: * service dhcpcd_<interface> /system/bin/dhcpcd -ABKL -f dhcpcd.conf */ -int dhcp_do_request(const char *interface, - char *ipaddr, - char *gateway, - uint32_t *prefixLength, - char *dns[], - char *server, - uint32_t *lease, - char *vendorInfo, - char *domain, - char *mtu) +int dhcp_start(const char *interface) { char result_prop_name[PROPERTY_KEY_MAX]; char daemon_prop_name[PROPERTY_KEY_MAX]; @@ -230,21 +262,7 @@ int dhcp_do_request(const char *interface, return -1; } - if (!property_get(result_prop_name, prop_value, NULL)) { - /* shouldn't ever happen, given the success of wait_for_property() */ - snprintf(errmsg, sizeof(errmsg), "%s", "DHCP result property was not set"); - return -1; - } - if (strcmp(prop_value, "ok") == 0) { - if (fill_ip_info(interface, ipaddr, gateway, prefixLength, dns, - server, lease, vendorInfo, domain, mtu) == -1) { - return -1; - } - return 0; - } else { - snprintf(errmsg, sizeof(errmsg), "DHCP result was %s", prop_value); - return -1; - } + return 0; } /** @@ -320,16 +338,7 @@ char *dhcp_get_errmsg() { * service iprenew_<interface> /system/bin/dhcpcd -n * */ -int dhcp_do_request_renew(const char *interface, - char *ipaddr, - char *gateway, - uint32_t *prefixLength, - char *dns[], - char *server, - uint32_t *lease, - char *vendorInfo, - char *domain, - char *mtu) +int dhcp_start_renew(const char *interface) { char result_prop_name[PROPERTY_KEY_MAX]; char prop_value[PROPERTY_VALUE_MAX] = {'\0'}; @@ -359,16 +368,5 @@ int dhcp_do_request_renew(const char *interface, return -1; } - if (!property_get(result_prop_name, prop_value, NULL)) { - /* shouldn't ever happen, given the success of wait_for_property() */ - snprintf(errmsg, sizeof(errmsg), "%s", "DHCP Renew result property was not set"); - return -1; - } - if (strcmp(prop_value, "ok") == 0) { - return fill_ip_info(interface, ipaddr, gateway, prefixLength, dns, - server, lease, vendorInfo, domain, mtu); - } else { - snprintf(errmsg, sizeof(errmsg), "DHCP Renew result was %s", prop_value); - return -1; - } + return 0; } diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp index a80965fd0..ad0500dab 100644 --- a/libprocessgroup/processgroup.cpp +++ b/libprocessgroup/processgroup.cpp @@ -252,14 +252,15 @@ static int killProcessGroupOnce(uid_t uid, int initialPid, int signal) int killProcessGroup(uid_t uid, int initialPid, int signal) { int processes; - int sleep_us = 100; + const int sleep_us = 5 * 1000; // 5ms int64_t startTime = android::uptimeMillis(); + int retry = 40; while ((processes = killProcessGroupOnce(uid, initialPid, signal)) > 0) { SLOGV("killed %d processes for processgroup %d\n", processes, initialPid); - if (sleep_us < 128000) { + if (retry > 0) { usleep(sleep_us); - sleep_us *= 2; + --retry; } else { SLOGE("failed to kill %d processes for processgroup %d\n", processes, initialPid); diff --git a/libsuspend/autosuspend_autosleep.c b/libsuspend/autosuspend_autosleep.c index 0d31e741e..7262cc7e8 100644 --- a/libsuspend/autosuspend_autosleep.c +++ b/libsuspend/autosuspend_autosleep.c @@ -40,7 +40,7 @@ static int autosuspend_autosleep_enable(void) ALOGV("autosuspend_autosleep_enable\n"); - ret = write(autosleep_fd, sleep_state, strlen(sleep_state)); + ret = TEMP_FAILURE_RETRY(write(autosleep_fd, sleep_state, strlen(sleep_state))); if (ret < 0) { strerror_r(errno, buf, sizeof(buf)); ALOGE("Error writing to %s: %s\n", SYS_POWER_AUTOSLEEP, buf); @@ -62,7 +62,7 @@ static int autosuspend_autosleep_disable(void) ALOGV("autosuspend_autosleep_disable\n"); - ret = write(autosleep_fd, on_state, strlen(on_state)); + ret = TEMP_FAILURE_RETRY(write(autosleep_fd, on_state, strlen(on_state))); if (ret < 0) { strerror_r(errno, buf, sizeof(buf)); ALOGE("Error writing to %s: %s\n", SYS_POWER_AUTOSLEEP, buf); @@ -86,7 +86,7 @@ struct autosuspend_ops *autosuspend_autosleep_init(void) { char buf[80]; - autosleep_fd = open(SYS_POWER_AUTOSLEEP, O_WRONLY); + autosleep_fd = TEMP_FAILURE_RETRY(open(SYS_POWER_AUTOSLEEP, O_WRONLY)); if (autosleep_fd < 0) { strerror_r(errno, buf, sizeof(buf)); ALOGE("Error opening %s: %s\n", SYS_POWER_AUTOSLEEP, buf); diff --git a/libsuspend/autosuspend_earlysuspend.c b/libsuspend/autosuspend_earlysuspend.c index 2bece4c3b..3793a699e 100644 --- a/libsuspend/autosuspend_earlysuspend.c +++ b/libsuspend/autosuspend_earlysuspend.c @@ -49,11 +49,9 @@ int wait_for_fb_wake(void) { int err = 0; char buf; - int fd = open(EARLYSUSPEND_WAIT_FOR_FB_WAKE, O_RDONLY, 0); + int fd = TEMP_FAILURE_RETRY(open(EARLYSUSPEND_WAIT_FOR_FB_WAKE, O_RDONLY, 0)); // if the file doesn't exist, the error will be caught in read() below - do { - err = read(fd, &buf, 1); - } while (err < 0 && errno == EINTR); + err = TEMP_FAILURE_RETRY(read(fd, &buf, 1)); ALOGE_IF(err < 0, "*** ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno)); close(fd); @@ -64,11 +62,9 @@ static int wait_for_fb_sleep(void) { int err = 0; char buf; - int fd = open(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, O_RDONLY, 0); + int fd = TEMP_FAILURE_RETRY(open(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, O_RDONLY, 0)); // if the file doesn't exist, the error will be caught in read() below - do { - err = read(fd, &buf, 1); - } while (err < 0 && errno == EINTR); + err = TEMP_FAILURE_RETRY(read(fd, &buf, 1)); ALOGE_IF(err < 0, "*** ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno)); close(fd); @@ -134,7 +130,7 @@ static int autosuspend_earlysuspend_disable(void) ALOGV("autosuspend_earlysuspend_disable\n"); - ret = write(sPowerStatefd, pwr_state_on, strlen(pwr_state_on)); + ret = TEMP_FAILURE_RETRY(write(sPowerStatefd, pwr_state_on, strlen(pwr_state_on))); if (ret < 0) { strerror_r(errno, buf, sizeof(buf)); ALOGE("Error writing to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf); @@ -195,7 +191,7 @@ struct autosuspend_ops *autosuspend_earlysuspend_init(void) char buf[80]; int ret; - sPowerStatefd = open(EARLYSUSPEND_SYS_POWER_STATE, O_RDWR); + sPowerStatefd = TEMP_FAILURE_RETRY(open(EARLYSUSPEND_SYS_POWER_STATE, O_RDWR)); if (sPowerStatefd < 0) { strerror_r(errno, buf, sizeof(buf)); @@ -203,7 +199,7 @@ struct autosuspend_ops *autosuspend_earlysuspend_init(void) return NULL; } - ret = write(sPowerStatefd, "on", 2); + ret = TEMP_FAILURE_RETRY(write(sPowerStatefd, "on", 2)); if (ret < 0) { strerror_r(errno, buf, sizeof(buf)); ALOGW("Error writing 'on' to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf); diff --git a/libsuspend/autosuspend_wakeup_count.c b/libsuspend/autosuspend_wakeup_count.c index 7483a8fb2..23a029027 100644 --- a/libsuspend/autosuspend_wakeup_count.c +++ b/libsuspend/autosuspend_wakeup_count.c @@ -19,6 +19,7 @@ #include <pthread.h> #include <semaphore.h> #include <stddef.h> +#include <stdbool.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> @@ -38,7 +39,7 @@ static int wakeup_count_fd; static pthread_t suspend_thread; static sem_t suspend_lockout; static const char *sleep_state = "mem"; -static void (*wakeup_func)(void) = NULL; +static void (*wakeup_func)(bool success) = NULL; static void *suspend_thread_func(void *arg __attribute__((unused))) { @@ -46,12 +47,14 @@ static void *suspend_thread_func(void *arg __attribute__((unused))) char wakeup_count[20]; int wakeup_count_len; int ret; + bool success; while (1) { usleep(100000); ALOGV("%s: read wakeup_count\n", __func__); lseek(wakeup_count_fd, 0, SEEK_SET); - wakeup_count_len = read(wakeup_count_fd, wakeup_count, sizeof(wakeup_count)); + wakeup_count_len = TEMP_FAILURE_RETRY(read(wakeup_count_fd, wakeup_count, + sizeof(wakeup_count))); if (wakeup_count_len < 0) { strerror_r(errno, buf, sizeof(buf)); ALOGE("Error reading from %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf); @@ -71,22 +74,21 @@ static void *suspend_thread_func(void *arg __attribute__((unused))) continue; } + success = true; ALOGV("%s: write %*s to wakeup_count\n", __func__, wakeup_count_len, wakeup_count); - ret = write(wakeup_count_fd, wakeup_count, wakeup_count_len); + ret = TEMP_FAILURE_RETRY(write(wakeup_count_fd, wakeup_count, wakeup_count_len)); if (ret < 0) { strerror_r(errno, buf, sizeof(buf)); ALOGE("Error writing to %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf); } else { ALOGV("%s: write %s to %s\n", __func__, sleep_state, SYS_POWER_STATE); - ret = write(state_fd, sleep_state, strlen(sleep_state)); + ret = TEMP_FAILURE_RETRY(write(state_fd, sleep_state, strlen(sleep_state))); if (ret < 0) { - strerror_r(errno, buf, sizeof(buf)); - ALOGE("Error writing to %s: %s\n", SYS_POWER_STATE, buf); - } else { - void (*func)(void) = wakeup_func; - if (func != NULL) { - (*func)(); - } + success = false; + } + void (*func)(bool success) = wakeup_func; + if (func != NULL) { + (*func)(success); } } @@ -138,7 +140,7 @@ static int autosuspend_wakeup_count_disable(void) return ret; } -void set_wakeup_callback(void (*func)(void)) +void set_wakeup_callback(void (*func)(bool success)) { if (wakeup_func != NULL) { ALOGE("Duplicate wakeup callback applied, keeping original"); @@ -157,14 +159,14 @@ struct autosuspend_ops *autosuspend_wakeup_count_init(void) int ret; char buf[80]; - state_fd = open(SYS_POWER_STATE, O_RDWR); + state_fd = TEMP_FAILURE_RETRY(open(SYS_POWER_STATE, O_RDWR)); if (state_fd < 0) { strerror_r(errno, buf, sizeof(buf)); ALOGE("Error opening %s: %s\n", SYS_POWER_STATE, buf); goto err_open_state; } - wakeup_count_fd = open(SYS_POWER_WAKEUP_COUNT, O_RDWR); + wakeup_count_fd = TEMP_FAILURE_RETRY(open(SYS_POWER_WAKEUP_COUNT, O_RDWR)); if (wakeup_count_fd < 0) { strerror_r(errno, buf, sizeof(buf)); ALOGE("Error opening %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf); diff --git a/libsuspend/include/suspend/autosuspend.h b/libsuspend/include/suspend/autosuspend.h index 10e3d27d2..59188a804 100644 --- a/libsuspend/include/suspend/autosuspend.h +++ b/libsuspend/include/suspend/autosuspend.h @@ -18,6 +18,7 @@ #define _LIBSUSPEND_AUTOSUSPEND_H_ #include <sys/cdefs.h> +#include <stdbool.h> __BEGIN_DECLS @@ -46,9 +47,11 @@ int autosuspend_disable(void); /* * set_wakeup_callback * - * Set a function to be called each time the device wakes up from suspend. + * Set a function to be called each time the device returns from suspend. + * success is true if the suspend was sucessful and false if the suspend + * aborted due to some reason. */ -void set_wakeup_callback(void (*func)(void)); +void set_wakeup_callback(void (*func)(bool success)); __END_DECLS diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp index 909df8647..23dcd6273 100644 --- a/libsysutils/src/NetlinkEvent.cpp +++ b/libsysutils/src/NetlinkEvent.cpp @@ -47,20 +47,8 @@ const int LOCAL_NFLOG_PACKET = NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET; #include <netlink/handlers.h> #include <netlink/msg.h> -const int NetlinkEvent::NlActionUnknown = 0; -const int NetlinkEvent::NlActionAdd = 1; -const int NetlinkEvent::NlActionRemove = 2; -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; -const int NetlinkEvent::NlActionRdnss = 8; -const int NetlinkEvent::NlActionRouteUpdated = 9; -const int NetlinkEvent::NlActionRouteRemoved = 10; - NetlinkEvent::NetlinkEvent() { - mAction = NlActionUnknown; + mAction = Action::kUnknown; memset(mParams, 0, sizeof(mParams)); mPath = NULL; mSubsystem = NULL; @@ -154,8 +142,8 @@ bool NetlinkEvent::parseIfInfoMessage(const struct nlmsghdr *nh) { switch(rta->rta_type) { case IFLA_IFNAME: asprintf(&mParams[0], "INTERFACE=%s", (char *) RTA_DATA(rta)); - mAction = (ifi->ifi_flags & IFF_LOWER_UP) ? NlActionLinkUp : - NlActionLinkDown; + mAction = (ifi->ifi_flags & IFF_LOWER_UP) ? Action::kLinkUp : + Action::kLinkDown; mSubsystem = strdup("net"); return true; } @@ -244,8 +232,8 @@ bool NetlinkEvent::parseIfAddrMessage(const struct nlmsghdr *nh) { } // Fill in netlink event information. - mAction = (type == RTM_NEWADDR) ? NlActionAddressUpdated : - NlActionAddressRemoved; + mAction = (type == RTM_NEWADDR) ? Action::kAddressUpdated : + Action::kAddressRemoved; mSubsystem = strdup("net"); asprintf(&mParams[0], "ADDRESS=%s/%d", addrstr, ifaddr->ifa_prefixlen); @@ -276,7 +264,7 @@ bool NetlinkEvent::parseUlogPacketMessage(const struct nlmsghdr *nh) { asprintf(&mParams[0], "ALERT_NAME=%s", pm->prefix); asprintf(&mParams[1], "INTERFACE=%s", devname); mSubsystem = strdup("qlog"); - mAction = NlActionChange; + mAction = Action::kChange; return true; } @@ -311,7 +299,7 @@ bool NetlinkEvent::parseNfPacketMessage(struct nlmsghdr *nh) { asprintf(&mParams[0], "UID=%d", uid); mParams[1] = hex; mSubsystem = strdup("strict"); - mAction = NlActionChange; + mAction = Action::kChange; return true; } @@ -397,8 +385,8 @@ bool NetlinkEvent::parseRtMessage(const struct nlmsghdr *nh) { return false; // Fill in netlink event information. - mAction = (type == RTM_NEWROUTE) ? NlActionRouteUpdated : - NlActionRouteRemoved; + mAction = (type == RTM_NEWROUTE) ? Action::kRouteUpdated : + Action::kRouteRemoved; mSubsystem = strdup("net"); asprintf(&mParams[0], "ROUTE=%s/%d", dst, prefixLength); asprintf(&mParams[1], "GATEWAY=%s", (*gw) ? gw : ""); @@ -467,25 +455,27 @@ bool NetlinkEvent::parseNdUserOptMessage(const struct nlmsghdr *nh) { SLOGE("Invalid optlen %d for RDNSS option\n", optlen); return false; } - int numaddrs = (optlen - 1) / 2; + const int numaddrs = (optlen - 1) / 2; // Find the lifetime. struct nd_opt_rdnss *rndss_opt = (struct nd_opt_rdnss *) opthdr; - uint32_t lifetime = ntohl(rndss_opt->nd_opt_rdnss_lifetime); + const uint32_t lifetime = ntohl(rndss_opt->nd_opt_rdnss_lifetime); // Construct "SERVERS=<comma-separated string of DNS addresses>". - // Reserve (INET6_ADDRSTRLEN + 1) chars for each address: all but the - // the last address are followed by ','; the last is followed by '\0'. static const char kServerTag[] = "SERVERS="; - static const int kTagLength = sizeof(kServerTag) - 1; - int bufsize = kTagLength + numaddrs * (INET6_ADDRSTRLEN + 1); + static const size_t kTagLength = strlen(kServerTag); + // Reserve sufficient space for an IPv6 link-local address: all but the + // last address are followed by ','; the last is followed by '\0'. + static const size_t kMaxSingleAddressLength = + INET6_ADDRSTRLEN + strlen("%") + IFNAMSIZ + strlen(","); + const size_t bufsize = kTagLength + numaddrs * kMaxSingleAddressLength; char *buf = (char *) malloc(bufsize); if (!buf) { SLOGE("RDNSS option: out of memory\n"); return false; } strcpy(buf, kServerTag); - int pos = kTagLength; + size_t pos = kTagLength; struct in6_addr *addrs = (struct in6_addr *) (rndss_opt + 1); for (int i = 0; i < numaddrs; i++) { @@ -494,10 +484,14 @@ bool NetlinkEvent::parseNdUserOptMessage(const struct nlmsghdr *nh) { } inet_ntop(AF_INET6, addrs + i, buf + pos, bufsize - pos); pos += strlen(buf + pos); + if (IN6_IS_ADDR_LINKLOCAL(addrs + i)) { + buf[pos++] = '%'; + pos += strlcpy(buf + pos, ifname, bufsize - pos); + } } buf[pos] = '\0'; - mAction = NlActionRdnss; + mAction = Action::kRdnss; mSubsystem = strdup("net"); asprintf(&mParams[0], "INTERFACE=%s", ifname); asprintf(&mParams[1], "LIFETIME=%u", lifetime); @@ -617,11 +611,11 @@ bool NetlinkEvent::parseAsciiNetlinkMessage(char *buffer, int size) { const char* a; if ((a = HAS_CONST_PREFIX(s, end, "ACTION=")) != NULL) { if (!strcmp(a, "add")) - mAction = NlActionAdd; + mAction = Action::kAdd; else if (!strcmp(a, "remove")) - mAction = NlActionRemove; + mAction = Action::kRemove; else if (!strcmp(a, "change")) - mAction = NlActionChange; + mAction = Action::kChange; } else if ((a = HAS_CONST_PREFIX(s, end, "SEQNUM=")) != NULL) { mSeq = atoi(a); } else if ((a = HAS_CONST_PREFIX(s, end, "SUBSYSTEM=")) != NULL) { diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp index 527a6a0a3..3011ed7a8 100644 --- a/libsysutils/src/SocketListener.cpp +++ b/libsysutils/src/SocketListener.cpp @@ -86,6 +86,7 @@ int SocketListener::startListener(int backlog) { return -1; } SLOGV("got mSock = %d for %s", mSock, mSocketName); + fcntl(mSock, F_SETFD, FD_CLOEXEC); } if (mListen && listen(mSock, backlog) < 0) { @@ -212,6 +213,7 @@ void SocketListener::runListener() { sleep(1); continue; } + fcntl(c, F_SETFD, FD_CLOEXEC); pthread_mutex_lock(&mClientsLock); mClients->push_back(new SocketClient(c, true, mUseCmdNum)); pthread_mutex_unlock(&mClientsLock); diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c index 684f40133..b8e32154f 100644 --- a/libusbhost/usbhost.c +++ b/libusbhost/usbhost.c @@ -56,6 +56,9 @@ #define USB_FS_ID_SCANNER USB_FS_DIR "/%d/%d" #define USB_FS_ID_FORMAT USB_FS_DIR "/%03d/%03d" +// Some devices fail to send string descriptors if we attempt reading > 255 bytes +#define MAX_STRING_DESCRIPTOR_LENGTH 255 + // From drivers/usb/core/devio.c // I don't know why this isn't in a kernel header #define MAX_USBFS_BUFFER_SIZE 16384 @@ -449,8 +452,8 @@ const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_ char* usb_device_get_string(struct usb_device *device, int id) { char string[256]; - __u16 buffer[128]; - __u16 languages[128]; + __u16 buffer[MAX_STRING_DESCRIPTOR_LENGTH / sizeof(__u16)]; + __u16 languages[MAX_STRING_DESCRIPTOR_LENGTH / sizeof(__u16)]; int i, result; int languageCount = 0; @@ -498,6 +501,12 @@ char* usb_device_get_product_name(struct usb_device *device) return usb_device_get_string(device, desc->iProduct); } +int usb_device_get_version(struct usb_device *device) +{ + struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc; + return desc->bcdUSB; +} + char* usb_device_get_serial(struct usb_device *device) { struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc; diff --git a/libutils/Android.mk b/libutils/Android.mk index ae12cb561..bae16bba2 100644 --- a/libutils/Android.mk +++ b/libutils/Android.mk @@ -15,11 +15,9 @@ LOCAL_PATH:= $(call my-dir) commonSources:= \ - BlobCache.cpp \ CallStack.cpp \ FileMap.cpp \ JenkinsHash.cpp \ - LinearAllocator.cpp \ LinearTransform.cpp \ Log.cpp \ NativeHandle.cpp \ @@ -66,6 +64,7 @@ include $(CLEAR_VARS) # we have the common sources, plus some device-specific stuff LOCAL_SRC_FILES:= \ $(commonSources) \ + BlobCache.cpp \ Looper.cpp \ Trace.cpp @@ -75,7 +74,8 @@ endif LOCAL_CFLAGS += -Werror LOCAL_STATIC_LIBRARIES := \ - libcutils + libcutils \ + libc LOCAL_SHARED_LIBRARIES := \ libbacktrace \ @@ -103,6 +103,22 @@ LOCAL_CLANG := true LOCAL_SANITIZE := integer include $(BUILD_SHARED_LIBRARY) +# Include subdirectory makefiles +# ============================================================ + +include $(CLEAR_VARS) +LOCAL_MODULE := SharedBufferTest +LOCAL_STATIC_LIBRARIES := libutils libcutils +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_SRC_FILES := SharedBufferTest.cpp +include $(BUILD_NATIVE_TEST) + +include $(CLEAR_VARS) +LOCAL_MODULE := SharedBufferTest +LOCAL_STATIC_LIBRARIES := libutils libcutils +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_SRC_FILES := SharedBufferTest.cpp +include $(BUILD_HOST_NATIVE_TEST) # Build the tests in the tests/ subdirectory. include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/libutils/BlobCache.cpp b/libutils/BlobCache.cpp index 0ea09cfa8..126995b8c 100644 --- a/libutils/BlobCache.cpp +++ b/libutils/BlobCache.cpp @@ -25,13 +25,15 @@ #include <utils/Errors.h> #include <utils/Log.h> +#include <cutils/properties.h> + namespace android { // BlobCache::Header::mMagicNumber value static const uint32_t blobCacheMagic = ('_' << 24) + ('B' << 16) + ('b' << 8) + '$'; // BlobCache::Header::mBlobCacheVersion value -static const uint32_t blobCacheVersion = 2; +static const uint32_t blobCacheVersion = 3; // BlobCache::Header::mDeviceVersion value static const uint32_t blobCacheDeviceVersion = 1; @@ -165,7 +167,7 @@ static inline size_t align4(size_t size) { } size_t BlobCache::getFlattenedSize() const { - size_t size = align4(sizeof(Header)); + size_t size = align4(sizeof(Header) + PROPERTY_VALUE_MAX); for (size_t i = 0; i < mCacheEntries.size(); i++) { const CacheEntry& e(mCacheEntries[i]); sp<Blob> keyBlob = e.getKey(); @@ -187,10 +189,13 @@ status_t BlobCache::flatten(void* buffer, size_t size) const { header->mBlobCacheVersion = blobCacheVersion; header->mDeviceVersion = blobCacheDeviceVersion; header->mNumEntries = mCacheEntries.size(); + char buildId[PROPERTY_VALUE_MAX]; + header->mBuildIdLength = property_get("ro.build.id", buildId, ""); + memcpy(header->mBuildId, buildId, header->mBuildIdLength); // Write cache entries uint8_t* byteBuffer = reinterpret_cast<uint8_t*>(buffer); - off_t byteOffset = align4(sizeof(Header)); + off_t byteOffset = align4(sizeof(Header) + header->mBuildIdLength); for (size_t i = 0; i < mCacheEntries.size(); i++) { const CacheEntry& e(mCacheEntries[i]); sp<Blob> keyBlob = e.getKey(); @@ -239,15 +244,19 @@ status_t BlobCache::unflatten(void const* buffer, size_t size) { ALOGE("unflatten: bad magic number: %" PRIu32, header->mMagicNumber); return BAD_VALUE; } + char buildId[PROPERTY_VALUE_MAX]; + int len = property_get("ro.build.id", buildId, ""); if (header->mBlobCacheVersion != blobCacheVersion || - header->mDeviceVersion != blobCacheDeviceVersion) { + header->mDeviceVersion != blobCacheDeviceVersion || + len != header->mBuildIdLength || + strncmp(buildId, header->mBuildId, len)) { // We treat version mismatches as an empty cache. return OK; } // Read cache entries const uint8_t* byteBuffer = reinterpret_cast<const uint8_t*>(buffer); - off_t byteOffset = align4(sizeof(Header)); + off_t byteOffset = align4(sizeof(Header) + header->mBuildIdLength); size_t numEntries = header->mNumEntries; for (size_t i = 0; i < numEntries; i++) { if (byteOffset + sizeof(EntryHeader) > size) { diff --git a/libutils/LinearAllocator.cpp b/libutils/LinearAllocator.cpp deleted file mode 100644 index 8b90696ff..000000000 --- a/libutils/LinearAllocator.cpp +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright 2012, The Android Open Source Project - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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. - */ - -#define LOG_TAG "LinearAllocator" -#define LOG_NDEBUG 1 - -#include <stdlib.h> -#include <utils/LinearAllocator.h> -#include <utils/Log.h> - - -// The ideal size of a page allocation (these need to be multiples of 8) -#define INITIAL_PAGE_SIZE ((size_t)4096) // 4kb -#define MAX_PAGE_SIZE ((size_t)131072) // 128kb - -// The maximum amount of wasted space we can have per page -// Allocations exceeding this will have their own dedicated page -// If this is too low, we will malloc too much -// Too high, and we may waste too much space -// Must be smaller than INITIAL_PAGE_SIZE -#define MAX_WASTE_SIZE ((size_t)1024) - -#if ALIGN_DOUBLE -#define ALIGN_SZ (sizeof(double)) -#else -#define ALIGN_SZ (sizeof(int)) -#endif - -#define ALIGN(x) ((x + ALIGN_SZ - 1 ) & ~(ALIGN_SZ - 1)) -#define ALIGN_PTR(p) ((void*)(ALIGN((size_t)p))) - -#if LOG_NDEBUG -#define ADD_ALLOCATION(size) -#define RM_ALLOCATION(size) -#else -#include <utils/Thread.h> -#include <utils/Timers.h> -static size_t s_totalAllocations = 0; -static nsecs_t s_nextLog = 0; -static android::Mutex s_mutex; - -static void _logUsageLocked() { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - if (now > s_nextLog) { - s_nextLog = now + milliseconds_to_nanoseconds(10); - ALOGV("Total memory usage: %zu kb", s_totalAllocations / 1024); - } -} - -static void _addAllocation(size_t size) { - android::AutoMutex lock(s_mutex); - s_totalAllocations += size; - _logUsageLocked(); -} - -#define ADD_ALLOCATION(size) _addAllocation(size); -#define RM_ALLOCATION(size) _addAllocation(-size); -#endif - -#define min(x,y) (((x) < (y)) ? (x) : (y)) - -namespace android { - -class LinearAllocator::Page { -public: - Page* next() { return mNextPage; } - void setNext(Page* next) { mNextPage = next; } - - Page() - : mNextPage(0) - {} - - void* operator new(size_t /*size*/, void* buf) { return buf; } - - void* start() { - return (void*) (((size_t)this) + sizeof(Page)); - } - - void* end(int pageSize) { - return (void*) (((size_t)start()) + pageSize); - } - -private: - Page(const Page& /*other*/) {} - Page* mNextPage; -}; - -LinearAllocator::LinearAllocator() - : mPageSize(INITIAL_PAGE_SIZE) - , mMaxAllocSize(MAX_WASTE_SIZE) - , mNext(0) - , mCurrentPage(0) - , mPages(0) - , mTotalAllocated(0) - , mWastedSpace(0) - , mPageCount(0) - , mDedicatedPageCount(0) {} - -LinearAllocator::~LinearAllocator(void) { - Page* p = mPages; - while (p) { - Page* next = p->next(); - p->~Page(); - free(p); - RM_ALLOCATION(mPageSize); - p = next; - } -} - -void* LinearAllocator::start(Page* p) { - return ALIGN_PTR(((size_t*)p) + sizeof(Page)); -} - -void* LinearAllocator::end(Page* p) { - return ((char*)p) + mPageSize; -} - -bool LinearAllocator::fitsInCurrentPage(size_t size) { - return mNext && ((char*)mNext + size) <= end(mCurrentPage); -} - -void LinearAllocator::ensureNext(size_t size) { - if (fitsInCurrentPage(size)) return; - - if (mCurrentPage && mPageSize < MAX_PAGE_SIZE) { - mPageSize = min(MAX_PAGE_SIZE, mPageSize * 2); - mPageSize = ALIGN(mPageSize); - } - mWastedSpace += mPageSize; - Page* p = newPage(mPageSize); - if (mCurrentPage) { - mCurrentPage->setNext(p); - } - mCurrentPage = p; - if (!mPages) { - mPages = mCurrentPage; - } - mNext = start(mCurrentPage); -} - -void* LinearAllocator::alloc(size_t size) { - size = ALIGN(size); - if (size > mMaxAllocSize && !fitsInCurrentPage(size)) { - ALOGV("Exceeded max size %zu > %zu", size, mMaxAllocSize); - // Allocation is too large, create a dedicated page for the allocation - Page* page = newPage(size); - mDedicatedPageCount++; - page->setNext(mPages); - mPages = page; - if (!mCurrentPage) - mCurrentPage = mPages; - return start(page); - } - ensureNext(size); - void* ptr = mNext; - mNext = ((char*)mNext) + size; - mWastedSpace -= size; - return ptr; -} - -void LinearAllocator::rewindIfLastAlloc(void* ptr, size_t allocSize) { - // Don't bother rewinding across pages - allocSize = ALIGN(allocSize); - if (ptr >= start(mCurrentPage) && ptr < end(mCurrentPage) - && ptr == ((char*)mNext - allocSize)) { - mTotalAllocated -= allocSize; - mWastedSpace += allocSize; - mNext = ptr; - } -} - -LinearAllocator::Page* LinearAllocator::newPage(size_t pageSize) { - pageSize = ALIGN(pageSize + sizeof(LinearAllocator::Page)); - ADD_ALLOCATION(pageSize); - mTotalAllocated += pageSize; - mPageCount++; - void* buf = malloc(pageSize); - return new (buf) Page(); -} - -static const char* toSize(size_t value, float& result) { - if (value < 2000) { - result = value; - return "B"; - } - if (value < 2000000) { - result = value / 1024.0f; - return "KB"; - } - result = value / 1048576.0f; - return "MB"; -} - -void LinearAllocator::dumpMemoryStats(const char* prefix) { - float prettySize; - const char* prettySuffix; - prettySuffix = toSize(mTotalAllocated, prettySize); - ALOGD("%sTotal allocated: %.2f%s", prefix, prettySize, prettySuffix); - prettySuffix = toSize(mWastedSpace, prettySize); - ALOGD("%sWasted space: %.2f%s (%.1f%%)", prefix, prettySize, prettySuffix, - (float) mWastedSpace / (float) mTotalAllocated * 100.0f); - ALOGD("%sPages %zu (dedicated %zu)", prefix, mPageCount, mDedicatedPageCount); -} - -}; // namespace android diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp index 20c1e927f..b14884b33 100644 --- a/libutils/Looper.cpp +++ b/libutils/Looper.cpp @@ -20,7 +20,9 @@ #include <errno.h> #include <fcntl.h> #include <limits.h> +#include <inttypes.h> #include <string.h> +#include <sys/eventfd.h> #include <unistd.h> @@ -70,41 +72,21 @@ static pthread_key_t gTLSKey = 0; Looper::Looper(bool allowNonCallbacks) : mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false), - mResponseIndex(0), mNextMessageUptime(LLONG_MAX) { - int wakeFds[2]; - int result = pipe(wakeFds); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe: %s", strerror(errno)); - - mWakeReadPipeFd = wakeFds[0]; - mWakeWritePipeFd = wakeFds[1]; - - result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking: %s", - strerror(errno)); - - result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking: %s", - strerror(errno)); - - mIdling = false; - - // Allocate the epoll instance and register the wake pipe. - mEpollFd = epoll_create(EPOLL_SIZE_HINT); - LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno)); - - struct epoll_event eventItem; - memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union - eventItem.events = EPOLLIN; - eventItem.data.fd = mWakeReadPipeFd; - result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance: %s", - strerror(errno)); + mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false), + mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) { + mWakeEventFd = eventfd(0, EFD_NONBLOCK); + LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd: %s", + strerror(errno)); + + AutoMutex _l(mLock); + rebuildEpollLocked(); } Looper::~Looper() { - close(mWakeReadPipeFd); - close(mWakeWritePipeFd); - close(mEpollFd); + close(mWakeEventFd); + if (mEpollFd >= 0) { + close(mEpollFd); + } } void Looper::initTLSKey() { @@ -158,6 +140,50 @@ bool Looper::getAllowNonCallbacks() const { return mAllowNonCallbacks; } +void Looper::rebuildEpollLocked() { + // Close old epoll instance if we have one. + if (mEpollFd >= 0) { +#if DEBUG_CALLBACKS + ALOGD("%p ~ rebuildEpollLocked - rebuilding epoll set", this); +#endif + close(mEpollFd); + } + + // Allocate the new epoll instance and register the wake pipe. + mEpollFd = epoll_create(EPOLL_SIZE_HINT); + LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno)); + + struct epoll_event eventItem; + memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union + eventItem.events = EPOLLIN; + eventItem.data.fd = mWakeEventFd; + int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s", + strerror(errno)); + + for (size_t i = 0; i < mRequests.size(); i++) { + const Request& request = mRequests.valueAt(i); + struct epoll_event eventItem; + request.initEventItem(&eventItem); + + int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem); + if (epollResult < 0) { + ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s", + request.fd, strerror(errno)); + } + } +} + +void Looper::scheduleEpollRebuildLocked() { + if (!mEpollRebuildRequired) { +#if DEBUG_CALLBACKS + ALOGD("%p ~ scheduleEpollRebuildLocked - scheduling epoll set rebuild", this); +#endif + mEpollRebuildRequired = true; + wake(); + } +} + int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) { int result = 0; for (;;) { @@ -208,7 +234,7 @@ int Looper::pollInner(int timeoutMillis) { timeoutMillis = messageTimeoutMillis; } #if DEBUG_POLL_AND_WAKE - ALOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d", + ALOGD("%p ~ pollOnce - next message in %" PRId64 "ns, adjusted timeout: timeoutMillis=%d", this, mNextMessageUptime - now, timeoutMillis); #endif } @@ -219,17 +245,24 @@ int Looper::pollInner(int timeoutMillis) { mResponseIndex = 0; // We are about to idle. - mIdling = true; + mPolling = true; struct epoll_event eventItems[EPOLL_MAX_EVENTS]; int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); // No longer idling. - mIdling = false; + mPolling = false; // Acquire lock. mLock.lock(); + // Rebuild epoll set if needed. + if (mEpollRebuildRequired) { + mEpollRebuildRequired = false; + rebuildEpollLocked(); + goto Done; + } + // Check for poll error. if (eventCount < 0) { if (errno == EINTR) { @@ -257,11 +290,11 @@ int Looper::pollInner(int timeoutMillis) { for (int i = 0; i < eventCount; i++) { int fd = eventItems[i].data.fd; uint32_t epollEvents = eventItems[i].events; - if (fd == mWakeReadPipeFd) { + if (fd == mWakeEventFd) { if (epollEvents & EPOLLIN) { awoken(); } else { - ALOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents); + ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents); } } else { ssize_t requestIndex = mRequests.indexOfKey(fd); @@ -328,10 +361,14 @@ Done: ; ALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p", this, response.request.callback.get(), fd, events, data); #endif + // Invoke the callback. Note that the file descriptor may be closed by + // the callback (and potentially even reused) before the function returns so + // we need to be a little careful when removing the file descriptor afterwards. int callbackResult = response.request.callback->handleEvent(fd, events, data); if (callbackResult == 0) { - removeFd(fd); + removeFd(fd, response.request.seq); } + // Clear the callback reference in the response structure promptly because we // will not clear the response vector itself until the next poll. response.request.callback.clear(); @@ -372,12 +409,9 @@ void Looper::wake() { ALOGD("%p ~ wake", this); #endif - ssize_t nWrite; - do { - nWrite = write(mWakeWritePipeFd, "W", 1); - } while (nWrite == -1 && errno == EINTR); - - if (nWrite != 1) { + uint64_t inc = 1; + ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t))); + if (nWrite != sizeof(uint64_t)) { if (errno != EAGAIN) { ALOGW("Could not write wake signal: %s", strerror(errno)); } @@ -389,11 +423,8 @@ void Looper::awoken() { ALOGD("%p ~ awoken", this); #endif - char buffer[16]; - ssize_t nRead; - do { - nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); - } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer)); + uint64_t counter; + TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t))); } void Looper::pushResponse(int events, const Request& request) { @@ -427,23 +458,20 @@ int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callb ident = POLL_CALLBACK; } - int epollEvents = 0; - if (events & EVENT_INPUT) epollEvents |= EPOLLIN; - if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT; - { // acquire lock AutoMutex _l(mLock); Request request; request.fd = fd; request.ident = ident; + request.events = events; + request.seq = mNextRequestSeq++; request.callback = callback; request.data = data; + if (mNextRequestSeq == -1) mNextRequestSeq = 0; // reserve sequence number -1 struct epoll_event eventItem; - memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union - eventItem.events = epollEvents; - eventItem.data.fd = fd; + request.initEventItem(&eventItem); ssize_t requestIndex = mRequests.indexOfKey(fd); if (requestIndex < 0) { @@ -456,8 +484,36 @@ int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callb } else { int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem); if (epollResult < 0) { - ALOGE("Error modifying epoll events for fd %d: %s", fd, strerror(errno)); - return -1; + if (errno == ENOENT) { + // Tolerate ENOENT because it means that an older file descriptor was + // closed before its callback was unregistered and meanwhile a new + // file descriptor with the same number has been created and is now + // being registered for the first time. This error may occur naturally + // when a callback has the side-effect of closing the file descriptor + // before returning and unregistering itself. Callback sequence number + // checks further ensure that the race is benign. + // + // Unfortunately due to kernel limitations we need to rebuild the epoll + // set from scratch because it may contain an old file handle that we are + // now unable to remove since its file descriptor is no longer valid. + // No such problem would have occurred if we were using the poll system + // call instead, but that approach carries others disadvantages. +#if DEBUG_CALLBACKS + ALOGD("%p ~ addFd - EPOLL_CTL_MOD failed due to file descriptor " + "being recycled, falling back on EPOLL_CTL_ADD: %s", + this, strerror(errno)); +#endif + epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem); + if (epollResult < 0) { + ALOGE("Error modifying or adding epoll events for fd %d: %s", + fd, strerror(errno)); + return -1; + } + scheduleEpollRebuildLocked(); + } else { + ALOGE("Error modifying epoll events for fd %d: %s", fd, strerror(errno)); + return -1; + } } mRequests.replaceValueAt(requestIndex, request); } @@ -466,8 +522,12 @@ int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callb } int Looper::removeFd(int fd) { + return removeFd(fd, -1); +} + +int Looper::removeFd(int fd, int seq) { #if DEBUG_CALLBACKS - ALOGD("%p ~ removeFd - fd=%d", this, fd); + ALOGD("%p ~ removeFd - fd=%d, seq=%d", this, fd, seq); #endif { // acquire lock @@ -477,13 +537,48 @@ int Looper::removeFd(int fd) { return 0; } - int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL); - if (epollResult < 0) { - ALOGE("Error removing epoll events for fd %d: %s", fd, strerror(errno)); - return -1; + // Check the sequence number if one was given. + if (seq != -1 && mRequests.valueAt(requestIndex).seq != seq) { +#if DEBUG_CALLBACKS + ALOGD("%p ~ removeFd - sequence number mismatch, oldSeq=%d", + this, mRequests.valueAt(requestIndex).seq); +#endif + return 0; } + // Always remove the FD from the request map even if an error occurs while + // updating the epoll set so that we avoid accidentally leaking callbacks. mRequests.removeItemsAt(requestIndex); + + int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL); + if (epollResult < 0) { + if (seq != -1 && (errno == EBADF || errno == ENOENT)) { + // Tolerate EBADF or ENOENT when the sequence number is known because it + // means that the file descriptor was closed before its callback was + // unregistered. This error may occur naturally when a callback has the + // side-effect of closing the file descriptor before returning and + // unregistering itself. + // + // Unfortunately due to kernel limitations we need to rebuild the epoll + // set from scratch because it may contain an old file handle that we are + // now unable to remove since its file descriptor is no longer valid. + // No such problem would have occurred if we were using the poll system + // call instead, but that approach carries others disadvantages. +#if DEBUG_CALLBACKS + ALOGD("%p ~ removeFd - EPOLL_CTL_DEL failed due to file descriptor " + "being closed: %s", this, strerror(errno)); +#endif + scheduleEpollRebuildLocked(); + } else { + // Some other error occurred. This is really weird because it means + // our list of callbacks got out of sync with the epoll set somehow. + // We defensively rebuild the epoll set to avoid getting spurious + // notifications with nowhere to go. + ALOGE("Error removing epoll events for fd %d: %s", fd, strerror(errno)); + scheduleEpollRebuildLocked(); + return -1; + } + } } // release lock return 1; } @@ -502,7 +597,7 @@ void Looper::sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& h void Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler, const Message& message) { #if DEBUG_CALLBACKS - ALOGD("%p ~ sendMessageAtTime - uptime=%lld, handler=%p, what=%d", + ALOGD("%p ~ sendMessageAtTime - uptime=%" PRId64 ", handler=%p, what=%d", this, uptime, handler.get(), message.what); #endif @@ -568,8 +663,18 @@ void Looper::removeMessages(const sp<MessageHandler>& handler, int what) { } // release lock } -bool Looper::isIdling() const { - return mIdling; +bool Looper::isPolling() const { + return mPolling; +} + +void Looper::Request::initEventItem(struct epoll_event* eventItem) const { + int epollEvents = 0; + if (events & EVENT_INPUT) epollEvents |= EPOLLIN; + if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT; + + memset(eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union + eventItem->events = epollEvents; + eventItem->data.fd = fd; } } // namespace android diff --git a/libutils/SharedBuffer.cpp b/libutils/SharedBuffer.cpp index 003e38697..c7dd1ab34 100644 --- a/libutils/SharedBuffer.cpp +++ b/libutils/SharedBuffer.cpp @@ -14,9 +14,12 @@ * limitations under the License. */ +#define __STDC_LIMIT_MACROS +#include <stdint.h> #include <stdlib.h> #include <string.h> +#include <log/log.h> #include <utils/Atomic.h> #include "SharedBuffer.h" @@ -27,6 +30,11 @@ namespace android { SharedBuffer* SharedBuffer::alloc(size_t size) { + // Don't overflow if the combined size of the buffer / header is larger than + // size_max. + LOG_ALWAYS_FATAL_IF((size >= (SIZE_MAX - sizeof(SharedBuffer))), + "Invalid buffer size %zu", size); + SharedBuffer* sb = static_cast<SharedBuffer *>(malloc(sizeof(SharedBuffer) + size)); if (sb) { sb->mRefs = 1; @@ -53,7 +61,7 @@ SharedBuffer* SharedBuffer::edit() const memcpy(sb->data(), data(), size()); release(); } - return sb; + return sb; } SharedBuffer* SharedBuffer::editResize(size_t newSize) const @@ -61,6 +69,11 @@ SharedBuffer* SharedBuffer::editResize(size_t newSize) const if (onlyOwner()) { SharedBuffer* buf = const_cast<SharedBuffer*>(this); if (buf->mSize == newSize) return buf; + // Don't overflow if the combined size of the new buffer / header is larger than + // size_max. + LOG_ALWAYS_FATAL_IF((newSize >= (SIZE_MAX - sizeof(SharedBuffer))), + "Invalid buffer size %zu", newSize); + buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize); if (buf != NULL) { buf->mSize = newSize; diff --git a/libutils/SharedBufferTest.cpp b/libutils/SharedBufferTest.cpp new file mode 100644 index 000000000..a0484ffb5 --- /dev/null +++ b/libutils/SharedBufferTest.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 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 __STDC_LIMIT_MACROS + +#include <gtest/gtest.h> + +#include <memory> +#include <stdint.h> + +#include "SharedBuffer.h" + +TEST(SharedBufferTest, TestAlloc) { + EXPECT_DEATH(android::SharedBuffer::alloc(SIZE_MAX), ""); + EXPECT_DEATH(android::SharedBuffer::alloc(SIZE_MAX - sizeof(android::SharedBuffer)), ""); + + // Make sure we don't die here. + // Check that null is returned, as we are asking for the whole address space. + android::SharedBuffer* buf = + android::SharedBuffer::alloc(SIZE_MAX - sizeof(android::SharedBuffer) - 1); + ASSERT_TRUE(NULL == buf); + + buf = android::SharedBuffer::alloc(0); + ASSERT_FALSE(NULL == buf); + ASSERT_EQ(0U, buf->size()); + buf->release(); +} + +TEST(SharedBufferTest, TestEditResize) { + android::SharedBuffer* buf = android::SharedBuffer::alloc(10); + EXPECT_DEATH(buf->editResize(SIZE_MAX - sizeof(android::SharedBuffer)), ""); + buf = android::SharedBuffer::alloc(10); + EXPECT_DEATH(buf->editResize(SIZE_MAX), ""); + + buf = android::SharedBuffer::alloc(10); + // Make sure we don't die here. + // Check that null is returned, as we are asking for the whole address space. + buf = buf->editResize(SIZE_MAX - sizeof(android::SharedBuffer) - 1); + ASSERT_TRUE(NULL == buf); + + buf = android::SharedBuffer::alloc(10); + buf = buf->editResize(0); + ASSERT_EQ(0U, buf->size()); + buf->release(); +} diff --git a/libutils/String8.cpp b/libutils/String8.cpp index 81bfccf79..771d31266 100644 --- a/libutils/String8.cpp +++ b/libutils/String8.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +#define __STDC_LIMIT_MACROS +#include <stdint.h> + #include <utils/String8.h> #include <utils/Compat.h> diff --git a/logd/logd.rc b/logd/logd.rc index 498baecd5..da6a0bcd0 100644 --- a/logd/logd.rc +++ b/logd/logd.rc @@ -3,6 +3,7 @@ service logd /system/bin/logd socket logd stream 0666 logd logd socket logdr seqpacket 0666 logd logd socket logdw dgram 0222 logd logd + group root system service logd-reinit /system/bin/logd --reinit oneshot diff --git a/logd/main.cpp b/logd/main.cpp index cbdf0b65f..d22a7aaae 100644 --- a/logd/main.cpp +++ b/logd/main.cpp @@ -103,6 +103,10 @@ static int drop_privs() { return -1; } + if (setgroups(0, NULL) == -1) { + return -1; + } + if (setgid(AID_LOGD) != 0) { return -1; } diff --git a/logwrapper/logwrap.c b/logwrapper/logwrap.c index 39bc8fd1b..28d6de7fb 100644 --- a/logwrapper/logwrap.c +++ b/logwrapper/logwrap.c @@ -326,7 +326,7 @@ static int parent(const char *tag, int parent_read, pid_t pid, if (log_target & LOG_KLOG) { snprintf(log_info.klog_fmt, sizeof(log_info.klog_fmt), - "<6>%.*s: %%s", MAX_KLOG_TAG, log_info.btag); + "<6>%.*s: %%s\n", MAX_KLOG_TAG, log_info.btag); } if ((log_target & LOG_FILE) && !file_path) { diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in index e8b46eb15..32817fa68 100644 --- a/rootdir/init.environ.rc.in +++ b/rootdir/init.environ.rc.in @@ -5,8 +5,8 @@ on init export ANDROID_ASSETS /system/app export ANDROID_DATA /data export ANDROID_STORAGE /storage + export EXTERNAL_STORAGE /sdcard export ASEC_MOUNTPOINT /mnt/asec - export LOOP_MOUNTPOINT /mnt/obb export BOOTCLASSPATH %BOOTCLASSPATH% export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH% %EXPORT_GLOBAL_ASAN_OPTIONS% diff --git a/rootdir/init.rc b/rootdir/init.rc index dc85d98cd..10635d3d0 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -48,28 +48,33 @@ on init chown root system /sys/fs/cgroup/memory/sw/tasks chmod 0660 /sys/fs/cgroup/memory/sw/tasks + # Mount staging areas for devices managed by vold # See storage config details at http://source.android.com/tech/storage/ - mkdir /mnt/shell 0700 shell shell - mkdir /mnt/media_rw 0700 media_rw media_rw - mkdir /storage 0751 root sdcard_r + mount tmpfs tmpfs /mnt mode=0755,uid=0,gid=1000 + restorecon_recursive /mnt - # Directory for putting things only root should see. mkdir /mnt/secure 0700 root root - - # Directory for staging bindmounts - mkdir /mnt/secure/staging 0700 root root - - # Directory-target for where the secure container - # imagefile directory will be bind-mounted - mkdir /mnt/secure/asec 0700 root root - - # Secure container public mount points. - mkdir /mnt/asec 0700 root system - mount tmpfs tmpfs /mnt/asec mode=0755,gid=1000 - - # Filesystem image public mount points. - mkdir /mnt/obb 0700 root system - mount tmpfs tmpfs /mnt/obb mode=0755,gid=1000 + mkdir /mnt/secure/asec 0700 root root + mkdir /mnt/asec 0755 root system + mkdir /mnt/obb 0755 root system + mkdir /mnt/media_rw 0750 root media_rw + mkdir /mnt/user 0755 root root + mkdir /mnt/user/0 0755 root root + mkdir /mnt/expand 0771 system system + + # Storage views to support runtime permissions + mkdir /storage 0755 root root + mkdir /mnt/runtime 0700 root root + mkdir /mnt/runtime/default 0755 root root + mkdir /mnt/runtime/default/self 0755 root root + mkdir /mnt/runtime/read 0755 root root + mkdir /mnt/runtime/read/self 0755 root root + mkdir /mnt/runtime/write 0755 root root + mkdir /mnt/runtime/write/self 0755 root root + + # Symlink to keep legacy apps working in multi-user world + symlink /storage/self/primary /sdcard + symlink /mnt/user/0/primary /mnt/runtime/default/self/primary # memory control cgroup mkdir /dev/memcg 0700 root system @@ -97,6 +102,10 @@ on init # set fwmark on accepted sockets write /proc/sys/net/ipv4/tcp_fwmark_accept 1 + # disable icmp redirects + write /proc/sys/net/ipv4/conf/all/accept_redirects 0 + write /proc/sys/net/ipv6/conf/all/accept_redirects 0 + # Create cgroup mount points for process groups mkdir /dev/cpuctl mount cgroup none /dev/cpuctl cpu @@ -115,6 +124,28 @@ on init write /dev/cpuctl/bg_non_interactive/cpu.rt_runtime_us 700000 write /dev/cpuctl/bg_non_interactive/cpu.rt_period_us 1000000 + # sets up initial cpusets for ActivityManager + mkdir /dev/cpuset + mount cpuset none /dev/cpuset + mkdir /dev/cpuset/foreground + mkdir /dev/cpuset/background + # this ensures that the cpusets are present and usable, but the device's + # init.rc must actually set the correct cpus + write /dev/cpuset/foreground/cpus 0 + write /dev/cpuset/background/cpus 0 + write /dev/cpuset/foreground/mems 0 + write /dev/cpuset/background/mems 0 + chown system system /dev/cpuset + chown system system /dev/cpuset/foreground + chown system system /dev/cpuset/background + chown system system /dev/cpuset/tasks + chown system system /dev/cpuset/foreground/tasks + chown system system /dev/cpuset/background/tasks + chmod 0664 /dev/cpuset/foreground/tasks + chmod 0664 /dev/cpuset/background/tasks + chmod 0664 /dev/cpuset/tasks + + # qtaguid will limit access to specific data based on group memberships. # net_bw_acct grants impersonation of socket owners. # net_bw_stats grants access to other apps' detailed tagged-socket stats. @@ -178,8 +209,10 @@ on post-fs start logd # once everything is setup, no need to modify / mount rootfs rootfs / ro remount - # mount shared so changes propagate into child namespaces + # Mount shared so changes propagate into child namespaces mount rootfs rootfs / shared rec + # Mount default storage into root namespace + mount none /mnt/runtime/default /storage slave bind rec # We chown/chmod /cache again so because mount is run as root + defaults chown system cache /cache @@ -224,6 +257,8 @@ on post-fs-data start vold installkey /data + # Emulated internal storage area + mkdir /data/media 0770 media_rw media_rw # Start bootcharting as soon as possible after the data partition is # mounted to collect more data. mkdir /data/bootchart 0755 shell shell @@ -234,7 +269,10 @@ on post-fs-data # create basic filesystem structure mkdir /data/misc 01771 system misc - mkdir /data/misc/bluedroid 0770 bluetooth net_bt_stack + mkdir /data/misc/bluedroid 02770 bluetooth net_bt_stack + # Fix the access permissions and group ownership for 'bt_config.conf' + chmod 0660 /data/misc/bluedroid/bt_config.conf + chown bluetooth net_bt_stack /data/misc/bluedroid/bt_config.conf mkdir /data/misc/bluetooth 0770 system system mkdir /data/misc/keystore 0700 keystore keystore mkdir /data/misc/gatekeeper 0700 system system @@ -337,9 +375,9 @@ on boot write /proc/sys/vm/overcommit_memory 1 write /proc/sys/vm/min_free_order_shift 4 chown root system /sys/module/lowmemorykiller/parameters/adj - chmod 0220 /sys/module/lowmemorykiller/parameters/adj + chmod 0664 /sys/module/lowmemorykiller/parameters/adj chown root system /sys/module/lowmemorykiller/parameters/minfree - chmod 0220 /sys/module/lowmemorykiller/parameters/minfree + chmod 0664 /sys/module/lowmemorykiller/parameters/minfree # Tweak background writeout write /proc/sys/vm/dirty_expire_centisecs 200 @@ -478,6 +516,7 @@ service healthd /sbin/healthd class core critical seclabel u:r:healthd:s0 + group root system service console /system/bin/sh class core diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc index 9cf9ed96f..b735dc38a 100644 --- a/rootdir/ueventd.rc +++ b/rootdir/ueventd.rc @@ -95,3 +95,6 @@ subsystem adf /sys/devices/virtual/usb_composite/* enable 0664 root system /sys/devices/system/cpu/cpu* cpufreq/scaling_max_freq 0664 system system /sys/devices/system/cpu/cpu* cpufreq/scaling_min_freq 0664 system system + +# DVB API device nodes +/dev/dvb* 0660 root system diff --git a/run-as/package.c b/run-as/package.c index 9e1f5bb22..aea89e56b 100644 --- a/run-as/package.c +++ b/run-as/package.c @@ -16,6 +16,7 @@ */ #include <errno.h> #include <fcntl.h> +#include <stdio.h> #include <string.h> #include <sys/mman.h> #include <sys/stat.h> @@ -421,7 +422,7 @@ parse_positive_decimal(const char** pp, const char* end) * If the package database is corrupted, return -1 and set errno to EINVAL */ int -get_package_info(const char* pkgName, PackageInfo *info) +get_package_info(const char* pkgName, uid_t userId, PackageInfo *info) { char* buffer; size_t buffer_len; @@ -506,7 +507,20 @@ get_package_info(const char* pkgName, PackageInfo *info) if (q == p) goto BAD_FORMAT; - p = string_copy(info->dataDir, sizeof info->dataDir, p, q - p); + /* If userId == 0 (i.e. user is device owner) we can use dataDir value + * from packages.list, otherwise compose data directory as + * /data/user/$uid/$packageId + */ + if (userId == 0) { + p = string_copy(info->dataDir, sizeof info->dataDir, p, q - p); + } else { + snprintf(info->dataDir, + sizeof info->dataDir, + "/data/user/%d/%s", + userId, + pkgName); + p = q; + } /* skip spaces */ if (parse_spaces(&p, end) < 0) diff --git a/run-as/package.h b/run-as/package.h index 34603c013..eeb59137d 100644 --- a/run-as/package.h +++ b/run-as/package.h @@ -33,9 +33,11 @@ typedef struct { char seinfo[PATH_MAX]; } PackageInfo; -/* see documentation in package.c for these functiosn */ +/* see documentation in package.c for these functions */ -extern int get_package_info(const char* packageName, PackageInfo* info); +extern int get_package_info(const char* packageName, + uid_t userId, + PackageInfo* info); extern int check_data_path(const char* dataDir, uid_t uid); diff --git a/run-as/run-as.c b/run-as/run-as.c index 368b8f1e6..3f32e7d74 100644 --- a/run-as/run-as.c +++ b/run-as/run-as.c @@ -102,13 +102,14 @@ panic(const char* format, ...) static void usage(void) { - panic("Usage:\n " PROGNAME " <package-name> <command> [<args>]\n"); + panic("Usage:\n " PROGNAME " <package-name> [--user <uid>] <command> [<args>]\n"); } int main(int argc, char **argv) { const char* pkgname; - int myuid, uid, gid; + uid_t myuid, uid, gid, userAppId = 0; + int commandArgvOfs = 2, userId = 0; PackageInfo info; struct __user_cap_header_struct capheader; struct __user_cap_data_struct capdata[2]; @@ -136,14 +137,31 @@ int main(int argc, char **argv) panic("Could not set capabilities: %s\n", strerror(errno)); } - /* retrieve package information from system (does setegid) */ pkgname = argv[1]; - if (get_package_info(pkgname, &info) < 0) { + + /* get user_id from command line if provided */ + if ((argc >= 4) && !strcmp(argv[2], "--user")) { + userId = atoi(argv[3]); + if (userId < 0) + panic("Negative user id %d is provided\n", userId); + commandArgvOfs += 2; + } + + /* retrieve package information from system (does setegid) */ + if (get_package_info(pkgname, userId, &info) < 0) { panic("Package '%s' is unknown\n", pkgname); } + /* verify that user id is not too big. */ + if ((UID_MAX - info.uid) / AID_USER < (uid_t)userId) { + panic("User id %d is too big\n", userId); + } + + /* calculate user app ID. */ + userAppId = (AID_USER * userId) + info.uid; + /* reject system packages */ - if (info.uid < AID_APP) { + if (userAppId < AID_APP) { panic("Package '%s' is not an application\n", pkgname); } @@ -153,14 +171,14 @@ int main(int argc, char **argv) } /* check that the data directory path is valid */ - if (check_data_path(info.dataDir, info.uid) < 0) { + if (check_data_path(info.dataDir, userAppId) < 0) { panic("Package '%s' has corrupt installation\n", pkgname); } /* Ensure that we change all real/effective/saved IDs at the * same time to avoid nasty surprises. */ - uid = gid = info.uid; + uid = gid = userAppId; if(setresgid(gid,gid,gid) || setresuid(uid,uid,uid)) { panic("Permission denied\n"); } @@ -181,8 +199,9 @@ int main(int argc, char **argv) } /* User specified command for exec. */ - if ((argc >= 3) && (execvp(argv[2], argv+2) < 0)) { - panic("exec failed for %s: %s\n", argv[2], strerror(errno)); + if ((argc >= commandArgvOfs + 1) && + (execvp(argv[commandArgvOfs], argv+commandArgvOfs) < 0)) { + panic("exec failed for %s: %s\n", argv[commandArgvOfs], strerror(errno)); } /* Default exec shell. */ diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c index 5b657bb2c..d2d231571 100644 --- a/sdcard/sdcard.c +++ b/sdcard/sdcard.c @@ -74,22 +74,6 @@ * requiring any additional GIDs. * - Separate permissions for protecting directories like Pictures and Music. * - Multi-user separation on the same physical device. - * - * The derived permissions look like this: - * - * rwxrwx--x root:sdcard_rw / - * rwxrwx--- root:sdcard_pics /Pictures - * rwxrwx--- root:sdcard_av /Music - * - * rwxrwx--x root:sdcard_rw /Android - * rwxrwx--x root:sdcard_rw /Android/data - * rwxrwx--- u0_a12:sdcard_rw /Android/data/com.example - * rwxrwx--x root:sdcard_rw /Android/obb/ - * rwxrwx--- u0_a12:sdcard_rw /Android/obb/com.example - * - * rwxrwx--- root:sdcard_all /Android/user - * rwxrwx--x root:sdcard_rw /Android/user/10 - * rwxrwx--- u10_a12:sdcard_rw /Android/user/10/Android/data/com.example */ #define FUSE_TRACE 0 @@ -115,9 +99,6 @@ * the largest possible data payload. */ #define MAX_REQUEST_SIZE (sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) + MAX_WRITE) -/* Default number of threads. */ -#define DEFAULT_NUM_THREADS 2 - /* Pseudo-error constant used to indicate that no fuse status is needed * or that a reply has already been written. */ #define NO_STATUS 1 @@ -135,7 +116,7 @@ typedef enum { 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, + PERM_PRE_ROOT, /* This node is "/" */ PERM_ROOT, /* This node is "/Android" */ @@ -146,17 +127,8 @@ typedef enum { PERM_ANDROID_OBB, /* This node is "/Android/media" */ PERM_ANDROID_MEDIA, - /* 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; }; @@ -179,8 +151,7 @@ struct node { perm_t perm; userid_t userid; uid_t uid; - gid_t gid; - mode_t mode; + bool under_android; struct node *next; /* per-dir sibling list */ struct node *child; /* first contained file by this dir */ @@ -212,25 +183,21 @@ static bool str_icase_equals(void *keyA, void *keyB) { return strcasecmp(keyA, keyB) == 0; } -static int int_hash(void *key) { - return (int) (uintptr_t) key; -} +/* Global data for all FUSE mounts */ +struct fuse_global { + pthread_mutex_t lock; -static bool int_equals(void *keyA, void *keyB) { - return keyA == keyB; -} + uid_t uid; + gid_t gid; + bool multi_user; -/* Global data structure shared by all fuse handlers. */ -struct fuse { - pthread_mutex_t lock; + char source_path[PATH_MAX]; + char obb_path[PATH_MAX]; + + Hashmap* package_to_appid; __u64 next_generation; - int fd; - derive_t derive; - bool split_perms; - gid_t write_gid; struct node root; - char obbpath[PATH_MAX]; /* Used to allocate unique inode numbers for fuse nodes. We use * a simple counter based scheme where inode numbers from deleted @@ -251,11 +218,24 @@ struct fuse { */ __u32 inode_ctr; - Hashmap* package_to_appid; - Hashmap* appid_with_rw; + struct fuse* fuse_default; + struct fuse* fuse_read; + struct fuse* fuse_write; }; -/* Private data used by a single fuse handler. */ +/* Single FUSE mount */ +struct fuse { + struct fuse_global* global; + + char dest_path[PATH_MAX]; + + int fd; + + gid_t gid; + mode_t mask; +}; + +/* Private data used by a single FUSE handler */ struct fuse_handler { struct fuse* fuse; int token; @@ -412,8 +392,8 @@ static char* find_file_within(const char* path, const char* name, return actual; } -static void attr_from_stat(struct fuse_attr *attr, const struct stat *s, const struct node* node) -{ +static void attr_from_stat(struct fuse* fuse, struct fuse_attr *attr, + const struct stat *s, const struct node* node) { attr->ino = node->ino; attr->size = s->st_size; attr->blocks = s->st_blocks; @@ -427,12 +407,35 @@ static void attr_from_stat(struct fuse_attr *attr, const struct stat *s, const s attr->nlink = s->st_nlink; attr->uid = node->uid; - attr->gid = node->gid; - /* Filter requested mode based on underlying file, and - * pass through file type. */ + if (fuse->gid == AID_SDCARD_RW) { + /* As an optimization, certain trusted system components only run + * as owner but operate across all users. Since we're now handing + * out the sdcard_rw GID only to trusted apps, we're okay relaxing + * the user boundary enforcement for the default view. The UIDs + * assigned to app directories are still multiuser aware. */ + attr->gid = AID_SDCARD_RW; + } else { + attr->gid = multiuser_get_uid(node->userid, fuse->gid); + } + + int visible_mode = 0775 & ~fuse->mask; + if (node->perm == PERM_PRE_ROOT) { + /* Top of multi-user view should always be visible to ensure + * secondary users can traverse inside. */ + visible_mode = 0711; + } else if (node->under_android) { + /* Block "other" access to Android directories, since only apps + * belonging to a specific user should be in there; we still + * leave +x open for the default view. */ + if (fuse->gid == AID_SDCARD_RW) { + visible_mode = visible_mode & ~0006; + } else { + visible_mode = visible_mode & ~0007; + } + } int owner_mode = s->st_mode & 0700; - int filtered_mode = node->mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6)); + int filtered_mode = visible_mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6)); attr->mode = (attr->mode & S_IFMT) | filtered_mode; } @@ -458,97 +461,50 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent, node->perm = PERM_INHERIT; node->userid = parent->userid; node->uid = parent->uid; - node->gid = parent->gid; - node->mode = parent->mode; - - if (fuse->derive == DERIVE_NONE) { - return; - } + node->under_android = parent->under_android; /* Derive custom permissions based on parent and current node */ switch (parent->perm) { case PERM_INHERIT: /* Already inherited above */ break; - case PERM_LEGACY_PRE_ROOT: + case PERM_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: /* 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 (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; - } + node->under_android = true; } break; case PERM_ANDROID: if (!strcasecmp(node->name, "data")) { /* App-specific directories inside; let anyone traverse */ node->perm = PERM_ANDROID_DATA; - node->mode = 0771; } else if (!strcasecmp(node->name, "obb")) { /* App-specific directories inside; let anyone traverse */ node->perm = PERM_ANDROID_OBB; - node->mode = 0771; /* Single OBB directory is always shared */ - node->graft_path = fuse->obbpath; - node->graft_pathlen = strlen(fuse->obbpath); + node->graft_path = fuse->global->obb_path; + node->graft_pathlen = strlen(fuse->global->obb_path); } else if (!strcasecmp(node->name, "media")) { /* App-specific directories inside; let anyone traverse */ node->perm = PERM_ANDROID_MEDIA; - node->mode = 0771; - } else if (!strcasecmp(node->name, "user")) { - /* User directories must only be accessible to system, protected - * by sdcard_all. Zygote will bind mount the appropriate user- - * specific path. */ - node->perm = PERM_ANDROID_USER; - node->gid = AID_SDCARD_ALL; - node->mode = 0770; } break; case PERM_ANDROID_DATA: case PERM_ANDROID_OBB: case PERM_ANDROID_MEDIA: - appid = (appid_t) (uintptr_t) hashmapGet(fuse->package_to_appid, node->name); + appid = (appid_t) (uintptr_t) hashmapGet(fuse->global->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 = 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*) (uintptr_t) appid); } /* Kernel has already enforced everything we returned through @@ -556,7 +512,7 @@ static bool get_caller_has_rw_locked(struct fuse* fuse, const struct fuse_in_hea * 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) { + const char* name, int mode) { /* Always block security-sensitive files at root */ if (parent_node && parent_node->perm == PERM_ROOT) { if (!strcasecmp(name, "autorun.inf") @@ -566,34 +522,19 @@ static bool check_caller_access_to_name(struct fuse* fuse, } } - /* 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); + const struct fuse_in_header *hdr, const struct node* node, int mode) { + return check_caller_access_to_name(fuse, hdr, node->parent, node->name, mode); } struct node *create_node_locked(struct fuse* fuse, @@ -604,7 +545,7 @@ struct node *create_node_locked(struct fuse* fuse, // Detect overflows in the inode counter. "4 billion nodes should be enough // for everybody". - if (fuse->inode_ctr == 0) { + if (fuse->global->inode_ctr == 0) { ERROR("No more inode numbers available"); return NULL; } @@ -630,8 +571,8 @@ struct node *create_node_locked(struct fuse* fuse, } node->namelen = namelen; node->nid = ptr_to_id(node); - node->ino = fuse->inode_ctr++; - node->gen = fuse->next_generation++; + node->ino = fuse->global->inode_ctr++; + node->gen = fuse->global->next_generation++; node->deleted = false; @@ -685,7 +626,7 @@ static int rename_node_locked(struct node *node, const char *name, static struct node *lookup_node_by_id_locked(struct fuse *fuse, __u64 nid) { if (nid == FUSE_ROOT_ID) { - return &fuse->root; + return &fuse->global->root; } else { return id_to_ptr(nid); } @@ -728,59 +669,6 @@ static struct node* acquire_or_create_child_locked( return child; } -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 = derive; - fuse->split_perms = split_perms; - fuse->write_gid = write_gid; - fuse->inode_ctr = 1; - - 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.userid = 0; - fuse->root.uid = AID_ROOT; - - /* 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; - } -} - static void fuse_status(struct fuse *fuse, __u64 unique, int err) { struct fuse_out_header hdr; @@ -823,19 +711,19 @@ static int fuse_reply_entry(struct fuse* fuse, __u64 unique, return -errno; } - pthread_mutex_lock(&fuse->lock); + pthread_mutex_lock(&fuse->global->lock); node = acquire_or_create_child_locked(fuse, parent, name, actual_name); if (!node) { - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); return -ENOMEM; } memset(&out, 0, sizeof(out)); - attr_from_stat(&out.attr, &s, node); + attr_from_stat(fuse, &out.attr, &s, node); out.attr_valid = 10; out.entry_valid = 10; out.nodeid = node->nid; out.generation = node->gen; - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); fuse_reply(fuse, unique, &out, sizeof(out)); return NO_STATUS; } @@ -850,12 +738,43 @@ static int fuse_reply_attr(struct fuse* fuse, __u64 unique, const struct node* n return -errno; } memset(&out, 0, sizeof(out)); - attr_from_stat(&out.attr, &s, node); + attr_from_stat(fuse, &out.attr, &s, node); out.attr_valid = 10; fuse_reply(fuse, unique, &out, sizeof(out)); return NO_STATUS; } +static void fuse_notify_delete(struct fuse* fuse, const __u64 parent, + const __u64 child, const char* name) { + struct fuse_out_header hdr; + struct fuse_notify_delete_out data; + struct iovec vec[3]; + size_t namelen = strlen(name); + int res; + + hdr.len = sizeof(hdr) + sizeof(data) + namelen + 1; + hdr.error = FUSE_NOTIFY_DELETE; + hdr.unique = 0; + + data.parent = parent; + data.child = child; + data.namelen = namelen; + data.padding = 0; + + vec[0].iov_base = &hdr; + vec[0].iov_len = sizeof(hdr); + vec[1].iov_base = &data; + vec[1].iov_len = sizeof(data); + vec[2].iov_base = (void*) name; + vec[2].iov_len = namelen + 1; + + res = writev(fuse->fd, vec, 3); + /* Ignore ENOENT, since other views may not have seen the entry */ + if (res < 0 && errno != ENOENT) { + ERROR("*** NOTIFY FAILED *** %d\n", errno); + } +} + static int handle_lookup(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header *hdr, const char* name) { @@ -864,18 +783,18 @@ static int handle_lookup(struct fuse* fuse, struct fuse_handler* handler, char child_path[PATH_MAX]; const char* actual_name; - pthread_mutex_lock(&fuse->lock); + pthread_mutex_lock(&fuse->global->lock); parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, parent_path, sizeof(parent_path)); TRACE("[%d] LOOKUP %s @ %"PRIx64" (%s)\n", handler->token, name, hdr->nodeid, parent_node ? parent_node->name : "?"); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); if (!parent_node || !(actual_name = find_file_within(parent_path, name, child_path, sizeof(child_path), 1))) { return -ENOENT; } - if (!check_caller_access_to_name(fuse, hdr, parent_node, name, R_OK, false)) { + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, R_OK)) { return -EACCES; } @@ -887,7 +806,7 @@ static int handle_forget(struct fuse* fuse, struct fuse_handler* handler, { struct node* node; - pthread_mutex_lock(&fuse->lock); + pthread_mutex_lock(&fuse->global->lock); node = lookup_node_by_id_locked(fuse, hdr->nodeid); TRACE("[%d] FORGET #%"PRIu64" @ %"PRIx64" (%s)\n", handler->token, req->nlookup, hdr->nodeid, node ? node->name : "?"); @@ -897,7 +816,7 @@ static int handle_forget(struct fuse* fuse, struct fuse_handler* handler, release_node_locked(node); } } - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); return NO_STATUS; /* no reply */ } @@ -907,16 +826,16 @@ static int handle_getattr(struct fuse* fuse, struct fuse_handler* handler, struct node* node; char path[PATH_MAX]; - pthread_mutex_lock(&fuse->lock); + pthread_mutex_lock(&fuse->global->lock); node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); TRACE("[%d] GETATTR flags=%x fh=%"PRIx64" @ %"PRIx64" (%s)\n", handler->token, req->getattr_flags, req->fh, hdr->nodeid, node ? node->name : "?"); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); if (!node) { return -ENOENT; } - if (!check_caller_access_to_node(fuse, hdr, node, R_OK, false)) { + if (!check_caller_access_to_node(fuse, hdr, node, R_OK)) { return -EACCES; } @@ -926,24 +845,22 @@ static int handle_getattr(struct fuse* fuse, struct fuse_handler* handler, 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); + pthread_mutex_lock(&fuse->global->lock); node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); TRACE("[%d] SETATTR fh=%"PRIx64" valid=%x @ %"PRIx64" (%s)\n", handler->token, req->fh, req->valid, hdr->nodeid, node ? node->name : "?"); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); if (!node) { return -ENOENT; } if (!(req->valid & FATTR_FH) && - !check_caller_access_to_node(fuse, hdr, node, W_OK, has_rw)) { + !check_caller_access_to_node(fuse, hdr, node, W_OK)) { return -EACCES; } @@ -991,25 +908,23 @@ static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler, 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); + pthread_mutex_lock(&fuse->global->lock); parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, parent_path, sizeof(parent_path)); TRACE("[%d] MKNOD %s 0%o @ %"PRIx64" (%s)\n", handler->token, name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?"); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); if (!parent_node || !(actual_name = find_file_within(parent_path, name, child_path, sizeof(child_path), 1))) { return -ENOENT; } - if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) { + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) { return -EACCES; } __u32 mode = (req->mode & (~0777)) | 0664; @@ -1022,25 +937,23 @@ static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler, 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); + pthread_mutex_lock(&fuse->global->lock); parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, parent_path, sizeof(parent_path)); TRACE("[%d] MKDIR %s 0%o @ %"PRIx64" (%s)\n", handler->token, name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?"); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); if (!parent_node || !(actual_name = find_file_within(parent_path, name, child_path, sizeof(child_path), 1))) { return -ENOENT; } - if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) { + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) { return -EACCES; } __u32 mode = (req->mode & (~0777)) | 0775; @@ -1059,7 +972,7 @@ static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler, } if (parent_node->perm == PERM_ANDROID && !strcasecmp(name, "obb")) { char nomedia[PATH_MAX]; - snprintf(nomedia, PATH_MAX, "%s/.nomedia", fuse->obbpath); + snprintf(nomedia, PATH_MAX, "%s/.nomedia", fuse->global->obb_path); if (touch(nomedia, 0664) != 0) { ERROR("Failed to touch(%s): %s\n", nomedia, strerror(errno)); return -ENOENT; @@ -1072,72 +985,96 @@ static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler, 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; struct node* child_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); + pthread_mutex_lock(&fuse->global->lock); parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, parent_path, sizeof(parent_path)); TRACE("[%d] UNLINK %s @ %"PRIx64" (%s)\n", handler->token, name, hdr->nodeid, parent_node ? parent_node->name : "?"); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); if (!parent_node || !find_file_within(parent_path, name, child_path, sizeof(child_path), 1)) { return -ENOENT; } - if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) { + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) { return -EACCES; } if (unlink(child_path) < 0) { return -errno; } - pthread_mutex_lock(&fuse->lock); + pthread_mutex_lock(&fuse->global->lock); child_node = lookup_child_by_name_locked(parent_node, name); if (child_node) { child_node->deleted = true; } - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); + if (parent_node && child_node) { + /* Tell all other views that node is gone */ + TRACE("[%d] fuse_notify_delete parent=%"PRIx64", child=%"PRIx64", name=%s\n", + handler->token, (uint64_t) parent_node->nid, (uint64_t) child_node->nid, name); + if (fuse != fuse->global->fuse_default) { + fuse_notify_delete(fuse->global->fuse_default, parent_node->nid, child_node->nid, name); + } + if (fuse != fuse->global->fuse_read) { + fuse_notify_delete(fuse->global->fuse_read, parent_node->nid, child_node->nid, name); + } + if (fuse != fuse->global->fuse_write) { + fuse_notify_delete(fuse->global->fuse_write, parent_node->nid, child_node->nid, name); + } + } return 0; } 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* child_node; 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); + pthread_mutex_lock(&fuse->global->lock); parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, parent_path, sizeof(parent_path)); TRACE("[%d] RMDIR %s @ %"PRIx64" (%s)\n", handler->token, name, hdr->nodeid, parent_node ? parent_node->name : "?"); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); if (!parent_node || !find_file_within(parent_path, name, child_path, sizeof(child_path), 1)) { return -ENOENT; } - if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) { + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) { return -EACCES; } if (rmdir(child_path) < 0) { return -errno; } - pthread_mutex_lock(&fuse->lock); + pthread_mutex_lock(&fuse->global->lock); child_node = lookup_child_by_name_locked(parent_node, name); if (child_node) { child_node->deleted = true; } - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); + if (parent_node && child_node) { + /* Tell all other views that node is gone */ + TRACE("[%d] fuse_notify_delete parent=%"PRIx64", child=%"PRIx64", name=%s\n", + handler->token, (uint64_t) parent_node->nid, (uint64_t) child_node->nid, name); + if (fuse != fuse->global->fuse_default) { + fuse_notify_delete(fuse->global->fuse_default, parent_node->nid, child_node->nid, name); + } + if (fuse != fuse->global->fuse_read) { + fuse_notify_delete(fuse->global->fuse_read, parent_node->nid, child_node->nid, name); + } + if (fuse != fuse->global->fuse_write) { + fuse_notify_delete(fuse->global->fuse_write, parent_node->nid, child_node->nid, name); + } + } return 0; } @@ -1145,7 +1082,6 @@ static int handle_rename(struct fuse* fuse, struct fuse_handler* handler, 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; @@ -1156,8 +1092,7 @@ static int handle_rename(struct fuse* fuse, struct fuse_handler* handler, const char* new_actual_name; int res; - pthread_mutex_lock(&fuse->lock); - has_rw = get_caller_has_rw_locked(fuse, hdr); + pthread_mutex_lock(&fuse->global->lock); 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, @@ -1170,11 +1105,11 @@ static int handle_rename(struct fuse* fuse, struct fuse_handler* handler, res = -ENOENT; goto lookup_error; } - if (!check_caller_access_to_name(fuse, hdr, old_parent_node, old_name, W_OK, has_rw)) { + if (!check_caller_access_to_name(fuse, hdr, old_parent_node, old_name, W_OK)) { res = -EACCES; goto lookup_error; } - if (!check_caller_access_to_name(fuse, hdr, new_parent_node, new_name, W_OK, has_rw)) { + if (!check_caller_access_to_name(fuse, hdr, new_parent_node, new_name, W_OK)) { res = -EACCES; goto lookup_error; } @@ -1185,7 +1120,7 @@ static int handle_rename(struct fuse* fuse, struct fuse_handler* handler, goto lookup_error; } acquire_node_locked(child_node); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); /* Special case for renaming a file where destination is same path * differing only by case. In this case we don't want to look for a case @@ -1206,7 +1141,7 @@ static int handle_rename(struct fuse* fuse, struct fuse_handler* handler, goto io_error; } - pthread_mutex_lock(&fuse->lock); + pthread_mutex_lock(&fuse->global->lock); res = rename_node_locked(child_node, new_name, new_actual_name); if (!res) { remove_node_from_parent_locked(child_node); @@ -1215,11 +1150,11 @@ static int handle_rename(struct fuse* fuse, struct fuse_handler* handler, goto done; io_error: - pthread_mutex_lock(&fuse->lock); + pthread_mutex_lock(&fuse->global->lock); done: release_node_locked(child_node); lookup_error: - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); return res; } @@ -1237,24 +1172,22 @@ static int open_flags_to_access_mode(int open_flags) { 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); + pthread_mutex_lock(&fuse->global->lock); node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); TRACE("[%d] OPEN 0%o @ %"PRIx64" (%s)\n", handler->token, req->flags, hdr->nodeid, node ? node->name : "?"); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); if (!node) { return -ENOENT; } if (!check_caller_access_to_node(fuse, hdr, node, - open_flags_to_access_mode(req->flags), has_rw)) { + open_flags_to_access_mode(req->flags))) { return -EACCES; } h = malloc(sizeof(*h)); @@ -1335,14 +1268,14 @@ static int handle_statfs(struct fuse* fuse, struct fuse_handler* handler, struct fuse_statfs_out out; int res; - pthread_mutex_lock(&fuse->lock); + pthread_mutex_lock(&fuse->global->lock); TRACE("[%d] STATFS\n", handler->token); - res = get_node_path_locked(&fuse->root, path, sizeof(path)); - pthread_mutex_unlock(&fuse->lock); + res = get_node_path_locked(&fuse->global->root, path, sizeof(path)); + pthread_mutex_unlock(&fuse->global->lock); if (res < 0) { return -ENOENT; } - if (statfs(fuse->root.name, &stat) < 0) { + if (statfs(fuse->global->root.name, &stat) < 0) { return -errno; } memset(&out, 0, sizeof(out)); @@ -1409,16 +1342,16 @@ static int handle_opendir(struct fuse* fuse, struct fuse_handler* handler, struct fuse_open_out out; struct dirhandle *h; - pthread_mutex_lock(&fuse->lock); + pthread_mutex_lock(&fuse->global->lock); node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); TRACE("[%d] OPENDIR @ %"PRIx64" (%s)\n", handler->token, hdr->nodeid, node ? node->name : "?"); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&fuse->global->lock); if (!node) { return -ENOENT; } - if (!check_caller_access_to_node(fuse, hdr, node, R_OK, false)) { + if (!check_caller_access_to_node(fuse, hdr, node, R_OK)) { return -EACCES; } h = malloc(sizeof(*h)); @@ -1498,7 +1431,8 @@ static int handle_init(struct fuse* fuse, struct fuse_handler* handler, return -1; } - out.minor = MIN(req->minor, FUSE_KERNEL_MINOR_VERSION); + /* We limit ourselves to 15 because we don't handle BATCH_FORGET yet */ + out.minor = MIN(req->minor, 15); fuse_struct_size = sizeof(out); #if defined(FUSE_COMPAT_22_INIT_OUT_SIZE) /* FUSE_KERNEL_VERSION >= 23. */ @@ -1648,12 +1582,14 @@ static void handle_fuse_requests(struct fuse_handler* handler) { struct fuse* fuse = handler->fuse; for (;;) { - ssize_t len = read(fuse->fd, - handler->request_buffer, sizeof(handler->request_buffer)); + ssize_t len = TEMP_FAILURE_RETRY(read(fuse->fd, + handler->request_buffer, sizeof(handler->request_buffer))); if (len < 0) { - if (errno != EINTR) { - ERROR("[%d] handle_fuse_requests: errno=%d\n", handler->token, errno); + if (errno == ENODEV) { + ERROR("[%d] someone stole our marbles!\n", handler->token); + exit(2); } + ERROR("[%d] handle_fuse_requests: errno=%d\n", handler->token, errno); continue; } @@ -1700,22 +1636,15 @@ static bool remove_str_to_int(void *key, void *value, void *context) { 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); +static int read_package_list(struct fuse_global* global) { + pthread_mutex_lock(&global->lock); - 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); + hashmapForEach(global->package_to_appid, remove_str_to_int, global->package_to_appid); FILE* file = fopen(kPackagesListFile, "r"); if (!file) { ERROR("failed to open package list: %s\n", strerror(errno)); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&global->lock); return -1; } @@ -1727,28 +1656,18 @@ static int read_package_list(struct fuse *fuse) { 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*) (uintptr_t) appid); - - char* token = strtok(gids, ","); - while (token != NULL) { - if (strtoul(token, NULL, 10) == fuse->write_gid) { - hashmapPut(fuse->appid_with_rw, (void*) (uintptr_t) appid, (void*) (uintptr_t) 1); - break; - } - token = strtok(NULL, ","); - } + hashmapPut(global->package_to_appid, package_name_dup, (void*) (uintptr_t) appid); } } - TRACE("read_package_list: found %zu packages, %zu with write_gid\n", - hashmapSize(fuse->package_to_appid), - hashmapSize(fuse->appid_with_rw)); + TRACE("read_package_list: found %zu packages\n", + hashmapSize(global->package_to_appid)); fclose(file); - pthread_mutex_unlock(&fuse->lock); + pthread_mutex_unlock(&global->lock); return 0; } -static void watch_package_list(struct fuse* fuse) { +static void watch_package_list(struct fuse_global* global) { struct inotify_event *event; char event_buf[512]; @@ -1776,7 +1695,7 @@ static void watch_package_list(struct fuse* fuse) { /* Watch above will tell us about any future changes, so * read the current state. */ - if (read_package_list(fuse) == -1) { + if (read_package_list(global) == -1) { ERROR("read_package_list failed: %s\n", strerror(errno)); return; } @@ -1810,138 +1729,178 @@ static void watch_package_list(struct fuse* fuse) { } } -static int ignite_fuse(struct fuse* fuse, int num_threads) -{ - struct fuse_handler* handlers; - int i; +static int usage() { + ERROR("usage: sdcard [OPTIONS] <source_path> <label>\n" + " -u: specify UID to run as\n" + " -g: specify GID to run as\n" + " -U: specify user ID that owns device\n" + " -m: source_path is multi-user\n" + " -w: runtime write mount has full write access\n" + "\n"); + return 1; +} - handlers = malloc(num_threads * sizeof(struct fuse_handler)); - if (!handlers) { - ERROR("cannot allocate storage for threads\n"); - return -ENOMEM; - } +static int fuse_setup(struct fuse* fuse, gid_t gid, mode_t mask) { + char opts[256]; - for (i = 0; i < num_threads; i++) { - handlers[i].fuse = fuse; - handlers[i].token = i; + fuse->fd = open("/dev/fuse", O_RDWR); + if (fuse->fd == -1) { + ERROR("failed to open fuse device: %s\n", strerror(errno)); + return -1; } - /* When deriving permissions, this thread is used to process inotify events, - * otherwise it becomes one of the FUSE handlers. */ - 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]); - if (res) { - ERROR("failed to start thread #%d, error=%d\n", i, res); - goto quit; - } - } + umount2(fuse->dest_path, MNT_DETACH); - if (fuse->derive == DERIVE_NONE) { - handle_fuse_requests(&handlers[0]); - } else { - watch_package_list(fuse); + snprintf(opts, sizeof(opts), + "fd=%i,rootmode=40000,default_permissions,allow_other,user_id=%d,group_id=%d", + fuse->fd, fuse->global->uid, fuse->global->gid); + if (mount("/dev/fuse", fuse->dest_path, "fuse", MS_NOSUID | MS_NODEV | MS_NOEXEC | + MS_NOATIME, opts) != 0) { + ERROR("failed to mount fuse filesystem: %s\n", strerror(errno)); + return -1; } - ERROR("terminated prematurely\n"); + fuse->gid = gid; + fuse->mask = mask; - /* don't bother killing all of the other threads or freeing anything, - * should never get here anyhow */ -quit: - exit(1); + return 0; } -static int usage() -{ - 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 void run(const char* source_path, const char* label, uid_t uid, + gid_t gid, userid_t userid, bool multi_user, bool full_write) { + struct fuse_global global; + struct fuse fuse_default; + struct fuse fuse_read; + struct fuse fuse_write; + struct fuse_handler handler_default; + struct fuse_handler handler_read; + struct fuse_handler handler_write; + pthread_t thread_default; + pthread_t thread_read; + pthread_t thread_write; + + memset(&global, 0, sizeof(global)); + memset(&fuse_default, 0, sizeof(fuse_default)); + memset(&fuse_read, 0, sizeof(fuse_read)); + memset(&fuse_write, 0, sizeof(fuse_write)); + memset(&handler_default, 0, sizeof(handler_default)); + memset(&handler_read, 0, sizeof(handler_read)); + memset(&handler_write, 0, sizeof(handler_write)); + + pthread_mutex_init(&global.lock, NULL); + global.package_to_appid = hashmapCreate(256, str_hash, str_icase_equals); + global.uid = uid; + global.gid = gid; + global.multi_user = multi_user; + global.next_generation = 0; + global.inode_ctr = 1; + + memset(&global.root, 0, sizeof(global.root)); + global.root.nid = FUSE_ROOT_ID; /* 1 */ + global.root.refcount = 2; + global.root.namelen = strlen(source_path); + global.root.name = strdup(source_path); + global.root.userid = userid; + global.root.uid = AID_ROOT; + global.root.under_android = false; + + strcpy(global.source_path, source_path); + + if (multi_user) { + global.root.perm = PERM_PRE_ROOT; + snprintf(global.obb_path, sizeof(global.obb_path), "%s/obb", source_path); + } else { + global.root.perm = PERM_ROOT; + snprintf(global.obb_path, sizeof(global.obb_path), "%s/Android/obb", source_path); + } -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; - struct fuse fuse; + fuse_default.global = &global; + fuse_read.global = &global; + fuse_write.global = &global; - /* cleanup from previous instance, if necessary */ - umount2(dest_path, MNT_DETACH); + global.fuse_default = &fuse_default; + global.fuse_read = &fuse_read; + global.fuse_write = &fuse_write; - fd = open("/dev/fuse", O_RDWR); - if (fd < 0){ - ERROR("cannot open fuse device: %s\n", strerror(errno)); - return -1; - } + snprintf(fuse_default.dest_path, PATH_MAX, "/mnt/runtime/default/%s", label); + snprintf(fuse_read.dest_path, PATH_MAX, "/mnt/runtime/read/%s", label); + snprintf(fuse_write.dest_path, PATH_MAX, "/mnt/runtime/write/%s", label); - snprintf(opts, sizeof(opts), - "fd=%i,rootmode=40000,default_permissions,allow_other,user_id=%d,group_id=%d", - fd, uid, gid); + handler_default.fuse = &fuse_default; + handler_read.fuse = &fuse_read; + handler_write.fuse = &fuse_write; - res = mount("/dev/fuse", dest_path, "fuse", MS_NOSUID | MS_NODEV | MS_NOEXEC | - MS_NOATIME, opts); - if (res < 0) { - ERROR("cannot mount fuse filesystem: %s\n", strerror(errno)); - goto error; + handler_default.token = 0; + handler_read.token = 1; + handler_write.token = 2; + + umask(0); + + if (multi_user) { + /* Multi-user storage is fully isolated per user, so "other" + * permissions are completely masked off. */ + if (fuse_setup(&fuse_default, AID_SDCARD_RW, 0006) + || fuse_setup(&fuse_read, AID_EVERYBODY, 0027) + || fuse_setup(&fuse_write, AID_EVERYBODY, full_write ? 0007 : 0027)) { + ERROR("failed to fuse_setup\n"); + exit(1); + } + } else { + /* Physical storage is readable by all users on device, but + * the Android directories are masked off to a single user + * deep inside attr_from_stat(). */ + if (fuse_setup(&fuse_default, AID_SDCARD_RW, 0006) + || fuse_setup(&fuse_read, AID_EVERYBODY, full_write ? 0027 : 0022) + || fuse_setup(&fuse_write, AID_EVERYBODY, full_write ? 0007 : 0022)) { + ERROR("failed to fuse_setup\n"); + exit(1); + } } - res = setgroups(sizeof(kGroups) / sizeof(kGroups[0]), kGroups); - if (res < 0) { + /* Drop privs */ + if (setgroups(sizeof(kGroups) / sizeof(kGroups[0]), kGroups) < 0) { ERROR("cannot setgroups: %s\n", strerror(errno)); - goto error; + exit(1); } - - res = setgid(gid); - if (res < 0) { + if (setgid(gid) < 0) { ERROR("cannot setgid: %s\n", strerror(errno)); - goto error; + exit(1); } - - res = setuid(uid); - if (res < 0) { + if (setuid(uid) < 0) { ERROR("cannot setuid: %s\n", strerror(errno)); - goto error; + exit(1); } - fuse_init(&fuse, fd, source_path, write_gid, derive, split_perms); - - umask(0); - res = ignite_fuse(&fuse, num_threads); + if (multi_user) { + fs_prepare_dir(global.obb_path, 0775, uid, gid); + } - /* we do not attempt to umount the file system here because we are no longer - * running as the root user */ + if (pthread_create(&thread_default, NULL, start_handler, &handler_default) + || pthread_create(&thread_read, NULL, start_handler, &handler_read) + || pthread_create(&thread_write, NULL, start_handler, &handler_write)) { + ERROR("failed to pthread_create\n"); + exit(1); + } -error: - close(fd); - return res; + watch_package_list(&global); + ERROR("terminated prematurely\n"); + exit(1); } -int main(int argc, char **argv) -{ - int res; +int main(int argc, char **argv) { const char *source_path = NULL; - const char *dest_path = NULL; + const char *label = NULL; uid_t uid = 0; gid_t gid = 0; - gid_t write_gid = AID_SDCARD_RW; - int num_threads = DEFAULT_NUM_THREADS; - derive_t derive = DERIVE_NONE; - bool split_perms = false; + userid_t userid = 0; + bool multi_user = false; + bool full_write = false; int i; struct rlimit rlim; int fs_version; int opt; - while ((opt = getopt(argc, argv, "u:g:w:t:dls")) != -1) { + while ((opt = getopt(argc, argv, "u:g:U:mw")) != -1) { switch (opt) { case 'u': uid = strtoul(optarg, NULL, 10); @@ -1949,20 +1908,14 @@ int main(int argc, char **argv) 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 = DERIVE_UNIFIED; + case 'U': + userid = strtoul(optarg, NULL, 10); break; - case 'l': - derive = DERIVE_LEGACY; + case 'm': + multi_user = true; break; - case 's': - split_perms = true; + case 'w': + full_write = true; break; case '?': default: @@ -1974,12 +1927,8 @@ int main(int argc, char **argv) char* arg = argv[i]; if (!source_path) { source_path = arg; - } else if (!dest_path) { - dest_path = arg; - } else if (!uid) { - uid = strtoul(arg, NULL, 10); - } else if (!gid) { - gid = strtoul(arg, NULL, 10); + } else if (!label) { + label = arg; } else { ERROR("too many arguments\n"); return usage(); @@ -1990,22 +1939,14 @@ int main(int argc, char **argv) ERROR("no source path specified\n"); return usage(); } - if (!dest_path) { - ERROR("no dest path specified\n"); + if (!label) { + ERROR("no label specified\n"); return usage(); } if (!uid || !gid) { ERROR("uid and gid must be nonzero\n"); return usage(); } - if (num_threads < 1) { - 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; @@ -2018,6 +1959,6 @@ int main(int argc, char **argv) sleep(1); } - res = run(source_path, dest_path, uid, gid, write_gid, num_threads, derive, split_perms); - return res < 0 ? 1 : 0; + run(source_path, label, uid, gid, userid, multi_user, full_write); + return 1; } |
